Skip to content

Commit 66bf57d

Browse files
authored
test: E2E Test Rework (#101)
1 parent 7b7c899 commit 66bf57d

34 files changed

Lines changed: 2094 additions & 3020 deletions

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = {
55
env: {
66
es6: true,
77
node: true,
8+
jest: true,
89
},
910
parser: '@typescript-eslint/parser',
1011
plugins: ['@typescript-eslint'],

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ plugins
3333
.vscode/launch.json
3434
.env
3535
.vercel
36+
37+
e2e-tests/fixtures/.tracking/*

README.md

Lines changed: 81 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</p>
44

55
> **⚠️ Experimental:** This wizard is still in an experimental phase. If you
6-
> have any feedback, please drop an email to **joshua** [at] **posthog** [dot]
6+
> have any feedback, please drop an email to **joshua** [at] **posthog** [dot] >
77
> **com**.
88
99
<h1>PostHog wizard ✨</h1>
@@ -17,13 +17,14 @@ To use the wizard, you can run it directly using:
1717
npx @posthog/wizard
1818
```
1919

20-
Currently the wizard can be used for **React, NextJS, Svelte, Astro and React Native**
21-
projects. If you have other integrations you would like the wizard to support,
22-
please open a [GitHub issue](https://github.com/posthog/wizard/issues)!
20+
Currently the wizard can be used for **React, NextJS, Svelte, Astro and React
21+
Native** projects. If you have other integrations you would like the wizard to
22+
support, please open a [GitHub issue](https://github.com/posthog/wizard/issues)!
2323

2424
## MCP Commands
2525

26-
The wizard also includes commands for managing PostHog MCP (Model Context Protocol) servers:
26+
The wizard also includes commands for managing PostHog MCP (Model Context
27+
Protocol) servers:
2728

2829
```bash
2930
# Install PostHog MCP server to supported clients
@@ -37,97 +38,136 @@ npx @posthog/wizard mcp remove
3738

3839
The following CLI arguments are available:
3940

40-
| Option | Description | Type | Default | Choices | Environment Variable |
41-
| ----------------- | -------------------------------------------------------------------------- | ------- | ------------------------------- | ---------------------------------------------------- | --------------------------------- |
42-
| `--help` | Show help | boolean | | | |
43-
| `--version` | Show version number | boolean | | | |
44-
| `--debug` | Enable verbose logging | boolean | `false` | | `POSTHOG_WIZARD_DEBUG` |
45-
| `--region` | PostHog cloud region (when not specified, prompts for selection) | string | | "us", "eu" | `POSTHOG_WIZARD_REGION` |
46-
| `--default` | Use default options for all prompts | boolean | `true` | | `POSTHOG_WIZARD_DEFAULT` |
47-
| `--signup` | Create a new PostHog account during setup | boolean | `false` | | `POSTHOG_WIZARD_SIGNUP` |
48-
| `--integration` | Integration to set up | string | | "nextjs", "astro", "react", "svelte", "react-native" | |
49-
| `--force-install` | Force install packages even if peer dependency checks fail | boolean | `false` | | `POSTHOG_WIZARD_FORCE_INSTALL` |
50-
| `--install-dir` | Directory to install PostHog in | string | | | `POSTHOG_WIZARD_INSTALL_DIR` |
41+
| Option | Description | Type | Default | Choices | Environment Variable |
42+
| ----------------- | ---------------------------------------------------------------- | ------- | ------- | ---------------------------------------------------- | ------------------------------ |
43+
| `--help` | Show help | boolean | | | |
44+
| `--version` | Show version number | boolean | | | |
45+
| `--debug` | Enable verbose logging | boolean | `false` | | `POSTHOG_WIZARD_DEBUG` |
46+
| `--region` | PostHog cloud region (when not specified, prompts for selection) | string | | "us", "eu" | `POSTHOG_WIZARD_REGION` |
47+
| `--default` | Use default options for all prompts | boolean | `true` | | `POSTHOG_WIZARD_DEFAULT` |
48+
| `--signup` | Create a new PostHog account during setup | boolean | `false` | | `POSTHOG_WIZARD_SIGNUP` |
49+
| `--integration` | Integration to set up | string | | "nextjs", "astro", "react", "svelte", "react-native" | |
50+
| `--force-install` | Force install packages even if peer dependency checks fail | boolean | `false` | | `POSTHOG_WIZARD_FORCE_INSTALL` |
51+
| `--install-dir` | Directory to install PostHog in | string | | | `POSTHOG_WIZARD_INSTALL_DIR` |
5152

5253
> Note: A large amount of the scaffolding for this came from the amazing Sentry
5354
> wizard, which you can find [here](https://github.com/getsentry/sentry-wizard)
5455
> 💖
5556
5657
# Steal this code
5758

58-
While the wizard works great on its own, we also find the approach used by this project is [a powerful way to improve AI agent coding sessions](https://posthog.com/blog/envoy-wizard-llm-agent). Agents can run CLI tools, which means that conventional code like this can participate in the AI revolution as well – with all the benefits and control that conventional code implies.
59+
While the wizard works great on its own, we also find the approach used by this
60+
project is
61+
[a powerful way to improve AI agent coding sessions](https://posthog.com/blog/envoy-wizard-llm-agent).
62+
Agents can run CLI tools, which means that conventional code like this can
63+
participate in the AI revolution as well – with all the benefits and control
64+
that conventional code implies.
5965

60-
If you want to use this code as a starting place for your own project, here's a quick explainer on its structure.
66+
If you want to use this code as a starting place for your own project, here's a
67+
quick explainer on its structure.
6168

6269
## Entrypoint: `run.ts`
6370

64-
The entrypoint for this tool is `run.ts`. Use this file to interpret arguments and set up the general flow of the application.
71+
The entrypoint for this tool is `run.ts`. Use this file to interpret arguments
72+
and set up the general flow of the application.
6573

6674
## Analytics
6775

68-
Did you know you can capture PostHog events even for smaller, supporting products like a command line tool? `src/utils/analytics.ts` is a great example of how to do it.
76+
Did you know you can capture PostHog events even for smaller, supporting
77+
products like a command line tool? `src/utils/analytics.ts` is a great example
78+
of how to do it.
6979

70-
This file wraps `posthog-node` with some convenience functions to set up an analytics session and log events. We can see the usage and outcomes of this wizard alongside all of our other PostHog product data, and this is very powerful. For example: we could show in-product surveys to people who have used the wizard to improve the experience.
80+
This file wraps `posthog-node` with some convenience functions to set up an
81+
analytics session and log events. We can see the usage and outcomes of this
82+
wizard alongside all of our other PostHog product data, and this is very
83+
powerful. For example: we could show in-product surveys to people who have used
84+
the wizard to improve the experience.
7185

7286
## Leave rules behind
7387

74-
Supporting agent sessions after we leave is important. There are plenty of ways to break or misconfigure PostHog, so guarding against this is key.
88+
Supporting agent sessions after we leave is important. There are plenty of ways
89+
to break or misconfigure PostHog, so guarding against this is key.
7590

76-
`src/utils/rules/add-editor-rules.ts` demonstrates how to dynamically construct rules files and store them in the project's `.cursor/rules` directory.
91+
`src/utils/rules/add-editor-rules.ts` demonstrates how to dynamically construct
92+
rules files and store them in the project's `.cursor/rules` directory.
7793

7894
## Prompts and LLM interactions
7995

80-
LLM agent sessions are *anti-deterministic*: really, anything can happen.
96+
LLM agent sessions are _anti-deterministic_: really, anything can happen.
8197

82-
But using LLMs for code generation is really advantageous: they can interpret existing code at scale and then modify it reliably.
98+
But using LLMs for code generation is really advantageous: they can interpret
99+
existing code at scale and then modify it reliably.
83100

84-
*If* they are well prompted.
101+
_If_ they are well prompted.
85102

86-
`src/lib/prompts.ts` demonstrates how to wrap a deterministic fence around a chaotic process. Every wizard session gets the same prompt, tailored to the specific files in the project.
103+
`src/lib/prompts.ts` demonstrates how to wrap a deterministic fence around a
104+
chaotic process. Every wizard session gets the same prompt, tailored to the
105+
specific files in the project.
87106

88-
These prompts are channeled using `src/utils/query.ts` to an LLM interface we host. This gives us more control: we can be certain of the model version and provider which interpret the prompts and modify the files. This way, we can find the right tools for the job and again, apply them consistently.
107+
These prompts are channeled using `src/utils/query.ts` to an LLM interface we
108+
host. This gives us more control: we can be certain of the model version and
109+
provider which interpret the prompts and modify the files. This way, we can find
110+
the right tools for the job and again, apply them consistently.
89111

90112
This also allows us to pick up the bill on behalf of our customers.
91113

92-
When we make improvements to this process, these are available instantly to all users of the wizard, no training delays or other ambiguity.
114+
When we make improvements to this process, these are available instantly to all
115+
users of the wizard, no training delays or other ambiguity.
93116

94-
## Testing locally
117+
## Running locally
95118

96119
Run:
97120

98121
```bash
99122
pnpm try --install-dir=[a path]
100123
```
101124

102-
103125
To build and use the tool locally:
104126

127+
```bash
128+
bin/build
105129
```
106-
pnpm build
107-
```
108-
This compiles the TypeScript code and prepares the `dist` directory. Run this command any time you make changes to the wizard's source code.
130+
131+
This compiles the TypeScript code and prepares the `dist` directory. Run this
132+
command any time you make changes to the wizard's source code.
109133

110134
```bash
111135
pnpm link --global
112136
```
113-
This command makes your local version of the wizard available system-wide. You generally only need to do this once.
114137

115-
Then:
138+
This command makes your local version of the wizard available system-wide. You
139+
generally only need to do this once.
140+
141+
Then:
116142

117143
```bash
118144
wizard [options]
119145
```
146+
120147
The wizard will execute your last build.
121148

122-
## Publishing your tool
149+
## Testing
123150

124-
To make your version of a tool usable with a one-line `npx` command:
151+
To run unit tests, run:
125152

126-
1. Edit `package.json`, especially details like `name`, `version`
127-
2. Run [`npm publish`](https://docs.npmjs.com/cli/v7/commands/npm-publish) from your project directory
128-
3. Now you can run it with `npx yourpackagename`
153+
```bash
154+
bin/test
155+
```
129156

157+
To run E2E tests run:
130158

159+
```bash
160+
bin/test-e2e
161+
```
131162

163+
E2E tests are a bit more complicated to create and adjust due to to their mocked
164+
LLM calls. See the `e2e-tests/README.md` for more information.
132165

166+
## Publishing your tool
133167

168+
To make your version of a tool usable with a one-line `npx` command:
169+
170+
1. Edit `package.json`, especially details like `name`, `version`
171+
2. Run [`npm publish`](https://docs.npmjs.com/cli/v7/commands/npm-publish) from
172+
your project directory
173+
3. Now you can run it with `npx yourpackagename`

bin.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ if (!satisfies(process.version, NODE_VERSION_RANGE)) {
1919
import { runMCPInstall, runMCPRemove } from './src/mcp';
2020
import type { CloudRegion, WizardOptions } from './src/utils/types';
2121
import { runWizard } from './src/run';
22+
import { server } from './e2e-tests/mocks/server';
23+
24+
if (process.env.NODE_ENV === 'test') {
25+
server.listen({
26+
onUnhandledRequest: 'bypass',
27+
});
28+
}
2229

2330
yargs(hideBin(process.argv))
2431
.env('POSTHOG_WIZARD')

bin/test-e2e

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Run tests (includes linting and formatting)
5+
echo "Running E2E tests..."
6+
pnpm test:e2e

e2e-tests/README.md

Lines changed: 69 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,92 @@
11
# End-to-end tests for PostHog wizard
22

3-
## Structure
3+
## Running Tests Locally
4+
5+
E2E Tests can be run locally from the root of the project with:
6+
7+
`pnpm test:e2e`
8+
9+
To run a specific test application
10+
11+
`pnpm test:e2e NextJS`
12+
13+
To record new fixtures:
14+
15+
`pnpm test:e2e-record`
16+
17+
## Writing Framework tests
18+
19+
Most of the E2E framework tests share a lot of common functionality such as
20+
terminal inputs and test setup/teardown. To accomodate for the large amount of
21+
frameworks we test, we expose the `createFrameworkTest` function.
22+
23+
For example usage, see one of the tests in `e2e-tests/tests` such as
24+
`nextjs-app-router.test.ts`. Each framework test also requires a test
25+
application to be defined in `test-applications`.
26+
27+
To adjust the default behaviour of the framework, also take a look at
28+
`DEFAULT_WIZARD_STEPS` in `e2e-tests/utils/framework-test-utils.ts`
29+
30+
## Fixture Generation
31+
32+
To be able to mock our LLM calls in the E2E tests, we need to have a realistic
33+
fixture. To generate them, we a call to the `/query` endpoint in PostHog. We
34+
save this response as a fixture in `e2e-tests/fixtures`. The filename represents
35+
the hashed request body to the endpoint. When we run the tests again, we reuse
36+
those fixtures.
37+
38+
Whenever the request body to the LLM change we also regenerate the fixture. The
39+
request body can change because of a few things:
440

5-
```
6-
test-applications/
7-
|---- nextjs-app-router-test-app/
8-
tests/
9-
|---- nextjs-app-router.test.ts
10-
```
41+
- Prompt:
42+
- The system propmt
43+
- The provided framework files
44+
- etc.
45+
- LLM Model
46+
- Response Schema
47+
48+
Because we use a set seed for the LLM, this means our tests are deterministic
49+
and actually reflective of how they would work in production as well.
50+
51+
Two environment variables control our fixture management:
52+
53+
`RECORD_FIXTURES` performs a `/query` request to create a fixture if no matching
54+
fixture is found the for the request body. Can be `true or false`. If `false`
55+
and no matching fixture is found, the test will fail.
56+
57+
`CLEANUP_UNUSED_FIXTURES` deletes fixtures that were not used during an E2E jest
58+
run. Should only be set to true when running all E2E tests. Can be `true` or
59+
`false`
1160

1261
### Utilities
1362

14-
`utils/` contains helpers such as the wizard runner, assertion tools and file modifiers that can be used in (`*.test.ts`).
63+
`utils/` contains helpers such as the wizard runner, assertion tools and file
64+
modifiers that can be used in (`*.test.ts`).
1565

1666
#### Helpers
1767

1868
- `startWizardInstance` - Starts a new instance of `WizardTestEnv`.
1969

2070
- `initGit` - Initializes a temporary git repository in the test project.
2171
- `cleanupGit` - Cleans up the temporary git repository in the test project.
22-
- `revertLocalChanges` - Reverts local changes (git tracked or untracked) in the test project.
72+
- `revertLocalChanges` - Reverts local changes (git tracked or untracked) in the
73+
test project.
2374

2475
- `createFile` - Creates a file (optionally with content) in the test project.
2576
- `modifyFile` - Modifies a file in the test project.
2677

2778
- `checkFileExists` - Checks if a file exists in the test project.
28-
- `checkPackageJson` - Checks if the package package exists in the dependencies of the test project's `package.json`.
79+
- `checkPackageJson` - Checks if the package package exists in the dependencies
80+
of the test project's `package.json`.
2981

3082
- `checkIfBuilds` - Checks if the test project builds successfully.
31-
- `checkIfRunsOnDevMode` - Checks if the test project runs on dev mode successfully.
32-
- `checkIfRunsOnProdMode` - Checks if the test project runs on prod mode successfully.
83+
- `checkIfRunsOnDevMode` - Checks if the test project runs on dev mode
84+
successfully.
85+
- `checkIfRunsOnProdMode` - Checks if the test project runs on prod mode
86+
successfully.
3387

3488
#### `WizardTestEnv`
3589

36-
`WizardTestEnv` is a class that can be used to run the PostHog wizard in a test environment. It provides methods to run the wizard with specific arguments and stdio.
37-
38-
## Running Tests Locally
39-
40-
First, you need to create a `.env` file set the environment variables from the `.env.example` file in the root of the project.
41-
42-
Tests can be run locally from the root of the project with:
43-
44-
`pnpm test:e2e`
45-
46-
To run a specific test application
47-
48-
`pnpm test:e2e NextJS`
49-
50-
## Writing Tests
51-
52-
Each test file should contain a single test suite that tests the PostHog wizard for a specific framework. The test suite should contain a `beforeAll` and `afterAll` function that starts and stops the test application respectively.
90+
`WizardTestEnv` is a class that can be used to run the PostHog wizard in a test
91+
environment. It provides methods to run the wizard with specific arguments and
92+
stdio.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"newContent": "import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { PostHogProvider } from 'posthog-js/react'\nimport './index.css'\nimport App from './App.tsx'\n\ncreateRoot(document.getElementById('root')!).render(\n <StrictMode>\n <PostHogProvider\n apiKey={import.meta.env.VITE_PUBLIC_POSTHOG_KEY}\n options={{\n api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,\n defaults: '2025-05-24',\n capture_exceptions: true,\n debug: import.meta.env.MODE === 'development',\n }}\n >\n <App />\n </PostHogProvider>\n </StrictMode>,\n)\n"
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"files": [
3+
"src/main.tsx"
4+
]
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"newContent": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n async rewrites() {\n return [\n {\n source: \"/ingest/static/:path*\",\n destination: \"http://localhost:8010/static/:path*\",\n },\n {\n source: \"/ingest/:path*\",\n destination: \"http://localhost:8010/:path*\",\n },\n {\n source: \"/ingest/decide\",\n destination: \"http://localhost:8010/decide\",\n },\n ];\n },\n // This is required to support PostHog trailing slash API requests\n skipTrailingSlashRedirect: true,\n};\n\nexport default nextConfig;\n"
3+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"files": [
3+
"src/components/PostHogProvider.tsx",
4+
"src/lib/posthog.ts",
5+
"next.config.mjs",
6+
"src/app/layout.tsx"
7+
]
8+
}

0 commit comments

Comments
 (0)