diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml index e377d15d..b2dfe762 100644 --- a/.github/workflows/on-push.yml +++ b/.github/workflows/on-push.yml @@ -57,7 +57,7 @@ jobs: run: | yarn lint - test: + unit-test: runs-on: ubuntu-latest steps: - name: Checkout ${{ github.ref }} @@ -75,8 +75,38 @@ jobs: - name: Unit test run: | - yarn test + yarn test:unit + visual-test: + runs-on: ubuntu-latest + steps: + - name: Checkout ${{ github.ref }} + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ${{ env.NODE_VERSION_FILE }} + cache: ${{ env.PACKAGE_MANGER }} + cache-dependency-path: ${{ env.PACKAGE_MANGER_LOCK_FILE }} + + - name: Install dependencies + run: yarn install --frozen-lockfile --immutable + + - name: Run Playwright tests + run: yarn test:visual + + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: test-results + path: | + test-results/ + !**/*.zip + retention-days: 3 + + - name: Publish Test Summary Results + run: npx github-actions-ctrf playwright-report/test-results.json compile: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 5a7ee0c5..b3296a47 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,12 @@ dist dist-ssr *.local +# tests +test-results +playwright-report +/playwright/.cache/ + + # Editor directories and files .vscode/* !.vscode/extensions.json @@ -23,3 +29,4 @@ dist-ssr *.njsproj *.sln *.sw? +/blob-report/ diff --git a/package.json b/package.json index c35a2419..6003d5e5 100644 --- a/package.json +++ b/package.json @@ -11,16 +11,20 @@ "dev": "vite", "build": "tsc && vite build", "lint": "eslint --report-unused-disable-directives --max-warnings 0", - "test": "vitest run", + "test:unit": "vitest --config ./vitest.config.unit.ts", + "test:visual": "playwright test -c playwright.config.ts", + "test:visual:update": "playwright test --update-snapshots", "coverage": "vitest run --coverage", "preview": "vite preview", "prettier:check": "prettier --check 'src/**/*.{ts,tsx,js,jsx,json,css}'", - "prettier:format": "prettier --write 'src/**/*.{ts,tsx,js,jsx,json,css}'" + "prettier:format": "prettier --write 'src/**/*.{ts,tsx,js,jsx,json,css}'", + "postinstall": "npx playwright install --with-deps" }, "dependencies": { "@4chain-ag/react-configuration": "^1.0.4", "@bsv/sdk": "^1.0.37", "@bsv/spv-wallet-js-client": "^1.0.0-beta.20", + "@estruyf/github-actions-reporter": "^1.9.2", "@heroicons/react": "^2.1.3", "@hookform/resolvers": "^3.9.0", "@radix-ui/react-accordion": "^1.1.2", @@ -58,6 +62,7 @@ "devDependencies": { "@eslint/compat": "^1.0.1", "@eslint/js": "^9.3.0", + "@playwright/test": "^1.47.2", "@tanstack/eslint-plugin-query": "^5.35.6", "@tanstack/router-devtools": "^1.33.4", "@tanstack/router-vite-plugin": "^1.32.17", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..a8ca0b1b --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,64 @@ +import { defineConfig, devices } from '@playwright/test'; +import { GitHubActionOptions } from '@estruyf/github-actions-reporter'; + +/** + * 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: './src/__tests__', + snapshotPathTemplate: '{testDir}/visual/{testName}/{arg}{ext}', + expect: { + // Maximum time expect() should wait for the condition to be met. + timeout: 5000, + }, + /* 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 ? 1 : 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: [ + !process.env.CI + ? ['html', { outputFolder: 'playwright-report/html' }] + : [ + '@estruyf/github-actions-reporter', + { + title: 'Visual tests report', + useDetails: true, + }, + ], + ], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'yarn run dev', + port: 5173, + reuseExistingServer: !process.env.CI, + timeout: 60 * 1000, + }, + use: { + baseURL: 'http://localhost:5173', + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, +}); diff --git a/src/__tests__/visual/login-page-login-form/login-page-login-form-1.png b/src/__tests__/visual/login-page-login-form/login-page-login-form-1.png new file mode 100644 index 00000000..8138ce9e Binary files /dev/null and b/src/__tests__/visual/login-page-login-form/login-page-login-form-1.png differ diff --git a/src/__tests__/visual/login-page.visual.test.ts b/src/__tests__/visual/login-page.visual.test.ts new file mode 100644 index 00000000..e3341dad --- /dev/null +++ b/src/__tests__/visual/login-page.visual.test.ts @@ -0,0 +1,10 @@ +import { expect, test } from '@playwright/test'; + +test.describe('login page', () => { + test('login form', async ({ page }) => { + await page.goto('/'); + await page.waitForSelector('form#login-form'); + + await expect(page).toHaveScreenshot(); + }); +}); diff --git a/src/routes/login.tsx b/src/routes/login.tsx index a7369fa6..0328e39a 100644 --- a/src/routes/login.tsx +++ b/src/routes/login.tsx @@ -157,7 +157,7 @@ export function LoginForm() {

SPV Wallet Admin

- + Login diff --git a/src/utils/getAddress.test.ts b/src/utils/getAddress.unit.test.ts similarity index 100% rename from src/utils/getAddress.test.ts rename to src/utils/getAddress.unit.test.ts diff --git a/vite.config.ts b/vite.config.ts index 03c8d6d0..b36a20ce 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,15 +1,16 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react-swc' -import { TanStackRouterVite } from '@tanstack/router-vite-plugin' -import path from "path"; +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import { TanStackRouterVite } from '@tanstack/router-vite-plugin'; +import path from 'path'; +export const srcAlias = { + '@': path.resolve(__dirname, './src'), +}; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [TanStackRouterVite(), react() ], + plugins: [TanStackRouterVite(), react()], resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - } -}) + alias: srcAlias, + }, +}); diff --git a/vitest.config.unit.ts b/vitest.config.unit.ts new file mode 100644 index 00000000..79d8efac --- /dev/null +++ b/vitest.config.unit.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; +import { srcAlias } from './vite.config'; + +export default defineConfig({ + resolve: { + alias: srcAlias, + }, + test: { + globals: true, + environment: 'node', + include: ['**/*.unit.test.ts'], + }, +}); diff --git a/yarn.lock b/yarn.lock index 39d60c22..2a314b38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,22 @@ resolved "https://registry.yarnpkg.com/@4chain-ag/react-configuration/-/react-configuration-1.0.4.tgz#4fb9dc952fbd0af88d22731ff3a6adbbab2eec7f" integrity sha512-dewTUItm4/xl48a8CQhfYlDa3KDibdorQBQ0WoY84Sw8LjaKC7MTka3iD0K8eOOsqLx2jvVsJ7Ie91krphmunA== +"@actions/core@^1.10.0": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a" + integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g== + dependencies: + "@actions/http-client" "^2.0.1" + uuid "^8.3.2" + +"@actions/http-client@^2.0.1": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674" + integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA== + dependencies: + tunnel "^0.0.6" + undici "^5.25.4" + "@alloc/quick-lru@^5.2.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" @@ -654,6 +670,20 @@ resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== +"@estruyf/github-actions-reporter@^1.9.2": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@estruyf/github-actions-reporter/-/github-actions-reporter-1.9.2.tgz#a3d8ee7823469a45cedb77a6ee22cea2abf82d35" + integrity sha512-D2+ePwTjbd4siY7VIWX1dJrE1bU3x9OO+FOj99hqU4zQhVGwMm0ozgNDUzJgHxdRZKtFoGcYWV5YLfZettR38A== + dependencies: + "@actions/core" "^1.10.0" + ansi-to-html "^0.7.2" + marked "^12.0.1" + +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@floating-ui/core@^1.0.0": version "1.6.2" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.2.tgz#d37f3e0ac1f1c756c7de45db13303a266226851a" @@ -781,6 +811,13 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@playwright/test@^1.47.2": + version "1.47.2" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.47.2.tgz#dbe7051336bfc5cc599954214f9111181dbc7475" + integrity sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ== + dependencies: + playwright "1.47.2" + "@radix-ui/number@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.0.1.tgz#644161a3557f46ed38a042acf4a770e826021674" @@ -2019,6 +2056,13 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +ansi-to-html@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.7.2.tgz#a92c149e4184b571eb29a0135ca001a8e2d710cb" + integrity sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g== + dependencies: + entities "^2.2.0" + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -2542,6 +2586,11 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +entities@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: version "1.23.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" @@ -3044,6 +3093,11 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -3715,6 +3769,11 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" +marked@^12.0.1: + version "12.0.2" + resolved "https://registry.yarnpkg.com/marked/-/marked-12.0.2.tgz#b31578fe608b599944c69807b00f18edab84647e" + integrity sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -4074,6 +4133,20 @@ pirates@^4.0.1: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +playwright-core@1.47.2: + version "1.47.2" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.47.2.tgz#7858da9377fa32a08be46ba47d7523dbd9460a4e" + integrity sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ== + +playwright@1.47.2: + version "1.47.2" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.47.2.tgz#155688aa06491ee21fb3e7555b748b525f86eb20" + integrity sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA== + dependencies: + playwright-core "1.47.2" + optionalDependencies: + fsevents "2.3.2" + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -4844,6 +4917,11 @@ tslib@^2.0.0, tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -4924,6 +5002,13 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici@^5.25.4: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + update-browserslist-db@^1.0.13: version "1.0.16" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" @@ -4969,6 +5054,11 @@ util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + vite-node@2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.0.5.tgz#36d909188fc6e3aba3da5fc095b3637d0d18e27b"