Merge pull request #15152 from backstage/rugvip/coverage-config

cli: fix coverage configuration and make next tests the new default
This commit is contained in:
Patrik Oldsberg
2022-12-10 15:16:47 +01:00
committed by GitHub
7 changed files with 34 additions and 83 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Updated Jest coverage configuration to only apply either in the root project or package configuration, depending on whether repo or package tests are run.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/cli': minor
---
The Jest configuration that was previously enabled with `BACKSTAGE_NEXT_TESTS` is now enabled by default. To revert to the old configuration you can now instead set `BACKSTAGE_OLD_TESTS`.
This new configuration uses the `babel` coverage provider rather than `v8`. It used to be that `v8` worked better when using Sucrase for transpilation, but now that we have switched to SWC, `babel` seems to work better. In addition, the new configuration also enables source maps by default, as they no longer have a negative impact on code coverage accuracy, and it also enables a modified Jest runtime with additional caching of script objects.
-2
View File
@@ -202,7 +202,6 @@ jobs:
if: ${{ steps.yarn-lock.outcome == 'success' }}
run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M --since origin/master
env:
BACKSTAGE_NEXT_TESTS: 1
BACKSTAGE_TEST_DISABLE_DOCKER: 1
BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }}
BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres9.ports[5432] }}
@@ -214,7 +213,6 @@ jobs:
yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=800M --coverage
bash <(curl -s https://codecov.io/bash) -N $(git rev-parse FETCH_HEAD)
env:
BACKSTAGE_NEXT_TESTS: 1
BACKSTAGE_TEST_DISABLE_DOCKER: 1
BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }}
BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres9.ports[5432] }}
-1
View File
@@ -105,7 +105,6 @@ jobs:
bash <(curl -s https://codecov.io/bash) -f packages/core-components/coverage/* -F core-components
bash <(curl -s https://codecov.io/bash) -f packages/core-plugin-api/coverage/* -F core-plugin-api
env:
BACKSTAGE_NEXT_TESTS: 1
BACKSTAGE_TEST_DISABLE_DOCKER: 1
BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }}
BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres9.ports[5432] }}
-1
View File
@@ -48,7 +48,6 @@ jobs:
- name: test
run: yarn backstage-cli repo test --maxWorkers=2 --workerIdleMemoryLimit=1300M
env:
BACKSTAGE_NEXT_TESTS: 1
BACKSTAGE_TEST_DISABLE_DOCKER: 1
# credit: https://github.com/appleboy/discord-action/issues/3#issuecomment-731426861
+22 -21
View File
@@ -21,15 +21,10 @@ const glob = require('util').promisify(require('glob'));
const { version } = require('../package.json');
const envOptions = {
nextTests: Boolean(process.env.BACKSTAGE_NEXT_TESTS),
oldTests: Boolean(process.env.BACKSTAGE_OLD_TESTS),
enableSourceMaps: Boolean(process.env.ENABLE_SOURCE_MAPS),
};
if (envOptions.nextTests) {
// Needed so that, at import-time, it can hook into Jest's internals.
require('./jestCachingModuleLoader');
}
const transformIgnorePattern = [
'@material-ui',
'ajv',
@@ -65,7 +60,7 @@ function getRoleConfig(role) {
}
}
async function getProjectConfig(targetPath, displayName) {
async function getProjectConfig(targetPath, extraConfig) {
const configJsPath = path.resolve(targetPath, 'jest.config.js');
const configTsPath = path.resolve(targetPath, 'jest.config.ts');
// If the package has it's own jest config, we use that instead.
@@ -125,11 +120,8 @@ async function getProjectConfig(targetPath, displayName) {
}
const options = {
...(displayName && { displayName }),
...extraConfig,
rootDir: path.resolve(targetPath, 'src'),
coverageDirectory: path.resolve(targetPath, 'coverage'),
coverageProvider: envOptions.nextTests ? 'babel' : 'v8',
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx,mjs,cjs}', '!**/*.d.ts'],
moduleNameMapper: {
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
},
@@ -138,7 +130,7 @@ async function getProjectConfig(targetPath, displayName) {
'\\.(mjs|cjs|js)$': [
require.resolve('./jestSwcTransform'),
{
sourceMaps: envOptions.enableSourceMaps || envOptions.nextTests,
sourceMaps: envOptions.enableSourceMaps || !envOptions.oldTests,
jsc: {
parser: {
syntax: 'ecmascript',
@@ -149,7 +141,7 @@ async function getProjectConfig(targetPath, displayName) {
'\\.jsx$': [
require.resolve('./jestSwcTransform'),
{
sourceMaps: envOptions.enableSourceMaps || envOptions.nextTests,
sourceMaps: envOptions.enableSourceMaps || !envOptions.oldTests,
jsc: {
parser: {
syntax: 'ecmascript',
@@ -166,7 +158,7 @@ async function getProjectConfig(targetPath, displayName) {
'\\.ts$': [
require.resolve('./jestSwcTransform'),
{
sourceMaps: envOptions.enableSourceMaps || envOptions.nextTests,
sourceMaps: envOptions.enableSourceMaps || !envOptions.oldTests,
jsc: {
parser: {
syntax: 'typescript',
@@ -177,7 +169,7 @@ async function getProjectConfig(targetPath, displayName) {
'\\.tsx$': [
require.resolve('./jestSwcTransform'),
{
sourceMaps: envOptions.enableSourceMaps || envOptions.nextTests,
sourceMaps: envOptions.enableSourceMaps || !envOptions.oldTests,
jsc: {
parser: {
syntax: 'typescript',
@@ -199,9 +191,9 @@ async function getProjectConfig(targetPath, displayName) {
// A bit more opinionated
testMatch: ['**/*.test.{js,jsx,ts,tsx,mjs,cjs}'],
runtime: envOptions.nextTests
? require.resolve('./jestCachingModuleLoader')
: undefined,
runtime: envOptions.oldTests
? undefined
: require.resolve('./jestCachingModuleLoader'),
transformIgnorePatterns: [`/node_modules/(?:${transformIgnorePattern})/`],
...getRoleConfig(closestPkgJson?.backstage?.role),
@@ -237,15 +229,21 @@ async function getRootConfig() {
const targetPackagePath = path.resolve(targetPath, 'package.json');
const exists = await fs.pathExists(targetPackagePath);
const coverageConfig = {
coverageDirectory: path.resolve(targetPath, 'coverage'),
coverageProvider: envOptions.oldTests ? 'v8' : 'babel',
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx,mjs,cjs}', '!**/*.d.ts'],
};
if (!exists) {
return getProjectConfig(targetPath);
return getProjectConfig(targetPath, coverageConfig);
}
// Check whether the current package is a workspace root or not
const data = await fs.readJson(targetPackagePath);
const workspacePatterns = data.workspaces && data.workspaces.packages;
if (!workspacePatterns) {
return getProjectConfig(targetPath);
return getProjectConfig(targetPath, coverageConfig);
}
// If the target package is a workspace root, we find all packages in the
@@ -269,7 +267,9 @@ async function getRootConfig() {
testScript?.includes('backstage-cli test') ||
testScript?.includes('backstage-cli package test');
if (testScript && isSupportedTestScript) {
return await getProjectConfig(projectPath, packageData.name);
return await getProjectConfig(projectPath, {
displayName: packageData.name,
});
}
return undefined;
@@ -279,6 +279,7 @@ async function getRootConfig() {
return {
rootDir: targetPath,
projects: configs,
...coverageConfig,
};
}
@@ -14,61 +14,11 @@
* limitations under the License.
*/
const fs = require('fs');
const { default: JestRuntime } = require('jest-runtime');
const fileTransformCache = new Map();
const scriptTransformCache = new Map();
let runtimeGeneration = 0;
let isWatchMode;
module.exports = class CachingJestRuntime extends JestRuntime {
// Each Jest run creates a new runtime, including when rerunning tests in
// watch mode. This keeps track of whether we've switched runtime instance.
__runtimeGeneration = runtimeGeneration++;
transformFile(filename, options) {
if (!isWatchMode) {
return super.transformFile(filename, options);
}
const entry = fileTransformCache.get(filename);
if (entry) {
// Only check modification time if it's from a different runtime generation
if (entry.generation === this.__runtimeGeneration) {
return entry.code;
}
// Keep track of the modification time of files so that we can properly
// reprocess them in watch mode.
const { mtimeMs } = fs.statSync(filename);
if (mtimeMs > entry.mtimeMs) {
const code = super.transformFile(filename, options);
fileTransformCache.set(filename, {
code,
mtimeMs,
generation: this.__runtimeGeneration,
});
return code;
}
fileTransformCache.set(filename, {
...entry,
generation: this.__runtimeGeneration,
});
return entry.code;
}
const code = super.transformFile(filename, options);
fileTransformCache.set(filename, {
code,
mtimeMs: fs.statSync(filename).mtimeMs,
generation: this.__runtimeGeneration,
});
return code;
}
// This may or may not be a good idea. Theoretically I don't know why this would impact
// test correctness and flakiness, but it seems like it may introduce flakiness and strange failures.
// It does seem to speed up test execution by a fair amount though.
@@ -84,11 +34,3 @@ module.exports = class CachingJestRuntime extends JestRuntime {
return script;
}
};
// Inject hook into createHasteMap, as it's the only way that we can
// determine (from our scope here) if we're in "watch mode" or not.
const originalCreateHasteMap = JestRuntime.createHasteMap;
JestRuntime.createHasteMap = (config, options = undefined) => {
isWatchMode = options && options.watch;
return originalCreateHasteMap(config, options);
};