diff --git a/README.md b/README.md index 95007756..9872fdfd 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ In your `package.json` add a new script to invoke the `replay` command: } ``` -Set the `PUPPETEER_HEADLESS` environment variable to control whether the browser is start in a headful or headless mode. For example, +Set the `PUPPETEER_HEADLESS` environment variable or `--headless` CLI flag to control whether the browser is start in a headful or headless mode. For example, ``` PUPPETEER_HEADLESS=true npx @puppeteer/replay recording.json # runs in headless mode, the default mode. @@ -61,6 +61,14 @@ PUPPETEER_HEADLESS=false npx @puppeteer/replay recording.json # runs in headful PUPPETEER_HEADLESS=chrome npx @puppeteer/replay recording.json # runs in the new experimental headless mode. ``` +Use the `--extension` CLI flag to provide a [custom replay extension](https://github.com/puppeteer/replay#2-customize-replay) for running the recording. For [example](https://github.com/puppeteer/replay/blob/main/examples/cli-extension/extension.js), + +```sh +npx @puppeteer/replay --extension examples/cli-extension/extension.js recording.json +``` + +Run `npx @puppeteer/replay --help` to see all CLI options. + Using [the replay lib API](/examples/replay-from-file-using-puppeteer/main.js): ```js diff --git a/examples/cli-extension/extension.js b/examples/cli-extension/extension.js new file mode 100644 index 00000000..7f226ce5 --- /dev/null +++ b/examples/cli-extension/extension.js @@ -0,0 +1,24 @@ +import { PuppeteerRunnerExtension } from '../../lib/main.js'; + +export default class Extension extends PuppeteerRunnerExtension { + async beforeAllSteps(flow) { + await super.beforeAllSteps(flow); + console.log('starting'); + } + + async beforeEachStep(step, flow) { + await super.beforeEachStep(step, flow); + console.log('before', step); + } + + async afterEachStep(step, flow) { + await super.afterEachStep(step, flow); + console.log('after', step); + } + + async afterAllSteps(flow) { + await super.afterAllSteps(flow); + console.log('done'); + await this.browser.close(); + } +} diff --git a/package.json b/package.json index 53c554f5..39578e7b 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/chai": "4.3.1", "@types/mocha": "9.1.1", "@types/node": "17.0.36", + "@types/yargs": "17.0.10", "@typescript-eslint/eslint-plugin": "5.26.0", "@typescript-eslint/parser": "5.27.0", "c8": "7.11.3", @@ -68,5 +69,8 @@ "puppeteer": { "optional": true } + }, + "dependencies": { + "yargs": "17.5.1" } } diff --git a/src/CLIUtils.ts b/src/CLIUtils.ts index c723e589..55d1ce8c 100644 --- a/src/CLIUtils.ts +++ b/src/CLIUtils.ts @@ -16,12 +16,10 @@ import { parse, createRunner } from './main.js'; import { readFileSync } from 'fs'; +import { join } from 'path'; +import { cwd } from 'process'; import { PuppeteerRunnerOwningBrowserExtension } from './PuppeteerRunnerExtension.js'; -export function getFilenames(argv: string[]) { - return argv.slice(2); -} - export function getHeadlessEnvVar(headless?: string) { if (!headless) { return true; @@ -42,11 +40,16 @@ export function getHeadlessEnvVar(headless?: string) { export async function runFiles( files: string[], - opts: { log: boolean; headless: boolean | 'chrome' } = { + opts: { log: boolean; headless: boolean | 'chrome'; extension?: string } = { log: false, headless: true, } ): Promise { + let Extension = PuppeteerRunnerOwningBrowserExtension; + if (opts.extension) { + const module = await import(join(cwd(), opts.extension)); + Extension = module.default; + } for (const file of files) { opts.log && console.log(`Running ${file}...`); try { @@ -58,10 +61,7 @@ export async function runFiles( headless: opts.headless, }); const page = await browser.newPage(); - const extension = new PuppeteerRunnerOwningBrowserExtension( - browser, - page - ); + const extension = new Extension(browser, page); const runner = await createRunner(recording, extension); await runner.run(); opts.log && console.log(`Finished running ${file}`); diff --git a/src/cli.ts b/src/cli.ts index 1d73b38f..162bed46 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -16,13 +16,41 @@ limitations under the License. */ -import { getFilenames, getHeadlessEnvVar, runFiles } from './CLIUtils.js'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; -const recordings = getFilenames(process.argv); -if (!recordings.length) { - console.log(`Usage: replay filename [filename...]`); +import { getHeadlessEnvVar, runFiles } from './CLIUtils.js'; + +interface Arguments { + files: string[]; + extension?: string; + headless?: string; } -await runFiles(recordings, { - log: true, - headless: getHeadlessEnvVar(process.env.PUPPETEER_HEADLESS), -}); + +await yargs(hideBin(process.argv)) + .command( + '$0 ', + 'run files', + () => {}, + async (argv) => { + const args = argv as unknown as Arguments; + await runFiles(args.files, { + log: true, + headless: getHeadlessEnvVar( + args.headless || process.env.PUPPETEER_HEADLESS + ), + extension: args.extension, + }); + } + ) + .option('headless', { + type: 'string', + description: "Run using the browser's headless mode.", + choices: ['chrome', 'true', '1', '0', 'false'], + }) + .option('extension', { + alias: 'ext', + type: 'string', + description: 'Run using an extension identified by the path.', + }) + .parse(); diff --git a/test/cli_test.ts b/test/cli_test.ts index 0a9c5704..4314b012 100644 --- a/test/cli_test.ts +++ b/test/cli_test.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { getFilenames, runFiles, getHeadlessEnvVar } from '../src/CLIUtils.js'; +import { runFiles, getHeadlessEnvVar } from '../src/CLIUtils.js'; import { assert } from 'chai'; import path from 'path'; import url from 'url'; @@ -22,19 +22,6 @@ import url from 'url'; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); describe('cli', () => { - describe('getFilenames', () => { - it('extracts filenames from process.argv', () => { - assert.deepStrictEqual(getFilenames(['node', 'script.js']), []); - assert.deepStrictEqual(getFilenames(['node', 'script.js', 'file1']), [ - 'file1', - ]); - assert.deepStrictEqual( - getFilenames(['node', 'script.js', 'file1', 'file2', 'file3']), - ['file1', 'file2', 'file3'] - ); - }); - }); - describe('getHeadlessEnvVar', () => { it('extracts the headless parameter from process.argv', () => { assert.strictEqual(getHeadlessEnvVar(undefined), true); @@ -54,5 +41,15 @@ describe('cli', () => { await runFiles([path.join(__dirname, 'resources', 'replay.json')]) ); }); + + it('is able to run successfully with an extension', async () => { + assert.isTrue( + await runFiles([path.join(__dirname, 'resources', 'replay.json')], { + extension: path.join('examples', 'cli-extension', 'extension.js'), + headless: true, + log: false, + }) + ); + }); }); });