Skip to content

test: Run E2E tests in isolated tmp directory #16783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c4d6f92
test: Run E2E tests in isolated tmp directory
mydea Jul 1, 2025
e04b1e6
fix paths
mydea Jul 1, 2025
c529c91
update other actions
mydea Jul 1, 2025
fbc4f71
add console.log
mydea Jul 1, 2025
665d0a5
make abs
mydea Jul 1, 2025
69cb3f9
actually make abs
mydea Jul 1, 2025
a0a2ccf
just use __dirname??
mydea Jul 1, 2025
bd2b0ab
handle deps
mydea Jul 1, 2025
ffdf8ec
more generic link: handling and fix aws-lambda-layer e2e test
mydea Jul 2, 2025
2ce206c
more tests...
mydea Jul 2, 2025
9fea8e0
bump wrangler
mydea Jul 2, 2025
feb86e5
fix solidstart tests
mydea Jul 2, 2025
920a9ae
align all playwright versions of tests
mydea Jul 2, 2025
7c22869
remove SpanJSON type casting
mydea Jul 2, 2025
2c85402
add more missing deps
mydea Jul 2, 2025
e60a17a
add missing dep
mydea Jul 2, 2025
5cad46e
use wrangler version?
mydea Jul 2, 2025
c1b4000
require volta config
mydea Jul 2, 2025
cf0931f
deduplicate
mydea Jul 2, 2025
bd8723a
no need for nuxt external config
mydea Jul 2, 2025
5efa9f2
add all missing voltas
mydea Jul 2, 2025
4418370
avoid thingy
mydea Jul 2, 2025
6a4524f
avoid copy iitm stuff
mydea Jul 2, 2025
e56c174
fixes for nuxt-3-min
mydea Jul 2, 2025
73afebc
no need for copy stuff anymore
mydea Jul 2, 2025
a9dac98
avoid core
mydea Jul 3, 2025
4c9dd66
avoid core
mydea Jul 3, 2025
479db09
fix angular tests
mydea Jul 3, 2025
be6d26f
fix test
mydea Jul 3, 2025
6c963cb
remove overrides
mydea Jul 3, 2025
53e8a2b
remove unnecessary
mydea Jul 7, 2025
e355e45
rename angular-19
mydea Jul 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -920,19 +920,23 @@ jobs:
env:
E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }}

- name: Copy to temp
run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application
working-directory: dev-packages/e2e-tests

- name: Build E2E app
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 7
run: pnpm ${{ matrix.build-command || 'test:build' }}

- name: Install Playwright
uses: ./.github/actions/install-playwright
with:
browsers: chromium
cwd: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
cwd: ${{ runner.temp }}/test-application

- name: Run E2E test
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 10
run: pnpm test:assert

Expand All @@ -941,7 +945,7 @@ jobs:
if: failure()
with:
name: playwright-traces-job_e2e_playwright_tests-${{ matrix.test-application}}
path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application}}/test-results
path: ${{ runner.temp }}/test-application/test-results
overwrite: true
retention-days: 7

Expand All @@ -955,7 +959,7 @@ jobs:
if: always()
with:
name: E2E Test Dump (${{ matrix.label || matrix.test-application }})
path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/event-dumps
path: ${{ runner.temp }}/test-application/event-dumps
overwrite: true
retention-days: 7
if-no-files-found: ignore
Expand Down Expand Up @@ -1037,19 +1041,23 @@ jobs:
env:
E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }}

- name: Copy to temp
run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application
working-directory: dev-packages/e2e-tests

- name: Build E2E app
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 7
run: pnpm ${{ matrix.build-command || 'test:build' }}

- name: Install Playwright
uses: ./.github/actions/install-playwright
with:
browsers: chromium
cwd: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
cwd: ${{ runner.temp }}/test-application

- name: Run E2E test
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 10
run: pnpm ${{ matrix.assert-command || 'test:assert' }}

Expand All @@ -1063,20 +1071,19 @@ jobs:
if: always()
with:
name: E2E Test Dump (${{ matrix.label || matrix.test-application }})
path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/event-dumps
path: ${{ runner.temp }}/test-application/event-dumps
overwrite: true
retention-days: 7
if-no-files-found: ignore

- name: Deploy Astro to Cloudflare
uses: cloudflare/pages-action@v1
uses: cloudflare/wrangler-action@v3
if: matrix.test-application == 'cloudflare-astro'
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
directory: dist
workingDirectory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
command: pages deploy dist --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }}
workingDirectory: ${{ runner.temp }}/test-application

job_required_jobs_passed:
name: All required jobs passed or were skipped
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,23 @@ jobs:
env:
E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }}

- name: Copy to temp
run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application
working-directory: dev-packages/e2e-tests

- name: Build E2E app
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 7
run: yarn ${{ matrix.build-command }}

- name: Install Playwright
uses: ./.github/actions/install-playwright
with:
browsers: chromium
cwd: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
cwd: ${{ runner.temp }}/test-application

- name: Run E2E test
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 15
run: yarn test:assert

Expand Down
19 changes: 19 additions & 0 deletions dev-packages/e2e-tests/ciCopyToTemp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable no-console */

import { copyToTemp } from './lib/copyToTemp';

async function run(): Promise<void> {
const originalPath = process.argv[2];
const tmpDirPath = process.argv[3];

if (!originalPath || !tmpDirPath) {
throw new Error('Original path and tmp dir path are required');
}

console.log(`Copying ${originalPath} to ${tmpDirPath}...`);

await copyToTemp(originalPath, tmpDirPath);
}

// eslint-disable-next-line @typescript-eslint/no-floating-promises
run();
61 changes: 61 additions & 0 deletions dev-packages/e2e-tests/lib/copyToTemp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable no-console */
import { readFileSync, writeFileSync } from 'fs';
import { cp } from 'fs/promises';
import { join } from 'path';

export async function copyToTemp(originalPath: string, tmpDirPath: string): Promise<void> {
// copy files to tmp dir
await cp(originalPath, tmpDirPath, { recursive: true });

fixPackageJson(tmpDirPath);
}

function fixPackageJson(cwd: string): void {
const packageJsonPath = join(cwd, 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
volta?: Record<string, unknown>;
};

// 1. Fix file dependencies
if (packageJson.dependencies) {
fixFileLinkDependencies(packageJson.dependencies);
}
if (packageJson.devDependencies) {
fixFileLinkDependencies(packageJson.devDependencies);
}

// 2. Fix volta extends
if (!packageJson.volta) {
throw new Error('No volta config found, please provide one!');
}

if (typeof packageJson.volta.extends === 'string') {
const extendsPath = packageJson.volta.extends;
// We add a virtual dir to ensure that the relative depth is consistent
// dirPath is relative to ./../test-applications/xxx
const newPath = join(__dirname, 'virtual-dir/', extendsPath);
packageJson.volta.extends = newPath;
console.log(`Fixed volta.extends to ${newPath}`);
} else {
console.log('No volta.extends found');
}

writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
}

function fixFileLinkDependencies(dependencyObj: Record<string, string>): void {
for (const [key, value] of Object.entries(dependencyObj)) {
if (value.startsWith('link:')) {
const dirPath = value.replace('link:', '');

// We add a virtual dir to ensure that the relative depth is consistent
// dirPath is relative to ./../test-applications/xxx
const newPath = join(__dirname, 'virtual-dir/', dirPath);

dependencyObj[key] = `link:${newPath}`;
console.log(`Fixed ${key} dependency to ${newPath}`);
}
}
}
3 changes: 2 additions & 1 deletion dev-packages/e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"clean": "rimraf tmp node_modules && yarn clean:test-applications && yarn clean:pnpm",
"ci:build-matrix": "ts-node ./lib/getTestMatrix.ts",
"ci:build-matrix-optional": "ts-node ./lib/getTestMatrix.ts --optional=true",
"clean:test-applications": "rimraf --glob test-applications/**/{node_modules,dist,build,.next,.nuxt,.sveltekit,.react-router,.astro,.output,pnpm-lock.yaml,.last-run.json,test-results}",
"ci:copy-to-temp": "ts-node ./ciCopyToTemp.ts",
"clean:test-applications": "rimraf --glob test-applications/**/{node_modules,dist,build,.next,.nuxt,.sveltekit,.react-router,.astro,.output,pnpm-lock.yaml,.last-run.json,test-results,.angular,event-dumps}",
"clean:pnpm": "pnpm store prune"
},
"devDependencies": {
Expand Down
18 changes: 14 additions & 4 deletions dev-packages/e2e-tests/run.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* eslint-disable no-console */
import { spawn } from 'child_process';
import * as dotenv from 'dotenv';
import { mkdtemp, rm } from 'fs/promises';
import { sync as globSync } from 'glob';
import { resolve } from 'path';
import { tmpdir } from 'os';
import { join, resolve } from 'path';
import { copyToTemp } from './lib/copyToTemp';
import { registrySetup } from './registrySetup';

const DEFAULT_DSN = 'https://username@domain/123';
Expand Down Expand Up @@ -39,7 +42,7 @@ async function run(): Promise<void> {
dotenv.config();

// Allow to run a single app only via `yarn test:run <app-name>`
const appName = process.argv[2];
const appName = process.argv[2] || '';

const dsn = process.env.E2E_TEST_DSN || DEFAULT_DSN;

Expand Down Expand Up @@ -74,13 +77,20 @@ async function run(): Promise<void> {
console.log('');

for (const testAppPath of testAppPaths) {
const cwd = resolve('test-applications', testAppPath);
const originalPath = resolve('test-applications', testAppPath);
const tmpDirPath = await mkdtemp(join(tmpdir(), `sentry-e2e-tests-${appName}-`));

console.log(`Building ${testAppPath}...`);
await copyToTemp(originalPath, tmpDirPath);
const cwd = tmpDirPath;

console.log(`Building ${testAppPath} in ${tmpDirPath}...`);
await asyncExec('pnpm test:build', { env, cwd });

console.log(`Testing ${testAppPath}...`);
await asyncExec('pnpm test:assert', { env, cwd });

// clean up (although this is tmp, still nice to do)
await rm(tmpDirPath, { recursive: true });
}
} catch (error) {
console.error(error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ng": "ng",
"dev": "ng serve",
"proxy": "node start-event-proxy.mjs",
"preview": "http-server dist/angular-17/browser --port 8080",
"preview": "http-server dist/angular-17/browser --port 8080 --silent",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "playwright test",
Expand All @@ -31,6 +31,7 @@
"devDependencies": {
"@playwright/test": "~1.50.0",
"@sentry-internal/test-utils": "link:../../../test-utils",
"@sentry/core": "latest || *",
"@angular-devkit/build-angular": "^17.1.1",
"@angular/cli": "^17.1.1",
"@angular/compiler-cli": "^17.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
// Cannot use @sentry/angular here due to build stuff
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';

test('sends a pageload transaction with a parameterized URL', async ({ page }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ng": "ng",
"dev": "ng serve",
"proxy": "node start-event-proxy.mjs",
"preview": "http-server dist/angular-18/browser --port 8080",
"preview": "http-server dist/angular-18/browser --port 8080 --silent",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "playwright test",
Expand All @@ -31,6 +31,7 @@
"devDependencies": {
"@playwright/test": "~1.50.0",
"@sentry-internal/test-utils": "link:../../../test-utils",
"@sentry/core": "latest || *",
"@angular-devkit/build-angular": "^18.0.0",
"@angular/cli": "^18.0.0",
"@angular/compiler-cli": "^18.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
// Cannot use @sentry/angular here due to build stuff
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';

test('sends a pageload transaction with a parameterized URL', async ({ page }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ng": "ng",
"dev": "ng serve",
"proxy": "node start-event-proxy.mjs",
"preview": "http-server dist/angular-19/browser --port 8080",
"preview": "http-server dist/angular-19/browser --port 8080 --silent",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "playwright test",
Expand Down Expand Up @@ -34,6 +34,7 @@
"@angular/compiler-cli": "^19.0.0",
"@playwright/test": "~1.50.0",
"@sentry-internal/test-utils": "link:../../../test-utils",
"@sentry/core": "latest || *",
"@types/jasmine": "~5.1.0",
"http-server": "^14.1.1",
"jasmine-core": "~5.4.0",
Expand All @@ -43,5 +44,8 @@
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.6.2"
},
"volta": {
"extends": "../../package.json"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { startEventProxyServer } from '@sentry-internal/test-utils';

startEventProxyServer({
port: 3031,
proxyServerName: 'angular-18',
proxyServerName: 'angular-19',
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test';
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';

test('sends an error', async ({ page }) => {
const errorPromise = waitForError('angular-18', async errorEvent => {
const errorPromise = waitForError('angular-19', async errorEvent => {
return !errorEvent.type;
});

Expand Down Expand Up @@ -30,11 +30,11 @@ test('sends an error', async ({ page }) => {
});

test('assigns the correct transaction value after a navigation', async ({ page }) => {
const pageloadTxnPromise = waitForTransaction('angular-18', async transactionEvent => {
const pageloadTxnPromise = waitForTransaction('angular-19', async transactionEvent => {
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
});

const errorPromise = waitForError('angular-18', async errorEvent => {
const errorPromise = waitForError('angular-19', async errorEvent => {
return !errorEvent.type;
});

Expand Down
Loading
Loading