chrome-devtools-frontend 1.0.1636056 → 1.0.1640418
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front_end/core/common/Color.ts +0 -4
- package/front_end/core/host/AidaClientTypes.ts +8 -6
- package/front_end/core/root/Runtime.ts +2 -2
- package/front_end/core/sdk/DOMStorageModel.ts +1 -1
- package/front_end/core/sdk/SourceMap.ts +8 -3
- package/front_end/core/sdk/TargetManager.ts +14 -1
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +147 -0
- package/front_end/generated/ARIAProperties.js +17 -4
- package/front_end/generated/InspectorBackendCommands.ts +13 -7
- package/front_end/generated/SupportedCSSProperties.js +1 -0
- package/front_end/generated/protocol-mapping.d.ts +7 -0
- package/front_end/generated/protocol-proxy-api.d.ts +14 -0
- package/front_end/generated/protocol.ts +120 -2
- package/front_end/global_typings/global_defs.d.ts +13 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +116 -0
- package/front_end/models/ai_assistance/AiConversation.ts +22 -36
- package/front_end/models/ai_assistance/AiHistoryStorage.ts +0 -1
- package/front_end/models/ai_assistance/AiOrigins.ts +46 -0
- package/front_end/models/ai_assistance/AiUtils.ts +9 -0
- package/front_end/models/ai_assistance/README.md +16 -0
- package/front_end/models/ai_assistance/StorageItem.ts +30 -26
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +12 -5
- package/front_end/models/ai_assistance/agents/AiAgent.ts +86 -32
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +31 -10
- package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/FileAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/GreenDevAgent.ts +1 -3
- package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +19 -0
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +9 -4
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +41 -12
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +442 -122
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +2 -2
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -2
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/performance/AIContext.ts +7 -8
- package/front_end/models/ai_assistance/skills/README.md +40 -0
- package/front_end/models/ai_assistance/skills/Skill.ts +13 -0
- package/front_end/models/ai_assistance/skills/SkillRegistry.ts +10 -0
- package/front_end/models/ai_assistance/skills/styling.md +6 -0
- package/front_end/models/bindings/CompilerScriptMapping.ts +12 -4
- package/front_end/models/breakpoints/BreakpointManager.ts +54 -2
- package/front_end/models/greendev/Prototypes.ts +0 -7
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +20 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +5 -0
- package/front_end/models/issues_manager/EmailVerificationRequestIssue.ts +293 -0
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestInvalidEmail.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
- package/front_end/models/issues_manager/descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +1748 -1739
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +1 -1
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +9 -1
- package/front_end/models/stack_trace/StackTraceImpl.ts +29 -9
- package/front_end/models/stack_trace/StackTraceModel.ts +23 -11
- package/front_end/models/stack_trace/Trie.ts +11 -1
- package/front_end/models/trace/extras/TraceTree.ts +20 -1
- package/front_end/models/trace/insights/Common.ts +9 -0
- package/front_end/models/trace/lantern/core/NetworkAnalyzer.ts +21 -25
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +19 -75
- package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +10 -3
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +148 -2
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +2 -3
- package/front_end/panels/ai_assistance/components/chatMessage.css +27 -0
- package/front_end/panels/application/CookieItemsView.ts +24 -0
- package/front_end/panels/application/DOMStorageItemsView.ts +9 -4
- package/front_end/panels/application/preloading/components/PreloadingString.ts +6 -0
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +4 -4
- package/front_end/panels/console/ConsoleViewMessage.ts +13 -102
- package/front_end/panels/elements/StandaloneStylesContainer.ts +10 -0
- package/front_end/panels/elements/StylePropertiesSection.ts +6 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +30 -1
- package/front_end/panels/elements/StylesContainer.ts +3 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +54 -4
- package/front_end/panels/elements/elements-meta.ts +14 -0
- package/front_end/panels/layer_viewer/layerDetailsView.css +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +4 -4
- package/front_end/panels/network/NetworkDataGridNode.ts +14 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +2 -2
- package/front_end/panels/network/RequestHeadersView.ts +55 -19
- package/front_end/panels/network/networkTimingTable.css +2 -4
- package/front_end/panels/recorder/components/ReplaySection.ts +28 -16
- package/front_end/panels/recorder/converters/LighthouseConverter.snapshot.txt +47 -0
- package/front_end/panels/recorder/converters/PuppeteerConverter.snapshot.txt +49 -0
- package/front_end/panels/recorder/converters/PuppeteerReplayConverter.snapshot.txt +33 -0
- package/front_end/panels/settings/SettingsScreen.ts +1 -2
- package/front_end/panels/sources/BreakpointsView.ts +23 -42
- package/front_end/panels/sources/DebuggerPlugin.ts +12 -5
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +169 -106
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +1 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +2 -2
- package/front_end/third_party/marked/README.chromium +3 -6
- package/front_end/third_party/marked/package/README.md +5 -5
- package/front_end/third_party/marked/package/bin/main.js +27 -22
- package/front_end/third_party/marked/package/bin/marked.js +2 -1
- package/front_end/third_party/marked/package/lib/marked.esm.d.ts +346 -256
- package/front_end/third_party/marked/package/lib/marked.esm.js +67 -2698
- package/front_end/third_party/marked/package/lib/marked.esm.js.map +7 -1
- package/front_end/third_party/marked/package/lib/marked.umd.js +69 -2722
- package/front_end/third_party/marked/package/lib/marked.umd.js.map +7 -1
- package/front_end/third_party/marked/package/man/marked.1 +4 -2
- package/front_end/third_party/marked/package/man/marked.1.md +2 -1
- package/front_end/third_party/marked/package/package.json +49 -57
- package/front_end/third_party/puppeteer-replay/README.chromium +2 -2
- package/front_end/third_party/puppeteer-replay/package/lib/cli.js +84 -80
- package/front_end/third_party/puppeteer-replay/package/lib/cli.js.map +1 -1
- package/front_end/third_party/puppeteer-replay/package/lib/extension-test.js +79 -83
- package/front_end/third_party/puppeteer-replay/package/lib/extension-test.js.map +1 -1
- package/front_end/third_party/puppeteer-replay/package/lib/main.d.ts +43 -171
- package/front_end/third_party/puppeteer-replay/package/lib/main.js +51 -206
- package/front_end/third_party/puppeteer-replay/package/lib/main.js.map +1 -1
- package/front_end/third_party/puppeteer-replay/package/package.json +37 -67
- package/front_end/tsconfig.json +1 -1
- package/front_end/ui/components/markdown_view/CodeBlock.ts +17 -6
- package/front_end/ui/components/markdown_view/MarkdownView.ts +39 -3
- package/front_end/ui/components/markdown_view/codeBlock.css +11 -0
- package/front_end/ui/components/markdown_view/markdownView.css +17 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +0 -79
- package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +16 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
- package/inspector_overlay/testing/InspectorOverlayHelpers.ts +2 -0
- package/mcp/mcp.ts +1 -6
- package/package.json +14 -16
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +0 -1015
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgentOverlay.ts +0 -87
- package/front_end/third_party/marked/package/bin/marked +0 -215
- package/front_end/third_party/marked/package/lib/marked.cjs +0 -2726
- package/front_end/third_party/marked/package/lib/marked.cjs.map +0 -1
- package/front_end/third_party/marked/package/lib/marked.d.cts +0 -670
- package/front_end/third_party/marked/package/lib/marked.js +0 -2780
- package/front_end/third_party/marked/package/man/marked.1.txt +0 -86
- package/front_end/third_party/marked/package/marked.min.js +0 -6
- package/front_end/third_party/marked/package/src/Lexer.js +0 -492
- package/front_end/third_party/marked/package/src/Parser.js +0 -286
- package/front_end/third_party/marked/package/src/Renderer.js +0 -166
- package/front_end/third_party/marked/package/src/Slugger.js +0 -49
- package/front_end/third_party/marked/package/src/TextRenderer.js +0 -42
- package/front_end/third_party/marked/package/src/Tokenizer.js +0 -755
- package/front_end/third_party/marked/package/src/defaults.js +0 -29
- package/front_end/third_party/marked/package/src/helpers.js +0 -249
- package/front_end/third_party/marked/package/src/marked.js +0 -350
- package/front_end/third_party/marked/package/src/rules.js +0 -285
- package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.cjs +0 -2099
- package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.cjs.map +0 -1
- package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.d.cts +0 -686
- package/front_end/third_party/puppeteer-replay/package/lib/cjs/main.d.ts +0 -35
- package/mcp/HostBindings.ts +0 -319
- /package/front_end/third_party/marked/package/{LICENSE.md → LICENSE} +0 -0
|
@@ -1,2099 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
Copyright 2022 Google LLC
|
|
5
|
-
|
|
6
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
-
you may not use this file except in compliance with the License.
|
|
8
|
-
You may obtain a copy of the License at
|
|
9
|
-
|
|
10
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
|
|
12
|
-
Unless required by applicable law or agreed to in writing, software
|
|
13
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
-
See the License for the specific language governing permissions and
|
|
16
|
-
limitations under the License.
|
|
17
|
-
*/
|
|
18
|
-
exports.SelectorType = void 0;
|
|
19
|
-
(function (SelectorType) {
|
|
20
|
-
SelectorType["CSS"] = "css";
|
|
21
|
-
SelectorType["ARIA"] = "aria";
|
|
22
|
-
SelectorType["Text"] = "text";
|
|
23
|
-
SelectorType["XPath"] = "xpath";
|
|
24
|
-
SelectorType["Pierce"] = "pierce";
|
|
25
|
-
})(exports.SelectorType || (exports.SelectorType = {}));
|
|
26
|
-
exports.StepType = void 0;
|
|
27
|
-
(function (StepType) {
|
|
28
|
-
StepType["Change"] = "change";
|
|
29
|
-
StepType["Click"] = "click";
|
|
30
|
-
StepType["Close"] = "close";
|
|
31
|
-
StepType["CustomStep"] = "customStep";
|
|
32
|
-
StepType["DoubleClick"] = "doubleClick";
|
|
33
|
-
StepType["EmulateNetworkConditions"] = "emulateNetworkConditions";
|
|
34
|
-
StepType["Hover"] = "hover";
|
|
35
|
-
StepType["KeyDown"] = "keyDown";
|
|
36
|
-
StepType["KeyUp"] = "keyUp";
|
|
37
|
-
StepType["Navigate"] = "navigate";
|
|
38
|
-
StepType["Scroll"] = "scroll";
|
|
39
|
-
StepType["SetViewport"] = "setViewport";
|
|
40
|
-
StepType["WaitForElement"] = "waitForElement";
|
|
41
|
-
StepType["WaitForExpression"] = "waitForExpression";
|
|
42
|
-
})(exports.StepType || (exports.StepType = {}));
|
|
43
|
-
exports.AssertedEventType = void 0;
|
|
44
|
-
(function (AssertedEventType) {
|
|
45
|
-
AssertedEventType["Navigation"] = "navigation";
|
|
46
|
-
})(exports.AssertedEventType || (exports.AssertedEventType = {}));
|
|
47
|
-
|
|
48
|
-
var Schema = /*#__PURE__*/Object.freeze({
|
|
49
|
-
__proto__: null,
|
|
50
|
-
get AssertedEventType () { return exports.AssertedEventType; },
|
|
51
|
-
get SelectorType () { return exports.SelectorType; },
|
|
52
|
-
get StepType () { return exports.StepType; }
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
Copyright 2022 Google LLC
|
|
57
|
-
|
|
58
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
59
|
-
you may not use this file except in compliance with the License.
|
|
60
|
-
You may obtain a copy of the License at
|
|
61
|
-
|
|
62
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
63
|
-
|
|
64
|
-
Unless required by applicable law or agreed to in writing, software
|
|
65
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
66
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
67
|
-
See the License for the specific language governing permissions and
|
|
68
|
-
limitations under the License.
|
|
69
|
-
*/
|
|
70
|
-
function assertAllStepTypesAreHandled(s) {
|
|
71
|
-
throw new Error(`Unknown step type: ${s.type}`);
|
|
72
|
-
}
|
|
73
|
-
const typeableInputTypes = new Set([
|
|
74
|
-
'textarea',
|
|
75
|
-
'text',
|
|
76
|
-
'url',
|
|
77
|
-
'tel',
|
|
78
|
-
'search',
|
|
79
|
-
'password',
|
|
80
|
-
'number',
|
|
81
|
-
'email',
|
|
82
|
-
]);
|
|
83
|
-
const pointerDeviceTypes = new Set([
|
|
84
|
-
'mouse',
|
|
85
|
-
'pen',
|
|
86
|
-
'touch',
|
|
87
|
-
]);
|
|
88
|
-
const mouseButtonMap = new Map([
|
|
89
|
-
['primary', 'left'],
|
|
90
|
-
['auxiliary', 'middle'],
|
|
91
|
-
['secondary', 'right'],
|
|
92
|
-
['back', 'back'],
|
|
93
|
-
['forward', 'forward'],
|
|
94
|
-
]);
|
|
95
|
-
function hasProperty(data, prop) {
|
|
96
|
-
// TODO: use Object.hasOwn once types are available https://github.com/microsoft/TypeScript/issues/44253
|
|
97
|
-
if (!Object.prototype.hasOwnProperty.call(data, prop)) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
const keyedData = data;
|
|
101
|
-
return keyedData[prop] !== undefined;
|
|
102
|
-
}
|
|
103
|
-
function isObject(data) {
|
|
104
|
-
return typeof data === 'object' && data !== null;
|
|
105
|
-
}
|
|
106
|
-
function isString(data) {
|
|
107
|
-
return typeof data === 'string';
|
|
108
|
-
}
|
|
109
|
-
function isNumber(data) {
|
|
110
|
-
return typeof data === 'number';
|
|
111
|
-
}
|
|
112
|
-
function isArray(data) {
|
|
113
|
-
return Array.isArray(data);
|
|
114
|
-
}
|
|
115
|
-
function isBoolean(data) {
|
|
116
|
-
return typeof data === 'boolean';
|
|
117
|
-
}
|
|
118
|
-
function isIntegerArray(data) {
|
|
119
|
-
return isArray(data) && data.every((item) => Number.isInteger(item));
|
|
120
|
-
}
|
|
121
|
-
function isKnownDeviceType(data) {
|
|
122
|
-
return typeof data === 'string' && pointerDeviceTypes.has(data);
|
|
123
|
-
}
|
|
124
|
-
function isKnownMouseButton(data) {
|
|
125
|
-
return typeof data === 'string' && mouseButtonMap.has(data);
|
|
126
|
-
}
|
|
127
|
-
function parseTarget(step) {
|
|
128
|
-
if (hasProperty(step, 'target') && isString(step.target)) {
|
|
129
|
-
return step.target;
|
|
130
|
-
}
|
|
131
|
-
return undefined;
|
|
132
|
-
}
|
|
133
|
-
function parseFrame(step) {
|
|
134
|
-
if (hasProperty(step, 'frame')) {
|
|
135
|
-
if (isIntegerArray(step.frame)) {
|
|
136
|
-
return step.frame;
|
|
137
|
-
}
|
|
138
|
-
throw new Error('Step `frame` is not an integer array');
|
|
139
|
-
}
|
|
140
|
-
return undefined;
|
|
141
|
-
}
|
|
142
|
-
function parseNumber(step, prop) {
|
|
143
|
-
if (hasProperty(step, prop)) {
|
|
144
|
-
const maybeNumber = step[prop];
|
|
145
|
-
if (isNumber(maybeNumber)) {
|
|
146
|
-
return maybeNumber;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
throw new Error(`Step.${prop} is not a number`);
|
|
150
|
-
}
|
|
151
|
-
function parseBoolean(step, prop) {
|
|
152
|
-
if (hasProperty(step, prop)) {
|
|
153
|
-
const maybeBoolean = step[prop];
|
|
154
|
-
if (isBoolean(maybeBoolean)) {
|
|
155
|
-
return maybeBoolean;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
throw new Error(`Step.${prop} is not a boolean`);
|
|
159
|
-
}
|
|
160
|
-
function parseOptionalNumber(step, prop) {
|
|
161
|
-
if (hasProperty(step, prop)) {
|
|
162
|
-
return parseNumber(step, prop);
|
|
163
|
-
}
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
function parseOptionalString(step, prop) {
|
|
167
|
-
if (hasProperty(step, prop)) {
|
|
168
|
-
return parseString(step, prop);
|
|
169
|
-
}
|
|
170
|
-
return undefined;
|
|
171
|
-
}
|
|
172
|
-
function parseOptionalBoolean(step, prop) {
|
|
173
|
-
if (hasProperty(step, prop)) {
|
|
174
|
-
return parseBoolean(step, prop);
|
|
175
|
-
}
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
|
-
function parseString(step, prop) {
|
|
179
|
-
if (hasProperty(step, prop)) {
|
|
180
|
-
const maybeString = step[prop];
|
|
181
|
-
if (isString(maybeString)) {
|
|
182
|
-
return maybeString;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
throw new Error(`Step.${prop} is not a string`);
|
|
186
|
-
}
|
|
187
|
-
function parseSelectors(step) {
|
|
188
|
-
if (!hasProperty(step, 'selectors')) {
|
|
189
|
-
throw new Error('Step does not have required selectors');
|
|
190
|
-
}
|
|
191
|
-
if (!isArray(step.selectors)) {
|
|
192
|
-
throw new Error('Step selectors are not an array');
|
|
193
|
-
}
|
|
194
|
-
if (step.selectors.length === 0) {
|
|
195
|
-
throw new Error('Step does not have required selectors');
|
|
196
|
-
}
|
|
197
|
-
return step.selectors.map((s) => {
|
|
198
|
-
if (!isString(s) && !isArray(s)) {
|
|
199
|
-
throw new Error('Selector is not an array or string');
|
|
200
|
-
}
|
|
201
|
-
if (isArray(s)) {
|
|
202
|
-
return s.map((sub) => {
|
|
203
|
-
if (!isString(sub)) {
|
|
204
|
-
throw new Error('Selector element is not a string');
|
|
205
|
-
}
|
|
206
|
-
return sub;
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
return s;
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
function parseOptionalSelectors(step) {
|
|
213
|
-
if (!hasProperty(step, 'selectors')) {
|
|
214
|
-
return undefined;
|
|
215
|
-
}
|
|
216
|
-
return parseSelectors(step);
|
|
217
|
-
}
|
|
218
|
-
function parseAssertedEvent(event) {
|
|
219
|
-
if (!isObject(event)) {
|
|
220
|
-
throw new Error('Asserted event is not an object');
|
|
221
|
-
}
|
|
222
|
-
if (!hasProperty(event, 'type')) {
|
|
223
|
-
throw new Error('Asserted event is missing type');
|
|
224
|
-
}
|
|
225
|
-
if (event.type === exports.AssertedEventType.Navigation) {
|
|
226
|
-
return {
|
|
227
|
-
type: exports.AssertedEventType.Navigation,
|
|
228
|
-
url: parseOptionalString(event, 'url'),
|
|
229
|
-
title: parseOptionalString(event, 'title'),
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
throw new Error('Unknown assertedEvent type');
|
|
233
|
-
}
|
|
234
|
-
function parseAssertedEvents(events) {
|
|
235
|
-
if (!isArray(events)) {
|
|
236
|
-
return undefined;
|
|
237
|
-
}
|
|
238
|
-
return events.map(parseAssertedEvent);
|
|
239
|
-
}
|
|
240
|
-
function parseBaseStep(type, step) {
|
|
241
|
-
if (hasProperty(step, 'timeout') &&
|
|
242
|
-
isNumber(step.timeout) &&
|
|
243
|
-
!validTimeout(step.timeout)) {
|
|
244
|
-
throw new Error(timeoutErrorMessage);
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
type,
|
|
248
|
-
assertedEvents: hasProperty(step, 'assertedEvents')
|
|
249
|
-
? parseAssertedEvents(step.assertedEvents)
|
|
250
|
-
: undefined,
|
|
251
|
-
timeout: hasProperty(step, 'timeout') && isNumber(step.timeout)
|
|
252
|
-
? step.timeout
|
|
253
|
-
: undefined,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
function parseStepWithTarget(type, step) {
|
|
257
|
-
return {
|
|
258
|
-
...parseBaseStep(type, step),
|
|
259
|
-
target: parseTarget(step),
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
function parseStepWithFrame(type, step) {
|
|
263
|
-
return {
|
|
264
|
-
...parseStepWithTarget(type, step),
|
|
265
|
-
frame: parseFrame(step),
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
function parseStepWithSelectors(type, step) {
|
|
269
|
-
return {
|
|
270
|
-
...parseStepWithFrame(type, step),
|
|
271
|
-
selectors: parseSelectors(step),
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
function parseClickAttributes(step) {
|
|
275
|
-
const attributes = {
|
|
276
|
-
offsetX: parseNumber(step, 'offsetX'),
|
|
277
|
-
offsetY: parseNumber(step, 'offsetY'),
|
|
278
|
-
duration: parseOptionalNumber(step, 'duration'),
|
|
279
|
-
};
|
|
280
|
-
const deviceType = parseOptionalString(step, 'deviceType');
|
|
281
|
-
if (deviceType) {
|
|
282
|
-
if (!isKnownDeviceType(deviceType)) {
|
|
283
|
-
throw new Error(`'deviceType' for click steps must be one of the following: ${[
|
|
284
|
-
...pointerDeviceTypes,
|
|
285
|
-
].join(', ')}`);
|
|
286
|
-
}
|
|
287
|
-
attributes.deviceType = deviceType;
|
|
288
|
-
}
|
|
289
|
-
const button = parseOptionalString(step, 'button');
|
|
290
|
-
if (button) {
|
|
291
|
-
if (!isKnownMouseButton(button)) {
|
|
292
|
-
throw new Error(`'button' for click steps must be one of the following: ${[
|
|
293
|
-
...mouseButtonMap.keys(),
|
|
294
|
-
].join(', ')}`);
|
|
295
|
-
}
|
|
296
|
-
attributes.button = button;
|
|
297
|
-
}
|
|
298
|
-
return attributes;
|
|
299
|
-
}
|
|
300
|
-
function parseClickStep(step) {
|
|
301
|
-
return {
|
|
302
|
-
...parseStepWithSelectors(exports.StepType.Click, step),
|
|
303
|
-
...parseClickAttributes(step),
|
|
304
|
-
type: exports.StepType.Click,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
function parseDoubleClickStep(step) {
|
|
308
|
-
return {
|
|
309
|
-
...parseStepWithSelectors(exports.StepType.DoubleClick, step),
|
|
310
|
-
...parseClickAttributes(step),
|
|
311
|
-
type: exports.StepType.DoubleClick,
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
function parseHoverStep(step) {
|
|
315
|
-
return {
|
|
316
|
-
...parseStepWithSelectors(exports.StepType.Hover, step),
|
|
317
|
-
type: exports.StepType.Hover,
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
function parseChangeStep(step) {
|
|
321
|
-
return {
|
|
322
|
-
...parseStepWithSelectors(exports.StepType.Change, step),
|
|
323
|
-
type: exports.StepType.Change,
|
|
324
|
-
value: parseString(step, 'value'),
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
function parseKeyDownStep(step) {
|
|
328
|
-
return {
|
|
329
|
-
...parseStepWithTarget(exports.StepType.KeyDown, step),
|
|
330
|
-
type: exports.StepType.KeyDown,
|
|
331
|
-
// TODO: type-check keys.
|
|
332
|
-
key: parseString(step, 'key'),
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
function parseKeyUpStep(step) {
|
|
336
|
-
return {
|
|
337
|
-
...parseStepWithTarget(exports.StepType.KeyUp, step),
|
|
338
|
-
type: exports.StepType.KeyUp,
|
|
339
|
-
// TODO: type-check keys.
|
|
340
|
-
key: parseString(step, 'key'),
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
function parseEmulateNetworkConditionsStep(step) {
|
|
344
|
-
return {
|
|
345
|
-
...parseStepWithTarget(exports.StepType.EmulateNetworkConditions, step),
|
|
346
|
-
type: exports.StepType.EmulateNetworkConditions,
|
|
347
|
-
download: parseNumber(step, 'download'),
|
|
348
|
-
upload: parseNumber(step, 'upload'),
|
|
349
|
-
latency: parseNumber(step, 'latency'),
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
function parseCloseStep(step) {
|
|
353
|
-
return {
|
|
354
|
-
...parseStepWithTarget(exports.StepType.Close, step),
|
|
355
|
-
type: exports.StepType.Close,
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
function parseSetViewportStep(step) {
|
|
359
|
-
return {
|
|
360
|
-
...parseStepWithTarget(exports.StepType.SetViewport, step),
|
|
361
|
-
type: exports.StepType.SetViewport,
|
|
362
|
-
width: parseNumber(step, 'width'),
|
|
363
|
-
height: parseNumber(step, 'height'),
|
|
364
|
-
deviceScaleFactor: parseNumber(step, 'deviceScaleFactor'),
|
|
365
|
-
isMobile: parseBoolean(step, 'isMobile'),
|
|
366
|
-
hasTouch: parseBoolean(step, 'hasTouch'),
|
|
367
|
-
isLandscape: parseBoolean(step, 'isLandscape'),
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
function parseScrollStep(step) {
|
|
371
|
-
return {
|
|
372
|
-
...parseStepWithFrame(exports.StepType.Scroll, step),
|
|
373
|
-
type: exports.StepType.Scroll,
|
|
374
|
-
x: parseOptionalNumber(step, 'x'),
|
|
375
|
-
y: parseOptionalNumber(step, 'y'),
|
|
376
|
-
selectors: parseOptionalSelectors(step),
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
function parseNavigateStep(step) {
|
|
380
|
-
return {
|
|
381
|
-
...parseStepWithTarget(exports.StepType.Navigate, step),
|
|
382
|
-
type: exports.StepType.Navigate,
|
|
383
|
-
target: parseTarget(step),
|
|
384
|
-
url: parseString(step, 'url'),
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
function parseWaitForElementStep(step) {
|
|
388
|
-
const operator = parseOptionalString(step, 'operator');
|
|
389
|
-
if (operator && operator !== '>=' && operator !== '==' && operator !== '<=') {
|
|
390
|
-
throw new Error("WaitForElement step's operator is not one of '>=','==','<='");
|
|
391
|
-
}
|
|
392
|
-
if (hasProperty(step, 'attributes')) {
|
|
393
|
-
if (!isObject(step.attributes) ||
|
|
394
|
-
Object.values(step.attributes).some((attribute) => typeof attribute !== 'string')) {
|
|
395
|
-
throw new Error("WaitForElement step's attribute is not a dictionary of strings");
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
if (hasProperty(step, 'properties')) {
|
|
399
|
-
if (!isObject(step.properties)) {
|
|
400
|
-
throw new Error("WaitForElement step's attribute is not an object");
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
return {
|
|
404
|
-
...parseStepWithSelectors(exports.StepType.WaitForElement, step),
|
|
405
|
-
type: exports.StepType.WaitForElement,
|
|
406
|
-
operator: operator,
|
|
407
|
-
count: parseOptionalNumber(step, 'count'),
|
|
408
|
-
visible: parseOptionalBoolean(step, 'visible'),
|
|
409
|
-
attributes: hasProperty(step, 'attributes')
|
|
410
|
-
? step.attributes
|
|
411
|
-
: undefined,
|
|
412
|
-
properties: hasProperty(step, 'properties')
|
|
413
|
-
? step.properties
|
|
414
|
-
: undefined,
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function parseWaitForExpressionStep(step) {
|
|
418
|
-
if (!hasProperty(step, 'expression')) {
|
|
419
|
-
throw new Error('waitForExpression step is missing `expression`');
|
|
420
|
-
}
|
|
421
|
-
return {
|
|
422
|
-
...parseStepWithFrame(exports.StepType.WaitForExpression, step),
|
|
423
|
-
type: exports.StepType.WaitForExpression,
|
|
424
|
-
expression: parseString(step, 'expression'),
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
function parseCustomStep(step) {
|
|
428
|
-
if (!hasProperty(step, 'name')) {
|
|
429
|
-
throw new Error('customStep is missing name');
|
|
430
|
-
}
|
|
431
|
-
if (!isString(step.name)) {
|
|
432
|
-
throw new Error("customStep's name is not a string");
|
|
433
|
-
}
|
|
434
|
-
return {
|
|
435
|
-
...parseStepWithFrame(exports.StepType.CustomStep, step),
|
|
436
|
-
type: exports.StepType.CustomStep,
|
|
437
|
-
name: step.name,
|
|
438
|
-
parameters: hasProperty(step, 'parameters') ? step.parameters : undefined,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
function parseStep(step, idx) {
|
|
442
|
-
if (!isObject(step)) {
|
|
443
|
-
throw new Error(idx ? `Step ${idx} is not an object` : 'Step is not an object');
|
|
444
|
-
}
|
|
445
|
-
if (!hasProperty(step, 'type')) {
|
|
446
|
-
throw new Error(idx ? `Step ${idx} does not have a type` : 'Step does not have a type');
|
|
447
|
-
}
|
|
448
|
-
if (!isString(step.type)) {
|
|
449
|
-
throw new Error(idx
|
|
450
|
-
? `Type of the step ${idx} is not a string`
|
|
451
|
-
: 'Type of the step is not a string');
|
|
452
|
-
}
|
|
453
|
-
switch (step.type) {
|
|
454
|
-
case exports.StepType.Click:
|
|
455
|
-
return parseClickStep(step);
|
|
456
|
-
case exports.StepType.DoubleClick:
|
|
457
|
-
return parseDoubleClickStep(step);
|
|
458
|
-
case exports.StepType.Hover:
|
|
459
|
-
return parseHoverStep(step);
|
|
460
|
-
case exports.StepType.Change:
|
|
461
|
-
return parseChangeStep(step);
|
|
462
|
-
case exports.StepType.KeyDown:
|
|
463
|
-
return parseKeyDownStep(step);
|
|
464
|
-
case exports.StepType.KeyUp:
|
|
465
|
-
return parseKeyUpStep(step);
|
|
466
|
-
case exports.StepType.EmulateNetworkConditions:
|
|
467
|
-
return parseEmulateNetworkConditionsStep(step);
|
|
468
|
-
case exports.StepType.Close:
|
|
469
|
-
return parseCloseStep(step);
|
|
470
|
-
case exports.StepType.SetViewport:
|
|
471
|
-
return parseSetViewportStep(step);
|
|
472
|
-
case exports.StepType.Scroll:
|
|
473
|
-
return parseScrollStep(step);
|
|
474
|
-
case exports.StepType.Navigate:
|
|
475
|
-
return parseNavigateStep(step);
|
|
476
|
-
case exports.StepType.CustomStep:
|
|
477
|
-
return parseCustomStep(step);
|
|
478
|
-
case exports.StepType.WaitForElement:
|
|
479
|
-
return parseWaitForElementStep(step);
|
|
480
|
-
case exports.StepType.WaitForExpression:
|
|
481
|
-
return parseWaitForExpressionStep(step);
|
|
482
|
-
default:
|
|
483
|
-
throw new Error(`Step type ${step.type} is not supported`);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
function parseSteps(steps) {
|
|
487
|
-
const result = [];
|
|
488
|
-
if (!isArray(steps)) {
|
|
489
|
-
throw new Error('Recording `steps` is not an array');
|
|
490
|
-
}
|
|
491
|
-
for (const [idx, step] of steps.entries()) {
|
|
492
|
-
result.push(parseStep(step, idx));
|
|
493
|
-
}
|
|
494
|
-
return result;
|
|
495
|
-
}
|
|
496
|
-
function cleanUndefined(json) {
|
|
497
|
-
return JSON.parse(JSON.stringify(json));
|
|
498
|
-
}
|
|
499
|
-
const minTimeout = 1;
|
|
500
|
-
const maxTimeout = 30000;
|
|
501
|
-
const timeoutErrorMessage = `Timeout is not between ${minTimeout} and ${maxTimeout} milliseconds`;
|
|
502
|
-
function validTimeout(timeout) {
|
|
503
|
-
return timeout >= minTimeout && timeout <= maxTimeout;
|
|
504
|
-
}
|
|
505
|
-
function parse(data) {
|
|
506
|
-
if (!isObject(data)) {
|
|
507
|
-
throw new Error('Recording is not an object');
|
|
508
|
-
}
|
|
509
|
-
if (!hasProperty(data, 'title')) {
|
|
510
|
-
throw new Error('Recording is missing `title`');
|
|
511
|
-
}
|
|
512
|
-
if (!isString(data.title)) {
|
|
513
|
-
throw new Error('Recording `title` is not a string');
|
|
514
|
-
}
|
|
515
|
-
if (hasProperty(data, 'timeout') && !isNumber(data.timeout)) {
|
|
516
|
-
throw new Error('Recording `timeout` is not a number');
|
|
517
|
-
}
|
|
518
|
-
if (!hasProperty(data, 'steps')) {
|
|
519
|
-
throw new Error('Recording is missing `steps`');
|
|
520
|
-
}
|
|
521
|
-
if (hasProperty(data, 'timeout') &&
|
|
522
|
-
isNumber(data.timeout) &&
|
|
523
|
-
!validTimeout(data.timeout)) {
|
|
524
|
-
throw new Error(timeoutErrorMessage);
|
|
525
|
-
}
|
|
526
|
-
return cleanUndefined({
|
|
527
|
-
title: data.title,
|
|
528
|
-
timeout: hasProperty(data, 'timeout') && isNumber(data.timeout)
|
|
529
|
-
? data.timeout
|
|
530
|
-
: undefined,
|
|
531
|
-
selectorAttribute: hasProperty(data, 'selectorAttribute') && isString(data.selectorAttribute)
|
|
532
|
-
? data.selectorAttribute
|
|
533
|
-
: undefined,
|
|
534
|
-
steps: parseSteps(data.steps),
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* Detects what type of a selector the string contains. For example,
|
|
539
|
-
* `aria/Label` is a SelectorType.ARIA.
|
|
540
|
-
*
|
|
541
|
-
* Note that CSS selectors are special and usually don't require a prefix,
|
|
542
|
-
* therefore, SelectorType.CSS is the default type if other types didn't match.
|
|
543
|
-
*/
|
|
544
|
-
function getSelectorType(selector) {
|
|
545
|
-
for (const value of Object.values(exports.SelectorType)) {
|
|
546
|
-
if (selector.startsWith(`${value}/`)) {
|
|
547
|
-
return value;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
return exports.SelectorType.CSS;
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Converts a selector or an array of selector parts into a Puppeteer selector.
|
|
554
|
-
*
|
|
555
|
-
* @see https://pptr.dev/guides/query-selectors#p-elements
|
|
556
|
-
*/
|
|
557
|
-
function selectorToPElementSelector(selector) {
|
|
558
|
-
if (!Array.isArray(selector)) {
|
|
559
|
-
selector = [selector];
|
|
560
|
-
}
|
|
561
|
-
function escape(input) {
|
|
562
|
-
return input.replace(/['"()]/g, `\\$&`);
|
|
563
|
-
}
|
|
564
|
-
const result = selector.map((s) => {
|
|
565
|
-
switch (getSelectorType(s)) {
|
|
566
|
-
case exports.SelectorType.ARIA:
|
|
567
|
-
return `::-p-aria(${escape(s.substring(exports.SelectorType.ARIA.length + 1))})`;
|
|
568
|
-
case exports.SelectorType.CSS:
|
|
569
|
-
return s;
|
|
570
|
-
case exports.SelectorType.XPath:
|
|
571
|
-
return `::-p-xpath(${escape(s.substring(exports.SelectorType.XPath.length + 1))})`;
|
|
572
|
-
case exports.SelectorType.Pierce:
|
|
573
|
-
return `:scope >>> ${s.substring(exports.SelectorType.Pierce.length + 1)}`;
|
|
574
|
-
case exports.SelectorType.Text:
|
|
575
|
-
return `::-p-text(${escape(s.substring(exports.SelectorType.Text.length + 1))})`;
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
return result.join(' >>>> ');
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
Copyright 2022 Google LLC
|
|
583
|
-
|
|
584
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
585
|
-
you may not use this file except in compliance with the License.
|
|
586
|
-
You may obtain a copy of the License at
|
|
587
|
-
|
|
588
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
589
|
-
|
|
590
|
-
Unless required by applicable law or agreed to in writing, software
|
|
591
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
592
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
593
|
-
See the License for the specific language governing permissions and
|
|
594
|
-
limitations under the License.
|
|
595
|
-
*/
|
|
596
|
-
class StringifyExtension {
|
|
597
|
-
async beforeAllSteps(out, flow) { }
|
|
598
|
-
async afterAllSteps(out, flow) { }
|
|
599
|
-
async beforeEachStep(out, step, flow) { }
|
|
600
|
-
async stringifyStep(out, step, flow) { }
|
|
601
|
-
async afterEachStep(out, step, flow) { }
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/**
|
|
605
|
-
Copyright 2022 Google LLC
|
|
606
|
-
|
|
607
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
608
|
-
you may not use this file except in compliance with the License.
|
|
609
|
-
You may obtain a copy of the License at
|
|
610
|
-
|
|
611
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
612
|
-
|
|
613
|
-
Unless required by applicable law or agreed to in writing, software
|
|
614
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
615
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
616
|
-
See the License for the specific language governing permissions and
|
|
617
|
-
limitations under the License.
|
|
618
|
-
*/
|
|
619
|
-
/**
|
|
620
|
-
* Stringifies a user flow to JSON with source maps.
|
|
621
|
-
*
|
|
622
|
-
* You probably want to strip the source map because not all
|
|
623
|
-
* parsers support comments in JSON.
|
|
624
|
-
*/
|
|
625
|
-
class JSONStringifyExtension extends StringifyExtension {
|
|
626
|
-
async beforeAllSteps(out, flow) {
|
|
627
|
-
const copy = {
|
|
628
|
-
...flow,
|
|
629
|
-
steps: undefined,
|
|
630
|
-
};
|
|
631
|
-
// Stringify top-level attributes.
|
|
632
|
-
const text = JSON.stringify(copy, null, out.getIndent());
|
|
633
|
-
const lines = text.split('\n');
|
|
634
|
-
lines.pop();
|
|
635
|
-
lines[lines.length - 1] += ',';
|
|
636
|
-
lines.push(out.getIndent() + `"steps": [`);
|
|
637
|
-
out.appendLine(lines.join('\n')).startBlock().startBlock();
|
|
638
|
-
}
|
|
639
|
-
async afterAllSteps(out) {
|
|
640
|
-
out
|
|
641
|
-
.endBlock()
|
|
642
|
-
.endBlock()
|
|
643
|
-
.appendLine(out.getIndent() + `]`)
|
|
644
|
-
.appendLine('}');
|
|
645
|
-
}
|
|
646
|
-
async stringifyStep(out, step, flow) {
|
|
647
|
-
const stepText = JSON.stringify(step, null, out.getIndent());
|
|
648
|
-
if (!flow) {
|
|
649
|
-
out.appendLine(stepText);
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
const separator = flow.steps.lastIndexOf(step) === flow.steps.length - 1 ? '' : ',';
|
|
653
|
-
out.appendLine(stepText + separator);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
Copyright 2022 Google LLC
|
|
659
|
-
|
|
660
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
661
|
-
you may not use this file except in compliance with the License.
|
|
662
|
-
You may obtain a copy of the License at
|
|
663
|
-
|
|
664
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
665
|
-
|
|
666
|
-
Unless required by applicable law or agreed to in writing, software
|
|
667
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
668
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
669
|
-
See the License for the specific language governing permissions and
|
|
670
|
-
limitations under the License.
|
|
671
|
-
*/
|
|
672
|
-
class InMemoryLineWriter {
|
|
673
|
-
#indentation;
|
|
674
|
-
#currentIndentation = 0;
|
|
675
|
-
#lines = [];
|
|
676
|
-
constructor(indentation) {
|
|
677
|
-
this.#indentation = indentation;
|
|
678
|
-
}
|
|
679
|
-
appendLine(line) {
|
|
680
|
-
const lines = line.split('\n').map((line) => {
|
|
681
|
-
const indentedLine = line
|
|
682
|
-
? this.#indentation.repeat(this.#currentIndentation) + line.trimEnd()
|
|
683
|
-
: '';
|
|
684
|
-
return indentedLine;
|
|
685
|
-
});
|
|
686
|
-
this.#lines.push(...lines);
|
|
687
|
-
return this;
|
|
688
|
-
}
|
|
689
|
-
startBlock() {
|
|
690
|
-
this.#currentIndentation++;
|
|
691
|
-
return this;
|
|
692
|
-
}
|
|
693
|
-
endBlock() {
|
|
694
|
-
this.#currentIndentation--;
|
|
695
|
-
if (this.#currentIndentation < 0) {
|
|
696
|
-
throw new Error('Extra endBlock');
|
|
697
|
-
}
|
|
698
|
-
return this;
|
|
699
|
-
}
|
|
700
|
-
toString() {
|
|
701
|
-
// Scripts should end with a final blank line.
|
|
702
|
-
return this.#lines.join('\n') + '\n';
|
|
703
|
-
}
|
|
704
|
-
getIndent() {
|
|
705
|
-
return this.#indentation;
|
|
706
|
-
}
|
|
707
|
-
getSize() {
|
|
708
|
-
return this.#lines.length;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
Copyright 2022 Google LLC
|
|
714
|
-
|
|
715
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
716
|
-
you may not use this file except in compliance with the License.
|
|
717
|
-
You may obtain a copy of the License at
|
|
718
|
-
|
|
719
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
720
|
-
|
|
721
|
-
Unless required by applicable law or agreed to in writing, software
|
|
722
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
723
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
724
|
-
See the License for the specific language governing permissions and
|
|
725
|
-
limitations under the License.
|
|
726
|
-
*/
|
|
727
|
-
/**
|
|
728
|
-
* Copyright (c) 2020 The Chromium Authors. All rights reserved.
|
|
729
|
-
* Use of this source code is governed by a BSD-style license that can be
|
|
730
|
-
* found in the LICENSE file.
|
|
731
|
-
*/
|
|
732
|
-
function formatJSONAsJS(json, indent) {
|
|
733
|
-
const buffer = [];
|
|
734
|
-
format(json, buffer, 1, indent);
|
|
735
|
-
return buffer.join('');
|
|
736
|
-
}
|
|
737
|
-
function format(json, buffer = [], level = 1, indent = ' ') {
|
|
738
|
-
switch (typeof json) {
|
|
739
|
-
case 'bigint':
|
|
740
|
-
case 'symbol':
|
|
741
|
-
case 'function':
|
|
742
|
-
case 'undefined':
|
|
743
|
-
throw new Error('Invalid JSON');
|
|
744
|
-
case 'number':
|
|
745
|
-
case 'boolean':
|
|
746
|
-
buffer.push(String(json));
|
|
747
|
-
break;
|
|
748
|
-
case 'string':
|
|
749
|
-
buffer.push(formatAsJSLiteral(json));
|
|
750
|
-
break;
|
|
751
|
-
case 'object': {
|
|
752
|
-
if (json === null) {
|
|
753
|
-
buffer.push('null');
|
|
754
|
-
}
|
|
755
|
-
else if (Array.isArray(json)) {
|
|
756
|
-
buffer.push('[\n');
|
|
757
|
-
for (let i = 0; i < json.length; i++) {
|
|
758
|
-
buffer.push(indent.repeat(level));
|
|
759
|
-
format(json[i], buffer, level + 1, indent);
|
|
760
|
-
if (i !== json.length - 1) {
|
|
761
|
-
buffer.push(',');
|
|
762
|
-
}
|
|
763
|
-
buffer.push('\n');
|
|
764
|
-
}
|
|
765
|
-
buffer.push(indent.repeat(level - 1) + ']');
|
|
766
|
-
}
|
|
767
|
-
else {
|
|
768
|
-
buffer.push('{\n');
|
|
769
|
-
const keys = Object.keys(json);
|
|
770
|
-
for (let i = 0; i < keys.length; i++) {
|
|
771
|
-
const key = keys[i];
|
|
772
|
-
const value = json[key];
|
|
773
|
-
if (value === undefined) {
|
|
774
|
-
continue;
|
|
775
|
-
}
|
|
776
|
-
buffer.push(indent.repeat(level));
|
|
777
|
-
buffer.push(key);
|
|
778
|
-
buffer.push(': ');
|
|
779
|
-
format(value, buffer, level + 1, indent);
|
|
780
|
-
if (i !== keys.length - 1) {
|
|
781
|
-
buffer.push(',');
|
|
782
|
-
}
|
|
783
|
-
buffer.push('\n');
|
|
784
|
-
}
|
|
785
|
-
buffer.push(indent.repeat(level - 1) + '}');
|
|
786
|
-
}
|
|
787
|
-
break;
|
|
788
|
-
}
|
|
789
|
-
default:
|
|
790
|
-
throw new Error('Unknown object type');
|
|
791
|
-
}
|
|
792
|
-
return buffer;
|
|
793
|
-
}
|
|
794
|
-
// Taken from https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/front_end/core/platform/string-utilities.ts;l=29;drc=111134437ee51d74433829bed0088f7239e18867.
|
|
795
|
-
const toHexadecimal = (charCode, padToLength) => {
|
|
796
|
-
return charCode.toString(16).toUpperCase().padStart(padToLength, '0');
|
|
797
|
-
};
|
|
798
|
-
// Remember to update the third group in the regexps patternsToEscape and
|
|
799
|
-
// patternsToEscapePlusSingleQuote when adding new entries in this map.
|
|
800
|
-
const escapedReplacements = new Map([
|
|
801
|
-
['\b', '\\b'],
|
|
802
|
-
['\f', '\\f'],
|
|
803
|
-
['\n', '\\n'],
|
|
804
|
-
['\r', '\\r'],
|
|
805
|
-
['\t', '\\t'],
|
|
806
|
-
['\v', '\\v'],
|
|
807
|
-
["'", "\\'"],
|
|
808
|
-
['\\', '\\\\'],
|
|
809
|
-
['<!--', '\\x3C!--'],
|
|
810
|
-
['<script', '\\x3Cscript'],
|
|
811
|
-
['</script', '\\x3C/script'],
|
|
812
|
-
]);
|
|
813
|
-
const formatAsJSLiteral = (content) => {
|
|
814
|
-
const patternsToEscape = /(\\|<(?:!--|\/?script))|(\p{Control})|(\p{Surrogate})/gu;
|
|
815
|
-
const patternsToEscapePlusSingleQuote = /(\\|'|<(?:!--|\/?script))|(\p{Control})|(\p{Surrogate})/gu;
|
|
816
|
-
const escapePattern = (match, pattern, controlChar, loneSurrogate) => {
|
|
817
|
-
if (controlChar) {
|
|
818
|
-
if (escapedReplacements.has(controlChar)) {
|
|
819
|
-
// @ts-ignore https://github.com/microsoft/TypeScript/issues/13086
|
|
820
|
-
return escapedReplacements.get(controlChar);
|
|
821
|
-
}
|
|
822
|
-
const twoDigitHex = toHexadecimal(controlChar.charCodeAt(0), 2);
|
|
823
|
-
return '\\x' + twoDigitHex;
|
|
824
|
-
}
|
|
825
|
-
if (loneSurrogate) {
|
|
826
|
-
const fourDigitHex = toHexadecimal(loneSurrogate.charCodeAt(0), 4);
|
|
827
|
-
return '\\u' + fourDigitHex;
|
|
828
|
-
}
|
|
829
|
-
if (pattern) {
|
|
830
|
-
return escapedReplacements.get(pattern) || '';
|
|
831
|
-
}
|
|
832
|
-
return match;
|
|
833
|
-
};
|
|
834
|
-
let escapedContent = '';
|
|
835
|
-
let quote = '';
|
|
836
|
-
if (!content.includes("'")) {
|
|
837
|
-
quote = "'";
|
|
838
|
-
escapedContent = content.replace(patternsToEscape, escapePattern);
|
|
839
|
-
}
|
|
840
|
-
else if (!content.includes('"')) {
|
|
841
|
-
quote = '"';
|
|
842
|
-
escapedContent = content.replace(patternsToEscape, escapePattern);
|
|
843
|
-
}
|
|
844
|
-
else if (!content.includes('`') && !content.includes('${')) {
|
|
845
|
-
quote = '`';
|
|
846
|
-
escapedContent = content.replace(patternsToEscape, escapePattern);
|
|
847
|
-
}
|
|
848
|
-
else {
|
|
849
|
-
quote = "'";
|
|
850
|
-
escapedContent = content.replace(patternsToEscapePlusSingleQuote, escapePattern);
|
|
851
|
-
}
|
|
852
|
-
return `${quote}${escapedContent}${quote}`;
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
/**
|
|
856
|
-
Copyright 2022 Google LLC
|
|
857
|
-
|
|
858
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
859
|
-
you may not use this file except in compliance with the License.
|
|
860
|
-
You may obtain a copy of the License at
|
|
861
|
-
|
|
862
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
863
|
-
|
|
864
|
-
Unless required by applicable law or agreed to in writing, software
|
|
865
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
866
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
867
|
-
See the License for the specific language governing permissions and
|
|
868
|
-
limitations under the License.
|
|
869
|
-
*/
|
|
870
|
-
class PuppeteerStringifyExtension extends StringifyExtension {
|
|
871
|
-
#shouldAppendWaitForElementHelper = false;
|
|
872
|
-
#targetBrowser;
|
|
873
|
-
constructor(targetBrowser = 'chrome') {
|
|
874
|
-
super();
|
|
875
|
-
this.#targetBrowser = targetBrowser;
|
|
876
|
-
}
|
|
877
|
-
async beforeAllSteps(out, flow) {
|
|
878
|
-
out.appendLine("const puppeteer = require('puppeteer'); // v23.0.0 or later");
|
|
879
|
-
out.appendLine('');
|
|
880
|
-
out.appendLine('(async () => {').startBlock();
|
|
881
|
-
if (this.#targetBrowser === 'firefox') {
|
|
882
|
-
out.appendLine(`const browser = await puppeteer.launch({browser: 'firefox'});`);
|
|
883
|
-
}
|
|
884
|
-
else {
|
|
885
|
-
out.appendLine('const browser = await puppeteer.launch();');
|
|
886
|
-
}
|
|
887
|
-
out.appendLine('const page = await browser.newPage();');
|
|
888
|
-
out.appendLine(`const timeout = ${flow.timeout || defaultTimeout};`);
|
|
889
|
-
out.appendLine('page.setDefaultTimeout(timeout);');
|
|
890
|
-
out.appendLine('');
|
|
891
|
-
this.#shouldAppendWaitForElementHelper = false;
|
|
892
|
-
}
|
|
893
|
-
async afterAllSteps(out, flow) {
|
|
894
|
-
out.appendLine('');
|
|
895
|
-
out.appendLine('await browser.close();');
|
|
896
|
-
out.appendLine('');
|
|
897
|
-
if (this.#shouldAppendWaitForElementHelper) {
|
|
898
|
-
for (const line of waitForElementHelper.split('\n')) {
|
|
899
|
-
out.appendLine(line);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
out.endBlock().appendLine('})().catch(err => {').startBlock();
|
|
903
|
-
out.appendLine('console.error(err);');
|
|
904
|
-
out.appendLine('process.exit(1);');
|
|
905
|
-
out.endBlock().appendLine('});');
|
|
906
|
-
}
|
|
907
|
-
async stringifyStep(out, step, flow) {
|
|
908
|
-
out.appendLine('{').startBlock();
|
|
909
|
-
if (step.timeout !== undefined) {
|
|
910
|
-
out.appendLine(`const timeout = ${step.timeout};`);
|
|
911
|
-
}
|
|
912
|
-
this.#appendContext(out, step);
|
|
913
|
-
const waitForEvents = step.assertedEvents && step.type !== exports.StepType.Navigate;
|
|
914
|
-
if (waitForEvents) {
|
|
915
|
-
out.appendLine('const promises = [];');
|
|
916
|
-
out.appendLine('const startWaitingForEvents = () => {').startBlock();
|
|
917
|
-
for (const event of step.assertedEvents) {
|
|
918
|
-
switch (event.type) {
|
|
919
|
-
case exports.AssertedEventType.Navigation: {
|
|
920
|
-
out.appendLine(`promises.push(${'frame' in step && step.frame ? 'frame' : 'targetPage'}.waitForNavigation());`);
|
|
921
|
-
break;
|
|
922
|
-
}
|
|
923
|
-
default:
|
|
924
|
-
throw new Error(`Event type ${event.type} is not supported`);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
out.endBlock().appendLine('}');
|
|
928
|
-
}
|
|
929
|
-
this.#appendStepType(out, step);
|
|
930
|
-
if (waitForEvents) {
|
|
931
|
-
out.appendLine('await Promise.all(promises);');
|
|
932
|
-
}
|
|
933
|
-
out.endBlock().appendLine('}');
|
|
934
|
-
}
|
|
935
|
-
#appendTarget(out, target) {
|
|
936
|
-
if (target === 'main') {
|
|
937
|
-
out.appendLine('const targetPage = page;');
|
|
938
|
-
}
|
|
939
|
-
else {
|
|
940
|
-
out.appendLine(`const target = await browser.waitForTarget(t => t.url() === ${formatJSONAsJS(target, out.getIndent())}, { timeout });`);
|
|
941
|
-
out.appendLine('const targetPage = await target.page();');
|
|
942
|
-
out.appendLine('targetPage.setDefaultTimeout(timeout);');
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
#appendFrame(out, path) {
|
|
946
|
-
out.appendLine('let frame = targetPage.mainFrame();');
|
|
947
|
-
for (const index of path) {
|
|
948
|
-
out.appendLine(`frame = frame.childFrames()[${index}];`);
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
#appendContext(out, step) {
|
|
952
|
-
// TODO fix optional target: should it be main?
|
|
953
|
-
this.#appendTarget(out, step.target || 'main');
|
|
954
|
-
// TODO fix optional frame: should it be required?
|
|
955
|
-
if (step.frame) {
|
|
956
|
-
this.#appendFrame(out, step.frame);
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
#appendLocators(out, step, action) {
|
|
960
|
-
out.appendLine('await puppeteer.Locator.race([').startBlock();
|
|
961
|
-
out.appendLine(step.selectors
|
|
962
|
-
.map((s) => {
|
|
963
|
-
return `${step.frame ? 'frame' : 'targetPage'}.locator(${formatJSONAsJS(selectorToPElementSelector(s), out.getIndent())})`;
|
|
964
|
-
})
|
|
965
|
-
.join(',\n'));
|
|
966
|
-
out.endBlock().appendLine('])');
|
|
967
|
-
out.startBlock().appendLine('.setTimeout(timeout)');
|
|
968
|
-
if (step.assertedEvents?.length) {
|
|
969
|
-
out.appendLine(`.on('action', () => startWaitingForEvents())`);
|
|
970
|
-
}
|
|
971
|
-
action();
|
|
972
|
-
out.endBlock();
|
|
973
|
-
}
|
|
974
|
-
#appendClickStep(out, step) {
|
|
975
|
-
this.#appendLocators(out, step, () => {
|
|
976
|
-
out.appendLine('.click({');
|
|
977
|
-
if (step.duration) {
|
|
978
|
-
out.appendLine(` delay: ${step.duration},`);
|
|
979
|
-
}
|
|
980
|
-
if (step.button) {
|
|
981
|
-
out.appendLine(` button: '${mouseButtonMap.get(step.button)}',`);
|
|
982
|
-
}
|
|
983
|
-
out.appendLine(' offset: {');
|
|
984
|
-
out.appendLine(` x: ${step.offsetX},`);
|
|
985
|
-
out.appendLine(` y: ${step.offsetY},`);
|
|
986
|
-
out.appendLine(' },');
|
|
987
|
-
out.appendLine('});');
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
#appendDoubleClickStep(out, step) {
|
|
991
|
-
this.#appendLocators(out, step, () => {
|
|
992
|
-
out.appendLine('.click({');
|
|
993
|
-
out.appendLine(` count: 2,`);
|
|
994
|
-
if (step.duration) {
|
|
995
|
-
out.appendLine(` delay: ${step.duration},`);
|
|
996
|
-
}
|
|
997
|
-
if (step.button) {
|
|
998
|
-
out.appendLine(` button: '${mouseButtonMap.get(step.button)}',`);
|
|
999
|
-
}
|
|
1000
|
-
out.appendLine(' offset: {');
|
|
1001
|
-
out.appendLine(` x: ${step.offsetX},`);
|
|
1002
|
-
out.appendLine(` y: ${step.offsetY},`);
|
|
1003
|
-
out.appendLine(' },');
|
|
1004
|
-
out.appendLine('});');
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
#appendHoverStep(out, step) {
|
|
1008
|
-
this.#appendLocators(out, step, () => {
|
|
1009
|
-
out.appendLine('.hover();');
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
#appendChangeStep(out, step) {
|
|
1013
|
-
this.#appendLocators(out, step, () => {
|
|
1014
|
-
out.appendLine(`.fill(${formatJSONAsJS(step.value, out.getIndent())});`);
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
#appendEmulateNetworkConditionsStep(out, step) {
|
|
1018
|
-
out.appendLine('await targetPage.emulateNetworkConditions({');
|
|
1019
|
-
out.appendLine(` offline: ${!step.download && !step.upload},`);
|
|
1020
|
-
out.appendLine(` downloadThroughput: ${step.download},`);
|
|
1021
|
-
out.appendLine(` uploadThroughput: ${step.upload},`);
|
|
1022
|
-
out.appendLine(` latency: ${step.latency},`);
|
|
1023
|
-
out.appendLine('});');
|
|
1024
|
-
}
|
|
1025
|
-
#appendKeyDownStep(out, step) {
|
|
1026
|
-
out.appendLine(`await targetPage.keyboard.down(${formatJSONAsJS(step.key, out.getIndent())});`);
|
|
1027
|
-
}
|
|
1028
|
-
#appendKeyUpStep(out, step) {
|
|
1029
|
-
out.appendLine(`await targetPage.keyboard.up(${formatJSONAsJS(step.key, out.getIndent())});`);
|
|
1030
|
-
}
|
|
1031
|
-
#appendCloseStep(out, step) {
|
|
1032
|
-
out.appendLine('await targetPage.close()');
|
|
1033
|
-
}
|
|
1034
|
-
#appendViewportStep(out, step) {
|
|
1035
|
-
out.appendLine(`await targetPage.setViewport(${formatJSONAsJS({
|
|
1036
|
-
width: step.width,
|
|
1037
|
-
height: step.height,
|
|
1038
|
-
}, out.getIndent())})`);
|
|
1039
|
-
}
|
|
1040
|
-
#appendScrollStep(out, step) {
|
|
1041
|
-
if ('selectors' in step) {
|
|
1042
|
-
this.#appendLocators(out, step, () => {
|
|
1043
|
-
out.appendLine(`.scroll({ scrollTop: ${step.y}, scrollLeft: ${step.x}});`);
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
else {
|
|
1047
|
-
out.appendLine(`await targetPage.evaluate((x, y) => { window.scroll(x, y); }, ${step.x}, ${step.y})`);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
#appendStepType(out, step) {
|
|
1051
|
-
switch (step.type) {
|
|
1052
|
-
case exports.StepType.Click:
|
|
1053
|
-
return this.#appendClickStep(out, step);
|
|
1054
|
-
case exports.StepType.DoubleClick:
|
|
1055
|
-
return this.#appendDoubleClickStep(out, step);
|
|
1056
|
-
case exports.StepType.Hover:
|
|
1057
|
-
return this.#appendHoverStep(out, step);
|
|
1058
|
-
case exports.StepType.Change:
|
|
1059
|
-
return this.#appendChangeStep(out, step);
|
|
1060
|
-
case exports.StepType.EmulateNetworkConditions:
|
|
1061
|
-
return this.#appendEmulateNetworkConditionsStep(out, step);
|
|
1062
|
-
case exports.StepType.KeyDown:
|
|
1063
|
-
return this.#appendKeyDownStep(out, step);
|
|
1064
|
-
case exports.StepType.KeyUp:
|
|
1065
|
-
return this.#appendKeyUpStep(out, step);
|
|
1066
|
-
case exports.StepType.Close:
|
|
1067
|
-
return this.#appendCloseStep(out, step);
|
|
1068
|
-
case exports.StepType.SetViewport:
|
|
1069
|
-
return this.#appendViewportStep(out, step);
|
|
1070
|
-
case exports.StepType.Scroll:
|
|
1071
|
-
return this.#appendScrollStep(out, step);
|
|
1072
|
-
case exports.StepType.Navigate:
|
|
1073
|
-
return this.#appendNavigationStep(out, step);
|
|
1074
|
-
case exports.StepType.WaitForElement:
|
|
1075
|
-
return this.#appendWaitForElementStep(out, step);
|
|
1076
|
-
case exports.StepType.WaitForExpression:
|
|
1077
|
-
return this.#appendWaitExpressionStep(out, step);
|
|
1078
|
-
case exports.StepType.CustomStep:
|
|
1079
|
-
return; // TODO: implement these
|
|
1080
|
-
default:
|
|
1081
|
-
return assertAllStepTypesAreHandled(step);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
#appendNavigationStep(out, step) {
|
|
1085
|
-
out.appendLine(`await targetPage.goto(${formatJSONAsJS(step.url, out.getIndent())});`);
|
|
1086
|
-
}
|
|
1087
|
-
#appendWaitExpressionStep(out, step) {
|
|
1088
|
-
out.appendLine(`await ${step.frame ? 'frame' : 'targetPage'}.waitForFunction(${formatJSONAsJS(step.expression, out.getIndent())}, { timeout });`);
|
|
1089
|
-
}
|
|
1090
|
-
#appendWaitForElementStep(out, step) {
|
|
1091
|
-
this.#shouldAppendWaitForElementHelper = true;
|
|
1092
|
-
out.appendLine(`await waitForElement(${formatJSONAsJS(step, out.getIndent())}, ${step.frame ? 'frame' : 'targetPage'}, timeout);`);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
const defaultTimeout = 5000;
|
|
1096
|
-
const waitForElementHelper = `async function waitForElement(step, frame, timeout) {
|
|
1097
|
-
const {
|
|
1098
|
-
count = 1,
|
|
1099
|
-
operator = '>=',
|
|
1100
|
-
visible = true,
|
|
1101
|
-
properties,
|
|
1102
|
-
attributes,
|
|
1103
|
-
} = step;
|
|
1104
|
-
const compFn = {
|
|
1105
|
-
'==': (a, b) => a === b,
|
|
1106
|
-
'>=': (a, b) => a >= b,
|
|
1107
|
-
'<=': (a, b) => a <= b,
|
|
1108
|
-
}[operator];
|
|
1109
|
-
await waitForFunction(async () => {
|
|
1110
|
-
const elements = await querySelectorsAll(step.selectors, frame);
|
|
1111
|
-
let result = compFn(elements.length, count);
|
|
1112
|
-
const elementsHandle = await frame.evaluateHandle((...elements) => {
|
|
1113
|
-
return elements;
|
|
1114
|
-
}, ...elements);
|
|
1115
|
-
await Promise.all(elements.map((element) => element.dispose()));
|
|
1116
|
-
if (result && (properties || attributes)) {
|
|
1117
|
-
result = await elementsHandle.evaluate(
|
|
1118
|
-
(elements, properties, attributes) => {
|
|
1119
|
-
for (const element of elements) {
|
|
1120
|
-
if (attributes) {
|
|
1121
|
-
for (const [name, value] of Object.entries(attributes)) {
|
|
1122
|
-
if (element.getAttribute(name) !== value) {
|
|
1123
|
-
return false;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
if (properties) {
|
|
1128
|
-
if (!isDeepMatch(properties, element)) {
|
|
1129
|
-
return false;
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
return true;
|
|
1134
|
-
|
|
1135
|
-
function isDeepMatch(a, b) {
|
|
1136
|
-
if (a === b) {
|
|
1137
|
-
return true;
|
|
1138
|
-
}
|
|
1139
|
-
if ((a && !b) || (!a && b)) {
|
|
1140
|
-
return false;
|
|
1141
|
-
}
|
|
1142
|
-
if (!(a instanceof Object) || !(b instanceof Object)) {
|
|
1143
|
-
return false;
|
|
1144
|
-
}
|
|
1145
|
-
for (const [key, value] of Object.entries(a)) {
|
|
1146
|
-
if (!isDeepMatch(value, b[key])) {
|
|
1147
|
-
return false;
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return true;
|
|
1151
|
-
}
|
|
1152
|
-
},
|
|
1153
|
-
properties,
|
|
1154
|
-
attributes
|
|
1155
|
-
);
|
|
1156
|
-
}
|
|
1157
|
-
await elementsHandle.dispose();
|
|
1158
|
-
return result === visible;
|
|
1159
|
-
}, timeout);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
async function querySelectorsAll(selectors, frame) {
|
|
1163
|
-
for (const selector of selectors) {
|
|
1164
|
-
const result = await querySelectorAll(selector, frame);
|
|
1165
|
-
if (result.length) {
|
|
1166
|
-
return result;
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
return [];
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
async function querySelectorAll(selector, frame) {
|
|
1173
|
-
if (!Array.isArray(selector)) {
|
|
1174
|
-
selector = [selector];
|
|
1175
|
-
}
|
|
1176
|
-
if (!selector.length) {
|
|
1177
|
-
throw new Error('Empty selector provided to querySelectorAll');
|
|
1178
|
-
}
|
|
1179
|
-
let elements = [];
|
|
1180
|
-
for (let i = 0; i < selector.length; i++) {
|
|
1181
|
-
const part = selector[i];
|
|
1182
|
-
if (i === 0) {
|
|
1183
|
-
elements = await frame.$$(part);
|
|
1184
|
-
} else {
|
|
1185
|
-
const tmpElements = elements;
|
|
1186
|
-
elements = [];
|
|
1187
|
-
for (const el of tmpElements) {
|
|
1188
|
-
elements.push(...(await el.$$(part)));
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
if (elements.length === 0) {
|
|
1192
|
-
return [];
|
|
1193
|
-
}
|
|
1194
|
-
if (i < selector.length - 1) {
|
|
1195
|
-
const tmpElements = [];
|
|
1196
|
-
for (const el of elements) {
|
|
1197
|
-
const newEl = (await el.evaluateHandle(el => el.shadowRoot ? el.shadowRoot : el)).asElement();
|
|
1198
|
-
if (newEl) {
|
|
1199
|
-
tmpElements.push(newEl);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
elements = tmpElements;
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
return elements;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
async function waitForFunction(fn, timeout) {
|
|
1209
|
-
let isActive = true;
|
|
1210
|
-
const timeoutId = setTimeout(() => {
|
|
1211
|
-
isActive = false;
|
|
1212
|
-
}, timeout);
|
|
1213
|
-
while (isActive) {
|
|
1214
|
-
const result = await fn();
|
|
1215
|
-
if (result) {
|
|
1216
|
-
clearTimeout(timeoutId);
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1220
|
-
}
|
|
1221
|
-
throw new Error('Timed out');
|
|
1222
|
-
}`;
|
|
1223
|
-
|
|
1224
|
-
/**
|
|
1225
|
-
Copyright 2022 Google LLC
|
|
1226
|
-
|
|
1227
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1228
|
-
you may not use this file except in compliance with the License.
|
|
1229
|
-
You may obtain a copy of the License at
|
|
1230
|
-
|
|
1231
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1232
|
-
|
|
1233
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1234
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1235
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1236
|
-
See the License for the specific language governing permissions and
|
|
1237
|
-
limitations under the License.
|
|
1238
|
-
*/
|
|
1239
|
-
const alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
1240
|
-
const charToIdx = alpha.split('').reduce((acc, char, idx) => {
|
|
1241
|
-
acc.set(char, idx);
|
|
1242
|
-
return acc;
|
|
1243
|
-
}, new Map());
|
|
1244
|
-
const LEAST_5_BIT_MASK = 0b011111;
|
|
1245
|
-
const CONTINUATION_BIT_MASK = 0b100000;
|
|
1246
|
-
const MAX_INT = 2147483647;
|
|
1247
|
-
/**
|
|
1248
|
-
* Encoding variable length integer into base64 (6-bit):
|
|
1249
|
-
*
|
|
1250
|
-
* 1 N N N N N | 0 N N N N N
|
|
1251
|
-
*
|
|
1252
|
-
* The first bit indicates if there is more data for the int.
|
|
1253
|
-
*/
|
|
1254
|
-
function encodeInt(num) {
|
|
1255
|
-
if (num < 0) {
|
|
1256
|
-
throw new Error('Only postive integers and zero are supported');
|
|
1257
|
-
}
|
|
1258
|
-
if (num > MAX_INT) {
|
|
1259
|
-
throw new Error('Only integers between 0 and ' + MAX_INT + ' are supported');
|
|
1260
|
-
}
|
|
1261
|
-
const result = [];
|
|
1262
|
-
do {
|
|
1263
|
-
let payload = num & LEAST_5_BIT_MASK;
|
|
1264
|
-
num >>>= 5;
|
|
1265
|
-
if (num > 0)
|
|
1266
|
-
payload |= CONTINUATION_BIT_MASK;
|
|
1267
|
-
result.push(alpha[payload]);
|
|
1268
|
-
} while (num !== 0);
|
|
1269
|
-
return result.join('');
|
|
1270
|
-
}
|
|
1271
|
-
function encode(nums) {
|
|
1272
|
-
const parts = [];
|
|
1273
|
-
for (const num of nums) {
|
|
1274
|
-
parts.push(encodeInt(num));
|
|
1275
|
-
}
|
|
1276
|
-
return parts.join('');
|
|
1277
|
-
}
|
|
1278
|
-
function decode(str) {
|
|
1279
|
-
const results = [];
|
|
1280
|
-
const chrs = str.split('');
|
|
1281
|
-
let result = 0;
|
|
1282
|
-
let shift = 0;
|
|
1283
|
-
for (const ch of chrs) {
|
|
1284
|
-
const num = charToIdx.get(ch);
|
|
1285
|
-
result |= (num & LEAST_5_BIT_MASK) << shift;
|
|
1286
|
-
shift += 5;
|
|
1287
|
-
const hasMore = num & CONTINUATION_BIT_MASK;
|
|
1288
|
-
if (!hasMore) {
|
|
1289
|
-
results.push(result);
|
|
1290
|
-
result = 0;
|
|
1291
|
-
shift = 0;
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
return results;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
/**
|
|
1298
|
-
Copyright 2022 Google LLC
|
|
1299
|
-
|
|
1300
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1301
|
-
you may not use this file except in compliance with the License.
|
|
1302
|
-
You may obtain a copy of the License at
|
|
1303
|
-
|
|
1304
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1305
|
-
|
|
1306
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1307
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1308
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1309
|
-
See the License for the specific language governing permissions and
|
|
1310
|
-
limitations under the License.
|
|
1311
|
-
*/
|
|
1312
|
-
const SOURCE_MAP_PREFIX = '//# recorderSourceMap=';
|
|
1313
|
-
/**
|
|
1314
|
-
* Stringifes an entire recording. The following hooks are invoked with the `flow` parameter containing the entire flow:
|
|
1315
|
-
* - `beforeAllSteps` (once)
|
|
1316
|
-
* - `beforeEachStep` (per step)
|
|
1317
|
-
* - `stringifyStep` (per step)
|
|
1318
|
-
* - `afterEachStep` (per step)
|
|
1319
|
-
* - `afterAllSteps` (once)
|
|
1320
|
-
*/
|
|
1321
|
-
async function stringify(flow, opts) {
|
|
1322
|
-
if (!opts) {
|
|
1323
|
-
opts = {};
|
|
1324
|
-
}
|
|
1325
|
-
const ext = opts.extension ?? new PuppeteerStringifyExtension();
|
|
1326
|
-
const out = opts.writer ?? new InMemoryLineWriter(opts.indentation ?? ' ');
|
|
1327
|
-
await ext.beforeAllSteps?.(out, flow);
|
|
1328
|
-
const sourceMap = [1]; // The first int indicates the version.
|
|
1329
|
-
for (const step of flow.steps) {
|
|
1330
|
-
const firstLine = out.getSize();
|
|
1331
|
-
await ext.beforeEachStep?.(out, step, flow);
|
|
1332
|
-
await ext.stringifyStep(out, step, flow);
|
|
1333
|
-
await ext.afterEachStep?.(out, step, flow);
|
|
1334
|
-
const lastLine = out.getSize();
|
|
1335
|
-
sourceMap.push(...[firstLine, lastLine - firstLine]);
|
|
1336
|
-
}
|
|
1337
|
-
await ext.afterAllSteps?.(out, flow);
|
|
1338
|
-
out.appendLine(SOURCE_MAP_PREFIX + encode(sourceMap));
|
|
1339
|
-
return out.toString();
|
|
1340
|
-
}
|
|
1341
|
-
/**
|
|
1342
|
-
* Stringifes a single step. Only the following hooks are invoked with the `flow` parameter as undefined:
|
|
1343
|
-
* - `beforeEachStep`
|
|
1344
|
-
* - `stringifyStep`
|
|
1345
|
-
* - `afterEachStep`
|
|
1346
|
-
*/
|
|
1347
|
-
async function stringifyStep(step, opts) {
|
|
1348
|
-
if (!opts) {
|
|
1349
|
-
opts = {};
|
|
1350
|
-
}
|
|
1351
|
-
let ext = opts.extension;
|
|
1352
|
-
if (!ext) {
|
|
1353
|
-
ext = new PuppeteerStringifyExtension();
|
|
1354
|
-
}
|
|
1355
|
-
if (!opts.indentation) {
|
|
1356
|
-
opts.indentation = ' ';
|
|
1357
|
-
}
|
|
1358
|
-
const out = opts.writer ?? new InMemoryLineWriter(opts.indentation ?? ' ');
|
|
1359
|
-
await ext.beforeEachStep?.(out, step);
|
|
1360
|
-
await ext.stringifyStep(out, step);
|
|
1361
|
-
await ext.afterEachStep?.(out, step);
|
|
1362
|
-
return out.toString();
|
|
1363
|
-
}
|
|
1364
|
-
function isSourceMapLine(line) {
|
|
1365
|
-
return line.trim().startsWith(SOURCE_MAP_PREFIX);
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Extracts a source map from a text.
|
|
1369
|
-
*/
|
|
1370
|
-
function parseSourceMap(text) {
|
|
1371
|
-
const lines = text.split('\n');
|
|
1372
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1373
|
-
const line = lines[i];
|
|
1374
|
-
if (isSourceMapLine(line)) {
|
|
1375
|
-
return decode(line.trim().substring(SOURCE_MAP_PREFIX.length));
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
function stripSourceMap(text) {
|
|
1381
|
-
const lines = text.split('\n');
|
|
1382
|
-
return lines.filter((line) => !isSourceMapLine(line)).join('\n');
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
/**
|
|
1386
|
-
Copyright 2022 Google LLC
|
|
1387
|
-
|
|
1388
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1389
|
-
you may not use this file except in compliance with the License.
|
|
1390
|
-
You may obtain a copy of the License at
|
|
1391
|
-
|
|
1392
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1393
|
-
|
|
1394
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1395
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1396
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1397
|
-
See the License for the specific language governing permissions and
|
|
1398
|
-
limitations under the License.
|
|
1399
|
-
*/
|
|
1400
|
-
class RunnerExtension {
|
|
1401
|
-
async beforeAllSteps(flow) { }
|
|
1402
|
-
async afterAllSteps(flow) { }
|
|
1403
|
-
async beforeEachStep(step, flow) { }
|
|
1404
|
-
async runStep(step, flow) { }
|
|
1405
|
-
async afterEachStep(step, flow) { }
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
const comparators = {
|
|
1409
|
-
'==': (a, b) => a === b,
|
|
1410
|
-
'>=': (a, b) => a >= b,
|
|
1411
|
-
'<=': (a, b) => a <= b,
|
|
1412
|
-
};
|
|
1413
|
-
function waitForTimeout(timeout) {
|
|
1414
|
-
return new Promise((resolve) => {
|
|
1415
|
-
setTimeout(resolve, timeout);
|
|
1416
|
-
});
|
|
1417
|
-
}
|
|
1418
|
-
class PuppeteerRunnerExtension extends RunnerExtension {
|
|
1419
|
-
browser;
|
|
1420
|
-
page;
|
|
1421
|
-
timeout;
|
|
1422
|
-
constructor(browser, page, opts) {
|
|
1423
|
-
super();
|
|
1424
|
-
this.browser = browser;
|
|
1425
|
-
this.page = page;
|
|
1426
|
-
this.timeout = opts?.timeout || 5000;
|
|
1427
|
-
}
|
|
1428
|
-
async #ensureAutomationEmulatation(pageOrFrame) {
|
|
1429
|
-
try {
|
|
1430
|
-
await pageOrFrame
|
|
1431
|
-
._client()
|
|
1432
|
-
.send('Emulation.setAutomationOverride', { enabled: true });
|
|
1433
|
-
}
|
|
1434
|
-
catch {
|
|
1435
|
-
// ignore errors as not all versions support this command.
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
#getTimeoutForStep(step, flow) {
|
|
1439
|
-
return step.timeout || flow?.timeout || this.timeout;
|
|
1440
|
-
}
|
|
1441
|
-
async runStep(step, flow) {
|
|
1442
|
-
const timeout = this.#getTimeoutForStep(step, flow);
|
|
1443
|
-
const page = this.page;
|
|
1444
|
-
const browser = this.browser;
|
|
1445
|
-
const targetPage = await getTargetPageForStep(browser, page, step, timeout);
|
|
1446
|
-
let targetFrame = null;
|
|
1447
|
-
if (!targetPage && step.target) {
|
|
1448
|
-
targetFrame = await page.waitForFrame(step.target, { timeout });
|
|
1449
|
-
}
|
|
1450
|
-
const targetPageOrFrame = targetFrame || targetPage;
|
|
1451
|
-
if (!targetPageOrFrame) {
|
|
1452
|
-
throw new Error('Target is not found for step: ' + JSON.stringify(step));
|
|
1453
|
-
}
|
|
1454
|
-
await this.#ensureAutomationEmulatation(targetPageOrFrame);
|
|
1455
|
-
const localFrame = await getFrame(targetPageOrFrame, step);
|
|
1456
|
-
await this.runStepInFrame(step, page, targetPageOrFrame, localFrame, timeout);
|
|
1457
|
-
}
|
|
1458
|
-
/**
|
|
1459
|
-
* @internal
|
|
1460
|
-
*/
|
|
1461
|
-
async runStepInFrame(step, mainPage, targetPageOrFrame, localFrame, timeout) {
|
|
1462
|
-
let assertedEventsPromise = null;
|
|
1463
|
-
const startWaitingForEvents = () => {
|
|
1464
|
-
assertedEventsPromise = waitForEvents(localFrame, step, timeout);
|
|
1465
|
-
};
|
|
1466
|
-
const locatorRace = this.page.locatorRace;
|
|
1467
|
-
switch (step.type) {
|
|
1468
|
-
case exports.StepType.DoubleClick:
|
|
1469
|
-
await locatorRace(step.selectors.map((selector) => {
|
|
1470
|
-
return localFrame.locator(selectorToPElementSelector(selector));
|
|
1471
|
-
}))
|
|
1472
|
-
.setTimeout(timeout)
|
|
1473
|
-
.on('action', () => startWaitingForEvents())
|
|
1474
|
-
.click({
|
|
1475
|
-
count: 2,
|
|
1476
|
-
button: step.button && mouseButtonMap.get(step.button),
|
|
1477
|
-
delay: step.duration,
|
|
1478
|
-
offset: {
|
|
1479
|
-
x: step.offsetX,
|
|
1480
|
-
y: step.offsetY,
|
|
1481
|
-
},
|
|
1482
|
-
});
|
|
1483
|
-
break;
|
|
1484
|
-
case exports.StepType.Click:
|
|
1485
|
-
await locatorRace(step.selectors.map((selector) => {
|
|
1486
|
-
return localFrame.locator(selectorToPElementSelector(selector));
|
|
1487
|
-
}))
|
|
1488
|
-
.setTimeout(timeout)
|
|
1489
|
-
.on('action', () => startWaitingForEvents())
|
|
1490
|
-
.click({
|
|
1491
|
-
delay: step.duration,
|
|
1492
|
-
button: step.button && mouseButtonMap.get(step.button),
|
|
1493
|
-
offset: {
|
|
1494
|
-
x: step.offsetX,
|
|
1495
|
-
y: step.offsetY,
|
|
1496
|
-
},
|
|
1497
|
-
});
|
|
1498
|
-
break;
|
|
1499
|
-
case exports.StepType.Hover:
|
|
1500
|
-
await locatorRace(step.selectors.map((selector) => {
|
|
1501
|
-
return localFrame.locator(selectorToPElementSelector(selector));
|
|
1502
|
-
}))
|
|
1503
|
-
.setTimeout(timeout)
|
|
1504
|
-
.on('action', () => startWaitingForEvents())
|
|
1505
|
-
.hover();
|
|
1506
|
-
break;
|
|
1507
|
-
case exports.StepType.EmulateNetworkConditions:
|
|
1508
|
-
{
|
|
1509
|
-
startWaitingForEvents();
|
|
1510
|
-
const { download, upload, latency } = step;
|
|
1511
|
-
await mainPage.emulateNetworkConditions({
|
|
1512
|
-
offline: !download && !upload,
|
|
1513
|
-
download,
|
|
1514
|
-
upload,
|
|
1515
|
-
latency,
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
break;
|
|
1519
|
-
case exports.StepType.KeyDown:
|
|
1520
|
-
{
|
|
1521
|
-
startWaitingForEvents();
|
|
1522
|
-
await mainPage.keyboard.down(step.key);
|
|
1523
|
-
await waitForTimeout(100);
|
|
1524
|
-
}
|
|
1525
|
-
break;
|
|
1526
|
-
case exports.StepType.KeyUp:
|
|
1527
|
-
{
|
|
1528
|
-
startWaitingForEvents();
|
|
1529
|
-
await mainPage.keyboard.up(step.key);
|
|
1530
|
-
await waitForTimeout(100);
|
|
1531
|
-
}
|
|
1532
|
-
break;
|
|
1533
|
-
case exports.StepType.Close:
|
|
1534
|
-
{
|
|
1535
|
-
if ('close' in targetPageOrFrame) {
|
|
1536
|
-
startWaitingForEvents();
|
|
1537
|
-
await targetPageOrFrame.close();
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
break;
|
|
1541
|
-
case exports.StepType.Change:
|
|
1542
|
-
await locatorRace(step.selectors.map((selector) => {
|
|
1543
|
-
return localFrame.locator(selectorToPElementSelector(selector));
|
|
1544
|
-
}))
|
|
1545
|
-
.on('action', () => startWaitingForEvents())
|
|
1546
|
-
.setTimeout(timeout)
|
|
1547
|
-
.fill(step.value);
|
|
1548
|
-
break;
|
|
1549
|
-
case exports.StepType.SetViewport: {
|
|
1550
|
-
if ('setViewport' in targetPageOrFrame) {
|
|
1551
|
-
startWaitingForEvents();
|
|
1552
|
-
await targetPageOrFrame.setViewport(step);
|
|
1553
|
-
}
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
case exports.StepType.Scroll: {
|
|
1557
|
-
if ('selectors' in step) {
|
|
1558
|
-
await locatorRace(step.selectors.map((selector) => {
|
|
1559
|
-
return localFrame.locator(selectorToPElementSelector(selector));
|
|
1560
|
-
}))
|
|
1561
|
-
.on('action', () => startWaitingForEvents())
|
|
1562
|
-
.setTimeout(timeout)
|
|
1563
|
-
.scroll({
|
|
1564
|
-
scrollLeft: step.x || 0,
|
|
1565
|
-
scrollTop: step.y || 0,
|
|
1566
|
-
});
|
|
1567
|
-
}
|
|
1568
|
-
else {
|
|
1569
|
-
startWaitingForEvents();
|
|
1570
|
-
await localFrame.evaluate((x, y) => {
|
|
1571
|
-
/* c8 ignore start */
|
|
1572
|
-
window.scroll(x, y);
|
|
1573
|
-
/* c8 ignore stop */
|
|
1574
|
-
}, step.x || 0, step.y || 0);
|
|
1575
|
-
}
|
|
1576
|
-
break;
|
|
1577
|
-
}
|
|
1578
|
-
case exports.StepType.Navigate: {
|
|
1579
|
-
startWaitingForEvents();
|
|
1580
|
-
await localFrame.goto(step.url);
|
|
1581
|
-
break;
|
|
1582
|
-
}
|
|
1583
|
-
case exports.StepType.WaitForElement: {
|
|
1584
|
-
try {
|
|
1585
|
-
startWaitingForEvents();
|
|
1586
|
-
await waitForElement(step, localFrame, timeout);
|
|
1587
|
-
}
|
|
1588
|
-
catch (err) {
|
|
1589
|
-
if (err.message === 'Timed out') {
|
|
1590
|
-
throw new Error('waitForElement timed out. The element(s) could not be found.');
|
|
1591
|
-
}
|
|
1592
|
-
else {
|
|
1593
|
-
throw err;
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
break;
|
|
1597
|
-
}
|
|
1598
|
-
case exports.StepType.WaitForExpression: {
|
|
1599
|
-
startWaitingForEvents();
|
|
1600
|
-
await localFrame.waitForFunction(step.expression, {
|
|
1601
|
-
timeout,
|
|
1602
|
-
});
|
|
1603
|
-
break;
|
|
1604
|
-
}
|
|
1605
|
-
case exports.StepType.CustomStep: {
|
|
1606
|
-
// TODO implement these steps
|
|
1607
|
-
break;
|
|
1608
|
-
}
|
|
1609
|
-
default:
|
|
1610
|
-
assertAllStepTypesAreHandled(step);
|
|
1611
|
-
}
|
|
1612
|
-
await assertedEventsPromise;
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
class PuppeteerRunnerOwningBrowserExtension extends PuppeteerRunnerExtension {
|
|
1616
|
-
async afterAllSteps() {
|
|
1617
|
-
await this.browser.close();
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
async function getFrame(pageOrFrame, step) {
|
|
1621
|
-
let frame = 'mainFrame' in pageOrFrame ? pageOrFrame.mainFrame() : pageOrFrame;
|
|
1622
|
-
if ('frame' in step && step.frame) {
|
|
1623
|
-
for (const index of step.frame) {
|
|
1624
|
-
frame = frame.childFrames()[index];
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
return frame;
|
|
1628
|
-
}
|
|
1629
|
-
async function getTargetPageForStep(browser, page, step, timeout) {
|
|
1630
|
-
if (!step.target || step.target === 'main') {
|
|
1631
|
-
return page;
|
|
1632
|
-
}
|
|
1633
|
-
const target = await browser.waitForTarget((t) => t.url() === step.target, {
|
|
1634
|
-
timeout,
|
|
1635
|
-
});
|
|
1636
|
-
const targetPage = await target.page();
|
|
1637
|
-
if (!targetPage) {
|
|
1638
|
-
return null;
|
|
1639
|
-
}
|
|
1640
|
-
targetPage.setDefaultTimeout(timeout);
|
|
1641
|
-
return targetPage;
|
|
1642
|
-
}
|
|
1643
|
-
async function waitForEvents(pageOrFrame, step, timeout) {
|
|
1644
|
-
const promises = [];
|
|
1645
|
-
if (step.assertedEvents) {
|
|
1646
|
-
for (const event of step.assertedEvents) {
|
|
1647
|
-
switch (event.type) {
|
|
1648
|
-
case exports.AssertedEventType.Navigation: {
|
|
1649
|
-
promises.push(pageOrFrame.waitForNavigation({
|
|
1650
|
-
timeout,
|
|
1651
|
-
}));
|
|
1652
|
-
continue;
|
|
1653
|
-
}
|
|
1654
|
-
default:
|
|
1655
|
-
throw new Error(`Event type ${event.type} is not supported`);
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
await Promise.all(promises);
|
|
1660
|
-
}
|
|
1661
|
-
async function waitForElement(step, frame, timeout) {
|
|
1662
|
-
const { count = 1, operator = '>=', visible = true, properties, attributes, } = step;
|
|
1663
|
-
const compFn = comparators[operator];
|
|
1664
|
-
await waitForFunction(async () => {
|
|
1665
|
-
const elements = await querySelectorsAll(step.selectors, frame);
|
|
1666
|
-
let result = compFn(elements.length, count);
|
|
1667
|
-
const elementsHandle = await frame.evaluateHandle((...elements) => {
|
|
1668
|
-
return elements;
|
|
1669
|
-
}, ...elements);
|
|
1670
|
-
await Promise.all(elements.map((element) => element.dispose()));
|
|
1671
|
-
if (result && (properties || attributes)) {
|
|
1672
|
-
result = await elementsHandle.evaluate((elements, properties, attributes) => {
|
|
1673
|
-
if (attributes) {
|
|
1674
|
-
for (const element of elements) {
|
|
1675
|
-
for (const [name, value] of Object.entries(attributes)) {
|
|
1676
|
-
if (element.getAttribute(name) !== value) {
|
|
1677
|
-
return false;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
}
|
|
1682
|
-
if (properties) {
|
|
1683
|
-
for (const element of elements) {
|
|
1684
|
-
if (!isDeepMatch(properties, element)) {
|
|
1685
|
-
return false;
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
return true;
|
|
1690
|
-
function isDeepMatch(a, b) {
|
|
1691
|
-
if (a === b) {
|
|
1692
|
-
return true;
|
|
1693
|
-
}
|
|
1694
|
-
if ((a && !b) || (!a && b)) {
|
|
1695
|
-
return false;
|
|
1696
|
-
}
|
|
1697
|
-
if (!(a instanceof Object) || !(b instanceof Object)) {
|
|
1698
|
-
return false;
|
|
1699
|
-
}
|
|
1700
|
-
for (const [key, value] of Object.entries(a)) {
|
|
1701
|
-
if (!isDeepMatch(value, b[key])) {
|
|
1702
|
-
return false;
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
return true;
|
|
1706
|
-
}
|
|
1707
|
-
}, properties, attributes);
|
|
1708
|
-
}
|
|
1709
|
-
await elementsHandle.dispose();
|
|
1710
|
-
return result === visible;
|
|
1711
|
-
}, timeout);
|
|
1712
|
-
}
|
|
1713
|
-
async function querySelectorsAll(selectors, frame) {
|
|
1714
|
-
for (const selector of selectors) {
|
|
1715
|
-
const result = await querySelectorAll(selector, frame);
|
|
1716
|
-
if (result.length) {
|
|
1717
|
-
return result;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
return [];
|
|
1721
|
-
}
|
|
1722
|
-
async function querySelectorAll(selector, frame) {
|
|
1723
|
-
if (!Array.isArray(selector)) {
|
|
1724
|
-
selector = [selector];
|
|
1725
|
-
}
|
|
1726
|
-
if (!selector.length) {
|
|
1727
|
-
throw new Error('Empty selector provided to querySelectorAll');
|
|
1728
|
-
}
|
|
1729
|
-
let elementHandles = await frame.$$(selector[0]);
|
|
1730
|
-
if (!elementHandles.length) {
|
|
1731
|
-
return [];
|
|
1732
|
-
}
|
|
1733
|
-
for (const part of selector.slice(1, selector.length)) {
|
|
1734
|
-
elementHandles = (await Promise.all(elementHandles.map(async (handle) => {
|
|
1735
|
-
const innerHandle = await handle.evaluateHandle((el) => el.shadowRoot ? el.shadowRoot : el);
|
|
1736
|
-
const elementHandles = await innerHandle.$$(part);
|
|
1737
|
-
innerHandle.dispose();
|
|
1738
|
-
handle.dispose();
|
|
1739
|
-
return elementHandles;
|
|
1740
|
-
}))).flat();
|
|
1741
|
-
if (!elementHandles.length) {
|
|
1742
|
-
return [];
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
return elementHandles;
|
|
1746
|
-
}
|
|
1747
|
-
async function waitForFunction(fn, timeout) {
|
|
1748
|
-
let isActive = true;
|
|
1749
|
-
const timeoutId = setTimeout(() => {
|
|
1750
|
-
isActive = false;
|
|
1751
|
-
}, timeout);
|
|
1752
|
-
while (isActive) {
|
|
1753
|
-
const result = await fn();
|
|
1754
|
-
if (result) {
|
|
1755
|
-
clearTimeout(timeoutId);
|
|
1756
|
-
return;
|
|
1757
|
-
}
|
|
1758
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1759
|
-
}
|
|
1760
|
-
throw new Error('Timed out');
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
/**
|
|
1764
|
-
Copyright 2022 Google LLC
|
|
1765
|
-
|
|
1766
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1767
|
-
you may not use this file except in compliance with the License.
|
|
1768
|
-
You may obtain a copy of the License at
|
|
1769
|
-
|
|
1770
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1771
|
-
|
|
1772
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1773
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1774
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1775
|
-
See the License for the specific language governing permissions and
|
|
1776
|
-
limitations under the License.
|
|
1777
|
-
*/
|
|
1778
|
-
async function _runStepWithHooks(extension, step, flow) {
|
|
1779
|
-
await extension.beforeEachStep?.(step, flow);
|
|
1780
|
-
await extension.runStep(step, flow);
|
|
1781
|
-
await extension.afterEachStep?.(step, flow);
|
|
1782
|
-
}
|
|
1783
|
-
class Runner {
|
|
1784
|
-
#flow;
|
|
1785
|
-
#extension;
|
|
1786
|
-
#aborted = false;
|
|
1787
|
-
/**
|
|
1788
|
-
* @internal
|
|
1789
|
-
*/
|
|
1790
|
-
constructor(extension) {
|
|
1791
|
-
this.#extension = extension;
|
|
1792
|
-
}
|
|
1793
|
-
abort() {
|
|
1794
|
-
this.#aborted = true;
|
|
1795
|
-
}
|
|
1796
|
-
set flow(flow) {
|
|
1797
|
-
this.#flow = flow;
|
|
1798
|
-
}
|
|
1799
|
-
async runBeforeAllSteps(flow) {
|
|
1800
|
-
await this.#extension.beforeAllSteps?.(flow);
|
|
1801
|
-
}
|
|
1802
|
-
async runAfterAllSteps(flow) {
|
|
1803
|
-
await this.#extension.afterAllSteps?.(flow);
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
* Runs the provided `step` with `beforeEachStep` and `afterEachStep` hooks.
|
|
1807
|
-
* Parameters from the `flow` apply if the `flow` is set.
|
|
1808
|
-
*/
|
|
1809
|
-
async runStep(step) {
|
|
1810
|
-
await _runStepWithHooks(this.#extension, step);
|
|
1811
|
-
}
|
|
1812
|
-
/**
|
|
1813
|
-
* Run all the steps in the flow
|
|
1814
|
-
* @returns whether all the steps are run or the execution is aborted
|
|
1815
|
-
*/
|
|
1816
|
-
async run() {
|
|
1817
|
-
if (!this.#flow) {
|
|
1818
|
-
throw new Error('Set the flow on the runner instance before calling `run`.');
|
|
1819
|
-
}
|
|
1820
|
-
const flow = this.#flow;
|
|
1821
|
-
this.#aborted = false;
|
|
1822
|
-
await this.#extension.beforeAllSteps?.(flow);
|
|
1823
|
-
if (this.#aborted) {
|
|
1824
|
-
return false;
|
|
1825
|
-
}
|
|
1826
|
-
for (const step of flow.steps) {
|
|
1827
|
-
if (this.#aborted) {
|
|
1828
|
-
await this.#extension.afterAllSteps?.(flow);
|
|
1829
|
-
return false;
|
|
1830
|
-
}
|
|
1831
|
-
await _runStepWithHooks(this.#extension, step, flow);
|
|
1832
|
-
}
|
|
1833
|
-
await this.#extension.afterAllSteps?.(flow);
|
|
1834
|
-
return true;
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
async function createRunner(flowOrExtension, maybeExtension) {
|
|
1838
|
-
const extension = flowOrExtension instanceof RunnerExtension
|
|
1839
|
-
? flowOrExtension
|
|
1840
|
-
: maybeExtension;
|
|
1841
|
-
const flow = !(flowOrExtension instanceof RunnerExtension)
|
|
1842
|
-
? flowOrExtension
|
|
1843
|
-
: undefined;
|
|
1844
|
-
const runner = new Runner(extension ?? (await createPuppeteerRunnerOwningBrowserExtension()));
|
|
1845
|
-
if (flow) {
|
|
1846
|
-
runner.flow = flow;
|
|
1847
|
-
}
|
|
1848
|
-
return runner;
|
|
1849
|
-
}
|
|
1850
|
-
async function createPuppeteerRunnerOwningBrowserExtension() {
|
|
1851
|
-
const { default: puppeteer } = await import('puppeteer');
|
|
1852
|
-
const browser = await puppeteer.launch();
|
|
1853
|
-
const page = await browser.newPage();
|
|
1854
|
-
return new PuppeteerRunnerOwningBrowserExtension(browser, page);
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
/**
|
|
1858
|
-
Copyright 2022 Google LLC
|
|
1859
|
-
|
|
1860
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1861
|
-
you may not use this file except in compliance with the License.
|
|
1862
|
-
You may obtain a copy of the License at
|
|
1863
|
-
|
|
1864
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1865
|
-
|
|
1866
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1867
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1868
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1869
|
-
See the License for the specific language governing permissions and
|
|
1870
|
-
limitations under the License.
|
|
1871
|
-
*/
|
|
1872
|
-
/**
|
|
1873
|
-
* Stringifies a user flow to a script that uses \@puppeteer/replay's own API.
|
|
1874
|
-
*/
|
|
1875
|
-
class PuppeteerReplayStringifyExtension extends StringifyExtension {
|
|
1876
|
-
async beforeAllSteps(out) {
|
|
1877
|
-
out.appendLine("import url from 'url';");
|
|
1878
|
-
out.appendLine("import { createRunner } from '@puppeteer/replay';");
|
|
1879
|
-
out.appendLine('');
|
|
1880
|
-
out.appendLine('export async function run(extension) {').startBlock();
|
|
1881
|
-
out.appendLine('const runner = await createRunner(extension);');
|
|
1882
|
-
out.appendLine('');
|
|
1883
|
-
out.appendLine('await runner.runBeforeAllSteps();');
|
|
1884
|
-
out.appendLine('');
|
|
1885
|
-
}
|
|
1886
|
-
async afterAllSteps(out) {
|
|
1887
|
-
out.appendLine('');
|
|
1888
|
-
out
|
|
1889
|
-
.appendLine('await runner.runAfterAllSteps();')
|
|
1890
|
-
.endBlock()
|
|
1891
|
-
.appendLine('}');
|
|
1892
|
-
out.appendLine('');
|
|
1893
|
-
out
|
|
1894
|
-
.appendLine('if (process && import.meta.url === url.pathToFileURL(process.argv[1]).href) {')
|
|
1895
|
-
.startBlock()
|
|
1896
|
-
.appendLine('run()')
|
|
1897
|
-
.endBlock()
|
|
1898
|
-
.appendLine('}');
|
|
1899
|
-
}
|
|
1900
|
-
async stringifyStep(out, step) {
|
|
1901
|
-
out.appendLine(`await runner.runStep(${formatJSONAsJS(step, out.getIndent())});`);
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
/**
|
|
1906
|
-
Copyright 2022 Google LLC
|
|
1907
|
-
|
|
1908
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1909
|
-
you may not use this file except in compliance with the License.
|
|
1910
|
-
You may obtain a copy of the License at
|
|
1911
|
-
|
|
1912
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1913
|
-
|
|
1914
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1915
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1916
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1917
|
-
See the License for the specific language governing permissions and
|
|
1918
|
-
limitations under the License.
|
|
1919
|
-
*/
|
|
1920
|
-
function isNavigationStep(step) {
|
|
1921
|
-
return Boolean(step.type === exports.StepType.Navigate ||
|
|
1922
|
-
step.assertedEvents?.some((event) => event.type === exports.AssertedEventType.Navigation));
|
|
1923
|
-
}
|
|
1924
|
-
function isMobileFlow(flow) {
|
|
1925
|
-
for (const step of flow.steps) {
|
|
1926
|
-
if (step.type === exports.StepType.SetViewport) {
|
|
1927
|
-
return step.isMobile;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
return false;
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
/**
|
|
1934
|
-
Copyright 2022 Google LLC
|
|
1935
|
-
|
|
1936
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
1937
|
-
you may not use this file except in compliance with the License.
|
|
1938
|
-
You may obtain a copy of the License at
|
|
1939
|
-
|
|
1940
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
1941
|
-
|
|
1942
|
-
Unless required by applicable law or agreed to in writing, software
|
|
1943
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
1944
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1945
|
-
See the License for the specific language governing permissions and
|
|
1946
|
-
limitations under the License.
|
|
1947
|
-
*/
|
|
1948
|
-
class LighthouseStringifyExtension extends PuppeteerStringifyExtension {
|
|
1949
|
-
#isProcessingTimespan = false;
|
|
1950
|
-
async beforeAllSteps(out, flow) {
|
|
1951
|
-
out.appendLine(`const fs = require('fs');`);
|
|
1952
|
-
await super.beforeAllSteps(out, flow);
|
|
1953
|
-
out.appendLine(`const lhApi = await import('lighthouse'); // v10.0.0 or later`);
|
|
1954
|
-
const flags = {
|
|
1955
|
-
screenEmulation: {
|
|
1956
|
-
disabled: true,
|
|
1957
|
-
},
|
|
1958
|
-
};
|
|
1959
|
-
out.appendLine(`const flags = ${formatJSONAsJS(flags, out.getIndent())}`);
|
|
1960
|
-
if (isMobileFlow(flow)) {
|
|
1961
|
-
out.appendLine(`const config = undefined;`);
|
|
1962
|
-
}
|
|
1963
|
-
else {
|
|
1964
|
-
out.appendLine('const config = lhApi.desktopConfig;');
|
|
1965
|
-
}
|
|
1966
|
-
out.appendLine(`const lhFlow = await lhApi.startFlow(page, {name: ${formatJSONAsJS(flow.title, out.getIndent())}, config, flags});`);
|
|
1967
|
-
}
|
|
1968
|
-
async stringifyStep(out, step, flow) {
|
|
1969
|
-
if (step.type === exports.StepType.SetViewport) {
|
|
1970
|
-
await super.stringifyStep(out, step, flow);
|
|
1971
|
-
return;
|
|
1972
|
-
}
|
|
1973
|
-
const isNavigation = isNavigationStep(step);
|
|
1974
|
-
if (isNavigation) {
|
|
1975
|
-
if (this.#isProcessingTimespan) {
|
|
1976
|
-
out.appendLine(`await lhFlow.endTimespan();`);
|
|
1977
|
-
this.#isProcessingTimespan = false;
|
|
1978
|
-
}
|
|
1979
|
-
out.appendLine(`await lhFlow.startNavigation();`);
|
|
1980
|
-
}
|
|
1981
|
-
else if (!this.#isProcessingTimespan) {
|
|
1982
|
-
out.appendLine(`await lhFlow.startTimespan();`);
|
|
1983
|
-
this.#isProcessingTimespan = true;
|
|
1984
|
-
}
|
|
1985
|
-
await super.stringifyStep(out, step, flow);
|
|
1986
|
-
if (isNavigation) {
|
|
1987
|
-
out.appendLine(`await lhFlow.endNavigation();`);
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
async afterAllSteps(out, flow) {
|
|
1991
|
-
if (this.#isProcessingTimespan) {
|
|
1992
|
-
out.appendLine(`await lhFlow.endTimespan();`);
|
|
1993
|
-
}
|
|
1994
|
-
out.appendLine(`const lhFlowReport = await lhFlow.generateReport();`);
|
|
1995
|
-
out.appendLine(`fs.writeFileSync(__dirname + '/flow.report.html', lhFlowReport)`);
|
|
1996
|
-
await super.afterAllSteps(out, flow);
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
/**
|
|
2001
|
-
Copyright 2022 Google LLC
|
|
2002
|
-
|
|
2003
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
2004
|
-
you may not use this file except in compliance with the License.
|
|
2005
|
-
You may obtain a copy of the License at
|
|
2006
|
-
|
|
2007
|
-
https://www.apache.org/licenses/LICENSE-2.0
|
|
2008
|
-
|
|
2009
|
-
Unless required by applicable law or agreed to in writing, software
|
|
2010
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
2011
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2012
|
-
See the License for the specific language governing permissions and
|
|
2013
|
-
limitations under the License.
|
|
2014
|
-
*/
|
|
2015
|
-
class LighthouseRunnerExtension extends PuppeteerRunnerExtension {
|
|
2016
|
-
#isTimespanRunning = false;
|
|
2017
|
-
#isNavigationRunning = false;
|
|
2018
|
-
#lhFlow;
|
|
2019
|
-
async createFlowResult() {
|
|
2020
|
-
if (!this.#lhFlow) {
|
|
2021
|
-
throw new Error('Cannot get flow result before running the flow');
|
|
2022
|
-
}
|
|
2023
|
-
return this.#lhFlow.createFlowResult();
|
|
2024
|
-
}
|
|
2025
|
-
async beforeAllSteps(flow) {
|
|
2026
|
-
await super.beforeAllSteps?.(flow);
|
|
2027
|
-
const { startFlow, desktopConfig } = await import('lighthouse');
|
|
2028
|
-
let config = undefined;
|
|
2029
|
-
if (!isMobileFlow(flow)) {
|
|
2030
|
-
config = desktopConfig;
|
|
2031
|
-
}
|
|
2032
|
-
this.#lhFlow = await startFlow(this.page, {
|
|
2033
|
-
config,
|
|
2034
|
-
flags: { screenEmulation: { disabled: true } },
|
|
2035
|
-
name: flow.title,
|
|
2036
|
-
});
|
|
2037
|
-
}
|
|
2038
|
-
async beforeEachStep(step, flow) {
|
|
2039
|
-
await super.beforeEachStep?.(step, flow);
|
|
2040
|
-
if (step.type === exports.StepType.SetViewport)
|
|
2041
|
-
return;
|
|
2042
|
-
if (isNavigationStep(step)) {
|
|
2043
|
-
if (this.#isTimespanRunning) {
|
|
2044
|
-
await this.#lhFlow.endTimespan();
|
|
2045
|
-
this.#isTimespanRunning = false;
|
|
2046
|
-
}
|
|
2047
|
-
await this.#lhFlow.startNavigation();
|
|
2048
|
-
this.#isNavigationRunning = true;
|
|
2049
|
-
}
|
|
2050
|
-
else if (!this.#isTimespanRunning) {
|
|
2051
|
-
await this.#lhFlow.startTimespan();
|
|
2052
|
-
this.#isTimespanRunning = true;
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
async afterEachStep(step, flow) {
|
|
2056
|
-
if (this.#isNavigationRunning) {
|
|
2057
|
-
await this.#lhFlow.endNavigation();
|
|
2058
|
-
this.#isNavigationRunning = false;
|
|
2059
|
-
}
|
|
2060
|
-
await super.afterEachStep?.(step, flow);
|
|
2061
|
-
}
|
|
2062
|
-
async afterAllSteps(flow) {
|
|
2063
|
-
if (this.#isTimespanRunning) {
|
|
2064
|
-
await this.#lhFlow.endTimespan();
|
|
2065
|
-
}
|
|
2066
|
-
await super.afterAllSteps?.(flow);
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2069
|
-
|
|
2070
|
-
exports.JSONStringifyExtension = JSONStringifyExtension;
|
|
2071
|
-
exports.LighthouseRunnerExtension = LighthouseRunnerExtension;
|
|
2072
|
-
exports.LighthouseStringifyExtension = LighthouseStringifyExtension;
|
|
2073
|
-
exports.PuppeteerReplayStringifyExtension = PuppeteerReplayStringifyExtension;
|
|
2074
|
-
exports.PuppeteerRunnerExtension = PuppeteerRunnerExtension;
|
|
2075
|
-
exports.PuppeteerRunnerOwningBrowserExtension = PuppeteerRunnerOwningBrowserExtension;
|
|
2076
|
-
exports.PuppeteerStringifyExtension = PuppeteerStringifyExtension;
|
|
2077
|
-
exports.Runner = Runner;
|
|
2078
|
-
exports.RunnerExtension = RunnerExtension;
|
|
2079
|
-
exports.Schema = Schema;
|
|
2080
|
-
exports.StringifyExtension = StringifyExtension;
|
|
2081
|
-
exports.assertAllStepTypesAreHandled = assertAllStepTypesAreHandled;
|
|
2082
|
-
exports.createRunner = createRunner;
|
|
2083
|
-
exports.formatAsJSLiteral = formatAsJSLiteral;
|
|
2084
|
-
exports.formatJSONAsJS = formatJSONAsJS;
|
|
2085
|
-
exports.getSelectorType = getSelectorType;
|
|
2086
|
-
exports.maxTimeout = maxTimeout;
|
|
2087
|
-
exports.minTimeout = minTimeout;
|
|
2088
|
-
exports.mouseButtonMap = mouseButtonMap;
|
|
2089
|
-
exports.parse = parse;
|
|
2090
|
-
exports.parseSourceMap = parseSourceMap;
|
|
2091
|
-
exports.parseStep = parseStep;
|
|
2092
|
-
exports.pointerDeviceTypes = pointerDeviceTypes;
|
|
2093
|
-
exports.selectorToPElementSelector = selectorToPElementSelector;
|
|
2094
|
-
exports.stringify = stringify;
|
|
2095
|
-
exports.stringifyStep = stringifyStep;
|
|
2096
|
-
exports.stripSourceMap = stripSourceMap;
|
|
2097
|
-
exports.typeableInputTypes = typeableInputTypes;
|
|
2098
|
-
exports.validTimeout = validTimeout;
|
|
2099
|
-
//# sourceMappingURL=main.cjs.map
|