From b67bb076af31341bcc54fb28ee4d3dae30715847 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sun, 18 Oct 2026 08:08:24 +0200 Subject: [PATCH] test(#653): pin one document branch and the pickers' MIME patterns The reverse classifier check read only the one includes(ext) list, so a second branch such as `if (ext === 'rtf') return 'document';` passed every test; the method may now return 'document' from exactly one place. The accept= checks kept only .ext tokens, so adding text/* to a picker passed; the non-extension tokens must be exactly image/* and video/*. Both verified by mutation (each fails the suite). Co-Authored-By: Claude Opus 5.5 (1M context) --- test/upload-document-list-parity.test.ts | 29 ++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/test/upload-document-list-parity.test.ts b/test/upload-document-list-parity.test.ts index 05aa23f8..64c346df 100644 --- a/test/upload-document-list-parity.test.ts +++ b/test/upload-document-list-parity.test.ts @@ -44,6 +44,22 @@ function acceptDocumentExts(accept: string): string[] { .sort(); } +/** The non-extension tokens of an `accept=` value (MIME patterns such as `image/*`), sorted. */ +function acceptOtherTokens(accept: string): string[] { + return accept + .split(',') + .map((t) => t.trim()) + .filter((t) => t && !t.startsWith('.')) + .sort(); +} + +/** + * The only MIME patterns a prompt picker offers. A wildcard such as `text/*` here would + * let the picker hand over files the extension list above never names, which the + * extension check alone cannot see. + */ +const PICKER_MIME_TOKENS = ['image/*', 'video/*']; + type AttachKind = (file: { name: string; type: string }) => string | null; function loadPromptAttachKind(): { kind: AttachKind; method: string } { @@ -71,6 +87,10 @@ describe('prompt-upload document list parity (ALLOWED_DOCUMENT_EXTS ⇄ browser const { kind, method } = loadPromptAttachKind(); const found = [...method.matchAll(/if \(\[([^\]]+)\]\.includes\(ext\)\) \{\s*return 'document';/g)]; expect(found.length, 'expected one document extension list in image-input.js _promptAttachKind()').toBe(1); + // A second document branch (`if (ext === 'rtf') return 'document';`) would slip past the + // list check above, so the method may return 'document' from exactly one place. + const returns = method.match(/return 'document'/g) ?? []; + expect(returns.length, "image-input.js _promptAttachKind() must return 'document' from one place only").toBe(1); const clientExts = [...found[0][1].matchAll(/'([^']+)'/g)].map((q) => q[1]); // And it is the list that actually returns 'document'. for (const ext of clientExts) expect(kind({ name: `report.${ext}`, type: '' })).toBe('document'); @@ -80,8 +100,9 @@ describe('prompt-upload document list parity (ALLOWED_DOCUMENT_EXTS ⇄ browser it('index.html #attachFileInput accept= lists exactly the server document extensions', () => { const input = new JSDOM(read('index.html')).window.document.getElementById('attachFileInput'); expect(input, '#attachFileInput not found in index.html').not.toBeNull(); - const exts = acceptDocumentExts(input!.getAttribute('accept') ?? ''); - expect(exts, 'index.html #attachFileInput accept=').toEqual(DOCUMENT_EXTS); + const accept = input!.getAttribute('accept') ?? ''; + expect(acceptDocumentExts(accept), 'index.html #attachFileInput accept=').toEqual(DOCUMENT_EXTS); + expect(acceptOtherTokens(accept), 'index.html #attachFileInput accept= MIME patterns').toEqual(PICKER_MIME_TOKENS); }); it('keyboard-accessory.js: both .paste-file-input accept= list exactly the server document extensions', () => { @@ -94,6 +115,10 @@ describe('prompt-upload document list parity (ALLOWED_DOCUMENT_EXTS ⇄ browser expect(acceptDocumentExts(accept), `keyboard-accessory.js .paste-file-input #${i + 1} accept=`).toEqual( DOCUMENT_EXTS ); + expect( + acceptOtherTokens(accept), + `keyboard-accessory.js .paste-file-input #${i + 1} accept= MIME patterns` + ).toEqual(PICKER_MIME_TOKENS); }); });