diff --git a/__snapshots__/PuppeteerReplayStringifyExtension.test.ts.js b/__snapshots__/PuppeteerReplayStringifyExtension.test.ts.js new file mode 100644 index 00000000..52814f60 --- /dev/null +++ b/__snapshots__/PuppeteerReplayStringifyExtension.test.ts.js @@ -0,0 +1,52 @@ +exports[ + 'PuppeteerReplayStringifyExtension should print the script for a click step 1' +] = ` +await runner.runStep({ + "type": "click", + "target": "main", + "selectors": [ + "aria/Test" + ], + "offsetX": 1, + "offsetY": 1, + "assertedEvents": [ + { + "type": "navigation" + } + ] +}); + +`; + +exports['PuppeteerReplayStringifyExtension should print an entire script 1'] = ` +import url from 'url'; +import { createRunner } from '@puppeteer/replay'; + +export async function run(extension) { + const runner = await createRunner(extension); + + await runner.runBeforeAllSteps(); + + await runner.runStep({ + "type": "click", + "target": "main", + "selectors": [ + "aria/Test" + ], + "offsetX": 1, + "offsetY": 1, + "assertedEvents": [ + { + "type": "navigation" + } + ] + }); + + await runner.runAfterAllSteps(); +} + +if (process && import.meta.url === url.pathToFileURL(process.argv[1]).href) { + run() +} + +`; diff --git a/src/InMemoryLineWriter.ts b/src/InMemoryLineWriter.ts index 19e12ffc..b8ce94f0 100644 --- a/src/InMemoryLineWriter.ts +++ b/src/InMemoryLineWriter.ts @@ -26,10 +26,14 @@ export class InMemoryLineWriter implements LineWriter { } appendLine(line: string): LineWriter { - const indentedLine = line - ? this.#indentation.repeat(this.#currentIndentation) + line.trimEnd() - : ''; - this.#lines.push(indentedLine); + const lines = line.split('\n').map((line) => { + const indentedLine = line + ? this.#indentation.repeat(this.#currentIndentation) + line.trimEnd() + : ''; + return indentedLine; + }); + + this.#lines.push(...lines); return this; } @@ -47,4 +51,8 @@ export class InMemoryLineWriter implements LineWriter { // Scripts should end with a final blank line. return this.#lines.join('\n') + '\n'; } + + getIndent(): string { + return this.#indentation; + } } diff --git a/src/LineWriter.ts b/src/LineWriter.ts index edfdc4d6..b97754d2 100644 --- a/src/LineWriter.ts +++ b/src/LineWriter.ts @@ -18,4 +18,5 @@ export interface LineWriter { appendLine(line: string): LineWriter; startBlock(): LineWriter; endBlock(): LineWriter; + getIndent(): string; } diff --git a/src/PuppeteerReplayStringifyExtension.ts b/src/PuppeteerReplayStringifyExtension.ts new file mode 100644 index 00000000..01e1b4fa --- /dev/null +++ b/src/PuppeteerReplayStringifyExtension.ts @@ -0,0 +1,58 @@ +/** + Copyright 2022 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import type { LineWriter } from './LineWriter.js'; +import type { Step } from './Schema.js'; +import { StringifyExtension } from './StringifyExtension.js'; + +/** + * Stringifies a user flow to a script that uses \@puppeteer/replay's own API. + */ +export class PuppeteerReplayStringifyExtension extends StringifyExtension { + override async beforeAllSteps(out: LineWriter) { + out.appendLine("import url from 'url';"); + out.appendLine("import { createRunner } from '@puppeteer/replay';"); + out.appendLine(''); + out.appendLine('export async function run(extension) {').startBlock(); + out.appendLine('const runner = await createRunner(extension);'); + out.appendLine(''); + out.appendLine('await runner.runBeforeAllSteps();'); + out.appendLine(''); + } + + override async afterAllSteps(out: LineWriter) { + out.appendLine(''); + out + .appendLine('await runner.runAfterAllSteps();') + .endBlock() + .appendLine('}'); + out.appendLine(''); + out + .appendLine( + 'if (process && import.meta.url === url.pathToFileURL(process.argv[1]).href) {' + ) + .startBlock() + .appendLine('run()') + .endBlock() + .appendLine('}'); + } + + override async stringifyStep(out: LineWriter, step: Step) { + out.appendLine( + `await runner.runStep(${JSON.stringify(step, null, out.getIndent())});` + ); + } +} diff --git a/src/main.ts b/src/main.ts index b8fef1b9..a1527bfc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -28,4 +28,5 @@ export { createRunner, Runner } from './Runner.js'; export { PuppeteerRunnerExtension } from './PuppeteerRunnerExtension.js'; export { PuppeteerRunnerOwningBrowserExtension } from './PuppeteerRunnerExtension.js'; export { PuppeteerStringifyExtension } from './PuppeteerStringifyExtension.js'; +export { PuppeteerReplayStringifyExtension } from './PuppeteerReplayStringifyExtension.js'; export { LighthouseStringifyExtension } from './lighthouse/LighthouseStringifyExtension.js'; diff --git a/test/PuppeteerReplayStringifyExtension.test.ts b/test/PuppeteerReplayStringifyExtension.test.ts new file mode 100644 index 00000000..1b1576fa --- /dev/null +++ b/test/PuppeteerReplayStringifyExtension.test.ts @@ -0,0 +1,56 @@ +/** + Copyright 2022 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import snapshot from 'snap-shot-it'; +import { stringify } from '../src/stringify.js'; +import { InMemoryLineWriter } from '../src/InMemoryLineWriter.js'; +import { PuppeteerReplayStringifyExtension } from '../src/PuppeteerReplayStringifyExtension.js'; +import { StepType, AssertedEventType } from '../src/Schema.js'; + +describe('PuppeteerReplayStringifyExtension', () => { + const ext = new PuppeteerReplayStringifyExtension(); + + it('should print the script for a click step', async () => { + const step = { + type: StepType.Click as const, + target: 'main', + selectors: ['aria/Test'], + offsetX: 1, + offsetY: 1, + assertedEvents: [{ type: AssertedEventType.Navigation as const }], + }; + const writer = new InMemoryLineWriter(' '); + await ext.stringifyStep(writer, step); + snapshot(writer.toString()); + }); + + it('should print an entire script', async () => { + const step = { + type: StepType.Click as const, + target: 'main', + selectors: ['aria/Test'], + offsetX: 1, + offsetY: 1, + assertedEvents: [{ type: AssertedEventType.Navigation as const }], + }; + const flow = { title: 'test', steps: [step] }; + snapshot( + await stringify(flow, { + extension: ext, + }) + ); + }); +});