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('archive/dist/archive.html'); test.describe('Archive Browser', () => { test.beforeEach(async ({ page }) => { await page.addInitScript(MOCK_FS_INIT_SCRIPT); }); test('loads without errors', async ({ page }) => { await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('#appContainer', { timeout: 15000 }); // Page title contains "Archive" await expect(page).toHaveTitle(/Archive/i); // No-directory message is shown before any directory is opened await expect(page.locator('#noDirectoryMessage')).toBeVisible(); // The open-directory button is present await expect(page.locator('#addDirectoryBtn')).toBeVisible(); }); test('scans a mock directory tree and displays files', async ({ page }) => { await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 }); // Log to see if the scan is working page.on('console', msg => { console.log('Browser console:', msg.text()); }); page.on('pageerror', err => { console.log('Page error:', err.message); }); // The archive expects a two-level structure: root → transmittal-folder → files. // Flat files at the root are not counted — they must be inside subdirectories. await page.evaluate(() => { window.__setMockDirectoryTree('test-project', { '2025-01-15_123456-EM-TRN-0001 (IFC) - First Transmittal': { '123456-EL-SPC-2623_A (IFC) - Specification.pdf': '%PDF', '123456-EL-DRW-0001_B (IFR) - Drawing.dwg': 'DWG', }, '2025-02-10_123456-EM-TRN-0002 (IFC) - Second Transmittal': { '789012-ME-CAL-0001_A (IFA) - Calculation.pdf': '%PDF', }, }); console.log('Mock directory set up'); }); await page.locator('#addDirectoryBtn').click(); // Wait for scanning to complete await page.waitForTimeout(2000); // Log state for debugging await page.evaluate(() => { console.log('After scanning:'); console.log(' window.app exists:', typeof window.app !== 'undefined'); if (typeof window.app !== 'undefined') { console.log(' window.app type:', typeof window.app); console.log(' window.app constructor:', window.app.constructor.name); console.log(' window.app.directories:', Array.isArray(window.app.directories) ? window.app.directories.length : window.app.directories); console.log(' window.app.files:', Array.isArray(window.app.files) ? window.app.files.length : window.app.files); console.log(' window.app.filteredFiles:', Array.isArray(window.app.filteredFiles) ? window.app.filteredFiles.length : window.app.filteredFiles); console.log(' window.app modules:', Object.keys(window.app || {}).filter(k => k.startsWith('modules'))); } else { console.log(' window.app is undefined!'); } }); // Select all grouping folders so files are included in the file list await page.evaluate(() => { const cb = document.getElementById('selectAllGroupingCheckbox'); if (cb && !cb.checked) cb.click(); }); await page.waitForTimeout(500); // The files table should have at least one data row const rowCount = await page.locator('#filesTableBody tr').count(); expect(rowCount).toBeGreaterThanOrEqual(1); // Status bar should show files were found (any non-empty text is fine) const fileCountText = await page.locator('#fileCount').textContent(); expect(fileCountText).toBeTruthy(); }); test('projectFilter filters local-mode scan at root depth (virtual merge)', async ({ page }) => { // The corresponding URL flow is `archive.html?projects=A,B` → // url-state.restore() → window.app.projectFilter Set. We set the Set // directly here because the existing test environment hits an // unrelated init() error before url-state.restore() runs (a // getElementById returning null in events.js); a separate test would // be needed to re-confirm the URL-parsing path. The behavior under // test is the source.js depth-0 filter check, which only reads // window.app.projectFilter. await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('#addDirectoryBtn', { timeout: 15000 }); // Three top-level projects under one root; only A and B should be scanned. await page.evaluate(() => { window.__setMockDirectoryTree('combined-root', { 'Project-A': { '2025-01-15_123456-EM-TRN-0001 (IFC) - First': { '123456-EL-SPC-0001_A (IFC) - SpecA.pdf': '%PDF', }, }, 'Project-B': { '2025-02-10_789012-EM-TRN-0001 (IFC) - Second': { '789012-EL-SPC-0002_A (IFC) - SpecB.pdf': '%PDF', }, }, 'Project-C': { '2025-03-01_345678-EM-TRN-0001 (IFC) - Third': { '345678-EL-SPC-0003_A (IFC) - SpecC.pdf': '%PDF', }, }, }); }); // Set projectFilter to {A, B}, mimicking what url-state.restore() // would do for a URL like archive.html?projects=Project-A,Project-B. await page.evaluate(() => { window.app.projectFilter = new Set(['Project-A', 'Project-B']); }); await page.locator('#addDirectoryBtn').click(); await page.waitForTimeout(2000); // Surface all files into the table. await page.evaluate(() => { const cb = document.getElementById('selectAllGroupingCheckbox'); if (cb && !cb.checked) cb.click(); }); await page.waitForTimeout(500); const tableText = await page.locator('#filesTableBody').textContent(); expect(tableText).toContain('SpecA'); expect(tableText).toContain('SpecB'); expect(tableText).not.toContain('SpecC'); }); test('parser module uses shared zddc helpers (not its own wrappers)', async ({ page }) => { await page.goto(`file://${HTML_PATH}`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('#appContainer', { timeout: 15000 }); const probe = await page.evaluate(() => ({ // archive's parser module should NOT re-export wrappers we removed hasParseFileNameWrapper: typeof window.app.modules.parser?.parseFileName === 'function', hasParseRevisionWrapper: typeof window.app.modules.parser?.parseRevision === 'function', // but should still expose archive-specific helpers hasIsTransmittalFolder: typeof window.app.modules.parser?.isTransmittalFolder === 'function', hasGroupFiles: typeof window.app.modules.parser?.groupFilesByTrackingNumber === 'function', // and isTransmittalFolder should agree with shared zddc.parseFolder validFolder: window.app.modules.parser.isTransmittalFolder('2025-10-31_123456-EM-SUB-0001 (IFR) - General Arrangement'), invalidFolder: window.app.modules.parser.isTransmittalFolder('not-a-zddc-folder'), })); expect(probe.hasParseFileNameWrapper).toBe(false); expect(probe.hasParseRevisionWrapper).toBe(false); expect(probe.hasIsTransmittalFolder).toBe(true); expect(probe.hasGroupFiles).toBe(true); expect(probe.validFolder).toBe(true); expect(probe.invalidFolder).toBe(false); }); });