Skip to content

Commit ac8fd09

Browse files
committed
feat: Add comprehensive E2E test suite with Playwright
- Add Playwright configuration and GitHub Actions workflow - Implement test utilities and base test setup - Add E2E tests for core navigation functionality - Add comprehensive search feature tests (filters, saved searches) - Add dashboard, alerts, and chart explorer tests - Add session and trace workflow tests - Add source management tests - Update CI/CD pipeline to include E2E testing - Improve test data attributes and component accessibility
1 parent 0d9f3fe commit ac8fd09

29 files changed

+1661
-54
lines changed

.github/workflows/e2e-tests.yml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
name: E2E Tests
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
workflow_call:
8+
# Allow this workflow to be called by other workflows
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
e2e-tests:
16+
name: End-to-End Tests
17+
runs-on: ubuntu-24.04
18+
timeout-minutes: 15
19+
container:
20+
image: mcr.microsoft.com/playwright:v1.55.0-jammy
21+
permissions:
22+
contents: read
23+
pull-requests: write
24+
issues: write
25+
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@v4
29+
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version-file: '.nvmrc'
34+
cache-dependency-path: 'yarn.lock'
35+
cache: 'yarn'
36+
37+
- name: Install dependencies
38+
run: yarn install
39+
40+
- name: Build dependencies
41+
run: npx nx run-many -t ci:build
42+
43+
- name: Run Playwright tests
44+
run: |
45+
cd packages/app
46+
yarn test:e2e
47+
48+
- name: Upload Playwright report
49+
uses: actions/upload-artifact@v4
50+
if: always()
51+
with:
52+
name: playwright-report
53+
path: packages/app/playwright-report/
54+
retention-days: 30
55+
56+
- name: Upload test results
57+
uses: actions/upload-artifact@v4
58+
if: always()
59+
with:
60+
name: test-results
61+
path: packages/app/test-results/
62+
retention-days: 30
63+
64+
- name: Comment PR with test results
65+
uses: actions/github-script@v7
66+
if: always() && github.event_name == 'pull_request'
67+
with:
68+
script: |
69+
const fs = require('fs');
70+
const path = require('path');
71+
72+
try {
73+
const resultsPath = path.join('packages/app/test-results/results.json');
74+
if (fs.existsSync(resultsPath)) {
75+
const results = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));
76+
const { stats } = results;
77+
78+
const total = stats.expected + stats.unexpected + stats.flaky + stats.skipped;
79+
const failed = stats.unexpected > 0;
80+
81+
const summary = failed
82+
? `❌ **${stats.unexpected} test${stats.unexpected > 1 ? 's' : ''} failed**`
83+
: `✅ **All tests passed**`;
84+
85+
const body = `## E2E Test Results
86+
87+
${summary} • ${stats.expected} passed • ${stats.skipped} skipped • ${Math.round(stats.duration / 1000)}s
88+
89+
${failed ? `
90+
<details>
91+
<summary>View Details</summary>
92+
93+
| Status | Count |
94+
|--------|-------|
95+
| ✅ Passed | ${stats.expected} |
96+
| ❌ Failed | ${stats.unexpected} |
97+
| ⚠️ Flaky | ${stats.flaky} |
98+
| ⏭️ Skipped | ${stats.skipped} |
99+
100+
</details>
101+
102+
[View full report →](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})` : ''}`;
103+
104+
github.rest.issues.createComment({
105+
issue_number: context.issue.number,
106+
owner: context.repo.owner,
107+
repo: context.repo.repo,
108+
body: body
109+
});
110+
}
111+
} catch (error) {
112+
console.log('Could not post test results:', error.message);
113+
}

.github/workflows/main.yml

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -98,33 +98,9 @@ jobs:
9898
if: steps.changed-files.outputs.any_changed == 'true'
9999
working-directory: ./smoke-tests/otel-collector
100100
run: bats .
101-
app-smoke-test:
102-
name: HyperDX App Smoke Test
103-
runs-on: ubuntu-latest
101+
e2e-tests:
102+
name: End-to-End Tests
103+
uses: ./.github/workflows/e2e-tests.yml
104104
permissions:
105+
contents: read
105106
pull-requests: write
106-
contents: write
107-
steps:
108-
- name: Checkout
109-
id: checkout
110-
uses: actions/checkout@v4
111-
- name: Waiting for vercel preview to be ready
112-
uses: patrickedqvist/[email protected]
113-
id: waitFor200
114-
with:
115-
token: ${{ secrets.GITHUB_TOKEN }}
116-
max_timeout: 1200
117-
check_interval: 10
118-
- run: echo ${{steps.waitFor200.outputs.url}}
119-
120-
- name: Stably Runner Action
121-
uses: stablyhq/stably-runner-action@v3
122-
with:
123-
test-suite-id: cmc548u5u0001la04q7y8ddj2
124-
github-token: ${{ secrets.GITHUB_TOKEN }}
125-
api-key: ${{ secrets.STABLY_API_KEY }}
126-
environment: PRODUCTION
127-
variable-overrides: |
128-
{
129-
"SITE_URL": "${{ steps.waitFor200.outputs.url }}"
130-
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ e2e/cypress/screenshots/
5757
e2e/cypress/videos/
5858
e2e/cypress/results
5959

60+
# playwright
61+
**/test-results/
62+
**/playwright-report/
63+
**/playwright/.cache/
64+
6065
# scripts
6166
scripts/*.csv
6267
**/venv

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ dev-unit:
5959
ci-unit:
6060
npx nx run-many -t ci:unit
6161

62+
.PHONY: e2e
63+
e2e:
64+
@if [ -z "$(tags)" ]; then \
65+
echo "Running all E2E tests in local mode..."; \
66+
cd packages/app && yarn test:e2e; \
67+
else \
68+
echo "Running E2E tests with tags: $(tags)"; \
69+
cd packages/app && yarn test:e2e --grep "$(tags)"; \
70+
fi
71+
6272
# TODO: check db connections before running the migration CLIs
6373
.PHONY: dev-migrate-db
6474
dev-migrate-db:

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
"packageManager": "[email protected]",
5353
"resolutions": {
5454
"@types/react": "18.3.1",
55-
"@types/react-dom": "18.3.1"
55+
"@types/react-dom": "18.3.1",
56+
"@types/express": "4.17.21",
57+
"@types/express-serve-static-core": "4.17.43"
5658
}
5759
}

packages/app/.eslintrc.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,15 @@ module.exports = {
4444
],
4545
},
4646
},
47+
{
48+
// Disable strict rules for E2E test files
49+
files: ['tests/e2e/**/*.ts', 'tests/e2e/**/*.js'],
50+
rules: {
51+
'no-console': 'off',
52+
'no-empty': 'off',
53+
'@typescript-eslint/no-explicit-any': 'off',
54+
'@next/next/no-html-link-for-pages': 'off',
55+
},
56+
},
4757
],
4858
};

packages/app/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
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",
2023
"storybook": "storybook dev -p 6006",
2124
"storybook:build": "storybook build",
2225
"knip": "knip"
@@ -104,6 +107,7 @@
104107
"@chromatic-com/storybook": "^1.5.0",
105108
"@hookform/devtools": "^4.3.1",
106109
"@jedmao/location": "^3.0.0",
110+
"@playwright/test": "^1.47.0",
107111
"@storybook/addon-essentials": "^8.1.5",
108112
"@storybook/addon-interactions": "^8.1.5",
109113
"@storybook/addon-links": "^8.1.5",

packages/app/playwright.config.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
/**
4+
* @see https://playwright.dev/docs/test-configuration
5+
*/
6+
export default defineConfig({
7+
testDir: './tests/e2e',
8+
/* Global setup to ensure server is ready */
9+
globalSetup: require.resolve('./global-setup.js'),
10+
/* Run tests in files in parallel */
11+
fullyParallel: true,
12+
/* Fail the build on CI if you accidentally left test.only in the source code. */
13+
forbidOnly: !!process.env.CI,
14+
/* Retry on CI only */
15+
retries: process.env.CI ? 2 : 1,
16+
/* Use multiple workers on CI for faster execution */
17+
workers: undefined,
18+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
19+
reporter: [
20+
['html'],
21+
['json', { outputFile: 'test-results/results.json' }],
22+
...(process.env.CI ? [['github', {}] as const] : []),
23+
],
24+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
25+
use: {
26+
/* Base URL to use in actions like `await page.goto('/')`. */
27+
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8080',
28+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
29+
trace: 'on-first-retry',
30+
/* Take screenshot on failure */
31+
screenshot: 'only-on-failure',
32+
/* Record video on failure */
33+
video: 'retain-on-failure',
34+
},
35+
36+
/* Configure projects for different test environments */
37+
projects: [
38+
{
39+
name: 'chromium',
40+
use: {
41+
...devices['Desktop Chrome'],
42+
},
43+
},
44+
],
45+
46+
/* Run your local dev server before starting the tests */
47+
webServer: {
48+
command:
49+
'NEXT_PUBLIC_IS_LOCAL_MODE=true NEXT_TELEMETRY_DISABLED=1 yarn run dev',
50+
port: 8080,
51+
reuseExistingServer: !process.env.CI,
52+
timeout: 180 * 1000,
53+
stdout: 'pipe',
54+
stderr: 'pipe',
55+
},
56+
});

packages/app/src/AutocompleteInput.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function AutocompleteInput({
2525
showHotkey,
2626
onSubmit,
2727
queryHistoryType,
28+
'data-testid': dataTestId,
2829
}: {
2930
inputRef: React.RefObject<HTMLInputElement>;
3031
value?: string;
@@ -42,6 +43,7 @@ export default function AutocompleteInput({
4243
language?: 'sql' | 'lucene';
4344
showHotkey?: boolean;
4445
queryHistoryType?: string;
46+
'data-testid'?: string;
4547
}) {
4648
const suggestionsLimit = 10;
4749

@@ -242,6 +244,7 @@ export default function AutocompleteInput({
242244
className="border-0 fs-8"
243245
value={value}
244246
size={size}
247+
data-testid={dataTestId}
245248
onChange={e => onChange(e.target.value)}
246249
onFocus={() => {
247250
setSelectedAutocompleteIndex(-1);

packages/app/src/DBDashboardPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
944944
language="lucene"
945945
placeholder="Search your events w/ Lucene ex. column:foo"
946946
enableHotkey
947+
data-testid="search-input"
947948
/>
948949
)
949950
}
@@ -1047,6 +1048,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
10471048
) : null}
10481049
</Box>
10491050
<Button
1051+
data-testid="add-new-tile-button"
10501052
variant="outline"
10511053
mt="sm"
10521054
color={dashboard?.tiles.length === 0 ? 'green' : 'dark.3'}

0 commit comments

Comments
 (0)