Skip to content

Commit 929203c

Browse files
authored
chore: make test:e2e agent-friendly with --grep support and dev server reuse (#15755)
AI agents consistently struggle with two aspects of the e2e test workflow: 1. **`--grep` not supported by `test:e2e`** - Agents default to passing `--grep` to `pnpm test:e2e` even when instructed to invoke Playwright directly. Rather than fighting this, just support it. 2. **Duplicate dev servers** - Agents frequently run `test:e2e` multiple times without stopping previous dev servers, leading to port conflicts, stalled processes, and minutes of wasted time before they realize the server never started. They are **not** smart enough to make sure to reliably close old dev servers themselves. ### Changes - Forward the `--grep` flag from `test:e2e` to the underlying Playwright command - Before spawning a new dev server, check if port 3000 is already in use - if so, skip spawning and reuse the existing server
1 parent 8791a72 commit 929203c

File tree

1 file changed

+30
-12
lines changed

1 file changed

+30
-12
lines changed

test/runE2E.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { spawn } from 'child_process'
22
import globby from 'globby'
33
import minimist from 'minimist'
4+
import { createServer } from 'net'
45
import path from 'path'
56
import shelljs from 'shelljs'
67
import slash from 'slash'
@@ -31,6 +32,7 @@ const {
3132
_: args,
3233
bail,
3334
'fully-parallel': fullyParallel,
35+
grep,
3436
part,
3537
shard,
3638
workers,
@@ -74,7 +76,7 @@ if (!suiteName) {
7476
if (!baseTestFolder) {
7577
throw new Error(`No base test folder found for ${file}`)
7678
}
77-
executePlaywright(file, baseTestFolder, bail)
79+
await executePlaywright(file, baseTestFolder, bail)
7880
}
7981
} else {
8082
let inputSuitePath: string | undefined = suiteName
@@ -107,14 +109,15 @@ if (!suiteName) {
107109

108110
// Run all spec files in the folder with a single dev server and playwright invocation
109111
// This avoids port conflicts when multiple spec files exist in the same folder
110-
executePlaywright(
112+
await executePlaywright(
111113
allSuitesInFolder,
112114
baseTestFolder,
113115
false,
114116
suiteConfigPath,
115117
shard,
116118
fullyParallel,
117119
workers,
120+
grep,
118121
)
119122
}
120123

@@ -127,14 +130,15 @@ console.log('\n')
127130
// baseTestFolder is the most top level folder of the test suite, that contains the payload config.
128131
// We need this because pnpm dev for a given test suite will always be run from the top level test folder,
129132
// not from a nested suite folder.
130-
function executePlaywright(
133+
async function executePlaywright(
131134
suitePaths: string | string[],
132135
baseTestFolder: string,
133136
bail = false,
134137
suiteConfigPath?: string,
135138
shardArg?: string,
136139
fullyParallelArg?: boolean,
137140
workersArg?: number,
141+
grepArg?: string,
138142
) {
139143
const paths = Array.isArray(suitePaths) ? suitePaths : [suitePaths]
140144
console.log(`Executing ${paths.join(', ')}...`)
@@ -158,19 +162,33 @@ function executePlaywright(
158162

159163
process.env.START_MEMORY_DB = 'true'
160164

161-
const child = spawn('pnpm', spawnDevArgs, {
162-
cwd: path.resolve(dirname, '..'),
163-
env: {
164-
...process.env,
165-
},
166-
stdio: 'inherit',
165+
const portInUse = await new Promise<boolean>((resolve) => {
166+
const server = createServer()
167+
server.once('error', () => resolve(true))
168+
server.once('listening', () => server.close(() => resolve(false)))
169+
server.listen(3000)
167170
})
168171

172+
let child: ReturnType<typeof spawn> | undefined
173+
174+
if (portInUse) {
175+
console.log('Port 3000 is already in use — reusing existing dev server.')
176+
} else {
177+
child = spawn('pnpm', spawnDevArgs, {
178+
cwd: path.resolve(dirname, '..'),
179+
env: {
180+
...process.env,
181+
},
182+
stdio: 'inherit',
183+
})
184+
}
185+
169186
const shardFlag = shardArg ? ` --shard=${shardArg}` : ''
170187
const fullyParallelFlag = fullyParallelArg ? ' --fully-parallel' : ''
171188
const workersFlag = workersArg !== undefined ? ` --workers=${workersArg}` : ''
189+
const grepFlag = grepArg ? ` --grep="${grepArg}"` : ''
172190
const cmd = slash(
173-
`${playwrightBin} test ${paths.join(' ')} -c ${playwrightCfg}${shardFlag}${fullyParallelFlag}${workersFlag}`,
191+
`${playwrightBin} test ${paths.join(' ')} -c ${playwrightCfg}${shardFlag}${fullyParallelFlag}${workersFlag}${grepFlag}`,
174192
)
175193
console.log('\n', cmd)
176194
const { code, stdout } = shelljs.exec(cmd, {
@@ -183,10 +201,10 @@ function executePlaywright(
183201
if (bail) {
184202
console.error(`TEST FAILURE DURING ${suite} suite.`)
185203
}
186-
child.kill(1)
204+
child?.kill(1)
187205
process.exit(1)
188206
} else {
189-
child.kill()
207+
child?.kill()
190208
}
191209
testRunCodes.push(results)
192210

0 commit comments

Comments
 (0)