patchright-core 1.49.2 → 1.50.0
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/ThirdPartyNotices.txt +9 -9
- package/bin/reinstall_msedge_beta_linux.sh +6 -0
- package/bin/reinstall_msedge_dev_linux.sh +6 -0
- package/bin/reinstall_msedge_stable_linux.sh +6 -0
- package/browsers.json +17 -16
- package/lib/androidServerImpl.js +1 -1
- package/lib/cli/program.js +6 -30
- package/lib/client/channelOwner.js +35 -55
- package/lib/client/clientInstrumentation.js +2 -0
- package/lib/client/connection.js +3 -3
- package/lib/client/network.js +3 -1
- package/lib/client/waiter.js +1 -1
- package/lib/generated/consoleApiSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +1 -1
- package/lib/inProcessFactory.js +2 -0
- package/lib/protocol/debug.js +1 -1
- package/lib/protocol/validator.js +2 -2
- package/lib/remote/playwrightConnection.js +4 -3
- package/lib/remote/playwrightServer.js +2 -1
- package/lib/server/bidi/bidiBrowser.js +9 -6
- package/lib/server/bidi/bidiExecutionContext.js +20 -1
- package/lib/server/bidi/bidiInput.js +7 -5
- package/lib/server/bidi/bidiNetworkManager.js +8 -9
- package/lib/server/bidi/bidiPage.js +9 -20
- package/lib/server/bidi/third_party/bidiKeyboard.js +9 -7
- package/lib/server/browserContext.js +24 -16
- package/lib/server/chromium/crBrowser.js +10 -10
- package/lib/server/chromium/crExecutionContext.js +1 -5
- package/lib/server/chromium/crInput.js +15 -4
- package/lib/server/chromium/crPage.js +17 -31
- package/lib/server/codegen/csharp.js +12 -2
- package/lib/server/codegen/java.js +14 -3
- package/lib/server/codegen/javascript.js +10 -2
- package/lib/server/codegen/jsonl.js +1 -1
- package/lib/server/codegen/python.js +5 -4
- package/lib/server/debugController.js +15 -40
- package/lib/server/debugger.js +1 -1
- package/lib/server/deviceDescriptorsSource.json +50 -50
- package/lib/server/dispatchers/browserContextDispatcher.js +2 -13
- package/lib/server/dispatchers/debugControllerDispatcher.js +4 -2
- package/lib/server/dispatchers/frameDispatcher.js +3 -2
- package/lib/server/dispatchers/pageDispatcher.js +1 -1
- package/lib/server/dispatchers/webSocketRouteDispatcher.js +10 -11
- package/lib/server/dom.js +7 -2
- package/lib/server/firefox/ffBrowser.js +6 -6
- package/lib/server/firefox/ffInput.js +15 -4
- package/lib/server/firefox/ffPage.js +13 -28
- package/lib/server/frames.js +25 -30
- package/lib/server/har/harTracer.js +1 -1
- package/lib/server/input.js +2 -3
- package/lib/server/network.js +2 -2
- package/lib/server/page.js +23 -16
- package/lib/server/recorder/chat.js +177 -0
- package/lib/server/recorder/contextRecorder.js +6 -15
- package/lib/server/recorder/recorderApp.js +1 -1
- package/lib/server/recorder/recorderCollection.js +4 -16
- package/lib/server/recorder/recorderRunner.js +7 -3
- package/lib/server/recorder/recorderUtils.js +5 -29
- package/lib/server/recorder.js +12 -9
- package/lib/server/registry/browserFetcher.js +1 -1
- package/lib/server/registry/dependencies.js +5 -5
- package/lib/server/registry/index.js +118 -5
- package/lib/server/registry/nativeDeps.js +7 -4
- package/lib/server/trace/recorder/snapshotterInjected.js +12 -5
- package/lib/server/trace/viewer/traceViewer.js +6 -1
- package/lib/server/transport.js +1 -0
- package/lib/server/webkit/webkit.js +1 -1
- package/lib/server/webkit/wkBrowser.js +6 -6
- package/lib/server/webkit/wkExecutionContext.js +1 -0
- package/lib/server/webkit/wkInput.js +15 -5
- package/lib/server/webkit/wkPage.js +7 -25
- package/lib/utils/comparators.js +16 -10
- package/lib/utils/debugLogger.js +3 -1
- package/lib/utils/hostPlatform.js +14 -8
- package/lib/utils/isomorphic/ariaSnapshot.js +176 -52
- package/lib/utils/isomorphic/cssParser.js +4 -4
- package/lib/utils/isomorphic/locatorGenerators.js +2 -2
- package/lib/utils/isomorphic/locatorParser.js +18 -12
- package/lib/utils/isomorphic/urlMatch.js +2 -4
- package/lib/utils/processLauncher.js +1 -1
- package/lib/utils/wsServer.js +1 -0
- package/lib/utils/zones.js +18 -20
- package/lib/utilsBundleImpl/index.js +95 -95
- package/lib/vite/htmlReport/index.html +14 -14
- package/lib/vite/{traceViewer/assets/codeMirrorModule-VZNWuWvU.js → recorder/assets/codeMirrorModule-3Qn3tPnZ.js} +1 -1
- package/lib/vite/recorder/assets/{index-CqeZmzx8.js → index-Bek6JFv8.js} +78 -78
- package/lib/vite/recorder/assets/{index-iA1aAGZg.css → index-CAQewHss.css} +1 -1
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/{recorder/assets/codeMirrorModule-DUzBrnvO.js → traceViewer/assets/codeMirrorModule-aLkSUGpW.js} +1 -1
- package/lib/vite/traceViewer/assets/defaultSettingsView-CxUo6zd3.js +243 -0
- package/lib/vite/traceViewer/defaultSettingsView.DtIkrKWn.css +1 -0
- package/lib/vite/traceViewer/index.Bhu5cv5R.js +2 -0
- package/lib/vite/traceViewer/index.html +3 -6
- package/lib/vite/traceViewer/sw.bundle.js +3 -3
- package/lib/vite/traceViewer/uiMode.BBy7FOVd.js +5 -0
- package/lib/vite/traceViewer/{uiMode.voC1ZiOQ.css → uiMode.Be_ME-Go.css} +1 -1
- package/lib/vite/traceViewer/uiMode.html +4 -7
- package/package.json +1 -1
- package/types/protocol.d.ts +269 -20
- package/types/types.d.ts +44 -23
- package/bin/PrintDeps.exe +0 -0
- package/bin/README.md +0 -2
- package/lib/server/ariaSnapshot.js +0 -33
- package/lib/server/recorder/recorderInTraceViewer.js +0 -144
- package/lib/utils/isomorphic/recorderUtils.js +0 -227
- package/lib/vite/traceViewer/assets/inspectorTab-BV-Uf3j9.js +0 -68
- package/lib/vite/traceViewer/assets/testServerConnection-DeE2kSzz.js +0 -1
- package/lib/vite/traceViewer/assets/workbench-B4WPcYi9.js +0 -9
- package/lib/vite/traceViewer/embedded.BLPSqdbm.js +0 -2
- package/lib/vite/traceViewer/embedded.html +0 -18
- package/lib/vite/traceViewer/embedded.w7WN2u1R.css +0 -1
- package/lib/vite/traceViewer/index.BGZfFXXF.js +0 -2
- package/lib/vite/traceViewer/inspectorTab.DEOUW62d.css +0 -1
- package/lib/vite/traceViewer/recorder.B_SY1GJM.css +0 -0
- package/lib/vite/traceViewer/recorder.eWs2vuTG.js +0 -2
- package/lib/vite/traceViewer/recorder.html +0 -17
- package/lib/vite/traceViewer/uiMode.CW2d9h0S.js +0 -5
- package/lib/vite/traceViewer/workbench.C-zR9ysA.css +0 -1
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
6
|
+
exports.ParserError = exports.KeyParser = void 0;
|
|
7
|
+
exports.parseAriaSnapshot = parseAriaSnapshot;
|
|
8
|
+
exports.parseAriaSnapshotUnsafe = parseAriaSnapshotUnsafe;
|
|
9
|
+
exports.valueOrRegex = valueOrRegex;
|
|
9
10
|
/**
|
|
10
11
|
* Copyright (c) Microsoft Corporation.
|
|
11
12
|
*
|
|
@@ -24,65 +25,191 @@ exports.parseYamlTemplate = parseYamlTemplate;
|
|
|
24
25
|
|
|
25
26
|
// https://www.w3.org/TR/wai-aria-1.2/#role_definitions
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (result.children && result.children.length === 1) return result.children[0];
|
|
34
|
-
return result;
|
|
28
|
+
// We pass parsed template between worlds using JSON, make it easy.
|
|
29
|
+
|
|
30
|
+
function parseAriaSnapshotUnsafe(yaml, text) {
|
|
31
|
+
const result = parseAriaSnapshot(yaml, text);
|
|
32
|
+
if (result.errors.length) throw new Error(result.errors[0].message);
|
|
33
|
+
return result.fragment;
|
|
35
34
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
function parseAriaSnapshot(yaml, text, options = {}) {
|
|
36
|
+
var _fragment$children;
|
|
37
|
+
const lineCounter = new yaml.LineCounter();
|
|
38
|
+
const parseOptions = {
|
|
39
|
+
keepSourceTokens: true,
|
|
40
|
+
lineCounter,
|
|
41
|
+
...options
|
|
42
|
+
};
|
|
43
|
+
const yamlDoc = yaml.parseDocument(text, parseOptions);
|
|
44
|
+
const errors = [];
|
|
45
|
+
const convertRange = range => {
|
|
46
|
+
return [lineCounter.linePos(range[0]), lineCounter.linePos(range[1])];
|
|
47
|
+
};
|
|
48
|
+
const addError = error => {
|
|
49
|
+
errors.push({
|
|
50
|
+
message: error.message,
|
|
51
|
+
range: [lineCounter.linePos(error.pos[0]), lineCounter.linePos(error.pos[1])]
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const convertSeq = (container, seq) => {
|
|
55
|
+
for (const item of seq.items) {
|
|
56
|
+
const itemIsString = item instanceof yaml.Scalar && typeof item.value === 'string';
|
|
57
|
+
if (itemIsString) {
|
|
58
|
+
const childNode = KeyParser.parse(item, parseOptions, errors);
|
|
59
|
+
if (childNode) {
|
|
60
|
+
container.children = container.children || [];
|
|
61
|
+
container.children.push(childNode);
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const itemIsMap = item instanceof yaml.YAMLMap;
|
|
66
|
+
if (itemIsMap) {
|
|
67
|
+
convertMap(container, item);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
errors.push({
|
|
71
|
+
message: 'Sequence items should be strings or maps',
|
|
72
|
+
range: convertRange(item.range || seq.range)
|
|
73
|
+
});
|
|
43
74
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
75
|
+
};
|
|
76
|
+
const convertMap = (container, map) => {
|
|
77
|
+
for (const entry of map.items) {
|
|
78
|
+
container.children = container.children || [];
|
|
79
|
+
// Key must by a string
|
|
80
|
+
const keyIsString = entry.key instanceof yaml.Scalar && typeof entry.key.value === 'string';
|
|
81
|
+
if (!keyIsString) {
|
|
82
|
+
errors.push({
|
|
83
|
+
message: 'Only string keys are supported',
|
|
84
|
+
range: convertRange(entry.key.range || map.range)
|
|
51
85
|
});
|
|
52
86
|
continue;
|
|
53
87
|
}
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
88
|
+
const key = entry.key;
|
|
89
|
+
const value = entry.value;
|
|
90
|
+
|
|
91
|
+
// - text: "text"
|
|
92
|
+
if (key.value === 'text') {
|
|
93
|
+
const valueIsString = value instanceof yaml.Scalar && typeof value.value === 'string';
|
|
94
|
+
if (!valueIsString) {
|
|
95
|
+
errors.push({
|
|
96
|
+
message: 'Text value should be a string',
|
|
97
|
+
range: convertRange(entry.value.range || map.range)
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
container.children.push({
|
|
57
102
|
kind: 'text',
|
|
58
|
-
text: valueOrRegex(value)
|
|
103
|
+
text: valueOrRegex(value.value)
|
|
59
104
|
});
|
|
60
105
|
continue;
|
|
61
106
|
}
|
|
62
|
-
|
|
63
|
-
|
|
107
|
+
|
|
108
|
+
// role "name": ...
|
|
109
|
+
const childNode = KeyParser.parse(key, parseOptions, errors);
|
|
110
|
+
if (!childNode) continue;
|
|
111
|
+
|
|
112
|
+
// - role "name": "text"
|
|
113
|
+
const valueIsScalar = value instanceof yaml.Scalar;
|
|
114
|
+
if (valueIsScalar) {
|
|
115
|
+
const type = typeof value.value;
|
|
116
|
+
if (type !== 'string' && type !== 'number' && type !== 'boolean') {
|
|
117
|
+
errors.push({
|
|
118
|
+
message: 'Node value should be a string or a sequence',
|
|
119
|
+
range: convertRange(entry.value.range || map.range)
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
container.children.push({
|
|
64
124
|
...childNode,
|
|
65
125
|
children: [{
|
|
66
126
|
kind: 'text',
|
|
67
|
-
text: valueOrRegex(value)
|
|
127
|
+
text: valueOrRegex(String(value.value))
|
|
68
128
|
}]
|
|
69
129
|
});
|
|
70
130
|
continue;
|
|
71
131
|
}
|
|
72
|
-
|
|
73
|
-
|
|
132
|
+
|
|
133
|
+
// - role "name":
|
|
134
|
+
// - child
|
|
135
|
+
const valueIsSequence = value instanceof yaml.YAMLSeq;
|
|
136
|
+
if (valueIsSequence) {
|
|
137
|
+
container.children.push(childNode);
|
|
138
|
+
convertSeq(childNode, value);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
errors.push({
|
|
142
|
+
message: 'Map values should be strings or sequences',
|
|
143
|
+
range: convertRange(entry.value.range || map.range)
|
|
144
|
+
});
|
|
74
145
|
}
|
|
146
|
+
};
|
|
147
|
+
const fragment = {
|
|
148
|
+
kind: 'role',
|
|
149
|
+
role: 'fragment'
|
|
150
|
+
};
|
|
151
|
+
yamlDoc.errors.forEach(addError);
|
|
152
|
+
if (errors.length) return {
|
|
153
|
+
errors,
|
|
154
|
+
fragment
|
|
155
|
+
};
|
|
156
|
+
if (!(yamlDoc.contents instanceof yaml.YAMLSeq)) {
|
|
157
|
+
errors.push({
|
|
158
|
+
message: 'Aria snapshot must be a YAML sequence, elements starting with " -"',
|
|
159
|
+
range: yamlDoc.contents ? convertRange(yamlDoc.contents.range) : [{
|
|
160
|
+
line: 0,
|
|
161
|
+
col: 0
|
|
162
|
+
}, {
|
|
163
|
+
line: 0,
|
|
164
|
+
col: 0
|
|
165
|
+
}]
|
|
166
|
+
});
|
|
75
167
|
}
|
|
168
|
+
if (errors.length) return {
|
|
169
|
+
errors,
|
|
170
|
+
fragment
|
|
171
|
+
};
|
|
172
|
+
convertSeq(fragment, yamlDoc.contents);
|
|
173
|
+
if (errors.length) return {
|
|
174
|
+
errors,
|
|
175
|
+
fragment: emptyFragment
|
|
176
|
+
};
|
|
177
|
+
if (((_fragment$children = fragment.children) === null || _fragment$children === void 0 ? void 0 : _fragment$children.length) === 1) return {
|
|
178
|
+
fragment: fragment.children[0],
|
|
179
|
+
errors
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
fragment,
|
|
183
|
+
errors
|
|
184
|
+
};
|
|
76
185
|
}
|
|
186
|
+
const emptyFragment = {
|
|
187
|
+
kind: 'role',
|
|
188
|
+
role: 'fragment'
|
|
189
|
+
};
|
|
77
190
|
function normalizeWhitespace(text) {
|
|
78
191
|
return text.replace(/[\r\n\s\t]+/g, ' ').trim();
|
|
79
192
|
}
|
|
80
193
|
function valueOrRegex(value) {
|
|
81
|
-
return value.startsWith('/') && value.endsWith('/')
|
|
194
|
+
return value.startsWith('/') && value.endsWith('/') && value.length > 1 ? {
|
|
195
|
+
pattern: value.slice(1, -1)
|
|
196
|
+
} : normalizeWhitespace(value);
|
|
82
197
|
}
|
|
83
198
|
class KeyParser {
|
|
84
|
-
static parse(
|
|
85
|
-
|
|
199
|
+
static parse(text, options, errors) {
|
|
200
|
+
try {
|
|
201
|
+
return new KeyParser(text.value)._parse();
|
|
202
|
+
} catch (e) {
|
|
203
|
+
if (e instanceof ParserError) {
|
|
204
|
+
const message = options.prettyErrors === false ? e.message : e.message + ':\n\n' + text.value + '\n' + ' '.repeat(e.pos) + '^\n';
|
|
205
|
+
errors.push({
|
|
206
|
+
message,
|
|
207
|
+
range: [options.lineCounter.linePos(text.range[0]), options.lineCounter.linePos(text.range[0] + e.pos)]
|
|
208
|
+
});
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
throw e;
|
|
212
|
+
}
|
|
86
213
|
}
|
|
87
214
|
constructor(input) {
|
|
88
215
|
this._input = void 0;
|
|
@@ -132,8 +259,8 @@ class KeyParser {
|
|
|
132
259
|
}
|
|
133
260
|
this._throwError('Unterminated string');
|
|
134
261
|
}
|
|
135
|
-
_throwError(message,
|
|
136
|
-
throw new
|
|
262
|
+
_throwError(message, offset = 0) {
|
|
263
|
+
throw new ParserError(message, offset || this._pos);
|
|
137
264
|
}
|
|
138
265
|
_readRegex() {
|
|
139
266
|
let result = '';
|
|
@@ -148,7 +275,9 @@ class KeyParser {
|
|
|
148
275
|
escaped = true;
|
|
149
276
|
result += ch;
|
|
150
277
|
} else if (ch === '/' && !insideClass) {
|
|
151
|
-
return
|
|
278
|
+
return {
|
|
279
|
+
pattern: result
|
|
280
|
+
};
|
|
152
281
|
} else if (ch === '[') {
|
|
153
282
|
insideClass = true;
|
|
154
283
|
result += ch;
|
|
@@ -165,11 +294,11 @@ class KeyParser {
|
|
|
165
294
|
const ch = this._peek();
|
|
166
295
|
if (ch === '"') {
|
|
167
296
|
this._next();
|
|
168
|
-
return this._readString();
|
|
297
|
+
return normalizeWhitespace(this._readString());
|
|
169
298
|
}
|
|
170
299
|
if (ch === '/') {
|
|
171
300
|
this._next();
|
|
172
|
-
return
|
|
301
|
+
return this._readRegex();
|
|
173
302
|
}
|
|
174
303
|
return null;
|
|
175
304
|
}
|
|
@@ -251,17 +380,12 @@ class KeyParser {
|
|
|
251
380
|
if (!value) this._throwError(message || 'Assertion error', valuePos);
|
|
252
381
|
}
|
|
253
382
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
constructor(message, input, pos) {
|
|
259
|
-
super(message + ':\n\n' + input + '\n' + ' '.repeat(pos) + '^\n');
|
|
260
|
-
this.shortMessage = void 0;
|
|
383
|
+
exports.KeyParser = KeyParser;
|
|
384
|
+
class ParserError extends Error {
|
|
385
|
+
constructor(message, pos) {
|
|
386
|
+
super(message);
|
|
261
387
|
this.pos = void 0;
|
|
262
|
-
this.shortMessage = message;
|
|
263
388
|
this.pos = pos;
|
|
264
|
-
this.stack = undefined;
|
|
265
389
|
}
|
|
266
390
|
}
|
|
267
|
-
exports.
|
|
391
|
+
exports.ParserError = ParserError;
|
|
@@ -46,7 +46,7 @@ function parseCSS(selector, customNames) {
|
|
|
46
46
|
tokens = css.tokenize(selector);
|
|
47
47
|
if (!(tokens[tokens.length - 1] instanceof css.EOFToken)) tokens.push(new css.EOFToken());
|
|
48
48
|
} catch (e) {
|
|
49
|
-
const newMessage = e.message + ` while parsing selector "${selector}"
|
|
49
|
+
const newMessage = e.message + ` while parsing css selector "${selector}". Did you mean to CSS.escape it?`;
|
|
50
50
|
const index = (e.stack || '').indexOf(e.message);
|
|
51
51
|
if (index !== -1) e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);
|
|
52
52
|
e.message = newMessage;
|
|
@@ -61,11 +61,11 @@ function parseCSS(selector, customNames) {
|
|
|
61
61
|
// TODO: Consider treating these as strings?
|
|
62
62
|
token instanceof css.URLToken || token instanceof css.PercentageToken;
|
|
63
63
|
});
|
|
64
|
-
if (unsupportedToken) throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing selector "${selector}"
|
|
64
|
+
if (unsupportedToken) throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
|
|
65
65
|
let pos = 0;
|
|
66
66
|
const names = new Set();
|
|
67
67
|
function unexpected() {
|
|
68
|
-
return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing selector "${selector}"
|
|
68
|
+
return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
|
|
69
69
|
}
|
|
70
70
|
function skipWhitespace() {
|
|
71
71
|
while (tokens[pos] instanceof css.WhitespaceToken) pos++;
|
|
@@ -227,7 +227,7 @@ function parseCSS(selector, customNames) {
|
|
|
227
227
|
}
|
|
228
228
|
const result = consumeFunctionArguments();
|
|
229
229
|
if (!isEOF()) throw unexpected();
|
|
230
|
-
if (result.some(arg => typeof arg !== 'object' || !('simples' in arg))) throw new InvalidSelectorError(`Error while parsing selector "${selector}"
|
|
230
|
+
if (result.some(arg => typeof arg !== 'object' || !('simples' in arg))) throw new InvalidSelectorError(`Error while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
|
|
231
231
|
return {
|
|
232
232
|
selector: result,
|
|
233
233
|
names: Array.from(names)
|
|
@@ -25,7 +25,7 @@ var _selectorParser = require("./selectorParser");
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
function asLocator(lang, selector, isFrameLocator = false) {
|
|
28
|
-
return asLocators(lang, selector, isFrameLocator)[0];
|
|
28
|
+
return asLocators(lang, selector, isFrameLocator, 1)[0];
|
|
29
29
|
}
|
|
30
30
|
function asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {
|
|
31
31
|
try {
|
|
@@ -235,7 +235,7 @@ function combineTokens(factory, tokens, maxOutputSize) {
|
|
|
235
235
|
const visit = index => {
|
|
236
236
|
if (index === tokens.length) {
|
|
237
237
|
result.push(factory.chainLocators(currentTokens));
|
|
238
|
-
return
|
|
238
|
+
return result.length < maxOutputSize;
|
|
239
239
|
}
|
|
240
240
|
for (const taken of tokens[index]) {
|
|
241
241
|
currentTokens[index] = taken;
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.locatorOrSelectorAsSelector = locatorOrSelectorAsSelector;
|
|
7
|
+
exports.unsafeLocatorOrSelectorAsSelector = unsafeLocatorOrSelectorAsSelector;
|
|
7
8
|
var _stringUtils = require("./stringUtils");
|
|
8
9
|
var _locatorGenerators = require("./locatorGenerators");
|
|
9
10
|
var _selectorParser = require("./selectorParser");
|
|
@@ -66,7 +67,7 @@ function parseLocator(locator, testIdAttributeName) {
|
|
|
66
67
|
template = template.toLowerCase().replace(/get_by_alt_text/g, 'getbyalttext').replace(/get_by_test_id/g, 'getbytestid').replace(/get_by_([\w]+)/g, 'getby$1').replace(/has_not_text/g, 'hasnottext').replace(/has_text/g, 'hastext').replace(/has_not/g, 'hasnot').replace(/frame_locator/g, 'framelocator').replace(/content_frame/g, 'contentframe').replace(/[{}\s]/g, '').replace(/new\(\)/g, '').replace(/new[\w]+\.[\w]+options\(\)/g, '').replace(/\.set/g, ',set').replace(/\.or_\(/g, 'or(') // Python has "or_" instead of "or".
|
|
67
68
|
.replace(/\.and_\(/g, 'and(') // Python has "and_" instead of "and".
|
|
68
69
|
.replace(/:/g, '=').replace(/,re\.ignorecase/g, 'i').replace(/,pattern.case_insensitive/g, 'i').replace(/,regexoptions.ignorecase/g, 'i').replace(/re.compile\(([^)]+)\)/g, '$1') // Python has regex strings as r"foo"
|
|
69
|
-
.replace(/pattern.compile\(([^)]+)\)/g, 'r$1').replace(/newregex\(([^)]+)\)/g, 'r$1').replace(/string=/g, '=').replace(/regex=/g, '=').replace(/,,/g, ',');
|
|
70
|
+
.replace(/pattern.compile\(([^)]+)\)/g, 'r$1').replace(/newregex\(([^)]+)\)/g, 'r$1').replace(/string=/g, '=').replace(/regex=/g, '=').replace(/,,/g, ',').replace(/,\)/g, ')');
|
|
70
71
|
const preferredQuote = params.map(p => p.quote).filter(quote => '\'"`'.includes(quote))[0];
|
|
71
72
|
return {
|
|
72
73
|
selector: transform(template, params, testIdAttributeName),
|
|
@@ -123,7 +124,7 @@ function transform(template, params, testIdAttributeName) {
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
// Transform to selector engines.
|
|
126
|
-
template = template.replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => ',' + group1.toLowerCase() + '=' + group2.toLowerCase()).replace(/framelocator\(([^)]+)\)/g, '$1.internal:control=enter-frame').replace(/contentframe(\(\))?/g, 'internal:control=enter-frame').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, 'locator($1).internal:has-not-text=$2').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+)\)/g, '$1').replace(/getbyrole\(([^)]+)\)/g, 'internal:role=$1').replace(/getbytext\(([^)]+)\)/g, 'internal:text=$1').replace(/getbylabel\(([^)]+)\)/g, 'internal:label=$1').replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName}=$1]`).replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, 'internal:attr=[$1=$2]').replace(/first(\(\))?/g, 'nth=0').replace(/last(\(\))?/g, 'nth=-1').replace(/nth\(([^)]+)\)/g, 'nth=$1').replace(/filter\(,?hastext=([^)]+)\)/g, 'internal:has-text=$1').replace(/filter\(,?hasnottext=([^)]+)\)/g, 'internal:has-not-text=$1').replace(/filter\(,?has2=([^)]+)\)/g, 'internal:has=$1').replace(/filter\(,?hasnot2=([^)]+)\)/g, 'internal:has-not=$1').replace(/,exact=false/g, '').replace(/,exact=true/g, 's').replace(/\,/g, '][');
|
|
127
|
+
template = template.replace(/\,set([\w]+)\(([^)]+)\)/g, (_, group1, group2) => ',' + group1.toLowerCase() + '=' + group2.toLowerCase()).replace(/framelocator\(([^)]+)\)/g, '$1.internal:control=enter-frame').replace(/contentframe(\(\))?/g, 'internal:control=enter-frame').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+),hasnottext=([^),]+)\)/g, 'locator($1).internal:has-not-text=$2').replace(/locator\(([^)]+),hastext=([^),]+)\)/g, 'locator($1).internal:has-text=$2').replace(/locator\(([^)]+)\)/g, '$1').replace(/getbyrole\(([^)]+)\)/g, 'internal:role=$1').replace(/getbytext\(([^)]+)\)/g, 'internal:text=$1').replace(/getbylabel\(([^)]+)\)/g, 'internal:label=$1').replace(/getbytestid\(([^)]+)\)/g, `internal:testid=[${testIdAttributeName}=$1]`).replace(/getby(placeholder|alt|title)(?:text)?\(([^)]+)\)/g, 'internal:attr=[$1=$2]').replace(/first(\(\))?/g, 'nth=0').replace(/last(\(\))?/g, 'nth=-1').replace(/nth\(([^)]+)\)/g, 'nth=$1').replace(/filter\(,?hastext=([^)]+)\)/g, 'internal:has-text=$1').replace(/filter\(,?hasnottext=([^)]+)\)/g, 'internal:has-not-text=$1').replace(/filter\(,?has2=([^)]+)\)/g, 'internal:has=$1').replace(/filter\(,?hasnot2=([^)]+)\)/g, 'internal:has-not=$1').replace(/,exact=false/g, '').replace(/,exact=true/g, 's').replace(/,includehidden=/g, ',include-hidden=').replace(/\,/g, '][');
|
|
127
128
|
const parts = template.split('.');
|
|
128
129
|
// Turn "internal:control=enter-frame >> nth=0" into "nth=0 >> internal:control=enter-frame"
|
|
129
130
|
// because these are swapped in locators vs selectors.
|
|
@@ -157,23 +158,28 @@ function transform(template, params, testIdAttributeName) {
|
|
|
157
158
|
}).join(' >> ');
|
|
158
159
|
}
|
|
159
160
|
function locatorOrSelectorAsSelector(language, locator, testIdAttributeName) {
|
|
161
|
+
try {
|
|
162
|
+
return unsafeLocatorOrSelectorAsSelector(language, locator, testIdAttributeName);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function unsafeLocatorOrSelectorAsSelector(language, locator, testIdAttributeName) {
|
|
160
168
|
try {
|
|
161
169
|
(0, _selectorParser.parseSelector)(locator);
|
|
162
170
|
return locator;
|
|
163
171
|
} catch (e) {}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (locators.some(candidate => digestForComparison(language, candidate) === digest)) return selector;
|
|
172
|
-
} catch (e) {}
|
|
172
|
+
const {
|
|
173
|
+
selector,
|
|
174
|
+
preferredQuote
|
|
175
|
+
} = parseLocator(locator, testIdAttributeName);
|
|
176
|
+
const locators = (0, _locatorGenerators.asLocators)(language, selector, undefined, undefined, preferredQuote);
|
|
177
|
+
const digest = digestForComparison(language, locator);
|
|
178
|
+
if (locators.some(candidate => digestForComparison(language, candidate) === digest)) return selector;
|
|
173
179
|
return '';
|
|
174
180
|
}
|
|
175
181
|
function digestForComparison(language, locator) {
|
|
176
182
|
locator = locator.replace(/\s/g, '');
|
|
177
|
-
if (language === 'javascript') locator = locator.replace(/\\?["`]/g, '\'');
|
|
183
|
+
if (language === 'javascript') locator = locator.replace(/\\?["`]/g, '\'').replace(/,{}/g, '');
|
|
178
184
|
return locator;
|
|
179
185
|
}
|
|
@@ -101,14 +101,12 @@ function urlMatches(baseURL, urlString, match) {
|
|
|
101
101
|
}
|
|
102
102
|
if ((0, _stringUtils.isString)(match)) match = globToRegex(match);
|
|
103
103
|
if (isRegExp(match)) return match.test(urlString);
|
|
104
|
-
|
|
105
|
-
const url = parsedURL(urlString);
|
|
104
|
+
const url = parseURL(urlString);
|
|
106
105
|
if (!url) return false;
|
|
107
|
-
if (typeof match === 'string') return url.pathname === match;
|
|
108
106
|
if (typeof match !== 'function') throw new Error('url parameter should be string, RegExp or function');
|
|
109
107
|
return match(url);
|
|
110
108
|
}
|
|
111
|
-
function
|
|
109
|
+
function parseURL(url) {
|
|
112
110
|
try {
|
|
113
111
|
return new URL(url);
|
|
114
112
|
} catch (e) {
|
|
@@ -149,7 +149,7 @@ async function launchProcess(options) {
|
|
|
149
149
|
let processClosed = false;
|
|
150
150
|
let fulfillCleanup = () => {};
|
|
151
151
|
const waitForCleanup = new Promise(f => fulfillCleanup = f);
|
|
152
|
-
spawnedProcess.once('
|
|
152
|
+
spawnedProcess.once('close', (exitCode, signal) => {
|
|
153
153
|
options.log(`[pid=${spawnedProcess.pid}] <process did exit: exitCode=${exitCode}, signal=${signal}>`);
|
|
154
154
|
processClosed = true;
|
|
155
155
|
gracefullyCloseSet.delete(gracefullyClose);
|
package/lib/utils/wsServer.js
CHANGED
package/lib/utils/zones.js
CHANGED
|
@@ -24,31 +24,24 @@ var _async_hooks = require("async_hooks");
|
|
|
24
24
|
class ZoneManager {
|
|
25
25
|
constructor() {
|
|
26
26
|
this._asyncLocalStorage = new _async_hooks.AsyncLocalStorage();
|
|
27
|
+
this._emptyZone = Zone.createEmpty(this._asyncLocalStorage);
|
|
27
28
|
}
|
|
28
29
|
run(type, data, func) {
|
|
29
|
-
|
|
30
|
-
return this._asyncLocalStorage.run(zone, func);
|
|
30
|
+
return this.current().with(type, data).run(func);
|
|
31
31
|
}
|
|
32
32
|
zoneData(type) {
|
|
33
|
-
|
|
34
|
-
return zone === null || zone === void 0 ? void 0 : zone.get(type);
|
|
33
|
+
return this.current().data(type);
|
|
35
34
|
}
|
|
36
|
-
|
|
35
|
+
current() {
|
|
37
36
|
var _this$_asyncLocalStor;
|
|
38
|
-
return (_this$_asyncLocalStor = this._asyncLocalStorage.getStore()) !== null && _this$_asyncLocalStor !== void 0 ? _this$_asyncLocalStor :
|
|
37
|
+
return (_this$_asyncLocalStor = this._asyncLocalStorage.getStore()) !== null && _this$_asyncLocalStor !== void 0 ? _this$_asyncLocalStor : this._emptyZone;
|
|
39
38
|
}
|
|
40
|
-
|
|
41
|
-
return this.
|
|
39
|
+
empty() {
|
|
40
|
+
return this._emptyZone;
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
class Zone {
|
|
45
|
-
static
|
|
46
|
-
var _asyncLocalStorage$ge;
|
|
47
|
-
const store = new Map((_asyncLocalStorage$ge = asyncLocalStorage.getStore()) === null || _asyncLocalStorage$ge === void 0 ? void 0 : _asyncLocalStorage$ge._data);
|
|
48
|
-
store.set(type, data);
|
|
49
|
-
return new Zone(asyncLocalStorage, store);
|
|
50
|
-
}
|
|
51
|
-
static _createEmpty(asyncLocalStorage) {
|
|
44
|
+
static createEmpty(asyncLocalStorage) {
|
|
52
45
|
return new Zone(asyncLocalStorage, new Map());
|
|
53
46
|
}
|
|
54
47
|
constructor(asyncLocalStorage, store) {
|
|
@@ -57,13 +50,18 @@ class Zone {
|
|
|
57
50
|
this._asyncLocalStorage = asyncLocalStorage;
|
|
58
51
|
this._data = store;
|
|
59
52
|
}
|
|
53
|
+
with(type, data) {
|
|
54
|
+
return new Zone(this._asyncLocalStorage, new Map(this._data).set(type, data));
|
|
55
|
+
}
|
|
56
|
+
without(type) {
|
|
57
|
+
const data = type ? new Map(this._data) : new Map();
|
|
58
|
+
data.delete(type);
|
|
59
|
+
return new Zone(this._asyncLocalStorage, data);
|
|
60
|
+
}
|
|
60
61
|
run(func) {
|
|
61
|
-
|
|
62
|
-
const entries = [...this._data.entries()].filter(([type]) => type !== 'apiZone' && type !== 'expectZone');
|
|
63
|
-
const resetZone = new Zone(this._asyncLocalStorage, new Map(entries));
|
|
64
|
-
return this._asyncLocalStorage.run(resetZone, func);
|
|
62
|
+
return this._asyncLocalStorage.run(this, func);
|
|
65
63
|
}
|
|
66
|
-
|
|
64
|
+
data(type) {
|
|
67
65
|
return this._data.get(type);
|
|
68
66
|
}
|
|
69
67
|
}
|