Skip to content

Commit 74cb56d

Browse files
authored
feat(cli): playwright config file selection (#1255)
1 parent 713dbba commit 74cb56d

13 files changed

Lines changed: 207 additions & 14 deletions

File tree

packages/create-cli/e2e/__tests__/bootstrap.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,10 @@ describe('bootstrap', () => {
291291

292292
it('Should copy the playwright config', async () => {
293293
const directory = path.join(__dirname, 'fixtures', 'playwright-project')
294+
const playwrightConfigPath = path.join(directory, 'playwright.config.ts')
294295
const commandOutput = await runChecklyCreateCli({
295296
directory,
296-
promptsInjection: [true, false, false, true],
297+
promptsInjection: [true, false, false, true, playwrightConfigPath],
297298
})
298299

299300
expectVersionAndName({ commandOutput, latestVersion, greeting })

packages/create-cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,4 @@
8383
"uuid": "^11.1.0",
8484
"vitest": "3.1.2"
8585
}
86-
}
86+
}

packages/create-cli/src/actions/playwright.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,26 @@ function addOrReplacePlaywrightConfig (ast: any, node: any) {
9393
ast.properties.push(node)
9494
}
9595
}
96+
97+
export function updatePlaywrightConfigPath (dirPath: string, relativePath: string) {
98+
const checklyConfig = getChecklyConfigFile(dirPath)
99+
if (!checklyConfig) {
100+
return
101+
}
102+
103+
const ast = recast.parse(checklyConfig.checklyConfig)
104+
const checksAst = findPropertyByName(ast, 'checks')
105+
if (!checksAst) {
106+
return
107+
}
108+
109+
const configPathProp = findPropertyByName(checksAst.value, 'playwrightConfigPath')
110+
if (configPathProp) {
111+
configPathProp.value = recast.types.builders.literal(relativePath)
112+
}
113+
114+
fs.writeFileSync(
115+
path.join(dirPath, checklyConfig.fileName),
116+
recast.print(ast, { tabWidth: 2 }).code,
117+
)
118+
}

packages/create-cli/src/commands/bootstrap.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import * as path from 'path'
12
import axios from 'axios'
23
import chalk from 'chalk'
34
import prompts from 'prompts'
45
import { Command, Flags } from '@oclif/core'
56
import { getUserGreeting, header, footer, hint } from '../utils/messages'
6-
import { getPlaywrightConfig, hasPackageJsonFile } from '../utils/directory'
7+
import { collectConfigPaths, getPlaywrightConfig, hasPackageJsonFile, PLAYWRIGHT_CONFIG_FILES } from '../utils/directory'
78
import {
89
copyPlaywrightConfig,
910
createProject,
1011
getProjectDirectory,
1112
installDependenciesAndInitGit,
1213
installWithinProject,
1314
} from '../utils/installation'
15+
import { askPlaywrightConfigPath } from '../utils/prompts'
1416

1517
/**
1618
* This code is heavily inspired by the amazing create-astro package over at
@@ -115,10 +117,20 @@ export default class Bootstrap extends Command {
115117

116118
await installDependenciesAndInitGit({ projectDirectory })
117119

118-
const playwrightConfig = getPlaywrightConfig(projectDirectory)
119-
// Only prompt playwright copy when not using exampels
120+
let playwrightConfig: string | undefined
121+
if (interactive) {
122+
const candidates = collectConfigPaths(projectDirectory, PLAYWRIGHT_CONFIG_FILES)
123+
const { playwrightConfigPath } = await askPlaywrightConfigPath(candidates, projectDirectory, onCancel)
124+
playwrightConfig = playwrightConfigPath
125+
? path.relative(projectDirectory, playwrightConfigPath)
126+
: undefined
127+
} else {
128+
playwrightConfig = getPlaywrightConfig(projectDirectory)
129+
}
130+
131+
// Only prompt playwright copy when not using examples
120132
if (playwrightConfig && existingProject) {
121-
await copyPlaywrightConfig({ projectDirectory, playwrightConfig })
133+
await copyPlaywrightConfig({ projectDirectory, playwrightConfig, onCancel })
122134
}
123135

124136
// Show appropriate footer

packages/create-cli/src/utils/__tests__/directory.spec.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import path from 'node:path'
22

33
import { describe, expect, test } from 'vitest'
44

5-
import { hasPackageJsonFile, isValidProjectDirectory, readPackageJson } from '../directory'
5+
import { collectConfigPaths, hasPackageJsonFile, isValidProjectDirectory, readPackageJson } from '../directory'
66

77
describe('isValidProjectDirectory()', () => {
88
type TestTuple = [string, boolean]
@@ -30,6 +30,27 @@ describe('hasPackageJsonFile()', () => {
3030
})
3131
})
3232

33+
describe('collectConfigPaths()', () => {
34+
const suffixes = ['playwright.config.ts', 'playwright.config.js']
35+
36+
test('finds configs recursively, sorted by depth then ts over js', () => {
37+
const fixtureDir = path.join(__dirname, './fixtures/playwright-multi')
38+
const results = collectConfigPaths(fixtureDir, suffixes)
39+
.map(r => path.relative(fixtureDir, r).split(path.sep).join('/'))
40+
expect(results).toEqual([
41+
'playwright.config.ts',
42+
'playwright.config.js',
43+
'staging/playwright.config.ts',
44+
'dev/playwright.config.js',
45+
])
46+
})
47+
48+
test('returns empty array when no matches', () => {
49+
const results = collectConfigPaths(path.join(__dirname, './fixtures/empty-project'), suffixes)
50+
expect(results).toEqual([])
51+
})
52+
})
53+
3354
describe('readPackageJson()', () => {
3455
type TestTuple = [string, string]
3556
const cases: TestTuple[] = [
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default {}

packages/create-cli/src/utils/__tests__/prompts.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
askCreateInitialBrowserCheck,
1313
askInstallDependencies,
1414
askInitializeGit,
15+
askPlaywrightConfigPath,
1516
} from '../prompts'
1617
import * as directoryUtils from '../../utils/directory'
1718

@@ -69,4 +70,49 @@ describe('prompts', () => {
6970
}
7071
expect(onCancel).toBeCalledTimes(availableTemplates.length)
7172
})
73+
74+
it('should ask to select a playwright config path', async () => {
75+
const projectDir = path.resolve(__dirname, './fixtures/playwright-multi')
76+
const candidates = [
77+
path.join(projectDir, 'playwright.config.ts'),
78+
path.join(projectDir, 'staging/playwright.config.ts'),
79+
]
80+
81+
// Select first candidate
82+
prompts.inject([candidates[0]])
83+
const { playwrightConfigPath } = await askPlaywrightConfigPath(candidates, projectDir, onCancel)
84+
expect(playwrightConfigPath).toBe(candidates[0])
85+
})
86+
87+
it('should ask to select custom when choosing custom option', async () => {
88+
const projectDir = path.resolve(__dirname, './fixtures/playwright-multi')
89+
const candidates = [
90+
path.join(projectDir, 'playwright.config.ts'),
91+
]
92+
93+
// Select custom (index 1 = last item), then provide a valid path
94+
prompts.inject(['__custom__', 'playwright.config.ts'])
95+
const { playwrightConfigPath } = await askPlaywrightConfigPath(candidates, projectDir, onCancel)
96+
expect(playwrightConfigPath).toBe(path.resolve(projectDir, 'playwright.config.ts'))
97+
})
98+
99+
it('should skip playwright config when user selects Skip', async () => {
100+
const projectDir = path.resolve(__dirname, './fixtures/playwright-multi')
101+
const candidates = [
102+
path.join(projectDir, 'playwright.config.ts'),
103+
path.join(projectDir, 'staging/playwright.config.ts'),
104+
]
105+
106+
prompts.inject(['__skip__'])
107+
const { playwrightConfigPath } = await askPlaywrightConfigPath(candidates, projectDir, onCancel)
108+
expect(playwrightConfigPath).toBeUndefined()
109+
})
110+
111+
it('should fall back to custom input when no candidates', async () => {
112+
const projectDir = path.resolve(__dirname, './fixtures/playwright-multi')
113+
114+
prompts.inject(['staging/playwright.config.ts'])
115+
const { playwrightConfigPath } = await askPlaywrightConfigPath([], projectDir, onCancel)
116+
expect(playwrightConfigPath).toBe(path.resolve(projectDir, 'staging/playwright.config.ts'))
117+
})
72118
})

0 commit comments

Comments
 (0)