Skip to content

Commit 3192172

Browse files
chore(test): run related E2E deploy tests on PRs (#63763)
1 parent 754fada commit 3192172

File tree

10 files changed

+217
-86
lines changed

10 files changed

+217
-86
lines changed

.github/workflows/test_e2e_deploy.yml

Lines changed: 0 additions & 72 deletions
This file was deleted.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Test E2E (Vercel Deploy), related
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize]
6+
7+
jobs:
8+
test:
9+
if: github.repository_owner == 'vercel'
10+
runs-on: ubuntu-latest
11+
12+
env:
13+
CARGO_PROFILE_RELEASE_LTO: 'true'
14+
DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }}
15+
DD_ENV: 'ci'
16+
NAPI_CLI_VERSION: 2.16.2
17+
NEXT_JUNIT_TEST_REPORT: 'true'
18+
NEXT_TELEMETRY_DISABLED: 1
19+
NEXT_TEST_JOB: 1
20+
NEXT_TEST_MODE: 'deploy'
21+
NODE_LTS_VERSION: 20
22+
TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }}
23+
TURBO_REMOTE_ONLY: 'true'
24+
TURBO_TEAM: 'vercel'
25+
TURBO_VERSION: 1.12.5
26+
VERCEL_TEST_TEAM: vtest314-next-e2e-tests
27+
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
28+
29+
strategy:
30+
fail-fast: false
31+
32+
steps:
33+
- name: Setup Node.js
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: ${{ env.NODE_LTS_VERSION }}
37+
check-latest: true
38+
39+
- name: Setup pnpm
40+
run: corepack enable
41+
42+
- name: Checkout
43+
uses: actions/checkout@v4
44+
with:
45+
fetch-depth: 25
46+
47+
- name: Setup tests
48+
run: |
49+
pnpm install
50+
pnpm run build
51+
npm i -g vercel@latest
52+
node scripts/run-e2e-test-project-reset.mjs
53+
54+
- name: Run tests
55+
run: |
56+
docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.41.2-jammy /bin/bash -c "cd /work && \
57+
NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && \
58+
corepack enable > /dev/null && \
59+
NEXT_JUNIT_TEST_REPORT=${{ env.NEXT_JUNIT_TEST_REPORT }} \
60+
DATADOG_API_KEY=${{ env.DATADOG_API_KEY }} \
61+
DD_ENV=${{ env.DD_ENV }} \
62+
VERCEL_TEST_TOKEN=${{ env.VERCEL_TEST_TOKEN }} \
63+
VERCEL_TEST_TEAM=${{ env.VERCEL_TEST_TEAM }} \
64+
NEXT_TEST_JOB=${{ env.NEXT_TEST_JOB }} \
65+
NEXT_TEST_MODE=${{ env.NEXT_TEST_MODE }} \
66+
TEST_TIMINGS_TOKEN=${{ env.TEST_TIMINGS_TOKEN }} \
67+
xvfb-run node run-tests.js --related --timings -c 1 >> /proc/1/fd/1"
68+
69+
- name: Save test report as artifacts
70+
if: always()
71+
uses: actions/upload-artifact@v4
72+
with:
73+
name: test-reports
74+
if-no-files-found: ignore
75+
retention-days: 2
76+
path: test/test-junit-report
77+
78+
- name: Upload test report to Datadog
79+
continue-on-error: true
80+
run: |
81+
pnpx @datadog/[email protected] junit upload --tags test.type:nextjs_deploy_e2e --service nextjs ./test/test-junit-report
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Test E2E (Vercel Deploy), scheduled
2+
3+
on:
4+
schedule:
5+
# run every day at midnight
6+
- cron: '0 0 * * *'
7+
# allow triggering manually as well
8+
workflow_dispatch:
9+
10+
jobs:
11+
test:
12+
if: github.repository_owner == 'vercel'
13+
runs-on: ubuntu-latest
14+
15+
env:
16+
CARGO_PROFILE_RELEASE_LTO: 'true'
17+
DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }}
18+
DD_ENV: 'ci'
19+
NAPI_CLI_VERSION: 2.16.2
20+
NEXT_JUNIT_TEST_REPORT: 'true'
21+
NEXT_TELEMETRY_DISABLED: 1
22+
NEXT_TEST_CONTINUE_ON_ERROR: 1
23+
NEXT_TEST_JOB: 1
24+
NEXT_TEST_MODE: 'deploy'
25+
NODE_LTS_VERSION: 20
26+
TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }}
27+
TURBO_REMOTE_ONLY: 'true'
28+
TURBO_TEAM: 'vercel'
29+
TURBO_VERSION: 1.12.5
30+
VERCEL_TEST_TEAM: vtest314-next-e2e-tests
31+
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
32+
33+
strategy:
34+
fail-fast: false
35+
matrix:
36+
group: [1, 2]
37+
38+
steps:
39+
- name: Setup Node.js
40+
uses: actions/setup-node@v4
41+
with:
42+
node-version: ${{ env.NODE_LTS_VERSION }}
43+
check-latest: true
44+
45+
- name: Setup pnpm
46+
run: corepack enable
47+
48+
- name: Checkout
49+
uses: actions/checkout@v4
50+
with:
51+
fetch-depth: 25
52+
53+
- name: Setup tests
54+
run: |
55+
pnpm install
56+
pnpm run build
57+
npm i -g vercel@latest
58+
node scripts/run-e2e-test-project-reset.mjs
59+
60+
- name: Run tests
61+
run: |
62+
docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.41.2-jammy /bin/bash -c "cd /work && \
63+
NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && \
64+
corepack enable > /dev/null && \
65+
NEXT_JUNIT_TEST_REPORT=${{ env.NEXT_JUNIT_TEST_REPORT }} \
66+
DATADOG_API_KEY=${{ env.DATADOG_API_KEY }} \
67+
DD_ENV=${{ env.DD_ENV }} \
68+
VERCEL_TEST_TOKEN=${{ env.VERCEL_TEST_TOKEN }} \
69+
VERCEL_TEST_TEAM=${{ env.VERCEL_TEST_TEAM }} \
70+
NEXT_TEST_JOB=${{ env.NEXT_TEST_JOB }} \
71+
NEXT_TEST_MODE=${{ env.NEXT_TEST_MODE }} \
72+
TEST_TIMINGS_TOKEN=${{ env.TEST_TIMINGS_TOKEN }} \
73+
xvfb-run node run-tests.js --type e2e --timings -g ${{ matrix.group }}/2 -c 1 >> /proc/1/fd/1"
74+
75+
- name: Save test report as artifacts
76+
if: always()
77+
uses: actions/upload-artifact@v4
78+
with:
79+
name: test-reports
80+
if-no-files-found: ignore
81+
retention-days: 2
82+
path: test/test-junit-report
83+
84+
- name: Upload test report to Datadog
85+
continue-on-error: true
86+
run: |
87+
pnpx @datadog/[email protected] junit upload --tags test.type:nextjs_deploy_e2e --service nextjs ./test/test-junit-report

packages/next/src/server/base-server.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ import { AppRouteRouteMatcherProvider } from './future/route-matcher-providers/a
102102
import { PagesAPIRouteMatcherProvider } from './future/route-matcher-providers/pages-api-route-matcher-provider'
103103
import { PagesRouteMatcherProvider } from './future/route-matcher-providers/pages-route-matcher-provider'
104104
import { ServerManifestLoader } from './future/route-matcher-providers/helpers/manifest-loaders/server-manifest-loader'
105-
import { getTracer, SpanKind } from './lib/trace/tracer'
105+
import { getTracer, isBubbledError, SpanKind } from './lib/trace/tracer'
106106
import { BaseServerSpan } from './lib/trace/constants'
107107
import { I18NProvider } from './future/helpers/i18n-provider'
108108
import { sendResponse } from './send-response'
@@ -1390,7 +1390,11 @@ export default abstract class Server<
13901390
return this.renderError(null, req, res, '/_error', {})
13911391
}
13921392

1393-
if (this.minimalMode || this.renderOpts.dev || (err as any).bubble) {
1393+
if (
1394+
this.minimalMode ||
1395+
this.renderOpts.dev ||
1396+
(isBubbledError(err) && err.bubble)
1397+
) {
13941398
throw err
13951399
}
13961400
this.logError(getProperError(err))

packages/next/src/server/lib/trace/tracer.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { FetchEventResult } from '../../web/types'
12
import type { SpanTypes } from './constants'
23
import { LogSpanAllowList, NextVanillaSpanAllowlist } from './constants'
34

@@ -36,10 +37,22 @@ const isPromise = <T>(p: any): p is Promise<T> => {
3637
return p !== null && typeof p === 'object' && typeof p.then === 'function'
3738
}
3839

39-
type BubbledError = Error & { bubble?: boolean }
40+
export class BubbledError extends Error {
41+
constructor(
42+
public readonly bubble?: boolean,
43+
public readonly result?: FetchEventResult
44+
) {
45+
super()
46+
}
47+
}
48+
49+
export function isBubbledError(error: unknown): error is BubbledError {
50+
if (typeof error !== 'object' || error === null) return false
51+
return error instanceof BubbledError
52+
}
4053

4154
const closeSpanWithError = (span: Span, error?: Error) => {
42-
if ((error as BubbledError | undefined)?.bubble === true) {
55+
if (isBubbledError(error) && error.bubble) {
4356
span.setAttribute('next.bubble', true)
4457
} else {
4558
if (error) {
@@ -307,7 +320,7 @@ class NextTracerImpl implements NextTracer {
307320
}
308321
try {
309322
if (fn.length > 1) {
310-
return fn(span, (err?: Error) => closeSpanWithError(span, err))
323+
return fn(span, (err) => closeSpanWithError(span, err))
311324
}
312325

313326
const result = fn(span)

packages/next/src/server/next-server.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ import {
8888
INSTRUMENTATION_HOOK_FILENAME,
8989
RSC_PREFETCH_SUFFIX,
9090
} from '../lib/constants'
91-
import { getTracer } from './lib/trace/tracer'
91+
import { BubbledError, getTracer } from './lib/trace/tracer'
9292
import { NextNodeServerSpan } from './lib/trace/constants'
9393
import { nodeFs } from './lib/node-fs-methods'
9494
import { getRouteRegex } from '../shared/lib/router/utils/route-regex'
@@ -1675,10 +1675,7 @@ export default class NextNodeServer extends BaseServer<
16751675
if ('response' in result) {
16761676
if (isMiddlewareInvoke) {
16771677
bubblingResult = true
1678-
const err = new Error()
1679-
;(err as any).result = result
1680-
;(err as any).bubble = true
1681-
throw err
1678+
throw new BubbledError(true, result)
16821679
}
16831680

16841681
for (const [key, value] of Object.entries(

run-tests.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ let argv = require('yargs/yargs')(process.argv.slice(2))
2525
.string('g')
2626
.alias('g', 'group')
2727
.number('c')
28+
.boolean('related')
29+
.alias('r', 'related')
2830
.alias('c', 'concurrency').argv
2931

3032
function escapeRegexp(str) {
@@ -197,6 +199,7 @@ async function main() {
197199
group: argv.group ?? false,
198200
testPattern: argv.testPattern ?? false,
199201
type: argv.type ?? false,
202+
related: argv.related ?? false,
200203
retries: argv.retries ?? DEFAULT_NUM_RETRIES,
201204
}
202205
let numRetries = options.retries
@@ -232,12 +235,22 @@ async function main() {
232235
let prevTimings
233236

234237
if (tests.length === 0) {
238+
/** @type {RegExp | undefined} */
235239
let testPatternRegex
236240

237241
if (options.testPattern) {
238242
testPatternRegex = new RegExp(options.testPattern)
239243
}
240244

245+
if (options.related) {
246+
const { getRelatedTests } = await import('./scripts/run-related-test.mjs')
247+
const tests = await getRelatedTests()
248+
if (tests.length)
249+
testPatternRegex = new RegExp(tests.map(escapeRegexp).join('|'))
250+
251+
console.log('Running related tests:', testPatternRegex.toString())
252+
}
253+
241254
tests = (
242255
await glob('**/*.test.{js,ts,tsx}', {
243256
nodir: true,

scripts/run-related-test.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ async function getChangedFilesFromPackages(baseBranch = 'canary') {
1919
await exec('git config --global --add safe.directory /work')
2020
await exec(`git remote set-branches --add origin ${baseBranch}`)
2121
await exec(`git fetch origin ${baseBranch} --depth=20`)
22-
const { stdout } = await exec(`git diff --name-only ${baseBranch}`)
22+
const { stdout } = await exec(
23+
`git diff 'origin/${baseBranch}...' --name-only`
24+
)
2325
return stdout
2426
.trim()
2527
.split('\n')

0 commit comments

Comments
 (0)