diff --git a/.gitignore b/.gitignore index cfff0f4..ecec16c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,12 @@ dist-ssr *.pyc # Bundled CUDA runtime DLLs for the CUDA build (copied from the toolkit -# locally; ~600 MB, never committed). See RELEASE_PLAN.md "CUDA release variant". +# locally; ~600 MB, never committed). src-tauri/cuda-redist/ + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/package.json b/package.json index d3d03e3..3697e06 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,11 @@ "zustand": "^5.0.12" }, "devDependencies": { + "@playwright/test": "^1.61.1", "@tailwindcss/vite": "^4.2.2", "@tauri-apps/cli": "^2", "@types/d3-force": "^3.0.10", + "@types/node": "^26.0.1", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.6.0", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..6dfc0d9 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,79 @@ +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: process.env.CI ? 1 : undefined, + /* 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-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + 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/pnpm-lock.yaml b/pnpm-lock.yaml index 5dd8c70..8762e9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,15 +48,21 @@ importers: specifier: ^5.0.12 version: 5.0.12(@types/react@19.2.14)(react@19.2.4) devDependencies: + '@playwright/test': + specifier: ^1.61.1 + version: 1.61.1 '@tailwindcss/vite': specifier: ^4.2.2 - version: 4.2.2(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.2.2(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)) '@tauri-apps/cli': specifier: ^2 version: 2.10.1 '@types/d3-force': specifier: ^3.0.10 version: 3.0.10 + '@types/node': + specifier: ^26.0.1 + version: 26.0.1 '@types/react': specifier: ^19.1.8 version: 19.2.14 @@ -65,7 +71,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.6.0 - version: 4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.7.0(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -74,7 +80,7 @@ importers: version: 5.8.3 vite: specifier: ^7.0.4 - version: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + version: 7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0) website: dependencies: @@ -96,7 +102,7 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.2.2 - version: 4.2.2(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.2.2(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)) '@types/react': specifier: ^19.1.8 version: 19.2.14 @@ -105,7 +111,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.6.0 - version: 4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.7.0(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 @@ -114,10 +120,10 @@ importers: version: 5.8.3 vite: specifier: ^7.0.4 - version: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + version: 7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0) vite-imagetools: specifier: ^10.0.0 - version: 10.0.0(rollup@4.60.1)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)) + version: 10.0.0(rollup@4.60.1)(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)) packages: @@ -538,6 +544,11 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@playwright/test@1.61.1': + resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} + engines: {node: '>=18'} + hasBin: true + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -906,6 +917,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/node@26.0.1': + resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -1010,6 +1024,11 @@ packages: react-dom: optional: true + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1147,6 +1166,16 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.61.1: + resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==} + engines: {node: '>=18'} + hasBin: true + postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} @@ -1208,6 +1237,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -1597,6 +1629,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@playwright/test@1.61.1': + dependencies: + playwright: 1.61.1 + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/pluginutils@5.4.0(rollup@4.60.1)': @@ -1743,12 +1779,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.2(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0))': + '@tailwindcss/vite@4.2.2(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0))': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + vite: 7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0) '@tanstack/react-virtual@3.13.23(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: @@ -1856,6 +1892,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/node@26.0.1': + dependencies: + undici-types: 8.3.0 + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -1864,7 +1904,7 @@ snapshots: dependencies: csstype: 3.2.3 - '@vitejs/plugin-react@4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0))': + '@vitejs/plugin-react@4.7.0(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -1872,7 +1912,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + vite: 7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0) transitivePeerDependencies: - supports-color @@ -1963,6 +2003,9 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -2053,6 +2096,14 @@ snapshots: picomatch@4.0.4: {} + playwright-core@1.61.1: {} + + playwright@1.61.1: + dependencies: + playwright-core: 1.61.1 + optionalDependencies: + fsevents: 2.3.2 + postcss@8.5.8: dependencies: nanoid: 3.3.11 @@ -2151,22 +2202,24 @@ snapshots: typescript@5.8.3: {} + undici-types@8.3.0: {} + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 - vite-imagetools@10.0.0(rollup@4.60.1)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0)): + vite-imagetools@10.0.0(rollup@4.60.1)(vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)): dependencies: '@rollup/pluginutils': 5.4.0(rollup@4.60.1) imagetools-core: 9.1.0 sharp: 0.34.5 - vite: 7.3.1(jiti@2.6.1)(lightningcss@1.32.0) + vite: 7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0) transitivePeerDependencies: - rollup - vite@7.3.1(jiti@2.6.1)(lightningcss@1.32.0): + vite@7.3.1(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -2175,6 +2228,7 @@ snapshots: rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: + '@types/node': 26.0.1 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 diff --git a/tests/example.spec.ts b/tests/example.spec.ts new file mode 100644 index 0000000..54a906a --- /dev/null +++ b/tests/example.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +});