From d91a37f356a358a363a94c3ca6604def55a8d03e Mon Sep 17 00:00:00 2001 From: ZDDC Date: Thu, 21 May 2026 16:49:23 -0500 Subject: [PATCH] test(transmittal): cover the publish-time validation gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit transmittal/js/validation.js had no in-browser coverage. Add a spec for both halves: the live #tracking-number aria-invalid binding (whitespace / underscore) and validateBeforePublish() — a clean transmittal passes; a tracking number with spaces/underscores fails and focuses the field; a per-file bad tracking number or revision is flagged by row. (The earlier audit's "transmittal is untested" was inaccurate — it already has paste/FS round-trip, drag-drop, and init-state specs; this fills the validation gap none of them covered.) Co-Authored-By: Claude Opus 4.7 (1M context) --- playwright.config.js | 4 ++ tests/transmittal-validation.spec.js | 82 ++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tests/transmittal-validation.spec.js diff --git a/playwright.config.js b/playwright.config.js index 8216626..8199f31 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -43,6 +43,10 @@ export default defineConfig({ name: 'transmittal-drag-drop', testMatch: 'transmittal-drag-drop.spec.js', }, + { + name: 'transmittal-validation', + testMatch: 'transmittal-validation.spec.js', + }, { name: 'classifier', testMatch: 'classifier.spec.js', diff --git a/tests/transmittal-validation.spec.js b/tests/transmittal-validation.spec.js new file mode 100644 index 0000000..15c605e --- /dev/null +++ b/tests/transmittal-validation.spec.js @@ -0,0 +1,82 @@ +import { test, expect } from '@playwright/test'; +import { MOCK_FS_INIT_SCRIPT } from './fixtures/mock-fs-api.js'; +import * as path from 'path'; + +const HTML_PATH = path.resolve('transmittal/dist/transmittal.html'); + +// Covers the transmittal validation module (transmittal/js/validation.js), +// which had no in-browser coverage: +// - the live #tracking-number aria-invalid binding (whitespace / underscore) +// - validateBeforePublish(): the publish-time gate that rejects a tracking +// number or a per-file tracking number / revision containing spaces or +// underscores. +test.describe('Transmittal – validation', () => { + test.beforeEach(async ({ page }) => { + await page.addInitScript(MOCK_FS_INIT_SCRIPT); + await page.goto(`file://${HTML_PATH}`, { waitUntil: 'networkidle' }); + await page.waitForSelector('#tracking-number'); + }); + + test('tracking-number field flags whitespace and underscores live', async ({ page }) => { + const input = page.locator('#tracking-number'); + + await input.fill('123456-EL-TRX-0001'); + await expect(input).toHaveAttribute('aria-invalid', 'false'); + + await input.fill('123456 EL TRX'); // space + await expect(input).toHaveAttribute('aria-invalid', 'true'); + await expect(input).toHaveClass(/border-red-500/); + + await input.fill('123456_EL_TRX'); // underscore + await expect(input).toHaveAttribute('aria-invalid', 'true'); + + await input.fill('123456-EL-TRX-0002'); // clean again — clears the flag + await expect(input).toHaveAttribute('aria-invalid', 'false'); + await expect(input).not.toHaveClass(/border-red-500/); + }); + + test('validateBeforePublish passes for a clean transmittal', async ({ page }) => { + const result = await page.evaluate(() => { + const app = window.transmittalApp; + app.dom.qs('#tracking-number').value = '123456-EL-TRX-0001'; + app.data.files = [{ + trackingNumber: '123456-EL-SPC-2623', revision: 'A', + status: 'IFC', extension: 'pdf', title: 'Spec', + name: 'x', path: '', size: 0, fileSize: 0, sha256: '', + }]; + return app.modules.validation.validateBeforePublish(); + }); + expect(result.ok).toBe(true); + expect(result.message).toBe(''); + }); + + test('validateBeforePublish rejects a tracking number with spaces/underscores and focuses it', async ({ page }) => { + const result = await page.evaluate(() => { + const app = window.transmittalApp; + app.dom.qs('#tracking-number').value = 'BAD NUMBER_01'; + app.data.files = []; + const r = app.modules.validation.validateBeforePublish(); + return { ok: r.ok, message: r.message, focusId: r.focusEl ? r.focusEl.id : null }; + }); + expect(result.ok).toBe(false); + expect(result.message).toContain('spaces or underscores'); + expect(result.focusId).toBe('tracking-number'); // gate points the user at the bad field + }); + + test('validateBeforePublish flags per-file bad tracking numbers and revisions by row', async ({ page }) => { + const result = await page.evaluate(() => { + const app = window.transmittalApp; + app.dom.qs('#tracking-number').value = '123456-EL-TRX-0001'; // header is clean + app.data.files = [ + { trackingNumber: '123456 EL SPC', revision: 'A' }, // row 1: space in tracking + { trackingNumber: '123456-EL-DRW', revision: 'B 1' }, // row 2: space in revision + ]; + return app.modules.validation.validateBeforePublish(); + }); + expect(result.ok).toBe(false); + expect(result.message).toContain('Row 1'); + expect(result.message).toContain('Row 2'); + expect(result.message).toMatch(/tracking number must not contain spaces or underscores/i); + expect(result.message).toMatch(/revision must not contain spaces/i); + }); +});