build(deps): bump playwright from 1.49.1 to 1.50.1

This commit is contained in:
2025-02-21 17:22:03 -07:00
parent 79c9869e65
commit dc6d9c68a9
174 changed files with 3064 additions and 1955 deletions

View File

@@ -3,6 +3,7 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.compareBuffersOrStrings = compareBuffersOrStrings;
exports.getComparator = getComparator;
var _utilsBundle = require("../utilsBundle");
var _pixelmatch = _interopRequireDefault(require("../third_party/pixelmatch"));
@@ -111,19 +112,24 @@ function compareText(actual, expectedBuffer) {
if (typeof actual !== 'string') return {
errorMessage: 'Actual result should be a string'
};
const expected = expectedBuffer.toString('utf-8');
let expected = expectedBuffer.toString('utf-8');
if (expected === actual) return null;
const diffs = _utilsBundle.diff.diffChars(expected, actual);
return {
errorMessage: diff_prettyTerminal(diffs)
};
}
function diff_prettyTerminal(diffs) {
const result = diffs.map(part => {
const text = part.value;
if (part.added) return _utilsBundle.colors.green(text);else if (part.removed) return _utilsBundle.colors.reset(_utilsBundle.colors.strikethrough(_utilsBundle.colors.red(text)));else return text;
// Eliminate '\\ No newline at end of file'
if (!actual.endsWith('\n')) actual += '\n';
if (!expected.endsWith('\n')) expected += '\n';
const lines = _utilsBundle.diff.createPatch('file', expected, actual, undefined, undefined, {
context: 5
}).split('\n');
const coloredLines = lines.slice(4).map(line => {
if (line.startsWith('-')) return _utilsBundle.colors.red(line);
if (line.startsWith('+')) return _utilsBundle.colors.green(line);
if (line.startsWith('@@')) return _utilsBundle.colors.dim(line);
return line;
});
return result.join('');
const errorMessage = coloredLines.join('\n');
return {
errorMessage
};
}
function resizeImage(image, size) {
if (image.width === size.width && image.height === size.height) return image;

View File

@@ -46,7 +46,9 @@ const debugLoggerColorMap = {
// cyan
'server:channel': 34,
// green
'server:metadata': 33 // blue
'server:metadata': 33,
// blue,
'recorder': 45 // cyan
};
class DebugLogger {
constructor() {

View File

@@ -66,22 +66,28 @@ function calculatePlatform() {
// KDE Neon is ubuntu-based and has the same versions.
// TUXEDO OS is ubuntu-based and has the same versions.
if ((distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.id) === 'ubuntu' || (distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.id) === 'pop' || (distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.id) === 'neon' || (distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.id) === 'tuxedo') {
const isOfficiallySupportedPlatform = (distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.id) === 'ubuntu';
if (parseInt(distroInfo.version, 10) <= 19) return {
const isUbuntu = (distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.id) === 'ubuntu';
const version = distroInfo === null || distroInfo === void 0 ? void 0 : distroInfo.version;
const major = parseInt(distroInfo.version, 10);
if (major < 20) return {
hostPlatform: 'ubuntu18.04' + archSuffix,
isOfficiallySupportedPlatform: false
};
if (parseInt(distroInfo.version, 10) <= 21) return {
if (major < 22) return {
hostPlatform: 'ubuntu20.04' + archSuffix,
isOfficiallySupportedPlatform
isOfficiallySupportedPlatform: isUbuntu && version === '20.04'
};
if (parseInt(distroInfo.version, 10) <= 22) return {
if (major < 24) return {
hostPlatform: 'ubuntu22.04' + archSuffix,
isOfficiallySupportedPlatform
isOfficiallySupportedPlatform: isUbuntu && version === '22.04'
};
if (major < 26) return {
hostPlatform: 'ubuntu24.04' + archSuffix,
isOfficiallySupportedPlatform: isUbuntu && version === '24.04'
};
return {
hostPlatform: 'ubuntu24.04' + archSuffix,
isOfficiallySupportedPlatform
hostPlatform: 'ubuntu' + distroInfo.version + archSuffix,
isOfficiallySupportedPlatform: false
};
}
// Linux Mint is ubuntu-based but does not have the same versions

View File

@@ -207,10 +207,6 @@ class HttpServer {
readable.pipe(response);
}
_onRequest(request, response) {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Request-Method', '*');
response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
if (request.headers.origin) response.setHeader('Access-Control-Allow-Headers', request.headers.origin);
if (request.method === 'OPTIONS') {
response.writeHead(200);
response.end();

View File

@@ -3,9 +3,10 @@
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.AriaKeyError = void 0;
exports.parseAriaKey = parseAriaKey;
exports.parseYamlTemplate = parseYamlTemplate;
exports.ParserError = exports.KeyParser = void 0;
exports.parseAriaSnapshot = parseAriaSnapshot;
exports.parseAriaSnapshotUnsafe = parseAriaSnapshotUnsafe;
exports.valueOrRegex = valueOrRegex;
/**
* Copyright (c) Microsoft Corporation.
*
@@ -24,65 +25,191 @@ exports.parseYamlTemplate = parseYamlTemplate;
// https://www.w3.org/TR/wai-aria-1.2/#role_definitions
function parseYamlTemplate(fragment) {
const result = {
kind: 'role',
role: 'fragment'
};
populateNode(result, fragment);
if (result.children && result.children.length === 1) return result.children[0];
return result;
// We pass parsed template between worlds using JSON, make it easy.
function parseAriaSnapshotUnsafe(yaml, text) {
const result = parseAriaSnapshot(yaml, text);
if (result.errors.length) throw new Error(result.errors[0].message);
return result.fragment;
}
function populateNode(node, container) {
for (const object of container) {
if (typeof object === 'string') {
const childNode = KeyParser.parse(object);
node.children = node.children || [];
node.children.push(childNode);
continue;
function parseAriaSnapshot(yaml, text, options = {}) {
var _fragment$children;
const lineCounter = new yaml.LineCounter();
const parseOptions = {
keepSourceTokens: true,
lineCounter,
...options
};
const yamlDoc = yaml.parseDocument(text, parseOptions);
const errors = [];
const convertRange = range => {
return [lineCounter.linePos(range[0]), lineCounter.linePos(range[1])];
};
const addError = error => {
errors.push({
message: error.message,
range: [lineCounter.linePos(error.pos[0]), lineCounter.linePos(error.pos[1])]
});
};
const convertSeq = (container, seq) => {
for (const item of seq.items) {
const itemIsString = item instanceof yaml.Scalar && typeof item.value === 'string';
if (itemIsString) {
const childNode = KeyParser.parse(item, parseOptions, errors);
if (childNode) {
container.children = container.children || [];
container.children.push(childNode);
}
continue;
}
const itemIsMap = item instanceof yaml.YAMLMap;
if (itemIsMap) {
convertMap(container, item);
continue;
}
errors.push({
message: 'Sequence items should be strings or maps',
range: convertRange(item.range || seq.range)
});
}
for (const key of Object.keys(object)) {
node.children = node.children || [];
const value = object[key];
if (key === 'text') {
node.children.push({
kind: 'text',
text: valueOrRegex(value)
};
const convertMap = (container, map) => {
for (const entry of map.items) {
container.children = container.children || [];
// Key must by a string
const keyIsString = entry.key instanceof yaml.Scalar && typeof entry.key.value === 'string';
if (!keyIsString) {
errors.push({
message: 'Only string keys are supported',
range: convertRange(entry.key.range || map.range)
});
continue;
}
const childNode = KeyParser.parse(key);
if (childNode.kind === 'text') {
node.children.push({
const key = entry.key;
const value = entry.value;
// - text: "text"
if (key.value === 'text') {
const valueIsString = value instanceof yaml.Scalar && typeof value.value === 'string';
if (!valueIsString) {
errors.push({
message: 'Text value should be a string',
range: convertRange(entry.value.range || map.range)
});
continue;
}
container.children.push({
kind: 'text',
text: valueOrRegex(value)
text: valueOrRegex(value.value)
});
continue;
}
if (typeof value === 'string') {
node.children.push({
// role "name": ...
const childNode = KeyParser.parse(key, parseOptions, errors);
if (!childNode) continue;
// - role "name": "text"
const valueIsScalar = value instanceof yaml.Scalar;
if (valueIsScalar) {
const type = typeof value.value;
if (type !== 'string' && type !== 'number' && type !== 'boolean') {
errors.push({
message: 'Node value should be a string or a sequence',
range: convertRange(entry.value.range || map.range)
});
continue;
}
container.children.push({
...childNode,
children: [{
kind: 'text',
text: valueOrRegex(value)
text: valueOrRegex(String(value.value))
}]
});
continue;
}
node.children.push(childNode);
populateNode(childNode, value);
// - role "name":
// - child
const valueIsSequence = value instanceof yaml.YAMLSeq;
if (valueIsSequence) {
container.children.push(childNode);
convertSeq(childNode, value);
continue;
}
errors.push({
message: 'Map values should be strings or sequences',
range: convertRange(entry.value.range || map.range)
});
}
};
const fragment = {
kind: 'role',
role: 'fragment'
};
yamlDoc.errors.forEach(addError);
if (errors.length) return {
errors,
fragment
};
if (!(yamlDoc.contents instanceof yaml.YAMLSeq)) {
errors.push({
message: 'Aria snapshot must be a YAML sequence, elements starting with " -"',
range: yamlDoc.contents ? convertRange(yamlDoc.contents.range) : [{
line: 0,
col: 0
}, {
line: 0,
col: 0
}]
});
}
if (errors.length) return {
errors,
fragment
};
convertSeq(fragment, yamlDoc.contents);
if (errors.length) return {
errors,
fragment: emptyFragment
};
if (((_fragment$children = fragment.children) === null || _fragment$children === void 0 ? void 0 : _fragment$children.length) === 1) return {
fragment: fragment.children[0],
errors
};
return {
fragment,
errors
};
}
const emptyFragment = {
kind: 'role',
role: 'fragment'
};
function normalizeWhitespace(text) {
return text.replace(/[\r\n\s\t]+/g, ' ').trim();
}
function valueOrRegex(value) {
return value.startsWith('/') && value.endsWith('/') ? new RegExp(value.slice(1, -1)) : normalizeWhitespace(value);
return value.startsWith('/') && value.endsWith('/') && value.length > 1 ? {
pattern: value.slice(1, -1)
} : normalizeWhitespace(value);
}
class KeyParser {
static parse(input) {
return new KeyParser(input)._parse();
static parse(text, options, errors) {
try {
return new KeyParser(text.value)._parse();
} catch (e) {
if (e instanceof ParserError) {
const message = options.prettyErrors === false ? e.message : e.message + ':\n\n' + text.value + '\n' + ' '.repeat(e.pos) + '^\n';
errors.push({
message,
range: [options.lineCounter.linePos(text.range[0]), options.lineCounter.linePos(text.range[0] + e.pos)]
});
return null;
}
throw e;
}
}
constructor(input) {
this._input = void 0;
@@ -132,8 +259,8 @@ class KeyParser {
}
this._throwError('Unterminated string');
}
_throwError(message, pos) {
throw new AriaKeyError(message, this._input, pos || this._pos);
_throwError(message, offset = 0) {
throw new ParserError(message, offset || this._pos);
}
_readRegex() {
let result = '';
@@ -148,7 +275,9 @@ class KeyParser {
escaped = true;
result += ch;
} else if (ch === '/' && !insideClass) {
return result;
return {
pattern: result
};
} else if (ch === '[') {
insideClass = true;
result += ch;
@@ -165,11 +294,11 @@ class KeyParser {
const ch = this._peek();
if (ch === '"') {
this._next();
return this._readString();
return normalizeWhitespace(this._readString());
}
if (ch === '/') {
this._next();
return new RegExp(this._readRegex());
return this._readRegex();
}
return null;
}
@@ -251,17 +380,12 @@ class KeyParser {
if (!value) this._throwError(message || 'Assertion error', valuePos);
}
}
function parseAriaKey(key) {
return KeyParser.parse(key);
}
class AriaKeyError extends Error {
constructor(message, input, pos) {
super(message + ':\n\n' + input + '\n' + ' '.repeat(pos) + '^\n');
this.shortMessage = void 0;
exports.KeyParser = KeyParser;
class ParserError extends Error {
constructor(message, pos) {
super(message);
this.pos = void 0;
this.shortMessage = message;
this.pos = pos;
this.stack = undefined;
}
}
exports.AriaKeyError = AriaKeyError;
exports.ParserError = ParserError;

View File

@@ -46,7 +46,7 @@ function parseCSS(selector, customNames) {
tokens = css.tokenize(selector);
if (!(tokens[tokens.length - 1] instanceof css.EOFToken)) tokens.push(new css.EOFToken());
} catch (e) {
const newMessage = e.message + ` while parsing selector "${selector}"`;
const newMessage = e.message + ` while parsing css selector "${selector}". Did you mean to CSS.escape it?`;
const index = (e.stack || '').indexOf(e.message);
if (index !== -1) e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);
e.message = newMessage;
@@ -61,11 +61,11 @@ function parseCSS(selector, customNames) {
// TODO: Consider treating these as strings?
token instanceof css.URLToken || token instanceof css.PercentageToken;
});
if (unsupportedToken) throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing selector "${selector}"`);
if (unsupportedToken) throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
let pos = 0;
const names = new Set();
function unexpected() {
return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing selector "${selector}"`);
return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing css selector "${selector}". Did you mean to CSS.escape it?`);
}
function skipWhitespace() {
while (tokens[pos] instanceof css.WhitespaceToken) pos++;
@@ -227,7 +227,7 @@ function parseCSS(selector, customNames) {
}
const result = consumeFunctionArguments();
if (!isEOF()) throw unexpected();
if (result.some(arg => typeof arg !== 'object' || !('simples' in arg))) throw new InvalidSelectorError(`Error while parsing selector "${selector}"`);
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?`);
return {
selector: result,
names: Array.from(names)

View File

@@ -25,7 +25,7 @@ var _selectorParser = require("./selectorParser");
*/
function asLocator(lang, selector, isFrameLocator = false) {
return asLocators(lang, selector, isFrameLocator)[0];
return asLocators(lang, selector, isFrameLocator, 1)[0];
}
function asLocators(lang, selector, isFrameLocator = false, maxOutputSize = 20, preferredQuote) {
try {
@@ -235,7 +235,7 @@ function combineTokens(factory, tokens, maxOutputSize) {
const visit = index => {
if (index === tokens.length) {
result.push(factory.chainLocators(currentTokens));
return currentTokens.length < maxOutputSize;
return result.length < maxOutputSize;
}
for (const taken of tokens[index]) {
currentTokens[index] = taken;

View File

@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
value: true
});
exports.locatorOrSelectorAsSelector = locatorOrSelectorAsSelector;
exports.unsafeLocatorOrSelectorAsSelector = unsafeLocatorOrSelectorAsSelector;
var _stringUtils = require("./stringUtils");
var _locatorGenerators = require("./locatorGenerators");
var _selectorParser = require("./selectorParser");
@@ -66,7 +67,7 @@ function parseLocator(locator, testIdAttributeName) {
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".
.replace(/\.and_\(/g, 'and(') // Python has "and_" instead of "and".
.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"
.replace(/pattern.compile\(([^)]+)\)/g, 'r$1').replace(/newregex\(([^)]+)\)/g, 'r$1').replace(/string=/g, '=').replace(/regex=/g, '=').replace(/,,/g, ',');
.replace(/pattern.compile\(([^)]+)\)/g, 'r$1').replace(/newregex\(([^)]+)\)/g, 'r$1').replace(/string=/g, '=').replace(/regex=/g, '=').replace(/,,/g, ',').replace(/,\)/g, ')');
const preferredQuote = params.map(p => p.quote).filter(quote => '\'"`'.includes(quote))[0];
return {
selector: transform(template, params, testIdAttributeName),
@@ -123,7 +124,7 @@ function transform(template, params, testIdAttributeName) {
}
// Transform to selector engines.
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, '][');
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, '][');
const parts = template.split('.');
// Turn "internal:control=enter-frame >> nth=0" into "nth=0 >> internal:control=enter-frame"
// because these are swapped in locators vs selectors.
@@ -157,23 +158,28 @@ function transform(template, params, testIdAttributeName) {
}).join(' >> ');
}
function locatorOrSelectorAsSelector(language, locator, testIdAttributeName) {
try {
return unsafeLocatorOrSelectorAsSelector(language, locator, testIdAttributeName);
} catch (e) {
return '';
}
}
function unsafeLocatorOrSelectorAsSelector(language, locator, testIdAttributeName) {
try {
(0, _selectorParser.parseSelector)(locator);
return locator;
} catch (e) {}
try {
const {
selector,
preferredQuote
} = parseLocator(locator, testIdAttributeName);
const locators = (0, _locatorGenerators.asLocators)(language, selector, undefined, undefined, preferredQuote);
const digest = digestForComparison(language, locator);
if (locators.some(candidate => digestForComparison(language, candidate) === digest)) return selector;
} catch (e) {}
const {
selector,
preferredQuote
} = parseLocator(locator, testIdAttributeName);
const locators = (0, _locatorGenerators.asLocators)(language, selector, undefined, undefined, preferredQuote);
const digest = digestForComparison(language, locator);
if (locators.some(candidate => digestForComparison(language, candidate) === digest)) return selector;
return '';
}
function digestForComparison(language, locator) {
locator = locator.replace(/\s/g, '');
if (language === 'javascript') locator = locator.replace(/\\?["`]/g, '\'');
if (language === 'javascript') locator = locator.replace(/\\?["`]/g, '\'').replace(/,{}/g, '');
return locator;
}

View File

@@ -1,227 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildFullSelector = buildFullSelector;
exports.toKeyboardModifiers = toKeyboardModifiers;
exports.traceParamsForAction = traceParamsForAction;
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function buildFullSelector(framePath, selector) {
return [...framePath, selector].join(' >> internal:control=enter-frame >> ');
}
const kDefaultTimeout = 5_000;
function traceParamsForAction(actionInContext) {
const {
action
} = actionInContext;
switch (action.name) {
case 'navigate':
{
const params = {
url: action.url
};
return {
method: 'goto',
apiName: 'page.goto',
params
};
}
case 'openPage':
case 'closePage':
throw new Error('Not reached');
}
const selector = buildFullSelector(actionInContext.frame.framePath, action.selector);
switch (action.name) {
case 'click':
{
const params = {
selector,
strict: true,
modifiers: toKeyboardModifiers(action.modifiers),
button: action.button,
clickCount: action.clickCount,
position: action.position
};
return {
method: 'click',
apiName: 'locator.click',
params
};
}
case 'press':
{
const params = {
selector,
strict: true,
key: [...toKeyboardModifiers(action.modifiers), action.key].join('+')
};
return {
method: 'press',
apiName: 'locator.press',
params
};
}
case 'fill':
{
const params = {
selector,
strict: true,
value: action.text
};
return {
method: 'fill',
apiName: 'locator.fill',
params
};
}
case 'setInputFiles':
{
const params = {
selector,
strict: true,
localPaths: action.files
};
return {
method: 'setInputFiles',
apiName: 'locator.setInputFiles',
params
};
}
case 'check':
{
const params = {
selector,
strict: true
};
return {
method: 'check',
apiName: 'locator.check',
params
};
}
case 'uncheck':
{
const params = {
selector,
strict: true
};
return {
method: 'uncheck',
apiName: 'locator.uncheck',
params
};
}
case 'select':
{
const params = {
selector,
strict: true,
options: action.options.map(option => ({
value: option
}))
};
return {
method: 'selectOption',
apiName: 'locator.selectOption',
params
};
}
case 'assertChecked':
{
const params = {
selector: action.selector,
expression: 'to.be.checked',
isNot: !action.checked,
timeout: kDefaultTimeout
};
return {
method: 'expect',
apiName: 'expect.toBeChecked',
params
};
}
case 'assertText':
{
const params = {
selector,
expression: 'to.have.text',
expectedText: [],
isNot: false,
timeout: kDefaultTimeout
};
return {
method: 'expect',
apiName: 'expect.toContainText',
params
};
}
case 'assertValue':
{
const params = {
selector,
expression: 'to.have.value',
expectedValue: undefined,
isNot: false,
timeout: kDefaultTimeout
};
return {
method: 'expect',
apiName: 'expect.toHaveValue',
params
};
}
case 'assertVisible':
{
const params = {
selector,
expression: 'to.be.visible',
isNot: false,
timeout: kDefaultTimeout
};
return {
method: 'expect',
apiName: 'expect.toBeVisible',
params
};
}
case 'assertSnapshot':
{
const params = {
selector,
expression: 'to.match.snapshot',
expectedText: [],
isNot: false,
timeout: kDefaultTimeout
};
return {
method: 'expect',
apiName: 'expect.toMatchAriaSnapshot',
params
};
}
}
}
function toKeyboardModifiers(modifiers) {
const result = [];
if (modifiers & 1) result.push('Alt');
if (modifiers & 2) result.push('ControlOrMeta');
if (modifiers & 4) result.push('ControlOrMeta');
if (modifiers & 8) result.push('Shift');
return result;
}

View File

@@ -101,14 +101,12 @@ function urlMatches(baseURL, urlString, match) {
}
if ((0, _stringUtils.isString)(match)) match = globToRegex(match);
if (isRegExp(match)) return match.test(urlString);
if (typeof match === 'string' && match === urlString) return true;
const url = parsedURL(urlString);
const url = parseURL(urlString);
if (!url) return false;
if (typeof match === 'string') return url.pathname === match;
if (typeof match !== 'function') throw new Error('url parameter should be string, RegExp or function');
return match(url);
}
function parsedURL(url) {
function parseURL(url) {
try {
return new URL(url);
} catch (e) {

View File

@@ -47,7 +47,7 @@ function httpRequest(params, onResponse, onError) {
const timeout = (_params$timeout = params.timeout) !== null && _params$timeout !== void 0 ? _params$timeout : NET_DEFAULT_TIMEOUT;
const proxyURL = (0, _utilsBundle.getProxyForUrl)(params.url);
if (proxyURL) {
const parsedProxyURL = new URL(proxyURL);
const parsedProxyURL = _url.default.parse(proxyURL);
if (params.url.startsWith('http:')) {
options = {
path: parsedUrl.href,

View File

@@ -149,7 +149,7 @@ async function launchProcess(options) {
let processClosed = false;
let fulfillCleanup = () => {};
const waitForCleanup = new Promise(f => fulfillCleanup = f);
spawnedProcess.once('exit', (exitCode, signal) => {
spawnedProcess.once('close', (exitCode, signal) => {
options.log(`[pid=${spawnedProcess.pid}] <process did exit: exitCode=${exitCode}, signal=${signal}>`);
processClosed = true;
gracefullyCloseSet.delete(gracefullyClose);

View File

@@ -26,6 +26,7 @@ var _debugLogger = require("./debugLogger");
let lastConnectionId = 0;
const kConnectionSymbol = Symbol('kConnection');
const perMessageDeflate = exports.perMessageDeflate = {
serverNoContextTakeover: true,
zlibDeflateOptions: {
level: 3
},

View File

@@ -24,31 +24,24 @@ var _async_hooks = require("async_hooks");
class ZoneManager {
constructor() {
this._asyncLocalStorage = new _async_hooks.AsyncLocalStorage();
this._emptyZone = Zone.createEmpty(this._asyncLocalStorage);
}
run(type, data, func) {
const zone = Zone._createWithData(this._asyncLocalStorage, type, data);
return this._asyncLocalStorage.run(zone, func);
return this.current().with(type, data).run(func);
}
zoneData(type) {
const zone = this._asyncLocalStorage.getStore();
return zone === null || zone === void 0 ? void 0 : zone.get(type);
return this.current().data(type);
}
currentZone() {
current() {
var _this$_asyncLocalStor;
return (_this$_asyncLocalStor = this._asyncLocalStorage.getStore()) !== null && _this$_asyncLocalStor !== void 0 ? _this$_asyncLocalStor : Zone._createEmpty(this._asyncLocalStorage);
return (_this$_asyncLocalStor = this._asyncLocalStorage.getStore()) !== null && _this$_asyncLocalStor !== void 0 ? _this$_asyncLocalStor : this._emptyZone;
}
exitZones(func) {
return this._asyncLocalStorage.run(undefined, func);
empty() {
return this._emptyZone;
}
}
class Zone {
static _createWithData(asyncLocalStorage, type, data) {
var _asyncLocalStorage$ge;
const store = new Map((_asyncLocalStorage$ge = asyncLocalStorage.getStore()) === null || _asyncLocalStorage$ge === void 0 ? void 0 : _asyncLocalStorage$ge._data);
store.set(type, data);
return new Zone(asyncLocalStorage, store);
}
static _createEmpty(asyncLocalStorage) {
static createEmpty(asyncLocalStorage) {
return new Zone(asyncLocalStorage, new Map());
}
constructor(asyncLocalStorage, store) {
@@ -57,13 +50,18 @@ class Zone {
this._asyncLocalStorage = asyncLocalStorage;
this._data = store;
}
run(func) {
// Reset apiZone and expectZone, but restore stepZone.
const entries = [...this._data.entries()].filter(([type]) => type !== 'apiZone' && type !== 'expectZone');
const resetZone = new Zone(this._asyncLocalStorage, new Map(entries));
return this._asyncLocalStorage.run(resetZone, func);
with(type, data) {
return new Zone(this._asyncLocalStorage, new Map(this._data).set(type, data));
}
get(type) {
without(type) {
const data = type ? new Map(this._data) : new Map();
data.delete(type);
return new Zone(this._asyncLocalStorage, data);
}
run(func) {
return this._asyncLocalStorage.run(this, func);
}
data(type) {
return this._data.get(type);
}
}