Skip to content

Commit

Permalink
test: allow overriding the worker fixtures
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Aug 14, 2020
1 parent 737bfa2 commit fea6e5e
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 90 deletions.
97 changes: 21 additions & 76 deletions test/chromium/oopif.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import '../base.fixture';
import { registerFixture } from '../runner/fixtures';
import { Page, Browser, BrowserContext } from '../..';
import { registerWorkerFixture } from '../runner/fixtures';

const {FFOX, CHROMIUM, WEBKIT} = testOptions;
const {CHROMIUM} = testOptions;

declare global {
interface FixtureState {
sppBrowser: Browser;
sppContext: BrowserContext;
sppPage: Page;
}
}
registerFixture('sppBrowser', async ({browserType, defaultBrowserOptions}, test) => {
registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, test) => {
const browser = await browserType.launch({
...defaultBrowserOptions,
args: (defaultBrowserOptions.args || []).concat(['--site-per-process'])
Expand All @@ -35,30 +28,14 @@ registerFixture('sppBrowser', async ({browserType, defaultBrowserOptions}, test)
await browser.close();
});

registerFixture('sppContext', async ({sppBrowser}, test) => {
const context = await sppBrowser.newContext();
await test(context);
await context.close();
});

registerFixture('sppPage', async ({sppContext}, test) => {
const page = await sppContext.newPage();
await test(page);
});


it.skip(!CHROMIUM)('should report oopif frames', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should report oopif frames', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
});

it.skip(!CHROMIUM)('should handle oopif detach', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should handle oopif detach', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
Expand All @@ -71,9 +48,7 @@ it.skip(!CHROMIUM)('should handle oopif detach', async function({sppBrowser, spp
expect(detachedFrame).toBe(frame);
});

it.skip(!CHROMIUM)('should handle remote -> local -> remote transitions', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should handle remote -> local -> remote transitions', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
Expand All @@ -92,9 +67,7 @@ it.skip(!CHROMIUM)('should handle remote -> local -> remote transitions', async
expect(await countOOPIFs(browser)).toBe(1);
});

it.fail(true)('should get the proper viewport', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
it.fail(true)('should get the proper viewport', async({browser, page, server}) => {
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
Expand All @@ -113,9 +86,7 @@ it.fail(true)('should get the proper viewport', async({sppBrowser, sppPage, serv
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(false);
});

it.skip(!CHROMIUM)('should expose function', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should expose function', async({browser, page, server}) => {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
Expand All @@ -127,9 +98,7 @@ it.skip(!CHROMIUM)('should expose function', async({sppBrowser, sppPage, server}
expect(result).toBe(36);
});

it.skip(!CHROMIUM)('should emulate media', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should emulate media', async({browser, page, server}) => {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
Expand All @@ -139,10 +108,7 @@ it.skip(!CHROMIUM)('should emulate media', async({sppBrowser, sppPage, server})
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
});

it.skip(!CHROMIUM)('should emulate offline', async({sppBrowser, sppPage, sppContext, server}) => {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
it.skip(!CHROMIUM)('should emulate offline', async({browser, page, context, server}) => {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
Expand All @@ -152,8 +118,7 @@ it.skip(!CHROMIUM)('should emulate offline', async({sppBrowser, sppPage, sppCont
expect(await oopif.evaluate(() => navigator.onLine)).toBe(false);
});

it.skip(!CHROMIUM)('should support context options', async({sppBrowser, server, playwright}) => {
const browser = sppBrowser;
it.skip(!CHROMIUM)('should support context options', async({browser, server, playwright}) => {
const iPhone = playwright.devices['iPhone 6']
const context = await browser.newContext({ ...iPhone, timezoneId: 'America/Jamaica', locale: 'fr-CH', userAgent: 'UA' });
const page = await context.newPage();
Expand All @@ -175,9 +140,7 @@ it.skip(!CHROMIUM)('should support context options', async({sppBrowser, server,
await context.close();
});

it.skip(!CHROMIUM)('should respect route', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should respect route', async({browser, page, server}) => {
let intercepted = false;
await page.route('**/digits/0.png', route => {
intercepted = true;
Expand All @@ -189,27 +152,21 @@ it.skip(!CHROMIUM)('should respect route', async({sppBrowser, sppPage, server})
expect(intercepted).toBe(true);
});

it.skip(!CHROMIUM)('should take screenshot', async({sppBrowser, sppPage, server, golden}) => {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should take screenshot', async({browser, page, server, golden}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(await page.screenshot()).toMatchImage(golden('screenshot-oopif.png'));
});

it.skip(!CHROMIUM)('should load oopif iframes with subresources and request interception', async function({sppBrowser, sppPage, server, context}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should load oopif iframes with subresources and request interception', async function({browser, page, server, context}) {
await page.route('**/*', route => route.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
});

it.skip(!CHROMIUM)('should report main requests', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should report main requests', async function({browser, page, server}) {
const requestFrames = [];
page.on('request', r => requestFrames.push(r.frame()));
const finishedFrames = [];
Expand Down Expand Up @@ -247,10 +204,7 @@ it.skip(!CHROMIUM)('should report main requests', async function({sppBrowser, sp
expect(finishedFrames[2]).toBe(grandChild);
});

it.skip(!CHROMIUM)('should support exposeFunction', async function({sppBrowser, sppContext, sppPage, server}) {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
it.skip(!CHROMIUM)('should support exposeFunction', async function({browser, context, page, server}) {
await context.exposeFunction('dec', a => a - 1);
await page.exposeFunction('inc', a => a + 1);
await page.goto(server.PREFIX + '/dynamic-oopif.html');
Expand All @@ -262,10 +216,7 @@ it.skip(!CHROMIUM)('should support exposeFunction', async function({sppBrowser,
expect(await page.frames()[1].evaluate(() => window['dec'](4))).toBe(3);
});

it.skip(!CHROMIUM)('should support addInitScript', async function({sppBrowser, sppContext, sppPage, server}) {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
it.skip(!CHROMIUM)('should support addInitScript', async function({browser, context, page, server}) {
await context.addInitScript(() => window['bar'] = 17);
await page.addInitScript(() => window['foo'] = 42);
await page.goto(server.PREFIX + '/dynamic-oopif.html');
Expand All @@ -277,9 +228,7 @@ it.skip(!CHROMIUM)('should support addInitScript', async function({sppBrowser, s
expect(await page.frames()[1].evaluate(() => window['bar'])).toBe(17);
});
// @see https://github.com/microsoft/playwright/issues/1240
it.skip(!CHROMIUM)('should click a button when it overlays oopif', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should click a button when it overlays oopif', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/button-overlay-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
await page.click('button');
Expand Down Expand Up @@ -311,9 +260,7 @@ it.skip(!CHROMIUM)('should report google.com frame with headful', async({browser
await browser.close();
});

it.skip(!CHROMIUM)('ElementHandle.boundingBox() should work', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('ElementHandle.boundingBox() should work', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => {
iframe.style.width = '500px';
Expand All @@ -336,9 +283,7 @@ it.skip(!CHROMIUM)('ElementHandle.boundingBox() should work', async function({sp
expect(await handle2.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 });
});

it.skip(!CHROMIUM)('should click', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
it.skip(!CHROMIUM)('should click', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => {
iframe.style.width = '500px';
Expand Down
11 changes: 9 additions & 2 deletions test/runner/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
const debug = require('debug');

const registrations = new Map();
const filesWithRegistrations = new Set();

class Fixture {
constructor(pool, name, scope, fn) {
Expand Down Expand Up @@ -139,7 +140,13 @@ function fixtureParameterNames(fn) {
}

function innerRegisterFixture(name, scope, fn) {
registrations.set(name, { scope, fn });
const stackFrame = new Error().stack.split('\n').slice(1).filter(line => !line.includes(__filename))[0];
const location = stackFrame.replace(/.*at Object.<anonymous> \((.*)\)/, '$1');
const file = location.replace(/^(.+):\d+:\d+$/, '$1');
const registration = { scope, fn, file, location };
registrations.set(name, registration);
if (scope === 'worker')
filesWithRegistrations.add(file);
};

function registerFixture(name, fn) {
Expand All @@ -150,4 +157,4 @@ function registerWorkerFixture (name, fn) {
innerRegisterFixture(name, 'worker', fn);
};

module.exports = { FixturePool, registerFixture, registerWorkerFixture };
module.exports = { FixturePool, registerFixture, registerWorkerFixture, filesWithRegistrations };
2 changes: 1 addition & 1 deletion test/runner/fixturesUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

const { FixturePool, registerFixture, registerWorkerFixture } = require('./fixtures');
const { Test, Suite } = require('mocha');
const {installTransform} = require('./transform');
const { installTransform } = require('./transform');
const commonSuite = require('mocha/lib/interfaces/common');

Error.stackTraceLimit = 15;
Expand Down
13 changes: 5 additions & 8 deletions test/runner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ const path = require('path');
const program = require('commander');
const { Runner } = require('./runner');
const Mocha = require('mocha');
const constants = require('mocha/lib/runner').constants;
const { fixturesUI } = require('./fixturesUI');
const colors = require('colors/safe');

class NullReporter {}

Expand All @@ -36,7 +34,8 @@ program
.option('--timeout <timeout>', 'Specify test timeout threshold (in milliseconds), default: 10000', 10000)
.action(async (command) => {
// Collect files
const files = collectFiles(path.join(process.cwd(), command.args[0]), command.args.slice(1));
const files = [];
collectFiles(path.join(process.cwd(), command.args[0]), command.args.slice(1), files);
const rootSuite = new Mocha.Suite('', new Mocha.Context(), true);

let total = 0;
Expand All @@ -62,7 +61,7 @@ program
mocha.suite.title = path.basename(file);
}

// Now run the tests.
// Filter tests.
if (rootSuite.hasOnly())
rootSuite.filterOnly();
if (!command.reporter) {
Expand All @@ -89,13 +88,12 @@ program

program.parse(process.argv);

function collectFiles(dir, filters) {
function collectFiles(dir, filters, files) {
if (fs.statSync(dir).isFile())
return [dir];
const files = [];
for (const name of fs.readdirSync(dir)) {
if (fs.lstatSync(path.join(dir, name)).isDirectory()) {
files.push(...collectFiles(path.join(dir, name), filters));
collectFiles(path.join(dir, name), filters, files);
continue;
}
if (!name.includes('spec'))
Expand All @@ -111,5 +109,4 @@ function collectFiles(dir, filters) {
}
}
}
return files;
}
51 changes: 48 additions & 3 deletions test/runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
*/

const child_process = require('child_process');
const crypto = require('crypto');
const path = require('path');
const { EventEmitter } = require('events');
const Mocha = require('mocha');
const builtinReporters = require('mocha/lib/reporters');
const DotRunner = require('./dotReporter');
const { filesWithRegistrations } = require('./fixtures');

const constants = Mocha.Runner.constants;
// Mocha runner does not remove uncaughtException listeners.
Expand Down Expand Up @@ -63,11 +65,28 @@ class Runner extends EventEmitter {
}
}

_filesSortedByWorkerHash() {
const result = [];
for (const file of this._files.keys())
result.push({ file, hash: computeWorkerHash(file) });
result.sort((a, b) => a.hash < b.hash ? -1 : (a.hash === b.hash ? 0 : 1));
return result;
}

async run() {
this.emit(constants.EVENT_RUN_BEGIN, {});
for (const file of this._files.keys()) {
const files = this._filesSortedByWorkerHash();
console.log(files);
while (files.length) {
const worker = await this._obtainWorker();
this._runJob(worker, file);
const requiredHash = files[0].hash;
if (worker.hash && worker.hash !== requiredHash) {
this._restartWorker(worker);
continue;
}
const entry = files.shift();
worker.hash = requiredHash;
this._runJob(worker, entry.file);
}
await new Promise(f => this._runCompleteCallback = f);
this.emit(constants.EVENT_RUN_END, {});
Expand Down Expand Up @@ -135,7 +154,7 @@ class Runner extends EventEmitter {
worker.init().then(() => this._workerAvailable(worker));
}

async _restartWorker(worker) {
_restartWorker(worker) {
worker.stop();
this._createWorker();
}
Expand Down Expand Up @@ -214,4 +233,30 @@ class Worker extends EventEmitter {
}
}

function collectRequires(file, allDeps) {
if (allDeps.has(file))
return;
allDeps.add(file);
const cache = require.cache[file];
const deps = cache.children.map(m => m.id);
for (const dep of deps)
collectRequires(dep, allDeps);
}

function computeWorkerHash(file) {
// At this point, filesWithRegistrations contains all the files with worker fixture registrations.
// For every test, build the require closure and map each file to fixtures declared in it.
// This collection of fixtures is the fingerprint of the worker setup, a "worker hash".
// Tests with the matching "worker hash" will reuse the same worker.
const deps = new Set();
const hash = crypto.createHash('sha1');
collectRequires(file, deps);
for (const dep of deps) {
if (!filesWithRegistrations.has(dep))
continue;
hash.update(dep);
}
return hash.digest('hex');
}

module.exports = { Runner };

0 comments on commit fea6e5e

Please sign in to comment.