From 8da1484ebf6959d13ebd12f2518aff413ae4b682 Mon Sep 17 00:00:00 2001 From: Test Date: Sat, 7 Mar 2026 00:35:35 +0100 Subject: [PATCH] test: Playwright smoke test for push error UX Expose mockHandlers on window for Playwright overrides. Test that rejected push shows "Pull first" message, auth error shows "authentication error", and success shows "Committed and pushed". Co-Authored-By: Claude Opus 4.6 --- src/App.tsx | 4 +- src/mock-tauri/index.ts | 3 +- tests/smoke/better-sync-error-ux.spec.ts | 71 ++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/smoke/better-sync-error-ux.spec.ts diff --git a/src/App.tsx b/src/App.tsx index 4335583a..6d872b4a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -48,10 +48,12 @@ import { filterEntries } from './utils/noteListHelpers' import { openLocalFile } from './utils/url' import './App.css' -// Type declaration for mock content storage +// Type declarations for mock content storage and test overrides declare global { interface Window { __mockContent?: Record + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mock handler map for Playwright test overrides + __mockHandlers?: Record any> } } diff --git a/src/mock-tauri/index.ts b/src/mock-tauri/index.ts index c0d4aa3e..0ab471c5 100644 --- a/src/mock-tauri/index.ts +++ b/src/mock-tauri/index.ts @@ -14,9 +14,10 @@ export function isTauri(): boolean { return typeof window !== 'undefined' && ('__TAURI__' in window || '__TAURI_INTERNALS__' in window) } -// Initialize window.__mockContent for browser testing +// Initialize window globals for browser testing and Playwright overrides if (typeof window !== 'undefined') { window.__mockContent = MOCK_CONTENT + window.__mockHandlers = mockHandlers } export async function mockInvoke(cmd: string, args?: Record): Promise { diff --git a/tests/smoke/better-sync-error-ux.spec.ts b/tests/smoke/better-sync-error-ux.spec.ts new file mode 100644 index 00000000..32f63c8a --- /dev/null +++ b/tests/smoke/better-sync-error-ux.spec.ts @@ -0,0 +1,71 @@ +import { test, expect } from '@playwright/test' +import { openCommandPalette, executeCommand } from './helpers' + +test.describe('Sync error UX — actionable push error messages', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/') + await page.waitForLoadState('networkidle') + }) + + test('rejected push shows actionable "Pull first" message in toast', async ({ page }) => { + // Override git_push mock to return a rejected result + await page.evaluate(() => { + window.__mockHandlers!.git_push = () => ({ + status: 'rejected', + message: 'Push rejected: remote has new commits. Pull first, then push.', + }) + }) + + // Open commit dialog via command palette + await openCommandPalette(page) + await executeCommand(page, 'Commit & Push') + + // Wait for the CommitDialog textarea + const textarea = page.locator('textarea[placeholder="Commit message..."]') + await textarea.waitFor({ timeout: 5000 }) + await textarea.fill('test commit') + + // Click the "Commit & Push" button in the dialog + const commitButton = page.getByRole('button', { name: 'Commit & Push' }) + await commitButton.click() + + // Verify the toast shows the actionable rejection message + const toast = page.locator('.fixed.bottom-8') + await expect(toast).toContainText('Pull first', { timeout: 5000 }) + }) + + test('auth error push shows authentication message in toast', async ({ page }) => { + await page.evaluate(() => { + window.__mockHandlers!.git_push = () => ({ + status: 'auth_error', + message: 'Push failed: authentication error. Check your credentials.', + }) + }) + + await openCommandPalette(page) + await executeCommand(page, 'Commit & Push') + + const textarea = page.locator('textarea[placeholder="Commit message..."]') + await textarea.waitFor({ timeout: 5000 }) + await textarea.fill('test commit') + + await page.getByRole('button', { name: 'Commit & Push' }).click() + + const toast = page.locator('.fixed.bottom-8') + await expect(toast).toContainText('authentication error', { timeout: 5000 }) + }) + + test('successful push shows normal success message', async ({ page }) => { + await openCommandPalette(page) + await executeCommand(page, 'Commit & Push') + + const textarea = page.locator('textarea[placeholder="Commit message..."]') + await textarea.waitFor({ timeout: 5000 }) + await textarea.fill('test commit') + + await page.getByRole('button', { name: 'Commit & Push' }).click() + + const toast = page.locator('.fixed.bottom-8') + await expect(toast).toContainText('Committed and pushed', { timeout: 5000 }) + }) +})