chore: implement custom mocha interface for the runner (#8929)

Instead of checking skipped tests in mocha-utils this PR
implements a custom mocha interface for better flexibility
when skipping tests. That should allow skipping tests without
running before and after hooks.
This commit is contained in:
Alex Rudenko 2022-09-09 11:12:18 +02:00 committed by GitHub
parent 9b120f6c7b
commit 504c7a1ae6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 203 additions and 97 deletions

View File

@ -22,7 +22,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
import {it} from './mocha-utils.js';
describe('Target.createCDPSession', function () {
setupTestBrowserHooks();

View File

@ -17,7 +17,6 @@
import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js';
import sinon from 'sinon';
import expect from 'expect';
import {it} from './mocha-utils.js';
describe('EventEmitter', () => {
let emitter: EventEmitter;

View File

@ -23,7 +23,6 @@ import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {EventEmitter} from '../../lib/cjs/puppeteer/common/EventEmitter.js';
import {Frame} from '../../lib/cjs/puppeteer/common/Frame.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import {it} from './mocha-utils.js';
class MockCDPSession extends EventEmitter {
async send(): Promise<any> {}

View File

@ -16,7 +16,6 @@
import {getTestState} from './mocha-utils'; // eslint-disable-line import/extensions
import utils from './utils.js';
import {it} from './mocha-utils.js';
import expect from 'expect';

View File

@ -22,7 +22,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Accessibility', function () {
setupTestBrowserHooks();

View File

@ -24,7 +24,6 @@ import {
import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js';
import utils from './utils.js';
import assert from 'assert';
import {it} from './mocha-utils.js';
describe('AriaQueryHandler', () => {
setupTestBrowserHooks();

View File

@ -16,7 +16,6 @@
import expect from 'expect';
import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Browser specs', function () {
setupTestBrowserHooks();

View File

@ -17,7 +17,6 @@
import expect from 'expect';
import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
import {waitEvent} from './utils.js';
import {it} from './mocha-utils.js';
describe('BrowserContext', function () {
setupTestBrowserHooks();

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Chromium-Specific Launcher tests', function () {
describe('Puppeteer.launch |browserURL| option', function () {

View File

@ -21,7 +21,6 @@ import {
setupTestBrowserHooks,
} from './mocha-utils.js';
import utils from './utils.js';
import {it} from './mocha-utils.js';
describe('Page.click', function () {
setupTestBrowserHooks();
@ -419,7 +418,7 @@ describe('Page.click', function () {
).toBe('Clicked');
});
// @see https://github.com/puppeteer/puppeteer/issues/4110
xit('should click the button with fixed position inside an iframe', async () => {
it.skip('should click the button with fixed position inside an iframe', async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Cookie specs', () => {
setupTestBrowserHooks();

View File

@ -20,7 +20,6 @@ import {
setupTestPageAndContextHooks,
setupTestBrowserHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Coverage specs', function () {
describe('JSCoverage', function () {
@ -134,7 +133,7 @@ describe('Coverage specs', function () {
).toBeGolden('jscoverage-involved.txt');
});
// @see https://crbug.com/990945
xit('should not hang when there is a debugger statement', async () => {
it.skip('should not hang when there is a debugger statement', async () => {
const {page, server} = getTestState();
await page.coverage.startJSCoverage();
@ -190,7 +189,7 @@ describe('Coverage specs', function () {
});
});
// @see https://crbug.com/990945
xit('should not hang when there is a debugger statement', async () => {
it.skip('should not hang when there is a debugger statement', async () => {
const {page, server} = getTestState();
await page.coverage.startJSCoverage();

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('DefaultBrowserContext', function () {
setupTestBrowserHooks();

View File

@ -21,7 +21,6 @@ import {
setupTestPageAndContextHooks,
setupTestBrowserHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Page.Events.Dialog', function () {
setupTestBrowserHooks();

View File

@ -20,7 +20,6 @@ import {
setupTestPageAndContextHooks,
setupTestBrowserHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Input.drag', function () {
setupTestBrowserHooks();

View File

@ -22,7 +22,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
import utils from './utils.js';

View File

@ -21,7 +21,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Emulation', () => {
setupTestBrowserHooks();

View File

@ -21,7 +21,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
const bigint = typeof BigInt !== 'undefined';
@ -38,7 +37,7 @@ describe('Evaluation specs', function () {
});
expect(result).toBe(21);
});
(bigint ? it : xit)('should transfer BigInt', async () => {
(bigint ? it : it.skip)('should transfer BigInt', async () => {
const {page} = getTestState();
const result = await page.evaluate((a: bigint) => {
@ -259,7 +258,7 @@ describe('Evaluation specs', function () {
expect(result).not.toBe(object);
expect(result).toEqual(object);
});
(bigint ? it : xit)('should return BigInt', async () => {
(bigint ? it : it.skip)('should return BigInt', async () => {
const {page} = getTestState();
const result = await page.evaluate(() => {

View File

@ -20,7 +20,6 @@ import expect from 'expect';
import {getTestState} from './mocha-utils.js';
import path from 'path';
import {it} from './mocha-utils.js';
describe('Fixtures', function () {
it('dumpio option should work with pipe option', async () => {

View File

@ -23,7 +23,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import utils, {dumpFrames} from './utils.js';
import {it} from './mocha-utils.js';
describe('Frame specs', function () {
setupTestBrowserHooks();

View File

@ -25,7 +25,6 @@ import {
PuppeteerNode,
} from '../../lib/cjs/puppeteer/node/Puppeteer.js';
import {getTestState} from './mocha-utils.js';
import {it} from './mocha-utils.js';
const rmAsync = promisify(rimraf);
const mkdtempAsync = promisify(fs.mkdtemp);
@ -244,7 +243,7 @@ describe('headful tests', function () {
expect(cookie).toBe('foo=true');
});
// TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548
xit('OOPIF: should report google.com frame', async () => {
it.skip('OOPIF: should report google.com frame', async () => {
const {server, puppeteer} = getTestState();
// https://google.com is isolated by default in Chromium embedder.

View File

@ -21,7 +21,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Emulate idle state', () => {
setupTestBrowserHooks();

View File

@ -23,7 +23,6 @@ import {
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import {getTestState} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('ignoreHTTPSErrors', function () {
/* Note that this test creates its own browser rather than use

View File

@ -21,7 +21,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
const FILE_TO_UPLOAD = path.join(__dirname, '/../assets/file-to-upload.txt');

View File

@ -21,7 +21,6 @@ import {
setupTestPageAndContextHooks,
shortWaitForArrayToHaveAtLeastNElements,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('JSHandle', function () {
setupTestBrowserHooks();

View File

@ -23,7 +23,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {KeyInput} from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js';
import {it} from './mocha-utils.js';
describe('Keyboard', function () {
setupTestBrowserHooks();

View File

@ -26,7 +26,6 @@ import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Product} from '../../lib/cjs/puppeteer/common/Product.js';
import {getTestState, itOnlyRegularInstall} from './mocha-utils.js';
import utils from './utils.js';
import {it} from './mocha-utils.js';
const mkdtempAsync = promisify(fs.mkdtemp);
const readFileAsync = promisify(fs.readFile);

View File

@ -34,7 +34,6 @@ import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js';
import {TestServer} from '../../utils/testserver/lib/index.js';
import {extendExpectWithToBeGolden} from './utils.js';
import * as Mocha from 'mocha';
import {getTestId} from '../../utils/mochaRunner/lib/utils.js';
const setupServer = async () => {
const assetsPath = path.join(__dirname, '../assets');
@ -162,7 +161,7 @@ export const itOnlyRegularInstall = (
body: Mocha.AsyncFunc
): Mocha.Test => {
if (alternativeInstall || process.env['BINARY']) {
return xit(description, body);
return it.skip(description, body);
} else {
return it(description, body);
}
@ -297,34 +296,3 @@ export const shortWaitForArrayToHaveAtLeastNElements = async (
});
}
};
type SyncFn = (this: Mocha.Context) => void;
const skippedTests: string[] = process.env['PUPPETEER_SKIPPED_TEST_CONFIG']
? JSON.parse(process.env['PUPPETEER_SKIPPED_TEST_CONFIG'])
: [];
function skipTestIfNeeded(test: Mocha.Test): void {
const testId = getTestId(test.file!, test.fullTitle());
if (
skippedTests.find(skippedTest => {
return testId.startsWith(skippedTest);
})
) {
try {
test.skip();
} catch {}
}
}
export function it(title: string, fn?: Mocha.AsyncFunc | SyncFn): Mocha.Test {
const test = Mocha.it.call(null, title, fn as any);
skipTestIfNeeded(test);
return test;
}
it.only = function (title: string, fn?: Mocha.AsyncFunc | SyncFn): Mocha.Test {
const test = Mocha.it.only.call(null, title, fn as any);
skipTestIfNeeded(test);
return test;
};

View File

@ -21,7 +21,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {KeyInput} from '../../lib/cjs/puppeteer/common/USKeyboardLayout.js';
import {it} from './mocha-utils.js';
interface Dimensions {
x: number;

View File

@ -24,7 +24,6 @@ import {
import os from 'os';
import {ServerResponse} from 'http';
import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {it} from './mocha-utils.js';
describe('navigation', function () {
setupTestBrowserHooks();

View File

@ -26,7 +26,6 @@ import {
import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
import {ServerResponse} from 'http';
import {it} from './mocha-utils.js';
describe('network', function () {
setupTestBrowserHooks();

View File

@ -22,7 +22,6 @@ import {
BrowserContext,
} from '../../lib/cjs/puppeteer/common/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {it} from './mocha-utils.js';
describe('OOPIF', function () {
/* We use a special browser for this test as we need the --site-per-process flag */

View File

@ -27,7 +27,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import utils, {attachFrame, waitEvent} from './utils.js';
import {it} from './mocha-utils.js';
describe('Page', function () {
setupTestBrowserHooks();
@ -1758,7 +1757,7 @@ describe('Page', function () {
});
// @see https://github.com/puppeteer/puppeteer/issues/4840
xit('should throw when added with content to the CSP page', async () => {
it.skip('should throw when added with content to the CSP page', async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/csp.html');

View File

@ -22,7 +22,6 @@ import type {Server, IncomingMessage, ServerResponse} from 'http';
import type {Browser} from '../../lib/cjs/puppeteer/common/Browser.js';
import type {AddressInfo} from 'net';
import {TestServer} from '../../utils/testserver/lib/index.js';
import {it} from './mocha-utils.js';
let HOSTNAME = os.hostname();

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Query handler tests', function () {
setupTestBrowserHooks();

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('querySelector', function () {
setupTestBrowserHooks();

View File

@ -29,7 +29,6 @@ import {
HTTPRequest,
InterceptResolutionAction,
} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {it} from './mocha-utils.js';
describe('request interception', function () {
setupTestBrowserHooks();

View File

@ -25,7 +25,6 @@ import {
} from './mocha-utils.js';
import {HTTPRequest} from '../../lib/cjs/puppeteer/common/HTTPRequest.js';
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
import {it} from './mocha-utils.js';
describe('request interception', function () {
setupTestBrowserHooks();

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Screenshots', function () {
setupTestBrowserHooks();

View File

@ -24,7 +24,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import utils from './utils.js';
import {it} from './mocha-utils.js';
const {waitEvent} = utils;

View File

@ -20,7 +20,6 @@ import {
setupTestBrowserHooks,
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {it} from './mocha-utils.js';
describe('Touchscreen', function () {
setupTestBrowserHooks();

View File

@ -20,7 +20,6 @@ import expect from 'expect';
import {getTestState} from './mocha-utils.js';
import {Browser} from '../../lib/cjs/puppeteer/common/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {it} from './mocha-utils.js';
describe('Tracing', function () {
let outputFile!: string;

View File

@ -22,7 +22,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {attachFrame, detachFrame} from './utils.js';
import {it} from './mocha-utils.js';
describe('waittask specs', function () {
setupTestBrowserHooks();

View File

@ -23,7 +23,6 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
import {waitEvent} from './utils.js';
import {it} from './mocha-utils.js';
describe('Workers', function () {
setupTestBrowserHooks();

View File

@ -0,0 +1,119 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Mocha from 'mocha';
import commonInterface from 'mocha/lib/interfaces/common';
import {getTestId} from './utils.js';
type SuiteFunction = ((this: Mocha.Suite) => void) | undefined;
type ExclusiveSuiteFunction = (this: Mocha.Suite) => void;
const skippedTests: Array<{testIdPattern: string; skip: true}> = process.env[
'PUPPETEER_SKIPPED_TEST_CONFIG'
]
? JSON.parse(process.env['PUPPETEER_SKIPPED_TEST_CONFIG'])
: [];
skippedTests.reverse();
function shouldSkipTest(test: Mocha.Test): boolean {
const testId = getTestId(test.file!, test.fullTitle());
// TODO: more efficient lookup.
const defintion = skippedTests.find(skippedTest => {
return testId.startsWith(skippedTest.testIdPattern);
});
if (defintion && defintion.skip) {
return true;
}
return false;
}
function customBDDInterface(suite: Mocha.Suite) {
const suites = [suite];
suite.on(
Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE,
function (context, file, mocha) {
const common = commonInterface(suites, context, mocha);
context['before'] = common.before;
context['after'] = common.after;
context['beforeEach'] = common.beforeEach;
context['afterEach'] = common.afterEach;
if (mocha.options.delay) {
context['run'] = common.runWithSuite(suite);
}
function describe(title: string, fn: SuiteFunction) {
return common.suite.create({
title: title,
file: file,
fn: fn,
});
}
describe.only = function (title: string, fn: ExclusiveSuiteFunction) {
return common.suite.only({
title: title,
file: file,
fn: fn,
});
};
describe.skip = function (title: string, fn: SuiteFunction) {
return common.suite.skip({
title: title,
file: file,
fn: fn,
});
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
context['describe'] = describe;
function it(title: string, fn: Mocha.TestFunction) {
const suite = suites[0]!;
const test = new Mocha.Test(title, suite.isPending() ? undefined : fn);
test.file = file;
test.parent = suite;
if (shouldSkipTest(test)) {
const test = new Mocha.Test(title);
test.file = file;
suite.addTest(test);
return test;
} else {
suite.addTest(test);
return test;
}
}
it.only = function (title: string, fn: Mocha.TestFunction) {
return common.test.only(mocha, context['it'](title, fn));
};
it.skip = function (title: string) {
return context['it'](title);
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
context.it = it;
}
);
}
customBDDInterface.description = 'Custom BDD';
module.exports = customBDDInterface;

View File

@ -1,3 +1,19 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
TestExpectation,
MochaResults,
@ -19,7 +35,6 @@ import {
readJSON,
filterByParameters,
getExpectationUpdates,
getSkippedTests,
} from './utils.js';
function getApplicableTestSuites(
@ -80,16 +95,17 @@ async function main() {
parameters
);
const skippedTests = getSkippedTests(applicableExpectations);
const env = extendProcessEnv([
...parameters.map(param => {
return parsedSuitesFile.parameterDefinitons[param];
}),
{
PUPPETEER_SKIPPED_TEST_CONFIG: JSON.stringify(
skippedTests.map(ex => {
return ex.testIdPattern;
applicableExpectations.map(ex => {
return {
testIdPattern: ex.testIdPattern,
skip: ex.expectations.includes('SKIP'),
};
})
),
},
@ -102,7 +118,13 @@ async function main() {
console.log('Running', JSON.stringify(parameters), tmpFilename);
const handle = spawn(
'npx mocha',
['--reporter=json', '--reporter-option', 'output=' + tmpFilename],
[
'-u',
path.join(__dirname, 'interface.js'),
'--reporter=json',
'--reporter-option',
'output=' + tmpFilename,
],
{
shell: true,
cwd: process.cwd(),

View File

@ -1,3 +1,19 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import assert from 'assert/strict';
import test from 'node:test';
import {filterByParameters, getTestResultForFailure} from './utils.js';

View File

@ -1,3 +1,19 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {z} from 'zod';
export const zPlatform = z.enum(['win32', 'linux', 'darwin']);

View File

@ -1,3 +1,19 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
MochaTestResult,
TestExpectation,
@ -140,14 +156,6 @@ export function getTestResultForFailure(
return test.err?.code === 'ERR_MOCHA_TIMEOUT' ? 'TIMEOUT' : 'FAIL';
}
export function getSkippedTests(
expectations: TestExpectation[]
): TestExpectation[] {
return expectations.filter(ex => {
return ex.expectations.includes('SKIP');
});
}
export function getTestId(file: string, fullTitle: string): string {
return `[${getFilename(file)}] ${fullTitle}`;
}