Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
chore: migrate unit tests to Mocha (#5600)
Rather than maintain our own test runner we should instead lean on the community and use Mocha which is very popular and also our test runner of choice in DevTools too.

Note that this commit doesn't remove the TestRunner source as it's still used for other unit tests, but they will be updated in a future PR and then we can remove the TestRunner.

The main bulk of this PR is updating the tests as the old TestRunner passed in contextual data via the `it` function callback whereas Mocha does not, so we introduce some helpers for the tests to make it easier.
  • Loading branch information
jackfranklin committed Apr 9, 2020
1 parent 262da92 commit 17cd870
Show file tree
Hide file tree
Showing 43 changed files with 4,111 additions and 2,764 deletions.
9 changes: 8 additions & 1 deletion .eslintrc.js
Expand Up @@ -10,6 +10,10 @@ module.exports = {
"ecmaVersion": 9
},

"plugins": [
"mocha"
],

/**
* ESLint rules
*
Expand Down Expand Up @@ -107,6 +111,9 @@ module.exports = {
"indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
"key-spacing": [2, {
"beforeColon": false
}]
}],

// ensure we don't have any it.only or describe.only in prod
"mocha/no-exclusive-tests": "error"
}
};
6 changes: 6 additions & 0 deletions .mocharc.js
@@ -0,0 +1,6 @@
module.exports = {
file: ['./test/mocha-utils.js'],
spec: 'test/*.spec.js',
reporter: 'dot',
timeout: process.env.PUPPETEER_PRODUCT === 'firefox' ? 15 * 1000 : 10 * 1000,
};
8 changes: 5 additions & 3 deletions package.json
Expand Up @@ -12,8 +12,9 @@
"firefox_revision": "latest"
},
"scripts": {
"unit": "node test/test.js",
"funit": "PUPPETEER_PRODUCT=firefox node test/test.js",
"unit": "mocha --config .mocharc.js",
"coverage": "cross-env COVERAGE=1 npm run unit",
"funit": "PUPPETEER_PRODUCT=firefox npm run unit",
"debug-unit": "node --inspect-brk test/test.js",
"test-doclint": "node utils/doclint/check_public_api/test/test.js && node utils/doclint/preprocessor/test.js",
"test": "npm run tsc && npm run lint --silent && npm run coverage && npm run test-doclint && npm run test-types && node utils/testrunner/test/test.js",
Expand All @@ -22,7 +23,6 @@
"install": "node install.js",
"lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc",
"doc": "node utils/doclint/cli.js",
"coverage": "cross-env COVERAGE=true npm run unit",
"tsc": "tsc --version && tsc -p . && cp src/protocol.d.ts lib/ && cp src/externs.d.ts lib/",
"apply-next-version": "node utils/apply_next_version.js",
"bundle": "npm run tsc && npx browserify -r ./index.js:puppeteer -o utils/browser/puppeteer-web.js",
Expand Down Expand Up @@ -57,10 +57,12 @@
"commonmark": "^0.28.1",
"cross-env": "^5.0.5",
"eslint": "^6.8.0",
"eslint-plugin-mocha": "^6.3.0",
"esprima": "^4.0.0",
"expect": "^25.2.7",
"jpeg-js": "^0.3.4",
"minimist": "^1.2.0",
"mocha": "^7.1.1",
"ncp": "^2.0.0",
"pixelmatch": "^4.0.2",
"pngjs": "^3.3.3",
Expand Down
129 changes: 69 additions & 60 deletions test/CDPSession.spec.js
Expand Up @@ -15,69 +15,78 @@
*/

const {waitEvent} = require('./utils');
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');

module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describeChromeOnly('Target.createCDPSession', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();

describe('Target.createCDPSession', function() {
it('should work', async function({page, server}) {
const client = await page.target().createCDPSession();
it('should work', async() => {
const { page } = getTestState();

await Promise.all([
client.send('Runtime.enable'),
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
]);
const foo = await page.evaluate(() => window.foo);
expect(foo).toBe('bar');
});
it('should send events', async function({page, server}) {
const client = await page.target().createCDPSession();
await client.send('Network.enable');
const events = [];
client.on('Network.requestWillBeSent', event => events.push(event));
await page.goto(server.EMPTY_PAGE);
expect(events.length).toBe(1);
});
it('should enable and disable domains independently', async function({page, server}) {
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain.
await page.coverage.startJSCoverage();
await page.coverage.stopJSCoverage();
// generate a script in page and wait for the event.
const [event] = await Promise.all([
waitEvent(client, 'Debugger.scriptParsed'),
page.evaluate('//# sourceURL=foo.js')
]);
const client = await page.target().createCDPSession();

await Promise.all([
client.send('Runtime.enable'),
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
]);
const foo = await page.evaluate(() => window.foo);
expect(foo).toBe('bar');
});
it('should send events', async() => {
const { page, server } = getTestState();

const client = await page.target().createCDPSession();
await client.send('Network.enable');
const events = [];
client.on('Network.requestWillBeSent', event => events.push(event));
await page.goto(server.EMPTY_PAGE);
expect(events.length).toBe(1);
});
it('should enable and disable domains independently', async() => {
const { page } = getTestState();

const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain.
await page.coverage.startJSCoverage();
await page.coverage.stopJSCoverage();
// generate a script in page and wait for the event.
const [event] = await Promise.all([
waitEvent(client, 'Debugger.scriptParsed'),
page.evaluate('//# sourceURL=foo.js')
]);
// expect events to be dispatched.
expect(event.url).toBe('foo.js');
});
it('should be able to detach session', async function({page, server}) {
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await client.detach();
let error = null;
try {
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
} catch (e) {
error = e;
}
expect(error.message).toContain('Session closed.');
});
it('should throw nice errors', async function({page}) {
const client = await page.target().createCDPSession();
const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist');
expect(event.url).toBe('foo.js');
});
it('should be able to detach session', async() => {
const { page } = getTestState();

const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await client.detach();
let error = null;
try {
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
} catch (e) {
error = e;
}
expect(error.message).toContain('Session closed.');
});
it('should throw nice errors', async() => {
const { page } = getTestState();

const client = await page.target().createCDPSession();
const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist');

async function theSourceOfTheProblems() {
await client.send('ThisCommand.DoesNotExist');
}
});
async function theSourceOfTheProblems() {
await client.send('ThisCommand.DoesNotExist');
}
});
};
});
34 changes: 34 additions & 0 deletions test/README.md
@@ -0,0 +1,34 @@
# Puppeteer unit tests

Unit tests in Puppeteer are written using [Mocha] as the test runner and [Expect] as the assertions library.

## Test state


We have some common setup that runs before each test and is defined in `mocha-utils.js`.

You can use the `getTestState` function to read state. It exposes the following that you can use in your tests. These will be reset/tidied between tests automatically for you:

* `puppeteer`: an instance of the Puppeteer library. This is exactly what you'd get if you ran `require('puppeteer')`.
* `puppeteerPath`: the path to the root source file for Puppeteer.
* `defaultBrowserOptions`: the default options the Puppeteer browser is launched from in test mode, so tests can use them and override if required.
* `server`: a dummy test server instance (see `utils/testserver` for more).
* `httpsServer`: a dummy test server HTTPS instance (see `utils/testserver` for more).
* `isFirefox`: true if running in Firefox.
* `isChrome`: true if running Chromium.
* `isHeadless`: true if the test is in headless mode.

If your test needs a browser instance, you can use the `setupTestBrowserHooks()` function which will automatically configure a browser that will be cleaned between each test suite run. You access this via `getTestState()`.

If your test needs a Puppeteer page and context, you can use the `setupTestPageAndContextHooks()` function which will configure these. You can access `page` and `context` from `getTestState()` once you have done this.

The best place to look is an existing test to see how they use the helpers.

## Skipping tests for Firefox

Tests that are not expected to pass in Firefox can be skipped. You can skip an individual test by using `itFailsFirefox` rather than `it`. Similarly you can skip a describe block with `describeFailsFirefox`.

There is also `describeChromeOnly` which will only execute the test if running in Chromium. Note that this is different from `describeFailsFirefox`: the goal is to get any `FailsFirefox` calls passing in Firefox, whereas `describeChromeOnly` should be used to test behaviour that will only ever apply in Chromium.

[Mocha]: https://mochajs.org/
[Expect]: https://www.npmjs.com/package/expect

0 comments on commit 17cd870

Please sign in to comment.