Warning
This is a very experimental approach to calculating CSS Code Coverage and currently very much a work in progress.
Takes your generated coverage files and turns them into something actually usable. Accepts coverage reports generated by browsers (Edge/Chrome/chromium), Puppeteer, Playwright.
Features include:
- 🤩 Prettifies CSS for easy inspection and updates coverage ranges after prettification
- 🪄 Marks each line of each CSS file as covered or uncovered
- 📑 A single stylesheet that's reported over multiple URL's is combined into a single one, coverage ranges merged
- 🗂️ Creates a report of total line coverage, byte coverage and coverage details per individual stylesheet discovered
npm install @projectwallace/css-code-coverageimport { calculate_coverage } from '@projectwallace/css-code-coverage'
function parse_html(html) {
return new DOMParser().parseFromString(html, 'text/html')
}
let report = calculcate_coverage(coverage_data, parse_html)See src/index.ts for the data that's returned.
There are two principal ways of collecting CSS Coverage data:
In Edge, Chrome or chromium you can manually collect coverage in the browser's DevTools. In all cases you'll generate coverage data manually and the browser will let you export the data to a JSON file. Note that this JSON contains both JS coverage as well as the CSS coverage. Learn how it works:
- Collect coverage in Microsoft Edge: https://learn.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium/coverage/
- Collect coevrage in Google Chrome: https://developer.chrome.com/docs/devtools/coverage/
Additionally, DevTools Tips writes about it in their explainer.
You end up with one or more JSON files that contain coverage data. We provide a helper parse_coverage() that both parses the JSON and validates it so you can pass it directly into calculate_coverage().
// Read a single JSON or a folder full of JSON files with coverage data
// Coverage data looks like this:
// {
// url: 'https://www.projectwallace.com/style.css',
// text: 'a { color: blue; text-decoration: underline; }', etc.
// ranges: [
// { start: 0, end: 46 }
// ]
// }
import { parse_coverage } from '@projectwallace/css-code-coverage'
let files = await fs.glob('./css-coverage/**/*.json')
let coverage_data = []
for (let file of files) {
let json_content = await fs.readFile(file, 'urf-8')
coverage_data.push(...parse_coverage(json_content))
}Both Puppeteer and Playwright provide an API to programmatically get the coverage data, allowing you to put that directly into this library. Here is the gist:
// Start collecting coverage
await page.coverage.startCSSCoverage()
// Load the page, do all sorts of interactions to increase coverage, etc.
await page.goto('http://example.com')
// Stop the coverage and store the result in a variable to pass along
let coverage = await page.coverage.stopCSSCoverage()
// Now we can process it
import { calculate_coverage } from '@projectwallace/css-code-coverage'
function parse_html(html) {
return new DOMParser().parseFromString(html, 'text/html')
}
let report = calculcate_coverage(coverage, parse_html)Coverage generators also create coverage ranges for <style> blocks in HTML. If this applies to your code you should provide a HTML parser that we use to 'scrape' the HTML in case the browser gives us not just plain CSS contents. Depending on where you run this analysis you can use:
-
Browser:
function parse_html(html) { return new DOMParser().parseFromString(html, 'text/html') }
-
Node (using linkedom in this example, but other parsers could work, too):
// $ npm install linkedom import { DOMParser } from 'linkedom' function parse_html(html: string) { return new DOMParser().parseFromString(html, 'text/html') }