diff --git a/.gitignore b/.gitignore index 1691153c..ce0b1bea 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,11 @@ node_modules #internal/native/include #internal/native/lib -ui/reports \ No newline at end of file +ui/reports + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/ui/.gitignore b/ui/.gitignore index a547bf36..2e5968d2 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -22,3 +22,10 @@ dist-ssr *.njsproj *.sln *.sw? + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/ui/package-lock.json b/ui/package-lock.json index 437b0c93..ab5d4708 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -50,6 +50,7 @@ "@inlang/plugin-m-function-matcher": "^2.1.0", "@inlang/plugin-message-format": "^4.0.0", "@inlang/sdk": "^2.4.9", + "@playwright/test": "^1.56.1", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", @@ -1276,6 +1277,22 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@react-aria/focus": { "version": "3.21.2", "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz", @@ -6188,6 +6205,53 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", + "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.56.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", + "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", diff --git a/ui/package.json b/ui/package.json index 0f4d7f64..51981118 100644 --- a/ui/package.json +++ b/ui/package.json @@ -95,6 +95,7 @@ "tailwindcss": "^4.1.17", "typescript": "^5.9.3", "vite": "^7.1.12", - "vite-tsconfig-paths": "^5.1.4" + "vite-tsconfig-paths": "^5.1.4", + "@playwright/test": "^1.56.1" } } diff --git a/ui/playwright.config.ts b/ui/playwright.config.ts new file mode 100644 index 00000000..1f1b4a61 --- /dev/null +++ b/ui/playwright.config.ts @@ -0,0 +1,82 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: 1, + timeout: 10 * 60 * 1000, // 10 minutes + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on', + video: 'on', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + // Headless must be disabled otherwise WebRTC will not work properly + use: { ...devices['Desktop Chrome'], headless: false }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://localhost:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/ui/src/components/StatusCards.tsx b/ui/src/components/StatusCards.tsx index 8cbe9f38..3404b983 100644 --- a/ui/src/components/StatusCards.tsx +++ b/ui/src/components/StatusCards.tsx @@ -26,7 +26,7 @@ export default function StatusCard({ ) : null}