Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8.10.3
version: 8.15.1
run_install: false
- name: Get pnpm store directory
shell: bash
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ Configuration is optional. It should be put in a file at `config/coverage.js` (`

- `excludes`: Defaults to `['*/mirage/**/*']`. An array of globs to exclude from instrumentation. Useful to exclude files from coverage statistics.

- `extension`: Defaults to `['.gjs', '.gts', '.js', '.ts', '.cjs', '.mjs', '.mts', '.cts']`. Tell Istanbul to instrument only files with the provided extensions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstefanovic97 should this actually be

Suggested change
- `extension`: Defaults to `['.gjs', '.gts', '.js', '.ts', '.cjs', '.mjs', '.mts', '.cts']`. Tell Istanbul to instrument only files with the provided extensions.
- `extensions`: Defaults to `['.gjs', '.gts', '.js', '.ts', '.cjs', '.mjs', '.mts', '.cts']`. Tells Istanbul to instrument only files with the provided extensions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SergeAstapov well in the code it really is extension reason for this is I decided to keep it the same as the option in Isntabul since it too is singular


- `coverageFolder`: Defaults to `coverage`. A folder relative to the root of your project to store coverage results.

- `parallel`: Defaults to `false`. Should be set to true if parallel testing is being used for separate test runs, for example when using [ember-exam](https://github.com/trentmwillis/ember-exam) with the `--partition` flag. This will generate the coverage reports in directories suffixed with `_<random_string>` to avoid overwriting other threads reports. These reports can be joined by using the `ember coverage-merge` command (potentially as part of the [posttest hook](https://docs.npmjs.com/misc/scripts) in your `package.json`).
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"test": "vitest"
},
"devDependencies": {
"@babel/core": "^7.23.9",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstefanovic97 why is this needed in the root of monorepo? I guess each package that needs it should declare such kind of dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used in tests here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, now I see - test-packages folder contains both vite tests and test packages.
IMO seems confusing but for sure we should follow existing structure in this PR.

"@release-it-plugins/lerna-changelog": "^6.0.0",
"@release-it-plugins/workspaces": "^4.0.0",
"chai-files": "^1.4.0",
Expand Down Expand Up @@ -46,6 +47,6 @@
"version": "2.0.3",
"volta": {
"node": "16.20.2",
"pnpm": "8.10.2"
"pnpm": "8.15.1"
}
}
24 changes: 23 additions & 1 deletion packages/ember-cli-code-coverage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ module.exports = {
buildBabelPlugin(opts = {}) {
let cwd = opts.cwd || process.cwd();
let exclude = ['*/mirage/**/*', '*/node_modules/**/*'];
let extension = [
'.gjs',
'.gts',
'.js',
'.ts',
'.cjs',
'.mjs',
'.mts',
'.cts',
];
let coverageEnvVar = 'COVERAGE';
let configBase = 'config';

Expand All @@ -48,19 +58,25 @@ module.exports = {
if (config.coverageEnvVar) {
coverageEnvVar = config.coverageEnvVar;
}

if (config.extension) {
extension = config.extension;
}
}

if (process.env[coverageEnvVar] !== 'true') {
return [];
}

let useStringLookupForPlugin = false;
if (opts.embroider === true) {
try {
// Attempt to import the utility @embroider/compat uses in >3.1 to locate the embroider working directory
// the presence of this `locateEmbroiderWorkingDir` method coincides with the shift to utilize `rewritten-app` tmp dir
// eslint-disable-next-line node/no-missing-require
let { locateEmbroiderWorkingDir } = require('@embroider/core');
cwd = path.resolve(locateEmbroiderWorkingDir(cwd), 'rewritten-app');
useStringLookupForPlugin = true;
} catch (err) {
// otherwise, fall back to the method used in embroider <3.1
let {
Expand All @@ -72,7 +88,13 @@ module.exports = {
}

const IstanbulPlugin = require.resolve('babel-plugin-istanbul');
return [[IstanbulPlugin, { cwd, include: '**/*', exclude }]];
return [
// String lookup is needed to workaround https://github.com/embroider-build/embroider/issues/1525

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it turns out... We're going to require string lookup eventually, and forbid passing JS references.

JS references are not parallizable, but string references are! (it's a babel thing 🤷 )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @NullVoxPopuli are there any problems or situations where this string lookup might not work?
I just tried this out on a new embroider app, I'm investigating #407 and I don't get any errors but it seems like that plugin isn't running

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thing you could try is using a resolved string, so you get the absolute path of the plugin for "this instance of ember-cli-code-coverage".

path.resolve('the-string-you-have'); // returns absolute string, so no matter who is requiring, their resolve isn't local to who is doing the requiring 

useStringLookupForPlugin
? 'ember-cli-code-coverage/lib/gjs-gts-istanbul-ignore-template-plugin'
: require('./lib/gjs-gts-istanbul-ignore-template-plugin'),
[IstanbulPlugin, { cwd, include: '**/*', exclude, extension }],
];
},

includedCommands() {
Expand Down
20 changes: 14 additions & 6 deletions packages/ember-cli-code-coverage/lib/attach-middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const getConfig = require('./config');
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const libSourceMaps = require('istanbul-lib-source-maps');

const sourceMapStore = libSourceMaps.createSourceMapStore();

const WRITE_COVERAGE = '/write-coverage';

Expand Down Expand Up @@ -134,17 +137,22 @@ function adjustCoverage(coverage, options) {
namespaceMappings,
modifyAssetLocation
);
coverage[filePath].path = path.relative(root, relativeToProjectRoot);
memo[path.relative(root, relativeToProjectRoot)] = coverage[filePath];
coverage[filePath].data.path = path.relative(root, relativeToProjectRoot);
memo[path.relative(root, relativeToProjectRoot)] = coverage[filePath].data;
return memo;
}, {});

return adjustedCoverage;
}

function writeCoverage(coverage, options, map) {
async function writeCoverage(coverage, options, map) {
let { root } = options;
const adjustedCoverage = adjustCoverage(coverage, options);

const remappedCoverage = await sourceMapStore.transformCoverage(
libCoverage.createCoverageMap(coverage)
);

const adjustedCoverage = adjustCoverage(remappedCoverage.data, options);

Object.entries(adjustedCoverage).forEach(([relativePath, cov]) => {
// this filters out files that dont reside within the project
Expand Down Expand Up @@ -187,8 +195,8 @@ function reportCoverage(map, root, configPath) {
});
}

function coverageHandler(map, options, req, res) {
writeCoverage(req.body, options, map);
async function coverageHandler(map, options, req, res) {
await writeCoverage(req.body, options, map);
reportCoverage(map, options.root, options.configPath);

res.send(map.getCoverageSummary().toJSON());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* When gjs/gts is converted to js it looks like this
*
* @example
* template(`
* <div>
* template code
* </div>`
* , {
* eval() {
* return eval(arguments[0]);
* }
*});
*
* The problem is Istanbul's babel plugin will try to instrument the `eval` method which can skew coverage results.
* In order to avoid this we add this plugin to add Istanbul ignore comments above `eval` to make sure it won't be instrumented by babel
*/
const gjsGtsTemplateIgnoreVisitor = {
CallExpression(path) {
const { node } = path;
let { callee } = node;

if (callee.name !== 'template') {
return;
}

const callScopeBinding = path.scope.getBinding('template');

if (
callScopeBinding.kind === 'module' &&
callScopeBinding.path.parent.source.value === '@ember/template-compiler'
) {
const babelIgnoreComment = {
type: 'CommentBlock',
value: ' istanbul ignore next ',
};

if (!path.findParent((path) => path.isStatement()).node.leadingComments) {
path.findParent((path) => path.isStatement()).node.leadingComments = [];
}
path
.findParent((path) => path.isStatement())
.node.leadingComments.push(babelIgnoreComment);
}
},
};

module.exports = function () {
return {
visitor: {
Program: {
enter(path, state) {
const filename = state.file.opts.filename;

if (!filename?.match(/\.g[tj]s$/)) {
return;
}

// We need to do early traverse to make sure this runs before istanbuls plugin
path.traverse(gjsGtsTemplateIgnoreVisitor, state);
},
},
},
};
};
1 change: 1 addition & 0 deletions packages/ember-cli-code-coverage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"istanbul-lib-coverage": "^3.2.1",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.1.6",
"istanbul-lib-source-maps": "^4.0.1",
"node-dir": "^0.1.17",
"walk-sync": "^2.1.0"
},
Expand Down
14 changes: 14 additions & 0 deletions packages/ember-cli-code-coverage/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading