Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2a22526
feat: Enhance HVSC library with cache status and action tracing
chrisgleissner Feb 8, 2026
45c2a06
feat: enhance HVSC library state management with extraction tracking …
chrisgleissner Feb 8, 2026
ca65a7e
feat: update images for improved documentation and enhance UX with bl…
chrisgleissner Feb 8, 2026
c1478ef
feat: Enhance SidCard with volume and pan controls, including max and…
chrisgleissner Feb 8, 2026
e22c05a
feat: Improve UX and enhance tests for item selection, playback, and …
chrisgleissner Feb 8, 2026
94320bc
feat: Update file picker identifiers and enhance demo mode dialog han…
chrisgleissner Feb 8, 2026
4aa6596
Add button styling functions and tests for on/off states
chrisgleissner Feb 9, 2026
5cd4606
style: Format buttonStyles function and tests for improved readability
chrisgleissner Feb 9, 2026
08f6fbf
feat: add HVSC base URL override functionality in SettingsPage
chrisgleissner Feb 9, 2026
3674a54
feat: enhance Maestro flows and scripts for improved emulator handlin…
chrisgleissner Feb 9, 2026
4ffce37
feat: add IDs to import options in ItemSelectionDialog for better acc…
chrisgleissner Feb 9, 2026
7a0c313
feat: enhance edge configurations and add HVSC library setup for impr…
chrisgleissner Feb 9, 2026
062155a
feat: enhance user flow in smoke test and update safety warning in Se…
chrisgleissner Feb 9, 2026
a114282
feat: simplify safety warning message in SettingsPage for clarity
chrisgleissner Feb 9, 2026
4963bb0
feat: enhance Maestro configurations and improve tracing setup for be…
chrisgleissner Feb 9, 2026
06fba81
Improve HVSC handling and ensure app continues running when screen lo…
chrisgleissner Feb 9, 2026
70491e7
feat: enhance demo mode dialog handling and update songlengths file path
chrisgleissner Feb 9, 2026
27e7a9b
feat: add error handling tests for hvscFilesystem and hvscArchiveExtr…
chrisgleissner Feb 9, 2026
6878326
feat: implement playlist addition functionality and enhance error han…
chrisgleissner Feb 9, 2026
c0bb15f
refactor: improve test readability and structure across multiple test…
chrisgleissner Feb 9, 2026
f57aa5e
Increased coverage and added license note
chrisgleissner Feb 9, 2026
516e1bf
refactor: clean up whitespace and formatting in test files for consis…
chrisgleissner Feb 9, 2026
314bc15
chore: remove unused add-license.sh script
chrisgleissner Feb 9, 2026
8d8edea
feat: enhance local file browser with folder listing and parent path …
chrisgleissner Feb 9, 2026
26735e2
refactor: improve formatting and consistency in test files
chrisgleissner Feb 9, 2026
29a22e2
feat: add diagnostics activity tracking tests with event emission ver…
chrisgleissner Feb 9, 2026
83e9af5
refactor: update diagnostics activity tests for improved readability …
chrisgleissner Feb 10, 2026
6c89a95
Add unit tests for appSettings, mockConfig, useSonglengths, and volum…
chrisgleissner Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 80 additions & 47 deletions PLANS.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,84 @@
# PLANS.md

This file is the authoritative execution contract for the Home page SID compact redesign.
This file is the authoritative execution contract for the fixes requested in the prompt.
Strict loop: plan -> execute -> verify. A task is checked only after implementation and verification.

## 1. Objective
- [ ] Confirm current Home SID layout and drive toggle styling references.
- [ ] Define compact two-row SID layout targets and verify space reduction goal.
- [ ] Enumerate data sources needed for SID enablement, address display, volume, and pan.

## 2. Core Design Decision (Mandatory)
- [ ] Implement two-row compact SID layout for all SID sockets (no single-row, no collapsible sections).
- [ ] Ensure layout is static (no dynamic height changes).

## 3. Per-SID Layout (Authoritative Spec)
- [ ] Row 1: render label, base address (hex-only, monospaced), right-aligned ON/OFF toggle matching drive styling.
- [ ] Row 2: render labeled volume and pan sliders with center detent and touch-safe sizes.
- [ ] Ensure sliders are disabled (visible) when SID is OFF.
- [ ] Provide live value feedback during drag only (tooltip/value bubble).

## 4. Strict Exclusions From Home Page
- [ ] Remove editable base address controls from Home SID section.
- [ ] Remove model/filter/register/persistent numeric or dropdown SID controls from Home.

## 5. Base Address Handling (Critical Safety Rule)
- [ ] Always display base address as hex-only text and keep it read-only.
- [ ] Confirm Home SID rows do not add long-press navigation (optional behavior not implemented).

## 6. Error and Diagnostic Behavior
- [ ] Detect enabled-but-silent SID condition and apply non-destructive visual indicator on base address.

## 7. Accessibility and Ergonomics (Non-Negotiable)
- [ ] Enforce >= 48 dp touch targets and sufficient contrast.
- [ ] Ensure disabled state uses opacity + desaturation, not color alone.
- [ ] Avoid relying on color-only state indicators.

## 8. Technical Constraints
- [ ] Use CSS Grid/Flexbox only; no expand/collapse behavior.
- [ ] Keep layout static in Capacitor and avoid dynamic height on interaction.

## 9. Verification Requirements
- [ ] Verify phone-sized viewport layout and one-hand slider usability.
- [ ] Verify center detent is soft and does not block any supported values.
- [ ] Verify live value feedback appears only during slider interaction.
- [ ] Verify SID ON/OFF toggle matches Drive A/B behavior and base address remains visible.
- [ ] Ensure no regressions to Drive, Play, or Config pages.

## 10. Completion Criteria
- [ ] All PLANS.md tasks checked off.
- [ ] Home SID section matches the two-row layout and space reduction goals.
- [ ] All tests pass locally (unit + integration/E2E) and CI is green.
## 1. Global Button Interaction Fix (App-wide)
- [ ] Locate the shared button component(s), theme styles, and focus handling paths used across Home/Play/Choose File.
- [ ] Implement `StatelessButton` defaults with immediate focus/selection clearing after click.
- [ ] Implement `StatefulButton` opt-in that binds illumination strictly to explicit state variables.
- [ ] Audit and update Home Machine/Config buttons, Play Previous/Next, and Choose File to stateless.
- [ ] Bind Play (and Pause if applicable) to stateful illumination only while real state is active.
- [ ] Add emulator tests for stateless reset and stateful illumination behavior.

## 2. Homepage SID Group - Type Display (Minimal, Consistent)
- [ ] Identify Home SID header row layout and data sources for SID type, address, and enablement.
- [ ] Insert SID type between name and base address with aligned columns across rows.
- [ ] Ensure SID socket types are read-only and non-interactive.
- [ ] Add UltiSID profile dropdown for Alt SIDs only, exposing only the sound-defining profile.
- [ ] Preserve existing header + sliders structure and avoid advanced UltiSID settings here.
- [ ] Add emulator tests for layout consistency and editable vs read-only behavior.

## 3. RAM Dump (Save RAM) Fix (Real C64U Required)
### 3.1 RAM Dump Robustness and Correctness
- [ ] Reproduce RAM dump behavior against real c64u with small, large, and full ranges.
- [ ] Capture request/response details, chunk sizes, and failure modes (short reads, zero-length).
- [ ] Update RAM dump logic for empirically correct chunk sizes, retries, and fail-fast diagnostics.
- [ ] Add event traces for chunk start, expected length, actual length, and completion/failure.
- [ ] Implement recovery behavior: reset/reboot, re-verify liveness, and retry safely.
- [ ] Clean up any temporary device state or files created during experiments.
- [ ] Add integration tests for chunk size, monotonic progression, and full coverage.

### 3.2 Strengthened C64 Availability and Liveness Check
- [ ] Implement two-level liveness check (jiffy clock and raster) with 50ms wait for clock advance.
- [ ] Define decision logic for healthy vs IRQ-disabled vs wedged states.
- [ ] Replace REST-only availability checks where correctness matters (RAM dump, post-reset).
- [ ] Add event traces for jiffy/raster samples and decision outcomes.
- [ ] Add integration tests for liveness outcomes and failure conditions.

## 4. SID Slider Stability Fix (Homepage)
- [ ] Identify current slider state flow and async REST update handling.
- [ ] Decouple UI slider state from async updates to prevent value snap-back.
- [ ] Ensure full value range remains reachable after release.
- [ ] Add emulator tests for drag-release stability.
- [ ] Add unit tests for state sequencing and async update logic.

## 5. LED Group Rework (Homepage)
- [ ] Identify LED group UI structure and navigation triggers.
- [ ] Remove navigation; keep all LED edits on Home.
- [ ] Match layout/interaction model to Video/Analog/Digital/HDMI Scanlines.
- [ ] Add inline dropdowns for each LED attribute and remove duplicate tint display.
- [ ] Add emulator tests for inline editing and no navigation.

## 6. HVSC Download/Ingestion/Indexing Rework
- [ ] Map current HVSC states and crash point for Ingest action.
- [ ] Introduce explicit domain states/events: DOWNLOAD, EXTRACT, INDEX, READY, FAILED.
- [ ] Refactor UI to strictly reflect state, collapse after completion, and support reset/retry.
- [ ] Fix crash on Ingest and add event trace ordering assertions.
- [ ] Add emulator E2E test for HVSC ingestion and storage snapshot assertions.

## 7. Playback Song Length Handling (Local vs C64U)
- [ ] Identify playback strategy selection points and current endpoint usage.
- [ ] Implement Local source: always POST with .ssl.
- [ ] Implement C64U source: FTP fetch + .ssl + POST when possible, else PUT.
- [ ] Add integration tests for endpoint selection and event trace assertions.

## 8. Add Items Interstitial Regression Fix
- [ ] Locate dialog state initialization and stale state source.
- [ ] Reset dialog state on open and restrict options to exactly two entries.
- [ ] Add emulator test asserting only C64U import and Local import options appear.

## 9. Play Page Volume Slider + Mute Rework (Real C64U Required)
- [ ] Refactor to single authoritative volume/mute state machine.
- [ ] Ensure slider updates volume reliably and UI reflects true mute state.
- [ ] Validate behavior against real c64u via API and audible verification.
- [ ] Add emulator tests and unit tests for volume/mute state machine.
- [ ] Add event trace assertions for volume/mute command flow.

## 10. Testing & CI Requirements (Mandatory)
- [ ] Add emulator-based tests covering all major fixes and regressions.
- [ ] Ensure event traces are primary oracle where applicable.
- [ ] Run unit, emulator, and Playwright tests as required.
- [ ] Run full build and confirm all checks pass locally.
- [ ] Verify CI status after push and address any failures.

Binary file modified doc/img/app/disks/01-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/disks/collection/01-view-all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/disks/sections/01-drives.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/disks/sections/02-disks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/00-overview-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/01-overview-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/interactions/01-toggle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/interactions/02-dropdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/interactions/03-input.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/01-system-info.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/02-machine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/03-quick-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/04-drives.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/05-printers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/06-sid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/07-streams.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sections/08-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/home/sid/01-reset-post-silence.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/01-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/import/01-import-interstitial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/import/02-c64u-file-picker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/import/03-local-file-picker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/playlist/01-view-all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/sections/01-playback-controls.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/sections/02-playlist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/img/app/play/sections/03-hvsc-library.png
Binary file modified doc/img/app/settings/01-overview.png
Binary file modified doc/img/app/settings/sections/01-appearance.png
Binary file modified doc/img/app/settings/sections/02-connection.png
Binary file modified doc/img/app/settings/sections/03-diagnostics.png
Binary file modified doc/img/app/settings/sections/08-about.png
49 changes: 49 additions & 0 deletions playwright/homeInteractivity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,53 @@ test.describe('Home interactions', () => {
await expect(page.getByTestId('home-sid-entry-socket1')).toContainText('Volume');
await expect(page.getByTestId('home-sid-entry-ultiSid1')).toContainText('Pan');
});

test('SID type column renders and LED controls stay inline', async ({ page }: { page: Page }) => {
await page.goto('/');
await waitForConnected(page);

const socketType = page.getByTestId('home-sid-type-socket1');
await expect(socketType).toBeVisible();
const socketTag = await socketType.evaluate((el) => el.tagName);
expect(socketTag).toBe('SPAN');

const ultiType = page.getByTestId('home-sid-type-ultiSid1');
await expect(ultiType).toBeVisible();
const ultiTag = await ultiType.evaluate((el) => el.tagName);
expect(ultiTag).toBe('BUTTON');

const ledControls = [
'home-led-mode',
'home-led-color',
'home-led-intensity',
'home-led-sid-select',
'home-led-tint',
];

for (const control of ledControls) {
await expect(page.getByTestId(control)).toBeVisible();
}

const beforePath = await page.evaluate(() => window.location.pathname);
await page.getByTestId('home-led-mode').click();
await page.keyboard.press('Escape');
const afterPath = await page.evaluate(() => window.location.pathname);
expect(afterPath).toBe(beforePath);
});

test('stateless actions clear focus after click', async ({ page }: { page: Page }) => {
await page.goto('/');
await waitForConnected(page);

const action = page.getByTestId('home-config-save-app');
await action.scrollIntoViewIfNeeded();
await action.click();

const activeTestId = await page.evaluate(() => {
const active = document.activeElement as HTMLElement | null;
return active?.dataset?.testid ?? null;
});

expect(activeTestId).not.toBe('home-config-save-app');
});
});
28 changes: 26 additions & 2 deletions playwright/hvsc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createMockC64Server } from '../tests/mocks/mockC64Server';
import { createMockHvscServer } from './mockHvscServer';
import { uiFixtures } from './uiMocks';
import { allowWarnings, assertNoUiIssues, attachStepScreenshot, finalizeEvidence, startStrictUiMonitoring } from './testArtifacts';
import { enableTraceAssertions } from './traceUtils';
import { assertTraceOrder, enableTraceAssertions, getTraces } from './traceUtils';

declare global {
interface Window {
Expand Down Expand Up @@ -45,6 +45,24 @@ test.describe('HVSC Play page', () => {
await attachStepScreenshot(page, testInfo, label);
};

const expectActionTraceSequence = async (page: Page, testInfo: TestInfo, actionName: string) => {
await expect.poll(async () => {
const traces = await getTraces(page);
const actionStart = traces.find((event) =>
event.type === 'action-start'
&& (event.data as { name?: string } | undefined)?.name === actionName,
);
if (!actionStart) return false;
const related = traces.filter((event) => event.correlationId === actionStart.correlationId);
try {
assertTraceOrder(testInfo, related, ['action-start', 'action-end']);
return true;
} catch {
return false;
}
}).toBe(true);
};

const seedBaseConfig = async (page: Page, baseUrl: string, hvscBaseUrl: string) => {
await page.addInitScript(
({ baseUrlArg, hvscUrl, snapshot }: { baseUrlArg: string; hvscUrl: string; snapshot: unknown }) => {
Expand Down Expand Up @@ -529,6 +547,7 @@ test.describe('HVSC Play page', () => {
await snap(page, testInfo, 'play-open');
await page.getByRole('button', { name: 'Download HVSC' }).click();
await expect(page.getByRole('button', { name: '/DEMOS/0-9', exact: true })).toBeVisible();
await expectActionTraceSequence(page, testInfo, 'HvscLibrary.handleHvscInstall');
await snap(page, testInfo, 'hvsc-installed');
});

Expand All @@ -549,6 +568,7 @@ test.describe('HVSC Play page', () => {

await page.getByRole('button', { name: 'Download HVSC' }).click();
await expect(page.getByTestId('hvsc-summary')).toContainText('HVSC downloaded successfully');
await expectActionTraceSequence(page, testInfo, 'HvscLibrary.handleHvscInstall');

const markerExists = await page.evaluate(async ({ baselineVersion }) => {
const fs = (window as any)?.Capacitor?.Plugins?.Filesystem;
Expand All @@ -565,6 +585,7 @@ test.describe('HVSC Play page', () => {

await page.getByRole('button', { name: 'Ingest HVSC' }).click();
await expect(page.getByTestId('hvsc-summary')).toContainText('HVSC downloaded successfully');
await expectActionTraceSequence(page, testInfo, 'HvscLibrary.handleHvscIngest');

await page.getByRole('button', { name: '/DEMOS/0-9', exact: true }).click();
await page.getByRole('button', { name: 'Play folder' }).click();
Expand Down Expand Up @@ -674,11 +695,14 @@ test.describe('HVSC Play page', () => {
test('HVSC ingestion progress updates incrementally', async ({ page }: { page: Page }, testInfo: TestInfo) => {
await installMocks(page, {
installedVersion: 0,
ingestionProgressSteps: [1],
downloadProgressSteps: [512, 2048],
ingestionProgressSteps: [1, 2],
installDelayMs: 400,
});
await page.goto('/play');
await page.getByRole('button', { name: 'Download HVSC' }).click();

await expect(page.getByTestId('hvsc-progress')).toBeVisible();
const files = page.getByTestId('hvsc-extraction-files');
await expect(files).toContainText('Files:');
await expect.poll(async () => files.textContent()).toContain('Files: 1');
Expand Down
33 changes: 26 additions & 7 deletions playwright/itemSelection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ test.describe('Item Selection Dialog UX', () => {
const dialog = page.locator('[role="dialog"]').first();
// Count visible close buttons in top-right area
const headerCloseButtons = await dialog.locator('button[aria-label*="Close"], button[class*="absolute"][class*="right"]').count();

await snap(page, testInfo, 'close-buttons-check');

// Should have exactly one close button
expect(headerCloseButtons).toBeLessThanOrEqual(1);
});
Expand All @@ -169,10 +169,10 @@ test.describe('Item Selection Dialog UX', () => {
// Modal should leave at least 10% margin at top and bottom combined
const heightRatio = dialogBox.height / viewport.height;
expect(heightRatio).toBeLessThan(0.90);

// Modal should not start at viewport top
expect(dialogBox.y).toBeGreaterThan(viewport.height * 0.05);

await snap(page, testInfo, 'modal-sizing-verified');
}
});
Expand All @@ -199,6 +199,25 @@ test.describe('Item Selection Dialog UX', () => {
await snap(page, testInfo, 'c64u-file-picker');
});

test('add items dialog resets to interstitial on reopen', async ({ page }: { page: Page }, testInfo: TestInfo) => {
await page.goto('/play');
await openAddItemsDialog(page);

const dialog = page.getByRole('dialog');
await dialog.getByTestId('import-option-c64u').click();
await waitForFtpIdle(dialog);
await expect(dialog.getByTestId('c64u-file-picker')).toBeVisible();
await snap(page, testInfo, 'c64u-picker-open');

await dialog.getByRole('button', { name: 'Close' }).click();
await expect(page.getByRole('dialog')).toBeHidden();

await openAddItemsDialog(page);
const reopened = page.getByRole('dialog');
await expect(reopened.getByTestId('import-selection-interstitial')).toBeVisible();
await snap(page, testInfo, 'interstitial-reset');
});

test('local file picker is reachable from playlist flow', async ({ page }: { page: Page }, testInfo: TestInfo) => {
await seedLocalSource(page);
await page.goto('/play');
Expand All @@ -218,7 +237,7 @@ test.describe('Item Selection Dialog UX', () => {

await openAddItemsDialog(page);
await page.waitForSelector('[role="dialog"]');

// Select C64 Ultimate source to get file list
const dialog = page.getByRole('dialog');
await clickSourceSelectionButton(dialog, 'C64 Ultimate');
Expand All @@ -233,7 +252,7 @@ test.describe('Item Selection Dialog UX', () => {
});

await snap(page, testInfo, 'scrollable-check');

// Content should either be scrollable or have overflow classes
expect(isScrollable || (await scrollableContent.getAttribute('class'))?.includes('overflow')).toBeTruthy();
});
Expand Down Expand Up @@ -261,7 +280,7 @@ test.describe('Item Selection Dialog UX', () => {
const confirmButton = page.getByTestId('add-items-confirm');
await expect(confirmButton).toBeVisible();
await expect(confirmButton).toBeEnabled();

await snap(page, testInfo, 'confirm-button-visible');
});

Expand Down
12 changes: 8 additions & 4 deletions playwright/screenshots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { registerScreenshotSections, sanitizeSegment } from './screenshotCatalog
import {
installFixedClock,
installListPreviewLimit,
installLocalSourceSeed,
installStableStorage,
seedDiagnosticsTraces,
} from './visualSeeds';
Expand Down Expand Up @@ -234,6 +233,7 @@ test.describe('App screenshots', () => {
await captureLabeledSections(page, testInfo, 'home');

await page.emulateMedia({ colorScheme: 'dark', reducedMotion: 'reduce' });
await page.evaluate(() => window.scrollTo(0, 0));
await captureScreenshot(page, testInfo, 'home/01-overview-dark.png');
await page.emulateMedia({ colorScheme: 'light', reducedMotion: 'reduce' });
});
Expand Down Expand Up @@ -317,7 +317,9 @@ test.describe('App screenshots', () => {
});

test('capture import flow screenshots', { tag: '@screenshots' }, async ({ page }: { page: Page }, testInfo: TestInfo) => {
await installLocalSourceSeed(page);
await page.addInitScript(() => {
(window as Window & { __c64uDisableLocalAutoConfirm?: boolean }).__c64uDisableLocalAutoConfirm = true;
});
await page.goto('/play');

await page.getByRole('button', { name: /Add items|Add more items/i }).click();
Expand All @@ -334,8 +336,10 @@ test.describe('App screenshots', () => {

await page.getByRole('button', { name: /Add items|Add more items/i }).click();
const localDialog = page.getByRole('dialog');
await expect(localDialog.getByTestId('browse-source-seed-local-source')).toBeVisible();
await localDialog.getByTestId('browse-source-seed-local-source').click();
await localDialog.getByTestId('import-option-local').click();
const input = page.locator('input[type="file"][webkitdirectory]');
await expect(input).toHaveCount(1);
await input.setInputFiles([path.resolve('playwright/fixtures/local-play')]);
await expect(localDialog.getByTestId('local-file-picker')).toBeVisible();
await captureScreenshot(page, testInfo, 'play/import/03-local-file-picker.png');
});
Expand Down
8 changes: 8 additions & 0 deletions playwright/sourceSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import type { Locator, Page } from '@playwright/test';

export type SourceSelectionLabel = 'C64 Ultimate' | 'This device';

const getInterstitialTestId = (label: SourceSelectionLabel) =>
(label === 'C64 Ultimate' ? 'import-option-c64u' : 'import-option-local');

export const getSourceSelectionButton = (container: Page | Locator, label: SourceSelectionLabel) =>
container
.getByText(label, { exact: true })
Expand All @@ -13,5 +16,10 @@ export const clickSourceSelectionButton = async (
label: SourceSelectionLabel,
options: { force?: boolean } = {},
) => {
const interstitial = container.getByTestId(getInterstitialTestId(label));
if (await interstitial.count()) {
await interstitial.click({ force: options.force });
return;
}
await getSourceSelectionButton(container, label).click({ force: options.force });
};
Loading
Loading