59 lines
4.2 KiB
JavaScript
59 lines
4.2 KiB
JavaScript
import {test} from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {readFile} from 'node:fs/promises';
|
|
import ts from 'typescript';
|
|
|
|
async function moduleUrl(path,transform=code=>code){
|
|
const source=await readFile(new URL(path,import.meta.url),'utf8');
|
|
const {outputText}=ts.transpileModule(source,{compilerOptions:{target:ts.ScriptTarget.ES2022,module:ts.ModuleKind.ESNext}});
|
|
return 'data:text/javascript;base64,'+Buffer.from(transform(outputText)).toString('base64');
|
|
}
|
|
const protocolUrl=await moduleUrl('../src/lib/api/protocol.ts');
|
|
const appDownloadUrl=await moduleUrl('../src/lib/app-download.ts',code=>code.replace(/from ['"]\.\/api\/protocol['"]/,`from '${protocolUrl}'`));
|
|
const {downloadUrl}=await import(appDownloadUrl);
|
|
const clientUrl=await moduleUrl('../src/lib/client/download.ts',code=>code.replace(/from ['"]\.\.\/app-download['"]/,`from '${appDownloadUrl}'`));
|
|
const {configureDownloadLinks,shouldUseDownloadDialog}=await import(clientUrl);
|
|
const click={defaultPrevented:false,button:0,metaKey:false,ctrlKey:false,shiftKey:false,altKey:false};
|
|
|
|
test('download configuration selects the first valid HTTP URL and rejects empty or unsafe values',()=>{
|
|
assert.equal(downloadUrl({}),'');
|
|
assert.equal(downloadUrl({config:{appAddr:''}}),'');
|
|
assert.equal(downloadUrl({config:{officialAppAddr:'javascript:alert(1)',appAddr:'https://example.com/app.apk'}}),'https://example.com/app.apk');
|
|
assert.equal(downloadUrl({config:{appAddr:' '},newAddressInfo:{appAddr:'https://example.com/fallback.apk'}}),'https://example.com/fallback.apk');
|
|
assert.equal(downloadUrl({config:{officialAppAddr:'https://example.com/official',appAddr:'https://example.com/other'}}),'https://example.com/official');
|
|
for(const value of ['javascript:alert(1)','data:text/plain,app','ftp://example.com/app','https://user:password@example.com/app']){
|
|
assert.equal(downloadUrl({config:{appAddr:value}}),'');
|
|
}
|
|
});
|
|
|
|
test('all download entries stay hidden until configured and are hidden again if configuration is removed',()=>{
|
|
const entries=Array.from({length:4},()=>({hidden:true}));
|
|
const links=Array.from({length:4},()=>({href:undefined,removeAttribute(name){if(name==='href')this.href=undefined;}}));
|
|
const root={querySelectorAll:selector=>selector==='[data-app-download-entry]'?entries:links};
|
|
configureDownloadLinks({},root);assert.ok(entries.every(entry=>entry.hidden));assert.ok(links.every(link=>link.href===undefined));
|
|
configureDownloadLinks({config:{appAddr:'https://example.com/app.apk'}},root);
|
|
assert.ok(entries.every(entry=>!entry.hidden));assert.ok(links.every(link=>link.href==='https://example.com/app.apk'));
|
|
const dynamicEntry={hidden:true},dynamicLink={href:undefined,removeAttribute(name){if(name==='href')this.href=undefined;}};
|
|
entries.push(dynamicEntry);links.push(dynamicLink);
|
|
configureDownloadLinks({config:{appAddr:'https://example.com/new.apk'}},root);
|
|
assert.equal(dynamicEntry.hidden,false);assert.equal(dynamicLink.href,'https://example.com/new.apk');
|
|
configureDownloadLinks({},root);assert.ok(entries.every(entry=>entry.hidden));assert.ok(links.every(link=>link.href===undefined));
|
|
});
|
|
|
|
test('desktop downloads use the link directly regardless of touch support',()=>{
|
|
for(const agent of ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)','Mozilla/5.0 (Windows NT 10.0; Win64; x64)','Mozilla/5.0 (X11; Linux x86_64)']){
|
|
assert.equal(shouldUseDownloadDialog(click,agent,0),false);
|
|
}
|
|
assert.equal(shouldUseDownloadDialog(click,'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',5),false);
|
|
assert.equal(click.defaultPrevented,false);
|
|
});
|
|
|
|
test('mobile click dialogs preserve modified clicks and other native browser actions',()=>{
|
|
const phone='Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) Mobile';
|
|
assert.equal(shouldUseDownloadDialog(click,phone,5),true);
|
|
assert.equal(shouldUseDownloadDialog(click,'Mozilla/5.0 (Linux; Android 14)',5),true);
|
|
assert.equal(shouldUseDownloadDialog(click,'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',5),true);
|
|
for(const key of ['defaultPrevented','metaKey','ctrlKey','shiftKey','altKey'])assert.equal(shouldUseDownloadDialog({...click,[key]:true},phone,5),false);
|
|
assert.equal(shouldUseDownloadDialog({...click,button:1},phone,5),false);
|
|
});
|