mirror of
https://github.com/TheBinaryNinja/tvapp2.git
synced 2026-06-04 09:55:41 -04:00
build(deps): bump playwright from 1.49.1 to 1.50.1
This commit is contained in:
6
node_modules/playwright/README.md
generated
vendored
6
node_modules/playwright/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
@@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->131.0.6778.33<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->133.0.6943.16<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->132.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->134.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||
|
||||
|
||||
2
node_modules/playwright/jsx-runtime.mjs
generated
vendored
2
node_modules/playwright/jsx-runtime.mjs
generated
vendored
@@ -18,4 +18,4 @@ import jsxRuntime from './jsx-runtime.js';
|
||||
|
||||
export const jsx = jsxRuntime.jsx;
|
||||
export const jsxs = jsxRuntime.jsxs;
|
||||
export const Fragment = jsxRuntime.Fragment;
|
||||
export const Fragment = jsxRuntime.Fragment;
|
||||
|
||||
5
node_modules/playwright/lib/common/config.js
generated
vendored
5
node_modules/playwright/lib/common/config.js
generated
vendored
@@ -49,6 +49,7 @@ class FullConfigInternal {
|
||||
this.cliFailOnFlakyTests = void 0;
|
||||
this.cliLastFailed = void 0;
|
||||
this.testIdMatcher = void 0;
|
||||
this.lastFailedTestIdMatcher = void 0;
|
||||
this.defineConfigWasUsed = false;
|
||||
this.globalSetups = [];
|
||||
this.globalTeardowns = [];
|
||||
@@ -90,6 +91,7 @@ class FullConfigInternal {
|
||||
projects: [],
|
||||
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
|
||||
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, 'missing'),
|
||||
updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, 'patch'),
|
||||
version: require('../../package.json').version,
|
||||
workers: 0,
|
||||
webServer: null
|
||||
@@ -159,8 +161,7 @@ class FullProjectInternal {
|
||||
this.teardown = void 0;
|
||||
this.fullConfig = fullConfig;
|
||||
const testDir = takeFirst(pathResolve(configDir, projectConfig.testDir), pathResolve(configDir, config.testDir), fullConfig.configDir);
|
||||
const defaultSnapshotPathTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
|
||||
this.snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate, defaultSnapshotPathTemplate);
|
||||
this.snapshotPathTemplate = takeFirst(projectConfig.snapshotPathTemplate, config.snapshotPathTemplate);
|
||||
this.project = {
|
||||
grep: takeFirst(projectConfig.grep, config.grep, defaultGrep),
|
||||
grepInvert: takeFirst(projectConfig.grepInvert, config.grepInvert, null),
|
||||
|
||||
2
node_modules/playwright/lib/common/configLoader.js
generated
vendored
2
node_modules/playwright/lib/common/configLoader.js
generated
vendored
@@ -206,7 +206,7 @@ function validateConfig(file, config) {
|
||||
if (!('current' in config.shard) || typeof config.shard.current !== 'number' || config.shard.current < 1 || config.shard.current > config.shard.total) throw (0, _util.errorWithFile)(file, `config.shard.current must be a positive number, not greater than config.shard.total`);
|
||||
}
|
||||
if ('updateSnapshots' in config && config.updateSnapshots !== undefined) {
|
||||
if (typeof config.updateSnapshots !== 'string' || !['all', 'none', 'missing'].includes(config.updateSnapshots)) throw (0, _util.errorWithFile)(file, `config.updateSnapshots must be one of "all", "none" or "missing"`);
|
||||
if (typeof config.updateSnapshots !== 'string' || !['all', 'changed', 'missing', 'none'].includes(config.updateSnapshots)) throw (0, _util.errorWithFile)(file, `config.updateSnapshots must be one of "all", "changed", "missing" or "none"`);
|
||||
}
|
||||
if ('workers' in config && config.workers !== undefined) {
|
||||
if (typeof config.workers === 'number' && config.workers <= 0) throw (0, _util.errorWithFile)(file, `config.workers must be a positive number`);else if (typeof config.workers === 'string' && !config.workers.endsWith('%')) throw (0, _util.errorWithFile)(file, `config.workers must be a number or percentage`);
|
||||
|
||||
1
node_modules/playwright/lib/common/esmLoaderHost.js
generated
vendored
1
node_modules/playwright/lib/common/esmLoaderHost.js
generated
vendored
@@ -44,7 +44,6 @@ function registerESMLoader() {
|
||||
} = new MessageChannel();
|
||||
// register will wait until the loader is initialized.
|
||||
require('node:module').register(_url.default.pathToFileURL(require.resolve('../transform/esmLoader')), {
|
||||
parentURL: _url.default.pathToFileURL(__filename),
|
||||
data: {
|
||||
port: port2
|
||||
},
|
||||
|
||||
32
node_modules/playwright/lib/common/testType.js
generated
vendored
32
node_modules/playwright/lib/common/testType.js
generated
vendored
@@ -11,6 +11,7 @@ var _globals = require("./globals");
|
||||
var _test = require("./test");
|
||||
var _transform = require("../transform/transform");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _playwrightCore = require("playwright-core");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
@@ -56,7 +57,8 @@ class TestTypeImpl {
|
||||
test.fail.only = (0, _transform.wrapFunctionWithLocation)(this._createTest.bind(this, 'fail.only'));
|
||||
test.slow = (0, _transform.wrapFunctionWithLocation)(this._modifier.bind(this, 'slow'));
|
||||
test.setTimeout = (0, _transform.wrapFunctionWithLocation)(this._setTimeout.bind(this));
|
||||
test.step = this._step.bind(this);
|
||||
test.step = this._step.bind(this, 'pass');
|
||||
test.step.skip = this._step.bind(this, 'skip');
|
||||
test.use = (0, _transform.wrapFunctionWithLocation)(this._use.bind(this));
|
||||
test.extend = (0, _transform.wrapFunctionWithLocation)(this._extend.bind(this));
|
||||
test.info = () => {
|
||||
@@ -222,9 +224,19 @@ class TestTypeImpl {
|
||||
location
|
||||
});
|
||||
}
|
||||
async _step(title, body, options = {}) {
|
||||
async _step(expectation, title, body, options = {}) {
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`test.step() can only be called from a test`);
|
||||
if (expectation === 'skip') {
|
||||
const step = testInfo._addStep({
|
||||
category: 'test.step.skip',
|
||||
title,
|
||||
location: options.location,
|
||||
box: options.box
|
||||
});
|
||||
step.complete({});
|
||||
return undefined;
|
||||
}
|
||||
const step = testInfo._addStep({
|
||||
category: 'test.step',
|
||||
title,
|
||||
@@ -233,9 +245,21 @@ class TestTypeImpl {
|
||||
});
|
||||
return await _utils.zones.run('stepZone', step, async () => {
|
||||
try {
|
||||
const result = await body();
|
||||
let result = undefined;
|
||||
result = await (0, _utils.raceAgainstDeadline)(async () => {
|
||||
try {
|
||||
return await body();
|
||||
} catch (e) {
|
||||
var _result;
|
||||
// If the step timed out, the test fixtures will tear down, which in turn
|
||||
// will abort unfinished actions in the step body. Record such errors here.
|
||||
if ((_result = result) !== null && _result !== void 0 && _result.timedOut) testInfo._failWithError(e);
|
||||
throw e;
|
||||
}
|
||||
}, options.timeout ? (0, _utils.monotonicTime)() + options.timeout : 0);
|
||||
if (result.timedOut) throw new _playwrightCore.errors.TimeoutError(`Step timeout of ${options.timeout}ms exceeded.`);
|
||||
step.complete({});
|
||||
return result;
|
||||
return result.result;
|
||||
} catch (error) {
|
||||
step.complete({
|
||||
error
|
||||
|
||||
44
node_modules/playwright/lib/index.js
generated
vendored
44
node_modules/playwright/lib/index.js
generated
vendored
@@ -437,34 +437,43 @@ const playwrightFixtures = {
|
||||
await artifactsRecorder.willStartTest(testInfo);
|
||||
const tracingGroupSteps = [];
|
||||
const csiListener = {
|
||||
onApiCallBegin: (apiName, params, frames, userData, out) => {
|
||||
userData.apiName = apiName;
|
||||
onApiCallBegin: data => {
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo || apiName.includes('setTestIdAttribute') || apiName === 'tracing.groupEnd') return;
|
||||
// Some special calls do not get into steps.
|
||||
if (!testInfo || data.apiName.includes('setTestIdAttribute') || data.apiName === 'tracing.groupEnd') return;
|
||||
const zone = _utils.zones.zoneData('stepZone');
|
||||
if (zone && zone.category === 'expect') {
|
||||
// Display the internal locator._expect call under the name of the enclosing expect call,
|
||||
// and connect it to the existing expect step.
|
||||
data.apiName = zone.title;
|
||||
data.stepId = zone.stepId;
|
||||
return;
|
||||
}
|
||||
// In the general case, create a step for each api call and connect them through the stepId.
|
||||
const step = testInfo._addStep({
|
||||
location: frames[0],
|
||||
location: data.frames[0],
|
||||
category: 'pw:api',
|
||||
title: renderApiCall(apiName, params),
|
||||
apiName,
|
||||
params
|
||||
title: renderApiCall(data.apiName, data.params),
|
||||
apiName: data.apiName,
|
||||
params: data.params
|
||||
}, tracingGroupSteps[tracingGroupSteps.length - 1]);
|
||||
userData.step = step;
|
||||
out.stepId = step.stepId;
|
||||
if (apiName === 'tracing.group') tracingGroupSteps.push(step);
|
||||
data.userData = step;
|
||||
data.stepId = step.stepId;
|
||||
if (data.apiName === 'tracing.group') tracingGroupSteps.push(step);
|
||||
},
|
||||
onApiCallEnd: (userData, error) => {
|
||||
onApiCallEnd: data => {
|
||||
// "tracing.group" step will end later, when "tracing.groupEnd" finishes.
|
||||
if (userData.apiName === 'tracing.group') return;
|
||||
if (userData.apiName === 'tracing.groupEnd') {
|
||||
if (data.apiName === 'tracing.group') return;
|
||||
if (data.apiName === 'tracing.groupEnd') {
|
||||
const step = tracingGroupSteps.pop();
|
||||
step === null || step === void 0 || step.complete({
|
||||
error
|
||||
error: data.error
|
||||
});
|
||||
return;
|
||||
}
|
||||
const step = userData.step;
|
||||
const step = data.userData;
|
||||
step === null || step === void 0 || step.complete({
|
||||
error
|
||||
error: data.error
|
||||
});
|
||||
},
|
||||
onWillPause: ({
|
||||
@@ -781,8 +790,9 @@ class ArtifactsRecorder {
|
||||
async didFinishTest() {
|
||||
const captureScreenshots = this._shouldCaptureScreenshotUponFinish();
|
||||
if (captureScreenshots) await this._screenshotOnTestFailure();
|
||||
const leftoverContexts = [];
|
||||
let leftoverContexts = [];
|
||||
for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) leftoverContexts.push(...browserType._contexts);
|
||||
leftoverContexts = leftoverContexts.filter(context => !this._reusedContexts.has(context));
|
||||
const leftoverApiRequests = Array.from(this._playwright.request._contexts);
|
||||
|
||||
// Collect traces/screenshots for remaining contexts.
|
||||
|
||||
14
node_modules/playwright/lib/isomorphic/teleReceiver.js
generated
vendored
14
node_modules/playwright/lib/isomorphic/teleReceiver.js
generated
vendored
@@ -137,7 +137,7 @@ class TeleReporterReceiver {
|
||||
const result = test.results.find(r => r._id === resultId);
|
||||
const parentStep = payload.parentStepId ? result._stepMap.get(payload.parentStepId) : undefined;
|
||||
const location = this._absoluteLocation(payload.location);
|
||||
const step = new TeleTestStep(payload, parentStep, location);
|
||||
const step = new TeleTestStep(payload, parentStep, location, result);
|
||||
if (parentStep) parentStep.steps.push(step);else result.steps.push(step);
|
||||
result._stepMap.set(payload.id, step);
|
||||
(_this$_reporter$onSte = (_this$_reporter5 = this._reporter).onStepBegin) === null || _this$_reporter$onSte === void 0 || _this$_reporter$onSte.call(_this$_reporter5, test, result, step);
|
||||
@@ -147,6 +147,7 @@ class TeleReporterReceiver {
|
||||
const test = this._tests.get(testId);
|
||||
const result = test.results.find(r => r._id === resultId);
|
||||
const step = result._stepMap.get(payload.id);
|
||||
step._endPayload = payload;
|
||||
step.duration = payload.duration;
|
||||
step.error = payload.error;
|
||||
(_this$_reporter$onSte2 = (_this$_reporter6 = this._reporter).onStepEnd) === null || _this$_reporter$onSte2 === void 0 || _this$_reporter$onSte2.call(_this$_reporter6, test, result, step);
|
||||
@@ -360,19 +361,23 @@ class TeleTestCase {
|
||||
}
|
||||
exports.TeleTestCase = TeleTestCase;
|
||||
class TeleTestStep {
|
||||
constructor(payload, parentStep, location) {
|
||||
constructor(payload, parentStep, location, result) {
|
||||
this.title = void 0;
|
||||
this.category = void 0;
|
||||
this.location = void 0;
|
||||
this.parent = void 0;
|
||||
this.duration = -1;
|
||||
this.steps = [];
|
||||
this.error = void 0;
|
||||
this._result = void 0;
|
||||
this._endPayload = void 0;
|
||||
this._startTime = 0;
|
||||
this.title = payload.title;
|
||||
this.category = payload.category;
|
||||
this.location = location;
|
||||
this.parent = parentStep;
|
||||
this._startTime = payload.startTime;
|
||||
this._result = result;
|
||||
}
|
||||
titlePath() {
|
||||
var _this$parent2;
|
||||
@@ -385,6 +390,10 @@ class TeleTestStep {
|
||||
set startTime(value) {
|
||||
this._startTime = +value;
|
||||
}
|
||||
get attachments() {
|
||||
var _this$_endPayload$att, _this$_endPayload;
|
||||
return (_this$_endPayload$att = (_this$_endPayload = this._endPayload) === null || _this$_endPayload === void 0 || (_this$_endPayload = _this$_endPayload.attachments) === null || _this$_endPayload === void 0 ? void 0 : _this$_endPayload.map(index => this._result.attachments[index])) !== null && _this$_endPayload$att !== void 0 ? _this$_endPayload$att : [];
|
||||
}
|
||||
}
|
||||
class TeleTestResult {
|
||||
constructor(retry, id) {
|
||||
@@ -438,6 +447,7 @@ const baseFullConfig = exports.baseFullConfig = {
|
||||
quiet: false,
|
||||
shard: null,
|
||||
updateSnapshots: 'missing',
|
||||
updateSourceMethod: 'patch',
|
||||
version: '',
|
||||
workers: 0,
|
||||
webServer: null
|
||||
|
||||
12
node_modules/playwright/lib/isomorphic/testTree.js
generated
vendored
12
node_modules/playwright/lib/isomorphic/testTree.js
generated
vendored
@@ -52,15 +52,19 @@ class TestTree {
|
||||
this._treeItemById.set(rootFolder, this.rootItem);
|
||||
const visitSuite = (project, parentSuite, parentGroup) => {
|
||||
for (const suite of parentSuite.suites) {
|
||||
const title = suite.title || '<anonymous>';
|
||||
let group = parentGroup.children.find(item => item.kind === 'group' && item.title === title);
|
||||
if (!suite.title) {
|
||||
// Flatten anonymous describes.
|
||||
visitSuite(project, suite, parentGroup);
|
||||
continue;
|
||||
}
|
||||
let group = parentGroup.children.find(item => item.kind === 'group' && item.title === suite.title);
|
||||
if (!group) {
|
||||
group = {
|
||||
kind: 'group',
|
||||
subKind: 'describe',
|
||||
id: 'suite:' + parentSuite.titlePath().join('\x1e') + '\x1e' + title,
|
||||
id: 'suite:' + parentSuite.titlePath().join('\x1e') + '\x1e' + suite.title,
|
||||
// account for anonymous suites
|
||||
title,
|
||||
title: suite.title,
|
||||
location: suite.location,
|
||||
duration: 0,
|
||||
parent: parentGroup,
|
||||
|
||||
15
node_modules/playwright/lib/matchers/expect.js
generated
vendored
15
node_modules/playwright/lib/matchers/expect.js
generated
vendored
@@ -170,6 +170,7 @@ const customAsyncMatchers = {
|
||||
toContainText: _matchers.toContainText,
|
||||
toHaveAccessibleDescription: _matchers.toHaveAccessibleDescription,
|
||||
toHaveAccessibleName: _matchers.toHaveAccessibleName,
|
||||
toHaveAccessibleErrorMessage: _matchers.toHaveAccessibleErrorMessage,
|
||||
toHaveAttribute: _matchers.toHaveAttribute,
|
||||
toHaveClass: _matchers.toHaveClass,
|
||||
toHaveCount: _matchers.toHaveCount,
|
||||
@@ -234,9 +235,10 @@ class ExpectMetaInfoProxyHandler {
|
||||
// out all the frames that belong to the test runner from caught runtime errors.
|
||||
const stackFrames = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());
|
||||
|
||||
// Enclose toPass in a step to maintain async stacks, toPass matcher is always async.
|
||||
// toPass and poll matchers can contain other steps, expects and API calls,
|
||||
// so they behave like a retriable step.
|
||||
const stepInfo = {
|
||||
category: 'expect',
|
||||
category: matcherName === 'toPass' || this._info.poll ? 'step' : 'expect',
|
||||
title: (0, _util.trimLongString)(title, 1024),
|
||||
params: args[0] ? {
|
||||
expected: args[0]
|
||||
@@ -248,6 +250,8 @@ class ExpectMetaInfoProxyHandler {
|
||||
const jestError = (0, _matcherHint.isJestError)(e) ? e : null;
|
||||
const error = jestError ? new _matcherHint.ExpectError(jestError, customMessage, stackFrames) : e;
|
||||
if (jestError !== null && jestError !== void 0 && jestError.matcherResult.suggestedRebaseline) {
|
||||
// NOTE: this is a workaround for the fact that we can't pass the suggested rebaseline
|
||||
// for passing matchers. See toMatchAriaSnapshot for a counterpart.
|
||||
step.complete({
|
||||
suggestedRebaseline: jestError === null || jestError === void 0 ? void 0 : jestError.matcherResult.suggestedRebaseline
|
||||
});
|
||||
@@ -263,12 +267,7 @@ class ExpectMetaInfoProxyHandler {
|
||||
};
|
||||
try {
|
||||
const callback = () => matcher.call(target, ...args);
|
||||
// toPass and poll matchers can contain other steps, expects and API calls,
|
||||
// so they behave like a retriable step.
|
||||
const result = matcherName === 'toPass' || this._info.poll ? _utils.zones.run('stepZone', step, callback) : _utils.zones.run('expectZone', {
|
||||
title,
|
||||
stepId: step.stepId
|
||||
}, callback);
|
||||
const result = _utils.zones.run('stepZone', step, callback);
|
||||
if (result instanceof Promise) return result.then(finalizer).catch(reportStepError);
|
||||
finalizer();
|
||||
return result;
|
||||
|
||||
64
node_modules/playwright/lib/matchers/matchers.js
generated
vendored
64
node_modules/playwright/lib/matchers/matchers.js
generated
vendored
@@ -16,6 +16,7 @@ exports.toBeOK = toBeOK;
|
||||
exports.toBeVisible = toBeVisible;
|
||||
exports.toContainText = toContainText;
|
||||
exports.toHaveAccessibleDescription = toHaveAccessibleDescription;
|
||||
exports.toHaveAccessibleErrorMessage = toHaveAccessibleErrorMessage;
|
||||
exports.toHaveAccessibleName = toHaveAccessibleName;
|
||||
exports.toHaveAttribute = toHaveAttribute;
|
||||
exports.toHaveCSS = toHaveCSS;
|
||||
@@ -58,9 +59,8 @@ var _config = require("../common/config");
|
||||
function toBeAttached(locator, options) {
|
||||
const attached = !options || options.attached === undefined || options.attached;
|
||||
const expected = attached ? 'attached' : 'detached';
|
||||
const unexpected = attached ? 'detached' : 'attached';
|
||||
const arg = attached ? '' : '{ attached: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(attached ? 'to.be.attached' : 'to.be.detached', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -68,19 +68,31 @@ function toBeAttached(locator, options) {
|
||||
}, options);
|
||||
}
|
||||
function toBeChecked(locator, options) {
|
||||
const checked = !options || options.checked === undefined || options.checked;
|
||||
const expected = checked ? 'checked' : 'unchecked';
|
||||
const unexpected = checked ? 'unchecked' : 'checked';
|
||||
const arg = checked ? '' : '{ checked: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(checked ? 'to.be.checked' : 'to.be.unchecked', {
|
||||
const checked = options === null || options === void 0 ? void 0 : options.checked;
|
||||
const indeterminate = options === null || options === void 0 ? void 0 : options.indeterminate;
|
||||
const expectedValue = {
|
||||
checked,
|
||||
indeterminate
|
||||
};
|
||||
let expected;
|
||||
let arg;
|
||||
if (options !== null && options !== void 0 && options.indeterminate) {
|
||||
expected = 'indeterminate';
|
||||
arg = `{ indeterminate: true }`;
|
||||
} else {
|
||||
expected = (options === null || options === void 0 ? void 0 : options.checked) === false ? 'unchecked' : 'checked';
|
||||
arg = (options === null || options === void 0 ? void 0 : options.checked) === false ? `{ checked: false }` : '';
|
||||
}
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.checked', {
|
||||
isNot,
|
||||
timeout
|
||||
timeout,
|
||||
expectedValue
|
||||
});
|
||||
}, options);
|
||||
}
|
||||
function toBeDisabled(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', 'disabled', 'enabled', '', async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', 'disabled', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.disabled', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -90,9 +102,8 @@ function toBeDisabled(locator, options) {
|
||||
function toBeEditable(locator, options) {
|
||||
const editable = !options || options.editable === undefined || options.editable;
|
||||
const expected = editable ? 'editable' : 'readOnly';
|
||||
const unexpected = editable ? 'readOnly' : 'editable';
|
||||
const arg = editable ? '' : '{ editable: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(editable ? 'to.be.editable' : 'to.be.readonly', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -100,7 +111,7 @@ function toBeEditable(locator, options) {
|
||||
}, options);
|
||||
}
|
||||
function toBeEmpty(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', 'empty', 'notEmpty', '', async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', 'empty', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.empty', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -110,9 +121,8 @@ function toBeEmpty(locator, options) {
|
||||
function toBeEnabled(locator, options) {
|
||||
const enabled = !options || options.enabled === undefined || options.enabled;
|
||||
const expected = enabled ? 'enabled' : 'disabled';
|
||||
const unexpected = enabled ? 'disabled' : 'enabled';
|
||||
const arg = enabled ? '' : '{ enabled: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(enabled ? 'to.be.enabled' : 'to.be.disabled', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -120,7 +130,7 @@ function toBeEnabled(locator, options) {
|
||||
}, options);
|
||||
}
|
||||
function toBeFocused(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', 'focused', 'inactive', '', async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', 'focused', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.focused', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -128,7 +138,7 @@ function toBeFocused(locator, options) {
|
||||
}, options);
|
||||
}
|
||||
function toBeHidden(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', 'hidden', 'visible', '', async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', 'hidden', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.hidden', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -138,9 +148,8 @@ function toBeHidden(locator, options) {
|
||||
function toBeVisible(locator, options) {
|
||||
const visible = !options || options.visible === undefined || options.visible;
|
||||
const expected = visible ? 'visible' : 'hidden';
|
||||
const unexpected = visible ? 'hidden' : 'visible';
|
||||
const arg = visible ? '' : '{ visible: false }';
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||
return await locator._expect(visible ? 'to.be.visible' : 'to.be.hidden', {
|
||||
isNot,
|
||||
timeout
|
||||
@@ -148,7 +157,7 @@ function toBeVisible(locator, options) {
|
||||
}, options);
|
||||
}
|
||||
function toBeInViewport(locator, options) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeInViewport', locator, 'Locator', 'in viewport', 'outside viewport', '', async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toBeInViewport', locator, 'Locator', 'in viewport', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.in.viewport', {
|
||||
isNot,
|
||||
expectedNumber: options === null || options === void 0 ? void 0 : options.ratio,
|
||||
@@ -216,6 +225,19 @@ function toHaveAccessibleName(locator, expected, options) {
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveAccessibleErrorMessage(locator, expected, options) {
|
||||
return _toMatchText.toMatchText.call(this, 'toHaveAccessibleErrorMessage', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = (0, _utils.serializeExpectedTextValues)([expected], {
|
||||
ignoreCase: options === null || options === void 0 ? void 0 : options.ignoreCase,
|
||||
normalizeWhiteSpace: true
|
||||
});
|
||||
return await locator._expect('to.have.accessible.error.message', {
|
||||
expectedText: expectedText,
|
||||
isNot,
|
||||
timeout
|
||||
});
|
||||
}, expected, options);
|
||||
}
|
||||
function toHaveAttribute(locator, name, expected, options) {
|
||||
if (!options) {
|
||||
// Update params for the case toHaveAttribute(name, options);
|
||||
@@ -225,7 +247,7 @@ function toHaveAttribute(locator, name, expected, options) {
|
||||
}
|
||||
}
|
||||
if (expected === undefined) {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', 'have attribute', 'not have attribute', '', async (isNot, timeout) => {
|
||||
return _toBeTruthy.toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', 'have attribute', '', async (isNot, timeout) => {
|
||||
return await locator._expect('to.have.attribute', {
|
||||
expressionArg: name,
|
||||
isNot,
|
||||
|
||||
7
node_modules/playwright/lib/matchers/toBeTruthy.js
generated
vendored
7
node_modules/playwright/lib/matchers/toBeTruthy.js
generated
vendored
@@ -22,7 +22,7 @@ var _matcherHint = require("./matcherHint");
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function toBeTruthy(matcherName, receiver, receiverType, expected, unexpected, arg, query, options = {}) {
|
||||
async function toBeTruthy(matcherName, receiver, receiverType, expected, arg, query, options = {}) {
|
||||
var _options$timeout;
|
||||
(0, _util.expectTypes)(receiver, [receiverType], matcherName);
|
||||
const matcherOptions = {
|
||||
@@ -45,7 +45,6 @@ async function toBeTruthy(matcherName, receiver, receiverType, expected, unexpec
|
||||
};
|
||||
}
|
||||
const notFound = received === _matcherHint.kNoElementsFoundError ? received : undefined;
|
||||
const actual = pass ? expected : unexpected;
|
||||
let printedReceived;
|
||||
let printedExpected;
|
||||
if (pass) {
|
||||
@@ -53,7 +52,7 @@ async function toBeTruthy(matcherName, receiver, receiverType, expected, unexpec
|
||||
printedReceived = `Received: ${notFound ? _matcherHint.kNoElementsFoundError : expected}`;
|
||||
} else {
|
||||
printedExpected = `Expected: ${expected}`;
|
||||
printedReceived = `Received: ${notFound ? _matcherHint.kNoElementsFoundError : unexpected}`;
|
||||
printedReceived = `Received: ${notFound ? _matcherHint.kNoElementsFoundError : received}`;
|
||||
}
|
||||
const message = () => {
|
||||
const header = (0, _matcherHint.matcherHint)(this, receiver, matcherName, 'locator', arg, matcherOptions, timedOut ? timeout : undefined);
|
||||
@@ -63,7 +62,7 @@ async function toBeTruthy(matcherName, receiver, receiverType, expected, unexpec
|
||||
return {
|
||||
message,
|
||||
pass,
|
||||
actual,
|
||||
actual: received,
|
||||
name: matcherName,
|
||||
expected,
|
||||
log,
|
||||
|
||||
93
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js
generated
vendored
93
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js
generated
vendored
@@ -5,12 +5,14 @@ Object.defineProperty(exports, "__esModule", {
|
||||
});
|
||||
exports.toMatchAriaSnapshot = toMatchAriaSnapshot;
|
||||
var _matcherHint = require("./matcherHint");
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _expectBundle = require("../common/expectBundle");
|
||||
var _util = require("../util");
|
||||
var _expect = require("./expect");
|
||||
var _globals = require("../common/globals");
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
@@ -27,8 +29,8 @@ var _utils = require("playwright-core/lib/utils");
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function toMatchAriaSnapshot(receiver, expected, options = {}) {
|
||||
var _options$timeout;
|
||||
async function toMatchAriaSnapshot(receiver, expectedParam, options = {}) {
|
||||
var _testInfo$_projectInt;
|
||||
const matcherName = 'toMatchAriaSnapshot';
|
||||
const testInfo = (0, _globals.currentTestInfo)();
|
||||
if (!testInfo) throw new Error(`toMatchAriaSnapshot() must be called during the test`);
|
||||
@@ -36,18 +38,41 @@ async function toMatchAriaSnapshot(receiver, expected, options = {}) {
|
||||
pass: !this.isNot,
|
||||
message: () => '',
|
||||
name: 'toMatchAriaSnapshot',
|
||||
expected
|
||||
expected: ''
|
||||
};
|
||||
const updateSnapshots = testInfo.config.updateSnapshots;
|
||||
const pathTemplate = (_testInfo$_projectInt = testInfo._projectInternal.expect) === null || _testInfo$_projectInt === void 0 || (_testInfo$_projectInt = _testInfo$_projectInt.toMatchAriaSnapshot) === null || _testInfo$_projectInt === void 0 ? void 0 : _testInfo$_projectInt.pathTemplate;
|
||||
const defaultTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}';
|
||||
const matcherOptions = {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise
|
||||
};
|
||||
if (typeof expected !== 'string') {
|
||||
throw new Error([(0, _matcherHint.matcherHint)(this, receiver, matcherName, receiver, expected, matcherOptions), `${_utilsBundle.colors.bold('Matcher error')}: ${(0, _expectBundle.EXPECTED_COLOR)('expected')} value must be a string`, this.utils.printWithType('Expected', expected, this.utils.printExpected)].join('\n\n'));
|
||||
let expected;
|
||||
let timeout;
|
||||
let expectedPath;
|
||||
if ((0, _utils.isString)(expectedParam)) {
|
||||
var _options$timeout;
|
||||
expected = expectedParam;
|
||||
timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : this.timeout;
|
||||
} else {
|
||||
var _expectedParam$timeou;
|
||||
if (expectedParam !== null && expectedParam !== void 0 && expectedParam.name) {
|
||||
expectedPath = testInfo._resolveSnapshotPath(pathTemplate, defaultTemplate, [(0, _util.sanitizeFilePathBeforeExtension)(expectedParam.name)]);
|
||||
} else {
|
||||
let snapshotNames = testInfo[snapshotNamesSymbol];
|
||||
if (!snapshotNames) {
|
||||
snapshotNames = {
|
||||
anonymousSnapshotIndex: 0
|
||||
};
|
||||
testInfo[snapshotNamesSymbol] = snapshotNames;
|
||||
}
|
||||
const fullTitleWithoutSpec = [...testInfo.titlePath.slice(1), ++snapshotNames.anonymousSnapshotIndex].join(' ');
|
||||
expectedPath = testInfo._resolveSnapshotPath(pathTemplate, defaultTemplate, [(0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec)) + '.yml']);
|
||||
}
|
||||
expected = await _fs.default.promises.readFile(expectedPath, 'utf8').catch(() => '');
|
||||
timeout = (_expectedParam$timeou = expectedParam === null || expectedParam === void 0 ? void 0 : expectedParam.timeout) !== null && _expectedParam$timeou !== void 0 ? _expectedParam$timeou : this.timeout;
|
||||
}
|
||||
const generateMissingBaseline = updateSnapshots === 'missing' && !expected;
|
||||
const generateNewBaseline = updateSnapshots === 'all' || generateMissingBaseline;
|
||||
if (generateMissingBaseline) {
|
||||
if (this.isNot) {
|
||||
const message = `Matchers using ".not" can't generate new baselines`;
|
||||
@@ -61,7 +86,6 @@ async function toMatchAriaSnapshot(receiver, expected, options = {}) {
|
||||
expected = `- none "Generating new baseline"`;
|
||||
}
|
||||
}
|
||||
const timeout = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : this.timeout;
|
||||
expected = unshift(expected);
|
||||
const {
|
||||
matches: pass,
|
||||
@@ -96,15 +120,46 @@ async function toMatchAriaSnapshot(receiver, expected, options = {}) {
|
||||
return messagePrefix + this.utils.printDiffOrStringify(expected, receivedText, labelExpected, 'Received', false) + (0, _util.callLogText)(log);
|
||||
}
|
||||
};
|
||||
if (!this.isNot && pass === this.isNot && generateNewBaseline) {
|
||||
// Only rebaseline failed snapshots.
|
||||
const suggestedRebaseline = `toMatchAriaSnapshot(\`\n${(0, _utils.escapeTemplateString)(indent(typedReceived.regex, '{indent} '))}\n{indent}\`)`;
|
||||
return {
|
||||
pass: this.isNot,
|
||||
message: () => '',
|
||||
name: 'toMatchAriaSnapshot',
|
||||
suggestedRebaseline
|
||||
};
|
||||
if (!this.isNot) {
|
||||
if (updateSnapshots === 'all' || updateSnapshots === 'changed' && pass === this.isNot || generateMissingBaseline) {
|
||||
if (expectedPath) {
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(expectedPath), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(expectedPath, typedReceived.regex, 'utf8');
|
||||
const relativePath = _path.default.relative(process.cwd(), expectedPath);
|
||||
if (updateSnapshots === 'missing') {
|
||||
const message = `A snapshot doesn't exist at ${relativePath}, writing actual.`;
|
||||
testInfo._hasNonRetriableError = true;
|
||||
testInfo._failWithError(new Error(message));
|
||||
} else {
|
||||
const message = `A snapshot is generated at ${relativePath}.`;
|
||||
/* eslint-disable no-console */
|
||||
console.log(message);
|
||||
}
|
||||
return {
|
||||
pass: true,
|
||||
message: () => '',
|
||||
name: 'toMatchAriaSnapshot'
|
||||
};
|
||||
} else {
|
||||
const suggestedRebaseline = `\`\n${(0, _utils.escapeTemplateString)(indent(typedReceived.regex, '{indent} '))}\n{indent}\``;
|
||||
if (updateSnapshots === 'missing') {
|
||||
const message = 'A snapshot is not provided, generating new baseline.';
|
||||
testInfo._hasNonRetriableError = true;
|
||||
testInfo._failWithError(new Error(message));
|
||||
}
|
||||
// TODO: ideally, we should return "pass: true" here because this matcher passes
|
||||
// when regenerating baselines. However, we can only access suggestedRebaseline in case
|
||||
// of an error, so we fail here and workaround it in the expect implementation.
|
||||
return {
|
||||
pass: false,
|
||||
message: () => '',
|
||||
name: 'toMatchAriaSnapshot',
|
||||
suggestedRebaseline
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: matcherName,
|
||||
@@ -123,10 +178,10 @@ function unshift(snapshot) {
|
||||
if (!line.trim()) continue;
|
||||
const match = line.match(/^(\s*)/);
|
||||
if (match && match[1].length < whitespacePrefixLength) whitespacePrefixLength = match[1].length;
|
||||
break;
|
||||
}
|
||||
return lines.filter(t => t.trim()).map(line => line.substring(whitespacePrefixLength)).join('\n');
|
||||
}
|
||||
function indent(snapshot, indent) {
|
||||
return snapshot.split('\n').map(line => indent + line).join('\n');
|
||||
}
|
||||
}
|
||||
const snapshotNamesSymbol = Symbol('snapshotNames');
|
||||
38
node_modules/playwright/lib/matchers/toMatchSnapshot.js
generated
vendored
38
node_modules/playwright/lib/matchers/toMatchSnapshot.js
generated
vendored
@@ -102,7 +102,8 @@ class SnapshotHelper {
|
||||
outputBasePath = testInfo._getOutputPath(sanitizedName);
|
||||
this.attachmentBaseName = sanitizedName;
|
||||
}
|
||||
this.expectedPath = testInfo.snapshotPath(...expectedPathSegments);
|
||||
const defaultTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
|
||||
this.expectedPath = testInfo._resolveSnapshotPath(configOptions.pathTemplate, defaultTemplate, expectedPathSegments);
|
||||
this.legacyExpectedPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-expected');
|
||||
this.previousPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-previous');
|
||||
this.actualPath = (0, _util.addSuffixToFilePath)(outputBasePath, '-actual');
|
||||
@@ -144,7 +145,7 @@ class SnapshotHelper {
|
||||
return Object.fromEntries(Object.entries(unfiltered).filter(([_, v]) => v !== undefined));
|
||||
}
|
||||
handleMissingNegated() {
|
||||
const isWriteMissingMode = this.updateSnapshots === 'all' || this.updateSnapshots === 'missing';
|
||||
const isWriteMissingMode = this.updateSnapshots !== 'none';
|
||||
const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', matchers using ".not" won\'t write them automatically.' : '.'}`;
|
||||
// NOTE: 'isNot' matcher implies inversed value.
|
||||
return this.createMatcherResult(message, true);
|
||||
@@ -159,7 +160,7 @@ class SnapshotHelper {
|
||||
return this.createMatcherResult(message, true);
|
||||
}
|
||||
handleMissing(actual) {
|
||||
const isWriteMissingMode = this.updateSnapshots === 'all' || this.updateSnapshots === 'missing';
|
||||
const isWriteMissingMode = this.updateSnapshots !== 'none';
|
||||
if (isWriteMissingMode) writeFileSync(this.expectedPath, actual);
|
||||
this.testInfo.attachments.push({
|
||||
name: (0, _util.addSuffixToFilePath)(this.attachmentBaseName, '-expected'),
|
||||
@@ -173,7 +174,7 @@ class SnapshotHelper {
|
||||
path: this.actualPath
|
||||
});
|
||||
const message = `A snapshot doesn't exist at ${this.expectedPath}${isWriteMissingMode ? ', writing actual.' : '.'}`;
|
||||
if (this.updateSnapshots === 'all') {
|
||||
if (this.updateSnapshots === 'all' || this.updateSnapshots === 'changed') {
|
||||
/* eslint-disable no-console */
|
||||
console.log(message);
|
||||
return this.createMatcherResult(message, true);
|
||||
@@ -252,14 +253,23 @@ function toMatchSnapshot(received, nameOrOptions = {}, optOptions = {}) {
|
||||
}
|
||||
if (!_fs.default.existsSync(helper.expectedPath)) return helper.handleMissing(received);
|
||||
const expected = _fs.default.readFileSync(helper.expectedPath);
|
||||
const result = helper.comparator(received, expected, helper.options);
|
||||
if (!result) return helper.handleMatching();
|
||||
if (helper.updateSnapshots === 'all') {
|
||||
if (!(0, _utils.compareBuffersOrStrings)(received, expected)) return helper.handleMatching();
|
||||
writeFileSync(helper.expectedPath, received);
|
||||
/* eslint-disable no-console */
|
||||
console.log(helper.expectedPath + ' is not the same, writing actual.');
|
||||
return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);
|
||||
}
|
||||
if (helper.updateSnapshots === 'changed') {
|
||||
const result = helper.comparator(received, expected, helper.options);
|
||||
if (!result) return helper.handleMatching();
|
||||
writeFileSync(helper.expectedPath, received);
|
||||
/* eslint-disable no-console */
|
||||
console.log(helper.expectedPath + ' does not match, writing actual.');
|
||||
return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);
|
||||
}
|
||||
const result = helper.comparator(received, expected, helper.options);
|
||||
if (!result) return helper.handleMatching();
|
||||
const receiver = (0, _utils.isString)(received) ? 'string' : 'Buffer';
|
||||
const header = (0, _matcherHint.matcherHint)(this, undefined, 'toMatchSnapshot', receiver, undefined, undefined);
|
||||
return helper.handleDifferent(received, expected, undefined, result.diff, header, result.errorMessage, undefined);
|
||||
@@ -344,8 +354,8 @@ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions =
|
||||
// General case:
|
||||
// - snapshot exists
|
||||
// - regular matcher (i.e. not a `.not`)
|
||||
// - perhaps an 'all' flag to update non-matching screenshots
|
||||
expectScreenshotOptions.expected = await _fs.default.promises.readFile(helper.expectedPath);
|
||||
const expected = await _fs.default.promises.readFile(helper.expectedPath);
|
||||
expectScreenshotOptions.expected = helper.updateSnapshots === 'all' ? undefined : expected;
|
||||
const {
|
||||
actual,
|
||||
previous,
|
||||
@@ -354,14 +364,22 @@ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions =
|
||||
log,
|
||||
timedOut
|
||||
} = await page._expectScreenshot(expectScreenshotOptions);
|
||||
if (!errorMessage) return helper.handleMatching();
|
||||
if (helper.updateSnapshots === 'all') {
|
||||
const writeFiles = () => {
|
||||
writeFileSync(helper.expectedPath, actual);
|
||||
writeFileSync(helper.actualPath, actual);
|
||||
/* eslint-disable no-console */
|
||||
console.log(helper.expectedPath + ' is re-generated, writing actual.');
|
||||
return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true);
|
||||
};
|
||||
if (!errorMessage) {
|
||||
// Screenshot is matching, but is not necessarily the same as the expected.
|
||||
if (helper.updateSnapshots === 'all' && actual && (0, _utils.compareBuffersOrStrings)(actual, expected)) {
|
||||
console.log(helper.expectedPath + ' is re-generated, writing actual.');
|
||||
return writeFiles();
|
||||
}
|
||||
return helper.handleMatching();
|
||||
}
|
||||
if (helper.updateSnapshots === 'changed' || helper.updateSnapshots === 'all') return writeFiles();
|
||||
const header = (0, _matcherHint.matcherHint)(this, undefined, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined);
|
||||
return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log);
|
||||
}
|
||||
|
||||
26
node_modules/playwright/lib/plugins/webServerPlugin.js
generated
vendored
26
node_modules/playwright/lib/plugins/webServerPlugin.js
generated
vendored
@@ -78,7 +78,7 @@ class WebServerPlugin {
|
||||
debugWebServer(`Starting WebServer process ${this._options.command}...`);
|
||||
const {
|
||||
launchedProcess,
|
||||
kill
|
||||
gracefullyClose
|
||||
} = await (0, _utils.launchProcess)({
|
||||
command: this._options.command,
|
||||
env: {
|
||||
@@ -89,14 +89,30 @@ class WebServerPlugin {
|
||||
cwd: this._options.cwd,
|
||||
stdio: 'stdin',
|
||||
shell: true,
|
||||
// Reject to indicate that we cannot close the web server gracefully
|
||||
// and should fallback to non-graceful shutdown.
|
||||
attemptToGracefullyClose: () => Promise.reject(),
|
||||
attemptToGracefullyClose: async () => {
|
||||
if (process.platform === 'win32') throw new Error('Graceful shutdown is not supported on Windows');
|
||||
if (!this._options.gracefulShutdown) throw new Error('skip graceful shutdown');
|
||||
const {
|
||||
signal,
|
||||
timeout = 0
|
||||
} = this._options.gracefulShutdown;
|
||||
|
||||
// proper usage of SIGINT is to send it to the entire process group, see https://www.cons.org/cracauer/sigint.html
|
||||
// there's no such convention for SIGTERM, so we decide what we want. signaling the process group for consistency.
|
||||
process.kill(-launchedProcess.pid, signal);
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = timeout !== 0 ? setTimeout(() => reject(new Error(`process didn't close gracefully within timeout`)), timeout) : undefined;
|
||||
launchedProcess.once('close', (...args) => {
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
log: () => {},
|
||||
onExit: code => processExitedReject(new Error(code ? `Process from config.webServer was not able to start. Exit code: ${code}` : 'Process from config.webServer exited early.')),
|
||||
tempDirectories: []
|
||||
});
|
||||
this._killProcess = kill;
|
||||
this._killProcess = gracefullyClose;
|
||||
debugWebServer(`Process started`);
|
||||
launchedProcess.stderr.on('data', data => {
|
||||
var _onStdErr, _ref;
|
||||
|
||||
10
node_modules/playwright/lib/program.js
generated
vendored
10
node_modules/playwright/lib/program.js
generated
vendored
@@ -268,6 +268,8 @@ async function mergeReports(reportDir, opts) {
|
||||
}
|
||||
function overridesFromOptions(options) {
|
||||
const shardPair = options.shard ? options.shard.split('/').map(t => parseInt(t, 10)) : undefined;
|
||||
let updateSnapshots;
|
||||
if (['all', 'changed', 'missing', 'none'].includes(options.updateSnapshots)) updateSnapshots = options.updateSnapshots;else updateSnapshots = 'updateSnapshots' in options ? 'changed' : undefined;
|
||||
const overrides = {
|
||||
forbidOnly: options.forbidOnly ? true : undefined,
|
||||
fullyParallel: options.fullyParallel ? true : undefined,
|
||||
@@ -285,7 +287,8 @@ function overridesFromOptions(options) {
|
||||
timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
|
||||
tsconfig: options.tsconfig ? _path.default.resolve(process.cwd(), options.tsconfig) : undefined,
|
||||
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : undefined,
|
||||
updateSnapshots: options.updateSnapshots ? 'all' : undefined,
|
||||
updateSnapshots,
|
||||
updateSourceMethod: options.updateSourceMethod,
|
||||
workers: options.workers
|
||||
};
|
||||
if (options.browser) {
|
||||
@@ -328,7 +331,10 @@ function resolveReporter(id) {
|
||||
});
|
||||
}
|
||||
const kTraceModes = ['on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure', 'retain-on-first-failure'];
|
||||
const testOptions = [['--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`], ['-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`], ['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`], ['--fail-on-flaky-tests', `Fail if any test is flagged as flaky (default: false)`], ['--forbid-only', `Fail if test.only is called (default: false)`], ['--fully-parallel', `Run all tests in parallel (default: false)`], ['--global-timeout <timeout>', `Maximum time this test suite can run in milliseconds (default: unlimited)`], ['-g, --grep <grep>', `Only run tests matching this regular expression (default: ".*")`], ['-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`], ['--headed', `Run tests in headed browsers (default: headless)`], ['--ignore-snapshots', `Ignore screenshot and snapshot expectations`], ['--last-failed', `Only re-run the failures`], ['--list', `Collect all the tests and report them, but do not run`], ['--max-failures <N>', `Stop after the first N failures`], ['--no-deps', 'Do not run project dependencies'], ['--output <dir>', `Folder for output artifacts (default: "test-results")`], ['--only-changed [ref]', `Only run test files that have been changed between 'HEAD' and 'ref'. Defaults to running all uncommitted changes. Only supports Git.`], ['--pass-with-no-tests', `Makes test run succeed even if no tests were found`], ['--project <project-name...>', `Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects)`], ['--quiet', `Suppress stdio`], ['--repeat-each <N>', `Run each test N times (default: 1)`], ['--reporter <reporter>', `Reporter to use, comma-separated, can be ${_config.builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${_config.defaultReporter}")`], ['--retries <retries>', `Maximum retry count for flaky tests, zero for no retries (default: no retries)`], ['--shard <shard>', `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"`], ['--timeout <timeout>', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${_config.defaultTimeout})`], ['--trace <mode>', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`], ['--tsconfig <path>', `Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately)`], ['--ui', `Run tests in interactive UI mode`], ['--ui-host <host>', 'Host to serve UI on; specifying this option opens UI in a browser tab'], ['--ui-port <port>', 'Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab'], ['-u, --update-snapshots', `Update snapshots with actual results (default: only create missing snapshots)`], ['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`], ['-x', `Stop after the first failure`]];
|
||||
|
||||
// Note: update docs/src/test-cli-js.md when you update this, program is the source of truth.
|
||||
|
||||
const testOptions = [/* deprecated */['--browser <browser>', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`], ['-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`], ['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`], ['--fail-on-flaky-tests', `Fail if any test is flagged as flaky (default: false)`], ['--forbid-only', `Fail if test.only is called (default: false)`], ['--fully-parallel', `Run all tests in parallel (default: false)`], ['--global-timeout <timeout>', `Maximum time this test suite can run in milliseconds (default: unlimited)`], ['-g, --grep <grep>', `Only run tests matching this regular expression (default: ".*")`], ['-gv, --grep-invert <grep>', `Only run tests that do not match this regular expression`], ['--headed', `Run tests in headed browsers (default: headless)`], ['--ignore-snapshots', `Ignore screenshot and snapshot expectations`], ['--last-failed', `Only re-run the failures`], ['--list', `Collect all the tests and report them, but do not run`], ['--max-failures <N>', `Stop after the first N failures`], ['--no-deps', 'Do not run project dependencies'], ['--output <dir>', `Folder for output artifacts (default: "test-results")`], ['--only-changed [ref]', `Only run test files that have been changed between 'HEAD' and 'ref'. Defaults to running all uncommitted changes. Only supports Git.`], ['--pass-with-no-tests', `Makes test run succeed even if no tests were found`], ['--project <project-name...>', `Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects)`], ['--quiet', `Suppress stdio`], ['--repeat-each <N>', `Run each test N times (default: 1)`], ['--reporter <reporter>', `Reporter to use, comma-separated, can be ${_config.builtInReporters.map(name => `"${name}"`).join(', ')} (default: "${_config.defaultReporter}")`], ['--retries <retries>', `Maximum retry count for flaky tests, zero for no retries (default: no retries)`], ['--shard <shard>', `Shard tests and execute only the selected shard, specify in the form "current/all", 1-based, for example "3/5"`], ['--timeout <timeout>', `Specify test timeout threshold in milliseconds, zero for unlimited (default: ${_config.defaultTimeout})`], ['--trace <mode>', `Force tracing mode, can be ${kTraceModes.map(mode => `"${mode}"`).join(', ')}`], ['--tsconfig <path>', `Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately)`], ['--ui', `Run tests in interactive UI mode`], ['--ui-host <host>', 'Host to serve UI on; specifying this option opens UI in a browser tab'], ['--ui-port <port>', 'Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab'], ['-u, --update-snapshots [mode]', `Update snapshots with actual results. Possible values are 'all', 'changed', 'missing' and 'none'. Not passing defaults to 'missing', passing without value defaults to 'changed'`], ['--update-source-method <method>', `Chooses the way source is updated. Possible values are 'overwrite', '3way' and 'patch'. Defaults to 'patch'`], ['-j, --workers <workers>', `Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%)`], ['-x', `Stop after the first failure`]];
|
||||
addTestCommand(_program.program);
|
||||
addShowReportCommand(_program.program);
|
||||
addListFilesCommand(_program.program);
|
||||
|
||||
206
node_modules/playwright/lib/reporters/base.js
generated
vendored
206
node_modules/playwright/lib/reporters/base.js
generated
vendored
@@ -3,22 +3,20 @@
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.colors = exports.BaseReporter = void 0;
|
||||
exports.TerminalReporter = void 0;
|
||||
exports.fitToWidth = fitToWidth;
|
||||
exports.formatError = formatError;
|
||||
exports.formatFailure = formatFailure;
|
||||
exports.formatResultFailure = formatResultFailure;
|
||||
exports.formatRetry = formatRetry;
|
||||
exports.formatTestHeader = formatTestHeader;
|
||||
exports.formatTestTitle = formatTestTitle;
|
||||
exports.kOutputSymbol = exports.isTTY = void 0;
|
||||
exports.nonTerminalScreen = exports.noColors = exports.kOutputSymbol = exports.internalScreen = void 0;
|
||||
exports.prepareErrorStack = prepareErrorStack;
|
||||
exports.relativeFilePath = relativeFilePath;
|
||||
exports.resolveOutputFile = resolveOutputFile;
|
||||
exports.separator = separator;
|
||||
exports.stepSuffix = stepSuffix;
|
||||
exports.stripAnsiEscapes = stripAnsiEscapes;
|
||||
exports.ttyWidth = void 0;
|
||||
exports.terminalScreen = void 0;
|
||||
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("playwright-core/lib/utils");
|
||||
@@ -42,11 +40,49 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
||||
*/
|
||||
|
||||
const kOutputSymbol = exports.kOutputSymbol = Symbol('output');
|
||||
const {
|
||||
isTTY,
|
||||
ttyWidth,
|
||||
colors
|
||||
} = (() => {
|
||||
const noColors = exports.noColors = {
|
||||
bold: t => t,
|
||||
cyan: t => t,
|
||||
dim: t => t,
|
||||
gray: t => t,
|
||||
green: t => t,
|
||||
red: t => t,
|
||||
yellow: t => t,
|
||||
black: t => t,
|
||||
blue: t => t,
|
||||
magenta: t => t,
|
||||
white: t => t,
|
||||
grey: t => t,
|
||||
bgBlack: t => t,
|
||||
bgRed: t => t,
|
||||
bgGreen: t => t,
|
||||
bgYellow: t => t,
|
||||
bgBlue: t => t,
|
||||
bgMagenta: t => t,
|
||||
bgCyan: t => t,
|
||||
bgWhite: t => t,
|
||||
strip: t => t,
|
||||
stripColors: t => t,
|
||||
reset: t => t,
|
||||
italic: t => t,
|
||||
underline: t => t,
|
||||
inverse: t => t,
|
||||
hidden: t => t,
|
||||
strikethrough: t => t,
|
||||
rainbow: t => t,
|
||||
zebra: t => t,
|
||||
america: t => t,
|
||||
trap: t => t,
|
||||
random: t => t,
|
||||
zalgo: t => t,
|
||||
enabled: false,
|
||||
enable: () => {},
|
||||
disable: () => {},
|
||||
setTheme: () => {}
|
||||
};
|
||||
|
||||
// Output goes to terminal.
|
||||
const terminalScreen = exports.terminalScreen = (() => {
|
||||
let isTTY = !!process.stdout.isTTY;
|
||||
let ttyWidth = process.stdout.columns || 0;
|
||||
if (process.env.PLAYWRIGHT_FORCE_TTY === 'false' || process.env.PLAYWRIGHT_FORCE_TTY === '0') {
|
||||
@@ -62,27 +98,33 @@ const {
|
||||
}
|
||||
let useColors = isTTY;
|
||||
if (process.env.DEBUG_COLORS === '0' || process.env.DEBUG_COLORS === 'false' || process.env.FORCE_COLOR === '0' || process.env.FORCE_COLOR === 'false') useColors = false;else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR) useColors = true;
|
||||
const colors = useColors ? _utilsBundle.colors : {
|
||||
bold: t => t,
|
||||
cyan: t => t,
|
||||
dim: t => t,
|
||||
gray: t => t,
|
||||
green: t => t,
|
||||
red: t => t,
|
||||
yellow: t => t,
|
||||
enabled: false
|
||||
};
|
||||
const colors = useColors ? _utilsBundle.colors : noColors;
|
||||
return {
|
||||
resolveFiles: 'cwd',
|
||||
isTTY,
|
||||
ttyWidth,
|
||||
colors
|
||||
};
|
||||
})();
|
||||
exports.colors = colors;
|
||||
exports.ttyWidth = ttyWidth;
|
||||
exports.isTTY = isTTY;
|
||||
class BaseReporter {
|
||||
|
||||
// Output does not go to terminal, but colors are controlled with terminal env vars.
|
||||
const nonTerminalScreen = exports.nonTerminalScreen = {
|
||||
colors: terminalScreen.colors,
|
||||
isTTY: false,
|
||||
ttyWidth: 0,
|
||||
resolveFiles: 'rootDir'
|
||||
};
|
||||
|
||||
// Internal output for post-processing, should always contain real colors.
|
||||
const internalScreen = exports.internalScreen = {
|
||||
colors: _utilsBundle.colors,
|
||||
isTTY: false,
|
||||
ttyWidth: 0,
|
||||
resolveFiles: 'rootDir'
|
||||
};
|
||||
class TerminalReporter {
|
||||
constructor(options = {}) {
|
||||
this.screen = terminalScreen;
|
||||
this.config = void 0;
|
||||
this.suite = void 0;
|
||||
this.totalTestCount = 0;
|
||||
@@ -123,7 +165,7 @@ class BaseReporter {
|
||||
onTestEnd(test, result) {
|
||||
if (result.status !== 'skipped' && result.status !== test.expectedStatus) ++this._failureCount;
|
||||
const projectName = test.titlePath()[1];
|
||||
const relativePath = relativeTestPath(this.config, test);
|
||||
const relativePath = relativeTestPath(this.screen, this.config, test);
|
||||
const fileAndProject = (projectName ? `[${projectName}] › ` : '') + relativePath;
|
||||
const entry = this.fileDurations.get(fileAndProject) || {
|
||||
duration: 0,
|
||||
@@ -140,18 +182,18 @@ class BaseReporter {
|
||||
this.result = result;
|
||||
}
|
||||
fitToScreen(line, prefix) {
|
||||
if (!ttyWidth) {
|
||||
if (!this.screen.ttyWidth) {
|
||||
// Guard against the case where we cannot determine available width.
|
||||
return line;
|
||||
}
|
||||
return fitToWidth(line, ttyWidth, prefix);
|
||||
return fitToWidth(line, this.screen.ttyWidth, prefix);
|
||||
}
|
||||
generateStartingMessage() {
|
||||
var _this$config$metadata;
|
||||
const jobs = (_this$config$metadata = this.config.metadata.actualWorkers) !== null && _this$config$metadata !== void 0 ? _this$config$metadata : this.config.workers;
|
||||
const shardDetails = this.config.shard ? `, shard ${this.config.shard.current} of ${this.config.shard.total}` : '';
|
||||
if (!this.totalTestCount) return '';
|
||||
return '\n' + colors.dim('Running ') + this.totalTestCount + colors.dim(` test${this.totalTestCount !== 1 ? 's' : ''} using `) + jobs + colors.dim(` worker${jobs !== 1 ? 's' : ''}${shardDetails}`);
|
||||
return '\n' + this.screen.colors.dim('Running ') + this.totalTestCount + this.screen.colors.dim(` test${this.totalTestCount !== 1 ? 's' : ''} using `) + jobs + this.screen.colors.dim(` worker${jobs !== 1 ? 's' : ''}${shardDetails}`);
|
||||
}
|
||||
getSlowTests() {
|
||||
if (!this.config.reportSlowTests) return [];
|
||||
@@ -173,27 +215,27 @@ class BaseReporter {
|
||||
}) {
|
||||
const tokens = [];
|
||||
if (unexpected.length) {
|
||||
tokens.push(colors.red(` ${unexpected.length} failed`));
|
||||
for (const test of unexpected) tokens.push(colors.red(formatTestHeader(this.config, test, {
|
||||
tokens.push(this.screen.colors.red(` ${unexpected.length} failed`));
|
||||
for (const test of unexpected) tokens.push(this.screen.colors.red(this.formatTestHeader(test, {
|
||||
indent: ' '
|
||||
})));
|
||||
}
|
||||
if (interrupted.length) {
|
||||
tokens.push(colors.yellow(` ${interrupted.length} interrupted`));
|
||||
for (const test of interrupted) tokens.push(colors.yellow(formatTestHeader(this.config, test, {
|
||||
tokens.push(this.screen.colors.yellow(` ${interrupted.length} interrupted`));
|
||||
for (const test of interrupted) tokens.push(this.screen.colors.yellow(this.formatTestHeader(test, {
|
||||
indent: ' '
|
||||
})));
|
||||
}
|
||||
if (flaky.length) {
|
||||
tokens.push(colors.yellow(` ${flaky.length} flaky`));
|
||||
for (const test of flaky) tokens.push(colors.yellow(formatTestHeader(this.config, test, {
|
||||
tokens.push(this.screen.colors.yellow(` ${flaky.length} flaky`));
|
||||
for (const test of flaky) tokens.push(this.screen.colors.yellow(this.formatTestHeader(test, {
|
||||
indent: ' '
|
||||
})));
|
||||
}
|
||||
if (skipped) tokens.push(colors.yellow(` ${skipped} skipped`));
|
||||
if (didNotRun) tokens.push(colors.yellow(` ${didNotRun} did not run`));
|
||||
if (expected) tokens.push(colors.green(` ${expected} passed`) + colors.dim(` (${(0, _utilsBundle.ms)(this.result.duration)})`));
|
||||
if (fatalErrors.length && expected + unexpected.length + interrupted.length + flaky.length > 0) tokens.push(colors.red(` ${fatalErrors.length === 1 ? '1 error was not a part of any test' : fatalErrors.length + ' errors were not a part of any test'}, see above for details`));
|
||||
if (skipped) tokens.push(this.screen.colors.yellow(` ${skipped} skipped`));
|
||||
if (didNotRun) tokens.push(this.screen.colors.yellow(` ${didNotRun} did not run`));
|
||||
if (expected) tokens.push(this.screen.colors.green(` ${expected} passed`) + this.screen.colors.dim(` (${(0, _utilsBundle.ms)(this.result.duration)})`));
|
||||
if (fatalErrors.length && expected + unexpected.length + interrupted.length + flaky.length > 0) tokens.push(this.screen.colors.red(` ${fatalErrors.length === 1 ? '1 error was not a part of any test' : fatalErrors.length + ' errors were not a part of any test'}, see above for details`));
|
||||
return tokens.join('\n');
|
||||
}
|
||||
generateSummary() {
|
||||
@@ -251,15 +293,15 @@ class BaseReporter {
|
||||
_printFailures(failures) {
|
||||
console.log('');
|
||||
failures.forEach((test, index) => {
|
||||
console.log(formatFailure(this.config, test, index + 1));
|
||||
console.log(this.formatFailure(test, index + 1));
|
||||
});
|
||||
}
|
||||
_printSlowTests() {
|
||||
const slowTests = this.getSlowTests();
|
||||
slowTests.forEach(([file, duration]) => {
|
||||
console.log(colors.yellow(' Slow test file: ') + file + colors.yellow(` (${(0, _utilsBundle.ms)(duration)})`));
|
||||
console.log(this.screen.colors.yellow(' Slow test file: ') + file + this.screen.colors.yellow(` (${(0, _utilsBundle.ms)(duration)})`));
|
||||
});
|
||||
if (slowTests.length) console.log(colors.yellow(' Consider splitting slow test files to speed up parallel execution'));
|
||||
if (slowTests.length) console.log(this.screen.colors.yellow(' Consider running tests from slow files in parallel, see https://playwright.dev/docs/test-parallel.'));
|
||||
}
|
||||
_printSummary(summary) {
|
||||
if (summary.trim()) console.log(summary);
|
||||
@@ -267,24 +309,36 @@ class BaseReporter {
|
||||
willRetry(test) {
|
||||
return test.outcome() === 'unexpected' && test.results.length <= test.retries;
|
||||
}
|
||||
formatTestTitle(test, step, omitLocation = false) {
|
||||
return formatTestTitle(this.screen, this.config, test, step, omitLocation);
|
||||
}
|
||||
formatTestHeader(test, options = {}) {
|
||||
return formatTestHeader(this.screen, this.config, test, options);
|
||||
}
|
||||
formatFailure(test, index) {
|
||||
return formatFailure(this.screen, this.config, test, index);
|
||||
}
|
||||
formatError(error) {
|
||||
return formatError(this.screen, error);
|
||||
}
|
||||
}
|
||||
exports.BaseReporter = BaseReporter;
|
||||
function formatFailure(config, test, index) {
|
||||
exports.TerminalReporter = TerminalReporter;
|
||||
function formatFailure(screen, config, test, index) {
|
||||
const lines = [];
|
||||
const header = formatTestHeader(config, test, {
|
||||
const header = formatTestHeader(screen, config, test, {
|
||||
indent: ' ',
|
||||
index,
|
||||
mode: 'error'
|
||||
});
|
||||
lines.push(colors.red(header));
|
||||
lines.push(screen.colors.red(header));
|
||||
for (const result of test.results) {
|
||||
const resultLines = [];
|
||||
const errors = formatResultFailure(test, result, ' ', colors.enabled);
|
||||
const errors = formatResultFailure(screen, test, result, ' ');
|
||||
if (!errors.length) continue;
|
||||
const retryLines = [];
|
||||
if (result.retry) {
|
||||
retryLines.push('');
|
||||
retryLines.push(colors.gray(separator(` Retry #${result.retry}`)));
|
||||
retryLines.push(screen.colors.gray(separator(screen, ` Retry #${result.retry}`)));
|
||||
}
|
||||
resultLines.push(...retryLines);
|
||||
resultLines.push(...errors.map(error => '\n' + error.message));
|
||||
@@ -293,37 +347,37 @@ function formatFailure(config, test, index) {
|
||||
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
||||
if (!attachment.path && !hasPrintableContent) continue;
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`)));
|
||||
resultLines.push(screen.colors.cyan(separator(screen, ` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`)));
|
||||
if (attachment.path) {
|
||||
const relativePath = _path.default.relative(process.cwd(), attachment.path);
|
||||
resultLines.push(colors.cyan(` ${relativePath}`));
|
||||
resultLines.push(screen.colors.cyan(` ${relativePath}`));
|
||||
// Make this extensible
|
||||
if (attachment.name === 'trace') {
|
||||
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||
resultLines.push(colors.cyan(` Usage:`));
|
||||
resultLines.push(screen.colors.cyan(` Usage:`));
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));
|
||||
resultLines.push(screen.colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));
|
||||
resultLines.push('');
|
||||
}
|
||||
} else {
|
||||
if (attachment.contentType.startsWith('text/') && attachment.body) {
|
||||
let text = attachment.body.toString();
|
||||
if (text.length > 300) text = text.slice(0, 300) + '...';
|
||||
for (const line of text.split('\n')) resultLines.push(colors.cyan(` ${line}`));
|
||||
for (const line of text.split('\n')) resultLines.push(screen.colors.cyan(` ${line}`));
|
||||
}
|
||||
}
|
||||
resultLines.push(colors.cyan(separator(' ')));
|
||||
resultLines.push(screen.colors.cyan(separator(screen, ' ')));
|
||||
}
|
||||
lines.push(...resultLines);
|
||||
}
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
function formatRetry(result) {
|
||||
function formatRetry(screen, result) {
|
||||
const retryLines = [];
|
||||
if (result.retry) {
|
||||
retryLines.push('');
|
||||
retryLines.push(colors.gray(separator(` Retry #${result.retry}`)));
|
||||
retryLines.push(screen.colors.gray(separator(screen, ` Retry #${result.retry}`)));
|
||||
}
|
||||
return retryLines;
|
||||
}
|
||||
@@ -331,20 +385,20 @@ function quotePathIfNeeded(path) {
|
||||
if (/\s/.test(path)) return `"${path}"`;
|
||||
return path;
|
||||
}
|
||||
function formatResultFailure(test, result, initialIndent, highlightCode) {
|
||||
function formatResultFailure(screen, test, result, initialIndent) {
|
||||
const errorDetails = [];
|
||||
if (result.status === 'passed' && test.expectedStatus === 'failed') {
|
||||
errorDetails.push({
|
||||
message: indent(colors.red(`Expected to fail, but passed.`), initialIndent)
|
||||
message: indent(screen.colors.red(`Expected to fail, but passed.`), initialIndent)
|
||||
});
|
||||
}
|
||||
if (result.status === 'interrupted') {
|
||||
errorDetails.push({
|
||||
message: indent(colors.red(`Test was interrupted.`), initialIndent)
|
||||
message: indent(screen.colors.red(`Test was interrupted.`), initialIndent)
|
||||
});
|
||||
}
|
||||
for (const error of result.errors) {
|
||||
const formattedError = formatError(error, highlightCode);
|
||||
const formattedError = formatError(screen, error);
|
||||
errorDetails.push({
|
||||
message: indent(formattedError.message, initialIndent),
|
||||
location: formattedError.location
|
||||
@@ -352,29 +406,29 @@ function formatResultFailure(test, result, initialIndent, highlightCode) {
|
||||
}
|
||||
return errorDetails;
|
||||
}
|
||||
function relativeFilePath(config, file) {
|
||||
return _path.default.relative(config.rootDir, file) || _path.default.basename(file);
|
||||
function relativeFilePath(screen, config, file) {
|
||||
if (screen.resolveFiles === 'cwd') return _path.default.relative(process.cwd(), file);
|
||||
return _path.default.relative(config.rootDir, file);
|
||||
}
|
||||
function relativeTestPath(config, test) {
|
||||
return relativeFilePath(config, test.location.file);
|
||||
function relativeTestPath(screen, config, test) {
|
||||
return relativeFilePath(screen, config, test.location.file);
|
||||
}
|
||||
function stepSuffix(step) {
|
||||
const stepTitles = step ? step.titlePath() : [];
|
||||
return stepTitles.map(t => t.split('\n')[0]).map(t => ' › ' + t).join('');
|
||||
}
|
||||
function formatTestTitle(config, test, step, omitLocation = false) {
|
||||
var _step$location$line, _step$location, _step$location$column, _step$location2;
|
||||
function formatTestTitle(screen, config, test, step, omitLocation = false) {
|
||||
// root, project, file, ...describes, test
|
||||
const [, projectName,, ...titles] = test.titlePath();
|
||||
let location;
|
||||
if (omitLocation) location = `${relativeTestPath(config, test)}`;else location = `${relativeTestPath(config, test)}:${(_step$location$line = step === null || step === void 0 || (_step$location = step.location) === null || _step$location === void 0 ? void 0 : _step$location.line) !== null && _step$location$line !== void 0 ? _step$location$line : test.location.line}:${(_step$location$column = step === null || step === void 0 || (_step$location2 = step.location) === null || _step$location2 === void 0 ? void 0 : _step$location2.column) !== null && _step$location$column !== void 0 ? _step$location$column : test.location.column}`;
|
||||
if (omitLocation) location = `${relativeTestPath(screen, config, test)}`;else location = `${relativeTestPath(screen, config, test)}:${test.location.line}:${test.location.column}`;
|
||||
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
||||
const testTitle = `${projectTitle}${location} › ${titles.join(' › ')}`;
|
||||
const extraTags = test.tags.filter(t => !testTitle.includes(t));
|
||||
return `${testTitle}${stepSuffix(step)}${extraTags.length ? ' ' + extraTags.join(' ') : ''}`;
|
||||
}
|
||||
function formatTestHeader(config, test, options = {}) {
|
||||
const title = formatTestTitle(config, test);
|
||||
function formatTestHeader(screen, config, test, options = {}) {
|
||||
const title = formatTestTitle(screen, config, test);
|
||||
const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;
|
||||
let fullHeader = header;
|
||||
|
||||
@@ -396,9 +450,9 @@ function formatTestHeader(config, test, options = {}) {
|
||||
}
|
||||
fullHeader = header + (stepPaths.size === 1 ? stepPaths.values().next().value : '');
|
||||
}
|
||||
return separator(fullHeader);
|
||||
return separator(screen, fullHeader);
|
||||
}
|
||||
function formatError(error, highlightCode) {
|
||||
function formatError(screen, error) {
|
||||
const message = error.message || error.value || '';
|
||||
const stack = error.stack;
|
||||
if (!stack && !error.location) return {
|
||||
@@ -412,23 +466,23 @@ function formatError(error, highlightCode) {
|
||||
tokens.push((parsedStack === null || parsedStack === void 0 ? void 0 : parsedStack.message) || message);
|
||||
if (error.snippet) {
|
||||
let snippet = error.snippet;
|
||||
if (!highlightCode) snippet = stripAnsiEscapes(snippet);
|
||||
if (!screen.colors.enabled) snippet = stripAnsiEscapes(snippet);
|
||||
tokens.push('');
|
||||
tokens.push(snippet);
|
||||
}
|
||||
if (parsedStack && parsedStack.stackLines.length) tokens.push(colors.dim(parsedStack.stackLines.join('\n')));
|
||||
if (parsedStack && parsedStack.stackLines.length) tokens.push(screen.colors.dim(parsedStack.stackLines.join('\n')));
|
||||
let location = error.location;
|
||||
if (parsedStack && !location) location = parsedStack.location;
|
||||
if (error.cause) tokens.push(colors.dim('[cause]: ') + formatError(error.cause, highlightCode).message);
|
||||
if (error.cause) tokens.push(screen.colors.dim('[cause]: ') + formatError(screen, error.cause).message);
|
||||
return {
|
||||
location,
|
||||
message: tokens.join('\n')
|
||||
};
|
||||
}
|
||||
function separator(text = '') {
|
||||
function separator(screen, text = '') {
|
||||
if (text) text += ' ';
|
||||
const columns = Math.min(100, ttyWidth || 100);
|
||||
return text + colors.dim('─'.repeat(Math.max(0, columns - text.length)));
|
||||
const columns = Math.min(100, screen.ttyWidth || 100);
|
||||
return text + screen.colors.dim('─'.repeat(Math.max(0, columns - text.length)));
|
||||
}
|
||||
function indent(lines, tab) {
|
||||
return lines.replace(/^(?=.+$)/gm, tab);
|
||||
|
||||
14
node_modules/playwright/lib/reporters/dot.js
generated
vendored
14
node_modules/playwright/lib/reporters/dot.js
generated
vendored
@@ -21,7 +21,7 @@ var _base = require("./base");
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DotReporter extends _base.BaseReporter {
|
||||
class DotReporter extends _base.TerminalReporter {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this._counter = 0;
|
||||
@@ -46,28 +46,28 @@ class DotReporter extends _base.BaseReporter {
|
||||
}
|
||||
++this._counter;
|
||||
if (result.status === 'skipped') {
|
||||
process.stdout.write(_base.colors.yellow('°'));
|
||||
process.stdout.write(this.screen.colors.yellow('°'));
|
||||
return;
|
||||
}
|
||||
if (this.willRetry(test)) {
|
||||
process.stdout.write(_base.colors.gray('×'));
|
||||
process.stdout.write(this.screen.colors.gray('×'));
|
||||
return;
|
||||
}
|
||||
switch (test.outcome()) {
|
||||
case 'expected':
|
||||
process.stdout.write(_base.colors.green('·'));
|
||||
process.stdout.write(this.screen.colors.green('·'));
|
||||
break;
|
||||
case 'unexpected':
|
||||
process.stdout.write(_base.colors.red(result.status === 'timedOut' ? 'T' : 'F'));
|
||||
process.stdout.write(this.screen.colors.red(result.status === 'timedOut' ? 'T' : 'F'));
|
||||
break;
|
||||
case 'flaky':
|
||||
process.stdout.write(_base.colors.yellow('±'));
|
||||
process.stdout.write(this.screen.colors.yellow('±'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
onError(error) {
|
||||
super.onError(error);
|
||||
console.log('\n' + (0, _base.formatError)(error, _base.colors.enabled).message);
|
||||
console.log('\n' + this.formatError(error).message);
|
||||
this._counter = 0;
|
||||
}
|
||||
async onEnd(result) {
|
||||
|
||||
20
node_modules/playwright/lib/reporters/github.js
generated
vendored
20
node_modules/playwright/lib/reporters/github.js
generated
vendored
@@ -43,10 +43,14 @@ class GitHubLogger {
|
||||
this._log(message, 'warning', options);
|
||||
}
|
||||
}
|
||||
class GitHubReporter extends _base.BaseReporter {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
class GitHubReporter extends _base.TerminalReporter {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
this.githubLogger = new GitHubLogger();
|
||||
this.screen = {
|
||||
...this.screen,
|
||||
colors: _base.noColors
|
||||
};
|
||||
}
|
||||
printsToStdio() {
|
||||
return false;
|
||||
@@ -56,7 +60,7 @@ class GitHubReporter extends _base.BaseReporter {
|
||||
this._printAnnotations();
|
||||
}
|
||||
onError(error) {
|
||||
const errorMessage = (0, _base.formatError)(error, false).message;
|
||||
const errorMessage = this.formatError(error).message;
|
||||
this.githubLogger.error(errorMessage);
|
||||
}
|
||||
_printAnnotations() {
|
||||
@@ -82,14 +86,14 @@ class GitHubReporter extends _base.BaseReporter {
|
||||
}
|
||||
_printFailureAnnotations(failures) {
|
||||
failures.forEach((test, index) => {
|
||||
const title = (0, _base.formatTestTitle)(this.config, test);
|
||||
const header = (0, _base.formatTestHeader)(this.config, test, {
|
||||
const title = this.formatTestTitle(test);
|
||||
const header = this.formatTestHeader(test, {
|
||||
indent: ' ',
|
||||
index: index + 1,
|
||||
mode: 'error'
|
||||
});
|
||||
for (const result of test.results) {
|
||||
const errors = (0, _base.formatResultFailure)(test, result, ' ', _base.colors.enabled);
|
||||
const errors = (0, _base.formatResultFailure)(this.screen, test, result, ' ');
|
||||
for (const error of errors) {
|
||||
var _error$location;
|
||||
const options = {
|
||||
@@ -100,7 +104,7 @@ class GitHubReporter extends _base.BaseReporter {
|
||||
options.line = error.location.line;
|
||||
options.col = error.location.column;
|
||||
}
|
||||
const message = [header, ...(0, _base.formatRetry)(result), error.message].join('\n');
|
||||
const message = [header, ...(0, _base.formatRetry)(this.screen, result), error.message].join('\n');
|
||||
this.githubLogger.error(message, options);
|
||||
}
|
||||
}
|
||||
|
||||
55
node_modules/playwright/lib/reporters/html.js
generated
vendored
55
node_modules/playwright/lib/reporters/html.js
generated
vendored
@@ -78,10 +78,10 @@ class HtmlReporter {
|
||||
const key = outputFolder + '|' + project.outputDir;
|
||||
if (reportedWarnings.has(key)) continue;
|
||||
reportedWarnings.add(key);
|
||||
console.log(_base.colors.red(`Configuration Error: HTML reporter output folder clashes with the tests output folder:`));
|
||||
console.log(_utilsBundle.colors.red(`Configuration Error: HTML reporter output folder clashes with the tests output folder:`));
|
||||
console.log(`
|
||||
html reporter folder: ${_base.colors.bold(outputFolder)}
|
||||
test results folder: ${_base.colors.bold(project.outputDir)}`);
|
||||
html reporter folder: ${_utilsBundle.colors.bold(outputFolder)}
|
||||
test results folder: ${_utilsBundle.colors.bold(project.outputDir)}`);
|
||||
console.log('');
|
||||
console.log(`HTML reporter will clear its output directory prior to being generated, which will lead to the artifact loss.
|
||||
`);
|
||||
@@ -129,7 +129,7 @@ class HtmlReporter {
|
||||
const portArg = this._port ? ` --port ${this._port}` : '';
|
||||
console.log('');
|
||||
console.log('To open last HTML report run:');
|
||||
console.log(_base.colors.cyan(`
|
||||
console.log(_utilsBundle.colors.cyan(`
|
||||
${packageManagerCommand} playwright show-report${relativeReportPath}${hostArg}${portArg}
|
||||
`));
|
||||
}
|
||||
@@ -145,7 +145,7 @@ function getHtmlReportOptionProcessEnv() {
|
||||
const htmlOpenEnv = process.env.PLAYWRIGHT_HTML_OPEN || process.env.PW_TEST_HTML_REPORT_OPEN;
|
||||
if (!htmlOpenEnv) return undefined;
|
||||
if (!isHtmlReportOption(htmlOpenEnv)) {
|
||||
console.log(_base.colors.red(`Configuration Error: HTML reporter Invalid value for PLAYWRIGHT_HTML_OPEN: ${htmlOpenEnv}. Valid values are: ${htmlReportOptions.join(', ')}`));
|
||||
console.log(_utilsBundle.colors.red(`Configuration Error: HTML reporter Invalid value for PLAYWRIGHT_HTML_OPEN: ${htmlOpenEnv}. Valid values are: ${htmlReportOptions.join(', ')}`));
|
||||
return undefined;
|
||||
}
|
||||
return htmlOpenEnv;
|
||||
@@ -159,7 +159,7 @@ async function showHTMLReport(reportFolder, host = 'localhost', port, testId) {
|
||||
try {
|
||||
(0, _utils.assert)(_fs.default.statSync(folder).isDirectory());
|
||||
} catch (e) {
|
||||
console.log(_base.colors.red(`No report found at "${folder}"`));
|
||||
console.log(_utilsBundle.colors.red(`No report found at "${folder}"`));
|
||||
(0, _utils.gracefullyProcessExitDoNotHang)(1);
|
||||
return;
|
||||
}
|
||||
@@ -171,7 +171,7 @@ async function showHTMLReport(reportFolder, host = 'localhost', port, testId) {
|
||||
});
|
||||
let url = server.urlPrefix('human-readable');
|
||||
console.log('');
|
||||
console.log(_base.colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
||||
console.log(_utilsBundle.colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
||||
if (testId) url += `#?testId=${testId}`;
|
||||
url = url.replace('0.0.0.0', 'localhost');
|
||||
await (0, _utilsBundle.open)(url, {
|
||||
@@ -217,12 +217,9 @@ class HtmlBuilder {
|
||||
async build(metadata, projectSuites, result, topLevelErrors) {
|
||||
const data = new Map();
|
||||
for (const projectSuite of projectSuites) {
|
||||
const testDir = projectSuite.project().testDir;
|
||||
for (const fileSuite of projectSuite.suites) {
|
||||
const fileName = this._relativeLocation(fileSuite.location).file;
|
||||
// Preserve file ids computed off the testDir.
|
||||
const relativeFile = _path.default.relative(testDir, fileSuite.location.file);
|
||||
const fileId = (0, _utils.calculateSha1)((0, _utils.toPosixPath)(relativeFile)).slice(0, 20);
|
||||
const fileId = (0, _utils.calculateSha1)((0, _utils.toPosixPath)(fileName)).slice(0, 20);
|
||||
let fileEntry = data.get(fileId);
|
||||
if (!fileEntry) {
|
||||
fileEntry = {
|
||||
@@ -285,7 +282,7 @@ class HtmlBuilder {
|
||||
stats: {
|
||||
...[...data.values()].reduce((a, e) => addStats(a, e.testFileSummary.stats), emptyStats())
|
||||
},
|
||||
errors: topLevelErrors.map(error => (0, _base.formatError)(error, true).message)
|
||||
errors: topLevelErrors.map(error => (0, _base.formatError)(_base.internalScreen, error).message)
|
||||
};
|
||||
htmlReport.files.sort((f1, f2) => {
|
||||
const w1 = f1.stats.unexpected * 1000 + f1.stats.flaky;
|
||||
@@ -490,30 +487,37 @@ class HtmlBuilder {
|
||||
duration: result.duration,
|
||||
startTime: result.startTime.toISOString(),
|
||||
retry: result.retry,
|
||||
steps: dedupeSteps(result.steps).map(s => this._createTestStep(s)),
|
||||
errors: (0, _base.formatResultFailure)(test, result, '', true).map(error => error.message),
|
||||
steps: dedupeSteps(result.steps).map(s => this._createTestStep(s, result)),
|
||||
errors: (0, _base.formatResultFailure)(_base.internalScreen, test, result, '').map(error => error.message),
|
||||
status: result.status,
|
||||
attachments: this._serializeAttachments([...result.attachments, ...result.stdout.map(m => stdioAttachment(m, 'stdout')), ...result.stderr.map(m => stdioAttachment(m, 'stderr'))])
|
||||
};
|
||||
}
|
||||
_createTestStep(dedupedStep) {
|
||||
_createTestStep(dedupedStep, result) {
|
||||
var _step$error;
|
||||
const {
|
||||
step,
|
||||
duration,
|
||||
count
|
||||
} = dedupedStep;
|
||||
const result = {
|
||||
const skipped = dedupedStep.step.category === 'test.step.skip';
|
||||
const testStep = {
|
||||
title: step.title,
|
||||
startTime: step.startTime.toISOString(),
|
||||
duration,
|
||||
steps: dedupeSteps(step.steps).map(s => this._createTestStep(s)),
|
||||
steps: dedupeSteps(step.steps).map(s => this._createTestStep(s, result)),
|
||||
attachments: step.attachments.map(s => {
|
||||
const index = result.attachments.indexOf(s);
|
||||
if (index === -1) throw new Error('Unexpected, attachment not found');
|
||||
return index;
|
||||
}),
|
||||
location: this._relativeLocation(step.location),
|
||||
error: (_step$error = step.error) === null || _step$error === void 0 ? void 0 : _step$error.message,
|
||||
count
|
||||
count,
|
||||
skipped
|
||||
};
|
||||
if (step.location) this._stepsInFile.set(step.location.file, result);
|
||||
return result;
|
||||
if (step.location) this._stepsInFile.set(step.location.file, testStep);
|
||||
return testStep;
|
||||
}
|
||||
_relativeLocation(location) {
|
||||
if (!location) return undefined;
|
||||
@@ -572,17 +576,10 @@ function isTextContentType(contentType) {
|
||||
return contentType.startsWith('text/') || contentType.startsWith('application/json');
|
||||
}
|
||||
function stdioAttachment(chunk, type) {
|
||||
if (typeof chunk === 'string') {
|
||||
return {
|
||||
name: type,
|
||||
contentType: 'text/plain',
|
||||
body: chunk
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: type,
|
||||
contentType: 'application/octet-stream',
|
||||
body: chunk
|
||||
contentType: 'text/plain',
|
||||
body: typeof chunk === 'string' ? chunk : chunk.toString('utf-8')
|
||||
};
|
||||
}
|
||||
function dedupeSteps(steps) {
|
||||
|
||||
2
node_modules/playwright/lib/reporters/internalReporter.js
generated
vendored
2
node_modules/playwright/lib/reporters/internalReporter.js
generated
vendored
@@ -123,7 +123,7 @@ function addLocationAndSnippetToError(config, error, file) {
|
||||
});
|
||||
// Convert /var/folders to /private/var/folders on Mac.
|
||||
if (!file || _fs.default.realpathSync(file) !== location.file) {
|
||||
tokens.push(_base.colors.gray(` at `) + `${(0, _base.relativeFilePath)(config, location.file)}:${location.line}`);
|
||||
tokens.push(_base.internalScreen.colors.gray(` at `) + `${(0, _base.relativeFilePath)(_base.internalScreen, config, location.file)}:${location.line}`);
|
||||
tokens.push('');
|
||||
}
|
||||
tokens.push(codeFrame);
|
||||
|
||||
2
node_modules/playwright/lib/reporters/json.js
generated
vendored
2
node_modules/playwright/lib/reporters/json.js
generated
vendored
@@ -203,7 +203,7 @@ class JSONReporter {
|
||||
return jsonResult;
|
||||
}
|
||||
_serializeError(error) {
|
||||
return (0, _base.formatError)(error, true);
|
||||
return (0, _base.formatError)(_base.nonTerminalScreen, error);
|
||||
}
|
||||
_serializeTestStep(step) {
|
||||
const steps = step.steps.filter(s => s.category === 'test.step');
|
||||
|
||||
2
node_modules/playwright/lib/reporters/junit.js
generated
vendored
2
node_modules/playwright/lib/reporters/junit.js
generated
vendored
@@ -167,7 +167,7 @@ class JUnitReporter {
|
||||
message: `${_path.default.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
|
||||
type: 'FAILURE'
|
||||
},
|
||||
text: (0, _base.stripAnsiEscapes)((0, _base.formatFailure)(this.config, test))
|
||||
text: (0, _base.stripAnsiEscapes)((0, _base.formatFailure)(_base.nonTerminalScreen, this.config, test))
|
||||
});
|
||||
}
|
||||
const systemOut = [];
|
||||
|
||||
12
node_modules/playwright/lib/reporters/line.js
generated
vendored
12
node_modules/playwright/lib/reporters/line.js
generated
vendored
@@ -21,7 +21,7 @@ var _base = require("./base");
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class LineReporter extends _base.BaseReporter {
|
||||
class LineReporter extends _base.TerminalReporter {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this._current = 0;
|
||||
@@ -51,7 +51,7 @@ class LineReporter extends _base.BaseReporter {
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS) stream.write(`\u001B[1A\u001B[2K`);
|
||||
if (test && this._lastTest !== test) {
|
||||
// Write new header for the output.
|
||||
const title = _base.colors.dim((0, _base.formatTestTitle)(this.config, test));
|
||||
const title = this.screen.colors.dim(this.formatTestTitle(test));
|
||||
stream.write(this.fitToScreen(title) + `\n`);
|
||||
this._lastTest = test;
|
||||
}
|
||||
@@ -73,20 +73,20 @@ class LineReporter extends _base.BaseReporter {
|
||||
super.onTestEnd(test, result);
|
||||
if (!this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected' || result.status === 'interrupted')) {
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS) process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||
console.log((0, _base.formatFailure)(this.config, test, ++this._failures));
|
||||
console.log(this.formatFailure(test, ++this._failures));
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
_updateLine(test, result, step) {
|
||||
const retriesPrefix = this.totalTestCount < this._current ? ` (retries)` : ``;
|
||||
const prefix = `[${this._current}/${this.totalTestCount}]${retriesPrefix} `;
|
||||
const currentRetrySuffix = result.retry ? _base.colors.yellow(` (retry #${result.retry})`) : '';
|
||||
const title = (0, _base.formatTestTitle)(this.config, test, step) + currentRetrySuffix;
|
||||
const currentRetrySuffix = result.retry ? this.screen.colors.yellow(` (retry #${result.retry})`) : '';
|
||||
const title = this.formatTestTitle(test, step) + currentRetrySuffix;
|
||||
if (process.env.PW_TEST_DEBUG_REPORTERS) process.stdout.write(`${prefix + title}\n`);else process.stdout.write(`\u001B[1A\u001B[2K${prefix + this.fitToScreen(title, prefix)}\n`);
|
||||
}
|
||||
onError(error) {
|
||||
super.onError(error);
|
||||
const message = (0, _base.formatError)(error, _base.colors.enabled).message + '\n';
|
||||
const message = this.formatError(error).message + '\n';
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS && this._didBegin) process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||
process.stdout.write(message);
|
||||
console.log();
|
||||
|
||||
49
node_modules/playwright/lib/reporters/list.js
generated
vendored
49
node_modules/playwright/lib/reporters/list.js
generated
vendored
@@ -27,7 +27,7 @@ var _utils = require("playwright-core/lib/utils");
|
||||
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
||||
const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓';
|
||||
const NEGATIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'x' : '✘';
|
||||
class ListReporter extends _base.BaseReporter {
|
||||
class ListReporter extends _base.TerminalReporter {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
this._lastRow = 0;
|
||||
@@ -51,11 +51,11 @@ class ListReporter extends _base.BaseReporter {
|
||||
onTestBegin(test, result) {
|
||||
const index = String(this._resultIndex.size + 1);
|
||||
this._resultIndex.set(result, index);
|
||||
if (!_base.isTTY) return;
|
||||
if (!this.screen.isTTY) return;
|
||||
this._maybeWriteNewLine();
|
||||
this._testRows.set(test, this._lastRow);
|
||||
const prefix = this._testPrefix(index, '');
|
||||
const line = _base.colors.dim((0, _base.formatTestTitle)(this.config, test)) + this._retrySuffix(result);
|
||||
const line = this.screen.colors.dim(this.formatTestTitle(test)) + this._retrySuffix(result);
|
||||
this._appendLine(line, prefix);
|
||||
}
|
||||
onStdOut(chunk, test, result) {
|
||||
@@ -77,41 +77,43 @@ class ListReporter extends _base.BaseReporter {
|
||||
onStepBegin(test, result, step) {
|
||||
if (step.category !== 'test.step') return;
|
||||
const testIndex = this._resultIndex.get(result) || '';
|
||||
if (!_base.isTTY) return;
|
||||
if (!this.screen.isTTY) return;
|
||||
if (this._printSteps) {
|
||||
this._maybeWriteNewLine();
|
||||
this._stepRows.set(step, this._lastRow);
|
||||
const prefix = this._testPrefix(this.getStepIndex(testIndex, result, step), '');
|
||||
const line = test.title + _base.colors.dim((0, _base.stepSuffix)(step));
|
||||
const line = test.title + this.screen.colors.dim((0, _base.stepSuffix)(step));
|
||||
this._appendLine(line, prefix);
|
||||
} else {
|
||||
this._updateLine(this._testRows.get(test), _base.colors.dim((0, _base.formatTestTitle)(this.config, test, step)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
this._updateLine(this._testRows.get(test), this.screen.colors.dim(this.formatTestTitle(test, step)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
}
|
||||
}
|
||||
onStepEnd(test, result, step) {
|
||||
if (step.category !== 'test.step') return;
|
||||
const testIndex = this._resultIndex.get(result) || '';
|
||||
if (!this._printSteps) {
|
||||
if (_base.isTTY) this._updateLine(this._testRows.get(test), _base.colors.dim((0, _base.formatTestTitle)(this.config, test, step.parent)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
if (this.screen.isTTY) this._updateLine(this._testRows.get(test), this.screen.colors.dim(this.formatTestTitle(test, step.parent)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
return;
|
||||
}
|
||||
const index = this.getStepIndex(testIndex, result, step);
|
||||
const title = _base.isTTY ? test.title + _base.colors.dim((0, _base.stepSuffix)(step)) : (0, _base.formatTestTitle)(this.config, test, step);
|
||||
const title = this.screen.isTTY ? test.title + this.screen.colors.dim((0, _base.stepSuffix)(step)) : this.formatTestTitle(test, step);
|
||||
const prefix = this._testPrefix(index, '');
|
||||
let text = '';
|
||||
if (step.error) text = _base.colors.red(title);else text = title;
|
||||
text += _base.colors.dim(` (${(0, _utilsBundle.ms)(step.duration)})`);
|
||||
if (step.error) text = this.screen.colors.red(title);else text = title;
|
||||
text += this.screen.colors.dim(` (${(0, _utilsBundle.ms)(step.duration)})`);
|
||||
this._updateOrAppendLine(this._stepRows.get(step), text, prefix);
|
||||
}
|
||||
_maybeWriteNewLine() {
|
||||
if (this._needNewLine) {
|
||||
this._needNewLine = false;
|
||||
process.stdout.write('\n');
|
||||
++this._lastRow;
|
||||
this._lastColumn = 0;
|
||||
}
|
||||
}
|
||||
_updateLineCountAndNewLineFlagForOutput(text) {
|
||||
this._needNewLine = text[text.length - 1] !== '\n';
|
||||
if (!_base.ttyWidth) return;
|
||||
if (!this.screen.ttyWidth) return;
|
||||
for (const ch of text) {
|
||||
if (ch === '\n') {
|
||||
this._lastColumn = 0;
|
||||
@@ -119,7 +121,7 @@ class ListReporter extends _base.BaseReporter {
|
||||
continue;
|
||||
}
|
||||
++this._lastColumn;
|
||||
if (this._lastColumn > _base.ttyWidth) {
|
||||
if (this._lastColumn > this.screen.ttyWidth) {
|
||||
this._lastColumn = 0;
|
||||
++this._lastRow;
|
||||
}
|
||||
@@ -133,7 +135,7 @@ class ListReporter extends _base.BaseReporter {
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
super.onTestEnd(test, result);
|
||||
const title = (0, _base.formatTestTitle)(this.config, test);
|
||||
const title = this.formatTestTitle(test);
|
||||
let prefix = '';
|
||||
let text = '';
|
||||
|
||||
@@ -145,24 +147,24 @@ class ListReporter extends _base.BaseReporter {
|
||||
this._resultIndex.set(result, index);
|
||||
}
|
||||
if (result.status === 'skipped') {
|
||||
prefix = this._testPrefix(index, _base.colors.green('-'));
|
||||
prefix = this._testPrefix(index, this.screen.colors.green('-'));
|
||||
// Do not show duration for skipped.
|
||||
text = _base.colors.cyan(title) + this._retrySuffix(result);
|
||||
text = this.screen.colors.cyan(title) + this._retrySuffix(result);
|
||||
} else {
|
||||
const statusMark = result.status === 'passed' ? POSITIVE_STATUS_MARK : NEGATIVE_STATUS_MARK;
|
||||
if (result.status === test.expectedStatus) {
|
||||
prefix = this._testPrefix(index, _base.colors.green(statusMark));
|
||||
prefix = this._testPrefix(index, this.screen.colors.green(statusMark));
|
||||
text = title;
|
||||
} else {
|
||||
prefix = this._testPrefix(index, _base.colors.red(statusMark));
|
||||
text = _base.colors.red(title);
|
||||
prefix = this._testPrefix(index, this.screen.colors.red(statusMark));
|
||||
text = this.screen.colors.red(title);
|
||||
}
|
||||
text += this._retrySuffix(result) + _base.colors.dim(` (${(0, _utilsBundle.ms)(result.duration)})`);
|
||||
text += this._retrySuffix(result) + this.screen.colors.dim(` (${(0, _utilsBundle.ms)(result.duration)})`);
|
||||
}
|
||||
this._updateOrAppendLine(this._testRows.get(test), text, prefix);
|
||||
}
|
||||
_updateOrAppendLine(row, text, prefix) {
|
||||
if (_base.isTTY) {
|
||||
if (this.screen.isTTY) {
|
||||
this._updateLine(row, text, prefix);
|
||||
} else {
|
||||
this._maybeWriteNewLine();
|
||||
@@ -178,6 +180,7 @@ class ListReporter extends _base.BaseReporter {
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
++this._lastRow;
|
||||
this._lastColumn = 0;
|
||||
}
|
||||
_updateLine(row, text, prefix) {
|
||||
const line = prefix + this.fitToScreen(text, prefix);
|
||||
@@ -194,15 +197,15 @@ class ListReporter extends _base.BaseReporter {
|
||||
}
|
||||
_testPrefix(index, statusMark) {
|
||||
const statusMarkLength = (0, _base.stripAnsiEscapes)(statusMark).length;
|
||||
return ' ' + statusMark + ' '.repeat(3 - statusMarkLength) + _base.colors.dim(index + ' ');
|
||||
return ' ' + statusMark + ' '.repeat(3 - statusMarkLength) + this.screen.colors.dim(index + ' ');
|
||||
}
|
||||
_retrySuffix(result) {
|
||||
return result.retry ? _base.colors.yellow(` (retry #${result.retry})`) : '';
|
||||
return result.retry ? this.screen.colors.yellow(` (retry #${result.retry})`) : '';
|
||||
}
|
||||
onError(error) {
|
||||
super.onError(error);
|
||||
this._maybeWriteNewLine();
|
||||
const message = (0, _base.formatError)(error, _base.colors.enabled).message + '\n';
|
||||
const message = this.formatError(error).message + '\n';
|
||||
this._updateLineCountAndNewLineFlagForOutput(message);
|
||||
process.stdout.write(message);
|
||||
}
|
||||
|
||||
4
node_modules/playwright/lib/reporters/markdown.js
generated
vendored
4
node_modules/playwright/lib/reporters/markdown.js
generated
vendored
@@ -25,7 +25,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class MarkdownReporter extends _base.BaseReporter {
|
||||
class MarkdownReporter extends _base.TerminalReporter {
|
||||
constructor(options) {
|
||||
super();
|
||||
this._options = void 0;
|
||||
@@ -69,7 +69,7 @@ class MarkdownReporter extends _base.BaseReporter {
|
||||
await _fs.default.promises.writeFile(reportFile, lines.join('\n'));
|
||||
}
|
||||
_printTestList(prefix, tests, lines, suffix) {
|
||||
for (const test of tests) lines.push(`${prefix} ${(0, _base.formatTestTitle)(this.config, test)}${suffix || ''}`);
|
||||
for (const test of tests) lines.push(`${prefix} ${this.formatTestTitle(test)}${suffix || ''}`);
|
||||
lines.push(``);
|
||||
}
|
||||
}
|
||||
|
||||
7
node_modules/playwright/lib/reporters/teleEmitter.js
generated
vendored
7
node_modules/playwright/lib/reporters/teleEmitter.js
generated
vendored
@@ -102,7 +102,7 @@ class TeleReporterEmitter {
|
||||
params: {
|
||||
testId: test.id,
|
||||
resultId: result[this._idSymbol],
|
||||
step: this._serializeStepEnd(step)
|
||||
step: this._serializeStepEnd(step, result)
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -245,11 +245,12 @@ class TeleReporterEmitter {
|
||||
location: this._relativeLocation(step.location)
|
||||
};
|
||||
}
|
||||
_serializeStepEnd(step) {
|
||||
_serializeStepEnd(step, result) {
|
||||
return {
|
||||
id: step[this._idSymbol],
|
||||
duration: step.duration,
|
||||
error: step.error
|
||||
error: step.error,
|
||||
attachments: step.attachments.map(a => result.attachments.indexOf(a))
|
||||
};
|
||||
}
|
||||
_relativeLocation(location) {
|
||||
|
||||
26
node_modules/playwright/lib/runner/dispatcher.js
generated
vendored
26
node_modules/playwright/lib/runner/dispatcher.js
generated
vendored
@@ -321,6 +321,7 @@ class JobDispatcher {
|
||||
startTime: new Date(params.wallTime),
|
||||
duration: -1,
|
||||
steps: [],
|
||||
attachments: [],
|
||||
location: params.location
|
||||
};
|
||||
steps.set(params.stepId, step);
|
||||
@@ -364,6 +365,11 @@ class JobDispatcher {
|
||||
body: params.body !== undefined ? Buffer.from(params.body, 'base64') : undefined
|
||||
};
|
||||
data.result.attachments.push(attachment);
|
||||
if (params.stepId) {
|
||||
var _this$_reporter$onStd4, _this$_reporter8;
|
||||
const step = data.steps.get(params.stepId);
|
||||
if (step) step.attachments.push(attachment);else (_this$_reporter$onStd4 = (_this$_reporter8 = this._reporter).onStdErr) === null || _this$_reporter$onStd4 === void 0 || _this$_reporter$onStd4.call(_this$_reporter8, 'Internal error: step id not found: ' + params.stepId);
|
||||
}
|
||||
}
|
||||
_failTestWithErrors(test, errors) {
|
||||
const runData = this._dataByTestId.get(test.id);
|
||||
@@ -372,9 +378,9 @@ class JobDispatcher {
|
||||
if (runData) {
|
||||
result = runData.result;
|
||||
} else {
|
||||
var _this$_reporter$onTes2, _this$_reporter8;
|
||||
var _this$_reporter$onTes2, _this$_reporter9;
|
||||
result = test._appendTestResult();
|
||||
(_this$_reporter$onTes2 = (_this$_reporter8 = this._reporter).onTestBegin) === null || _this$_reporter$onTes2 === void 0 || _this$_reporter$onTes2.call(_this$_reporter8, test, result);
|
||||
(_this$_reporter$onTes2 = (_this$_reporter9 = this._reporter).onTestBegin) === null || _this$_reporter$onTes2 === void 0 || _this$_reporter$onTes2.call(_this$_reporter9, test, result);
|
||||
}
|
||||
result.errors = [...errors];
|
||||
result.error = result.errors[0];
|
||||
@@ -396,8 +402,8 @@ class JobDispatcher {
|
||||
// Let's just fail the test run.
|
||||
this._failureTracker.onWorkerError();
|
||||
for (const error of errors) {
|
||||
var _this$_reporter$onErr2, _this$_reporter9;
|
||||
(_this$_reporter$onErr2 = (_this$_reporter9 = this._reporter).onError) === null || _this$_reporter$onErr2 === void 0 || _this$_reporter$onErr2.call(_this$_reporter9, error);
|
||||
var _this$_reporter$onErr2, _this$_reporter10;
|
||||
(_this$_reporter$onErr2 = (_this$_reporter10 = this._reporter).onError) === null || _this$_reporter$onErr2 === void 0 || _this$_reporter$onErr2.call(_this$_reporter10, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,9 +523,9 @@ class JobDispatcher {
|
||||
const allTestsSkipped = this._job.tests.every(test => test.expectedStatus === 'skipped');
|
||||
if (allTestsSkipped && !this._failureTracker.hasReachedMaxFailures()) {
|
||||
for (const test of this._job.tests) {
|
||||
var _this$_reporter$onTes3, _this$_reporter10;
|
||||
var _this$_reporter$onTes3, _this$_reporter11;
|
||||
const result = test._appendTestResult();
|
||||
(_this$_reporter$onTes3 = (_this$_reporter10 = this._reporter).onTestBegin) === null || _this$_reporter$onTes3 === void 0 || _this$_reporter$onTes3.call(_this$_reporter10, test, result);
|
||||
(_this$_reporter$onTes3 = (_this$_reporter11 = this._reporter).onTestBegin) === null || _this$_reporter$onTes3 === void 0 || _this$_reporter$onTes3.call(_this$_reporter11, test, result);
|
||||
result.status = 'skipped';
|
||||
this._reportTestEnd(test, result);
|
||||
}
|
||||
@@ -531,14 +537,14 @@ class JobDispatcher {
|
||||
return this._currentlyRunning;
|
||||
}
|
||||
_reportTestEnd(test, result) {
|
||||
var _this$_reporter$onTes4, _this$_reporter11;
|
||||
(_this$_reporter$onTes4 = (_this$_reporter11 = this._reporter).onTestEnd) === null || _this$_reporter$onTes4 === void 0 || _this$_reporter$onTes4.call(_this$_reporter11, test, result);
|
||||
var _this$_reporter$onTes4, _this$_reporter12;
|
||||
(_this$_reporter$onTes4 = (_this$_reporter12 = this._reporter).onTestEnd) === null || _this$_reporter$onTes4 === void 0 || _this$_reporter$onTes4.call(_this$_reporter12, test, result);
|
||||
const hadMaxFailures = this._failureTracker.hasReachedMaxFailures();
|
||||
this._failureTracker.onTestEnd(test, result);
|
||||
if (this._failureTracker.hasReachedMaxFailures()) {
|
||||
var _this$_reporter$onErr3, _this$_reporter12;
|
||||
var _this$_reporter$onErr3, _this$_reporter13;
|
||||
this._stopCallback();
|
||||
if (!hadMaxFailures) (_this$_reporter$onErr3 = (_this$_reporter12 = this._reporter).onError) === null || _this$_reporter$onErr3 === void 0 || _this$_reporter$onErr3.call(_this$_reporter12, {
|
||||
if (!hadMaxFailures) (_this$_reporter$onErr3 = (_this$_reporter13 = this._reporter).onError) === null || _this$_reporter$onErr3 === void 0 || _this$_reporter$onErr3.call(_this$_reporter13, {
|
||||
message: _utilsBundle.colors.red(`Testing stopped early after ${this._failureTracker.maxFailures()} maximum allowed failures.`)
|
||||
});
|
||||
}
|
||||
|
||||
2
node_modules/playwright/lib/runner/lastRun.js
generated
vendored
2
node_modules/playwright/lib/runner/lastRun.js
generated
vendored
@@ -37,7 +37,7 @@ class LastRunReporter {
|
||||
if (!this._lastRunFile) return;
|
||||
try {
|
||||
const lastRunInfo = JSON.parse(await _fs.default.promises.readFile(this._lastRunFile, 'utf8'));
|
||||
this._config.testIdMatcher = id => lastRunInfo.failedTests.includes(id);
|
||||
this._config.lastFailedTestIdMatcher = id => lastRunInfo.failedTests.includes(id);
|
||||
} catch {}
|
||||
}
|
||||
version() {
|
||||
|
||||
3
node_modules/playwright/lib/runner/loadUtils.js
generated
vendored
3
node_modules/playwright/lib/runner/loadUtils.js
generated
vendored
@@ -196,6 +196,9 @@ async function createRootSuite(testRun, errors, shouldFilterOnly, additionalFile
|
||||
(0, _suiteUtils.filterTestsRemoveEmptySuites)(rootSuite, test => testsInThisShard.has(test));
|
||||
}
|
||||
|
||||
// Explicitly apply --last-failed filter after sharding.
|
||||
if (config.lastFailedTestIdMatcher) (0, _suiteUtils.filterByTestIds)(rootSuite, config.lastFailedTestIdMatcher);
|
||||
|
||||
// Now prepend dependency projects without filtration.
|
||||
{
|
||||
// Filtering 'only' and sharding might have reduced the number of top-level projects.
|
||||
|
||||
100
node_modules/playwright/lib/runner/rebase.js
generated
vendored
100
node_modules/playwright/lib/runner/rebase.js
generated
vendored
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
||||
});
|
||||
exports.addSuggestedRebaseline = addSuggestedRebaseline;
|
||||
exports.applySuggestedRebaselines = applySuggestedRebaselines;
|
||||
exports.clearSuggestedRebaselines = clearSuggestedRebaselines;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _babelBundle = require("../transform/babelBundle");
|
||||
@@ -36,13 +37,18 @@ function addSuggestedRebaseline(location, suggestedRebaseline) {
|
||||
code: suggestedRebaseline
|
||||
});
|
||||
}
|
||||
function clearSuggestedRebaselines() {
|
||||
suggestedRebaselines.clear();
|
||||
}
|
||||
async function applySuggestedRebaselines(config, reporter) {
|
||||
if (config.config.updateSnapshots !== 'all' && config.config.updateSnapshots !== 'missing') return;
|
||||
if (config.config.updateSnapshots === 'none') return;
|
||||
if (!suggestedRebaselines.size) return;
|
||||
const [project] = (0, _projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
|
||||
if (!project) return;
|
||||
const patches = [];
|
||||
const files = [];
|
||||
const gitCache = new Map();
|
||||
const patchFile = _path.default.join(project.project.outputDir, 'rebaselines.patch');
|
||||
for (const fileName of [...suggestedRebaselines.keys()].sort()) {
|
||||
const source = await _fs.default.promises.readFile(fileName, 'utf8');
|
||||
const lines = source.split('\n');
|
||||
@@ -52,21 +58,24 @@ async function applySuggestedRebaselines(config, reporter) {
|
||||
(0, _babelBundle.traverse)(fileNode, {
|
||||
CallExpression: path => {
|
||||
const node = path.node;
|
||||
if (node.arguments.length !== 1) return;
|
||||
if (node.arguments.length < 1) return;
|
||||
if (!t.isMemberExpression(node.callee)) return;
|
||||
const argument = node.arguments[0];
|
||||
if (!t.isStringLiteral(argument) && !t.isTemplateLiteral(argument)) return;
|
||||
const matcher = node.callee.property;
|
||||
const prop = node.callee.property;
|
||||
if (!prop.loc || !argument.start || !argument.end) return;
|
||||
// Replacements are anchored by the location of the call expression.
|
||||
// However, replacement text is meant to only replace the first argument.
|
||||
for (const replacement of replacements) {
|
||||
// In Babel, rows are 1-based, columns are 0-based.
|
||||
if (matcher.loc.start.line !== replacement.location.line) continue;
|
||||
if (matcher.loc.start.column + 1 !== replacement.location.column) continue;
|
||||
const indent = lines[matcher.loc.start.line - 1].match(/^\s*/)[0];
|
||||
if (prop.loc.start.line !== replacement.location.line) continue;
|
||||
if (prop.loc.start.column + 1 !== replacement.location.column) continue;
|
||||
const indent = lines[prop.loc.start.line - 1].match(/^\s*/)[0];
|
||||
const newText = replacement.code.replace(/\{indent\}/g, indent);
|
||||
ranges.push({
|
||||
start: matcher.start,
|
||||
end: node.end,
|
||||
oldText: source.substring(matcher.start, node.end),
|
||||
start: argument.start,
|
||||
end: argument.end,
|
||||
oldText: source.substring(argument.start, argument.end),
|
||||
newText
|
||||
});
|
||||
// We can have multiple, hopefully equal, replacements for the same location,
|
||||
@@ -81,15 +90,25 @@ async function applySuggestedRebaselines(config, reporter) {
|
||||
for (const range of ranges) result = result.substring(0, range.start) + range.newText + result.substring(range.end);
|
||||
const relativeName = _path.default.relative(process.cwd(), fileName);
|
||||
files.push(relativeName);
|
||||
patches.push(createPatch(relativeName, source, result));
|
||||
if (config.config.updateSourceMethod === 'overwrite') {
|
||||
await _fs.default.promises.writeFile(fileName, result);
|
||||
} else if (config.config.updateSourceMethod === '3way') {
|
||||
await _fs.default.promises.writeFile(fileName, applyPatchWithConflictMarkers(source, result));
|
||||
} else {
|
||||
const gitFolder = findGitRoot(_path.default.dirname(fileName), gitCache);
|
||||
const relativeToGit = _path.default.relative(gitFolder || process.cwd(), fileName);
|
||||
patches.push(createPatch(relativeToGit, source, result));
|
||||
}
|
||||
}
|
||||
const patchFile = _path.default.join(project.project.outputDir, 'rebaselines.patch');
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(patchFile), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(patchFile, patches.join('\n'));
|
||||
const fileList = files.map(file => ' ' + _utilsBundle.colors.dim(file)).join('\n');
|
||||
reporter.onStdErr(`\nNew baselines created for:\n\n${fileList}\n\n ` + _utilsBundle.colors.cyan('git apply ' + _path.default.relative(process.cwd(), patchFile)) + '\n');
|
||||
reporter.onStdErr(`\nNew baselines created for:\n\n${fileList}\n`);
|
||||
if (config.config.updateSourceMethod === 'patch') {
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(patchFile), {
|
||||
recursive: true
|
||||
});
|
||||
await _fs.default.promises.writeFile(patchFile, patches.join('\n'));
|
||||
reporter.onStdErr(`\n ` + _utilsBundle.colors.cyan('git apply ' + _path.default.relative(process.cwd(), patchFile)) + '\n');
|
||||
}
|
||||
}
|
||||
function createPatch(fileName, before, after) {
|
||||
const file = fileName.replace(/\\/g, '/');
|
||||
@@ -97,4 +116,53 @@ function createPatch(fileName, before, after) {
|
||||
context: 3
|
||||
});
|
||||
return ['diff --git a/' + file + ' b/' + file, '--- a/' + file, '+++ b/' + file, ...text.split('\n').slice(4)].join('\n');
|
||||
}
|
||||
function findGitRoot(dir, cache) {
|
||||
const result = cache.get(dir);
|
||||
if (result !== undefined) return result;
|
||||
const gitPath = _path.default.join(dir, '.git');
|
||||
if (_fs.default.existsSync(gitPath) && _fs.default.lstatSync(gitPath).isDirectory()) {
|
||||
cache.set(dir, dir);
|
||||
return dir;
|
||||
}
|
||||
const parentDir = _path.default.dirname(dir);
|
||||
if (dir === parentDir) {
|
||||
cache.set(dir, null);
|
||||
return null;
|
||||
}
|
||||
const parentResult = findGitRoot(parentDir, cache);
|
||||
cache.set(dir, parentResult);
|
||||
return parentResult;
|
||||
}
|
||||
function applyPatchWithConflictMarkers(oldText, newText) {
|
||||
const diffResult = _utilsBundle.diff.diffLines(oldText, newText);
|
||||
let result = '';
|
||||
let conflict = false;
|
||||
diffResult.forEach(part => {
|
||||
if (part.added) {
|
||||
if (conflict) {
|
||||
result += part.value;
|
||||
result += '>>>>>>> SNAPSHOT\n';
|
||||
conflict = false;
|
||||
} else {
|
||||
result += '<<<<<<< HEAD\n';
|
||||
result += part.value;
|
||||
result += '=======\n';
|
||||
conflict = true;
|
||||
}
|
||||
} else if (part.removed) {
|
||||
result += '<<<<<<< HEAD\n';
|
||||
result += part.value;
|
||||
result += '=======\n';
|
||||
conflict = true;
|
||||
} else {
|
||||
if (conflict) {
|
||||
result += '>>>>>>> SNAPSHOT\n';
|
||||
conflict = false;
|
||||
}
|
||||
result += part.value;
|
||||
}
|
||||
});
|
||||
if (conflict) result += '>>>>>>> SNAPSHOT\n';
|
||||
return result;
|
||||
}
|
||||
6
node_modules/playwright/lib/runner/reporters.js
generated
vendored
6
node_modules/playwright/lib/runner/reporters.js
generated
vendored
@@ -87,13 +87,13 @@ async function createReporterForTestServer(file, messageSink) {
|
||||
_send: messageSink
|
||||
}));
|
||||
}
|
||||
function createErrorCollectingReporter(writeToConsole) {
|
||||
function createErrorCollectingReporter(screen, writeToConsole) {
|
||||
const errors = [];
|
||||
return {
|
||||
version: () => 'v2',
|
||||
onError(error) {
|
||||
errors.push(error);
|
||||
if (writeToConsole) process.stdout.write((0, _base.formatError)(error, _base.colors.enabled).message + '\n');
|
||||
if (writeToConsole) process.stdout.write((0, _base.formatError)(screen, error).message + '\n');
|
||||
},
|
||||
errors: () => errors
|
||||
};
|
||||
@@ -147,6 +147,6 @@ class ListModeReporter {
|
||||
}
|
||||
onError(error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('\n' + (0, _base.formatError)(error, false).message);
|
||||
console.error('\n' + (0, _base.formatError)(_base.terminalScreen, error).message);
|
||||
}
|
||||
}
|
||||
7
node_modules/playwright/lib/runner/runner.js
generated
vendored
7
node_modules/playwright/lib/runner/runner.js
generated
vendored
@@ -11,6 +11,7 @@ var _tasks = require("./tasks");
|
||||
var _compilationCache = require("../transform/compilationCache");
|
||||
var _internalReporter = require("../reporters/internalReporter");
|
||||
var _lastRun = require("./lastRun");
|
||||
var _base = require("../reporters/base");
|
||||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
@@ -79,7 +80,7 @@ class Runner {
|
||||
return status;
|
||||
}
|
||||
async findRelatedTestFiles(files) {
|
||||
const errorReporter = (0, _reporters.createErrorCollectingReporter)();
|
||||
const errorReporter = (0, _reporters.createErrorCollectingReporter)(_base.terminalScreen);
|
||||
const reporter = new _internalReporter.InternalReporter([errorReporter]);
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(this._config, reporter), [...(0, _tasks.createPluginSetupTasks)(this._config), (0, _tasks.createLoadTask)('in-process', {
|
||||
failOnLoadErrors: true,
|
||||
@@ -95,7 +96,7 @@ class Runner {
|
||||
};
|
||||
}
|
||||
async runDevServer() {
|
||||
const reporter = new _internalReporter.InternalReporter([(0, _reporters.createErrorCollectingReporter)(true)]);
|
||||
const reporter = new _internalReporter.InternalReporter([(0, _reporters.createErrorCollectingReporter)(_base.terminalScreen, true)]);
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(this._config, reporter), [...(0, _tasks.createPluginSetupTasks)(this._config), (0, _tasks.createLoadTask)('in-process', {
|
||||
failOnLoadErrors: true,
|
||||
filterOnly: false
|
||||
@@ -108,7 +109,7 @@ class Runner {
|
||||
};
|
||||
}
|
||||
async clearCache() {
|
||||
const reporter = new _internalReporter.InternalReporter([(0, _reporters.createErrorCollectingReporter)(true)]);
|
||||
const reporter = new _internalReporter.InternalReporter([(0, _reporters.createErrorCollectingReporter)(_base.terminalScreen, true)]);
|
||||
const status = await (0, _tasks.runTasks)(new _tasks.TestRun(this._config, reporter), [...(0, _tasks.createPluginSetupTasks)(this._config), (0, _tasks.createClearCacheTask)(this._config)]);
|
||||
return {
|
||||
status
|
||||
|
||||
3
node_modules/playwright/lib/runner/tasks.js
generated
vendored
3
node_modules/playwright/lib/runner/tasks.js
generated
vendored
@@ -267,6 +267,9 @@ function createLoadTask(mode, options) {
|
||||
function createApplyRebaselinesTask() {
|
||||
return {
|
||||
title: 'apply rebaselines',
|
||||
setup: async () => {
|
||||
(0, _rebase.clearSuggestedRebaselines)();
|
||||
},
|
||||
teardown: async ({
|
||||
config,
|
||||
reporter
|
||||
|
||||
8
node_modules/playwright/lib/runner/testServer.js
generated
vendored
8
node_modules/playwright/lib/runner/testServer.js
generated
vendored
@@ -23,6 +23,7 @@ var _webServerPlugin = require("../plugins/webServerPlugin");
|
||||
var _util = require("../util");
|
||||
var _teleReceiver = require("../isomorphic/teleReceiver");
|
||||
var _internalReporter = require("../reporters/internalReporter");
|
||||
var _base = require("../reporters/base");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
@@ -353,6 +354,9 @@ class TestServerDispatcher {
|
||||
...(params.updateSnapshots ? {
|
||||
updateSnapshots: params.updateSnapshots
|
||||
} : {}),
|
||||
...(params.updateSourceMethod ? {
|
||||
updateSourceMethod: params.updateSourceMethod
|
||||
} : {}),
|
||||
...(params.workers ? {
|
||||
workers: params.workers
|
||||
} : {})
|
||||
@@ -400,7 +404,7 @@ class TestServerDispatcher {
|
||||
await this._updateWatcher(true);
|
||||
}
|
||||
async findRelatedTestFiles(params) {
|
||||
const errorReporter = (0, _reporters.createErrorCollectingReporter)();
|
||||
const errorReporter = (0, _reporters.createErrorCollectingReporter)(_base.internalScreen);
|
||||
const reporter = new _internalReporter.InternalReporter([errorReporter]);
|
||||
const config = await this._loadConfigOrReportError(reporter);
|
||||
if (!config) return {
|
||||
@@ -528,7 +532,7 @@ async function innerRunTestServer(configLocation, configCLIOverrides, options, o
|
||||
return sigintWatcher.hadSignal() ? 'interrupted' : 'passed';
|
||||
}
|
||||
function chunkToPayload(type, chunk) {
|
||||
if (chunk instanceof Buffer) return {
|
||||
if (chunk instanceof Uint8Array) return {
|
||||
type,
|
||||
buffer: chunk.toString('base64')
|
||||
};
|
||||
|
||||
8
node_modules/playwright/lib/runner/watchMode.js
generated
vendored
8
node_modules/playwright/lib/runner/watchMode.js
generated
vendored
@@ -330,7 +330,7 @@ function readCommand() {
|
||||
const name = key === null || key === void 0 ? void 0 : key.name;
|
||||
if (name === 'q') return 'exit';
|
||||
if (name === 'h') {
|
||||
process.stdout.write(`${(0, _base.separator)()}
|
||||
process.stdout.write(`${(0, _base.separator)(_base.terminalScreen)}
|
||||
Run tests
|
||||
${_utilsBundle.colors.bold('enter')} ${_utilsBundle.colors.dim('run tests')}
|
||||
${_utilsBundle.colors.bold('f')} ${_utilsBundle.colors.dim('run failed tests')}
|
||||
@@ -379,14 +379,14 @@ function printConfiguration(options, title) {
|
||||
if (title) tokens.push(_utilsBundle.colors.dim(`(${title})`));
|
||||
tokens.push(_utilsBundle.colors.dim(`#${seq++}`));
|
||||
const lines = [];
|
||||
const sep = (0, _base.separator)();
|
||||
const sep = (0, _base.separator)(_base.terminalScreen);
|
||||
lines.push('\x1Bc' + sep);
|
||||
lines.push(`${tokens.join(' ')}`);
|
||||
lines.push(`${_utilsBundle.colors.dim('Show & reuse browser:')} ${_utilsBundle.colors.bold(showBrowserServer ? 'on' : 'off')}`);
|
||||
process.stdout.write(lines.join('\n'));
|
||||
}
|
||||
function printBufferPrompt(dirtyTestFiles, rootDir) {
|
||||
const sep = (0, _base.separator)();
|
||||
const sep = (0, _base.separator)(_base.terminalScreen);
|
||||
process.stdout.write('\x1Bc');
|
||||
process.stdout.write(`${sep}\n`);
|
||||
if (dirtyTestFiles.size === 0) {
|
||||
@@ -398,7 +398,7 @@ function printBufferPrompt(dirtyTestFiles, rootDir) {
|
||||
process.stdout.write(`\n${_utilsBundle.colors.dim(`Press`)} ${_utilsBundle.colors.bold('enter')} ${_utilsBundle.colors.dim('to run')}, ${_utilsBundle.colors.bold('q')} ${_utilsBundle.colors.dim('to quit or')} ${_utilsBundle.colors.bold('h')} ${_utilsBundle.colors.dim('for more options.')}\n\n`);
|
||||
}
|
||||
function printPrompt() {
|
||||
const sep = (0, _base.separator)();
|
||||
const sep = (0, _base.separator)(_base.terminalScreen);
|
||||
process.stdout.write(`
|
||||
${sep}
|
||||
${_utilsBundle.colors.dim('Waiting for file changes. Press')} ${_utilsBundle.colors.bold('enter')} ${_utilsBundle.colors.dim('to run tests')}, ${_utilsBundle.colors.bold('q')} ${_utilsBundle.colors.dim('to quit or')} ${_utilsBundle.colors.bold('h')} ${_utilsBundle.colors.dim('for more options.')}
|
||||
|
||||
6
node_modules/playwright/lib/worker/fixtureRunner.js
generated
vendored
6
node_modules/playwright/lib/worker/fixtureRunner.js
generated
vendored
@@ -62,13 +62,14 @@ class Fixture {
|
||||
};
|
||||
}
|
||||
async setup(testInfo, runnable) {
|
||||
var _this$registration$cu;
|
||||
this.runner.instanceForId.set(this.registration.id, this);
|
||||
if (typeof this.registration.fn !== 'function') {
|
||||
this.value = this.registration.fn;
|
||||
return;
|
||||
}
|
||||
await testInfo._runAsStage({
|
||||
title: `fixture: ${this.registration.name}`,
|
||||
title: `fixture: ${(_this$registration$cu = this.registration.customTitle) !== null && _this$registration$cu !== void 0 ? _this$registration$cu : this.registration.name}`,
|
||||
runnable: {
|
||||
...runnable,
|
||||
fixture: this._setupDescription
|
||||
@@ -135,8 +136,9 @@ class Fixture {
|
||||
// Do not even start the teardown for a fixture that does not have any
|
||||
// time remaining in the time slot. This avoids cascading timeouts.
|
||||
if (!testInfo._timeoutManager.isTimeExhaustedFor(fixtureRunnable)) {
|
||||
var _this$registration$cu2;
|
||||
await testInfo._runAsStage({
|
||||
title: `fixture: ${this.registration.name}`,
|
||||
title: `fixture: ${(_this$registration$cu2 = this.registration.customTitle) !== null && _this$registration$cu2 !== void 0 ? _this$registration$cu2 : this.registration.name}`,
|
||||
runnable: fixtureRunnable,
|
||||
stepInfo: this._stepInfo
|
||||
}, async () => {
|
||||
|
||||
59
node_modules/playwright/lib/worker/testInfo.js
generated
vendored
59
node_modules/playwright/lib/worker/testInfo.js
generated
vendored
@@ -74,6 +74,7 @@ class TestInfoImpl {
|
||||
this._projectInternal = void 0;
|
||||
this._configInternal = void 0;
|
||||
this._steps = [];
|
||||
this._stepMap = new Map();
|
||||
this._onDidFinishTestFunction = void 0;
|
||||
this._hasNonRetriableError = false;
|
||||
this._hasUnhandledError = false;
|
||||
@@ -144,7 +145,10 @@ class TestInfoImpl {
|
||||
})();
|
||||
this._attachmentsPush = this.attachments.push.bind(this.attachments);
|
||||
this.attachments.push = (...attachments) => {
|
||||
for (const a of attachments) this._attach(a.name, a);
|
||||
for (const a of attachments) {
|
||||
var _this$_parentStep;
|
||||
this._attach(a, (_this$_parentStep = this._parentStep()) === null || _this$_parentStep === void 0 ? void 0 : _this$_parentStep.stepId);
|
||||
}
|
||||
return this.attachments.length;
|
||||
};
|
||||
this._tracing = new _testTracing.TestTracing(this, workerParams.artifactsDir);
|
||||
@@ -176,6 +180,10 @@ class TestInfoImpl {
|
||||
if (steps[i].isStage && !steps[i].endWallTime) return steps[i];
|
||||
}
|
||||
}
|
||||
_parentStep() {
|
||||
var _zones$zoneData;
|
||||
return (_zones$zoneData = _utils.zones.zoneData('stepZone')) !== null && _zones$zoneData !== void 0 ? _zones$zoneData : this._findLastStageStep(this._steps); // If no parent step on stack, assume the current stage as parent.
|
||||
}
|
||||
_addStep(data, parentStep) {
|
||||
var _parentStep, _parentStep2;
|
||||
const stepId = `${data.category}@${++this._lastStepId}`;
|
||||
@@ -183,11 +191,7 @@ class TestInfoImpl {
|
||||
// Predefined stages form a fixed hierarchy - use the current one as parent.
|
||||
parentStep = this._findLastStageStep(this._steps);
|
||||
} else {
|
||||
if (!parentStep) parentStep = _utils.zones.zoneData('stepZone');
|
||||
if (!parentStep) {
|
||||
// If no parent step on stack, assume the current stage as parent.
|
||||
parentStep = this._findLastStageStep(this._steps);
|
||||
}
|
||||
if (!parentStep) parentStep = this._parentStep();
|
||||
}
|
||||
const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());
|
||||
data.boxedStack = (_parentStep = parentStep) === null || _parentStep === void 0 ? void 0 : _parentStep.boxedStack;
|
||||
@@ -196,10 +200,12 @@ class TestInfoImpl {
|
||||
data.location = data.location || data.boxedStack[0];
|
||||
}
|
||||
data.location = data.location || filteredStack[0];
|
||||
const attachmentIndices = [];
|
||||
const step = {
|
||||
stepId,
|
||||
...data,
|
||||
steps: [],
|
||||
attachmentIndices,
|
||||
complete: result => {
|
||||
if (step.endWallTime) return;
|
||||
step.endWallTime = Date.now();
|
||||
@@ -235,11 +241,13 @@ class TestInfoImpl {
|
||||
message: step.error.message || '',
|
||||
stack: step.error.stack
|
||||
} : undefined;
|
||||
this._tracing.appendAfterActionForStep(stepId, errorForTrace, result.attachments);
|
||||
const attachments = attachmentIndices.map(i => this.attachments[i]);
|
||||
this._tracing.appendAfterActionForStep(stepId, errorForTrace, attachments);
|
||||
}
|
||||
};
|
||||
const parentStepList = parentStep ? parentStep.steps : this._steps;
|
||||
parentStepList.push(step);
|
||||
this._stepMap.set(stepId, step);
|
||||
const payload = {
|
||||
testId: this.testId,
|
||||
stepId,
|
||||
@@ -250,7 +258,7 @@ class TestInfoImpl {
|
||||
location: data.location
|
||||
};
|
||||
this._onStepBegin(payload);
|
||||
this._tracing.appendBeforeActionForStep(stepId, (_parentStep2 = parentStep) === null || _parentStep2 === void 0 ? void 0 : _parentStep2.stepId, data.apiName || data.title, data.params, data.location ? [data.location] : []);
|
||||
this._tracing.appendBeforeActionForStep(stepId, (_parentStep2 = parentStep) === null || _parentStep2 === void 0 ? void 0 : _parentStep2.stepId, data.category, data.apiName || data.title, data.params, data.location ? [data.location] : []);
|
||||
return step;
|
||||
}
|
||||
_interrupt() {
|
||||
@@ -330,24 +338,32 @@ class TestInfoImpl {
|
||||
// ------------ TestInfo methods ------------
|
||||
|
||||
async attach(name, options = {}) {
|
||||
this._attach(name, await (0, _util.normalizeAndSaveAttachment)(this.outputPath(), name, options));
|
||||
}
|
||||
_attach(name, attachment) {
|
||||
var _attachment$body;
|
||||
const step = this._addStep({
|
||||
title: `attach "${name}"`,
|
||||
category: 'attach'
|
||||
});
|
||||
this._attachmentsPush(attachment);
|
||||
this._attach(await (0, _util.normalizeAndSaveAttachment)(this.outputPath(), name, options), step.stepId);
|
||||
step.complete({});
|
||||
}
|
||||
_attach(attachment, stepId) {
|
||||
var _attachment$body;
|
||||
const index = this._attachmentsPush(attachment) - 1;
|
||||
if (stepId) {
|
||||
this._stepMap.get(stepId).attachmentIndices.push(index);
|
||||
} else {
|
||||
var _this$_findLastStageS;
|
||||
// trace viewer has no means of representing attachments outside of a step, so we create an artificial action
|
||||
const callId = `attach@${++this._lastStepId}`;
|
||||
this._tracing.appendBeforeActionForStep(callId, (_this$_findLastStageS = this._findLastStageStep(this._steps)) === null || _this$_findLastStageS === void 0 ? void 0 : _this$_findLastStageS.stepId, 'attach', `attach "${attachment.name}"`, undefined, []);
|
||||
this._tracing.appendAfterActionForStep(callId, undefined, [attachment]);
|
||||
}
|
||||
this._onAttach({
|
||||
testId: this.testId,
|
||||
name: attachment.name,
|
||||
contentType: attachment.contentType,
|
||||
path: attachment.path,
|
||||
body: (_attachment$body = attachment.body) === null || _attachment$body === void 0 ? void 0 : _attachment$body.toString('base64')
|
||||
});
|
||||
step.complete({
|
||||
attachments: [attachment]
|
||||
body: (_attachment$body = attachment.body) === null || _attachment$body === void 0 ? void 0 : _attachment$body.toString('base64'),
|
||||
stepId
|
||||
});
|
||||
}
|
||||
outputPath(...pathSegments) {
|
||||
@@ -367,15 +383,20 @@ class TestInfoImpl {
|
||||
const fullTitleWithoutSpec = this.titlePath.slice(1).join(' ');
|
||||
return (0, _utils.sanitizeForFilePath)((0, _util.trimLongString)(fullTitleWithoutSpec));
|
||||
}
|
||||
snapshotPath(...pathSegments) {
|
||||
_resolveSnapshotPath(template, defaultTemplate, pathSegments) {
|
||||
const subPath = _path.default.join(...pathSegments);
|
||||
const parsedSubPath = _path.default.parse(subPath);
|
||||
const relativeTestFilePath = _path.default.relative(this.project.testDir, this._requireFile);
|
||||
const parsedRelativeTestFilePath = _path.default.parse(relativeTestFilePath);
|
||||
const projectNamePathSegment = (0, _utils.sanitizeForFilePath)(this.project.name);
|
||||
const snapshotPath = (this._projectInternal.snapshotPathTemplate || '').replace(/\{(.)?testDir\}/g, '$1' + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, '$1' + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? '$1' + this.snapshotSuffix : '').replace(/\{(.)?testFileDir\}/g, '$1' + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, '$1' + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? '$1' + projectNamePathSegment : '').replace(/\{(.)?testName\}/g, '$1' + this._fsSanitizedTestName()).replace(/\{(.)?testFileName\}/g, '$1' + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, '$1' + relativeTestFilePath).replace(/\{(.)?arg\}/g, '$1' + _path.default.join(parsedSubPath.dir, parsedSubPath.name)).replace(/\{(.)?ext\}/g, parsedSubPath.ext ? '$1' + parsedSubPath.ext : '');
|
||||
const actualTemplate = template || this._projectInternal.snapshotPathTemplate || defaultTemplate;
|
||||
const snapshotPath = actualTemplate.replace(/\{(.)?testDir\}/g, '$1' + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, '$1' + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? '$1' + this.snapshotSuffix : '').replace(/\{(.)?testFileDir\}/g, '$1' + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, '$1' + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? '$1' + projectNamePathSegment : '').replace(/\{(.)?testName\}/g, '$1' + this._fsSanitizedTestName()).replace(/\{(.)?testFileName\}/g, '$1' + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, '$1' + relativeTestFilePath).replace(/\{(.)?arg\}/g, '$1' + _path.default.join(parsedSubPath.dir, parsedSubPath.name)).replace(/\{(.)?ext\}/g, parsedSubPath.ext ? '$1' + parsedSubPath.ext : '');
|
||||
return _path.default.normalize(_path.default.resolve(this._configInternal.configDir, snapshotPath));
|
||||
}
|
||||
snapshotPath(...pathSegments) {
|
||||
const legacyTemplate = '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}';
|
||||
return this._resolveSnapshotPath(undefined, legacyTemplate, pathSegments);
|
||||
}
|
||||
skip(...args) {
|
||||
this._modifier('skip', args);
|
||||
}
|
||||
|
||||
4
node_modules/playwright/lib/worker/testTracing.js
generated
vendored
4
node_modules/playwright/lib/worker/testTracing.js
generated
vendored
@@ -212,14 +212,14 @@ class TestTracing {
|
||||
base64: typeof chunk === 'string' ? undefined : chunk.toString('base64')
|
||||
});
|
||||
}
|
||||
appendBeforeActionForStep(callId, parentId, apiName, params, stack) {
|
||||
appendBeforeActionForStep(callId, parentId, category, apiName, params, stack) {
|
||||
this._appendTraceEvent({
|
||||
type: 'before',
|
||||
callId,
|
||||
parentId,
|
||||
startTime: (0, _utils.monotonicTime)(),
|
||||
class: 'Test',
|
||||
method: 'step',
|
||||
method: category,
|
||||
apiName,
|
||||
params: Object.fromEntries(Object.entries(params || {}).map(([name, value]) => [name, generatePreview(value)])),
|
||||
stack
|
||||
|
||||
6
node_modules/playwright/lib/worker/workerMain.js
generated
vendored
6
node_modules/playwright/lib/worker/workerMain.js
generated
vendored
@@ -71,17 +71,19 @@ class WorkerMain extends _process.ProcessRunner {
|
||||
this._runFinished.resolve();
|
||||
process.on('unhandledRejection', reason => this.unhandledError(reason));
|
||||
process.on('uncaughtException', error => this.unhandledError(error));
|
||||
process.stdout.write = chunk => {
|
||||
process.stdout.write = (chunk, cb) => {
|
||||
var _this$_currentTest;
|
||||
this.dispatchEvent('stdOut', (0, _ipc.stdioChunkToParams)(chunk));
|
||||
(_this$_currentTest = this._currentTest) === null || _this$_currentTest === void 0 || _this$_currentTest._tracing.appendStdioToTrace('stdout', chunk);
|
||||
if (typeof cb === 'function') process.nextTick(cb);
|
||||
return true;
|
||||
};
|
||||
if (!process.env.PW_RUNNER_DEBUG) {
|
||||
process.stderr.write = chunk => {
|
||||
process.stderr.write = (chunk, cb) => {
|
||||
var _this$_currentTest2;
|
||||
this.dispatchEvent('stdErr', (0, _ipc.stdioChunkToParams)(chunk));
|
||||
(_this$_currentTest2 = this._currentTest) === null || _this$_currentTest2 === void 0 || _this$_currentTest2._tracing.appendStdioToTrace('stderr', chunk);
|
||||
if (typeof cb === 'function') process.nextTick(cb);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
4
node_modules/playwright/package.json
generated
vendored
4
node_modules/playwright/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "playwright",
|
||||
"version": "1.49.1",
|
||||
"version": "1.50.1",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -56,7 +56,7 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.1"
|
||||
"playwright-core": "1.50.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
|
||||
448
node_modules/playwright/types/test.d.ts
generated
vendored
448
node_modules/playwright/types/test.d.ts
generated
vendored
@@ -214,6 +214,27 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
||||
* [page.screenshot([options])](https://playwright.dev/docs/api/class-page#page-screenshot).
|
||||
*/
|
||||
stylePath?: string|Array<string>;
|
||||
|
||||
/**
|
||||
* A template controlling location of the screenshots. See
|
||||
* [testProject.snapshotPathTemplate](https://playwright.dev/docs/api/class-testproject#test-project-snapshot-path-template)
|
||||
* for details.
|
||||
*/
|
||||
pathTemplate?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration for the
|
||||
* [expect(locator).toMatchAriaSnapshot([options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-match-aria-snapshot-2)
|
||||
* method.
|
||||
*/
|
||||
toMatchAriaSnapshot?: {
|
||||
/**
|
||||
* A template controlling location of the aria snapshots. See
|
||||
* [testProject.snapshotPathTemplate](https://playwright.dev/docs/api/class-testproject#test-project-snapshot-path-template)
|
||||
* for details.
|
||||
*/
|
||||
pathTemplate?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -404,10 +425,14 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
||||
|
||||
/**
|
||||
* This option configures a template controlling location of snapshots generated by
|
||||
* [expect(page).toHaveScreenshot(name[, options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1)
|
||||
* [expect(page).toHaveScreenshot(name[, options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1),
|
||||
* [expect(locator).toMatchAriaSnapshot([options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-match-aria-snapshot-2)
|
||||
* and
|
||||
* [expect(value).toMatchSnapshot(name[, options])](https://playwright.dev/docs/api/class-snapshotassertions#snapshot-assertions-to-match-snapshot-1).
|
||||
*
|
||||
* You can configure templates for each assertion separately in
|
||||
* [testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect).
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
@@ -416,7 +441,19 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
||||
*
|
||||
* export default defineConfig({
|
||||
* testDir: './tests',
|
||||
*
|
||||
* // Single template for all assertions
|
||||
* snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
||||
*
|
||||
* // Assertion-specific templates
|
||||
* expect: {
|
||||
* toHaveScreenshot: {
|
||||
* pathTemplate: '{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
|
||||
* },
|
||||
* toMatchAriaSnapshot: {
|
||||
* pathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}',
|
||||
* },
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
@@ -447,27 +484,27 @@ interface TestProject<TestArgs = {}, WorkerArgs = {}> {
|
||||
* ```
|
||||
*
|
||||
* The list of supported tokens:
|
||||
* - `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the
|
||||
* `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated
|
||||
* snapshot name.
|
||||
* - `{arg}` - Relative snapshot path **without extension**. This comes from the arguments passed to
|
||||
* `toHaveScreenshot()`, `toMatchAriaSnapshot()` or `toMatchSnapshot()`; if called without arguments, this will be
|
||||
* an auto-generated snapshot name.
|
||||
* - Value: `foo/bar/baz`
|
||||
* - `{ext}` - snapshot extension (with dots)
|
||||
* - `{ext}` - Snapshot extension (with the leading dot).
|
||||
* - Value: `.png`
|
||||
* - `{platform}` - The value of `process.platform`.
|
||||
* - `{projectName}` - Project's file-system-sanitized name, if any.
|
||||
* - Value: `''` (empty string).
|
||||
* - `{snapshotDir}` - Project's
|
||||
* [testConfig.snapshotDir](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-dir).
|
||||
* [testProject.snapshotDir](https://playwright.dev/docs/api/class-testproject#test-project-snapshot-dir).
|
||||
* - Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`)
|
||||
* - `{testDir}` - Project's
|
||||
* [testConfig.testDir](https://playwright.dev/docs/api/class-testconfig#test-config-test-dir).
|
||||
* - Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with
|
||||
* [testProject.testDir](https://playwright.dev/docs/api/class-testproject#test-project-test-dir).
|
||||
* - Value: `/home/playwright/tests` (absolute path since `testDir` is resolved relative to directory with
|
||||
* config)
|
||||
* - `{testFileDir}` - Directories in relative path from `testDir` to **test file**.
|
||||
* - Value: `page`
|
||||
* - `{testFileName}` - Test file name with extension.
|
||||
* - Value: `page-click.spec.ts`
|
||||
* - `{testFilePath}` - Relative path from `testDir` to **test file**
|
||||
* - `{testFilePath}` - Relative path from `testDir` to **test file**.
|
||||
* - Value: `page/page-click.spec.ts`
|
||||
* - `{testName}` - File-system-sanitized test title, including parent describes but excluding file name.
|
||||
* - Value: `suite-test-should-work`
|
||||
@@ -991,6 +1028,27 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
* [YIQ color space](https://en.wikipedia.org/wiki/YIQ) and defaults `threshold` value to `0.2`.
|
||||
*/
|
||||
threshold?: number;
|
||||
|
||||
/**
|
||||
* A template controlling location of the screenshots. See
|
||||
* [testConfig.snapshotPathTemplate](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-path-template)
|
||||
* for details.
|
||||
*/
|
||||
pathTemplate?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration for the
|
||||
* [expect(locator).toMatchAriaSnapshot([options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-match-aria-snapshot-2)
|
||||
* method.
|
||||
*/
|
||||
toMatchAriaSnapshot?: {
|
||||
/**
|
||||
* A template controlling location of the aria snapshots. See
|
||||
* [testConfig.snapshotPathTemplate](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-path-template)
|
||||
* for details.
|
||||
*/
|
||||
pathTemplate?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1468,10 +1526,14 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
|
||||
/**
|
||||
* This option configures a template controlling location of snapshots generated by
|
||||
* [expect(page).toHaveScreenshot(name[, options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1)
|
||||
* [expect(page).toHaveScreenshot(name[, options])](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1),
|
||||
* [expect(locator).toMatchAriaSnapshot([options])](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-match-aria-snapshot-2)
|
||||
* and
|
||||
* [expect(value).toMatchSnapshot(name[, options])](https://playwright.dev/docs/api/class-snapshotassertions#snapshot-assertions-to-match-snapshot-1).
|
||||
*
|
||||
* You can configure templates for each assertion separately in
|
||||
* [testConfig.expect](https://playwright.dev/docs/api/class-testconfig#test-config-expect).
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
@@ -1480,7 +1542,19 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
*
|
||||
* export default defineConfig({
|
||||
* testDir: './tests',
|
||||
*
|
||||
* // Single template for all assertions
|
||||
* snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
|
||||
*
|
||||
* // Assertion-specific templates
|
||||
* expect: {
|
||||
* toHaveScreenshot: {
|
||||
* pathTemplate: '{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
|
||||
* },
|
||||
* toMatchAriaSnapshot: {
|
||||
* pathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}',
|
||||
* },
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
@@ -1511,27 +1585,27 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
* ```
|
||||
*
|
||||
* The list of supported tokens:
|
||||
* - `{arg}` - Relative snapshot path **without extension**. These come from the arguments passed to the
|
||||
* `toHaveScreenshot()` and `toMatchSnapshot()` calls; if called without arguments, this will be an auto-generated
|
||||
* snapshot name.
|
||||
* - `{arg}` - Relative snapshot path **without extension**. This comes from the arguments passed to
|
||||
* `toHaveScreenshot()`, `toMatchAriaSnapshot()` or `toMatchSnapshot()`; if called without arguments, this will be
|
||||
* an auto-generated snapshot name.
|
||||
* - Value: `foo/bar/baz`
|
||||
* - `{ext}` - snapshot extension (with dots)
|
||||
* - `{ext}` - Snapshot extension (with the leading dot).
|
||||
* - Value: `.png`
|
||||
* - `{platform}` - The value of `process.platform`.
|
||||
* - `{projectName}` - Project's file-system-sanitized name, if any.
|
||||
* - Value: `''` (empty string).
|
||||
* - `{snapshotDir}` - Project's
|
||||
* [testConfig.snapshotDir](https://playwright.dev/docs/api/class-testconfig#test-config-snapshot-dir).
|
||||
* [testProject.snapshotDir](https://playwright.dev/docs/api/class-testproject#test-project-snapshot-dir).
|
||||
* - Value: `/home/playwright/tests` (since `snapshotDir` is not provided in config, it defaults to `testDir`)
|
||||
* - `{testDir}` - Project's
|
||||
* [testConfig.testDir](https://playwright.dev/docs/api/class-testconfig#test-config-test-dir).
|
||||
* - Value: `/home/playwright/tests` (absolute path is since `testDir` is resolved relative to directory with
|
||||
* [testProject.testDir](https://playwright.dev/docs/api/class-testproject#test-project-test-dir).
|
||||
* - Value: `/home/playwright/tests` (absolute path since `testDir` is resolved relative to directory with
|
||||
* config)
|
||||
* - `{testFileDir}` - Directories in relative path from `testDir` to **test file**.
|
||||
* - Value: `page`
|
||||
* - `{testFileName}` - Test file name with extension.
|
||||
* - Value: `page-click.spec.ts`
|
||||
* - `{testFilePath}` - Relative path from `testDir` to **test file**
|
||||
* - `{testFilePath}` - Relative path from `testDir` to **test file**.
|
||||
* - Value: `page/page-click.spec.ts`
|
||||
* - `{testName}` - File-system-sanitized test title, including parent describes but excluding file name.
|
||||
* - Value: `suite-test-should-work`
|
||||
@@ -1665,11 +1739,12 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
|
||||
/**
|
||||
* Whether to update expected snapshots with the actual results produced by the test run. Defaults to `'missing'`.
|
||||
* - `'all'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be
|
||||
* updated.
|
||||
* - `'none'` - No snapshots are updated.
|
||||
* - `'all'` - All tests that are executed will update snapshots.
|
||||
* - `'changed'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not
|
||||
* be updated.
|
||||
* - `'missing'` - Missing snapshots are created, for example when authoring a new test and running it for the first
|
||||
* time. This is the default.
|
||||
* - `'none'` - No snapshots are updated.
|
||||
*
|
||||
* Learn more about [snapshots](https://playwright.dev/docs/test-snapshots).
|
||||
*
|
||||
@@ -1685,7 +1760,16 @@ interface TestConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
updateSnapshots?: "all"|"none"|"missing";
|
||||
updateSnapshots?: "all"|"changed"|"missing"|"none";
|
||||
|
||||
/**
|
||||
* Defines how to update snapshots in the source code.
|
||||
* - `'patch'` - Create a unified diff file that can be used to update the source code later. This is the default.
|
||||
* - `'3way'` - Generate merge conflict markers in source code. This allows user to manually pick relevant changes,
|
||||
* as if they are resolving a merge conflict in the IDE.
|
||||
* - `'overwrite'` - Overwrite the source code with the new snapshot values.
|
||||
*/
|
||||
updateSourceMethod?: "overwrite"|"3way"|"patch";
|
||||
|
||||
/**
|
||||
* The maximum number of concurrent worker processes to use for parallelizing tests. Can also be set as percentage of
|
||||
@@ -1834,7 +1918,13 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
/**
|
||||
* See [testConfig.updateSnapshots](https://playwright.dev/docs/api/class-testconfig#test-config-update-snapshots).
|
||||
*/
|
||||
updateSnapshots: "all"|"none"|"missing";
|
||||
updateSnapshots: "all"|"changed"|"missing"|"none";
|
||||
|
||||
/**
|
||||
* See
|
||||
* [testConfig.updateSourceMethod](https://playwright.dev/docs/api/class-testconfig#test-config-update-source-method).
|
||||
*/
|
||||
updateSourceMethod: "overwrite"|"3way"|"patch";
|
||||
|
||||
/**
|
||||
* Playwright version.
|
||||
@@ -1849,7 +1939,7 @@ export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
|
||||
|
||||
export type TestStatus = 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
|
||||
|
||||
type TestDetailsAnnotation = {
|
||||
export type TestDetailsAnnotation = {
|
||||
type: string;
|
||||
description?: string;
|
||||
};
|
||||
@@ -1876,7 +1966,7 @@ type ConditionBody<TestArgs> = (args: TestArgs) => boolean;
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue> {
|
||||
export interface TestType<TestArgs extends {}, WorkerArgs extends {}> {
|
||||
/**
|
||||
* Declares a test.
|
||||
* - `test(title, body)`
|
||||
@@ -5536,7 +5626,191 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||
* @param body Step body.
|
||||
* @param options
|
||||
*/
|
||||
step<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location }): Promise<T>;
|
||||
step: {
|
||||
/**
|
||||
* Declares a test step that is shown in the report.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
* import { test, expect } from '@playwright/test';
|
||||
*
|
||||
* test('test', async ({ page }) => {
|
||||
* await test.step('Log in', async () => {
|
||||
* // ...
|
||||
* });
|
||||
*
|
||||
* await test.step('Outer step', async () => {
|
||||
* // ...
|
||||
* // You can nest steps inside each other.
|
||||
* await test.step('Inner step', async () => {
|
||||
* // ...
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* **Details**
|
||||
*
|
||||
* The method returns the value returned by the step callback.
|
||||
*
|
||||
* ```js
|
||||
* import { test, expect } from '@playwright/test';
|
||||
*
|
||||
* test('test', async ({ page }) => {
|
||||
* const user = await test.step('Log in', async () => {
|
||||
* // ...
|
||||
* return 'john';
|
||||
* });
|
||||
* expect(user).toBe('john');
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* **Decorator**
|
||||
*
|
||||
* You can use TypeScript method decorators to turn a method into a step. Each call to the decorated method will show
|
||||
* up as a step in the report.
|
||||
*
|
||||
* ```js
|
||||
* function step(target: Function, context: ClassMethodDecoratorContext) {
|
||||
* return function replacementMethod(...args: any) {
|
||||
* const name = this.constructor.name + '.' + (context.name as string);
|
||||
* return test.step(name, async () => {
|
||||
* return await target.call(this, ...args);
|
||||
* });
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* class LoginPage {
|
||||
* constructor(readonly page: Page) {}
|
||||
*
|
||||
* @step
|
||||
* async login() {
|
||||
* const account = { username: 'Alice', password: 's3cr3t' };
|
||||
* await this.page.getByLabel('Username or email address').fill(account.username);
|
||||
* await this.page.getByLabel('Password').fill(account.password);
|
||||
* await this.page.getByRole('button', { name: 'Sign in' }).click();
|
||||
* await expect(this.page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* test('example', async ({ page }) => {
|
||||
* const loginPage = new LoginPage(page);
|
||||
* await loginPage.login();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* **Boxing**
|
||||
*
|
||||
* When something inside a step fails, you would usually see the error pointing to the exact action that failed. For
|
||||
* example, consider the following login step:
|
||||
*
|
||||
* ```js
|
||||
* async function login(page) {
|
||||
* await test.step('login', async () => {
|
||||
* const account = { username: 'Alice', password: 's3cr3t' };
|
||||
* await page.getByLabel('Username or email address').fill(account.username);
|
||||
* await page.getByLabel('Password').fill(account.password);
|
||||
* await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
* await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* test('example', async ({ page }) => {
|
||||
* await page.goto('https://github.com/login');
|
||||
* await login(page);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ```txt
|
||||
* Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
|
||||
* ... error details omitted ...
|
||||
*
|
||||
* 8 | await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
* > 9 | await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
* | ^
|
||||
* 10 | });
|
||||
* ```
|
||||
*
|
||||
* As we see above, the test may fail with an error pointing inside the step. If you would like the error to highlight
|
||||
* the "login" step instead of its internals, use the `box` option. An error inside a boxed step points to the step
|
||||
* call site.
|
||||
*
|
||||
* ```js
|
||||
* async function login(page) {
|
||||
* await test.step('login', async () => {
|
||||
* // ...
|
||||
* }, { box: true }); // Note the "box" option here.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```txt
|
||||
* Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
|
||||
* ... error details omitted ...
|
||||
*
|
||||
* 14 | await page.goto('https://github.com/login');
|
||||
* > 15 | await login(page);
|
||||
* | ^
|
||||
* 16 | });
|
||||
* ```
|
||||
*
|
||||
* You can also create a TypeScript decorator for a boxed step, similar to a regular step decorator above:
|
||||
*
|
||||
* ```js
|
||||
* function boxedStep(target: Function, context: ClassMethodDecoratorContext) {
|
||||
* return function replacementMethod(...args: any) {
|
||||
* const name = this.constructor.name + '.' + (context.name as string);
|
||||
* return test.step(name, async () => {
|
||||
* return await target.call(this, ...args);
|
||||
* }, { box: true }); // Note the "box" option here.
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* class LoginPage {
|
||||
* constructor(readonly page: Page) {}
|
||||
*
|
||||
* @boxedStep
|
||||
* async login() {
|
||||
* // ....
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* test('example', async ({ page }) => {
|
||||
* const loginPage = new LoginPage(page);
|
||||
* await loginPage.login(); // <-- Error will be reported on this line.
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param title Step name.
|
||||
* @param body Step body.
|
||||
* @param options
|
||||
*/
|
||||
<T>(title: string, body: () => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number }): Promise<T>;
|
||||
/**
|
||||
* Mark a test step as "skip" to temporarily disable its execution, useful for steps that are currently failing and
|
||||
* planned for a near-term fix. Playwright will not run the step.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* You can declare a skipped step, and Playwright will not run it.
|
||||
*
|
||||
* ```js
|
||||
* import { test, expect } from '@playwright/test';
|
||||
*
|
||||
* test('my test', async ({ page }) => {
|
||||
* // ...
|
||||
* await test.step.skip('not yet ready', async () => {
|
||||
* // ...
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param title Step name.
|
||||
* @param body Step body.
|
||||
* @param options
|
||||
*/
|
||||
skip(title: string, body: () => any | Promise<any>, options?: { box?: boolean, location?: Location, timeout?: number }): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions).
|
||||
*
|
||||
@@ -5617,7 +5891,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||
* Learn more about [fixtures](https://playwright.dev/docs/test-fixtures) and [parametrizing tests](https://playwright.dev/docs/test-parameterize).
|
||||
* @param fixtures An object containing fixtures and/or options. Learn more about [fixtures format](https://playwright.dev/docs/test-fixtures).
|
||||
*/
|
||||
extend<T extends KeyValue, W extends KeyValue = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestType<TestArgs & T, WorkerArgs & W>;
|
||||
extend<T extends {}, W extends {} = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestType<TestArgs & T, WorkerArgs & W>;
|
||||
/**
|
||||
* Returns information about the currently running test. This method can only be called during the test execution,
|
||||
* otherwise it throws.
|
||||
@@ -5638,19 +5912,18 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||
info(): TestInfo;
|
||||
}
|
||||
|
||||
type KeyValue = { [key: string]: any };
|
||||
export type TestFixture<R, Args extends KeyValue> = (args: Args, use: (r: R) => Promise<void>, testInfo: TestInfo) => any;
|
||||
export type WorkerFixture<R, Args extends KeyValue> = (args: Args, use: (r: R) => Promise<void>, workerInfo: WorkerInfo) => any;
|
||||
type TestFixtureValue<R, Args extends KeyValue> = Exclude<R, Function> | TestFixture<R, Args>;
|
||||
type WorkerFixtureValue<R, Args extends KeyValue> = Exclude<R, Function> | WorkerFixture<R, Args>;
|
||||
export type Fixtures<T extends KeyValue = {}, W extends KeyValue = {}, PT extends KeyValue = {}, PW extends KeyValue = {}> = {
|
||||
export type TestFixture<R, Args extends {}> = (args: Args, use: (r: R) => Promise<void>, testInfo: TestInfo) => any;
|
||||
export type WorkerFixture<R, Args extends {}> = (args: Args, use: (r: R) => Promise<void>, workerInfo: WorkerInfo) => any;
|
||||
type TestFixtureValue<R, Args extends {}> = Exclude<R, Function> | TestFixture<R, Args>;
|
||||
type WorkerFixtureValue<R, Args extends {}> = Exclude<R, Function> | WorkerFixture<R, Args>;
|
||||
export type Fixtures<T extends {} = {}, W extends {} = {}, PT extends {} = {}, PW extends {} = {}> = {
|
||||
[K in keyof PW]?: WorkerFixtureValue<PW[K], W & PW> | [WorkerFixtureValue<PW[K], W & PW>, { scope: 'worker', timeout?: number | undefined, title?: string, box?: boolean }];
|
||||
} & {
|
||||
[K in keyof PT]?: TestFixtureValue<PT[K], T & W & PT & PW> | [TestFixtureValue<PT[K], T & W & PT & PW>, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }];
|
||||
} & {
|
||||
[K in keyof W]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||
[K in Exclude<keyof W, keyof PW | keyof PT>]?: [WorkerFixtureValue<W[K], W & PW>, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||
} & {
|
||||
[K in keyof T]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||
[K in Exclude<keyof T, keyof PW | keyof PT>]?: TestFixtureValue<T[K], T & W & PT & PW> | [TestFixtureValue<T[K], T & W & PT & PW>, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }];
|
||||
};
|
||||
|
||||
type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||
@@ -5778,7 +6051,7 @@ export interface PlaywrightWorkerOptions {
|
||||
/**
|
||||
* Browser distribution channel.
|
||||
*
|
||||
* Use "chromium" to [opt in to new headless mode](https://playwright.dev/docs/browsers#opt-in-to-new-headless-mode).
|
||||
* Use "chromium" to [opt in to new headless mode](https://playwright.dev/docs/browsers#chromium-new-headless-mode).
|
||||
*
|
||||
* Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or
|
||||
* "msedge-canary" to use branded [Google Chrome and Microsoft Edge](https://playwright.dev/docs/browsers#google-chrome--microsoft-edge).
|
||||
@@ -6232,8 +6505,8 @@ export interface PlaywrightTestOptions {
|
||||
javaScriptEnabled: boolean;
|
||||
/**
|
||||
* Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value,
|
||||
* `Accept-Language` request header value as well as number and date formatting rules. Defaults to the system default
|
||||
* locale. Learn more about emulation in our [emulation guide](https://playwright.dev/docs/emulation#locale--timezone).
|
||||
* `Accept-Language` request header value as well as number and date formatting rules. Defaults to `en-US`. Learn more
|
||||
* about emulation in our [emulation guide](https://playwright.dev/docs/emulation#locale--timezone).
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
@@ -7472,8 +7745,8 @@ export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig
|
||||
export function defineConfig<T>(config: PlaywrightTestConfig<T>): PlaywrightTestConfig<T>;
|
||||
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>): PlaywrightTestConfig<T, W>;
|
||||
export function defineConfig(config: PlaywrightTestConfig, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig;
|
||||
export function defineConfig<T>(config: PlaywrightTestConfig<T>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T>;
|
||||
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T, W>;
|
||||
export function defineConfig<T>(config: PlaywrightTestConfig<T>, ...configs: PlaywrightTestConfig<T>[]): PlaywrightTestConfig<T>;
|
||||
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>, ...configs: PlaywrightTestConfig<T, W>[]): PlaywrightTestConfig<T, W>;
|
||||
|
||||
type MergedT<List> = List extends [TestType<infer T, any>, ...(infer Rest)] ? T & MergedT<Rest> : {};
|
||||
type MergedW<List> = List extends [TestType<any, infer W>, ...(infer Rest)] ? W & MergedW<Rest> : {};
|
||||
@@ -7589,8 +7862,21 @@ interface LocatorAssertions {
|
||||
* @param options
|
||||
*/
|
||||
toBeChecked(options?: {
|
||||
/**
|
||||
* Provides state to assert for. Asserts for input to be checked by default. This option can't be used when
|
||||
* [`indeterminate`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-checked-option-indeterminate)
|
||||
* is set to true.
|
||||
*/
|
||||
checked?: boolean;
|
||||
|
||||
/**
|
||||
* Asserts that the element is in the indeterminate (mixed) state. Only supported for checkboxes and radio buttons.
|
||||
* This option can't be true when
|
||||
* [`checked`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-checked-option-checked)
|
||||
* is provided.
|
||||
*/
|
||||
indeterminate?: boolean;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
|
||||
*/
|
||||
@@ -7888,6 +8174,34 @@ interface LocatorAssertions {
|
||||
timeout?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with a given
|
||||
* [aria errormessage](https://w3c.github.io/aria/#aria-errormessage).
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
* const locator = page.getByTestId('username-input');
|
||||
* await expect(locator).toHaveAccessibleErrorMessage('Username is required.');
|
||||
* ```
|
||||
*
|
||||
* @param errorMessage Expected accessible error message.
|
||||
* @param options
|
||||
*/
|
||||
toHaveAccessibleErrorMessage(errorMessage: string|RegExp, options?: {
|
||||
/**
|
||||
* Whether to perform case-insensitive match.
|
||||
* [`ignoreCase`](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-accessible-error-message-option-ignore-case)
|
||||
* option takes precedence over the corresponding regular expression flag if specified.
|
||||
*/
|
||||
ignoreCase?: boolean;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with a given
|
||||
* [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
|
||||
@@ -7967,21 +8281,24 @@ interface LocatorAssertions {
|
||||
|
||||
/**
|
||||
* Ensures the [Locator](https://playwright.dev/docs/api/class-locator) points to an element with given CSS classes.
|
||||
* This needs to be a full match or using a relaxed regular expression.
|
||||
* When a string is provided, it must fully match the element's `class` attribute. To match individual classes or
|
||||
* perform partial matches, use a regular expression:
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```html
|
||||
* <div class='selected row' id='component'></div>
|
||||
* <div class='middle selected row' id='component'></div>
|
||||
* ```
|
||||
*
|
||||
* ```js
|
||||
* const locator = page.locator('#component');
|
||||
* await expect(locator).toHaveClass(/selected/);
|
||||
* await expect(locator).toHaveClass('selected row');
|
||||
* await expect(locator).toHaveClass('middle selected row');
|
||||
* await expect(locator).toHaveClass(/(^|\s)selected(\s|$)/);
|
||||
* ```
|
||||
*
|
||||
* Note that if array is passed as an expected value, entire lists of elements can be asserted:
|
||||
* When an array is passed, the method asserts that the list of elements located matches the corresponding list of
|
||||
* expected class values. Each element's class attribute is matched against the corresponding string or regular
|
||||
* expression in the array:
|
||||
*
|
||||
* ```js
|
||||
* const locator = page.locator('list > .component');
|
||||
@@ -8439,6 +8756,34 @@ interface LocatorAssertions {
|
||||
timeout?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Asserts that the target element matches the given [accessibility snapshot](https://playwright.dev/docs/aria-snapshots).
|
||||
*
|
||||
* Snapshot is stored in a separate `.yml` file in a location configured by `expect.toMatchAriaSnapshot.pathTemplate`
|
||||
* and/or `snapshotPathTemplate` properties in the configuration file.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
* ```js
|
||||
* await expect(page.locator('body')).toMatchAriaSnapshot();
|
||||
* await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'body.yml' });
|
||||
* ```
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
toMatchAriaSnapshot(options?: {
|
||||
/**
|
||||
* Name of the snapshot to store in the snapshot folder corresponding to this test. Generates sequential names if not
|
||||
* specified.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
|
||||
*/
|
||||
timeout?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the Locator doesn't contain
|
||||
* text `"error"`:
|
||||
@@ -9374,6 +9719,19 @@ interface TestConfigWebServer {
|
||||
*/
|
||||
timeout?: number;
|
||||
|
||||
/**
|
||||
* How to shut down the process. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal:
|
||||
* 'SIGTERM', timeout: 500 }`, the process group is sent a `SIGTERM` signal, followed by `SIGKILL` if it doesn't exit
|
||||
* within 500ms. You can also use `SIGINT` as the signal instead. A `0` timeout means no `SIGKILL` will be sent.
|
||||
* Windows doesn't support `SIGTERM` and `SIGINT` signals, so this option is ignored on Windows. Note that shutting
|
||||
* down a Docker container requires `SIGTERM`.
|
||||
*/
|
||||
gracefulShutdown?: {
|
||||
signal: "SIGINT"|"SIGTERM";
|
||||
|
||||
timeout: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* The url on your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the
|
||||
* server is ready to accept connections. Redirects (3xx status codes) are being followed and the new location is
|
||||
|
||||
27
node_modules/playwright/types/testReporter.d.ts
generated
vendored
27
node_modules/playwright/types/testReporter.d.ts
generated
vendored
@@ -691,6 +691,33 @@ export interface TestStep {
|
||||
*/
|
||||
titlePath(): Array<string>;
|
||||
|
||||
/**
|
||||
* The list of files or buffers attached in the step execution through
|
||||
* [testInfo.attach(name[, options])](https://playwright.dev/docs/api/class-testinfo#test-info-attach).
|
||||
*/
|
||||
attachments: Array<{
|
||||
/**
|
||||
* Attachment name.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Content type of this attachment to properly present in the report, for example `'application/json'` or
|
||||
* `'image/png'`.
|
||||
*/
|
||||
contentType: string;
|
||||
|
||||
/**
|
||||
* Optional path on the filesystem to the attached file.
|
||||
*/
|
||||
path?: string;
|
||||
|
||||
/**
|
||||
* Optional attachment body used instead of a file.
|
||||
*/
|
||||
body?: Buffer;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Step category to differentiate steps with different origin and verbosity. Built-in categories are:
|
||||
* - `hook` for fixtures and hooks initialization and teardown
|
||||
|
||||
Reference in New Issue
Block a user