From 862a91fb35da497b0a7e2e9f19a1af3beda8323a Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Fri, 5 Jan 2024 15:38:25 +0100 Subject: [PATCH 1/7] feat: add ability to copy playwright config when creating the CLI Once creating the CLI it will check if there is a playwright.config file on the directory, then ask the user if he wants to copy it to the checkly config file --- packages/create-cli/package.json | 2 + packages/create-cli/src/actions/playwright.ts | 89 ++++++++++++ packages/create-cli/src/commands/bootstrap.ts | 8 +- packages/create-cli/src/utils/directory.ts | 10 ++ packages/create-cli/src/utils/fileloader.ts | 67 +++++++++ .../src/utils/handlebars-helpers.ts | 30 ++++ packages/create-cli/src/utils/installation.ts | 12 +- .../src/utils/playwright-template.ts | 134 ++++++++++++++++++ packages/create-cli/src/utils/prompts.ts | 9 ++ 9 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 packages/create-cli/src/actions/playwright.ts create mode 100644 packages/create-cli/src/utils/fileloader.ts create mode 100644 packages/create-cli/src/utils/handlebars-helpers.ts create mode 100644 packages/create-cli/src/utils/playwright-template.ts diff --git a/packages/create-cli/package.json b/packages/create-cli/package.json index 10f24a28..d6e33f9c 100644 --- a/packages/create-cli/package.json +++ b/packages/create-cli/package.json @@ -39,6 +39,7 @@ }, "homepage": "https://github.com/checkly/checkly-cli#readme", "dependencies": { + "handlebars": "4.7.8", "@oclif/core": "2.8.11", "@oclif/plugin-help": "5.1.20", "@oclif/plugin-plugins": "2.3.0", @@ -50,6 +51,7 @@ "ora": "5.4.1", "passwd-user": "3.0.0", "prompts": "2.4.2", + "recast": "0.23.4", "unique-names-generator": "4.7.1" }, "devDependencies": { diff --git a/packages/create-cli/src/actions/playwright.ts b/packages/create-cli/src/actions/playwright.ts new file mode 100644 index 00000000..030c9b63 --- /dev/null +++ b/packages/create-cli/src/actions/playwright.ts @@ -0,0 +1,89 @@ +import * as chalk from 'chalk' +import { spinner } from '../utils/terminal' +import { loadPlaywrightConfig } from '../utils/directory' +import * as recast from 'recast' +import * as path from 'path' +import playwrightConfigTemplate from '../utils/playwright-template' +import * as Handlebars from 'handlebars' +import * as fs from 'fs' +import * as ora from 'ora' +import { parse } from '../utils/handlebars-helpers' +export async function copyPlaywrightConfig (dirPath: string, playwrightConfigFileName: string) { + const copySpinner = spinner('Copying your playwright config') + try { + const config = await loadPlaywrightConfig(path.join(dirPath, playwrightConfigFileName)) + Handlebars.registerHelper('parse', parse) + const pwtConfig = Handlebars.compile(playwrightConfigTemplate, { compat: true })(config) + if (!pwtConfig) { + return + } + const checklyConfig = getChecklyConfigFile() + if (!checklyConfig) { + return handleError(copySpinner, 'Could not find your checkly config file') + } + const checklyAst = recast.parse(checklyConfig.checklyConfig) + const checksAst = findPropertyByName(checklyAst, 'checks') + if (!checksAst) { + return handleError(copySpinner, 'Could not parse you checkly file correctly') + } + const browserCheckAst = findPropertyByName(checksAst.value, 'browserChecks') + if (!browserCheckAst) { + return handleError(copySpinner, 'Could not parse you checkly file correctly') + } + const pwtConfigAst = findPropertyByName(recast.parse(pwtConfig), 'playwrightConfig') + addOrReplacePlaywrightConfig(browserCheckAst.value, pwtConfigAst) + fs.writeFileSync(path.join(dirPath, checklyConfig.fileName), recast.print(checklyAst, { tabWidth: 2 }).code) + } catch (e) { + handleError(copySpinner, e) + } + copySpinner.text = chalk.green('Playwright config copied!') + copySpinner.succeed() +} + +function handleError (copySpinner: ora.Ora, message: string | unknown) { + copySpinner.text = chalk.red('Something went wrong when copying your playwrightConfig file') + copySpinner.fail() + // eslint-disable-next-line + console.error(message) + process.exit(1) +} + +function getChecklyConfigFile (): {checklyConfig: string, fileName: string} | undefined { + const filenames: string[] = ['checkly.config.ts', 'checkly.config.js', 'checkly.config.mjs'] + let config + for (const configFile of filenames) { + const dir = path.resolve(path.dirname(configFile)) + const file = fs.readFileSync(path.resolve(dir, configFile)) + if (file) { + config = { + checklyConfig: file.toString(), + fileName: configFile, + } + break + } + } + return config +} + +function findPropertyByName (ast: any, name: string): + recast.types.namedTypes.Property | undefined { + let node + recast.visit(ast, { + visitProperty (path: any) { + if (path.node.key.name === name) { + node = path.node + } + return false + }, + }) + return node +} + +function addOrReplacePlaywrightConfig (ast: any, node: any) { + const playWrightConfig = findPropertyByName(ast, 'playwrightConfig') + if (playWrightConfig) { + playWrightConfig.value = node.value + } else { + ast.properties.push(node) + } +} diff --git a/packages/create-cli/src/commands/bootstrap.ts b/packages/create-cli/src/commands/bootstrap.ts index 602541c7..5ce357a1 100644 --- a/packages/create-cli/src/commands/bootstrap.ts +++ b/packages/create-cli/src/commands/bootstrap.ts @@ -3,8 +3,9 @@ import * as chalk from 'chalk' import * as prompts from 'prompts' import { Command, Flags } from '@oclif/core' import { getUserGreeting, header, footer, hint } from '../utils/messages.js' -import { hasPackageJsonFile } from '../utils/directory' +import { getPlaywrightConfig, hasPackageJsonFile } from '../utils/directory' import { + copyPlaywrightConfig, createProject, getProjectDirectory, installDependenciesAndInitGit, @@ -78,6 +79,11 @@ export default class Bootstrap extends Command { // ask and install dependencies and initialize git await installDependenciesAndInitGit({ projectDirectory }) + const playwrightConfig = getPlaywrightConfig(projectDirectory) + if (playwrightConfig) { + await copyPlaywrightConfig({ projectDirectory, playwrightConfig }) + } + await footer(projectDirectory) } } diff --git a/packages/create-cli/src/utils/directory.ts b/packages/create-cli/src/utils/directory.ts index 18b17d92..b0cc53d4 100644 --- a/packages/create-cli/src/utils/directory.ts +++ b/packages/create-cli/src/utils/directory.ts @@ -1,6 +1,7 @@ import * as fs from 'fs' import * as path from 'path' import { uniqueNamesGenerator, colors, animals } from 'unique-names-generator' +import { loadFile } from './fileloader' export interface PackageJson { name: string; @@ -13,10 +14,19 @@ export function hasPackageJsonFile (dirPath: string) { return fs.existsSync(path.join(dirPath, 'package.json')) } +export function getPlaywrightConfig (dirPath: string) { + const playwrightConfigFiles = ['playwright.config.ts', 'playwright.config.js'] + return playwrightConfigFiles.find(config => fs.existsSync(path.join(dirPath, config))) +} + export function readPackageJson (dirPath: string): PackageJson { return JSON.parse(fs.readFileSync(path.join(dirPath, 'package.json'), 'utf-8')) } +export function loadPlaywrightConfig (playwrightConfigPath: string) { + return loadFile(playwrightConfigPath) +} + export function isValidProjectDirectory (dirPath: string) { if (!fs.existsSync(dirPath)) { return true diff --git a/packages/create-cli/src/utils/fileloader.ts b/packages/create-cli/src/utils/fileloader.ts new file mode 100644 index 00000000..495dde4e --- /dev/null +++ b/packages/create-cli/src/utils/fileloader.ts @@ -0,0 +1,67 @@ +import { Service } from 'ts-node' +import { existsSync } from 'fs' +import * as path from 'path' + +export async function loadJsFile (filepath: string): Promise { + try { + let { default: exported } = { default: await require(filepath) } + if (exported instanceof Function) { + exported = await exported() + } + return exported + } catch (err: any) { + throw new Error(`Error loading file ${filepath}\n${err.stack}`) + } +} + +async function loadTsFile (filepath: string): Promise { + try { + const tsCompiler = await getTsCompiler() + tsCompiler.enabled(true) + let { default: exported } = await require(filepath) + if (exported instanceof Function) { + exported = await exported() + } + tsCompiler.enabled(false) // Re-disable the TS compiler + return exported + } catch (err: any) { + throw new Error(`Error loading file ${filepath}\n${err.stack}`) + } +} + +// To avoid a dependency on typescript for users with no TS checks, we need to dynamically import ts-node +let tsCompiler: Service +async function getTsCompiler (): Promise { + if (tsCompiler) return tsCompiler + try { + const tsNode = await import('ts-node') + tsCompiler = tsNode.register({ + moduleTypes: { + '**/*': 'cjs', + }, + compilerOptions: { + module: 'CommonJS', + }, + }) + } catch (err: any) { + if (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND') { + throw new Error('Please install "ts-node" and "typescript" to use TypeScript configuration files') + } + throw err + } + return tsCompiler +} + +export function loadFile (file: string) { + if (!existsSync(file)) { + return Promise.resolve(null) + } + switch (path.extname(file)) { + case '.js': + return loadJsFile(file) + case '.ts': + return loadTsFile(file) + default: + throw new Error(`Unsupported file extension ${file} for the config file`) + } +} diff --git a/packages/create-cli/src/utils/handlebars-helpers.ts b/packages/create-cli/src/utils/handlebars-helpers.ts new file mode 100644 index 00000000..0a9a0b65 --- /dev/null +++ b/packages/create-cli/src/utils/handlebars-helpers.ts @@ -0,0 +1,30 @@ +// Handlebars do not handle properly nested objects/arrays +export function parse (input: any): any { + if (typeof input === 'string') { + return `'${input}'` + } + if (typeof input === 'number' || typeof input === 'boolean') { + return input + } + if (Array.isArray(input)) { + let arr + for (const o of input) { + if (!arr) { + arr = parse(o) + continue + } + arr = `${arr}, ${parse(o)}` + } + return `[${arr}]` + } + if (typeof input === 'object') { + const keys = Object.keys(input) + let returnObj = '' + for (const key of keys) { + const curr = `${key}: ${parse(input[key])}` + returnObj = `${returnObj} ${curr},` + } + return `{${returnObj}}` + } + return input +} diff --git a/packages/create-cli/src/utils/installation.ts b/packages/create-cli/src/utils/installation.ts index 4be98dea..71b166b0 100644 --- a/packages/create-cli/src/utils/installation.ts +++ b/packages/create-cli/src/utils/installation.ts @@ -1,10 +1,11 @@ import Debug from 'debug' import { copyTemporaryFiles, generateProjectName, usePackageName, readPackageJson, hasPackageJsonFile, checkFilesToKeep } from './directory' -import { askInitializeProject, askProjectDirectory, askTemplate } from './prompts' +import { askCopyPlaywrightProject, askInitializeProject, askProjectDirectory, askTemplate } from './prompts' import { addDevDependecies, installDependencies } from '../actions/dependencies' import { copyTemplate } from '../actions/template' import { createCustomBrowserCheck } from '../actions/creates' import { initGit } from '../actions/git' +import * as playwright from '../actions/playwright' const debug = Debug('checkly:create-cli') const templateBaseRepo = 'checkly/checkly-cli/examples' @@ -77,3 +78,12 @@ export async function installDependenciesAndInitGit ( debug('Init .git & .gitignore') await initGit(projectDirectory) } + +export async function copyPlaywrightConfig ({ projectDirectory, playwrightConfig }: + { projectDirectory: string, playwrightConfig: string }) { + debug('Check if playwright config exists in project') + const { shouldCopyPlaywrightConfig } = await askCopyPlaywrightProject(projectDirectory) + if (shouldCopyPlaywrightConfig) { + await playwright.copyPlaywrightConfig(projectDirectory, playwrightConfig) + } +} diff --git a/packages/create-cli/src/utils/playwright-template.ts b/packages/create-cli/src/utils/playwright-template.ts new file mode 100644 index 00000000..9f6f4e5e --- /dev/null +++ b/packages/create-cli/src/utils/playwright-template.ts @@ -0,0 +1,134 @@ +const playwrightConfigTemplate = ` +const playwrightConfig = { + playwrightConfig: { + {{#timeout}} + timeout: {{.}}, + {{/timeout}} + {{#use}} + use: { + {{#baseURL}} + baseURL: '{{.}}', + {{/baseURL}} + {{#colorScheme}} + colorScheme: '{{.}}', + {{/colorScheme}} + {{#geolocation}} + geolocation: { + {{#longitude}} + longitude: {{.}}, + {{/longitude}} + {{#latitude}} + latitude: {{.}}, + {{/latitude}} + {{#accuracy}} + accuracy: {{.}}, + {{/accuracy}} + }, + {{/geolocation}} + {{#locale}} + locale: '{{.}}', + {{/locale}} + {{#if permissions}} + permissions: [{{#permissions}}'{{.}}',{{/permissions}}], + {{/if}} + {{#timezoneId}} + timezoneId: '{{.}}', + {{/timezoneId}} + {{#viewport}} + viewport: { + {{#width}} + width: {{.}}, + {{/width}} + {{#height}} + height: {{.}}, + {{/height}} + }, + {{/viewport}} + {{#deviceScaleFactor}} + deviceScaleFactor: {{.}}, + {{/deviceScaleFactor}} + {{#hasTouch}} + hasTouch: {{.}}, + {{/hasTouch}} + {{#isMobile}} + isMobile: {{.}}, + {{/isMobile}} + {{#javaScriptEnabled}} + javaScriptEnabled: {{.}}, + {{/javaScriptEnabled}} + {{#acceptDownloads}} + acceptDownloads: {{.}}, + {{/acceptDownloads}} + {{#extraHTTPHeaders}} + extraHTTPHeaders: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/extraHTTPHeaders}} + {{#httpCredentials}} + httpCredentials: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/httpCredentials}} + {{#ignoreHTTPSErrors}} + ignoreHTTPSErrors: {{.}}, + {{/ignoreHTTPSErrors}} + {{#offline}} + offline: {{.}}, + {{/offline}} + {{#actionTimeout}} + actionTimeout: {{.}}, + {{/actionTimeout}} + {{#navigationTimeout}} + navigationTimeout: {{.}}, + {{/navigationTimeout}} + {{#testIdAttribute}} + testIdAttribute: '{{.}}', + {{/testIdAttribute}} + {{#launchOptions}} + launchOptions: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/launchOptions}} + {{#contextOptions}} + contextOptions: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/contextOptions}} + {{#bypassCSP}} + bypassCSP: '{{.}}', + {{/bypassCSP}} + }, + {{/use}} + {{#expect}} + expect: { + {{#timeout}} + timeout: {{.}}, + {{/timeout}} + {{#toHaveScreenshot}} + toHaveScreenshot: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/toHaveScreenshot}} + {{#toMatchSnapshot}} + toMatchSnapshot: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/toMatchSnapshot}} + }, + {{/expect}} + } +} +` +export default playwrightConfigTemplate diff --git a/packages/create-cli/src/utils/prompts.ts b/packages/create-cli/src/utils/prompts.ts index 839481fe..7c4488fd 100644 --- a/packages/create-cli/src/utils/prompts.ts +++ b/packages/create-cli/src/utils/prompts.ts @@ -79,3 +79,12 @@ export function askInitializeGit (onCancel: any = () => true): Promise<{ initial initial: true, }, { onCancel }) } + +export function askCopyPlaywrightProject (onCancel: any): Promise<{ shouldCopyPlaywrightConfig: boolean }> { + return prompts({ + type: 'confirm', + name: 'shouldCopyPlaywrightConfig', + message: 'It looks like you have a playwright config, do you want to copy it?', + initial: true, + }, { onCancel }) +} From 84bc4361ddfe03746ea879bac942a38c37c5d4c3 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Fri, 5 Jan 2024 15:39:21 +0100 Subject: [PATCH 2/7] feat: add ability to sync playwright config with 'sync-playwright' command New feature that allows the user to sync (update or create) their playwright config with the checkly config --- packages/cli/package.json | 4 +- packages/cli/src/commands/sync-playwright.ts | 84 +++++++++++ .../playwright/playwright-config-loader.ts | 15 ++ .../cli/src/playwright/playwright-template.ts | 134 ++++++++++++++++++ .../cli/src/services/checkly-config-loader.ts | 20 ++- .../cli/src/services/handlebars-helpers.ts | 30 ++++ 6 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 packages/cli/src/commands/sync-playwright.ts create mode 100644 packages/cli/src/playwright/playwright-config-loader.ts create mode 100644 packages/cli/src/playwright/playwright-template.ts create mode 100644 packages/cli/src/services/handlebars-helpers.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index dcbca818..34983c9e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -93,7 +93,9 @@ "prompts": "2.4.2", "proxy-from-env": "1.1.0", "tunnel": "0.0.6", - "uuid": "9.0.0" + "uuid": "9.0.0", + "handlebars": "4.7.8", + "recast": "0.23.4" }, "devDependencies": { "@types/config": "3.3.3", diff --git a/packages/cli/src/commands/sync-playwright.ts b/packages/cli/src/commands/sync-playwright.ts new file mode 100644 index 00000000..49826236 --- /dev/null +++ b/packages/cli/src/commands/sync-playwright.ts @@ -0,0 +1,84 @@ +import { BaseCommand } from './baseCommand' +import * as recast from 'recast' +import playwrightConfigTemplate from '../playwright/playwright-template' +import { getChecklyConfigFile } from '../services/checkly-config-loader' +import { loadPlaywrightConfig } from '../playwright/playwright-config-loader' +import { parse } from '../services/handlebars-helpers' +import * as Handlebars from 'handlebars' +import fs from 'fs' +import path from 'path' +import { ux } from '@oclif/core' + +export default class SyncPlaywright extends BaseCommand { + static hidden = true + static description = 'Sync playwright config' + + async run (): Promise { + ux.action.start('Sync playwright config with checkly config', undefined, { stdout: true }) + + const config = await loadPlaywrightConfig() + if (!config) { + return this.handleError('Could not find any playwright.config file.') + } + + Handlebars.registerHelper('parse', parse) + const pwtConfig = Handlebars.compile(playwrightConfigTemplate)(config) + const configFile = getChecklyConfigFile() + if (!configFile) { + return this.handleError('Could not find a checkly config file') + } + + const checklyAst = recast.parse(configFile.checklyConfig) + const checksAst = this.findPropertyByName(checklyAst, 'checks') + if (!checksAst) { + return this.handleError('Could not parse your checkly config file') + } + + const browserCheckAst = this.findPropertyByName(checksAst.value, 'browserChecks') + if (!browserCheckAst) { + return this.handleError('Could not parse your checkly config file') + } + const pwtConfigAst = this.findPropertyByName(recast.parse(pwtConfig), 'playwrightConfig') + this.addOrReplacePlaywrightConfig(browserCheckAst.value, pwtConfigAst) + + const checklyConfigData = recast.print(checklyAst, { tabWidth: 2 }).code + const dir = path.resolve(path.dirname(configFile.fileName)) + this.reWriteChecklyConfigFile(checklyConfigData, configFile.fileName, dir) + + ux.action.stop('✅ ') + this.log('Successfully sync') + this.exit(0) + } + + private handleError (message: string) { + ux.action.stop('❌') + this.log(message) + this.exit(1) + } + + private findPropertyByName (ast: any, name: string): recast.types.namedTypes.Property | undefined { + let node + recast.visit(ast, { + visitProperty (path: any) { + if (path.node.key.name === name) { + node = path.node + } + return false + }, + }) + return node + } + + private addOrReplacePlaywrightConfig (ast: any, node: any) { + const playWrightConfig = this.findPropertyByName(ast, 'playwrightConfig') + if (playWrightConfig) { + playWrightConfig.value = node.value + } else { + ast.properties.push(node) + } + } + + private reWriteChecklyConfigFile (data: string, fileName: string, dir: string) { + fs.writeFileSync(path.join(dir, fileName), data) + } +} diff --git a/packages/cli/src/playwright/playwright-config-loader.ts b/packages/cli/src/playwright/playwright-config-loader.ts new file mode 100644 index 00000000..f1ec8682 --- /dev/null +++ b/packages/cli/src/playwright/playwright-config-loader.ts @@ -0,0 +1,15 @@ +import path from 'path' +import { loadFile } from '../services/checkly-config-loader' + +export async function loadPlaywrightConfig () { + let config + const filenames = ['playwright.config.ts', 'playwright.config.js'] + for (const configFile of filenames) { + const dir = path.resolve(path.dirname(configFile)) + config = await loadFile(path.join(dir, configFile)) + if (config) { + break + } + } + return config +} diff --git a/packages/cli/src/playwright/playwright-template.ts b/packages/cli/src/playwright/playwright-template.ts new file mode 100644 index 00000000..9f6f4e5e --- /dev/null +++ b/packages/cli/src/playwright/playwright-template.ts @@ -0,0 +1,134 @@ +const playwrightConfigTemplate = ` +const playwrightConfig = { + playwrightConfig: { + {{#timeout}} + timeout: {{.}}, + {{/timeout}} + {{#use}} + use: { + {{#baseURL}} + baseURL: '{{.}}', + {{/baseURL}} + {{#colorScheme}} + colorScheme: '{{.}}', + {{/colorScheme}} + {{#geolocation}} + geolocation: { + {{#longitude}} + longitude: {{.}}, + {{/longitude}} + {{#latitude}} + latitude: {{.}}, + {{/latitude}} + {{#accuracy}} + accuracy: {{.}}, + {{/accuracy}} + }, + {{/geolocation}} + {{#locale}} + locale: '{{.}}', + {{/locale}} + {{#if permissions}} + permissions: [{{#permissions}}'{{.}}',{{/permissions}}], + {{/if}} + {{#timezoneId}} + timezoneId: '{{.}}', + {{/timezoneId}} + {{#viewport}} + viewport: { + {{#width}} + width: {{.}}, + {{/width}} + {{#height}} + height: {{.}}, + {{/height}} + }, + {{/viewport}} + {{#deviceScaleFactor}} + deviceScaleFactor: {{.}}, + {{/deviceScaleFactor}} + {{#hasTouch}} + hasTouch: {{.}}, + {{/hasTouch}} + {{#isMobile}} + isMobile: {{.}}, + {{/isMobile}} + {{#javaScriptEnabled}} + javaScriptEnabled: {{.}}, + {{/javaScriptEnabled}} + {{#acceptDownloads}} + acceptDownloads: {{.}}, + {{/acceptDownloads}} + {{#extraHTTPHeaders}} + extraHTTPHeaders: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/extraHTTPHeaders}} + {{#httpCredentials}} + httpCredentials: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/httpCredentials}} + {{#ignoreHTTPSErrors}} + ignoreHTTPSErrors: {{.}}, + {{/ignoreHTTPSErrors}} + {{#offline}} + offline: {{.}}, + {{/offline}} + {{#actionTimeout}} + actionTimeout: {{.}}, + {{/actionTimeout}} + {{#navigationTimeout}} + navigationTimeout: {{.}}, + {{/navigationTimeout}} + {{#testIdAttribute}} + testIdAttribute: '{{.}}', + {{/testIdAttribute}} + {{#launchOptions}} + launchOptions: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/launchOptions}} + {{#contextOptions}} + contextOptions: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/contextOptions}} + {{#bypassCSP}} + bypassCSP: '{{.}}', + {{/bypassCSP}} + }, + {{/use}} + {{#expect}} + expect: { + {{#timeout}} + timeout: {{.}}, + {{/timeout}} + {{#toHaveScreenshot}} + toHaveScreenshot: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/toHaveScreenshot}} + {{#toMatchSnapshot}} + toMatchSnapshot: { + {{#each .}} + {{@key}}: {{{parse this}}}, + {{/each}} + }, + {{/toMatchSnapshot}} + }, + {{/expect}} + } +} +` +export default playwrightConfigTemplate diff --git a/packages/cli/src/services/checkly-config-loader.ts b/packages/cli/src/services/checkly-config-loader.ts index 3c2b3863..3cf44d47 100644 --- a/packages/cli/src/services/checkly-config-loader.ts +++ b/packages/cli/src/services/checkly-config-loader.ts @@ -7,6 +7,7 @@ import { Construct } from '../constructs/construct' import type { Region } from '..' import { ReporterType } from '../reporters/reporter' import { BrowserPlaywrightDefaults } from '../constructs/browser-defaults' +import * as fs from 'fs' export type CheckConfigDefaults = Pick { let config Session.loadingChecklyConfigFile = true diff --git a/packages/cli/src/services/handlebars-helpers.ts b/packages/cli/src/services/handlebars-helpers.ts new file mode 100644 index 00000000..0a9a0b65 --- /dev/null +++ b/packages/cli/src/services/handlebars-helpers.ts @@ -0,0 +1,30 @@ +// Handlebars do not handle properly nested objects/arrays +export function parse (input: any): any { + if (typeof input === 'string') { + return `'${input}'` + } + if (typeof input === 'number' || typeof input === 'boolean') { + return input + } + if (Array.isArray(input)) { + let arr + for (const o of input) { + if (!arr) { + arr = parse(o) + continue + } + arr = `${arr}, ${parse(o)}` + } + return `[${arr}]` + } + if (typeof input === 'object') { + const keys = Object.keys(input) + let returnObj = '' + for (const key of keys) { + const curr = `${key}: ${parse(input[key])}` + returnObj = `${returnObj} ${curr},` + } + return `{${returnObj}}` + } + return input +} From 455f376ce7a36305ed1a3c479679fde95a54f78d Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Fri, 5 Jan 2024 16:03:37 +0100 Subject: [PATCH 3/7] fix: update package-lock.json file --- package-lock.json | 238 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 192 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9ed30c56..7f3104b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4689,6 +4689,29 @@ "dev": true, "license": "MIT" }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "dev": true, @@ -4802,7 +4825,6 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.5", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5326,7 +5348,6 @@ }, "node_modules/call-bind": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.1", @@ -6347,7 +6368,6 @@ }, "node_modules/define-properties": { "version": "1.2.0", - "dev": true, "license": "MIT", "dependencies": { "has-property-descriptors": "^1.0.0", @@ -7833,7 +7853,6 @@ }, "node_modules/for-each": { "version": "0.3.3", - "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.3" @@ -7932,7 +7951,6 @@ }, "node_modules/function-bind": { "version": "1.1.1", - "dev": true, "license": "MIT" }, "node_modules/function.prototype.name": { @@ -7997,7 +8015,6 @@ }, "node_modules/get-intrinsic": { "version": "1.2.0", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.1", @@ -8214,7 +8231,6 @@ }, "node_modules/gopd": { "version": "1.0.1", - "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" @@ -8269,6 +8285,26 @@ "node": ">=8.0.0" } }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "dev": true, @@ -8279,7 +8315,6 @@ }, "node_modules/has": { "version": "1.0.3", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.1" @@ -8325,7 +8360,6 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.0", - "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.1" @@ -8347,7 +8381,6 @@ }, "node_modules/has-symbols": { "version": "1.0.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8358,7 +8391,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.0", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" @@ -8771,7 +8803,6 @@ }, "node_modules/is-arguments": { "version": "1.1.1", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -8829,7 +8860,6 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -8901,7 +8931,6 @@ }, "node_modules/is-generator-function": { "version": "1.0.10", - "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -8928,6 +8957,21 @@ "dev": true, "license": "MIT" }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "dev": true, @@ -9086,7 +9130,6 @@ }, "node_modules/is-typed-array": { "version": "1.1.10", - "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.5", @@ -12333,6 +12376,11 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "node_modules/nice-try": { "version": "1.0.5", "license": "MIT" @@ -13019,9 +13067,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -13036,7 +13098,6 @@ }, "node_modules/object.assign": { "version": "4.1.4", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -14057,6 +14118,21 @@ "once": "^1.3.0" } }, + "node_modules/recast": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", + "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", + "dependencies": { + "assert": "^2.0.0", + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, "node_modules/rechoir": { "version": "0.6.2", "dev": true, @@ -14529,7 +14605,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -15264,6 +15339,18 @@ "node": ">=4.2.0" } }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "dev": true, @@ -15369,7 +15456,6 @@ }, "node_modules/util": { "version": "0.12.5", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -15652,7 +15738,6 @@ }, "node_modules/which-typed-array": { "version": "1.1.9", - "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.5", @@ -16440,6 +16525,7 @@ "dotenv": "16.3.1", "git-repo-info": "2.1.1", "glob": "10.3.1", + "handlebars": "4.7.8", "indent-string": "4.0.0", "jwt-decode": "3.1.2", "log-symbols": "4.1.0", @@ -16448,6 +16534,7 @@ "p-queue": "6.6.2", "prompts": "2.4.2", "proxy-from-env": "1.1.0", + "recast": "0.23.4", "tunnel": "0.0.6", "uuid": "9.0.0" }, @@ -16600,9 +16687,11 @@ "debug": "4.3.4", "execa": "5.1.0", "giget": "1.1.2", + "handlebars": "4.7.8", "ora": "5.4.1", "passwd-user": "3.0.0", "prompts": "2.4.2", + "recast": "0.23.4", "unique-names-generator": "4.7.1" }, "bin": { @@ -20067,6 +20156,26 @@ "version": "2.0.6", "dev": true }, + "assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "requires": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "requires": { + "tslib": "^2.0.1" + } + }, "astral-regex": { "version": "2.0.0", "dev": true @@ -20148,8 +20257,7 @@ "version": "1.7.0" }, "available-typed-arrays": { - "version": "1.0.5", - "dev": true + "version": "1.0.5" }, "aws-sdk": { "version": "2.1356.0", @@ -20509,7 +20617,6 @@ }, "call-bind": { "version": "1.0.2", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -20598,6 +20705,7 @@ "dotenv": "16.3.1", "git-repo-info": "2.1.1", "glob": "10.3.1", + "handlebars": "4.7.8", "indent-string": "4.0.0", "jest": "29.6.2", "jwt-decode": "3.1.2", @@ -20609,6 +20717,7 @@ "p-queue": "6.6.2", "prompts": "2.4.2", "proxy-from-env": "1.1.0", + "recast": "0.23.4", "simple-git-hooks": "2.8.1", "ts-jest": "29.0.3", "ts-node": "10.9.1", @@ -21137,10 +21246,12 @@ "debug": "4.3.4", "execa": "5.1.0", "giget": "1.1.2", + "handlebars": "4.7.8", "jest": "29.6.2", "ora": "5.4.1", "passwd-user": "3.0.0", "prompts": "2.4.2", + "recast": "0.23.4", "rimraf": "5.0.1", "ts-jest": "29.0.3", "ts-node": "10.9.1", @@ -21471,7 +21582,6 @@ }, "define-properties": { "version": "1.2.0", - "dev": true, "requires": { "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" @@ -22404,7 +22514,6 @@ }, "for-each": { "version": "0.3.3", - "dev": true, "requires": { "is-callable": "^1.1.3" } @@ -22466,8 +22575,7 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "dev": true + "version": "1.1.1" }, "function.prototype.name": { "version": "1.1.5", @@ -22508,7 +22616,6 @@ }, "get-intrinsic": { "version": "1.2.0", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -22637,7 +22744,6 @@ }, "gopd": { "version": "1.0.1", - "dev": true, "requires": { "get-intrinsic": "^1.1.3" } @@ -22674,13 +22780,24 @@ "version": "2.0.0", "dev": true }, + "handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, "hard-rejection": { "version": "2.1.0", "dev": true }, "has": { "version": "1.0.3", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -22708,7 +22825,6 @@ }, "has-property-descriptors": { "version": "1.0.0", - "dev": true, "requires": { "get-intrinsic": "^1.1.1" } @@ -22718,12 +22834,10 @@ "dev": true }, "has-symbols": { - "version": "1.0.3", - "dev": true + "version": "1.0.3" }, "has-tostringtag": { "version": "1.0.0", - "dev": true, "requires": { "has-symbols": "^1.0.2" } @@ -22988,7 +23102,6 @@ }, "is-arguments": { "version": "1.1.1", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -23022,8 +23135,7 @@ } }, "is-callable": { - "version": "1.2.7", - "dev": true + "version": "1.2.7" }, "is-core-module": { "version": "2.11.0", @@ -23056,7 +23168,6 @@ }, "is-generator-function": { "version": "1.0.10", - "dev": true, "requires": { "has-tostringtag": "^1.0.0" } @@ -23071,6 +23182,15 @@ "version": "1.0.1", "dev": true }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "is-negative-zero": { "version": "2.0.2", "dev": true @@ -23151,7 +23271,6 @@ }, "is-typed-array": { "version": "1.1.10", - "dev": true, "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -25381,6 +25500,11 @@ "version": "0.6.3", "dev": true }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "nice-try": { "version": "1.0.5" }, @@ -25846,16 +25970,23 @@ "version": "1.12.3", "dev": true }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "object-keys": { - "version": "1.1.1", - "dev": true + "version": "1.1.1" }, "object-treeify": { "version": "1.1.33" }, "object.assign": { "version": "4.1.4", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -26477,6 +26608,18 @@ "once": "^1.3.0" } }, + "recast": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", + "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", + "requires": { + "assert": "^2.0.0", + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tslib": "^2.0.1" + } + }, "rechoir": { "version": "0.6.2", "dev": true, @@ -26741,8 +26884,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-support": { "version": "0.5.13", @@ -27208,6 +27350,12 @@ "typescript": { "version": "4.9.4" }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true + }, "unbox-primitive": { "version": "1.0.2", "dev": true, @@ -27276,7 +27424,6 @@ }, "util": { "version": "0.12.5", - "dev": true, "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -27482,7 +27629,6 @@ }, "which-typed-array": { "version": "1.1.9", - "dev": true, "requires": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", From 07d511b3d665435c52c45d10880e36991f3b4f23 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Mon, 8 Jan 2024 11:54:35 +0100 Subject: [PATCH 4/7] fix: do not fail if file does not exist --- packages/cli/src/services/checkly-config-loader.ts | 3 +++ packages/create-cli/src/actions/playwright.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/cli/src/services/checkly-config-loader.ts b/packages/cli/src/services/checkly-config-loader.ts index 3cf44d47..20808ea6 100644 --- a/packages/cli/src/services/checkly-config-loader.ts +++ b/packages/cli/src/services/checkly-config-loader.ts @@ -95,6 +95,9 @@ export function getChecklyConfigFile (): {checklyConfig: string, fileName: strin let config for (const configFile of filenames) { const dir = path.resolve(path.dirname(configFile)) + if (!existsSync(path.resolve(dir, configFile))) { + continue + } const file = fs.readFileSync(path.resolve(dir, configFile)) if (file) { config = { diff --git a/packages/create-cli/src/actions/playwright.ts b/packages/create-cli/src/actions/playwright.ts index 030c9b63..ba302ac3 100644 --- a/packages/create-cli/src/actions/playwright.ts +++ b/packages/create-cli/src/actions/playwright.ts @@ -53,6 +53,9 @@ function getChecklyConfigFile (): {checklyConfig: string, fileName: string} | un let config for (const configFile of filenames) { const dir = path.resolve(path.dirname(configFile)) + if (!fs.existsSync(path.resolve(dir, configFile))) { + continue + } const file = fs.readFileSync(path.resolve(dir, configFile)) if (file) { config = { From 692235f9069bbee9fd2eee6b09c54942415b6d50 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Thu, 11 Jan 2024 12:08:38 +0100 Subject: [PATCH 5/7] refactor: use JSON5 instead of handlebars --- package-lock.json | 6 +- packages/cli/package.json | 6 +- packages/cli/src/commands/sync-playwright.ts | 10 +- .../playwright/playwright-config-loader.ts | 4 + .../playwright/playwright-config-template.ts | 56 ++++++++ .../cli/src/playwright/playwright-template.ts | 134 ------------------ .../cli/src/services/handlebars-helpers.ts | 30 ---- packages/create-cli/package.json | 1 - packages/create-cli/src/actions/playwright.ts | 11 +- .../src/utils/handlebars-helpers.ts | 30 ---- .../src/utils/playwright-config-template.ts | 55 +++++++ .../src/utils/playwright-template.ts | 134 ------------------ 12 files changed, 131 insertions(+), 346 deletions(-) create mode 100644 packages/cli/src/playwright/playwright-config-template.ts delete mode 100644 packages/cli/src/playwright/playwright-template.ts delete mode 100644 packages/cli/src/services/handlebars-helpers.ts delete mode 100644 packages/create-cli/src/utils/handlebars-helpers.ts create mode 100644 packages/create-cli/src/utils/playwright-config-template.ts delete mode 100644 packages/create-cli/src/utils/playwright-template.ts diff --git a/package-lock.json b/package-lock.json index 7f3104b8..d1ed0358 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11016,7 +11016,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -16527,6 +16526,7 @@ "glob": "10.3.1", "handlebars": "4.7.8", "indent-string": "4.0.0", + "json5": "2.2.3", "jwt-decode": "3.1.2", "log-symbols": "4.1.0", "luxon": "3.3.0", @@ -20708,6 +20708,7 @@ "handlebars": "4.7.8", "indent-string": "4.0.0", "jest": "29.6.2", + "json5": "*", "jwt-decode": "3.1.2", "log-symbols": "4.1.0", "luxon": "3.3.0", @@ -24624,8 +24625,7 @@ "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jsonc-eslint-parser": { "version": "2.1.0", diff --git a/packages/cli/package.json b/packages/cli/package.json index 34983c9e..a32553bc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -85,6 +85,7 @@ "git-repo-info": "2.1.1", "glob": "10.3.1", "indent-string": "4.0.0", + "json5": "2.2.3", "jwt-decode": "3.1.2", "log-symbols": "4.1.0", "luxon": "3.3.0", @@ -92,10 +93,9 @@ "p-queue": "6.6.2", "prompts": "2.4.2", "proxy-from-env": "1.1.0", + "recast": "0.23.4", "tunnel": "0.0.6", - "uuid": "9.0.0", - "handlebars": "4.7.8", - "recast": "0.23.4" + "uuid": "9.0.0" }, "devDependencies": { "@types/config": "3.3.3", diff --git a/packages/cli/src/commands/sync-playwright.ts b/packages/cli/src/commands/sync-playwright.ts index 49826236..5143e22d 100644 --- a/packages/cli/src/commands/sync-playwright.ts +++ b/packages/cli/src/commands/sync-playwright.ts @@ -1,13 +1,11 @@ import { BaseCommand } from './baseCommand' import * as recast from 'recast' -import playwrightConfigTemplate from '../playwright/playwright-template' import { getChecklyConfigFile } from '../services/checkly-config-loader' import { loadPlaywrightConfig } from '../playwright/playwright-config-loader' -import { parse } from '../services/handlebars-helpers' -import * as Handlebars from 'handlebars' import fs from 'fs' import path from 'path' import { ux } from '@oclif/core' +import PlaywrightConfigTemplate from '../playwright/playwright-config-template' export default class SyncPlaywright extends BaseCommand { static hidden = true @@ -21,14 +19,12 @@ export default class SyncPlaywright extends BaseCommand { return this.handleError('Could not find any playwright.config file.') } - Handlebars.registerHelper('parse', parse) - const pwtConfig = Handlebars.compile(playwrightConfigTemplate)(config) const configFile = getChecklyConfigFile() if (!configFile) { return this.handleError('Could not find a checkly config file') } - const checklyAst = recast.parse(configFile.checklyConfig) + const checksAst = this.findPropertyByName(checklyAst, 'checks') if (!checksAst) { return this.handleError('Could not parse your checkly config file') @@ -38,6 +34,8 @@ export default class SyncPlaywright extends BaseCommand { if (!browserCheckAst) { return this.handleError('Could not parse your checkly config file') } + + const pwtConfig = new PlaywrightConfigTemplate(config).getConfigTemplate() const pwtConfigAst = this.findPropertyByName(recast.parse(pwtConfig), 'playwrightConfig') this.addOrReplacePlaywrightConfig(browserCheckAst.value, pwtConfigAst) diff --git a/packages/cli/src/playwright/playwright-config-loader.ts b/packages/cli/src/playwright/playwright-config-loader.ts index f1ec8682..82244b8f 100644 --- a/packages/cli/src/playwright/playwright-config-loader.ts +++ b/packages/cli/src/playwright/playwright-config-loader.ts @@ -1,10 +1,14 @@ import path from 'path' import { loadFile } from '../services/checkly-config-loader' +import fs from 'fs' export async function loadPlaywrightConfig () { let config const filenames = ['playwright.config.ts', 'playwright.config.js'] for (const configFile of filenames) { + if (!fs.existsSync(path.resolve(path.dirname(configFile)))) { + continue + } const dir = path.resolve(path.dirname(configFile)) config = await loadFile(path.join(dir, configFile)) if (config) { diff --git a/packages/cli/src/playwright/playwright-config-template.ts b/packages/cli/src/playwright/playwright-config-template.ts new file mode 100644 index 00000000..e090791c --- /dev/null +++ b/packages/cli/src/playwright/playwright-config-template.ts @@ -0,0 +1,56 @@ +import { PlaywrightConfig, Use, Expect } from '../constructs/browser-defaults' +import * as JSON5 from 'json5' + +export default class PlaywrightConfigTemplate { + playwrightConfig: PlaywrightConfig + + constructor ({ use, expect, timeout }: any) { + this.playwrightConfig = {} + if (use) { + this.playwrightConfig.use = this.getUseParams(use) + } + if (expect) { + this.playwrightConfig.expect = this.getExpectParams(expect) + } + this.playwrightConfig.timeout = timeout + } + + private getUseParams (use: any): Use { + return { + baseURL: use.baseURL, + colorScheme: use.colorScheme, + geolocation: use.geolocation, + locale: use.locale, + permissions: use.permissions, + timezoneId: use.timezoneId, + viewport: use.viewport, + deviceScaleFactor: use.deviceScaleFactor, + hasTouch: use.hasTouch, + isMobile: use.isMobile, + javaScriptEnabled: use.javaScriptEnabled, + acceptDownloads: use.acceptDownloads, + extraHTTPHeaders: use.extraHTTPHeaders, + httpCredentials: use.httpCredentials, + ignoreHTTPSErrors: use.ignoreHTTPSErrors, + offline: use.offline, + actionTimeout: use.actionTimeout, + navigationTimeout: use.navigationTimeout, + testIdAttribute: use.testIdAttribute, + launchOptions: use.launchOptions, + contextOptions: use.contextOptions, + bypassCSP: use.bypassCSP, + } + } + + private getExpectParams (expect: any): Expect { + return { + timeout: expect.timeout, + toHaveScreenshot: expect.toHaveScreenshot, + toMatchSnapshot: expect.toMatchSnapshot, + } + } + + getConfigTemplate () { + return `const playwrightConfig = ${JSON5.stringify(this, { space: 2 })}` + } +} diff --git a/packages/cli/src/playwright/playwright-template.ts b/packages/cli/src/playwright/playwright-template.ts deleted file mode 100644 index 9f6f4e5e..00000000 --- a/packages/cli/src/playwright/playwright-template.ts +++ /dev/null @@ -1,134 +0,0 @@ -const playwrightConfigTemplate = ` -const playwrightConfig = { - playwrightConfig: { - {{#timeout}} - timeout: {{.}}, - {{/timeout}} - {{#use}} - use: { - {{#baseURL}} - baseURL: '{{.}}', - {{/baseURL}} - {{#colorScheme}} - colorScheme: '{{.}}', - {{/colorScheme}} - {{#geolocation}} - geolocation: { - {{#longitude}} - longitude: {{.}}, - {{/longitude}} - {{#latitude}} - latitude: {{.}}, - {{/latitude}} - {{#accuracy}} - accuracy: {{.}}, - {{/accuracy}} - }, - {{/geolocation}} - {{#locale}} - locale: '{{.}}', - {{/locale}} - {{#if permissions}} - permissions: [{{#permissions}}'{{.}}',{{/permissions}}], - {{/if}} - {{#timezoneId}} - timezoneId: '{{.}}', - {{/timezoneId}} - {{#viewport}} - viewport: { - {{#width}} - width: {{.}}, - {{/width}} - {{#height}} - height: {{.}}, - {{/height}} - }, - {{/viewport}} - {{#deviceScaleFactor}} - deviceScaleFactor: {{.}}, - {{/deviceScaleFactor}} - {{#hasTouch}} - hasTouch: {{.}}, - {{/hasTouch}} - {{#isMobile}} - isMobile: {{.}}, - {{/isMobile}} - {{#javaScriptEnabled}} - javaScriptEnabled: {{.}}, - {{/javaScriptEnabled}} - {{#acceptDownloads}} - acceptDownloads: {{.}}, - {{/acceptDownloads}} - {{#extraHTTPHeaders}} - extraHTTPHeaders: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/extraHTTPHeaders}} - {{#httpCredentials}} - httpCredentials: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/httpCredentials}} - {{#ignoreHTTPSErrors}} - ignoreHTTPSErrors: {{.}}, - {{/ignoreHTTPSErrors}} - {{#offline}} - offline: {{.}}, - {{/offline}} - {{#actionTimeout}} - actionTimeout: {{.}}, - {{/actionTimeout}} - {{#navigationTimeout}} - navigationTimeout: {{.}}, - {{/navigationTimeout}} - {{#testIdAttribute}} - testIdAttribute: '{{.}}', - {{/testIdAttribute}} - {{#launchOptions}} - launchOptions: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/launchOptions}} - {{#contextOptions}} - contextOptions: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/contextOptions}} - {{#bypassCSP}} - bypassCSP: '{{.}}', - {{/bypassCSP}} - }, - {{/use}} - {{#expect}} - expect: { - {{#timeout}} - timeout: {{.}}, - {{/timeout}} - {{#toHaveScreenshot}} - toHaveScreenshot: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/toHaveScreenshot}} - {{#toMatchSnapshot}} - toMatchSnapshot: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/toMatchSnapshot}} - }, - {{/expect}} - } -} -` -export default playwrightConfigTemplate diff --git a/packages/cli/src/services/handlebars-helpers.ts b/packages/cli/src/services/handlebars-helpers.ts deleted file mode 100644 index 0a9a0b65..00000000 --- a/packages/cli/src/services/handlebars-helpers.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Handlebars do not handle properly nested objects/arrays -export function parse (input: any): any { - if (typeof input === 'string') { - return `'${input}'` - } - if (typeof input === 'number' || typeof input === 'boolean') { - return input - } - if (Array.isArray(input)) { - let arr - for (const o of input) { - if (!arr) { - arr = parse(o) - continue - } - arr = `${arr}, ${parse(o)}` - } - return `[${arr}]` - } - if (typeof input === 'object') { - const keys = Object.keys(input) - let returnObj = '' - for (const key of keys) { - const curr = `${key}: ${parse(input[key])}` - returnObj = `${returnObj} ${curr},` - } - return `{${returnObj}}` - } - return input -} diff --git a/packages/create-cli/package.json b/packages/create-cli/package.json index d6e33f9c..bd4c2939 100644 --- a/packages/create-cli/package.json +++ b/packages/create-cli/package.json @@ -39,7 +39,6 @@ }, "homepage": "https://github.com/checkly/checkly-cli#readme", "dependencies": { - "handlebars": "4.7.8", "@oclif/core": "2.8.11", "@oclif/plugin-help": "5.1.20", "@oclif/plugin-plugins": "2.3.0", diff --git a/packages/create-cli/src/actions/playwright.ts b/packages/create-cli/src/actions/playwright.ts index ba302ac3..f7a5e617 100644 --- a/packages/create-cli/src/actions/playwright.ts +++ b/packages/create-cli/src/actions/playwright.ts @@ -3,33 +3,34 @@ import { spinner } from '../utils/terminal' import { loadPlaywrightConfig } from '../utils/directory' import * as recast from 'recast' import * as path from 'path' -import playwrightConfigTemplate from '../utils/playwright-template' -import * as Handlebars from 'handlebars' import * as fs from 'fs' import * as ora from 'ora' -import { parse } from '../utils/handlebars-helpers' +import PlaywrightConfigTemplate from '../utils/playwright-config-template' export async function copyPlaywrightConfig (dirPath: string, playwrightConfigFileName: string) { const copySpinner = spinner('Copying your playwright config') try { const config = await loadPlaywrightConfig(path.join(dirPath, playwrightConfigFileName)) - Handlebars.registerHelper('parse', parse) - const pwtConfig = Handlebars.compile(playwrightConfigTemplate, { compat: true })(config) + const pwtConfig = new PlaywrightConfigTemplate(config).getConfigTemplate() if (!pwtConfig) { return } + const checklyConfig = getChecklyConfigFile() if (!checklyConfig) { return handleError(copySpinner, 'Could not find your checkly config file') } + const checklyAst = recast.parse(checklyConfig.checklyConfig) const checksAst = findPropertyByName(checklyAst, 'checks') if (!checksAst) { return handleError(copySpinner, 'Could not parse you checkly file correctly') } + const browserCheckAst = findPropertyByName(checksAst.value, 'browserChecks') if (!browserCheckAst) { return handleError(copySpinner, 'Could not parse you checkly file correctly') } + const pwtConfigAst = findPropertyByName(recast.parse(pwtConfig), 'playwrightConfig') addOrReplacePlaywrightConfig(browserCheckAst.value, pwtConfigAst) fs.writeFileSync(path.join(dirPath, checklyConfig.fileName), recast.print(checklyAst, { tabWidth: 2 }).code) diff --git a/packages/create-cli/src/utils/handlebars-helpers.ts b/packages/create-cli/src/utils/handlebars-helpers.ts deleted file mode 100644 index 0a9a0b65..00000000 --- a/packages/create-cli/src/utils/handlebars-helpers.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Handlebars do not handle properly nested objects/arrays -export function parse (input: any): any { - if (typeof input === 'string') { - return `'${input}'` - } - if (typeof input === 'number' || typeof input === 'boolean') { - return input - } - if (Array.isArray(input)) { - let arr - for (const o of input) { - if (!arr) { - arr = parse(o) - continue - } - arr = `${arr}, ${parse(o)}` - } - return `[${arr}]` - } - if (typeof input === 'object') { - const keys = Object.keys(input) - let returnObj = '' - for (const key of keys) { - const curr = `${key}: ${parse(input[key])}` - returnObj = `${returnObj} ${curr},` - } - return `{${returnObj}}` - } - return input -} diff --git a/packages/create-cli/src/utils/playwright-config-template.ts b/packages/create-cli/src/utils/playwright-config-template.ts new file mode 100644 index 00000000..a32ff257 --- /dev/null +++ b/packages/create-cli/src/utils/playwright-config-template.ts @@ -0,0 +1,55 @@ +import * as JSON5 from 'json5' + +export default class PlaywrightConfigTemplate { + playwrightConfig: Record + + constructor ({ use, expect, timeout }: any) { + this.playwrightConfig = {} + if (use) { + this.playwrightConfig.use = this.getUseParams(use) + } + if (expect) { + this.playwrightConfig.expect = this.getExpectParams(expect) + } + this.playwrightConfig.timeout = timeout + } + + private getUseParams (use: any): Record { + return { + baseURL: use.baseURL, + colorScheme: use.colorScheme, + geolocation: use.geolocation, + locale: use.locale, + permissions: use.permissions, + timezoneId: use.timezoneId, + viewport: use.viewport, + deviceScaleFactor: use.deviceScaleFactor, + hasTouch: use.hasTouch, + isMobile: use.isMobile, + javaScriptEnabled: use.javaScriptEnabled, + acceptDownloads: use.acceptDownloads, + extraHTTPHeaders: use.extraHTTPHeaders, + httpCredentials: use.httpCredentials, + ignoreHTTPSErrors: use.ignoreHTTPSErrors, + offline: use.offline, + actionTimeout: use.actionTimeout, + navigationTimeout: use.navigationTimeout, + testIdAttribute: use.testIdAttribute, + launchOptions: use.launchOptions, + contextOptions: use.contextOptions, + bypassCSP: use.bypassCSP, + } + } + + private getExpectParams (expect: any): Record { + return { + timeout: expect.timeout, + toHaveScreenshot: expect.toHaveScreenshot, + toMatchSnapshot: expect.toMatchSnapshot, + } + } + + getConfigTemplate () { + return `const playwrightConfig = ${JSON5.stringify(this, { space: 2 })}` + } +} diff --git a/packages/create-cli/src/utils/playwright-template.ts b/packages/create-cli/src/utils/playwright-template.ts deleted file mode 100644 index 9f6f4e5e..00000000 --- a/packages/create-cli/src/utils/playwright-template.ts +++ /dev/null @@ -1,134 +0,0 @@ -const playwrightConfigTemplate = ` -const playwrightConfig = { - playwrightConfig: { - {{#timeout}} - timeout: {{.}}, - {{/timeout}} - {{#use}} - use: { - {{#baseURL}} - baseURL: '{{.}}', - {{/baseURL}} - {{#colorScheme}} - colorScheme: '{{.}}', - {{/colorScheme}} - {{#geolocation}} - geolocation: { - {{#longitude}} - longitude: {{.}}, - {{/longitude}} - {{#latitude}} - latitude: {{.}}, - {{/latitude}} - {{#accuracy}} - accuracy: {{.}}, - {{/accuracy}} - }, - {{/geolocation}} - {{#locale}} - locale: '{{.}}', - {{/locale}} - {{#if permissions}} - permissions: [{{#permissions}}'{{.}}',{{/permissions}}], - {{/if}} - {{#timezoneId}} - timezoneId: '{{.}}', - {{/timezoneId}} - {{#viewport}} - viewport: { - {{#width}} - width: {{.}}, - {{/width}} - {{#height}} - height: {{.}}, - {{/height}} - }, - {{/viewport}} - {{#deviceScaleFactor}} - deviceScaleFactor: {{.}}, - {{/deviceScaleFactor}} - {{#hasTouch}} - hasTouch: {{.}}, - {{/hasTouch}} - {{#isMobile}} - isMobile: {{.}}, - {{/isMobile}} - {{#javaScriptEnabled}} - javaScriptEnabled: {{.}}, - {{/javaScriptEnabled}} - {{#acceptDownloads}} - acceptDownloads: {{.}}, - {{/acceptDownloads}} - {{#extraHTTPHeaders}} - extraHTTPHeaders: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/extraHTTPHeaders}} - {{#httpCredentials}} - httpCredentials: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/httpCredentials}} - {{#ignoreHTTPSErrors}} - ignoreHTTPSErrors: {{.}}, - {{/ignoreHTTPSErrors}} - {{#offline}} - offline: {{.}}, - {{/offline}} - {{#actionTimeout}} - actionTimeout: {{.}}, - {{/actionTimeout}} - {{#navigationTimeout}} - navigationTimeout: {{.}}, - {{/navigationTimeout}} - {{#testIdAttribute}} - testIdAttribute: '{{.}}', - {{/testIdAttribute}} - {{#launchOptions}} - launchOptions: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/launchOptions}} - {{#contextOptions}} - contextOptions: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/contextOptions}} - {{#bypassCSP}} - bypassCSP: '{{.}}', - {{/bypassCSP}} - }, - {{/use}} - {{#expect}} - expect: { - {{#timeout}} - timeout: {{.}}, - {{/timeout}} - {{#toHaveScreenshot}} - toHaveScreenshot: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/toHaveScreenshot}} - {{#toMatchSnapshot}} - toMatchSnapshot: { - {{#each .}} - {{@key}}: {{{parse this}}}, - {{/each}} - }, - {{/toMatchSnapshot}} - }, - {{/expect}} - } -} -` -export default playwrightConfigTemplate From 466c6b041cc6eda4fbb4ad3ba1a796e71e32edd1 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Thu, 11 Jan 2024 16:22:55 +0100 Subject: [PATCH 6/7] test: add test for sync-playwright feature --- package-lock.json | 76 ++++++++++++++++++- .../checkly.config.original.ts | 21 +++++ .../test-playwright-project/checkly.config.ts | 21 +++++ .../playwright.config.ts | 23 ++++++ .../cli/e2e/__tests__/sync-playwright.spec.ts | 36 +++++++++ packages/cli/package.json | 1 + .../e2e/__tests__/bootstrap.spec.ts | 36 +++++++++ .../fixtures/playwright-project/package.json | 16 ++++ .../playwright-project/playwright.config.ts | 23 ++++++ 9 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.original.ts create mode 100644 packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.ts create mode 100644 packages/cli/e2e/__tests__/fixtures/test-playwright-project/playwright.config.ts create mode 100644 packages/cli/e2e/__tests__/sync-playwright.spec.ts create mode 100644 packages/create-cli/e2e/__tests__/fixtures/playwright-project/package.json create mode 100644 packages/create-cli/e2e/__tests__/fixtures/playwright-project/playwright.config.ts diff --git a/package-lock.json b/package-lock.json index d1ed0358..fa73a4fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3327,6 +3327,21 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", + "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "dev": true, + "dependencies": { + "playwright": "1.40.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -13712,6 +13727,36 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz", + "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==", + "dev": true, + "dependencies": { + "playwright-core": "1.40.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz", + "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.13", "dev": true, @@ -16524,7 +16569,6 @@ "dotenv": "16.3.1", "git-repo-info": "2.1.1", "glob": "10.3.1", - "handlebars": "4.7.8", "indent-string": "4.0.0", "json5": "2.2.3", "jwt-decode": "3.1.2", @@ -16542,6 +16586,7 @@ "checkly": "bin/run" }, "devDependencies": { + "@playwright/test": "1.40.1", "@types/config": "3.3.3", "@types/glob": "8.0.0", "@types/jest": "29.5.3", @@ -19296,6 +19341,15 @@ "version": "0.11.0", "optional": true }, + "@playwright/test": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz", + "integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==", + "dev": true, + "requires": { + "playwright": "1.40.1" + } + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -20683,6 +20737,7 @@ "@oclif/plugin-not-found": "2.3.23", "@oclif/plugin-plugins": "2.3.0", "@oclif/plugin-warn-if-update-available": "2.0.24", + "@playwright/test": "1.40.1", "@types/config": "3.3.3", "@types/glob": "8.0.0", "@types/jest": "29.5.3", @@ -20705,10 +20760,9 @@ "dotenv": "16.3.1", "git-repo-info": "2.1.1", "glob": "10.3.1", - "handlebars": "4.7.8", "indent-string": "4.0.0", "jest": "29.6.2", - "json5": "*", + "json5": "2.2.3", "jwt-decode": "3.1.2", "log-symbols": "4.1.0", "luxon": "3.3.0", @@ -26369,6 +26423,22 @@ } } }, + "playwright": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz", + "integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.40.1" + } + }, + "playwright-core": { + "version": "1.40.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz", + "integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==", + "dev": true + }, "postcss-selector-parser": { "version": "6.0.13", "dev": true, diff --git a/packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.original.ts b/packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.original.ts new file mode 100644 index 00000000..f0a82ffd --- /dev/null +++ b/packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.original.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'checkly' + +const config = defineConfig({ + projectName: 'Test Playwright Project', + logicalId: 'test-playwright-project', + repoUrl: 'https://github.com/checkly/checkly-cli', + checks: { + locations: ['us-east-1', 'eu-west-1'], + tags: ['mac'], + runtimeId: '2022.10', + checkMatch: '**/*.check.ts', + browserChecks: { + testMatch: '**/__checks__/*.test.ts', + }, + }, + cli: { + runLocation: 'us-east-1', + }, +}) + +export default config diff --git a/packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.ts b/packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.ts new file mode 100644 index 00000000..f0a82ffd --- /dev/null +++ b/packages/cli/e2e/__tests__/fixtures/test-playwright-project/checkly.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'checkly' + +const config = defineConfig({ + projectName: 'Test Playwright Project', + logicalId: 'test-playwright-project', + repoUrl: 'https://github.com/checkly/checkly-cli', + checks: { + locations: ['us-east-1', 'eu-west-1'], + tags: ['mac'], + runtimeId: '2022.10', + checkMatch: '**/*.check.ts', + browserChecks: { + testMatch: '**/__checks__/*.test.ts', + }, + }, + cli: { + runLocation: 'us-east-1', + }, +}) + +export default config diff --git a/packages/cli/e2e/__tests__/fixtures/test-playwright-project/playwright.config.ts b/packages/cli/e2e/__tests__/fixtures/test-playwright-project/playwright.config.ts new file mode 100644 index 00000000..dc502145 --- /dev/null +++ b/packages/cli/e2e/__tests__/fixtures/test-playwright-project/playwright.config.ts @@ -0,0 +1,23 @@ +import { defineConfig, devices } from '@playwright/test' + +export default defineConfig({ + testDir: 'tests', + timeout: 1234, + use: { + baseURL: 'http://127.0.0.1:3000', + extraHTTPHeaders: { + foo: 'bar', + }, + }, + expect: { + toMatchSnapshot: { + maxDiffPixelRatio: 1, + }, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) diff --git a/packages/cli/e2e/__tests__/sync-playwright.spec.ts b/packages/cli/e2e/__tests__/sync-playwright.spec.ts new file mode 100644 index 00000000..3c7f3c43 --- /dev/null +++ b/packages/cli/e2e/__tests__/sync-playwright.spec.ts @@ -0,0 +1,36 @@ +import { runChecklyCli } from '../run-checkly' +import * as path from 'path' +import { loadChecklyConfig } from '../../src/services/checkly-config-loader' +import * as fs from 'fs' + +describe('sync-playwright', () => { + // Since we are modifying the file let's keep it clean after each test + afterEach(() => { + const configPath = path.join(__dirname, 'fixtures', 'test-playwright-project') + fs.copyFileSync(path.join(configPath, 'checkly.config.original.ts'), path.join(configPath, 'checkly.config.ts')) + }) + + it('should copy playwright config into checkly config', async () => { + const { status, stdout } = await runChecklyCli({ + args: ['sync-playwright'], + directory: path.join(__dirname, 'fixtures', 'test-playwright-project'), + }) + expect(status).toBe(0) + expect(stdout).toContain('Successfully sync') + const checklyConfig = await loadChecklyConfig(path.join(__dirname, 'fixtures', 'test-playwright-project')) + expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig).toBeDefined() + expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig?.timeout).toEqual(1234) + expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig?.use).toBeDefined() + expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig?.use?.baseURL).toEqual('http://127.0.0.1:3000') + expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig?.expect).toBeDefined() + }) + + it('should fail if no playwright config file exists', async () => { + const { status, stdout } = await runChecklyCli({ + args: ['sync-playwright'], + directory: path.join(__dirname, 'fixtures', 'test-project'), + }) + expect(status).toBe(1) + expect(stdout).toContain('Could not find any playwright.config file.') + }) +}) diff --git a/packages/cli/package.json b/packages/cli/package.json index a32553bc..8434454f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -107,6 +107,7 @@ "@types/tunnel": "0.0.3", "@types/uuid": "9.0.1", "@types/ws": "8.5.5", + "@playwright/test": "1.40.1", "config": "3.3.9", "cross-env": "7.0.3", "jest": "29.6.2", diff --git a/packages/create-cli/e2e/__tests__/bootstrap.spec.ts b/packages/create-cli/e2e/__tests__/bootstrap.spec.ts index f0f296d8..30d9f24b 100644 --- a/packages/create-cli/e2e/__tests__/bootstrap.spec.ts +++ b/packages/create-cli/e2e/__tests__/bootstrap.spec.ts @@ -13,6 +13,8 @@ const E2E_PROJECT_PREFIX = 'e2e-test-project-' function cleanupProjects () { rimraf.sync(`${path.join(__dirname, 'fixtures', 'empty-project', E2E_PROJECT_PREFIX)}*`, { glob: true }) rimraf.windowsSync(`${path.join(__dirname, 'fixtures', 'empty-project', E2E_PROJECT_PREFIX)}*`, { glob: true }) + rimraf.sync(path.join(__dirname, 'fixtures', 'playwright-project', '__checks__'), { glob: true }) + rimraf.sync(path.join(__dirname, 'fixtures', 'playwright-project', 'checkly.config.ts'), { glob: true }) } function expectVersionAndName ({ @@ -272,4 +274,38 @@ describe('bootstrap', () => { expect(fs.existsSync(path.join(projectFolder, 'node_modules'))).toBe(false) expect(fs.existsSync(path.join(projectFolder, '.git'))).toBe(false) }, 15000) + + it('Should copy the playwright config', () => { + const directory = path.join(__dirname, 'fixtures', 'playwright-project') + const commandOutput = runChecklyCreateCli({ + directory, + promptsInjection: [true, false, false, true], + }) + + expectVersionAndName({ commandOutput, latestVersion, greeting }) + + const { status, stdout, stderr } = commandOutput + + expect(stdout).toContain('Downloading example template...') + expect(stdout).toContain('Example template copied!') + expect(stdout).not.toContain('Installing packages') + expect(stdout).not.toContain('Packages installed successfully') + // no git initialization message + + expect(stdout).toContain('No worries. Just remember to install the dependencies after this setup') + expect(stdout).toContain('Copying your playwright config') + expect(stdout).toContain('Playwright config copied!') + + expectCompleteCreation({ commandOutput, projectFolder: directory }) + + expect(stderr).toBe('') + expect(status).toBe(0) + + expect(fs.existsSync(path.join(directory, 'package.json'))).toBe(true) + expect(fs.existsSync(path.join(directory, 'checkly.config.ts'))).toBe(true) + expect(fs.existsSync(path.join(directory, '__checks__', 'api.check.ts'))).toBe(true) + + // node_modules nor .git shouldn't exist + expect(fs.existsSync(path.join(directory, 'node_modules'))).toBe(false) + }, 15000) }) diff --git a/packages/create-cli/e2e/__tests__/fixtures/playwright-project/package.json b/packages/create-cli/e2e/__tests__/fixtures/playwright-project/package.json new file mode 100644 index 00000000..19b996ea --- /dev/null +++ b/packages/create-cli/e2e/__tests__/fixtures/playwright-project/package.json @@ -0,0 +1,16 @@ +{ + "name": "dummy-project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "checkly": "latest", + "ts-node": "latest", + "typescript": "latest" + } +} \ No newline at end of file diff --git a/packages/create-cli/e2e/__tests__/fixtures/playwright-project/playwright.config.ts b/packages/create-cli/e2e/__tests__/fixtures/playwright-project/playwright.config.ts new file mode 100644 index 00000000..dc502145 --- /dev/null +++ b/packages/create-cli/e2e/__tests__/fixtures/playwright-project/playwright.config.ts @@ -0,0 +1,23 @@ +import { defineConfig, devices } from '@playwright/test' + +export default defineConfig({ + testDir: 'tests', + timeout: 1234, + use: { + baseURL: 'http://127.0.0.1:3000', + extraHTTPHeaders: { + foo: 'bar', + }, + }, + expect: { + toMatchSnapshot: { + maxDiffPixelRatio: 1, + }, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) From dc7a3fb594d8ea3aee4f1ceb1007d71afed6c835 Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Mon, 15 Jan 2024 17:58:46 +0100 Subject: [PATCH 7/7] fix: prompts and logs for better experience --- packages/cli/e2e/__tests__/sync-playwright.spec.ts | 2 +- packages/cli/src/commands/sync-playwright.ts | 14 +++++++++----- packages/create-cli/src/utils/prompts.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/cli/e2e/__tests__/sync-playwright.spec.ts b/packages/cli/e2e/__tests__/sync-playwright.spec.ts index 3c7f3c43..fc8ef2ea 100644 --- a/packages/cli/e2e/__tests__/sync-playwright.spec.ts +++ b/packages/cli/e2e/__tests__/sync-playwright.spec.ts @@ -16,7 +16,7 @@ describe('sync-playwright', () => { directory: path.join(__dirname, 'fixtures', 'test-playwright-project'), }) expect(status).toBe(0) - expect(stdout).toContain('Successfully sync') + expect(stdout).toContain('Successfully updated Checkly config file') const checklyConfig = await loadChecklyConfig(path.join(__dirname, 'fixtures', 'test-playwright-project')) expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig).toBeDefined() expect(checklyConfig.config?.checks?.browserChecks?.playwrightConfig?.timeout).toEqual(1234) diff --git a/packages/cli/src/commands/sync-playwright.ts b/packages/cli/src/commands/sync-playwright.ts index 5143e22d..67fb4e34 100644 --- a/packages/cli/src/commands/sync-playwright.ts +++ b/packages/cli/src/commands/sync-playwright.ts @@ -9,10 +9,10 @@ import PlaywrightConfigTemplate from '../playwright/playwright-config-template' export default class SyncPlaywright extends BaseCommand { static hidden = true - static description = 'Sync playwright config' + static description = 'Copy Playwright config into the Checkly config file' async run (): Promise { - ux.action.start('Sync playwright config with checkly config', undefined, { stdout: true }) + ux.action.start('Syncing Playwright config to the Checkly config file', undefined, { stdout: true }) const config = await loadPlaywrightConfig() if (!config) { @@ -27,12 +27,16 @@ export default class SyncPlaywright extends BaseCommand { const checksAst = this.findPropertyByName(checklyAst, 'checks') if (!checksAst) { - return this.handleError('Could not parse your checkly config file') + return this.handleError('Unable to automatically sync your config file. This can happen if your Checkly config is ' + + 'built using helper functions or other JS/TS features. You can still manually set Playwright config values in ' + + 'your Checkly config: https://www.checklyhq.com/docs/cli/constructs-reference/#project') } const browserCheckAst = this.findPropertyByName(checksAst.value, 'browserChecks') if (!browserCheckAst) { - return this.handleError('Could not parse your checkly config file') + return this.handleError('Unable to automatically sync your config file. This can happen if your Checkly config is ' + + 'built using helper functions or other JS/TS features. You can still manually set Playwright config values in ' + + 'your Checkly config: https://www.checklyhq.com/docs/cli/constructs-reference/#project') } const pwtConfig = new PlaywrightConfigTemplate(config).getConfigTemplate() @@ -44,7 +48,7 @@ export default class SyncPlaywright extends BaseCommand { this.reWriteChecklyConfigFile(checklyConfigData, configFile.fileName, dir) ux.action.stop('✅ ') - this.log('Successfully sync') + this.log('Successfully updated Checkly config file') this.exit(0) } diff --git a/packages/create-cli/src/utils/prompts.ts b/packages/create-cli/src/utils/prompts.ts index 7c4488fd..6714bd42 100644 --- a/packages/create-cli/src/utils/prompts.ts +++ b/packages/create-cli/src/utils/prompts.ts @@ -84,7 +84,7 @@ export function askCopyPlaywrightProject (onCancel: any): Promise<{ shouldCopyPl return prompts({ type: 'confirm', name: 'shouldCopyPlaywrightConfig', - message: 'It looks like you have a playwright config, do you want to copy it?', + message: 'It looks like you have a Playwright config file. Would you like to copy your settings to your Checkly config file?', initial: true, }, { onCancel }) }