diff --git a/packages/testing/README.md b/packages/testing/README.md new file mode 100644 index 0000000..4240ef6 --- /dev/null +++ b/packages/testing/README.md @@ -0,0 +1,81 @@ +# Testing + +How we’ve configured testing in Goat Stack. + +Goat Stack uses Vitest for unit testing. It’s a fast and lightweight testing framework that’s compatible with Jest’s API. Unit tests are run automatically as part of the build task in Turborepo. + +## Running Tests + +To run tests, simply execute: + +```bash +pnpm test +``` + +This will run all tests in the `__tests__` folder in each app, however we’ve only written a couple of tests for app project so far. + +## Adding Tests + +We use Testing Library for our tests. It’s a great library that allows you to test your components in a way that’s close to how a user would interact with them. + +In the `__tests__` folder, create a new file called `[page].test.tsx` (where `[page]` is the name of the page you want to test). Here’s an example of a test for the home page: + +`apps/app/__tests__/home.test.tsx` + +```typescript +import { render, screen } from '@testing-library/react'; +import { expect, test } from 'vitest'; +import Page from '../app/(unauthenticated)/home/page'; + +test('Home Page', () => { + render(); + expect( + screen.getByRole('heading', { + level: 1, + name: 'Hello, world!', + }) + ).toBeDefined(); +}); +``` + +## Adding Vitest to other apps + +To add Vitest to another app, you’ll need to install the dependencies: + +```bash +pnpm install -D vitest @testing-library/react @testing-library/dom --filter [app] +``` + +as well as adding the `@goat/testing` package to the `devDependencies` section of the `package.json` file: + +`apps/[app]/package.json` + +```json +"devDependencies": { + "@goat/testing": "workspace:*" +} +``` + +Create a new file called `vitest.config.ts` in the root of the app and add the following: + +`apps/[app]/vitest.config.ts` + +```typescript +export { default } from '@goat/testing'; +``` + +Then, create a new file in the `__tests__` folder in the relevant app and add a test command to the `package.json` file: + +`apps/[app]/package.json` + +```json +{ + "scripts": { + "test": "vitest" + } +} +``` + +Turborepo will automatically pick up on the new test script and run the tests. + +Then, just follow the instructions above to write tests. diff --git a/packages/testing/index.js b/packages/testing/index.js new file mode 100644 index 0000000..09a01b1 --- /dev/null +++ b/packages/testing/index.js @@ -0,0 +1,18 @@ +const path = require('node:path'); +const react = require('@vitejs/plugin-react'); +const { defineConfig } = require('vitest/config'); + +const config = defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './'), + '@goat': path.resolve(__dirname, '../../packages'), + }, + }, +}); + +module.exports = config; \ No newline at end of file diff --git a/packages/testing/package.json b/packages/testing/package.json new file mode 100644 index 0000000..feee9bc --- /dev/null +++ b/packages/testing/package.json @@ -0,0 +1,17 @@ +{ + "name": "@goat/testing", + "version": "0.0.0", + "private": true, + "main": "./index.js", + "type": "commonjs", + "scripts": { + "clean": "git clean -xdf .cache .turbo dist node_modules", + "typecheck": "tsc --noEmit --emitDeclarationOnly false" + }, + "devDependencies": { + "@goat/typescript-config": "workspace:*", + "@vitejs/plugin-react": "^4.3.4", + "vitest": "^3.0.7", + "typescript": "5.7.3" + } +} diff --git a/packages/testing/tsconfig.json b/packages/testing/tsconfig.json new file mode 100644 index 0000000..cdc7ad6 --- /dev/null +++ b/packages/testing/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@goat/typescript-config/react-library.json", + "include": ["src"], + "exclude": ["dist", "build", "node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a00899c..329f917 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,15 @@ importers: specifier: 2.0.1 version: 2.0.1(@bufbuild/protoc-gen-es@2.2.4(@bufbuild/protobuf@2.2.4)) + packages/testing: + devDependencies: + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.3.4(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0)) + vitest: + specifier: ^3.0.7 + version: 3.1.1(jiti@2.4.2)(jsdom@26.0.0)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0) + packages/typescript-config: {} packages/ui: @@ -1162,6 +1171,9 @@ packages: '@vitest/expect@3.0.5': resolution: {integrity: sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==} + '@vitest/expect@3.1.1': + resolution: {integrity: sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==} + '@vitest/mocker@3.0.5': resolution: {integrity: sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==} peerDependencies: @@ -1173,24 +1185,50 @@ packages: vite: optional: true + '@vitest/mocker@3.1.1': + resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@3.0.5': resolution: {integrity: sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==} '@vitest/pretty-format@3.0.8': resolution: {integrity: sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==} + '@vitest/pretty-format@3.1.1': + resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==} + '@vitest/runner@3.0.5': resolution: {integrity: sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==} + '@vitest/runner@3.1.1': + resolution: {integrity: sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==} + '@vitest/snapshot@3.0.5': resolution: {integrity: sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==} + '@vitest/snapshot@3.1.1': + resolution: {integrity: sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==} + '@vitest/spy@3.0.5': resolution: {integrity: sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==} + '@vitest/spy@3.1.1': + resolution: {integrity: sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==} + '@vitest/utils@3.0.5': resolution: {integrity: sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==} + '@vitest/utils@3.1.1': + resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} + acorn@8.14.1: resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} engines: {node: '>=0.4.0'} @@ -2064,6 +2102,11 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + vite-node@3.1.1: + resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite@6.1.0: resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2132,6 +2175,34 @@ packages: jsdom: optional: true + vitest@3.1.1: + resolution: {integrity: sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.1.1 + '@vitest/ui': 3.1.1 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -3014,6 +3085,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 + '@vitest/expect@3.1.1': + dependencies: + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 + chai: 5.2.0 + tinyrainbow: 2.0.0 + '@vitest/mocker@3.0.5(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0))': dependencies: '@vitest/spy': 3.0.5 @@ -3022,6 +3100,14 @@ snapshots: optionalDependencies: vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0) + '@vitest/mocker@3.1.1(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0))': + dependencies: + '@vitest/spy': 3.1.1 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0) + '@vitest/pretty-format@3.0.5': dependencies: tinyrainbow: 2.0.0 @@ -3030,27 +3116,52 @@ snapshots: dependencies: tinyrainbow: 2.0.0 + '@vitest/pretty-format@3.1.1': + dependencies: + tinyrainbow: 2.0.0 + '@vitest/runner@3.0.5': dependencies: '@vitest/utils': 3.0.5 pathe: 2.0.3 + '@vitest/runner@3.1.1': + dependencies: + '@vitest/utils': 3.1.1 + pathe: 2.0.3 + '@vitest/snapshot@3.0.5': dependencies: '@vitest/pretty-format': 3.0.5 magic-string: 0.30.17 pathe: 2.0.3 + '@vitest/snapshot@3.1.1': + dependencies: + '@vitest/pretty-format': 3.1.1 + magic-string: 0.30.17 + pathe: 2.0.3 + '@vitest/spy@3.0.5': dependencies: tinyspy: 3.0.2 + '@vitest/spy@3.1.1': + dependencies: + tinyspy: 3.0.2 + '@vitest/utils@3.0.5': dependencies: '@vitest/pretty-format': 3.0.5 loupe: 3.1.3 tinyrainbow: 2.0.0 + '@vitest/utils@3.1.1': + dependencies: + '@vitest/pretty-format': 3.1.1 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + acorn@8.14.1: {} agent-base@7.1.3: {} @@ -3867,6 +3978,27 @@ snapshots: - tsx - yaml + vite-node@3.1.1(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0): dependencies: esbuild: 0.24.2 @@ -3917,6 +4049,44 @@ snapshots: - tsx - yaml + vitest@3.1.1(jiti@2.4.2)(jsdom@26.0.0)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0): + dependencies: + '@vitest/expect': 3.1.1 + '@vitest/mocker': 3.1.1(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0)) + '@vitest/pretty-format': 3.1.1 + '@vitest/runner': 3.1.1 + '@vitest/snapshot': 3.1.1 + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.2.0 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.8.1 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0) + vite-node: 3.1.1(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.3)(yaml@2.7.0) + why-is-node-running: 2.3.0 + optionalDependencies: + jsdom: 26.0.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0