88 lines
3.3 KiB
JavaScript
88 lines
3.3 KiB
JavaScript
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { readFileSync } from 'node:fs';
|
||
|
|
import { createRequire } from 'node:module';
|
||
|
|
import test from 'node:test';
|
||
|
|
|
||
|
|
const requireWeb = createRequire(new URL('../../apps/web/package.json', import.meta.url));
|
||
|
|
const ts = requireWeb('typescript');
|
||
|
|
|
||
|
|
function loadCommonJs(source, requireForModule) {
|
||
|
|
const compiled = ts.transpileModule(source, { compilerOptions: {
|
||
|
|
module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022,
|
||
|
|
}}).outputText;
|
||
|
|
const exported = {};
|
||
|
|
new Function('require', 'exports', 'module', compiled)(requireForModule, exported, { exports: exported });
|
||
|
|
return exported;
|
||
|
|
}
|
||
|
|
|
||
|
|
const clientSource = readFileSync(new URL('../../apps/web/src/api/client.ts', import.meta.url), 'utf8');
|
||
|
|
const downloadSource = readFileSync(new URL('../../apps/web/src/api/download.ts', import.meta.url), 'utf8');
|
||
|
|
const client = loadCommonJs(clientSource, requireWeb);
|
||
|
|
const download = loadCommonJs(downloadSource, specifier => {
|
||
|
|
if (specifier === './client') return client;
|
||
|
|
return requireWeb(specifier);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('parses RFC 5987 and plain Content-Disposition filenames', () => {
|
||
|
|
assert.equal(download.filenameFromContentDisposition(
|
||
|
|
`attachment; filename*=UTF-8''%E6%8E%92%E4%BA%A7%E8%AE%A1%E5%88%92.xlsx`,
|
||
|
|
'fallback.xlsx',
|
||
|
|
), '排产计划.xlsx');
|
||
|
|
assert.equal(
|
||
|
|
download.filenameFromContentDisposition('attachment; filename="report plan.xlsx"', 'fallback.xlsx'),
|
||
|
|
'report plan.xlsx',
|
||
|
|
);
|
||
|
|
assert.equal(download.filenameFromContentDisposition(null, 'fallback.xlsx'), 'fallback.xlsx');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('authenticated download carries credentials and client headers before saving the blob', async () => {
|
||
|
|
const originalWindow = globalThis.window;
|
||
|
|
const originalDocument = globalThis.document;
|
||
|
|
const originalCreateObjectURL = URL.createObjectURL;
|
||
|
|
const originalRevokeObjectURL = URL.revokeObjectURL;
|
||
|
|
const clicked = [];
|
||
|
|
const revoked = [];
|
||
|
|
let captured = null;
|
||
|
|
try {
|
||
|
|
globalThis.window = {
|
||
|
|
apsDesktop: undefined,
|
||
|
|
localStorage: {
|
||
|
|
getItem: () => 'visitor-12345678',
|
||
|
|
setItem: () => {},
|
||
|
|
},
|
||
|
|
fetch: async (_input, init) => {
|
||
|
|
captured = init;
|
||
|
|
return new Response(new Blob(['xlsx']), {
|
||
|
|
status: 200,
|
||
|
|
headers: { 'Content-Disposition': `attachment; filename*=UTF-8''%E8%AE%A1%E5%88%92.xlsx` },
|
||
|
|
});
|
||
|
|
},
|
||
|
|
setTimeout: callback => { callback(); return 0; },
|
||
|
|
dispatchEvent: () => true,
|
||
|
|
};
|
||
|
|
globalThis.document = {
|
||
|
|
createElement: () => ({
|
||
|
|
style: {},
|
||
|
|
click() { clicked.push(this.download); },
|
||
|
|
remove() {},
|
||
|
|
}),
|
||
|
|
body: { appendChild: () => {} },
|
||
|
|
};
|
||
|
|
URL.createObjectURL = () => 'blob:test';
|
||
|
|
URL.revokeObjectURL = value => { revoked.push(value); };
|
||
|
|
|
||
|
|
const filename = await download.downloadAuthenticatedFile('/api/reports/plan?format=xlsx', 'fallback.xlsx');
|
||
|
|
|
||
|
|
assert.equal(filename, '计划.xlsx');
|
||
|
|
assert.deepEqual(clicked, ['计划.xlsx']);
|
||
|
|
assert.deepEqual(revoked, ['blob:test']);
|
||
|
|
assert.equal(captured.credentials, 'include');
|
||
|
|
assert.equal(new Headers(captured.headers).get('X-APS-Visitor-ID'), 'visitor-12345678');
|
||
|
|
} finally {
|
||
|
|
globalThis.window = originalWindow;
|
||
|
|
globalThis.document = originalDocument;
|
||
|
|
URL.createObjectURL = originalCreateObjectURL;
|
||
|
|
URL.revokeObjectURL = originalRevokeObjectURL;
|
||
|
|
}
|
||
|
|
});
|