From 185fec5c0c2bdb3b5717b5abde5a5bdf7b674d0f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 2 Oct 2021 16:41:56 +0200 Subject: [PATCH 1/2] cli: add support for using the default jest config from project roots Signed-off-by: Patrik Oldsberg --- .changeset/empty-oranges-roll.md | 5 +++ docs/cli/commands.md | 5 ++- packages/cli/config/jest.js | 73 +++++++++++++++++++++++++++----- packages/cli/package.json | 1 + 4 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 .changeset/empty-oranges-roll.md diff --git a/.changeset/empty-oranges-roll.md b/.changeset/empty-oranges-roll.md new file mode 100644 index 0000000000..4534573497 --- /dev/null +++ b/.changeset/empty-oranges-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The default jest configuration used by the `test` command now supports yarn workspaces. By running `backstage-cli test` in the root of a monorepo, all packages will now automatically be included in the test suite and it will run just like it does within a package. Each package in the monorepo will still use its own local jest configuration, and only packages that have `backstage-cli test` in the `test` script within `package.json` will be included. diff --git a/docs/cli/commands.md b/docs/cli/commands.md index a27cc62712..5727833562 100644 --- a/docs/cli/commands.md +++ b/docs/cli/commands.md @@ -424,7 +424,10 @@ This command uses a default Jest configuration that is included in the CLI, which is set up with similar goals for speed, scale, and working within a monorepo. The configuration sets the `src` as the root directory, enforces the `.test.` infix for tests, and uses `src/setupTests.ts` as the test setup -location. +location. The included configuration also supports test execution at the root of +a yarn workspaces monorepo by automatically creating one grouped configuration +that includes all packages that have `backstage-cli test` in their package +`test` script. If needed, the configuration can be extended using a `"jest"` field in `package.json`, both within the target package and the monorepo root, with diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 1df33d2395..80b3e84cda 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -16,20 +16,23 @@ const fs = require('fs-extra'); const path = require('path'); +const glob = require('util').promisify(require('glob')); -async function getConfig() { +async function getProjectConfig(targetPath) { + 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. - if (await fs.pathExists('jest.config.js')) { - return require(path.resolve('jest.config.js')); - } else if (await fs.pathExists('jest.config.ts')) { - return require(path.resolve('jest.config.ts')); + if (await fs.pathExists(configJsPath)) { + return require(configJsPath); + } else if (await fs.pathExists(configTsPath)) { + return require(configTsPath); } // We read all "jest" config fields in package.json files all the way to the filesystem root. // All configs are merged together to create the final config, with longer paths taking precedence. // The merging of the configs is shallow, meaning e.g. all transforms are replaced if new ones are defined. const pkgJsonConfigs = []; - let currentPath = process.cwd(); + let currentPath = targetPath; // Some sanity check to avoid infinite loop for (let i = 0; i < 100; i++) { @@ -70,8 +73,8 @@ async function getConfig() { const transformModulePattern = transformModules && `(?!${transformModules})`; const options = { - rootDir: path.resolve('src'), - coverageDirectory: path.resolve('coverage'), + rootDir: path.resolve(targetPath, 'src'), + coverageDirectory: path.resolve(targetPath, 'coverage'), collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'], moduleNameMapper: { '\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'), @@ -96,11 +99,61 @@ async function getConfig() { }; // Use src/setupTests.ts as the default location for configuring test env - if (fs.existsSync('src/setupTests.ts')) { + if (fs.existsSync(path.resolve(targetPath, 'src/setupTests.ts'))) { options.setupFilesAfterEnv = ['/setupTests.ts']; } return Object.assign(options, ...pkgJsonConfigs); } -module.exports = getConfig(); +// This loads the root jest config, which in turn will either refer to a single +// configuration for the current package, or a collection of configurations for +// the target workspace packages +async function getRootConfig() { + const targetPath = process.cwd(); + const targetPackagePath = path.resolve(targetPath, 'package.json'); + const exists = await fs.pathExists(targetPackagePath); + + if (!exists) { + return getProjectConfig(targetPath); + } + + // 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); + } + + // If the target package is a workspace root, we find all packages in the + // workspace and load those in as separate jest projects instead. + const projectPaths = await Promise.all( + workspacePatterns.map(pattern => glob(path.join(targetPath, pattern))), + ).then(_ => _.flat()); + + const configs = await Promise.all( + projectPaths.flat().map(async projectPath => { + const packagePath = path.resolve(projectPath, 'package.json'); + if (!(await fs.pathExists(packagePath))) { + return undefined; + } + + // We check for the presence of "backstage-cli test" in the package test + // script to determine whether a given package should be tested + const packageData = await fs.readJson(packagePath); + const testScript = packageData.scripts && packageData.scripts.test; + if (testScript && testScript.includes('backstage-cli test')) { + return await getProjectConfig(projectPath); + } + + return undefined; + }), + ).then(cs => cs.filter(Boolean)); + + return { + rootDir: targetPath, + projects: configs, + }; +} + +module.exports = getRootConfig(); diff --git a/packages/cli/package.json b/packages/cli/package.json index dc922241da..4056cebd86 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -76,6 +76,7 @@ "express": "^4.17.1", "fork-ts-checker-webpack-plugin": "^4.0.5", "fs-extra": "9.1.0", + "glob": "^7.1.7", "handlebars": "^4.7.3", "html-webpack-plugin": "^5.3.1", "inquirer": "^7.0.4", From e254368371985afb4de2462a5b2fba521c40964f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 2 Oct 2021 16:44:06 +0200 Subject: [PATCH 2/2] root,create-app: switch over to using backstage-cli test in root Signed-off-by: Patrik Oldsberg --- .changeset/young-ways-trade.md | 12 ++++++++++++ package.json | 2 +- .../templates/default-app/package.json.hbs | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/young-ways-trade.md diff --git a/.changeset/young-ways-trade.md b/.changeset/young-ways-trade.md new file mode 100644 index 0000000000..0ebe68eed6 --- /dev/null +++ b/.changeset/young-ways-trade.md @@ -0,0 +1,12 @@ +--- +'@backstage/create-app': patch +--- + +Switched the default `test` script in the package root to use `backstage-cli test` rather than `lerna run test`. This is thanks to the `@backstage/cli` now supporting running the test command from the project root. + +To apply this change to an existing project, apply the following change to your root `package.json`: + +```diff +- "test": "lerna run test --since origin/master -- --coverage", ++ "test": "backstage-cli test", +``` diff --git a/package.json b/package.json index 4af1856079..03a502fee2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "tsc:full": "backstage-cli clean && tsc --skipLibCheck false --incremental false", "clean": "backstage-cli clean && lerna run clean", "diff": "lerna run diff --", - "test": "lerna run test --since origin/master -- --coverage", + "test": "backstage-cli test", "test:all": "lerna run test -- --coverage", "lint": "lerna run lint --since origin/master --", "lint:docs": "node ./scripts/check-docs-quality", diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index eb28e5fe0c..7e2702e213 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -15,7 +15,7 @@ "tsc:full": "tsc --skipLibCheck false --incremental false", "clean": "backstage-cli clean && lerna run clean", "diff": "lerna run diff --", - "test": "lerna run test --since origin/master -- --coverage", + "test": "backstage-cli test", "test:all": "lerna run test -- --coverage", "lint": "lerna run lint --since origin/master --", "lint:all": "lerna run lint --",