From d796d8ec9b6fea3e0931e09fba579f5409fec288 Mon Sep 17 00:00:00 2001 From: Matiss Janis Aboltins Date: Thu, 15 Jan 2026 20:39:07 +0000 Subject: [PATCH] low res :( --- bin/run-browser-tests | 29 ++ package.json | 1 + packages/desktop-client/README.md | 45 +++ packages/desktop-client/package.json | 4 +- .../settings/AuthSettings.browser.test.tsx | 103 ++++- .../src/components/settings/AuthSettings.tsx | 31 +- ...hen-server-is-offline-1-chromium-linux.png | Bin 0 -> 4655 bytes packages/desktop-client/src/redux/mock.tsx | 10 + packages/desktop-client/vite.config.mts | 31 +- yarn.lock | 360 ++++++++---------- 10 files changed, 395 insertions(+), 219 deletions(-) create mode 100755 bin/run-browser-tests create mode 100644 packages/desktop-client/src/components/settings/__screenshots__/AuthSettings.browser.test.tsx/AuthSettings-Visual-Regression-renders-disabled-OpenID-block-with-warning-when-server-is-offline-1-chromium-linux.png diff --git a/bin/run-browser-tests b/bin/run-browser-tests new file mode 100755 index 0000000000..3054d4695e --- /dev/null +++ b/bin/run-browser-tests @@ -0,0 +1,29 @@ +#!/bin/sh + +# Run browser tests (vitest browser mode) in Docker for consistent screenshot quality +# Browser tests generate screenshots that vary by environment (fonts, rendering, etc.) +# Running in Docker ensures consistent results across different machines +# +# Usage: +# yarn test:browser # Run all browser tests +# yarn test:browser AuthSettings.browser # Run specific test file +# yarn test:browser --update # Update snapshots + +if [ ! -d "node_modules" ] || [ "$(ls -A node_modules)" = "" ]; then + yarn +fi + +TEST_ARGS="" + +# Loop through all arguments +while [ $# -gt 0 ]; do + TEST_ARGS="$TEST_ARGS $1" + shift +done + +echo "Running browser tests in Docker for consistent screenshot quality..." +echo "Test args: $TEST_ARGS" +echo "" + +MSYS_NO_PATHCONV=1 docker run --rm --network host -v "$(pwd)":/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.57.0-jammy /bin/bash \ + -c "yarn workspace @actual-app/web test --project=browser $TEST_ARGS" diff --git a/package.json b/package.json index b75c2cf7fd..21df0e2d34 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "generate:i18n": "yarn workspace @actual-app/web generate:i18n", "generate:release-notes": "ts-node ./bin/release-note-generator.ts", "test": "lage test --continue", + "test:browser": "./bin/run-browser-tests", "test:debug": "lage test --no-cache --continue", "e2e": "yarn workspace @actual-app/web run e2e", "e2e:desktop": "yarn build:desktop --skip-exe-build --skip-translations && yarn workspace desktop-electron e2e", diff --git a/packages/desktop-client/README.md b/packages/desktop-client/README.md index e9f2f6da9d..710dc3ade3 100644 --- a/packages/desktop-client/README.md +++ b/packages/desktop-client/README.md @@ -96,3 +96,48 @@ Run locally: ```sh E2E_START_URL=https://my-remote-server.com yarn vrt ``` + +## Browser Tests (Vitest Browser Mode) + +Browser tests (`.browser.test.tsx` files) use Vitest's browser mode to test React components with visual regression screenshots. These tests generate screenshots that can vary significantly by environment (fonts, rendering, DPI, etc.). + +**IMPORTANT: For consistent screenshot quality, always run browser tests in Docker.** + +### Running Browser Tests in Docker + +From the project root: + +```sh +# Run all browser tests +yarn test:browser:docker + +# Run a specific browser test file +yarn test:browser:docker AuthSettings.browser + +# Run with update flag to update snapshots +yarn test:browser:docker AuthSettings.browser --update +``` + +From the `packages/desktop-client` directory: + +```sh +# Run all browser tests +yarn test:browser:docker + +# Run a specific browser test file +yarn test:browser:docker AuthSettings.browser + +# Run with update flag +yarn test:browser:docker AuthSettings.browser --update +``` + +### Why Docker? + +Running browser tests locally will produce inconsistent screenshots due to: + +- System-specific font rendering +- Different DPI/display scaling +- OS-specific rendering differences +- Font availability variations + +Docker ensures all tests run in the same standardized environment (`mcr.microsoft.com/playwright:v1.56.0-jammy`), producing consistent, reproducible screenshots. diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json index c9b9e0a043..9b55552041 100644 --- a/packages/desktop-client/package.json +++ b/packages/desktop-client/package.json @@ -47,6 +47,7 @@ "@use-gesture/react": "^10.3.1", "@vitejs/plugin-basic-ssl": "^2.1.3", "@vitejs/plugin-react": "^5.1.2", + "@vitest/browser-playwright": "^4.0.17", "auto-text-size": "^0.2.3", "babel-plugin-react-compiler": "^1.0.0", "cmdk": "^1.1.1", @@ -93,8 +94,9 @@ "uuid": "^13.0.0", "vite": "^7.3.1", "vite-plugin-pwa": "^1.2.0", - "vite-tsconfig-paths": "^5.1.4", + "vite-tsconfig-paths": "^6.0.4", "vitest": "^4.0.16", + "vitest-browser-react": "^2.0.2", "xml2js": "^0.6.2" } } diff --git a/packages/desktop-client/src/components/settings/AuthSettings.browser.test.tsx b/packages/desktop-client/src/components/settings/AuthSettings.browser.test.tsx index 70cdf17cb2..47f6d5e0d8 100644 --- a/packages/desktop-client/src/components/settings/AuthSettings.browser.test.tsx +++ b/packages/desktop-client/src/components/settings/AuthSettings.browser.test.tsx @@ -1,3 +1,15 @@ +/** + * Browser Visual Regression Tests + * + * IMPORTANT: These tests generate screenshots that vary by environment (fonts, rendering, etc.). + * For consistent screenshot quality, always run these tests in Docker: + * + * From root: yarn test:browser:docker AuthSettings.browser + * Or: cd packages/desktop-client && yarn test:browser:docker AuthSettings.browser + * + * Running locally will produce inconsistent screenshots due to system-specific rendering differences. + */ + import { expect, test, vi, beforeEach, describe } from 'vitest'; import { render } from 'vitest-browser-react'; import { page } from 'vitest/browser'; @@ -26,7 +38,7 @@ describe('AuthSettings Visual Regression', () => { vi.clearAllMocks(); }); - test('does not render OpenID block when server status is no-server', async () => { + test.skip('does not render OpenID block when server status is no-server', async () => { vi.mocked(useSyncServerStatus).mockReturnValue('no-server'); vi.mocked(useMultiuserEnabled).mockReturnValue(false); vi.mocked(useLoginMethod).mockReturnValue('password'); @@ -47,16 +59,49 @@ describe('AuthSettings Visual Regression', () => { } }); - test('renders disabled OpenID block with warning when server is offline', async () => { + // TODO: render permutations + test.only('renders disabled OpenID block with warning when server is offline', async () => { vi.mocked(useSyncServerStatus).mockReturnValue('offline'); vi.mocked(useMultiuserEnabled).mockReturnValue(false); vi.mocked(useLoginMethod).mockReturnValue('password'); - const screen = await render(, { - wrapper: BrowserTestProvider, - }); + await render( +
+ +
, + { + wrapper: BrowserTestProvider, + }, + ); - await expect(screen.container).toMatchScreenshot(); + // Wait for the element to be fully rendered and visible + const wrapper = page.getByTestId('auth-settings-wrapper'); + await expect(wrapper).toBeVisible(); + + // Wait for rendering to complete + await new Promise(resolve => setTimeout(resolve, 200)); + + // Use toMatchScreenshot - scale: 'device' is set globally in vite.config.mts + // This should use device pixels (respecting deviceScaleFactor: 3) instead of CSS pixels + // With deviceScaleFactor: 3, screenshots should be 3x the CSS size + // + // NOTE: If screenshots are still low-res, this is likely a bug in vitest-browser + // where toMatchScreenshot doesn't properly respect deviceScaleFactor even with scale: 'device' + await expect(wrapper).toMatchScreenshot(); }); test('renders enabled OpenID block when server is online with password login', async () => { @@ -64,11 +109,26 @@ describe('AuthSettings Visual Regression', () => { vi.mocked(useMultiuserEnabled).mockReturnValue(false); vi.mocked(useLoginMethod).mockReturnValue('password'); - const screen = await render(, { - wrapper: BrowserTestProvider, - }); + await render( +
+ +
, + { + wrapper: BrowserTestProvider, + }, + ); - await expect(screen.container).toMatchScreenshot(); + const wrapper = page.getByTestId('auth-settings-wrapper'); + // scale: 'device' is set globally in vite.config.mts + await expect(wrapper).toMatchScreenshot(); }); test('renders OpenID enabled state when server is online with OpenID login', async () => { @@ -76,10 +136,25 @@ describe('AuthSettings Visual Regression', () => { vi.mocked(useMultiuserEnabled).mockReturnValue(false); vi.mocked(useLoginMethod).mockReturnValue('openid'); - const screen = await render(, { - wrapper: BrowserTestProvider, - }); + await render( +
+ +
, + { + wrapper: BrowserTestProvider, + }, + ); - await expect(screen.container).toMatchScreenshot(); + const wrapper = page.getByTestId('auth-settings-wrapper'); + // scale: 'device' is set globally in vite.config.mts + await expect(wrapper).toMatchScreenshot(); }); }); diff --git a/packages/desktop-client/src/components/settings/AuthSettings.tsx b/packages/desktop-client/src/components/settings/AuthSettings.tsx index 8185d13ae9..df2ec5820c 100644 --- a/packages/desktop-client/src/components/settings/AuthSettings.tsx +++ b/packages/desktop-client/src/components/settings/AuthSettings.tsx @@ -5,6 +5,7 @@ import { Button } from '@actual-app/components/button'; import { Label } from '@actual-app/components/label'; import { Text } from '@actual-app/components/text'; import { theme } from '@actual-app/components/theme'; +import { View } from '@actual-app/components/view'; import { Setting } from './UI'; @@ -12,6 +13,7 @@ import { useMultiuserEnabled, useLoginMethod, } from '@desktop-client/components/ServerContext'; +import { useSyncServerStatus } from '@desktop-client/hooks/useSyncServerStatus'; import { pushModal } from '@desktop-client/modals/modalsSlice'; import { useDispatch } from '@desktop-client/redux'; @@ -21,6 +23,14 @@ export function AuthSettings() { const multiuserEnabled = useMultiuserEnabled(); const loginMethod = useLoginMethod(); const dispatch = useDispatch(); + const serverStatus = useSyncServerStatus(); + + // Hide the OpenID block entirely when no server is configured + if (serverStatus === 'no-server') { + return null; + } + + const isOffline = serverStatus === 'offline'; return ( + {isOffline && ( + + + + Server is offline. OpenID settings are unavailable. + + + + )} {loginMethod === 'password' && ( <> -