mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-02 18:11:04 +08:00
Update @puppeteer/replay to 2.9.0
Bug: none Change-Id: I0372a04e8714f11338d7df7c2b2ce21b24f3048a Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4226464 Commit-Queue: Simon Zünd <szuend@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org> Auto-Submit: Randolf Jung <jrandolf@chromium.org>
This commit is contained in:
committed by
Devtools-frontend LUCI CQ
parent
12f3d686cc
commit
51ffdadc72
+1
@@ -0,0 +1 @@
|
||||
../@puppeteer/replay/lib/extension-test.js
|
||||
+5
-4
@@ -525,9 +525,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@puppeteer/replay": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.7.1.tgz",
|
||||
"integrity": "sha512-w5lghKKto24XB7PGAyflkf1lSuFC+V4+UU5upJJkmWD+O0Q82H2PlHl/Wqwi9aQrngEkKSiK2xMK8Ycs5NddPA==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.9.0.tgz",
|
||||
"integrity": "sha512-nVxGZ6+UNQD7ALGndLThhZHRRiBb0Bw0ViFz64RVHU4Cch8gpGXMbN7xZl79q9xInnvRPaqeKZzdb1316jc25A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cli-table3": "0.6.3",
|
||||
@@ -535,7 +535,8 @@
|
||||
"yargs": "17.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"replay": "lib/cli.js"
|
||||
"replay": "lib/cli.js",
|
||||
"replay-extension-test": "lib/extension-test.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
|
||||
+13
@@ -246,6 +246,19 @@ console.log(
|
||||
|
||||
## Others
|
||||
|
||||
### Test your extensions using the replay lib
|
||||
|
||||
The replay lib offers a canonical recording and a test page that allows to
|
||||
verify that your extension produces all expected side effects on a page.
|
||||
|
||||
The test command supports both stringify and runner extensions. The stringify
|
||||
extension will be tested by running the stringified script using node. Run the
|
||||
test using the following command.
|
||||
|
||||
```
|
||||
npx -p @puppeteer/replay replay-extension-test --ext path-to-your-extension-js
|
||||
```
|
||||
|
||||
### Create a Chrome extension for Recorder (Available from Chrome 104 onwards)
|
||||
|
||||
You can create a Chrome extension for [Recorder](https://goo.gle/devtools-recorder). Refer to the [Chrome Extensions documentation](https://developer.chrome.com/docs/extensions/mv3/devtools/) for more details on how to extend DevTools.
|
||||
|
||||
+1
@@ -21,6 +21,7 @@ exports.SelectorType = void 0;
|
||||
SelectorType["ARIA"] = "aria";
|
||||
SelectorType["Text"] = "text";
|
||||
SelectorType["XPath"] = "xpath";
|
||||
SelectorType["Pierce"] = "pierce";
|
||||
})(exports.SelectorType = exports.SelectorType || (exports.SelectorType = {}));
|
||||
exports.StepType = void 0;
|
||||
(function (StepType) {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-1
@@ -35,7 +35,8 @@ declare enum SelectorType {
|
||||
CSS = "css",
|
||||
ARIA = "aria",
|
||||
Text = "text",
|
||||
XPath = "xpath"
|
||||
XPath = "xpath",
|
||||
Pierce = "pierce"
|
||||
}
|
||||
declare enum StepType {
|
||||
Change = "change",
|
||||
|
||||
+6
-5
@@ -3,7 +3,7 @@ import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import { parse, createRunner, PuppeteerRunnerOwningBrowserExtension } from '../lib/main.js';
|
||||
import { lstatSync, readFileSync, readdirSync } from 'fs';
|
||||
import { isAbsolute, join, extname, relative } from 'path';
|
||||
import { extname, join, relative, isAbsolute } from 'path';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { cwd } from 'process';
|
||||
import Table from 'cli-table3';
|
||||
@@ -108,6 +108,10 @@ function createStatusReport(results) {
|
||||
}
|
||||
return table;
|
||||
}
|
||||
async function importExtensionFromPath(path) {
|
||||
const module = await import(pathToFileURL(isAbsolute(path) ? path : join(cwd(), path)).toString());
|
||||
return module.default;
|
||||
}
|
||||
async function runFiles(files, opts = {
|
||||
log: true,
|
||||
headless: true,
|
||||
@@ -115,10 +119,7 @@ async function runFiles(files, opts = {
|
||||
let Extension = PuppeteerRunnerOwningBrowserExtension;
|
||||
let browser;
|
||||
if (opts.extension) {
|
||||
const module = await import(pathToFileURL(isAbsolute(opts.extension)
|
||||
? opts.extension
|
||||
: join(cwd(), opts.extension)).toString());
|
||||
Extension = module.default;
|
||||
Extension = await importExtensionFromPath(opts.extension);
|
||||
}
|
||||
const results = [];
|
||||
for (const file of files) {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+332
@@ -0,0 +1,332 @@
|
||||
#!/usr/bin/env node
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import { RunnerExtension, createRunner, parse, stringify } from '../lib/main.js';
|
||||
import 'fs';
|
||||
import { isAbsolute, join } from 'path';
|
||||
import { pathToFileURL } from 'url';
|
||||
import { cwd } from 'process';
|
||||
import 'cli-table3';
|
||||
import 'colorette';
|
||||
import http from 'http';
|
||||
import assert from 'assert/strict';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
async function importExtensionFromPath(path) {
|
||||
const module = await import(pathToFileURL(isAbsolute(path) ? path : join(cwd(), path)).toString());
|
||||
return module.default;
|
||||
}
|
||||
|
||||
const recording = {
|
||||
title: 'spec',
|
||||
steps: [
|
||||
{
|
||||
type: 'setViewport',
|
||||
width: 900,
|
||||
height: 700,
|
||||
deviceScaleFactor: 1,
|
||||
isMobile: false,
|
||||
hasTouch: false,
|
||||
isLandscape: false,
|
||||
},
|
||||
{
|
||||
type: 'navigate',
|
||||
url: 'http://localhost:8907/spec.html',
|
||||
assertedEvents: [
|
||||
{
|
||||
type: 'navigation',
|
||||
url: 'http://localhost:8907/spec.html',
|
||||
title: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'click',
|
||||
target: 'main',
|
||||
selectors: [
|
||||
['aria/Click'],
|
||||
['#button'],
|
||||
['xpath///*[@id="button"]'],
|
||||
['text/Click'],
|
||||
],
|
||||
offsetY: 18,
|
||||
offsetX: 36,
|
||||
},
|
||||
{
|
||||
type: 'doubleClick',
|
||||
target: 'main',
|
||||
selectors: [
|
||||
['aria/Click'],
|
||||
['#button'],
|
||||
['xpath///*[@id="button"]'],
|
||||
['text/Click'],
|
||||
],
|
||||
offsetY: 18,
|
||||
offsetX: 36,
|
||||
},
|
||||
{
|
||||
type: 'keyDown',
|
||||
target: 'main',
|
||||
key: 'Tab',
|
||||
},
|
||||
{
|
||||
type: 'keyUp',
|
||||
key: 'Tab',
|
||||
target: 'main',
|
||||
},
|
||||
{
|
||||
type: 'change',
|
||||
value: 'test',
|
||||
selectors: [['#input'], ['xpath///*[@id="input"]']],
|
||||
target: 'main',
|
||||
},
|
||||
{
|
||||
type: 'change',
|
||||
value: 'testSuffix',
|
||||
selectors: [['#input-prefilled']],
|
||||
target: 'main',
|
||||
},
|
||||
{
|
||||
type: 'keyDown',
|
||||
target: 'main',
|
||||
key: 'Enter',
|
||||
},
|
||||
{
|
||||
type: 'keyUp',
|
||||
key: 'Enter',
|
||||
target: 'main',
|
||||
},
|
||||
{
|
||||
type: 'click',
|
||||
selectors: [['#input'], ['xpath///*[@id="input"]']],
|
||||
target: 'main',
|
||||
button: 'secondary',
|
||||
offsetX: 1,
|
||||
offsetY: 1,
|
||||
},
|
||||
{
|
||||
type: 'hover',
|
||||
target: 'main',
|
||||
selectors: [
|
||||
['aria/Hover'],
|
||||
['#button'],
|
||||
['xpath///*[@id="hover"]'],
|
||||
['text/Hover'],
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'waitForExpression',
|
||||
expression: 'new Promise(resolve => setTimeout(() => resolve(true), 500))',
|
||||
},
|
||||
{
|
||||
type: 'waitForElement',
|
||||
target: 'main',
|
||||
selectors: ['#button'],
|
||||
count: 1,
|
||||
visible: true,
|
||||
properties: {
|
||||
id: 'button',
|
||||
},
|
||||
attributes: {
|
||||
id: 'button',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'change',
|
||||
value: 'optionB',
|
||||
selectors: [['#select']],
|
||||
target: 'main',
|
||||
},
|
||||
],
|
||||
};
|
||||
const expectedLog = `window dimensions 900x700
|
||||
click targetId=button button=0 value=
|
||||
click targetId=button button=0 value=
|
||||
dblclick targetId=button button=0 value=
|
||||
change targetId=input button=undefined value=test
|
||||
change targetId=input-prefilled button=undefined value=testSuffix
|
||||
contextmenu targetId=input button=2 value=test
|
||||
mouseenter targetId=hover button=0 value=
|
||||
change targetId=select button=undefined value=optionB
|
||||
`.trim();
|
||||
const files = new Map([
|
||||
[
|
||||
'spec.html',
|
||||
`<!DOCTYPE html>
|
||||
<button id="button" onclick="logEvent(event)" ondblclick="logEvent(event)">
|
||||
Click
|
||||
</button>
|
||||
<button
|
||||
id="hover"
|
||||
onmouseenter="logEvent(event)"
|
||||
onmouseleave="logEvent(event)"
|
||||
>
|
||||
Hover
|
||||
</button>
|
||||
<input id="input" oncontextmenu="logEvent(event)" onchange="logEvent(event)" />
|
||||
<input id="input-prefilled" onchange="logEvent(event)" value="test" />
|
||||
<select id="select" onchange="logEvent(event)">
|
||||
<option value=""></option>
|
||||
<option value="optionA">Option A</option>
|
||||
<option value="optionB">Option B</option>
|
||||
</select>
|
||||
<pre id="log"></pre>
|
||||
<script>
|
||||
function logStr(str) {
|
||||
log.innerText += str;
|
||||
const data = { username: 'example' };
|
||||
fetch('/log', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/text',
|
||||
},
|
||||
body: str,
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
function logEvent(event) {
|
||||
logStr(
|
||||
'\\n' +
|
||||
event.type +
|
||||
' targetId=' +
|
||||
event.target.id +
|
||||
' button=' +
|
||||
event.button +
|
||||
' value=' +
|
||||
event.target.value
|
||||
);
|
||||
}
|
||||
logStr(\`window dimensions \${window.innerWidth}x\${window.innerHeight}\`);
|
||||
input.addEventListener('contextmenu', (e) => e.preventDefault(), false);
|
||||
</script>`,
|
||||
],
|
||||
]);
|
||||
|
||||
/**
|
||||
Copyright 2023 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.
|
||||
*/
|
||||
async function startServer() {
|
||||
const log = {
|
||||
contents: '',
|
||||
};
|
||||
const server = http.createServer(async (req, res) => {
|
||||
var _a;
|
||||
if (req.method === 'GET') {
|
||||
for (const [file, content] of files.entries()) {
|
||||
if ((_a = req.url) === null || _a === void 0 ? void 0 : _a.endsWith(file)) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('Not found');
|
||||
return;
|
||||
}
|
||||
if (req.method === 'POST') {
|
||||
const body = await new Promise((resolve) => {
|
||||
const body = [];
|
||||
req
|
||||
.on('data', (chunk) => {
|
||||
body.push(chunk);
|
||||
})
|
||||
.on('end', () => {
|
||||
resolve(Buffer.concat(body).toString());
|
||||
});
|
||||
});
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
log.contents += body;
|
||||
return;
|
||||
}
|
||||
res.writeHead(400);
|
||||
res.end();
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
server.listen(8907, 'localhost', () => {
|
||||
resolve({ server, log });
|
||||
});
|
||||
});
|
||||
}
|
||||
yargs(hideBin(process.argv))
|
||||
.command('$0', 'Test an extension implementation', () => { }, async (argv) => {
|
||||
const args = argv;
|
||||
const Extension = await importExtensionFromPath(args.extension);
|
||||
const ext = new Extension();
|
||||
let run = async () => { };
|
||||
if (ext instanceof RunnerExtension) {
|
||||
console.log('runner');
|
||||
run = async () => {
|
||||
const extension = new Extension();
|
||||
const runner = await createRunner(parse(recording), extension);
|
||||
await runner.run();
|
||||
};
|
||||
}
|
||||
else {
|
||||
run = async () => {
|
||||
const exported = await stringify(parse(recording), {
|
||||
extension: new Extension(),
|
||||
});
|
||||
const childProcess = spawn('node', {
|
||||
stdio: ['pipe', 'pipe', 'inherit'],
|
||||
shell: true,
|
||||
});
|
||||
childProcess.stdin.write(exported);
|
||||
childProcess.stdin.end();
|
||||
await new Promise((resolve, reject) => {
|
||||
childProcess.on('close', (code) => code
|
||||
? reject(new Error(`Running node failed with code ${code}`))
|
||||
: resolve());
|
||||
});
|
||||
};
|
||||
}
|
||||
const { server, log } = await startServer();
|
||||
try {
|
||||
await run();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
finally {
|
||||
server.close();
|
||||
}
|
||||
assert.equal(log.contents, expectedLog);
|
||||
console.log('Run matches the expectations');
|
||||
})
|
||||
.option('extension', {
|
||||
alias: 'ext',
|
||||
type: 'string',
|
||||
description: 'The path to the extension module. The default export will be used as a Stringify or Runner extension based on instanceOf checks.',
|
||||
demandOption: true,
|
||||
})
|
||||
.parse();
|
||||
//# sourceMappingURL=extension-test.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+2
-1
@@ -35,7 +35,8 @@ declare enum SelectorType {
|
||||
CSS = "css",
|
||||
ARIA = "aria",
|
||||
Text = "text",
|
||||
XPath = "xpath"
|
||||
XPath = "xpath",
|
||||
Pierce = "pierce"
|
||||
}
|
||||
declare enum StepType {
|
||||
Change = "change",
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ var SelectorType;
|
||||
SelectorType["ARIA"] = "aria";
|
||||
SelectorType["Text"] = "text";
|
||||
SelectorType["XPath"] = "xpath";
|
||||
SelectorType["Pierce"] = "pierce";
|
||||
})(SelectorType = SelectorType || (SelectorType = {}));
|
||||
var StepType;
|
||||
(function (StepType) {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+7
-4
@@ -1,10 +1,13 @@
|
||||
{
|
||||
"name": "@puppeteer/replay",
|
||||
"version": "2.7.1",
|
||||
"version": "2.9.0",
|
||||
"description": "Replay is a library which provides an API to replay and stringify recordings created using Chrome DevTools Recorder](https://developer.chrome.com/docs/devtools/recorder/)",
|
||||
"main": "lib/cjs/main.cjs",
|
||||
"types": "lib/main.d.ts",
|
||||
"bin": "lib/cli.js",
|
||||
"bin": {
|
||||
"@puppeteer/replay": "lib/cli.js",
|
||||
"replay-extension-test": "lib/extension-test.js"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
@@ -70,8 +73,8 @@
|
||||
"mime": "3.0.0",
|
||||
"mocha": "10.1.0",
|
||||
"prettier": "2.7.1",
|
||||
"puppeteer": "19.4.1",
|
||||
"puppeteer-core": "19.4.1",
|
||||
"puppeteer": "19.6.2",
|
||||
"puppeteer-core": "19.6.2",
|
||||
"rimraf": "3.0.2",
|
||||
"rollup": "3.2.2",
|
||||
"rollup-plugin-dts": "5.0.0",
|
||||
|
||||
Generated
+9
-8
@@ -8,7 +8,7 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"devDependencies": {
|
||||
"@istanbuljs/schema": "0.1.3",
|
||||
"@puppeteer/replay": "^2.7.1",
|
||||
"@puppeteer/replay": "^2.9.0",
|
||||
"@types/chai": "4.3.0",
|
||||
"@types/codemirror": "0.0.108",
|
||||
"@types/estree": "0.0.50",
|
||||
@@ -597,9 +597,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@puppeteer/replay": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.7.1.tgz",
|
||||
"integrity": "sha512-w5lghKKto24XB7PGAyflkf1lSuFC+V4+UU5upJJkmWD+O0Q82H2PlHl/Wqwi9aQrngEkKSiK2xMK8Ycs5NddPA==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.9.0.tgz",
|
||||
"integrity": "sha512-nVxGZ6+UNQD7ALGndLThhZHRRiBb0Bw0ViFz64RVHU4Cch8gpGXMbN7xZl79q9xInnvRPaqeKZzdb1316jc25A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cli-table3": "0.6.3",
|
||||
@@ -607,7 +607,8 @@
|
||||
"yargs": "17.6.2"
|
||||
},
|
||||
"bin": {
|
||||
"replay": "lib/cli.js"
|
||||
"replay": "lib/cli.js",
|
||||
"replay-extension-test": "lib/extension-test.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -9240,9 +9241,9 @@
|
||||
}
|
||||
},
|
||||
"@puppeteer/replay": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.7.1.tgz",
|
||||
"integrity": "sha512-w5lghKKto24XB7PGAyflkf1lSuFC+V4+UU5upJJkmWD+O0Q82H2PlHl/Wqwi9aQrngEkKSiK2xMK8Ycs5NddPA==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/replay/-/replay-2.9.0.tgz",
|
||||
"integrity": "sha512-nVxGZ6+UNQD7ALGndLThhZHRRiBb0Bw0ViFz64RVHU4Cch8gpGXMbN7xZl79q9xInnvRPaqeKZzdb1316jc25A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cli-table3": "0.6.3",
|
||||
|
||||
@@ -41,7 +41,7 @@ LICENSES = [
|
||||
# List all DEPS here.
|
||||
DEPS = {
|
||||
"@istanbuljs/schema": "0.1.3",
|
||||
"@puppeteer/replay": "2.7.1",
|
||||
"@puppeteer/replay": "2.9.0",
|
||||
"@types/chai": "4.3.0",
|
||||
"@types/codemirror": "0.0.108",
|
||||
"@types/estree": "0.0.50",
|
||||
|
||||
Reference in New Issue
Block a user