test: convert some tests to the jest+fixtures (#2983)
This commit is contained in:
parent
1896e8edc0
commit
424f11d165
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
# XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR
|
||||
# Wrap `npm run` in a subshell to redirect STDERR to file.
|
||||
# Enable core dumps in the subshell.
|
||||
- run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run coverage 2>./testrun.log"
|
||||
- run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run test 2>./testrun.log"
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
DEBUG: "*,-pw:wrapped*"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ function isDate(obj: any): obj is Date {
|
|||
return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]';
|
||||
}
|
||||
|
||||
function isError(obj: any): obj is Error {
|
||||
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
||||
}
|
||||
|
||||
export function parseEvaluationResultValue(value: any, handles: any[] = []): any {
|
||||
if (value === undefined)
|
||||
return undefined;
|
||||
|
|
@ -85,7 +89,7 @@ function serialize(value: any, jsHandleSerializer: (value: any) => { fallThrough
|
|||
if (isPrimitiveValue(value))
|
||||
return value;
|
||||
|
||||
if (value instanceof Error) {
|
||||
if (isError(value)) {
|
||||
const error = value;
|
||||
if ('captureStackTrace' in global.Error) {
|
||||
// v8
|
||||
|
|
|
|||
|
|
@ -132,6 +132,10 @@ class Helper {
|
|||
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||
}
|
||||
|
||||
static isError(obj: any): obj is Error {
|
||||
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
||||
}
|
||||
|
||||
static isObject(obj: any): obj is NonNullable<object> {
|
||||
return typeof obj === 'object' && obj !== null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -693,7 +693,7 @@ export class PageBinding {
|
|||
const result = await binding!.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, ...args);
|
||||
context.evaluateInternal(deliverResult, { name, seq, result }).catch(logError(page._logger));
|
||||
} catch (error) {
|
||||
if (error instanceof Error)
|
||||
if (helper.isError(error))
|
||||
context.evaluateInternal(deliverError, { name, seq, message: error.message, stack: error.stack }).catch(logError(page._logger));
|
||||
else
|
||||
context.evaluateInternal(deliverErrorValue, { name, seq, error }).catch(logError(page._logger));
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { helper, assert } from '../helper';
|
|||
|
||||
|
||||
export function serializeError(e: any): types.Error {
|
||||
if (e instanceof Error)
|
||||
if (helper.isError(e))
|
||||
return { message: e.message, stack: e.stack, name: e.name };
|
||||
return { value: e };
|
||||
}
|
||||
|
|
|
|||
20
test/__snapshots__/coverage.jest.js.snap
Normal file
20
test/__snapshots__/coverage.jest.js.snap
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CSSCoverage should work with complicated usecases 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"url\\": \\"http://localhost:<PORT>/csscoverage/involved.html\\",
|
||||
\\"ranges\\": [
|
||||
{
|
||||
\\"start\\": 149,
|
||||
\\"end\\": 297
|
||||
},
|
||||
{
|
||||
\\"start\\": 327,
|
||||
\\"end\\": 433
|
||||
}
|
||||
],
|
||||
\\"text\\": \\"\\\\n@charset \\\\\\"utf-8\\\\\\";\\\\n@namespace svg url(http://www.w3.org/2000/svg);\\\\n@font-face {\\\\n font-family: \\\\\\"Example Font\\\\\\";\\\\n src: url(\\\\\\"./Dosis-Regular.ttf\\\\\\");\\\\n}\\\\n\\\\n#fluffy {\\\\n border: 1px solid black;\\\\n z-index: 1;\\\\n /* -webkit-disabled-property: rgb(1, 2, 3) */\\\\n -lol-cats: \\\\\\"dogs\\\\\\" /* non-existing property */\\\\n}\\\\n\\\\n@media (min-width: 1px) {\\\\n span {\\\\n -webkit-border-radius: 10px;\\\\n font-family: \\\\\\"Example Font\\\\\\";\\\\n animation: 1s identifier;\\\\n }\\\\n}\\\\n\\"
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
describe('Accessibility', function() {
|
||||
it('should work', async function({page}) {
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Auto waiting', () => {
|
||||
it('should await navigation when clicking anchor', async({page, server}) => {
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, HEADLESS, USES_HOOKS} = utils.testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, HEADLESS, USES_HOOKS} = testOptions;
|
||||
|
||||
async function giveItAChanceToClick(page) {
|
||||
for (let i = 0; i < 5; i++)
|
||||
|
|
@ -251,7 +251,7 @@ describe('Page.click', function() {
|
|||
await page.click('label[for="agree"]');
|
||||
expect(await page.evaluate(() => result.check)).toBe(false);
|
||||
});
|
||||
it('should not hang with touch-enabled viewports', async({server, browser}) => {
|
||||
it('should not hang with touch-enabled viewports', async({browser, playwright}) => {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/161
|
||||
const { viewport, hasTouch } = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ viewport, hasTouch });
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN} = testOptions;
|
||||
|
||||
describe('BrowserContext.cookies', function() {
|
||||
it('should return no cookies in pristine browser context', async({context, page, server}) => {
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
describe.skip(CHROMIUM)('Page.coverage missing', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
|
|
@ -141,11 +141,11 @@ describe.skip(!CHROMIUM)('CSSCoverage', function() {
|
|||
{start: 17, end: 38}
|
||||
]);
|
||||
});
|
||||
it('should work with complicated usecases', async function({page, server, golden}) {
|
||||
it('should work with complicated usecases', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/involved.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toBeGolden(golden('csscoverage-involved.txt'));
|
||||
expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toMatchSnapshot();
|
||||
});
|
||||
it('should ignore injected stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
const utils = require('./utils');
|
||||
const path = require('path');
|
||||
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, CHANNEL} = testOptions;
|
||||
|
||||
describe('Page.evaluate', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
|
|
@ -356,7 +357,7 @@ describe('Page.evaluate', function() {
|
|||
});
|
||||
expect(result).toEqual([42]);
|
||||
});
|
||||
(WEBKIT ? it.skip : it)('should not throw an error when evaluation does a synchronous navigation and returns an object', async({page, server}) => {
|
||||
it.fail(WEBKIT)('should not throw an error when evaluation does a synchronous navigation and returns an object', async({page, server}) => {
|
||||
// It is imporant to be on about:blank for sync reload.
|
||||
const result = await page.evaluate(() => {
|
||||
window.location.reload();
|
||||
|
|
@ -372,7 +373,7 @@ describe('Page.evaluate', function() {
|
|||
});
|
||||
expect(result).toBe(undefined);
|
||||
});
|
||||
(CHANNEL ? it.skip : it)('should transfer 100Mb of data from page to node.js', async({page}) => {
|
||||
it.skip(CHANNEL)('should transfer 100Mb of data from page to node.js', async({page}) => {
|
||||
const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a'));
|
||||
expect(a.length).toBe(100 * 1024 * 1024);
|
||||
});
|
||||
|
|
@ -388,7 +389,7 @@ describe('Page.evaluate', function() {
|
|||
const result = await page.evaluate(() => ({abc: 123}));
|
||||
expect(result).toEqual({abc: 123});
|
||||
});
|
||||
(FFOX ? it.skip : it)('should await promise from popup', async ({page, server}) => {
|
||||
it.fail(FFOX)('should await promise from popup', async ({page, server}) => {
|
||||
// Something is wrong about the way Firefox waits for the chained promise
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const result = await page.evaluate(() => {
|
||||
|
|
@ -573,14 +574,14 @@ describe('Frame.evaluate', function() {
|
|||
else
|
||||
expect(pageImpl._delegate._contextIdToContext.size).toBe(count);
|
||||
}
|
||||
(USES_HOOKS ? it.skip : it)('should dispose context on navigation', async({page, server, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should dispose context on navigation', async({page, server, toImpl}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(toImpl(page), 4);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expectContexts(toImpl(page), 2);
|
||||
});
|
||||
(USES_HOOKS ? it.skip : it)('should dispose context on cross-origin navigation', async({page, server, toImpl}) => {
|
||||
it.skip(USES_HOOKS)('should dispose context on cross-origin navigation', async({page, server, toImpl}) => {
|
||||
await page.goto(server.PREFIX + '/frames/one-frame.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expectContexts(toImpl(page), 4);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const { setUseApiName } = require('../../lib/progress');
|
|||
|
||||
module.exports = function registerFixtures(global) {
|
||||
|
||||
global.registerWorkerFixture('server', async ({}, test) => {
|
||||
global.registerWorkerFixture('http_server', async ({}, test) => {
|
||||
const assetsPath = path.join(__dirname, '..', 'assets');
|
||||
const cachedPath = path.join(__dirname, '..', 'assets', 'cached');
|
||||
|
||||
|
|
@ -40,14 +40,14 @@ module.exports = function registerFixtures(global) {
|
|||
server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
|
||||
|
||||
const httpsPort = port + 1;
|
||||
httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
|
||||
const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
|
||||
httpsServer.enableHTTPCache(cachedPath);
|
||||
httpsServer.PORT = httpsPort;
|
||||
httpsServer.PREFIX = `https://localhost:${httpsPort}`;
|
||||
httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
|
||||
httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
|
||||
|
||||
await test(server);
|
||||
await test({server, httpsServer});
|
||||
|
||||
await Promise.all([
|
||||
server.stop(),
|
||||
|
|
@ -55,6 +55,14 @@ module.exports = function registerFixtures(global) {
|
|||
]);
|
||||
});
|
||||
|
||||
global.registerWorkerFixture('defaultBrowserOptions', async({}, test) => {
|
||||
await test({
|
||||
handleSIGINT: false,
|
||||
slowMo: valueFromEnv('SLOW_MO', 0),
|
||||
headless: !!valueFromEnv('HEADLESS', true),
|
||||
});
|
||||
});
|
||||
|
||||
global.registerWorkerFixture('playwright', async({}, test) => {
|
||||
if (process.env.PWCHANNEL) {
|
||||
setUseApiName(false);
|
||||
|
|
@ -113,8 +121,8 @@ module.exports = function registerFixtures(global) {
|
|||
await test(playwright[process.env.BROWSER || 'chromium']);
|
||||
});
|
||||
|
||||
global.registerWorkerFixture('browser', async ({browserType}, test) => {
|
||||
const browser = await browserType.launch({ headless: !!global.HEADLESS });
|
||||
global.registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
await test(browser);
|
||||
await browser.close();
|
||||
});
|
||||
|
|
@ -129,4 +137,20 @@ module.exports = function registerFixtures(global) {
|
|||
const page = await context.newPage();
|
||||
await test(page);
|
||||
});
|
||||
|
||||
global.registerFixture('server', async ({http_server}, test) => {
|
||||
http_server.server.reset();
|
||||
await test(http_server.server);
|
||||
});
|
||||
|
||||
global.registerFixture('httpsServer', async ({http_server}, test) => {
|
||||
http_server.httpsServer.reset();
|
||||
await test(http_server.httpsServer);
|
||||
});
|
||||
}
|
||||
|
||||
function valueFromEnv(name, defaultValue) {
|
||||
if (!(name in process.env))
|
||||
return defaultValue;
|
||||
return JSON.parse(process.env[name]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,25 @@
|
|||
|
||||
const NodeEnvironment = require('jest-environment-node');
|
||||
const registerFixtures = require('./fixtures');
|
||||
const os = require('os');
|
||||
|
||||
const platform = os.platform();
|
||||
|
||||
class PlaywrightEnvironment extends NodeEnvironment {
|
||||
constructor(config, context) {
|
||||
super(config, context);
|
||||
this.fixturePool = new FixturePool();
|
||||
this.global.CHROMIUM = process.env.BROWSER === 'chromium' || !process.env.BROWSER;
|
||||
this.global.FFOX = process.env.BROWSER === 'firefox';
|
||||
this.global.WEBKIT = process.env.BROWSER === 'webkit';
|
||||
this.global.USES_HOOKS = process.env.PWCHANNEL === 'wire';
|
||||
this.global.CHANNEL = !!process.env.PWCHANNEL;
|
||||
this.global.HEADLESS = !!valueFromEnv('HEADLESS', true);
|
||||
const testOptions = {};
|
||||
testOptions.MAC = platform === 'darwin';
|
||||
testOptions.LINUX = platform === 'linux';
|
||||
testOptions.WIN = platform === 'win32';
|
||||
testOptions.CHROMIUM = process.env.BROWSER === 'chromium' || !process.env.BROWSER;
|
||||
testOptions.FFOX = process.env.BROWSER === 'firefox';
|
||||
testOptions.WEBKIT = process.env.BROWSER === 'webkit';
|
||||
testOptions.USES_HOOKS = process.env.PWCHANNEL === 'wire';
|
||||
testOptions.CHANNEL = !!process.env.PWCHANNEL;
|
||||
testOptions.HEADLESS = !!valueFromEnv('HEADLESS', true);
|
||||
this.global.testOptions = testOptions;
|
||||
|
||||
this.global.registerFixture = (name, fn) => {
|
||||
this.fixturePool.registerFixture(name, 'test', fn);
|
||||
|
|
@ -51,6 +59,24 @@ class PlaywrightEnvironment extends NodeEnvironment {
|
|||
}
|
||||
|
||||
async handleTestEvent(event, state) {
|
||||
if (event.name === 'setup') {
|
||||
const describeSkip = this.global.describe.skip;
|
||||
this.global.describe.skip = (...args) => {
|
||||
if (args.length = 1)
|
||||
return args[0] ? describeSkip : this.global.describe;
|
||||
return describeSkip(...args);
|
||||
};
|
||||
this.global.describe.fail = this.global.describe.skip;
|
||||
|
||||
const itSkip = this.global.it.skip;
|
||||
this.global.it.skip = (...args) => {
|
||||
if (args.length = 1)
|
||||
return args[0] ? itSkip : this.global.it;
|
||||
return itSkip(...args);
|
||||
};
|
||||
this.global.it.fail = this.global.it.skip;
|
||||
this.global.it.slow = () => this.global.it;
|
||||
}
|
||||
if (event.name === 'test_start') {
|
||||
const fn = event.test.fn;
|
||||
event.test.fn = async () => {
|
||||
|
|
@ -161,7 +187,7 @@ exports.default = exports.getPlaywrightEnv();
|
|||
|
||||
function fixtureParameterNames(fn) {
|
||||
const text = fn.toString();
|
||||
const match = text.match(/async\s*\(\s*{\s*([^}]*)\s*}/);
|
||||
const match = text.match(/async(?:\s+function)?\s*\(\s*{\s*([^}]*)\s*}/);
|
||||
if (!match || !match[1].trim())
|
||||
return [];
|
||||
let signature = match[1];
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = utils.testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
|
||||
|
||||
describe('Page.Events.Request', function() {
|
||||
it('should fire for navigation requests', async({page, server}) => {
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
const path = require('path');
|
||||
const util = require('util');
|
||||
const vm = require('vm');
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
|
||||
|
||||
describe('Page.close', function() {
|
||||
it('should reject all promises when page is closed', async({context}) => {
|
||||
|
|
@ -113,7 +113,7 @@ describe('Async stacks', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe.fail(FFOX && WIN).skip(USES_HOOKS)('Page.Events.Crash', function() {
|
||||
describe.fail(FFOX && WIN || USES_HOOKS)('Page.Events.Crash', function() {
|
||||
// Firefox Win: it just doesn't crash sometimes.
|
||||
|
||||
function crash(pageImpl) {
|
||||
|
|
@ -329,12 +329,12 @@ describe('Page.waitForRequest', function() {
|
|||
]);
|
||||
expect(request.url()).toBe(server.PREFIX + '/digits/2.png');
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
it('should respect timeout', async({page, playwright}) => {
|
||||
let error = null;
|
||||
await page.waitForEvent('request', { predicate: () => false, timeout: 1 }).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default timeout', async({page, server}) => {
|
||||
it('should respect default timeout', async({page, playwright}) => {
|
||||
let error = null;
|
||||
page.setDefaultTimeout(1);
|
||||
await page.waitForEvent('request', () => false).catch(e => error = e);
|
||||
|
|
@ -400,12 +400,12 @@ describe('Page.waitForResponse', function() {
|
|||
]);
|
||||
expect(response.url()).toBe(server.PREFIX + '/digits/2.png');
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
it('should respect timeout', async({page, playwright}) => {
|
||||
let error = null;
|
||||
await page.waitForEvent('response', { predicate: () => false, timeout: 1 }).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default timeout', async({page, server}) => {
|
||||
it('should respect default timeout', async({page, playwright}) => {
|
||||
let error = null;
|
||||
page.setDefaultTimeout(1);
|
||||
await page.waitForEvent('response', () => false).catch(e => error = e);
|
||||
|
|
@ -663,7 +663,7 @@ describe('Page.setContent', function() {
|
|||
const result = await page.content();
|
||||
expect(result).toBe(`${doctype}${expectedOutput}`);
|
||||
});
|
||||
it('should respect timeout', async({page, server}) => {
|
||||
it('should respect timeout', async({page, server, playwright}) => {
|
||||
const imgPath = '/img.png';
|
||||
// stall for image
|
||||
server.setRoute(imgPath, (req, res) => {});
|
||||
|
|
@ -671,7 +671,7 @@ describe('Page.setContent', function() {
|
|||
await page.setContent(`<img src="${server.PREFIX + imgPath}"></img>`, {timeout: 1}).catch(e => error = e);
|
||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||
});
|
||||
it('should respect default navigation timeout', async({page, server}) => {
|
||||
it('should respect default navigation timeout', async({page, server, playwright}) => {
|
||||
page.setDefaultNavigationTimeout(1);
|
||||
const imgPath = '/img.png';
|
||||
// stall for image
|
||||
|
|
@ -71,11 +71,6 @@ module.exports = {
|
|||
specs: [
|
||||
{
|
||||
files: [
|
||||
'./accessibility.spec.js',
|
||||
'./autowaiting.spec.js',
|
||||
'./click.spec.js',
|
||||
'./cookies.spec.js',
|
||||
'./coverage.spec.js',
|
||||
'./dialog.spec.js',
|
||||
'./dispatchevent.spec.js',
|
||||
'./download.spec.js',
|
||||
|
|
@ -88,8 +83,6 @@ module.exports = {
|
|||
'./keyboard.spec.ts',
|
||||
'./mouse.spec.js',
|
||||
'./navigation.spec.js',
|
||||
'./network.spec.js',
|
||||
'./page.spec.js',
|
||||
'./pdf.spec.js',
|
||||
'./queryselector.spec.js',
|
||||
'./screenshot.spec.js',
|
||||
|
|
|
|||
Loading…
Reference in a new issue