diff --git a/.eslintrc.js b/.eslintrc.js index a6939035c9b..49c970c4b6b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,6 +10,10 @@ module.exports = { "ecmaVersion": 9 }, + "plugins": [ + "mocha" + ], + /** * ESLint rules * @@ -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" } }; diff --git a/.mocharc.js b/.mocharc.js new file mode 100644 index 00000000000..32bbf7ee51d --- /dev/null +++ b/.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, +}; diff --git a/package.json b/package.json index e165913208b..2035cd26354 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", @@ -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", diff --git a/test/CDPSession.spec.js b/test/CDPSession.spec.js index 2647d2a2e36..5f16d0566ab 100644 --- a/test/CDPSession.spec.js +++ b/test/CDPSession.spec.js @@ -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') - ]); - // 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'); + const client = await page.target().createCDPSession(); - async function theSourceOfTheProblems() { - await client.send('ThisCommand.DoesNotExist'); - } - }); + 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() => { + 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'); + } + }); +}); diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000000..f69d4fe02ab --- /dev/null +++ b/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 diff --git a/test/accessibility.spec.js b/test/accessibility.spec.js index 754afcd2229..68022c7807c 100644 --- a/test/accessibility.spec.js +++ b/test/accessibility.spec.js @@ -14,14 +14,17 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, FFOX}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); - describe_fails_ffox('Accessibility', function() { - it('should work', async function({page}) { - await page.setContent(` +describeFailsFirefox('Accessibility', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + it('should work', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(`