From 6579858dd53e27bec5c649e7ffad12aaa5b19d05 Mon Sep 17 00:00:00 2001 From: "david.villarama" Date: Fri, 15 Feb 2019 10:22:49 -0800 Subject: [PATCH 1/4] Create new puppeteer plugin to dump code coverage --- lib/plugin/puppeteerCoverage.js | 140 ++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 lib/plugin/puppeteerCoverage.js diff --git a/lib/plugin/puppeteerCoverage.js b/lib/plugin/puppeteerCoverage.js new file mode 100644 index 000000000..c5086bfd9 --- /dev/null +++ b/lib/plugin/puppeteerCoverage.js @@ -0,0 +1,140 @@ +const Container = require("../container"); +const recorder = require("../recorder"); +const event = require("../event"); +const fs = require("fs"); +const path = require("path"); +const { clearString } = require("../utils"); +const debug = require("debug")("codeceptjs:plugin:puppeteerCoverage"); + +const defaultConfig = { + coverageDir: "output/coverage", + uniqueFileName: true +}; + +const supportedHelpers = ["Puppeteer"]; + +/** + * Builds the filename based on the test title + */ +function buildFileName(test, uniqueFileName) { + let fileName = clearString(test.title); + + // This prevent data driven to be included in the failed screenshot file name + if (fileName.indexOf("{") !== -1) { + fileName = fileName.substr(0, fileName.indexOf("{") - 3).trim(); + } + + if (test.ctx && test.ctx.test && test.ctx.test.type === "hook") { + fileName = clearString(`${test.title}_${test.ctx.test.title}`); + } + + if (uniqueFileName) { + const uuid = + test.uuid || + test.ctx.test.uuid || + Math.floor(new Date().getTime() / 1000); + fileName = `${fileName.substring(0, 10)}_${uuid}.coverage.json`; + } else { + fileName = `${fileName}.coverage.json`; + } + + return fileName; +} + +/** + * Dumps puppeteers code coverage after every test. + * + * ##### Configuration + * + * Configuration can either be taken from a corresponding helper (deprecated) or a from plugin config (recommended). + * + * ```js + * "plugins": { + * "puppeteerCoverage": { + * "enabled": true + * } + * } + * ``` + * + * Possible config options: + * + * * `outputDir`: directory to dump coverage files + * * `uniqueFileName`: generate a unique filename by adding uuid + * + * Notes: + * First of all, YMMV! + * To work, you need the client javascript code to be NOT uglified. They need to be built in "development" mode. + * And the end of your tests, you'll get a directory full of coverage per test run. Now what? + * You'll need to convert the coverage code to something istanbul can read. Good news is someone wrote the code + * for you (see puppeteer-to-istanbul link below). Then using istanbul you need to combine the converted + * coverage and create a report. Good luck! + * + * Links: + * * https://github.com/GoogleChrome/puppeteer/blob/v1.12.2/docs/api.md#class-coverage + * * https://github.com/istanbuljs/puppeteer-to-istanbul + * * https://github.com/gotwarlost/istanbul + */ +module.exports = function(config) { + const helpers = Container.helpers(); + let coverageRunning = false; + let helper; + + for (const helperName of supportedHelpers) { + if (Object.keys(helpers).indexOf(helperName) > -1) { + helper = helpers[helperName]; + } + } + + if (!helper) { + console.error("Coverage is only supported in Puppeteer"); + return; // no helpers for screenshot + } + + const options = Object.assign(defaultConfig, helper.options, config); + + event.dispatcher.on(event.all.before, async suite => { + debug("*** Collecting coverage for tests ****"); + }); + + /** + * Hack! we're going to try to "start" coverage before each step because this is + * when the browser is already up and is ready to start coverage. + */ + event.dispatcher.on(event.step.before, async step => { + recorder.add("starting coverage", async () => { + try { + if (!coverageRunning) { + debug("--> starting coverage <--"); + coverageRunning = true; + await helper.page.coverage.startJSCoverage(); + } + } catch (err) { + console.error(err); + } + }, true); + }); + + /** + * Save puppeteer coverage data after every test run + */ + event.dispatcher.on(event.test.after, async test => { + recorder.add("saving coverage", async () => { + try { + if (coverageRunning) { + debug("--> stopping coverage <--"); + coverageRunning = false; + const coverage = await helper.page.coverage.stopJSCoverage(); + + const coverageDir = path.resolve(process.cwd(), options.coverageDir); + fs.mkdirSync(coverageDir, {recursive: true}); + + const coveragePath = path.resolve(coverageDir, buildFileName(test, options.uniqueFileName)); + debug(`writing ${coveragePath}`); + fs.writeFileSync(coveragePath, JSON.stringify(coverage)); + } + } catch (err) { + console.error(err); + } + }, true); + }); +}; From 5e6cc2452d15916e30e3d2bc71a913cf3f819836 Mon Sep 17 00:00:00 2001 From: "david.villarama" Date: Fri, 15 Feb 2019 10:30:55 -0800 Subject: [PATCH 2/4] fixed some lint errors --- lib/plugin/puppeteerCoverage.js | 72 ++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/lib/plugin/puppeteerCoverage.js b/lib/plugin/puppeteerCoverage.js index c5086bfd9..6c6527da4 100644 --- a/lib/plugin/puppeteerCoverage.js +++ b/lib/plugin/puppeteerCoverage.js @@ -66,7 +66,7 @@ function buildFileName(test, uniqueFileName) { * To work, you need the client javascript code to be NOT uglified. They need to be built in "development" mode. * And the end of your tests, you'll get a directory full of coverage per test run. Now what? * You'll need to convert the coverage code to something istanbul can read. Good news is someone wrote the code - * for you (see puppeteer-to-istanbul link below). Then using istanbul you need to combine the converted + * for you (see puppeteer-to-istanbul link below). Then using istanbul you need to combine the converted * coverage and create a report. Good luck! * * Links: @@ -93,7 +93,7 @@ module.exports = function(config) { const options = Object.assign(defaultConfig, helper.options, config); event.dispatcher.on(event.all.before, async suite => { - debug("*** Collecting coverage for tests ****"); + debug('*** Collecting coverage for tests ****'); }); /** @@ -101,40 +101,54 @@ module.exports = function(config) { * when the browser is already up and is ready to start coverage. */ event.dispatcher.on(event.step.before, async step => { - recorder.add("starting coverage", async () => { - try { - if (!coverageRunning) { - debug("--> starting coverage <--"); - coverageRunning = true; - await helper.page.coverage.startJSCoverage(); + recorder.add( + "starting coverage", + async () => { + try { + if (!coverageRunning) { + debug('--> starting coverage <--'); + coverageRunning = true; + await helper.page.coverage.startJSCoverage(); + } + } catch (err) { + console.error(err); } - } catch (err) { - console.error(err); - } - }, true); + }, + true + ); }); /** * Save puppeteer coverage data after every test run */ event.dispatcher.on(event.test.after, async test => { - recorder.add("saving coverage", async () => { - try { - if (coverageRunning) { - debug("--> stopping coverage <--"); - coverageRunning = false; - const coverage = await helper.page.coverage.stopJSCoverage(); - - const coverageDir = path.resolve(process.cwd(), options.coverageDir); - fs.mkdirSync(coverageDir, {recursive: true}); - - const coveragePath = path.resolve(coverageDir, buildFileName(test, options.uniqueFileName)); - debug(`writing ${coveragePath}`); - fs.writeFileSync(coveragePath, JSON.stringify(coverage)); + recorder.add( + 'saving coverage', + async () => { + try { + if (coverageRunning) { + debug('--> stopping coverage <--'); + coverageRunning = false; + const coverage = await helper.page.coverage.stopJSCoverage(); + + const coverageDir = path.resolve( + process.cwd(), + options.coverageDir + ); + fs.mkdirSync(coverageDir, { recursive: true }); + + const coveragePath = path.resolve( + coverageDir, + buildFileName(test, options.uniqueFileName) + ); + debug(`writing ${coveragePath}`); + fs.writeFileSync(coveragePath, JSON.stringify(coverage)); + } + } catch (err) { + console.error(err); } - } catch (err) { - console.error(err); - } - }, true); + }, + true + ); }); }; From fcd6615f37ee798d4d7aa0451b2960d19e09aa84 Mon Sep 17 00:00:00 2001 From: "david.villarama" Date: Fri, 15 Feb 2019 10:36:45 -0800 Subject: [PATCH 3/4] ran lint -fix --- lib/plugin/puppeteerCoverage.js | 46 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/plugin/puppeteerCoverage.js b/lib/plugin/puppeteerCoverage.js index 6c6527da4..5dd9b38bc 100644 --- a/lib/plugin/puppeteerCoverage.js +++ b/lib/plugin/puppeteerCoverage.js @@ -1,17 +1,17 @@ -const Container = require("../container"); -const recorder = require("../recorder"); -const event = require("../event"); -const fs = require("fs"); -const path = require("path"); -const { clearString } = require("../utils"); -const debug = require("debug")("codeceptjs:plugin:puppeteerCoverage"); +const Container = require('../container'); +const recorder = require('../recorder'); +const event = require('../event'); +const fs = require('fs'); +const path = require('path'); +const { clearString } = require('../utils'); +const debug = require('debug')('codeceptjs:plugin:puppeteerCoverage'); const defaultConfig = { - coverageDir: "output/coverage", - uniqueFileName: true + coverageDir: 'output/coverage', + uniqueFileName: true, }; -const supportedHelpers = ["Puppeteer"]; +const supportedHelpers = ['Puppeteer']; /** * Builds the filename based on the test title @@ -20,11 +20,11 @@ function buildFileName(test, uniqueFileName) { let fileName = clearString(test.title); // This prevent data driven to be included in the failed screenshot file name - if (fileName.indexOf("{") !== -1) { - fileName = fileName.substr(0, fileName.indexOf("{") - 3).trim(); + if (fileName.indexOf('{') !== -1) { + fileName = fileName.substr(0, fileName.indexOf('{') - 3).trim(); } - if (test.ctx && test.ctx.test && test.ctx.test.type === "hook") { + if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook') { fileName = clearString(`${test.title}_${test.ctx.test.title}`); } @@ -74,7 +74,7 @@ function buildFileName(test, uniqueFileName) { * * https://github.com/istanbuljs/puppeteer-to-istanbul * * https://github.com/gotwarlost/istanbul */ -module.exports = function(config) { +module.exports = function (config) { const helpers = Container.helpers(); let coverageRunning = false; let helper; @@ -86,13 +86,13 @@ module.exports = function(config) { } if (!helper) { - console.error("Coverage is only supported in Puppeteer"); + console.error('Coverage is only supported in Puppeteer'); return; // no helpers for screenshot } const options = Object.assign(defaultConfig, helper.options, config); - event.dispatcher.on(event.all.before, async suite => { + event.dispatcher.on(event.all.before, async (suite) => { debug('*** Collecting coverage for tests ****'); }); @@ -100,9 +100,9 @@ module.exports = function(config) { * Hack! we're going to try to "start" coverage before each step because this is * when the browser is already up and is ready to start coverage. */ - event.dispatcher.on(event.step.before, async step => { + event.dispatcher.on(event.step.before, async (step) => { recorder.add( - "starting coverage", + 'starting coverage', async () => { try { if (!coverageRunning) { @@ -114,14 +114,14 @@ module.exports = function(config) { console.error(err); } }, - true + true, ); }); /** * Save puppeteer coverage data after every test run */ - event.dispatcher.on(event.test.after, async test => { + event.dispatcher.on(event.test.after, async (test) => { recorder.add( 'saving coverage', async () => { @@ -133,13 +133,13 @@ module.exports = function(config) { const coverageDir = path.resolve( process.cwd(), - options.coverageDir + options.coverageDir, ); fs.mkdirSync(coverageDir, { recursive: true }); const coveragePath = path.resolve( coverageDir, - buildFileName(test, options.uniqueFileName) + buildFileName(test, options.uniqueFileName), ); debug(`writing ${coveragePath}`); fs.writeFileSync(coveragePath, JSON.stringify(coverage)); @@ -148,7 +148,7 @@ module.exports = function(config) { console.error(err); } }, - true + true, ); }); }; From aad91e8a327dca83ef8419e798c91e1601dd54f9 Mon Sep 17 00:00:00 2001 From: "david.villarama" Date: Wed, 20 Feb 2019 09:15:11 -0800 Subject: [PATCH 4/4] update from cr --- lib/plugin/puppeteerCoverage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/plugin/puppeteerCoverage.js b/lib/plugin/puppeteerCoverage.js index 5dd9b38bc..d0126a323 100644 --- a/lib/plugin/puppeteerCoverage.js +++ b/lib/plugin/puppeteerCoverage.js @@ -1,6 +1,7 @@ const Container = require('../container'); const recorder = require('../recorder'); const event = require('../event'); +const output = require('../output'); const fs = require('fs'); const path = require('path'); const { clearString } = require('../utils'); @@ -93,7 +94,7 @@ module.exports = function (config) { const options = Object.assign(defaultConfig, helper.options, config); event.dispatcher.on(event.all.before, async (suite) => { - debug('*** Collecting coverage for tests ****'); + output.debug('*** Collecting coverage for tests ****'); }); /** @@ -141,7 +142,7 @@ module.exports = function (config) { coverageDir, buildFileName(test, options.uniqueFileName), ); - debug(`writing ${coveragePath}`); + output.print(`writing ${coveragePath}`); fs.writeFileSync(coveragePath, JSON.stringify(coverage)); } } catch (err) {