|
1 | 1 | # Tests |
2 | 2 |
|
3 | | -This directory contains utilities to support our automated testing efforts. |
| 3 | +The tests subject contains utilities, helpers, and infrastructure to support automated testing across docs.github.com. This includes test helpers, mock servers, schema validation, and shared testing patterns. |
4 | 4 |
|
5 | | -**This directory should not include test suites.** Please use the best subject folder available. |
| 5 | +> [!NOTE] |
| 6 | +> This directory should not include test suites. Test files belong in their respective subject directories (e.g., `src/search/tests/`, `src/frame/tests/`). |
6 | 7 |
|
7 | | -It's not strictly necessary to run tests locally while developing. You can |
8 | | -always open a pull request and rely on the CI service to run tests for you, |
9 | | -but it's helpful to run tests locally before pushing your changes to |
10 | | -GitHub. |
| 8 | +## Purpose & Scope |
11 | 9 |
|
12 | | -Tests are written using [vitest](https://vitest.dev/). |
| 10 | +This subject is responsible for: |
| 11 | +- Test utilities and helper functions shared across subjects |
| 12 | +- Mock server infrastructure for integration tests |
| 13 | +- Schema validation utilities (AJV) |
| 14 | +- Test data and fixtures management |
| 15 | +- Vitest configuration and setup |
| 16 | +- TypeScript declarations for test tooling |
| 17 | +- Shared testing patterns and conventions |
13 | 18 |
|
14 | | -`vitest` runs tests and handles assertions. |
| 19 | +Tests are written using [Vitest](https://vitest.dev/) for unit and integration tests, and [Playwright](https://playwright.dev/) for end-to-end browser tests. |
15 | 20 |
|
16 | | -## Install optional dependencies |
| 21 | +## Architecture & Key Assets |
17 | 22 |
|
18 | | -We typically rely on CI to run our tests, so some large test-only |
19 | | -dependencies are considered **optional**. To run the tests locally, you'll |
20 | | -need to make sure optional dependencies are installed by running: |
| 23 | +### Key capabilities and their locations |
21 | 24 |
|
22 | | -```shell |
| 25 | +- `lib/validate-json-schema.ts` - AJV validator factory for JSON schema validation |
| 26 | +- `mocks/start-mock-server.ts` - Creates mock HTTP server for integration tests |
| 27 | +- `helpers/e2etest.ts` - Utilities for end-to-end testing scenarios |
| 28 | +- `vitest.setup.ts` - Global Vitest configuration and hooks |
| 29 | + |
| 30 | +## Setup & Usage |
| 31 | + |
| 32 | +### Installing test dependencies |
| 33 | + |
| 34 | +Test-only dependencies are optional to keep standard installs faster: |
| 35 | + |
| 36 | +```bash |
23 | 37 | npm ci --include=optional |
24 | 38 | ``` |
25 | 39 |
|
26 | | -## Running all the tests |
27 | | - |
28 | | -Once you've followed the development instructions above, you can run the entire |
29 | | -test suite locally: |
| 40 | +### Running all tests |
30 | 41 |
|
31 | | -```shell |
| 42 | +```bash |
32 | 43 | npm test |
33 | 44 | ``` |
34 | 45 |
|
35 | | -## Watching all the tests |
| 46 | +### Running tests in watch mode |
36 | 47 |
|
37 | | -You can run a script that continually watches for changes and |
38 | | -re-runs the tests whenever a change is made. This command notifies you |
39 | | -when tests change to and from a passing or failing state, and it prints |
40 | | -out a test coverage report so you can see what files need testing. |
| 48 | +Continuously re-runs tests on file changes: |
41 | 49 |
|
42 | | -```shell |
| 50 | +```bash |
43 | 51 | npm run test-watch |
44 | 52 | ``` |
45 | 53 |
|
46 | | -## Running individual tests |
| 54 | +### Running specific tests |
47 | 55 |
|
48 | | -You can run specific tests in two ways: |
49 | | - |
50 | | -```shell |
51 | | -# The TEST_NAME can be a filename, partial filename, or path to a file or directory |
| 56 | +```bash |
| 57 | +# By filename or path |
52 | 58 | npm test -- <TEST_NAME> |
53 | 59 |
|
54 | | -vitest path/to/tests/directory |
55 | | -``` |
| 60 | +# By directory |
| 61 | +vitest src/search/tests |
56 | 62 |
|
57 | | -## Allowing logging in tests |
| 63 | +# Single test file |
| 64 | +vitest src/versions/tests/versions.ts |
| 65 | +``` |
58 | 66 |
|
59 | | -If you set up a `console.log` in the code and want to see the output, simply append the `--silent false` flag to your test to see console output. |
| 67 | +### Viewing console output |
60 | 68 |
|
61 | | -## Failed Local Tests |
| 69 | +By default, console.log is suppressed. To see output: |
62 | 70 |
|
63 | | -If the tests fail locally with an error like this: |
| 71 | +```bash |
| 72 | +npm test -- <TEST_NAME> --silent=false |
| 73 | +``` |
64 | 74 |
|
65 | | -`Could not find a production build in the '/Users/username/repos/docs-internal/.next' directory.` |
| 75 | +### Building before tests |
66 | 76 |
|
67 | | -You may need to run this before every test run: |
| 77 | +Some tests require a production build: |
68 | 78 |
|
69 | | -```shell |
| 79 | +```bash |
70 | 80 | npx next build |
| 81 | +npm test |
71 | 82 | ``` |
72 | 83 |
|
73 | | -## Linting |
| 84 | +Error: `Could not find a production build` means you need to run `next build`. |
74 | 85 |
|
75 | | -To validate all your JavaScript code (and auto-format some easily reparable mistakes), |
76 | | -run the linter: |
| 86 | +## Data & External Dependencies |
77 | 87 |
|
78 | | -```shell |
79 | | -npm run lint |
80 | | -``` |
81 | | - |
82 | | -## Keeping the server running |
| 88 | +### Dependencies |
| 89 | +- **Vitest** - Test runner and assertion library |
| 90 | +- **Playwright** - Browser automation for E2E tests |
| 91 | +- **AJV** - JSON schema validation |
| 92 | +- Mock server libraries for HTTP mocking |
83 | 93 |
|
84 | | -When you run `vitest` tests that depend on making real HTTP requests |
85 | | -to `localhost:4000`, the `vitest` tests have a hook that starts the |
86 | | -server before running all/any tests and stops the server when done. |
| 94 | +### Test data |
| 95 | +- Fixture content in `src/fixtures/` |
| 96 | +- Schema files in `helpers/schemas/` |
| 97 | +- Mock responses in `mocks/` |
87 | 98 |
|
88 | | -You can disable this, which might make it easier when debugging tests |
89 | | -since the server won't need to start and stop every time you run tests. |
| 99 | +### Server management |
90 | 100 |
|
91 | | -In one terminal, type: |
| 101 | +Tests that make HTTP requests to `localhost:4000`: |
| 102 | +- Vitest automatically starts/stops server via hooks |
| 103 | +- Disable with `START_VITEST_SERVER=false` for manual server control |
92 | 104 |
|
93 | | -```shell |
| 105 | +Manual server for debugging: |
| 106 | +```bash |
| 107 | +# Terminal 1 |
94 | 108 | NODE_ENV=test PORT=4000 tsx src/frame/server.ts |
| 109 | + |
| 110 | +# Terminal 2 |
| 111 | +START_VITEST_SERVER=false vitest src/versions/tests |
95 | 112 | ``` |
96 | 113 |
|
97 | | -In another terminal, type: |
| 114 | +## Cross-links & Ownership |
| 115 | + |
| 116 | +### Related subjects |
| 117 | +- [`src/fixtures`](../fixtures/README.md) - Fixture-based testing with minimal content |
| 118 | +- All subjects with `/tests/` directories - Test consumers |
| 119 | +- CI workflows in `.github/workflows/` - Automated test execution |
| 120 | + |
| 121 | +### Testing documentation |
| 122 | +- [Fixture content](../fixtures/README.md) - Fixture-based testing patterns |
| 123 | +- [Playwright E2E tests](../fixtures/PLAYWRIGHT.md) - Headless browser testing |
| 124 | + |
| 125 | +### Ownership |
| 126 | +- Team: Docs Engineering |
| 127 | + |
| 128 | +## Current State & Next Steps |
| 129 | + |
| 130 | +### Testing patterns |
98 | 131 |
|
99 | | -```shell |
100 | | -START_VITEST_SERVER=false vitests src/versions/tests |
| 132 | +**Unit tests** - Test individual functions/modules: |
| 133 | +```typescript |
| 134 | +import { describe, test, expect } from 'vitest' |
| 135 | + |
| 136 | +describe('myFunction', () => { |
| 137 | + test('returns expected value', () => { |
| 138 | + expect(myFunction('input')).toBe('output') |
| 139 | + }) |
| 140 | +}) |
101 | 141 | ``` |
102 | 142 |
|
103 | | -Or whatever the testing command you use is. |
| 143 | +**Integration tests** - Test HTTP endpoints: |
| 144 | +```typescript |
| 145 | +import { get } from '@/tests/helpers/e2etest' |
104 | 146 |
|
105 | | -The `START_VITEST_SERVER` environment variable needs to be set to `false`, |
106 | | -or else `vitest` will try to start a server on `:4000` too. |
| 147 | +test('GET /search returns results', async () => { |
| 148 | + const res = await get('/search?query=test') |
| 149 | + expect(res.statusCode).toBe(200) |
| 150 | +}) |
| 151 | +``` |
107 | 152 |
|
108 | | -## Debugging middleware errors |
| 153 | +**Playwright tests** - Browser automation: |
| 154 | +```typescript |
| 155 | +test('search works in browser', async ({ page }) => { |
| 156 | + await page.goto('/search') |
| 157 | + await page.fill('input[name="query"]', 'test') |
| 158 | + // ...assertions |
| 159 | +}) |
| 160 | +``` |
109 | 161 |
|
110 | | -By default, errors handled by the middleware are dealt with just like |
111 | | -any error in production. It's common to have end-to-end tests that expect |
112 | | -a page to throw a 500 Internal Server Error response. |
| 162 | +### Debugging middleware errors |
113 | 163 |
|
114 | | -If you don't expect that and you might struggle to see exactly where the |
115 | | -error is happening, set `$DEBUG_MIDDLEWARE_TESTS` to `true`. For example: |
| 164 | +Middleware errors are suppressed by default in tests. To see full errors: |
116 | 165 |
|
117 | | -```shell |
| 166 | +```bash |
118 | 167 | export DEBUG_MIDDLEWARE_TESTS=true |
119 | | -vitest src/shielding/tests -b |
| 168 | +vitest src/shielding/tests |
120 | 169 | ``` |
121 | 170 |
|
122 | | -## Fixture based testing |
| 171 | +### Linting tests |
| 172 | + |
| 173 | +```bash |
| 174 | +npm run lint |
| 175 | +``` |
| 176 | + |
| 177 | +### Known limitations |
| 178 | +- Optional dependencies must be installed for local testing |
| 179 | +- Some tests require production build (`next build`) |
| 180 | +- Server startup/shutdown adds overhead to test runs |
| 181 | +- Fixtures may lag behind actual content structure |
| 182 | + |
| 183 | +### Test organization |
| 184 | + |
| 185 | +Tests should be co-located with their subject: |
| 186 | +- ✅ `src/search/tests/api-search.ts` |
| 187 | +- ✅ `src/versions/tests/middleware.ts` |
| 188 | +- ❌ `src/tests/search-tests.ts` (wrong - not in subject) |
| 189 | + |
| 190 | +Shared utilities belong in `src/tests/`: |
| 191 | +- Helper functions used across subjects |
| 192 | +- Mock servers and fixtures |
| 193 | +- Schema validation utilities |
| 194 | +- Test infrastructure |
| 195 | + |
| 196 | +### Adding test helpers |
| 197 | + |
| 198 | +1. Create a file in `src/tests/helpers/` |
| 199 | +2. Export reusable functions |
| 200 | +3. Document usage with JSDoc |
| 201 | +4. Add tests for the helper itself |
| 202 | +5. Import in test files across subjects |
| 203 | + |
| 204 | +### CI integration |
| 205 | + |
| 206 | +Tests run automatically in GitHub Actions: |
| 207 | +- On pull requests |
| 208 | +- On pushes to main |
| 209 | +- Various test suites in parallel for speed |
| 210 | + |
| 211 | +See `.github/workflows/` for CI configuration. |
| 212 | + |
| 213 | +### Troubleshooting |
| 214 | + |
| 215 | +**Tests fail with missing build:** |
| 216 | +Run `npx next build` before tests. |
| 217 | + |
| 218 | +**Tests hang or timeout:** |
| 219 | +Check if server started correctly. Use `DEBUG_MIDDLEWARE_TESTS=true`. |
123 | 220 |
|
124 | | -See [Fixture content](src/fixtures/README.md). |
| 221 | +**Flaky tests:** |
| 222 | +- Check for race conditions |
| 223 | +- Ensure proper test isolation |
| 224 | +- Verify mocks are properly reset |
125 | 225 |
|
126 | | -## Headless tests with Playwright |
| 226 | +**Mock server issues:** |
| 227 | +Check `src/tests/mocks/start-mock-server.ts` is running and configured correctly. |
127 | 228 |
|
128 | | -See [Headless tests with Playwright](src/fixtures/PLAYWRIGHT.md) |
|
0 commit comments