From 117bfc49ca57048e7bac2e36e3a19c2e54829d63 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Thu, 12 Jun 2025 12:19:05 +0200 Subject: [PATCH 1/2] feat: introduce pwTags and pwProjects --- packages/cli/src/commands/test.ts | 46 ++++++++++++++-- .../cli/src/constructs/playwright-check.ts | 4 ++ packages/cli/src/services/util.ts | 54 ++++++++++++++++++- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/test.ts b/packages/cli/src/commands/test.ts index 528805058..003cb8279 100644 --- a/packages/cli/src/commands/test.ts +++ b/packages/cli/src/commands/test.ts @@ -16,9 +16,19 @@ import { loadChecklyConfig } from '../services/checkly-config-loader' import { filterByFileNamePattern, filterByCheckNamePattern, filterByTags } from '../services/test-filters' import type { Runtime } from '../rest/runtimes' import { AuthCommand } from './authCommand' -import { BrowserCheck, Check, Diagnostics, HeartbeatCheck, MultiStepCheck, Project, RetryStrategyBuilder, Session } from '../constructs' +import { + BrowserCheck, + Check, + Diagnostics, + HeartbeatCheck, + MultiStepCheck, + PlaywrightCheck, + Project, + RetryStrategyBuilder, + Session +} from '../constructs' import type { Region } from '..' -import { splitConfigFilePath, getGitInformation, getCiInformation, getEnvs } from '../services/util' +import { splitConfigFilePath, getGitInformation, getCiInformation, getEnvs, getPwtChecks } from '../services/util' import { createReporters, ReporterType } from '../reporters/reporter' import commonMessages from '../messages/common-messages' import { TestResultsShortLinks } from '../rest/test-sessions' @@ -112,6 +122,22 @@ export default class Test extends AuthCommand { allowNo: true, env: 'CHECKLY_VERIFY_RUNTIME_DEPENDENCIES', }), + 'pwTags': Flags.string({ + description: 'A comma separated list of tags to filter Playwright tests by.' + + ' Only Playwright tests wit at least one of the specified tags will be run.' + + ' Multiple --pwtTags flags can be passed, in which case tests will be run if they match any of the --pwtTags filters.' + + ' F.ex. `--pwTags production,webapp --pwTags production,backend` will run checks with tags (production AND webapp) OR (production AND backend).', + multiple: true, + required: false, + }), + 'pwProjects': Flags.string({ + description: 'A comma separated list of projects to filter Playwright tests by.' + + ' Only Playwright tests in the specified projects will be run.' + + ' Multiple --pwtProjects flags can be passed, in which case tests will be run if they match any of the --pwtProjects filters.' + + ' If combining with --pwtTags, only tests that match both the specified projects and tags will be run.' , + multiple: true, + required: false, + }) } static args = { @@ -146,6 +172,8 @@ export default class Test extends AuthCommand { 'update-snapshots': updateSnapshots, retries, 'verify-runtime-dependencies': verifyRuntimeDependencies, + pwTags, + pwProjects } = flags const filePatterns = argv as string[] @@ -164,6 +192,11 @@ export default class Test extends AuthCommand { const { data: account } = await api.accounts.get(config.getAccountId()) const { data: availableRuntimes } = await api.runtimes.getAll() + const playwrightConfigPath = checklyConfig.checks?.playwrightConfigPath + const playwrightChecks = (pwProjects || pwTags) + ? getPwtChecks(pwProjects, pwTags, playwrightConfigPath) + : checklyConfig.checks?.playwrightChecks + const project = await parseProject({ directory: configDirectory, projectLogicalId: checklyConfig.logicalId, @@ -183,14 +216,19 @@ export default class Test extends AuthCommand { defaultRuntimeId: account.runtimeId, verifyRuntimeDependencies, checklyConfigConstructs, - playwrightConfigPath: checklyConfig.checks?.playwrightConfigPath, + playwrightConfigPath, include: checklyConfig.checks?.include, - playwrightChecks: checklyConfig.checks?.playwrightChecks, + playwrightChecks, checkFilter: check => { if (check instanceof HeartbeatCheck) { return false } + if (pwProjects || pwTags) { + // if Playwright projects or tags are specified, we only want to run Playwright checks + return check instanceof PlaywrightCheck + } + let entrypointMatch = false if (check instanceof BrowserCheck || check instanceof MultiStepCheck) { // For historical reasons the path used for filtering has always diff --git a/packages/cli/src/constructs/playwright-check.ts b/packages/cli/src/constructs/playwright-check.ts index 1db27276d..5126d0785 100644 --- a/packages/cli/src/constructs/playwright-check.ts +++ b/packages/cli/src/constructs/playwright-check.ts @@ -73,6 +73,10 @@ export class PlaywrightCheck extends Check { } } + allowInChecklyConfig () { + return true + } + #findGroupByName (groupName: string) { return Object.values(Session.project?.data?.['check-group'] ?? {}) .find(group => group.name === groupName) diff --git a/packages/cli/src/services/util.ts b/packages/cli/src/services/util.ts index feef6b13f..3e655c216 100644 --- a/packages/cli/src/services/util.ts +++ b/packages/cli/src/services/util.ts @@ -17,7 +17,7 @@ import * as JSON5 from 'json5' import { PlaywrightConfig } from './playwright-config' import { access , readFile} from 'fs/promises' import { createHash } from 'crypto'; -import { Session } from '../constructs' +import { PlaywrightCheck, Session } from '../constructs' export interface GitInformation { commitId: string @@ -321,3 +321,55 @@ export async function writeChecklyConfigFile (dir: string, config: ChecklyConfig await fs.writeFile(configFile, configContent, { encoding: 'utf-8' }) } + +export function getPwtChecks(pwProjects: string[] | undefined, pwTags: string[]| undefined, playwrightConfigPath: string | undefined): PlaywrightCheck[] { + if (!playwrightConfigPath) { + return [] + } + let checks: PlaywrightCheck[] = [] + // We are creating new checks on the fly, so we need to set the loading state. + Session.loadingChecklyConfigFile = true + if (!pwProjects && pwTags) { + checks = pwTags.map(tags => createPlaywrightChecks(undefined, tags, playwrightConfigPath)) + } else if (pwProjects && !pwTags) { + checks = pwProjects.map(projects => createPlaywrightChecks(projects, '', playwrightConfigPath)) + } else if (pwProjects && pwTags) { + const res = [] + for (const project of pwProjects) { + for (const tag of pwTags) { + res.push(createPlaywrightChecks(project, tag, playwrightConfigPath)) + } + } + checks = res + } else { + checks = [] + } + Session.loadingChecklyConfigFile = false + return checks +} + +function createPlaywrightChecks(projects: string | undefined, tags: string, playwrightConfigPath: string) { + const logicalIdParts = [] + const nameParts = [] + if (projects) { + logicalIdParts.push(...projects) + nameParts.push(`Project: ${projects}`) + } + if (tags) { + logicalIdParts.push(tags) + nameParts.push(`Tags: ${tags}`) + } + const logicalId = `check-${logicalIdParts.map(part => + part + .replace(' ', '-') + .replace(/[^a-zA-Z0-9]/g, '-') + .toLowerCase()) + .join('-')}` + const name = `Playwright Check: ${nameParts.join(' - ')}` + return new PlaywrightCheck(logicalId, { + name, + playwrightConfigPath, + pwProjects: projects ? projects.split(',') : [], + pwTags: tags ? tags.split(',').map(tag => tag.trim()) : [], + }) +} From 1c2240eb7c767ed0abe5fb25e188e7b5474a0c55 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Thu, 12 Jun 2025 15:49:07 +0200 Subject: [PATCH 2/2] fix: create props instead of object for parsing --- packages/cli/src/constructs/playwright-check.ts | 4 ---- packages/cli/src/services/util.ts | 16 +++++++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/cli/src/constructs/playwright-check.ts b/packages/cli/src/constructs/playwright-check.ts index 5126d0785..1db27276d 100644 --- a/packages/cli/src/constructs/playwright-check.ts +++ b/packages/cli/src/constructs/playwright-check.ts @@ -73,10 +73,6 @@ export class PlaywrightCheck extends Check { } } - allowInChecklyConfig () { - return true - } - #findGroupByName (groupName: string) { return Object.values(Session.project?.data?.['check-group'] ?? {}) .find(group => group.name === groupName) diff --git a/packages/cli/src/services/util.ts b/packages/cli/src/services/util.ts index 3e655c216..c10d5008b 100644 --- a/packages/cli/src/services/util.ts +++ b/packages/cli/src/services/util.ts @@ -11,7 +11,7 @@ import archiver from 'archiver' import type { Archiver } from 'archiver' import { glob } from 'glob' import os from 'node:os' -import { ChecklyConfig } from './checkly-config-loader' +import { ChecklyConfig, PlaywrightSlimmedProp } from './checkly-config-loader' import { Parser } from './check-parser/parser' import * as JSON5 from 'json5' import { PlaywrightConfig } from './playwright-config' @@ -322,13 +322,12 @@ export async function writeChecklyConfigFile (dir: string, config: ChecklyConfig await fs.writeFile(configFile, configContent, { encoding: 'utf-8' }) } -export function getPwtChecks(pwProjects: string[] | undefined, pwTags: string[]| undefined, playwrightConfigPath: string | undefined): PlaywrightCheck[] { +export function getPwtChecks(pwProjects: string[] | undefined, pwTags: string[]| undefined, playwrightConfigPath: string | undefined): PlaywrightSlimmedProp[] { if (!playwrightConfigPath) { return [] } - let checks: PlaywrightCheck[] = [] + let checks: PlaywrightSlimmedProp[] = [] // We are creating new checks on the fly, so we need to set the loading state. - Session.loadingChecklyConfigFile = true if (!pwProjects && pwTags) { checks = pwTags.map(tags => createPlaywrightChecks(undefined, tags, playwrightConfigPath)) } else if (pwProjects && !pwTags) { @@ -344,11 +343,10 @@ export function getPwtChecks(pwProjects: string[] | undefined, pwTags: string[]| } else { checks = [] } - Session.loadingChecklyConfigFile = false return checks } -function createPlaywrightChecks(projects: string | undefined, tags: string, playwrightConfigPath: string) { +function createPlaywrightChecks(projects: string | undefined, tags: string, playwrightConfigPath: string): PlaywrightSlimmedProp { const logicalIdParts = [] const nameParts = [] if (projects) { @@ -366,10 +364,10 @@ function createPlaywrightChecks(projects: string | undefined, tags: string, play .toLowerCase()) .join('-')}` const name = `Playwright Check: ${nameParts.join(' - ')}` - return new PlaywrightCheck(logicalId, { + return { + logicalId, name, - playwrightConfigPath, pwProjects: projects ? projects.split(',') : [], pwTags: tags ? tags.split(',').map(tag => tag.trim()) : [], - }) + } }