Skip to content

Commit 609c4cd

Browse files
authored
feat: add guardrails for headed mode (#1197)
* feat: add guardrails for headed mode * fix: catch error [tra-1789] * fix: error message [tra-1789]
1 parent ab28d47 commit 609c4cd

File tree

5 files changed

+171
-0
lines changed

5 files changed

+171
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineConfig } from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: './tests',
5+
projects: [
6+
{
7+
name: 'chromium',
8+
use: {
9+
headless: false,
10+
},
11+
},
12+
],
13+
})
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: './tests',
5+
use: {
6+
headless: false,
7+
},
8+
})
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: './tests',
5+
use: {
6+
headless: true,
7+
},
8+
})

packages/cli/src/constructs/__tests__/playwright-check.spec.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,101 @@ describe('PlaywrightCheck', () => {
271271
}),
272272
]))
273273
})
274+
275+
it('should error if headless: false is set globally', async () => {
276+
Session.basePath = path.resolve(__dirname, './fixtures/playwright-check')
277+
Session.project = new Project('project-id', {
278+
name: 'Test Project',
279+
repoUrl: 'https://github.com/checkly/checkly-cli',
280+
})
281+
282+
const check = new PlaywrightCheck('foo', {
283+
name: 'Test Check',
284+
playwrightConfigPath: path.resolve(__dirname, './fixtures/playwright-check/playwright.config.headless-false.ts'),
285+
})
286+
287+
const diags = new Diagnostics()
288+
await check.validate(diags)
289+
290+
expect(diags.isFatal()).toEqual(true)
291+
expect(diags.observations).toEqual(expect.arrayContaining([
292+
expect.objectContaining({
293+
message: expect.stringContaining('The value provided for property "headless" is not valid.'),
294+
}),
295+
]))
296+
})
297+
298+
it('should error if headless: false is set in a project', async () => {
299+
Session.basePath = path.resolve(__dirname, './fixtures/playwright-check')
300+
Session.project = new Project('project-id', {
301+
name: 'Test Project',
302+
repoUrl: 'https://github.com/checkly/checkly-cli',
303+
})
304+
305+
const check = new PlaywrightCheck('foo', {
306+
name: 'Test Check',
307+
playwrightConfigPath: path.resolve(__dirname, './fixtures/playwright-check/playwright.config.headless-false-project.ts'),
308+
})
309+
310+
const diags = new Diagnostics()
311+
await check.validate(diags)
312+
313+
expect(diags.isFatal()).toEqual(true)
314+
expect(diags.observations).toEqual(expect.arrayContaining([
315+
expect.objectContaining({
316+
message: expect.stringContaining('The value provided for property "headless" is not valid.'),
317+
}),
318+
expect.objectContaining({
319+
message: expect.stringContaining('in project "chromium"'),
320+
}),
321+
]))
322+
})
323+
324+
it('should not error if headless: true is set', async () => {
325+
Session.basePath = path.resolve(__dirname, './fixtures/playwright-check')
326+
Session.project = new Project('project-id', {
327+
name: 'Test Project',
328+
repoUrl: 'https://github.com/checkly/checkly-cli',
329+
})
330+
331+
const check = new PlaywrightCheck('foo', {
332+
name: 'Test Check',
333+
playwrightConfigPath: path.resolve(__dirname, './fixtures/playwright-check/playwright.config.headless-true.ts'),
334+
})
335+
336+
const diags = new Diagnostics()
337+
await check.validate(diags)
338+
339+
expect(diags.isFatal()).toEqual(false)
340+
expect(diags.observations).not.toEqual(expect.arrayContaining([
341+
expect.objectContaining({
342+
message: expect.stringContaining('The value provided for property "headless" is not valid.'),
343+
}),
344+
]))
345+
})
346+
347+
it('should not error if headless is not set', async () => {
348+
Session.basePath = path.resolve(__dirname, './fixtures/playwright-check')
349+
Session.project = new Project('project-id', {
350+
name: 'Test Project',
351+
repoUrl: 'https://github.com/checkly/checkly-cli',
352+
})
353+
354+
const check = new PlaywrightCheck('foo', {
355+
name: 'Test Check',
356+
playwrightConfigPath: path.resolve(__dirname, './fixtures/playwright-check/playwright.config.ts'),
357+
})
358+
359+
const diags = new Diagnostics()
360+
await check.validate(diags)
361+
362+
expect(diags.isFatal()).toEqual(false)
363+
expect(diags.observations).not.toEqual(expect.arrayContaining([
364+
expect.objectContaining({
365+
message: expect.stringContaining('The value provided for property "headless" is not valid.'),
366+
}),
367+
]))
368+
})
274369
})
275370

276371
describe('defaults', () => {

packages/cli/src/constructs/playwright-check.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,51 @@ export class PlaywrightCheck extends RuntimeCheck {
238238
}
239239
}
240240

241+
protected async validateHeadlessMode (diagnostics: Diagnostics): Promise<void> {
242+
try {
243+
const playwrightConfig = await Session.loadFile<any>(this.playwrightConfigPath)
244+
245+
if (playwrightConfig?.use?.headless === false) {
246+
diagnostics.add(new InvalidPropertyValueDiagnostic(
247+
'headless',
248+
new Error(
249+
`headless: false is not supported.`
250+
+ `\n\n`
251+
+ `Checkly runs all Playwright checks in headless mode. `
252+
+ `Please remove this setting from your Playwright configuration, `
253+
+ `or set it to true.`,
254+
),
255+
))
256+
}
257+
258+
if (Array.isArray(playwrightConfig?.projects)) {
259+
for (const project of playwrightConfig.projects) {
260+
if (project?.use?.headless === false) {
261+
const projectName = project.name ? ` in project "${project.name}"` : ''
262+
diagnostics.add(new InvalidPropertyValueDiagnostic(
263+
'headless',
264+
new Error(
265+
`headless: false is not supported${projectName}.`
266+
+ `\n\n`
267+
+ `Checkly runs all Playwright checks in headless mode. `
268+
+ `Please remove this setting from your Playwright configuration, `
269+
+ `or set it to true.`,
270+
),
271+
))
272+
}
273+
}
274+
}
275+
} catch (err: any) {
276+
diagnostics.add(new InvalidPropertyValueDiagnostic(
277+
'playwrightConfigPath',
278+
new Error(
279+
`Unable to parse Playwright config "${this.playwrightConfigPath}": ${err.message}`,
280+
{ cause: err },
281+
),
282+
))
283+
}
284+
}
285+
241286
async validate (diagnostics: Diagnostics): Promise<void> {
242287
await super.validate(diagnostics)
243288
await this.validateRetryStrategy(diagnostics)
@@ -251,6 +296,8 @@ export class PlaywrightCheck extends RuntimeCheck {
251296
))
252297
}
253298

299+
await this.validateHeadlessMode(diagnostics)
300+
254301
this.#validateGroupReferences(diagnostics)
255302
}
256303

0 commit comments

Comments
 (0)