Skip to content

Commit 05f827d

Browse files
authored
Merge pull request #34 from PostHog/e2e-tests
e2e tests
2 parents ec39084 + 8053d34 commit 05f827d

File tree

9 files changed

+274
-8
lines changed

9 files changed

+274
-8
lines changed

.github/workflows/e2e.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: PostHog Examples - E2E tests
2+
permissions:
3+
contents: read
4+
5+
on:
6+
schedule:
7+
# Runs nightly at 12am PST (which is 8am UTC)
8+
- cron: '0 8 * * *'
9+
workflow_dispatch:
10+
11+
jobs:
12+
discover:
13+
runs-on: ubuntu-latest
14+
outputs:
15+
examples: ${{ steps.set-examples.outputs.examples }}
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Discover examples with Playwright configs
20+
id: set-examples
21+
run: |
22+
examples=$(find basics -maxdepth 2 -name "playwright.config.ts" -exec dirname {} \; | sed 's|^basics/||' | jq -R -s -c 'split("\n")[:-1]')
23+
echo "examples=$examples" >> $GITHUB_OUTPUT
24+
echo "Found examples: $examples"
25+
26+
test:
27+
needs: discover
28+
if: needs.discover.outputs.examples != '[]'
29+
timeout-minutes: 60
30+
runs-on: ubuntu-latest
31+
strategy:
32+
fail-fast: false
33+
matrix:
34+
example: ${{ fromJson(needs.discover.outputs.examples) }}
35+
steps:
36+
- uses: actions/checkout@v4
37+
38+
- uses: actions/setup-node@v4
39+
with:
40+
node-version: lts/*
41+
42+
- uses: pnpm/action-setup@v4
43+
with:
44+
version: latest
45+
46+
- name: Install dependencies
47+
run: |
48+
cd basics/${{ matrix.example }}
49+
pnpm install
50+
51+
- name: Install Playwright Browsers
52+
run: |
53+
cd basics/${{ matrix.example }}
54+
pnpm exec playwright install chromium --with-deps
55+
56+
- name: Run E2E Playwright tests
57+
run: |
58+
cd basics/${{ matrix.example }}
59+
pnpm run test:e2e
60+
env:
61+
NEXT_PUBLIC_POSTHOG_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_KEY }}
62+
NEXT_PUBLIC_POSTHOG_HOST: ${{ vars.NEXT_PUBLIC_POSTHOG_HOST }}
63+
PERSONAL_ACCESS_KEY: ${{ secrets.PERSONAL_ACCESS_KEY }}
64+
PROJECT_ID: ${{ vars.PROJECT_ID }}
65+
66+
- name: Send failure event to PostHog
67+
if: failure()
68+
run: |
69+
curl -X POST https://webhooks.us.posthog.com/public/webhooks/019a7a81-7961-0000-d3e3-b5f34cc2a32b \
70+
-H "Content-Type: application/json" \
71+
-d '{
72+
"event": "posthog-examples-repo-test-failure",
73+
"commitSha": "${{ github.sha }}",
74+
"jobStatus": "${{ job.status }}",
75+
"commitMessage": "${{ github.event.head_commit.message }}",
76+
"commitAuthor": "${{ github.event.head_commit.author.name }}",
77+
"ref": "${{ github.ref }}",
78+
"workflow": "${{ github.workflow }}",
79+
"runId": "${{ github.run_id }}",
80+
"runNumber": "${{ github.run_number }}",
81+
"jobUrl": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
82+
"matrixExample": "${{ matrix.example }}"
83+
}'
84+
85+
- uses: actions/upload-artifact@v4
86+
if: always()
87+
with:
88+
name: playwright-report-${{ matrix.example }}
89+
path: basics/${{ matrix.example }}/playwright-report/
90+
retention-days: 30
91+
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: PostHog Examples - Playwright Tests
1+
name: PostHog Examples - Integration tests
22
permissions:
33
contents: read
44

@@ -10,6 +10,7 @@ on:
1010
schedule:
1111
# Runs nightly at 12am PST (which is 8am UTC)
1212
- cron: '0 8 * * *'
13+
workflow_dispatch:
1314

1415
jobs:
1516
discover:
@@ -56,19 +57,36 @@ jobs:
5657
cd basics/${{ matrix.example }}
5758
pnpm exec playwright install chromium --with-deps
5859
59-
- name: Run Playwright tests
60+
- name: Run Integration Playwright tests
6061
run: |
6162
cd basics/${{ matrix.example }}
62-
pnpm exec playwright test
63+
pnpm run test:integration
6364
env:
6465
NEXT_PUBLIC_POSTHOG_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_KEY }}
6566
NEXT_PUBLIC_POSTHOG_HOST: ${{ vars.NEXT_PUBLIC_POSTHOG_HOST }}
6667

68+
- name: Send failure event to PostHog
69+
if: failure()
70+
run: |
71+
curl -X POST https://webhooks.us.posthog.com/public/webhooks/019a7a81-7961-0000-d3e3-b5f34cc2a32b \
72+
-H "Content-Type: application/json" \
73+
-d '{
74+
"event": "posthog-examples-repo-test-failure",
75+
"commitSha": "${{ github.sha }}",
76+
"jobStatus": "${{ job.status }}",
77+
"commitMessage": "${{ github.event.head_commit.message }}",
78+
"commitAuthor": "${{ github.event.head_commit.author.name }}",
79+
"ref": "${{ github.ref }}",
80+
"workflow": "${{ github.workflow }}",
81+
"runId": "${{ github.run_id }}",
82+
"runNumber": "${{ github.run_number }}",
83+
"jobUrl": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
84+
"example": "${{ matrix.example }}"
85+
}'
86+
6787
- uses: actions/upload-artifact@v4
6888
if: always()
6989
with:
7090
name: playwright-report-${{ matrix.example }}
7191
path: basics/${{ matrix.example }}/playwright-report/
7292
retention-days: 30
73-
74-
# TODO: report to PostHog which will warn in channel for failure.

basics/next-app-router/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"lint": "eslint",
1010
"postinstall": "playwright install chromium",
1111
"test": "playwright test",
12+
"test:integration": "playwright test example.spec.ts",
13+
"test:e2e": "playwright test e2e-query.spec.ts",
1214
"test:ui": "playwright test --ui",
1315
"test:report": "playwright show-report"
1416
},
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// @ignoreFile
2+
import { test, expect } from '@playwright/test';
3+
import packageJson from '../package.json';
4+
5+
test('e2e-query', async () => {
6+
const yesterday = new Date();
7+
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
8+
const dateStr = yesterday.toISOString().split('T')[0];
9+
10+
const packageName = packageJson.name;
11+
const yesterdayUsername = `${packageName}-${dateStr}-test-user`;
12+
13+
const posthogApiKey = process.env.PERSONAL_ACCESS_KEY;
14+
const posthogProjectId = process.env.PROJECT_ID;
15+
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.posthog.com';
16+
17+
if (!posthogApiKey || !posthogProjectId) {
18+
throw new Error('PERSONAL_ACCESS_KEY and POSTHOG_PROJECT_ID environment variables are required');
19+
}
20+
21+
const expectedEvents = ['server_login', '$web_vitals', 'user_logged_in', '$identify'];
22+
23+
const query = {
24+
kind: 'HogQLQuery',
25+
query: `SELECT * FROM events WHERE distinct_id = '${yesterdayUsername}'`
26+
};
27+
28+
const url = `${posthogHost}/api/projects/${posthogProjectId}/query/`;
29+
const response = await fetch(url, {
30+
method: 'POST',
31+
headers: {
32+
'Content-Type': 'application/json',
33+
'Authorization': `Bearer ${posthogApiKey}`
34+
},
35+
body: JSON.stringify({
36+
query: query,
37+
name: 'e2e-query-yesterday-user-events'
38+
})
39+
});
40+
41+
if (!response.ok) {
42+
const errorText = await response.text();
43+
throw new Error(`PostHog API request failed: ${response.status} ${response.statusText}\n${errorText}`);
44+
}
45+
46+
const data = await response.json();
47+
48+
expect(data).toHaveProperty('results');
49+
expect(Array.isArray(data.results)).toBe(true);
50+
51+
const columns = data.columns || [];
52+
const eventColumnIndex = columns.indexOf('event');
53+
54+
expect(eventColumnIndex).not.toBe(-1);
55+
expect(eventColumnIndex).toBeGreaterThanOrEqual(0);
56+
57+
const foundEvents = data.results
58+
.map((row: any[]) => row[eventColumnIndex])
59+
.filter((event: string) => event !== null && event !== undefined);
60+
61+
expectedEvents.forEach(expectedEvent => {
62+
expect(foundEvents).toContain(expectedEvent);
63+
});
64+
});
65+

basics/next-app-router/tests/example.spec.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ignoreFile
22
import { test, expect } from '@playwright/test';
3+
import packageJson from '../package.json';
34

45
// Global variables to store the generated username
56
let generatedUsername: string;
@@ -211,8 +212,12 @@ async function loginAsTestAgent(page: any) {
211212

212213
const randomPassword = 'test_password_123';
213214

214-
generatedUsername = 'test_user';
215-
215+
// Generate username in format: {package.json name}-{yyyy-mm-dd}-test-user
216+
const packageName = packageJson.name;
217+
const today = new Date();
218+
const dateStr = today.toISOString().split('T')[0]; // yyyy-mm-dd format (UTC)
219+
generatedUsername = `${packageName}-${dateStr}-test-user`;
220+
216221
// Fill in the username field
217222
await page.getByLabel('Username:').fill(generatedUsername);
218223

basics/next-pages-router/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"lint": "eslint",
1010
"postinstall": "playwright install chromium",
1111
"test": "playwright test",
12+
"test:integration": "playwright test example.spec.ts",
13+
"test:e2e": "playwright test e2e-query.spec.ts",
1214
"test:ui": "playwright test --ui",
1315
"test:report": "playwright show-report"
1416
},
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// @ignoreFile
2+
import { test, expect } from '@playwright/test';
3+
import packageJson from '../package.json';
4+
5+
test('e2e-query', async () => {
6+
const yesterday = new Date();
7+
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
8+
const dateStr = yesterday.toISOString().split('T')[0];
9+
10+
const packageName = packageJson.name;
11+
const yesterdayUsername = `${packageName}-${dateStr}-test-user`;
12+
13+
const posthogApiKey = process.env.PERSONAL_ACCESS_KEY;
14+
const posthogProjectId = process.env.PROJECT_ID;
15+
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.posthog.com';
16+
17+
if (!posthogApiKey || !posthogProjectId) {
18+
throw new Error('PERSONAL_ACCESS_KEY and POSTHOG_PROJECT_ID environment variables are required');
19+
}
20+
21+
const expectedEvents = ['server_login', '$web_vitals', 'user_logged_in', '$identify'];
22+
23+
const query = {
24+
kind: 'HogQLQuery',
25+
query: `SELECT * FROM events WHERE distinct_id = '${yesterdayUsername}'`
26+
};
27+
28+
const url = `${posthogHost}/api/projects/${posthogProjectId}/query/`;
29+
const response = await fetch(url, {
30+
method: 'POST',
31+
headers: {
32+
'Content-Type': 'application/json',
33+
'Authorization': `Bearer ${posthogApiKey}`
34+
},
35+
body: JSON.stringify({
36+
query: query,
37+
name: 'e2e-query-yesterday-user-events'
38+
})
39+
});
40+
41+
if (!response.ok) {
42+
const errorText = await response.text();
43+
throw new Error(`PostHog API request failed: ${response.status} ${response.statusText}\n${errorText}`);
44+
}
45+
46+
const data = await response.json();
47+
48+
expect(data).toHaveProperty('results');
49+
expect(Array.isArray(data.results)).toBe(true);
50+
51+
const columns = data.columns || [];
52+
const eventColumnIndex = columns.indexOf('event');
53+
54+
expect(eventColumnIndex).not.toBe(-1);
55+
expect(eventColumnIndex).toBeGreaterThanOrEqual(0);
56+
57+
const foundEvents = data.results
58+
.map((row: any[]) => row[eventColumnIndex])
59+
.filter((event: string) => event !== null && event !== undefined);
60+
61+
expectedEvents.forEach(expectedEvent => {
62+
expect(foundEvents).toContain(expectedEvent);
63+
});
64+
});

basics/next-pages-router/tests/example.spec.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ignoreFile
22
import { test, expect } from '@playwright/test';
3+
import packageJson from '../package.json';
34

45
// Global variables to store the generated username
56
let generatedUsername: string;
@@ -42,6 +43,11 @@ test('verify user is logged in', async ({ page }) => {
4243
verifyExpectedEvents();
4344
});
4445

46+
test.afterEach(async () => {
47+
// Wait 5 seconds after each test
48+
await new Promise(resolve => setTimeout(resolve, 5000));
49+
});
50+
4551
// Helper functions
4652

4753
// PostHog Events Snapshot Helper - Using Playwright's built-in snapshot functionality
@@ -211,7 +217,11 @@ async function loginAsTestAgent(page: any) {
211217

212218
const randomPassword = 'test_password_123';
213219

214-
generatedUsername = 'test_user';
220+
// Generate username in format: {package.json name}-{yyyy-mm-dd}-test-user
221+
const packageName = packageJson.name;
222+
const today = new Date();
223+
const dateStr = today.toISOString().split('T')[0]; // yyyy-mm-dd format (UTC)
224+
generatedUsername = `${packageName}-${dateStr}-test-user`;
215225

216226
// Fill in the username field
217227
await page.getByLabel('Username:').fill(generatedUsername);
@@ -225,3 +235,4 @@ async function loginAsTestAgent(page: any) {
225235
// Expect to see the welcome message after successful login
226236
await expect(page.getByText(`Welcome back, ${generatedUsername}!`)).toBeVisible();
227237
}
238+

test.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
curl -X POST https://us.posthog.com/api/environments/198052/endpoints/test/run \
2+
-H "Authorization: Bearer phx_ZlzpdDkd92DHfGH8n8rZ0xJFwySteo5viu9n4isVbjJItF8" \
3+
-H "Content-Type: application/json" \
4+
-d '{
5+
"variables_values": {
6+
"distinct_id": "posthog-pages-router-example-2025-11-11-test-user"
7+
}
8+
}'

0 commit comments

Comments
 (0)