├── .github ├── publish.yml └── release-please.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTIRUBTORS.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── bin ├── .gitkeep └── puppeteer-js-runner.js ├── index.js ├── lib ├── .gitkeep ├── output-files.js ├── puppeteer-to-istanbul.js └── puppeteer-to-v8.js ├── package.json └── test ├── fixtures ├── block-else-not-covered.json ├── block-logical-not-covered.json ├── function-coverage-full-duplicate.json ├── function-coverage-missing.json ├── http-es6-modules.json ├── inline-and-external-script-coverage.json ├── inline-script-coverage.json ├── merge-coverage │ ├── non-inline-script-a.json │ └── non-inline-script-b.json └── two-inline.json ├── output-files.js ├── puppeteer-to-istanbul.js ├── puppeteer-to-v8.js └── sample_js ├── block-else-not-covered.js ├── block-logical-not-covered.js ├── function-coverage-100.js ├── function-coverage-missing.js ├── function-no-coverage.js ├── inline.js └── two-inline.js /.github/publish.yml: -------------------------------------------------------------------------------- 1 | project: oss-automation 2 | secretId: node-tooling 3 | -------------------------------------------------------------------------------- /.github/release-please.yml: -------------------------------------------------------------------------------- 1 | releaseType: node 2 | handleGHRelease: true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | package-lock.json 4 | .nyc_output 5 | coverage -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "node" 5 | after_success: 6 | - c8 report --reporter=text-lcov | coveralls 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | ## [1.4.0](https://www.github.com/istanbuljs/puppeteer-to-istanbul/compare/v1.3.1...v1.4.0) (2020-06-11) 7 | 8 | 9 | ### Features 10 | 11 | * allow a custom storagePath ([#56](https://www.github.com/istanbuljs/puppeteer-to-istanbul/issues/56)) ([eb1aabc](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/eb1aabc705ed10bfc5010128eb0fbaf69c17f777)) 12 | * allow the output file to not include the hostname ([#57](https://www.github.com/istanbuljs/puppeteer-to-istanbul/issues/57)) ([3a5b312](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/3a5b312c0b02a9a2115a54ddd5095b3684257e78)) 13 | 14 | ### [1.3.1](https://www.github.com/istanbuljs/puppeteer-to-istanbul/compare/v1.3.0...v1.3.1) (2020-05-11) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * JSON encode URL ([8aa1c44](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/8aa1c448252da995e7507272878eb00f44242215)) 20 | 21 | ## [1.3.0](https://www.github.com/istanbuljs/puppeteer-to-istanbul/compare/v1.2.2...v1.3.0) (2020-04-14) 22 | 23 | 24 | ### Features 25 | 26 | * add support for complete path reporting on files with http(s) protocol ([#17](https://www.github.com/istanbuljs/puppeteer-to-istanbul/issues/17)) ([149716b](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/149716b5323b7b1025e43c17e577744601528f72)) 27 | * include original url in final output. ([#34](https://www.github.com/istanbuljs/puppeteer-to-istanbul/issues/34)) ([dec48a2](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/dec48a25d0e2145ad44a485d591b1b58d1039091)) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * do not overwrite coverage after each test suite ([#42](https://www.github.com/istanbuljs/puppeteer-to-istanbul/issues/42)) ([848aa76](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/848aa76533ce63f09a319d377b84163151505a5e)) 33 | * JSON being output was not valid ([de9109c](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/de9109c794523eb8924ef24770503908650cc952)) 34 | * write to disk incrementally ([#40](https://www.github.com/istanbuljs/puppeteer-to-istanbul/issues/40)) ([c57bd74](https://www.github.com/istanbuljs/puppeteer-to-istanbul/commit/c57bd741534813a7b42c8f300ddb61ef42086ef1)) 35 | 36 | ## [1.2.2](https://github.com/istanbuljs/puppeteer-to-istanbul/compare/v1.2.1...v1.2.2) (2018-03-03) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * we need to create the .nyc_output folder if it doesn't exist ([17c9e69](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/17c9e69)) 42 | 43 | 44 | 45 | 46 | ## [1.2.1](https://github.com/istanbuljs/puppeteer-to-istanbul/compare/v1.2.0...v1.2.1) (2018-03-01) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * clone should be a regular dependency not a dev dependency ([9dad7fc](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/9dad7fc)) 52 | 53 | 54 | 55 | 56 | # [1.2.0](https://github.com/istanbuljs/puppeteer-to-istanbul/compare/v1.1.0...v1.2.0) (2018-02-25) 57 | 58 | 59 | ### Bug Fixes 60 | 61 | * file:// was missing ([0ec1a0a](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/0ec1a0a)) 62 | * output format didn't quite match v8 ([#5](https://github.com/istanbuljs/puppeteer-to-istanbul/issues/5)) ([8b669f8](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/8b669f8)) 63 | 64 | 65 | ### Features 66 | 67 | * add entry-point document ([#10](https://github.com/istanbuljs/puppeteer-to-istanbul/issues/10)) ([1f49ae5](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/1f49ae5)) 68 | * Implemented JS local copying and renaming for conversion to v8 ([#6](https://github.com/istanbuljs/puppeteer-to-istanbul/issues/6)) ([be72192](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/be72192)) 69 | * implemented puppeteer to V8 class ([#4](https://github.com/istanbuljs/puppeteer-to-istanbul/issues/4)) ([327c1ef](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/327c1ef)) 70 | 71 | 72 | 73 | 74 | # 1.1.0 (2018-02-24) 75 | 76 | 77 | ### Features 78 | 79 | * add command-line-tool for outputting coverage in puppeteer format ([#1](https://github.com/istanbuljs/puppeteer-to-istanbul/issues/1)) ([111354d](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/111354d)) 80 | * initial library structure ([6b2deb5](https://github.com/istanbuljs/puppeteer-to-istanbul/commit/6b2deb5)) 81 | -------------------------------------------------------------------------------- /CONTIRUBTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | - [Benjamin E. Coe](https://github.com/bcoe) (ben@npmjs.com) 4 | 5 | Mentor and Project Lead 6 | - [Viktor Köves](https://github.com/vkoves/) (vkoves@hawk.iit.edu) 7 | 8 | Handled logic for converting from Puppeteer to V8 coverage format 9 | - [Robert Altman](https://github.com/rjaltman) (rjaltman04@gmail.com) 10 | 11 | Worked on handling file storage for Puppeteer to Istanbul 12 | - [Ashwin Gokhale](https://github.com/ashwinGokhale) (gokhale0@purdue.edu) 13 | 14 | Worked on a system to show coverage on the splash page 15 | - [Mustafa Musabeyli](https://github.com/musabeyli/) (musabeyli@wisc.edu) 16 | 17 | Styled and created the splash page 18 | - [Tony An](https://github.com/computerguy708/) (tan1@hawk.iit.edu) 19 | 20 | Wrote copy for the splash page 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2018, Contributors 4 | 5 | Permission to use, copy, modify, and/or distribute this software 6 | for any purpose with or without fee is hereby granted, provided 7 | that the above copyright notice and this permission notice 8 | appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 12 | OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE 13 | LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 14 | OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 15 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 16 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Puppeteer to Istanbul 2 | 3 | [![Build Status](https://travis-ci.org/istanbuljs/puppeteer-to-istanbul.svg?branch=master)](https://travis-ci.org/istanbuljs/puppeteer-to-istanbul) 4 | [![Coverage Status](https://coveralls.io/repos/github/istanbuljs/puppeteer-to-istanbul/badge.svg?branch=master)](https://coveralls.io/github/istanbuljs/puppeteer-to-istanbul?branch=master) 5 | [![Standard Version](https://img.shields.io/badge/release-standard%20version-brightgreen.svg)](https://github.com/conventional-changelog/standard-version) 6 | 7 | Convert coverage from the format outputted by [puppeteer](https://developers.google.com/web/tools/puppeteer/) to a format consumable by [Istanbul][istanbul]. 8 | 9 | ## Usage 10 | 11 | ### To Output Coverage in Istanbul Format with Puppeteer 12 | 13 | 1. install _puppeteer_, `npm i -D puppeteer`. 14 | 2. install _puppeteer-to-istanbul_, `npm i -D puppeteer-to-istanbul`. 15 | 3. run your code in puppeteer with coverage enabled: 16 | 17 | ```js 18 | (async () => { 19 | const pti = require('puppeteer-to-istanbul') 20 | const puppeteer = require('puppeteer') 21 | const browser = await puppeteer.launch() 22 | const page = await browser.newPage() 23 | 24 | // Enable both JavaScript and CSS coverage 25 | await Promise.all([ 26 | page.coverage.startJSCoverage(), 27 | page.coverage.startCSSCoverage() 28 | ]); 29 | // Navigate to page 30 | await page.goto('https://www.google.com'); 31 | // Disable both JavaScript and CSS coverage 32 | const [jsCoverage, cssCoverage] = await Promise.all([ 33 | page.coverage.stopJSCoverage(), 34 | page.coverage.stopCSSCoverage(), 35 | ]); 36 | pti.write([...jsCoverage, ...cssCoverage], { includeHostname: true , storagePath: './.nyc_output' }) 37 | await browser.close() 38 | })() 39 | ``` 40 | 41 | ### To Check Istanbul Reports 42 | 43 | 1. install nyc, `npm i nyc -g`. 44 | 2. use nyc's report functionality: 45 | 46 | ```bash 47 | nyc report --reporter=html 48 | ``` 49 | 50 | _puppeteer-to-istanbul_ outputs temporary files in a format that can be 51 | consumed by nyc. 52 | 53 | see [istanbul](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) for a list of possible reporters. 54 | 55 | ## Contributing 56 | 57 | The best way to get started with Puppeteer to Istanbul is by installing it for yourself and running tests. 58 | PTI requires the most recent build of __v8toistanbul__ to function properly, so start by running `npm install`. 59 | 60 | Next, ensure that all tests are passing before continuing by running `npm test` (or equivalently, `npm t`). This should generate a report that gives the same coverage as seen on this README. 61 | 62 | Note that a majority of the tests run against pre-generated fixtures, or JSON snippets, that come from Puppeteer's raw output. These are located in the `\test\fixtures` area. To generate one of your own, write or use one of the scripts in the test area `test\sample_js`, and run `bin/puppeteer-js-runner.js` through node, like so: 63 | 64 | `node bin/puppeteer-js-runner.js --file=/test/sample_js/sample2.js`. 65 | 66 | If you see an issue with Puppeteer to Istanbul, please open an issue! If you want to help improve Puppeteer to Istanbul, please fork the repository and open a pull request with your changes. 67 | 68 | Make sure to review our [contributing guide][contributing] for specific guidelines on contributing. 69 | 70 | [coveralls]: https://github.com/GoogleChrome/puppeteer 71 | [istanbul]: https://github.com/istanbuljs/istanbuljs 72 | [nyc]: https://github.com/istanbuljs/nyc 73 | [contributing]: https://github.com/istanbuljs/puppeteer-to-istanbul/blob/master/CONTRIBUTING.md 74 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istanbuljs/puppeteer-to-istanbul/5947d2dcd8923cbade75aefab23150bab02cfa61/bin/.gitkeep -------------------------------------------------------------------------------- /bin/puppeteer-js-runner.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | const puppeteer = require('puppeteer') 6 | require('yargs') // eslint-disable-line 7 | .usage('$0 ', 'output coverage data from puppeteer', (yargs) => { 8 | yargs 9 | .positional('input', { 10 | default: 'test/sample_js/sample1.js', 11 | describe: 'file to load into headless chromium' 12 | }) 13 | .positional('output', { 14 | default: 'test/fixtures/out.json' 15 | }) 16 | }, (argv) => { 17 | outputPuppeteerCoverage( 18 | path.resolve(process.cwd(), argv.input), 19 | path.resolve(process.cwd(), argv.output) 20 | ) 21 | }) 22 | .help() 23 | .demandCommand(1) 24 | .argv 25 | 26 | async function outputPuppeteerCoverage (input, output) { 27 | const browser = await puppeteer.launch() 28 | const page = await browser.newPage() 29 | 30 | // Enable both JavaScript and CSS coverage 31 | await Promise.all([ 32 | page.coverage.startJSCoverage(), 33 | page.coverage.startCSSCoverage() 34 | ]) 35 | 36 | // Script src goes up two directories, since it is in /bin/tmp 37 | // This makes it so the script is specified from the project directory level 38 | const pageHtml = ` 39 | 40 | 41 | 42 | 43 | ` 44 | 45 | fs.writeFileSync('/tmp/puppeteerTemp.html', pageHtml, 'utf8') 46 | 47 | // Navigate to page 48 | let url = 'file:///' + '/tmp/puppeteerTemp.html' 49 | await page.goto(url) 50 | 51 | // Disable both JavaScript and CSS coverage 52 | const [jsCoverage, cssCoverage] = await Promise.all([ 53 | page.coverage.stopJSCoverage(), 54 | page.coverage.stopCSSCoverage() 55 | ]) 56 | 57 | fs.writeFileSync(output, JSON.stringify(jsCoverage, null, 2), 'utf8') 58 | 59 | let totalBytes = 0 60 | let usedBytes = 0 61 | const coverage = [...jsCoverage, ...cssCoverage] 62 | for (const entry of coverage) { 63 | totalBytes += entry.text.length 64 | for (const range of entry.ranges) { usedBytes += range.end - range.start - 1 } 65 | } 66 | console.log(`Bytes used: ${usedBytes / totalBytes * 100}%`) 67 | 68 | await browser.close() 69 | } 70 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const PuppeteerToIstanbul = require('./lib/puppeteer-to-istanbul') 2 | 3 | module.exports = { 4 | write: (puppeteerFormat, options) => { 5 | const pti = PuppeteerToIstanbul(puppeteerFormat, options) 6 | pti.writeIstanbulFormat(options) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/istanbuljs/puppeteer-to-istanbul/5947d2dcd8923cbade75aefab23150bab02cfa61/lib/.gitkeep -------------------------------------------------------------------------------- /lib/output-files.js: -------------------------------------------------------------------------------- 1 | // output JavaScript bundled in puppeteer output to format 2 | // that can be eaten by Istanbul. 3 | 4 | // TODO: Put function interfaces on this file 5 | 6 | const fs = require('fs') 7 | const mkdirp = require('mkdirp') 8 | const clone = require('clone') 9 | const pathLib = require('path') 10 | const url = require('url') 11 | 12 | let iterator = {} 13 | 14 | class OutputFiles { 15 | constructor (coverageInfo, options = {}) { 16 | this.storagePath = options.storagePath || './.nyc_output/js' 17 | this.includeHostname = options.hasOwnProperty('includeHostname') ? options.includeHostname : true 18 | 19 | // Clone coverageInfo to prevent mutating the passed in data 20 | this.coverageInfo = clone(coverageInfo) 21 | this._parseAndIsolate() 22 | } 23 | 24 | parsePath (path) { 25 | let urlPath 26 | 27 | try { 28 | urlPath = new url.URL(path) 29 | } catch (error) { 30 | path = 'file://' + path 31 | urlPath = new url.URL(path) 32 | } 33 | 34 | let postProtocolPath = urlPath.pathname.substring(1) 35 | 36 | if (urlPath.hostname && this.includeHostname) { 37 | let hostnameAndPort = urlPath.hostname 38 | if (urlPath.port) { 39 | hostnameAndPort = hostnameAndPort + '_' + urlPath.port 40 | } 41 | 42 | postProtocolPath = hostnameAndPort + '/' + postProtocolPath 43 | } 44 | 45 | return postProtocolPath 46 | } 47 | 48 | rewritePath (path) { 49 | // generate a new path relative to ./coverage/js. 50 | // this would be around where you'd use mkdirp. 51 | 52 | let str = `` 53 | let parsedPath = this.parsePath(path) 54 | let isInline = false 55 | // Special case: when html present, strip and return specialized string 56 | if (pathLib.extname(parsedPath) === '.html') { 57 | isInline = true 58 | parsedPath = pathLib.resolve(this.storagePath, parsedPath + 'puppeteerTemp-inline') 59 | } else { 60 | parsedPath = pathLib.resolve(this.storagePath, pathLib.dirname(parsedPath), pathLib.basename(parsedPath, '.js')) 61 | } 62 | mkdirp.sync(this.storagePath) 63 | if (fs.existsSync(parsedPath + '.js') && isInline) { 64 | if (!Number.isInteger(iterator[parsedPath])) { 65 | iterator[parsedPath] = 1 66 | } else { 67 | iterator[parsedPath]++ 68 | } 69 | str = `${parsedPath}-${iterator[parsedPath]}.js` 70 | return str 71 | } else { 72 | str = `${parsedPath}.js` 73 | return str 74 | } 75 | } 76 | 77 | _parseAndIsolate () { 78 | for (let i = 0; i < this.coverageInfo.length; i++) { 79 | let path = this.rewritePath(this.coverageInfo[i].url) 80 | 81 | this.coverageInfo[i].originalUrl = this.coverageInfo[i].url 82 | this.coverageInfo[i].url = path 83 | 84 | mkdirp.sync(pathLib.parse(path).dir) 85 | 86 | fs.writeFileSync(path, this.coverageInfo[i].text) 87 | } 88 | } 89 | 90 | getTransformedCoverage () { 91 | return this.coverageInfo 92 | } 93 | } 94 | 95 | function genOutputFiles (coverageInfo, options) { 96 | return new OutputFiles(coverageInfo, options) 97 | } 98 | 99 | genOutputFiles.resetIterator = function () { 100 | iterator = {} 101 | } 102 | 103 | module.exports = genOutputFiles 104 | -------------------------------------------------------------------------------- /lib/puppeteer-to-istanbul.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const OutputFiles = require('./output-files') 3 | const mkdirp = require('mkdirp') 4 | const PuppeteerToV8 = require('./puppeteer-to-v8') 5 | const v8toIstanbul = require('v8-to-istanbul') 6 | 7 | let jsonPart = {} 8 | 9 | class PuppeteerToIstanbul { 10 | constructor (coverageInfo, options = {}) { 11 | this.storagePath = options.storagePath || './.nyc_output' 12 | this.includeHostname = options.hasOwnProperty('includeHostname') ? options.includeHostname : true 13 | 14 | this.coverageInfo = coverageInfo 15 | this.options = options 16 | this.puppeteerToConverter = OutputFiles(coverageInfo, options).getTransformedCoverage() 17 | this.puppeteerToV8Info = PuppeteerToV8(this.puppeteerToConverter).convertCoverage() 18 | } 19 | 20 | setCoverageInfo (coverageInfo) { 21 | this.coverageInfo = coverageInfo 22 | } 23 | 24 | writeIstanbulFormat () { 25 | mkdirp.sync(this.storagePath) 26 | 27 | const outFilePath = `${this.storagePath}/out.json` 28 | 29 | fs.writeFileSync(outFilePath, '') 30 | 31 | const fd = fs.openSync(outFilePath, 'a') 32 | 33 | this.puppeteerToV8Info.forEach((jsFile, index) => { 34 | const script = v8toIstanbul(jsFile.url) 35 | script.applyCoverage(jsFile.functions) 36 | 37 | let istanbulCoverage = script.toIstanbul() 38 | let keys = Object.keys(istanbulCoverage) 39 | 40 | if (jsonPart[keys[0]]) { 41 | // Merge coverage records 42 | mergeCoverageData(jsonPart[keys[0]].s, istanbulCoverage[keys[0]].s) 43 | } else { 44 | jsonPart[keys[0]] = istanbulCoverage[keys[0]] 45 | } 46 | jsonPart[keys[0]].originalUrl = jsFile.originalUrl 47 | }) 48 | 49 | fs.writeSync(fd, '{') 50 | Object.keys(jsonPart).forEach((url, index, keys) => { 51 | const data = jsonPart[url] 52 | const isLastIteration = index === (keys.length - 1) 53 | 54 | fs.writeSync(fd, `${JSON.stringify(url)}: ${JSON.stringify(data)}${(isLastIteration ? '' : ',')}`) 55 | }) 56 | fs.writeSync(fd, '}') 57 | fs.closeSync(fd) 58 | } 59 | } 60 | 61 | function mergeCoverageData (obja, objb) { 62 | Object.keys(obja).forEach(key => { 63 | obja[key] = (obja[key] || objb[key]) ? 1 : 0 64 | }) 65 | return obja 66 | } 67 | 68 | function genPuppeteerToIstanbul (coverageInfo, options) { 69 | return new PuppeteerToIstanbul(coverageInfo, options) 70 | } 71 | 72 | genPuppeteerToIstanbul.resetJSONPart = function () { 73 | jsonPart = {} 74 | } 75 | 76 | genPuppeteerToIstanbul.getJSONPart = function () { 77 | return JSON.parse(JSON.stringify(jsonPart)) 78 | } 79 | 80 | genPuppeteerToIstanbul.mergeCoverageData = mergeCoverageData 81 | 82 | module.exports = genPuppeteerToIstanbul 83 | -------------------------------------------------------------------------------- /lib/puppeteer-to-v8.js: -------------------------------------------------------------------------------- 1 | class PuppeteerToV8 { 2 | constructor (coverageInfo) { 3 | this.coverageInfo = coverageInfo 4 | } 5 | 6 | setCoverageInfo (coverageInfo) { 7 | this.coverageInfo = coverageInfo 8 | } 9 | 10 | convertCoverage () { 11 | // Iterate through coverage info and create IDs 12 | let id = 0 13 | 14 | return this.coverageInfo.map(coverageItem => { 15 | return { 16 | scriptId: id++, 17 | url: 'file://' + coverageItem.url, 18 | originalUrl: coverageItem.originalUrl, 19 | functions: [{ 20 | ranges: coverageItem.ranges.map(this.convertRange), 21 | isBlockCoverage: true 22 | }] 23 | } 24 | }) 25 | } 26 | 27 | // Takes in a Puppeteer range object with start and end properties and 28 | // converts it to a V8 range with startOffset, endOffset, and count properties 29 | convertRange (range) { 30 | return { 31 | startOffset: range.start, 32 | endOffset: range.end, 33 | count: 1 34 | } 35 | } 36 | } 37 | 38 | module.exports = (coverageInfo) => new PuppeteerToV8(coverageInfo) 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppeteer-to-istanbul", 3 | "version": "1.4.0", 4 | "description": "convert from puppeteer's coverage output to a format that can be used by istanbul reports", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "c8 --all mocha test/*.js", 8 | "posttest": "standard" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/istanbuljs/puppeteer-to-istanbul.git" 13 | }, 14 | "keywords": [ 15 | "istanbul", 16 | "puppeteer", 17 | "convert", 18 | "coverage" 19 | ], 20 | "author": "Hackillinois 2018, team Istanbul", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/istanbuljs/puppeteer-to-istanbul/issues" 24 | }, 25 | "homepage": "https://github.com/istanbuljs/puppeteer-to-istanbul#readme", 26 | "devDependencies": { 27 | "c8": "^7.1.0", 28 | "chai": "^4.2.0", 29 | "coveralls": "^3.0.11", 30 | "mocha": "^7.1.1", 31 | "puppeteer": "^2.1.1", 32 | "rimraf": "^3.0.0", 33 | "standard": "^11.0.0" 34 | }, 35 | "dependencies": { 36 | "clone": "^2.1.2", 37 | "mkdirp": "^1.0.4", 38 | "v8-to-istanbul": "^1.2.1", 39 | "yargs": "^15.3.1" 40 | }, 41 | "standard": { 42 | "ignore": "test/sample_js" 43 | }, 44 | "c8": { 45 | "include": [ 46 | "**/lib/*.js" 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/fixtures/block-else-not-covered.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:////Users/benjamincoe/bcoe/puppeteer-to-istanbul/test/sample_js/block-else-not-covered.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 44 8 | } 9 | ], 10 | "text": "if (true) {\n console.info('hello world!')\n} else {\n function fib () {\n return 'i am the fibonacci sequence'\n }\n}" 11 | } 12 | ] -------------------------------------------------------------------------------- /test/fixtures/block-logical-not-covered.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:////Users/benjamincoe/bcoe/puppeteer-to-istanbul/test/sample_js/block-logical-not-covered.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 27 8 | }, 9 | { 10 | "start": 61, 11 | "end": 73 12 | }, 13 | { 14 | "start": 78, 15 | "end": 111 16 | } 17 | ], 18 | "text": "const a = true\n\nif (false) {\n console.info('hello world!')\n} else if (a || !a) {\n console.info('i did run')\n} else {\n console.info('i should not run')\n}" 19 | } 20 | ] -------------------------------------------------------------------------------- /test/fixtures/function-coverage-full-duplicate.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:////Users/benjamincoe/bcoe/puppeteer-to-istanbul/test/sample_js/function-coverage-100.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 46 8 | }, 9 | { 10 | "start": 47, 11 | "end": 85 12 | }, 13 | { 14 | "start": 86, 15 | "end": 102 16 | } 17 | ], 18 | "text": "function a (num1, num2) {\n return num1 + num2\n}\n\nfunction b (num) {\n return num + 1\n}\n\na(1, 2)\nb(3)\n" 19 | }, 20 | { 21 | "url": "file:////Users/benjamincoe/bcoe/puppeteer-to-istanbul/test/sample_js/function-coverage-100.js", 22 | "ranges": [ 23 | { 24 | "start": 0, 25 | "end": 46 26 | }, 27 | { 28 | "start": 47, 29 | "end": 85 30 | }, 31 | { 32 | "start": 86, 33 | "end": 102 34 | } 35 | ], 36 | "text": "function a (num1, num2) {\n return num1 + num2\n}\n\nfunction b (num) {\n return num + 1\n}\n\na(1, 2)\nb(3)\n" 37 | } 38 | ] -------------------------------------------------------------------------------- /test/fixtures/function-coverage-missing.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:////Users/benjamincoe/bcoe/puppeteer-to-istanbul/test/sample_js/function-coverage-missing-1.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 46 8 | }, 9 | { 10 | "start": 47, 11 | "end": 50 12 | }, 13 | { 14 | "start": 87, 15 | "end": 97 16 | } 17 | ], 18 | "text": "function a(num1, num2) {\n return num1 + num2;\n}\n\nfunction b(num) {\n return num + 1;\n}\n\na(1, 2);" 19 | } 20 | ] -------------------------------------------------------------------------------- /test/fixtures/http-es6-modules.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "http://localhost:9000/js/index.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 277 8 | } 9 | ], 10 | "text": "import docReady from \"./utils/doc_ready.js\";\nimport Record from \"./models/record.js\";\nimport RecordView from \"./views/record.js\";\n\ndocReady(() => {\n let record = new Record(3, 4);\n let recordView = new RecordView(record);\n document.body.appendChild(recordView.render())\n});\n" 11 | }, 12 | { 13 | "url": "http://localhost/js/utils/doc_ready.js", 14 | "ranges": [ 15 | { 16 | "start": 0, 17 | "end": 80 18 | }, 19 | { 20 | "start": 146, 21 | "end": 150 22 | } 23 | ], 24 | "text": "export default (fn) => {\n if (document.readyState != \"loading\") {\n fn();\n } else {\n document.addEventListener(\"DOMContentLoaded\", fn);\n }\n};\n" 25 | }, 26 | { 27 | "url": "http://localhost:9000/js/models/record.js", 28 | "ranges": [ 29 | { 30 | "start": 0, 31 | "end": 90 32 | } 33 | ], 34 | "text": "export default class Record {\n constructor(x, y) {\n this.x = x;\n this.y = y;\n }\n}\n" 35 | }, 36 | { 37 | "url": "http://localhost:9000/js/views/record.js", 38 | "ranges": [ 39 | { 40 | "start": 0, 41 | "end": 298 42 | }, 43 | { 44 | "start": 301, 45 | "end": 304 46 | } 47 | ], 48 | "text": "export default class RecordView {\n constructor(record) {\n this.record = record\n }\n\n render() {\n this.elem = document.createElement(\"div\");\n this.elem.appendChild(\n document.createTextNode(\n \"x: \" + this.record.x + \"; y: \" + this.record.y\n )\n );\n return this.elem;\n }\n}" 49 | } 50 | ] -------------------------------------------------------------------------------- /test/fixtures/inline-and-external-script-coverage.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:////home/viktor/Documents/HackIllinois2018/puppeteer-to-istanbul/test/sample_js/block-else-not-covered.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 44 8 | } 9 | ], 10 | "text": "if (true) {\n console.info('hello world!')\n} else {\n function fib () {\n return 'i am the fibonacci sequence'\n }\n}" 11 | }, 12 | { 13 | "url": "file:///tmp/puppeteerTemp.html", 14 | "ranges": [ 15 | { 16 | "start": 0, 17 | "end": 55 18 | }, 19 | { 20 | "start": 60, 21 | "end": 66 22 | }, 23 | { 24 | "start": 109, 25 | "end": 126 26 | } 27 | ], 28 | "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " 29 | } 30 | ] -------------------------------------------------------------------------------- /test/fixtures/inline-script-coverage.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:///tmp/puppeteerTemp.html", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 59 8 | }, 9 | { 10 | "start": 66, 11 | "end": 74 12 | }, 13 | { 14 | "start": 121, 15 | "end": 140 16 | } 17 | ], 18 | "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " 19 | }, 20 | { 21 | "url": "file:///tmp/puppeteerTemp.html", 22 | "ranges": [ 23 | { 24 | "start": 0, 25 | "end": 59 26 | }, 27 | { 28 | "start": 66, 29 | "end": 74 30 | }, 31 | { 32 | "start": 121, 33 | "end": 140 34 | } 35 | ], 36 | "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /test/fixtures/merge-coverage/non-inline-script-a.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "http://localhost:9000/js/index.js", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 55 8 | } 9 | ], 10 | "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " 11 | }, 12 | { 13 | "url": "file:///tmp/puppeteerTemp.html", 14 | "ranges": [ 15 | { 16 | "start": 0, 17 | "end": 68 18 | }, 19 | { 20 | "start": 73, 21 | "end": 93 22 | } 23 | ], 24 | "text": "\n function e(num4, num5, num6) {\n return num4 * num5 - num6;\n }\n e(3,5,6);\n " 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /test/fixtures/merge-coverage/non-inline-script-b.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "http://localhost:9000/js/index.js", 4 | "ranges": [ 5 | { 6 | "start": 60, 7 | "end": 66 8 | }, 9 | { 10 | "start": 109, 11 | "end": 126 12 | } 13 | ], 14 | "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " 15 | }, 16 | { 17 | "url": "file:///tmp/puppeteerTemp.html", 18 | "ranges": [ 19 | { 20 | "start": 0, 21 | "end": 68 22 | }, 23 | { 24 | "start": 73, 25 | "end": 93 26 | } 27 | ], 28 | "text": "\n function e(num4, num5, num6) {\n return num4 * num5 - num6;\n }\n e(3,5,6);\n " 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/fixtures/two-inline.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "file:///tmp/puppeteerTemp.html", 4 | "ranges": [ 5 | { 6 | "start": 0, 7 | "end": 55 8 | }, 9 | { 10 | "start": 60, 11 | "end": 66 12 | }, 13 | { 14 | "start": 109, 15 | "end": 126 16 | } 17 | ], 18 | "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " 19 | }, 20 | { 21 | "url": "file:///tmp/puppeteerTemp.html", 22 | "ranges": [ 23 | { 24 | "start": 0, 25 | "end": 68 26 | }, 27 | { 28 | "start": 73, 29 | "end": 93 30 | } 31 | ], 32 | "text": "\n function e(num4, num5, num6) {\n return num4 * num5 - num6;\n }\n e(3,5,6);\n " 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /test/output-files.js: -------------------------------------------------------------------------------- 1 | /* globals describe, it, beforeEach */ 2 | 3 | const OutputFiles = require('../lib/output-files') 4 | const rimraf = require('rimraf') 5 | const pathLib = require('path') 6 | const url = require('url') 7 | 8 | const storagePath = '.nyc_output/js' 9 | const storagePathTop = '.nyc_output' 10 | 11 | require('chai').should() 12 | 13 | describe('output-files', () => { 14 | beforeEach(cleanupCoverage) 15 | 16 | // we can use path.basename(path[, ext]). 17 | it('exposes a handler that appropriately handles colliding names', () => { 18 | const outputFiles = OutputFiles(require('./fixtures/block-else-not-covered.json')) 19 | 20 | // Since block-else-not-covered was generated by the above line, this 21 | // should make a new file with -1 appended to the name 22 | var newPath = outputFiles.rewritePath('./sample_js/block-else-not-covered-1.js') 23 | if (newPath.includes('\\')) newPath = newPath.replace(/\\/g, '/') 24 | newPath.should.include(storagePath + '/sample_js/block-else-not-covered-1.js') 25 | }) 26 | 27 | it('handle multiple files with same name, and replace in json', () => { 28 | // Input from the fixture should be JSONified already 29 | const fixture = require('./fixtures/function-coverage-full-duplicate.json') 30 | const coverageInfo = OutputFiles(fixture).getTransformedCoverage() 31 | 32 | // Fixture should and output coverage should not be in the same place 33 | coverageInfo[0].url.should.not.eql(fixture[0].url) 34 | coverageInfo[1].url.should.not.eql(fixture[1].url) 35 | 36 | coverageInfo[0].url.should.eql(movedUrl(fixture[0].url)) 37 | // It's not inline script and has the same url, so we could know both of them are same file. 38 | // There is no need to create `-1.js` file. 39 | // In case multiple page instance load the same js file. 40 | coverageInfo[1].url.should.eql(movedUrl(fixture[0].url)) 41 | }) 42 | 43 | // call it something like indexHTML-inline-1.js 44 | it('appropriately handles only inline JavaScript', () => { 45 | const fixture = require('./fixtures/inline-script-coverage.json') 46 | // Reset iterator 47 | OutputFiles.resetIterator() 48 | const coverageInfo = OutputFiles(fixture).getTransformedCoverage() 49 | 50 | coverageInfo[0].url.should.include('puppeteerTemp-inline.js') 51 | coverageInfo[1].url.should.include('puppeteerTemp-inline-1.js') 52 | }) 53 | 54 | it('appropriately handles inline and external JavaScript', () => { 55 | const fixture = require('./fixtures/inline-and-external-script-coverage.json') 56 | const coverageInfo = OutputFiles(fixture).getTransformedCoverage() 57 | 58 | coverageInfo[0].url.should.eql(movedUrl(fixture[0].url)) 59 | coverageInfo[1].url.should.include('puppeteerTemp-inline.js') 60 | }) 61 | 62 | it('appropriately handles two cases of inline JavaScript', () => { 63 | const fixture = require('./fixtures/two-inline.json') 64 | // Reset iterator 65 | OutputFiles.resetIterator() 66 | const coverageInfo = OutputFiles(fixture).getTransformedCoverage() 67 | 68 | coverageInfo[0].url.should.include('puppeteerTemp-inline.js') 69 | coverageInfo[1].url.should.include('puppeteerTemp-inline-1.js') 70 | }) 71 | 72 | it('appropriately handles modules required via http/https', () => { 73 | const fixture = require('./fixtures/http-es6-modules.json') 74 | const coverageInfo = OutputFiles(fixture).getTransformedCoverage().map(info => ({...info, url: info.url.replace(/\\/g, '/')})) 75 | 76 | coverageInfo[0].url.should.include('js/index.js') 77 | coverageInfo[1].url.should.include('js/utils/doc_ready.js') 78 | coverageInfo[2].url.should.include('js/models/record.js') 79 | coverageInfo[3].url.should.include('js/views/record.js') 80 | }) 81 | 82 | it('appropriately handles modules required via http/https, with hostName excluded', () => { 83 | const fixture = require('./fixtures/http-es6-modules.json') 84 | const coverageInfo = OutputFiles(fixture, { includeHostname: false }).getTransformedCoverage().map(info => ({...info, url: info.url.replace(/\\/g, '/')})) 85 | 86 | coverageInfo[0].url.should.include('js/index.js') 87 | coverageInfo[1].url.should.include('js/utils/doc_ready.js') 88 | coverageInfo[2].url.should.include('js/models/record.js') 89 | coverageInfo[3].url.should.include('js/views/record.js') 90 | }) 91 | 92 | it('maintains original url in output', () => { 93 | const fixture = require('./fixtures/http-es6-modules.json') 94 | const coverageInfo = OutputFiles(fixture).getTransformedCoverage() 95 | 96 | coverageInfo[0].originalUrl.should.equal(fixture[0].url) 97 | coverageInfo[1].originalUrl.should.equal(fixture[1].url) 98 | coverageInfo[2].originalUrl.should.equal(fixture[2].url) 99 | coverageInfo[3].originalUrl.should.equal(fixture[3].url) 100 | }) 101 | 102 | function cleanupCoverage () { 103 | rimraf.sync(storagePathTop) 104 | } 105 | 106 | // Takes in a script and rewrites it to the path we expect in /coverage/js 107 | function movedUrl (path) { 108 | var urlPath = new url.URL(path) 109 | urlPath = urlPath.pathname.substring(1) 110 | return pathLib.resolve(storagePath, urlPath) 111 | } 112 | }) 113 | -------------------------------------------------------------------------------- /test/puppeteer-to-istanbul.js: -------------------------------------------------------------------------------- 1 | /* globals describe, it */ 2 | 3 | const should = require('chai').should() 4 | const fs = require('fs') 5 | const OutputFiles = require('../lib/output-files') 6 | 7 | var PuppeteerToIstanbul = require('../lib/puppeteer-to-istanbul') 8 | 9 | describe('puppeteer-to-istanbul', () => { 10 | it('outputs a valid out.json file, to the default location', () => { 11 | const fixture = require('./fixtures/two-inline.json') 12 | const pti = PuppeteerToIstanbul(fixture) 13 | pti.writeIstanbulFormat() 14 | const content = fs.readFileSync('.nyc_output/out.json', 'utf8') 15 | const jsonObject = JSON.parse(content) 16 | should.exist(jsonObject) 17 | fs.unlinkSync('.nyc_output/out.json') 18 | }) 19 | 20 | it('outputs a valid out.json file, in the custom location', () => { 21 | const fixture = require('./fixtures/two-inline.json') 22 | const pti = PuppeteerToIstanbul(fixture, { storagePath: '.nyc_output/custom' }) 23 | pti.writeIstanbulFormat() 24 | const content = fs.readFileSync('.nyc_output/custom/out.json', 'utf8') 25 | const jsonObject = JSON.parse(content) 26 | should.exist(jsonObject) 27 | fs.unlinkSync('.nyc_output/custom/out.json') 28 | }) 29 | 30 | it('correctly sets coverage info', () => { 31 | const fixture = require('./fixtures/two-inline.json') 32 | const pti = PuppeteerToIstanbul(fixture) 33 | pti.setCoverageInfo(fixture) 34 | pti.coverageInfo.should.eql(fixture) 35 | }) 36 | 37 | it('merge non-inline script coverage records', () => { 38 | OutputFiles.resetIterator() 39 | PuppeteerToIstanbul.resetJSONPart() 40 | 41 | const fixtureNonInlineScriptA = require('./fixtures/merge-coverage/non-inline-script-a.json') 42 | const fixtureNonInlineScriptB = require('./fixtures/merge-coverage/non-inline-script-b.json') 43 | const ptiA = PuppeteerToIstanbul(fixtureNonInlineScriptA) 44 | ptiA.writeIstanbulFormat() 45 | const jsonPartA = PuppeteerToIstanbul.getJSONPart() 46 | PuppeteerToIstanbul.resetJSONPart() 47 | const ptiB = PuppeteerToIstanbul(fixtureNonInlineScriptB) 48 | ptiB.writeIstanbulFormat() 49 | const jsonPartB = PuppeteerToIstanbul.getJSONPart() 50 | PuppeteerToIstanbul.resetJSONPart() 51 | OutputFiles.resetIterator() 52 | PuppeteerToIstanbul.resetJSONPart() 53 | const pti = PuppeteerToIstanbul(fixtureNonInlineScriptA.concat(fixtureNonInlineScriptB)) 54 | pti.writeIstanbulFormat() 55 | const jsonPart = PuppeteerToIstanbul.getJSONPart() 56 | const keys = Object.keys(jsonPart) 57 | 58 | // Non-inline script data should be merged. 59 | jsonPart[keys[0]].s.should.deep.equal(PuppeteerToIstanbul.mergeCoverageData(jsonPartA[keys[0]].s, jsonPartB[keys[0]].s)) 60 | // Inline script data should not be merged. So, there would be two inline files. Just likes: 61 | // [ 62 | // "/Users/puppeteer-to-istanbul/.nyc_output/js/localhost_9000/js/index.js" 63 | // "/Users/puppeteer-to-istanbul/.nyc_output/js/tmp/puppeteerTemp.htmlpuppeteerTemp-inline-1.js" 64 | // "/Users/puppeteer-to-istanbul/.nyc_output/js/tmp/puppeteerTemp.htmlpuppeteerTemp-inline-2.js" 65 | // ] 66 | Object.keys(jsonPart).should.deep.equal(Array.from(new Set(Object.keys(jsonPartA).concat(Object.keys(jsonPartB))))) 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /test/puppeteer-to-v8.js: -------------------------------------------------------------------------------- 1 | /* globals describe, it, before */ 2 | 3 | var PuppeteerToV8 = require('../lib/puppeteer-to-v8')() 4 | 5 | require('chai').should() 6 | 7 | describe('puppeteer-to-v8', () => { 8 | let v8Coverage 9 | const fixture = require('./fixtures/function-coverage-missing') 10 | 11 | before(() => { 12 | PuppeteerToV8.setCoverageInfo(fixture) 13 | 14 | v8Coverage = PuppeteerToV8.convertCoverage() 15 | }) 16 | 17 | it('translates ranges into v8 format', () => { 18 | // V8 coverage has ranges on a functions object, so check for that 19 | let firstV8Range = v8Coverage[0].functions[0].ranges[0] 20 | let firstFixtureRange = fixture[0].ranges[0] 21 | 22 | // The V8 range object has a few transformations, in particular 23 | // start -> startOffset, end -> endOffset and count = 1 being added 24 | firstV8Range.startOffset.should.eql(firstFixtureRange.start) 25 | firstV8Range.endOffset.should.eql(firstFixtureRange.end) 26 | firstV8Range.count.should.eql(1) 27 | }) 28 | 29 | // look at the uuid library: 30 | // uuid.v4() 31 | it('generates scriptId', () => { 32 | // Ensures that the scriptId is of type 'number' 33 | (typeof v8Coverage[0].scriptId).should.eql('number') 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/sample_js/block-else-not-covered.js: -------------------------------------------------------------------------------- 1 | if (true) { 2 | console.info('hello world!') 3 | } else { 4 | function fib () { 5 | return 'i am the fibonacci sequence' 6 | } 7 | } -------------------------------------------------------------------------------- /test/sample_js/block-logical-not-covered.js: -------------------------------------------------------------------------------- 1 | const a = true 2 | 3 | if (false) { 4 | console.info('hello world!') 5 | } else if (a || !a) { 6 | console.info('i did run') 7 | } else { 8 | console.info('i should not run') 9 | } -------------------------------------------------------------------------------- /test/sample_js/function-coverage-100.js: -------------------------------------------------------------------------------- 1 | function a (num1, num2) { 2 | return num1 + num2 3 | } 4 | 5 | function b (num) { 6 | return num + 1 7 | } 8 | 9 | a(1, 2) 10 | b(3) 11 | -------------------------------------------------------------------------------- /test/sample_js/function-coverage-missing.js: -------------------------------------------------------------------------------- 1 | function a (num1, num2) { 2 | return num1 + num2 3 | } 4 | 5 | function b (num) { 6 | return num + 1 7 | } 8 | 9 | a(1, 2) 10 | -------------------------------------------------------------------------------- /test/sample_js/function-no-coverage.js: -------------------------------------------------------------------------------- 1 | function a (num1, num2) { 2 | return num1 + num2 3 | } 4 | function b (num) { 5 | return num + 1 6 | } 7 | -------------------------------------------------------------------------------- /test/sample_js/inline.js: -------------------------------------------------------------------------------- 1 | /* 2 | This was included as a