diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml new file mode 100644 index 00000000..4b2bcf0a --- /dev/null +++ b/.github/workflows/web.yml @@ -0,0 +1,45 @@ +name: web-tests + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + unit: + name: Web unit tests + runs-on: ubuntu-latest + defaults: + run: + working-directory: apps/web + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + version: 10 + run_install: false + + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + cache-dependency-path: apps/web/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install + + - name: Run playwright install + run: pnpm exec playwright install + + - name: Run tests + run: pnpm test + diff --git a/apps/web/package.json b/apps/web/package.json index 325e7242..acd1fd29 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -7,6 +7,8 @@ "dev": "vite --host 0.0.0.0", "build": "tsc -b && vite build", "lint": "eslint .", + "test": "vitest run", + "test-cover": "vitest run --coverage", "preview": "vite preview", "prepare": "cd ../.. && husky ./apps/web/.husky", "format": "prettier --write . && git add --all", @@ -46,6 +48,8 @@ "@storybook/react-vite": "^8.6.12", "@storybook/test": "^8.6.12", "@tanstack/router-plugin": "^1.120.2", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", "@types/node": "^22.15.15", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", @@ -58,6 +62,7 @@ "eslint-plugin-storybook": "^0.12.0", "globals": "^16.0.0", "husky": "^9.1.7", + "jsdom": "^26.1.0", "lint-staged": "^15.5.2", "playwright": "^1.52.0", "prettier": "3.5.3", diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 419ac828..9bca726a 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -71,6 +71,12 @@ importers: "@tanstack/router-plugin": specifier: ^1.120.2 version: 1.120.2(@tanstack/react-router@1.120.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))(webpack@5.99.8(esbuild@0.25.4)) + "@testing-library/jest-dom": + specifier: ^6.6.3 + version: 6.6.3 + "@testing-library/react": + specifier: ^16.3.0 + version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) "@types/node": specifier: ^22.15.15 version: 22.15.15 @@ -107,6 +113,9 @@ importers: husky: specifier: ^9.1.7 version: 9.1.7 + jsdom: + specifier: ^26.1.0 + version: 26.1.0 lint-staged: specifier: ^15.5.2 version: 15.5.2 @@ -130,7 +139,7 @@ importers: version: 6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) vitest: specifier: ^3.1.3 - version: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) + version: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) packages: "@adobe/css-tools@4.4.2": @@ -146,6 +155,12 @@ packages: } engines: { node: ">=6.0.0" } + "@asamuzakjp/css-color@3.1.7": + resolution: + { + integrity: sha512-Ok5fYhtwdyJQmU1PpEv6Si7Y+A4cYb8yNM9oiIJC9TzXPMuN9fvdonKJqcnz9TbFqV6bQ8z0giRq0iaOpGZV2g==, + } + "@babel/code-frame@7.27.1": resolution: { @@ -320,6 +335,49 @@ packages: peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + "@csstools/color-helpers@5.0.2": + resolution: + { + integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==, + } + engines: { node: ">=18" } + + "@csstools/css-calc@2.1.3": + resolution: + { + integrity: sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==, + } + engines: { node: ">=18" } + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.4 + "@csstools/css-tokenizer": ^3.0.3 + + "@csstools/css-color-parser@3.0.9": + resolution: + { + integrity: sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==, + } + engines: { node: ">=18" } + peerDependencies: + "@csstools/css-parser-algorithms": ^3.0.4 + "@csstools/css-tokenizer": ^3.0.3 + + "@csstools/css-parser-algorithms@3.0.4": + resolution: + { + integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==, + } + engines: { node: ">=18" } + peerDependencies: + "@csstools/css-tokenizer": ^3.0.3 + + "@csstools/css-tokenizer@3.0.3": + resolution: + { + integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==, + } + engines: { node: ">=18" } + "@esbuild/aix-ppc64@0.25.4": resolution: { @@ -2433,6 +2491,31 @@ packages: } engines: { node: ">=14", npm: ">=6", yarn: ">=1" } + "@testing-library/jest-dom@6.6.3": + resolution: + { + integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==, + } + engines: { node: ">=14", npm: ">=6", yarn: ">=1" } + + "@testing-library/react@16.3.0": + resolution: + { + integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==, + } + engines: { node: ">=18" } + peerDependencies: + "@testing-library/dom": ^10.0.0 + "@types/react": ^18.0.0 || ^19.0.0 + "@types/react-dom": ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + "@testing-library/user-event@14.5.2": resolution: { @@ -2870,6 +2953,13 @@ packages: engines: { node: ">=0.4.0" } hasBin: true + agent-base@7.1.3: + resolution: + { + integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==, + } + engines: { node: ">= 14" } + ajv-formats@2.1.1: resolution: { @@ -3319,12 +3409,26 @@ packages: integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, } + cssstyle@4.3.1: + resolution: + { + integrity: sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==, + } + engines: { node: ">=18" } + csstype@3.1.3: resolution: { integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==, } + data-urls@5.0.0: + resolution: + { + integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==, + } + engines: { node: ">=18" } + debug@4.4.0: resolution: { @@ -3481,6 +3585,13 @@ packages: } engines: { node: ">=10.13.0" } + entities@6.0.0: + resolution: + { + integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==, + } + engines: { node: ">=0.12" } + environment@1.1.0: resolution: { @@ -4058,6 +4169,13 @@ packages: } engines: { node: ">= 0.4" } + html-encoding-sniffer@4.0.0: + resolution: + { + integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==, + } + engines: { node: ">=18" } + html-escaper@2.0.2: resolution: { @@ -4071,6 +4189,20 @@ packages: } engines: { node: ">= 0.8" } + http-proxy-agent@7.0.2: + resolution: + { + integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, + } + engines: { node: ">= 14" } + + https-proxy-agent@7.0.6: + resolution: + { + integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, + } + engines: { node: ">= 14" } + human-signals@5.0.0: resolution: { @@ -4225,6 +4357,12 @@ packages: } engines: { node: ">=0.12.0" } + is-potential-custom-element-name@1.0.1: + resolution: + { + integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, + } + is-promise@4.0.0: resolution: { @@ -4333,6 +4471,18 @@ packages: } engines: { node: ">=12.0.0" } + jsdom@26.1.0: + resolution: + { + integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==, + } + engines: { node: ">=18" } + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: { @@ -4796,6 +4946,12 @@ packages: } engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + nwsapi@2.2.20: + resolution: + { + integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==, + } + object-assign@4.1.1: resolution: { @@ -4878,6 +5034,12 @@ packages: } engines: { node: ">=6" } + parse5@7.3.0: + resolution: + { + integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, + } + parseurl@1.3.3: resolution: { @@ -5262,6 +5424,12 @@ packages: } engines: { node: ">= 18" } + rrweb-cssom@0.8.0: + resolution: + { + integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==, + } + run-parallel@1.2.0: resolution: { @@ -5287,6 +5455,13 @@ packages: integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, } + saxes@6.0.0: + resolution: + { + integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, + } + engines: { node: ">=v12.22.7" } + scheduler@0.26.0: resolution: { @@ -5579,6 +5754,12 @@ packages: } engines: { node: ">= 0.4" } + symbol-tree@3.2.4: + resolution: + { + integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, + } + tailwind-merge@3.0.2: resolution: { @@ -5708,6 +5889,19 @@ packages: } engines: { node: ">=14.0.0" } + tldts-core@6.1.86: + resolution: + { + integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==, + } + + tldts@6.1.86: + resolution: + { + integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==, + } + hasBin: true + to-regex-range@5.0.1: resolution: { @@ -5729,6 +5923,20 @@ packages: } engines: { node: ">=6" } + tough-cookie@5.1.2: + resolution: + { + integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==, + } + engines: { node: ">=16" } + + tr46@5.1.1: + resolution: + { + integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==, + } + engines: { node: ">=18" } + ts-api-utils@2.1.0: resolution: { @@ -5970,6 +6178,13 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: + { + integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, + } + engines: { node: ">=18" } + watchpack@2.4.2: resolution: { @@ -5977,6 +6192,13 @@ packages: } engines: { node: ">=10.13.0" } + webidl-conversions@7.0.0: + resolution: + { + integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==, + } + engines: { node: ">=12" } + webpack-sources@3.2.3: resolution: { @@ -6003,6 +6225,27 @@ packages: webpack-cli: optional: true + whatwg-encoding@3.1.1: + resolution: + { + integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==, + } + engines: { node: ">=18" } + + whatwg-mimetype@4.0.0: + resolution: + { + integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==, + } + engines: { node: ">=18" } + + whatwg-url@14.2.0: + resolution: + { + integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==, + } + engines: { node: ">=18" } + which-typed-array@1.1.19: resolution: { @@ -6075,6 +6318,19 @@ packages: utf-8-validate: optional: true + xml-name-validator@5.0.0: + resolution: + { + integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, + } + engines: { node: ">=18" } + + xmlchars@2.2.0: + resolution: + { + integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, + } + yallist@3.1.1: resolution: { @@ -6118,6 +6374,14 @@ snapshots: "@jridgewell/gen-mapping": 0.3.8 "@jridgewell/trace-mapping": 0.3.25 + "@asamuzakjp/css-color@3.1.7": + dependencies: + "@csstools/css-calc": 2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + "@csstools/css-color-parser": 3.0.9(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + "@csstools/css-parser-algorithms": 3.0.4(@csstools/css-tokenizer@3.0.3) + "@csstools/css-tokenizer": 3.0.3 + lru-cache: 10.4.3 + "@babel/code-frame@7.27.1": dependencies: "@babel/helper-validator-identifier": 7.27.1 @@ -6255,6 +6519,26 @@ snapshots: - "@chromatic-com/playwright" - react + "@csstools/color-helpers@5.0.2": {} + + "@csstools/css-calc@2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)": + dependencies: + "@csstools/css-parser-algorithms": 3.0.4(@csstools/css-tokenizer@3.0.3) + "@csstools/css-tokenizer": 3.0.3 + + "@csstools/css-color-parser@3.0.9(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)": + dependencies: + "@csstools/color-helpers": 5.0.2 + "@csstools/css-calc": 2.1.3(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + "@csstools/css-parser-algorithms": 3.0.4(@csstools/css-tokenizer@3.0.3) + "@csstools/css-tokenizer": 3.0.3 + + "@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)": + dependencies: + "@csstools/css-tokenizer": 3.0.3 + + "@csstools/css-tokenizer@3.0.3": {} + "@esbuild/aix-ppc64@0.25.4": optional: true @@ -7773,7 +8057,7 @@ snapshots: optionalDependencies: "@vitest/browser": 3.1.3(playwright@1.52.0)(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))(vitest@3.1.3) "@vitest/runner": 3.1.3 - vitest: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) + vitest: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) transitivePeerDependencies: - react - react-dom @@ -8030,6 +8314,26 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 + "@testing-library/jest-dom@6.6.3": + dependencies: + "@adobe/css-tools": 4.4.2 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + + "@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)": + dependencies: + "@babel/runtime": 7.27.1 + "@testing-library/dom": 10.4.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + "@types/react": 19.1.3 + "@types/react-dom": 19.1.3(@types/react@19.1.3) + "@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)": dependencies: "@testing-library/dom": 10.4.0 @@ -8192,7 +8496,7 @@ snapshots: magic-string: 0.30.17 sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) + vitest: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) ws: 8.18.2 optionalDependencies: playwright: 1.52.0 @@ -8216,7 +8520,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) + vitest: 3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1) optionalDependencies: "@vitest/browser": 3.1.3(playwright@1.52.0)(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))(vitest@3.1.3) transitivePeerDependencies: @@ -8385,6 +8689,8 @@ snapshots: acorn@8.14.1: {} + agent-base@7.1.3: {} + ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -8633,8 +8939,18 @@ snapshots: css.escape@1.5.1: {} + cssstyle@4.3.1: + dependencies: + "@asamuzakjp/css-color": 3.1.7 + rrweb-cssom: 0.8.0 + csstype@3.1.3: {} + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + debug@4.4.0: dependencies: ms: 2.1.3 @@ -8696,6 +9012,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.1 + entities@6.0.0: {} + environment@1.1.0: {} es-define-property@1.0.1: {} @@ -9084,6 +9402,10 @@ snapshots: dependencies: function-bind: 1.1.2 + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + html-escaper@2.0.2: {} http-errors@2.0.0: @@ -9094,6 +9416,20 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + human-signals@5.0.0: {} husky@9.1.7: {} @@ -9164,6 +9500,8 @@ snapshots: is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} is-regex@1.2.1: @@ -9228,6 +9566,33 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} + jsdom@26.1.0: + dependencies: + cssstyle: 4.3.1 + data-urls: 5.0.0 + decimal.js: 10.5.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.20 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.2 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -9447,6 +9812,8 @@ snapshots: dependencies: path-key: 4.0.0 + nwsapi@2.2.20: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -9496,6 +9863,10 @@ snapshots: dependencies: callsites: 3.1.0 + parse5@7.3.0: + dependencies: + entities: 6.0.0 + parseurl@1.3.3: {} path-exists@4.0.0: {} @@ -9811,6 +10182,8 @@ snapshots: transitivePeerDependencies: - supports-color + rrweb-cssom@0.8.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -9825,6 +10198,10 @@ snapshots: safer-buffer@2.1.2: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.26.0: {} schema-utils@4.3.2: @@ -10011,6 +10388,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + tailwind-merge@3.0.2: {} tailwind-variants@1.0.0(tailwindcss@4.1.5): @@ -10071,6 +10450,12 @@ snapshots: tinyspy@3.0.2: {} + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -10079,6 +10464,14 @@ snapshots: totalist@3.0.1: {} + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -10207,7 +10600,7 @@ snapshots: tsx: 4.19.4 yaml: 2.7.1 - vitest@3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1): + vitest@3.1.3(@types/node@22.15.15)(@vitest/browser@3.1.3)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1): dependencies: "@vitest/expect": 3.1.3 "@vitest/mocker": 3.1.3(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)) @@ -10233,6 +10626,7 @@ snapshots: optionalDependencies: "@types/node": 22.15.15 "@vitest/browser": 3.1.3(playwright@1.52.0)(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))(vitest@3.1.3) + jsdom: 26.1.0 transitivePeerDependencies: - jiti - less @@ -10247,11 +10641,17 @@ snapshots: - tsx - yaml + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 + webidl-conversions@7.0.0: {} + webpack-sources@3.2.3: {} webpack-virtual-modules@0.6.2: {} @@ -10287,6 +10687,17 @@ snapshots: - esbuild - uglify-js + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -10330,6 +10741,10 @@ snapshots: ws@8.18.2: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + yallist@3.1.1: {} yaml@2.7.1: {} diff --git a/apps/web/src/components/ui/Button/Button.test.tsx b/apps/web/src/components/ui/Button/Button.test.tsx new file mode 100644 index 00000000..06d0316f --- /dev/null +++ b/apps/web/src/components/ui/Button/Button.test.tsx @@ -0,0 +1,12 @@ +// src/components/Button.test.tsx +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { Button } from "./Button"; + +describe("Button component", () => { + it("renders with correct text", () => { + render(); + const button = screen.getByText(/Click Me!/i); + expect(button).toBeInTheDocument(); + }); +}); diff --git a/apps/web/src/components/ui/Button/Button.types.ts b/apps/web/src/components/ui/Button/Button.types.ts deleted file mode 100644 index 553f3293..00000000 --- a/apps/web/src/components/ui/Button/Button.types.ts +++ /dev/null @@ -1 +0,0 @@ -// This will be where button types and constants go diff --git a/apps/web/src/setupTests.ts b/apps/web/src/setupTests.ts new file mode 100644 index 00000000..bf210c79 --- /dev/null +++ b/apps/web/src/setupTests.ts @@ -0,0 +1,11 @@ +// src/setupTests.js (or .ts) +import "@testing-library/jest-dom"; +import { cleanup } from "@testing-library/react"; +import { afterEach } from "vitest"; + +// This is often implicitly handled by @testing-library/react with Vitest, +// but explicitly calling cleanup can be a good practice to ensure isolation +// between tests in case of specific scenarios or custom renderers. +afterEach(() => { + cleanup(); +}); diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 1ffef600..4d73ec11 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -3,5 +3,8 @@ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" } - ] + ], + "compilerOptions": { + "types": ["vitest/globals", "@testing-library/jest-dom"] + } } diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 743c393c..9d3cc68d 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -1,3 +1,4 @@ +/// import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; @@ -11,6 +12,11 @@ export default defineConfig({ react(), tailwindcss(), ], + test: { + environment: "jsdom", + setupFiles: ["./src/setupTests.ts"], + globals: true, + }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), diff --git a/apps/web/vitest.workspace.ts b/apps/web/vitest.workspace.ts index c56bdfe5..1da3a007 100644 --- a/apps/web/vitest.workspace.ts +++ b/apps/web/vitest.workspace.ts @@ -13,6 +13,15 @@ const dirname = // More info at: https://storybook.js.org/docs/writing-tests/test-addon export default defineWorkspace([ "vite.config.ts", + { + extends: "vite.config.ts", + test: { + name: "unit", + environment: "jsdom", + setupFiles: ["./src/setupTests.ts"], + globals: true, + }, + }, { extends: "vite.config.ts", plugins: [