Skip to content

Commit 68918e4

Browse files
authored
chore: Run playwright tests with mongo backend (#1493)
Enables broader testing Fixes: HDX-3069 To test: - By default `make e2e` runs playwright tests with a docker compose for mongo - To test the local-only mode, run `make e2e local=true` - Since we manage play.hyperdx.io, I envision us running both commands on release
1 parent 7a53880 commit 68918e4

File tree

14 files changed

+1062
-190
lines changed

14 files changed

+1062
-190
lines changed

.github/workflows/main.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ jobs:
104104
timeout-minutes: 15
105105
container:
106106
image: mcr.microsoft.com/playwright:v1.57.0-jammy
107+
options: --network-alias playwright
108+
services:
109+
mongodb:
110+
image: mongo:5.0.14-focal
111+
options: >-
112+
--health-cmd "mongosh --quiet --eval 'db.adminCommand({ping: 1});
113+
db.getSiblingDB(\"test\").test.insertOne({_id: \"hc\"});
114+
db.getSiblingDB(\"test\").test.deleteOne({_id: \"hc\"})'"
115+
--health-interval 10s --health-timeout 5s --health-retries 10
116+
--health-start-period 10s
107117
permissions:
108118
contents: read
109119
pull-requests: write
@@ -129,7 +139,15 @@ jobs:
129139
- name: Build dependencies
130140
run: npx nx run-many -t ci:build
131141

132-
- name: Run Playwright tests
142+
- name: Run Playwright tests (full-stack mode)
143+
# MongoDB service health check ensures it's ready before this step runs
144+
# Note: Tests use ClickHouse demo instance (otel_demo with empty password)
145+
# This is intentionally public - it's ClickHouse's read-only demo instance
146+
env:
147+
E2E_FULLSTACK: 'true'
148+
E2E_UNIQUE_USER: 'true'
149+
E2E_API_HEALTH_CHECK_MAX_RETRIES: '60'
150+
MONGO_URI: mongodb://mongodb:27017/hyperdx-e2e
133151
run: |
134152
cd packages/app
135153
yarn test:e2e --shard=${{ matrix.shard }}/4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ e2e/cypress/results
5959
**/test-results/
6060
**/playwright-report/
6161
**/playwright/.cache/
62+
**/.auth/
6263

6364
# scripts
6465
scripts/*.csv

Makefile

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,23 @@ ci-unit:
6767

6868
.PHONY: e2e
6969
e2e:
70-
@if [ -z "$(tags)" ]; then \
71-
echo "Running all E2E tests in local mode..."; \
72-
cd packages/app && yarn test:e2e; \
70+
@# Run full-stack by default (MongoDB + API + demo ClickHouse)
71+
@# Use 'make e2e local=true' to skip MongoDB and run local mode only
72+
@if [ "$(local)" = "true" ]; then \
73+
if [ -z "$(tags)" ]; then \
74+
./scripts/test-e2e.sh --local; \
75+
else \
76+
./scripts/test-e2e.sh --local --tags "$(tags)"; \
77+
fi; \
7378
else \
74-
echo "Running E2E tests with tags: $(tags)"; \
75-
cd packages/app && yarn test:e2e --grep "$(tags)"; \
79+
if [ -z "$(tags)" ]; then \
80+
./scripts/test-e2e.sh; \
81+
else \
82+
./scripts/test-e2e.sh --tags "$(tags)"; \
83+
fi; \
7684
fi
7785

86+
7887
# TODO: check db connections before running the migration CLIs
7988
.PHONY: dev-migrate-db
8089
dev-migrate-db:

packages/api/.env.e2e

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# ClickHouse connection to public demo instance
2+
# Password is intentionally empty - this is a read-only public demo instance
3+
CLICKHOUSE_HOST=https://sql-clickhouse.clickhouse.com
4+
CLICKHOUSE_PASSWORD=
5+
CLICKHOUSE_USER=otel_demo
6+
RUN_SCHEDULED_TASKS_EXTERNALLY=true
7+
FRONTEND_URL=http://localhost:28081
8+
# MongoDB connection string
9+
# Local: Uses custom port 29998 to avoid conflicts with existing MongoDB instances
10+
# CI: Overridden via MONGO_URI env var to mongodb://mongodb:27017/hyperdx-e2e
11+
MONGO_URI=mongodb://localhost:29998/hyperdx-e2e
12+
NODE_ENV=test
13+
PORT=29000
14+
OPAMP_PORT=24320
15+
16+
# Auto-create default connections and sources for new teams
17+
# Uses public demo ClickHouse instance with pre-populated data
18+
DEFAULT_CONNECTIONS='[{"name":"Demo ClickHouse","host":"https://sql-clickhouse.clickhouse.com","username":"otel_demo","password":""}]'
19+
DEFAULT_SOURCES='[{"from":{"databaseName":"otel_v2","tableName":"otel_logs"},"kind":"log","timestampValueExpression":"TimestampTime","name":"Demo Logs","displayedTimestampValueExpression":"Timestamp","implicitColumnExpression":"Body","serviceNameExpression":"ServiceName","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","defaultTableSelectExpression":"Timestamp,ServiceName,SeverityText,Body","severityTextExpression":"SeverityText","traceIdExpression":"TraceId","spanIdExpression":"SpanId","connection":"Demo ClickHouse","traceSourceId":"Traces","metricSourceId":"Demo Metrics"},{"from":{"databaseName":"otel_v2","tableName":"otel_traces"},"kind":"trace","timestampValueExpression":"Timestamp","name":"Demo Traces","displayedTimestampValueExpression":"Timestamp","implicitColumnExpression":"SpanName","serviceNameExpression":"ServiceName","eventAttributesExpression":"SpanAttributes","resourceAttributesExpression":"ResourceAttributes","defaultTableSelectExpression":"Timestamp,ServiceName,StatusCode,round(Duration/1e6),SpanName","traceIdExpression":"TraceId","spanIdExpression":"SpanId","durationExpression":"Duration","durationPrecision":9,"parentSpanIdExpression":"ParentSpanId","spanNameExpression":"SpanName","spanKindExpression":"SpanKind","statusCodeExpression":"StatusCode","statusMessageExpression":"StatusMessage","connection":"Demo ClickHouse","logSourceId":"Demo Logs","metricSourceId":"Demo Metrics"},{"from":{"databaseName":"otel_v2","tableName":""},"kind":"metric","timestampValueExpression":"TimeUnix","name":"Demo Metrics","resourceAttributesExpression":"ResourceAttributes","serviceNameExpression":"ServiceName","metricTables":{"gauge":"otel_metrics_gauge","histogram":"otel_metrics_histogram","sum":"otel_metrics_sum","summary":"otel_metrics_summary","exponential histogram":"otel_metrics_exponential_histogram"},"connection":"Demo ClickHouse","logSourceId":"Demo Logs","traceSourceId":"Demo Traces"}]'

packages/app/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@
66
next.config.original.js
77

88
public/__ENV.js
9+
10+
# E2E test authentication state
11+
tests/e2e/.auth/

packages/app/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
"ci:lint": "yarn lint && yarn tsc --noEmit && yarn lint:styles --quiet",
1818
"ci:unit": "jest --ci --coverage",
1919
"dev:unit": "jest --watchAll --detectOpenHandles",
20-
"test:e2e": "playwright test",
21-
"test:e2e:ui": "playwright test --ui",
22-
"test:e2e:debug": "playwright test --debug",
20+
"test:e2e": "node scripts/run-e2e.js",
2321
"test:e2e:ci": "../../scripts/test-e2e-ci.sh",
2422
"storybook": "storybook dev -p 6006",
2523
"storybook:build": "storybook build",

packages/app/playwright.config.ts

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
import { defineConfig, devices } from '@playwright/test';
2+
import path from 'path';
3+
4+
// Check if we should use full-stack mode (with backend)
5+
const USE_FULLSTACK = process.env.E2E_FULLSTACK === 'true';
6+
const AUTH_FILE = path.join(__dirname, 'tests/e2e/.auth/user.json');
7+
8+
// Timeout configuration constants (in milliseconds)
9+
const TEST_TIMEOUT_MS = 60 * 1000; // 60 seconds per test
10+
const API_SERVER_STARTUP_TIMEOUT_MS = 120 * 1000; // 2 minutes for API to start
11+
const APP_SERVER_STARTUP_TIMEOUT_MS = 180 * 1000; // 3 minutes for Next.js build + start
212

313
/**
414
* @see https://playwright.dev/docs/test-configuration
515
*/
616
export default defineConfig({
717
testDir: './tests/e2e',
818
/* Global setup to ensure server is ready */
9-
globalSetup: require.resolve('./global-setup.js'),
19+
globalSetup: USE_FULLSTACK
20+
? require.resolve('./tests/e2e/global-setup-fullstack.ts')
21+
: require.resolve('./global-setup.js'),
1022
/* Run tests in files in parallel */
1123
fullyParallel: true,
1224
/* Fail the build on CI if you accidentally left test.only in the source code. */
@@ -26,7 +38,9 @@ export default defineConfig({
2638
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
2739
use: {
2840
/* Base URL to use in actions like `await page.goto('/')`. */
29-
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8081',
41+
baseURL: USE_FULLSTACK
42+
? 'http://localhost:28081'
43+
: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8081',
3044
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
3145
trace: 'on-first-retry',
3246
/* Take screenshot on failure */
@@ -36,27 +50,57 @@ export default defineConfig({
3650
},
3751

3852
/* Global test timeout - CI needs more time than local */
39-
timeout: 60 * 1000,
53+
timeout: TEST_TIMEOUT_MS,
4054

4155
/* Configure projects for different test environments */
4256
projects: [
4357
{
4458
name: 'chromium',
4559
use: {
4660
...devices['Desktop Chrome'],
61+
// Use saved authentication state for full-stack mode
62+
...(USE_FULLSTACK && {
63+
storageState: AUTH_FILE,
64+
}),
4765
},
4866
},
4967
],
5068

5169
/* Run your local dev server before starting the tests */
52-
webServer: {
53-
command: process.env.CI
54-
? 'NEXT_PUBLIC_IS_LOCAL_MODE=true yarn build && NEXT_PUBLIC_IS_LOCAL_MODE=true PORT=8081 yarn start'
55-
: 'NEXT_PUBLIC_IS_LOCAL_MODE=true NEXT_TELEMETRY_DISABLED=1 PORT=8081 yarn run dev',
56-
port: 8081,
57-
reuseExistingServer: !process.env.CI,
58-
timeout: 180 * 1000,
59-
stdout: 'pipe',
60-
stderr: 'pipe',
61-
},
70+
// Note: webServer array syntax requires Playwright v1.32.0+ (current: v1.57.0)
71+
webServer: USE_FULLSTACK
72+
? [
73+
// Full-stack mode: Start API and App servers (infrastructure started separately)
74+
{
75+
// Loads configuration from .env.e2e (connections, settings)
76+
// Environment variables (MONGO_URI, etc.) can override .env.e2e values
77+
command: `cd ../api && ${process.env.MONGO_URI ? `MONGO_URI="${process.env.MONGO_URI}"` : ''} DOTENV_CONFIG_PATH=.env.e2e npx ts-node --transpile-only -r tsconfig-paths/register -r dotenv-expand/config -r @hyperdx/node-opentelemetry/build/src/tracing src/index.ts`,
78+
port: 29000,
79+
reuseExistingServer: !process.env.CI,
80+
timeout: API_SERVER_STARTUP_TIMEOUT_MS,
81+
stdout: 'pipe',
82+
stderr: 'pipe',
83+
},
84+
{
85+
command: process.env.CI
86+
? 'SERVER_URL=http://localhost:29000 PORT=28081 yarn build && SERVER_URL=http://localhost:29000 PORT=28081 yarn start'
87+
: 'SERVER_URL=http://localhost:29000 PORT=28081 NEXT_TELEMETRY_DISABLED=1 yarn run dev',
88+
port: 28081,
89+
reuseExistingServer: !process.env.CI,
90+
timeout: APP_SERVER_STARTUP_TIMEOUT_MS,
91+
stdout: 'pipe',
92+
stderr: 'pipe',
93+
},
94+
]
95+
: {
96+
// Local mode: Frontend only
97+
command: process.env.CI
98+
? 'NEXT_PUBLIC_IS_LOCAL_MODE=true yarn build && NEXT_PUBLIC_IS_LOCAL_MODE=true PORT=8081 yarn start'
99+
: 'NEXT_PUBLIC_IS_LOCAL_MODE=true NEXT_TELEMETRY_DISABLED=1 PORT=8081 yarn run dev',
100+
port: 8081,
101+
reuseExistingServer: !process.env.CI,
102+
timeout: APP_SERVER_STARTUP_TIMEOUT_MS,
103+
stdout: 'pipe',
104+
stderr: 'pipe',
105+
},
62106
});

packages/app/scripts/run-e2e.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* E2E Test Runner
5+
*
6+
* Usage:
7+
* yarn test:e2e # Run all tests (full-stack mode, default)
8+
* yarn test:e2e --local # Run frontend-only tests
9+
* yarn test:e2e --ui # Open Playwright UI (full-stack)
10+
* yarn test:e2e --ui --local # Open UI (local mode)
11+
* yarn test:e2e --debug # Debug mode (full-stack)
12+
* yarn test:e2e --debug --local # Debug (local mode)
13+
*/
14+
15+
import { spawn } from 'child_process';
16+
import path from 'path';
17+
import { fileURLToPath } from 'url';
18+
19+
// Get __dirname equivalent in ES modules
20+
const __filename = fileURLToPath(import.meta.url);
21+
const __dirname = path.dirname(__filename);
22+
23+
// Parse command line arguments
24+
const args = process.argv.slice(2);
25+
const useLocal = args.includes('--local');
26+
const useUI = args.includes('--ui');
27+
const useDebug = args.includes('--debug');
28+
29+
// Remove our custom flags from args
30+
const playwrightArgs = args.filter(
31+
arg => !['--local', '--ui', '--debug'].includes(arg),
32+
);
33+
34+
// Build playwright command
35+
const playwrightCmd = ['playwright', 'test'];
36+
37+
// Add mode flags
38+
if (useUI) {
39+
playwrightCmd.push('--ui');
40+
}
41+
if (useDebug) {
42+
playwrightCmd.push('--debug');
43+
}
44+
45+
// Add grep-invert for local mode (exclude @full-stack tests)
46+
if (useLocal) {
47+
playwrightCmd.push('--grep-invert', '@full-stack');
48+
}
49+
50+
// Add any additional playwright arguments
51+
playwrightCmd.push(...playwrightArgs);
52+
53+
// Set environment variables
54+
const env = {
55+
...process.env,
56+
...(!useLocal && { E2E_FULLSTACK: 'true' }),
57+
};
58+
59+
// Run playwright
60+
// eslint-disable-next-line no-console
61+
console.info(`Running: ${playwrightCmd.join(' ')}`);
62+
// eslint-disable-next-line no-console
63+
console.info(`Mode: ${useLocal ? 'Local (frontend only)' : 'Full-stack'}`);
64+
65+
const child = spawn('npx', playwrightCmd, {
66+
stdio: 'inherit',
67+
shell: true,
68+
env,
69+
cwd: path.join(__dirname, '..'),
70+
});
71+
72+
child.on('exit', code => {
73+
process.exit(code);
74+
});

0 commit comments

Comments
 (0)