Skip to content

Commit 4938861

Browse files
committed
e2e tests
1 parent 21fd2b7 commit 4938861

File tree

9 files changed

+289
-6
lines changed

9 files changed

+289
-6
lines changed

.github/workflows/e2e.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
- uses: actions/upload-artifact@v4
67+
if: always()
68+
with:
69+
name: playwright-report-${{ matrix.example }}
70+
path: basics/${{ matrix.example }}/playwright-report/
71+
retention-days: 30
72+
73+
# TODO: report to PostHog which will warn in channel for failure.
Lines changed: 4 additions & 3 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,10 +57,10 @@ 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 }}

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: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// @ignoreFile
2+
import { test, expect } from '@playwright/test';
3+
import packageJson from '../package.json';
4+
5+
test('e2e-query', async () => {
6+
// Calculate yesterday's date in yyyy-mm-dd format (UTC)
7+
const yesterday = new Date();
8+
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
9+
const dateStr = yesterday.toISOString().split('T')[0]; // yyyy-mm-dd format
10+
11+
// Generate yesterday's username in format: {package.json name}-{yyyy-mm-dd}-test-user
12+
const packageName = packageJson.name;
13+
const yesterdayUsername = `${packageName}-${dateStr}-test-user`;
14+
15+
console.log(`Querying for distinct_id: ${yesterdayUsername}`);
16+
17+
// Get PostHog credentials from environment variables
18+
const posthogApiKey = process.env.PERSONAL_ACCESS_KEY;
19+
const posthogProjectId = process.env.PROJECT_ID;
20+
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.posthog.com';
21+
22+
if (!posthogApiKey || !posthogProjectId) {
23+
throw new Error('PERSONAL_ACCESS_KEY and POSTHOG_PROJECT_ID environment variables are required');
24+
}
25+
26+
// Expected events to validate
27+
const expectedEvents = ['server_login', '$web_vitals', 'user_logged_in', '$identify'];
28+
29+
// Construct the query
30+
const query = {
31+
kind: 'HogQLQuery',
32+
query: `SELECT * FROM events WHERE distinct_id = '${yesterdayUsername}'`
33+
};
34+
35+
// Make the API request
36+
const url = `${posthogHost}/api/projects/${posthogProjectId}/query/`;
37+
const response = await fetch(url, {
38+
method: 'POST',
39+
headers: {
40+
'Content-Type': 'application/json',
41+
'Authorization': `Bearer ${posthogApiKey}`
42+
},
43+
body: JSON.stringify({
44+
query: query,
45+
name: 'e2e-query-yesterday-user-events'
46+
})
47+
});
48+
49+
if (!response.ok) {
50+
const errorText = await response.text();
51+
throw new Error(`PostHog API request failed: ${response.status} ${response.statusText}\n${errorText}`);
52+
}
53+
54+
const data = await response.json();
55+
56+
console.log('Query results:', JSON.stringify(data, null, 2));
57+
58+
// Assert that we got results
59+
expect(data).toHaveProperty('results');
60+
expect(Array.isArray(data.results)).toBe(true);
61+
62+
// Extract events from results
63+
// Results are arrays where each row corresponds to a column in the columns array
64+
// We need to find the index of the 'event' column from the columns array
65+
const columns = data.columns || [];
66+
const eventColumnIndex = columns.indexOf('event');
67+
68+
expect(eventColumnIndex).not.toBe(-1);
69+
expect(eventColumnIndex).toBeGreaterThanOrEqual(0);
70+
71+
const foundEvents = data.results
72+
.map((row: any[]) => row[eventColumnIndex])
73+
.filter((event: string) => event !== null && event !== undefined);
74+
75+
console.log(`✅ Found ${data.results.length} event(s) for yesterday's user`);
76+
console.log(`📋 Found events: ${foundEvents.join(', ')}`);
77+
78+
// Validate that all expected events are present
79+
expectedEvents.forEach(expectedEvent => {
80+
expect(foundEvents).toContain(expectedEvent);
81+
});
82+
83+
console.log(`✅ All expected events found: ${expectedEvents.join(', ')}`);
84+
85+
// Log all events for debugging
86+
data.results.forEach((result: any, index: number) => {
87+
const eventName = result[eventColumnIndex];
88+
console.log(`Event ${index + 1}: ${eventName}`);
89+
});
90+
});
91+

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: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// @ignoreFile
2+
import { test, expect } from '@playwright/test';
3+
import packageJson from '../package.json';
4+
5+
test('e2e-query', async () => {
6+
// Calculate yesterday's date in yyyy-mm-dd format (UTC)
7+
const yesterday = new Date();
8+
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
9+
const dateStr = yesterday.toISOString().split('T')[0]; // yyyy-mm-dd format
10+
11+
// Generate yesterday's username in format: {package.json name}-{yyyy-mm-dd}-test-user
12+
const packageName = packageJson.name;
13+
const yesterdayUsername = `${packageName}-${dateStr}-test-user`;
14+
15+
console.log(`Querying for distinct_id: ${yesterdayUsername}`);
16+
17+
// Get PostHog credentials from environment variables
18+
const posthogApiKey = process.env.PERSONAL_ACCESS_KEY;
19+
const posthogProjectId = process.env.PROJECT_ID;
20+
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.posthog.com';
21+
22+
if (!posthogApiKey || !posthogProjectId) {
23+
throw new Error('PERSONAL_ACCESS_KEY and POSTHOG_PROJECT_ID environment variables are required');
24+
}
25+
26+
// Expected events to validate
27+
const expectedEvents = ['server_login', '$web_vitals', 'user_logged_in', '$identify'];
28+
29+
// Construct the query
30+
const query = {
31+
kind: 'HogQLQuery',
32+
query: `SELECT * FROM events WHERE distinct_id = '${yesterdayUsername}'`
33+
};
34+
35+
// Make the API request
36+
const url = `${posthogHost}/api/projects/${posthogProjectId}/query/`;
37+
const response = await fetch(url, {
38+
method: 'POST',
39+
headers: {
40+
'Content-Type': 'application/json',
41+
'Authorization': `Bearer ${posthogApiKey}`
42+
},
43+
body: JSON.stringify({
44+
query: query,
45+
name: 'e2e-query-yesterday-user-events'
46+
})
47+
});
48+
49+
if (!response.ok) {
50+
const errorText = await response.text();
51+
throw new Error(`PostHog API request failed: ${response.status} ${response.statusText}\n${errorText}`);
52+
}
53+
54+
const data = await response.json();
55+
56+
console.log('Query results:', JSON.stringify(data, null, 2));
57+
58+
// Assert that we got results
59+
expect(data).toHaveProperty('results');
60+
expect(Array.isArray(data.results)).toBe(true);
61+
62+
// Extract events from results
63+
// Results are arrays where each row corresponds to a column in the columns array
64+
// We need to find the index of the 'event' column from the columns array
65+
const columns = data.columns || [];
66+
const eventColumnIndex = columns.indexOf('event');
67+
68+
expect(eventColumnIndex).not.toBe(-1);
69+
expect(eventColumnIndex).toBeGreaterThanOrEqual(0);
70+
71+
const foundEvents = data.results
72+
.map((row: any[]) => row[eventColumnIndex])
73+
.filter((event: string) => event !== null && event !== undefined);
74+
75+
console.log(`✅ Found ${data.results.length} event(s) for yesterday's user`);
76+
console.log(`📋 Found events: ${foundEvents.join(', ')}`);
77+
78+
// Validate that all expected events are present
79+
expectedEvents.forEach(expectedEvent => {
80+
expect(foundEvents).toContain(expectedEvent);
81+
});
82+
83+
console.log(`✅ All expected events found: ${expectedEvents.join(', ')}`);
84+
85+
// Log all events for debugging
86+
data.results.forEach((result: any, index: number) => {
87+
const eventName = result[eventColumnIndex];
88+
console.log(`Event ${index + 1}: ${eventName}`);
89+
});
90+
});

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)