@backstage/cli-module-test-jest 0.0.0-nightly-20260317031259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +15 -0
- package/bin/backstage-cli-module-test-jest +32 -0
- package/config/getJestEnvironment.js +49 -0
- package/config/jest-environment-jsdom/index.js +61 -0
- package/config/jest.js +422 -0
- package/config/jestCacheResultProcessor.cjs +23 -0
- package/config/jestCachingModuleLoader.js +35 -0
- package/config/jestFileTransform.js +44 -0
- package/config/jestRejectNetworkRequests.js +70 -0
- package/config/jestSucraseTransform.js +87 -0
- package/config/jestSwcTransform.js +44 -0
- package/config/jestYamlTransform.js +40 -0
- package/dist/commands/package/test.cjs.js +59 -0
- package/dist/commands/package/test.cjs.js.map +1 -0
- package/dist/commands/repo/test.cjs.js +299 -0
- package/dist/commands/repo/test.cjs.js.map +1 -0
- package/dist/index.cjs.js +25 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/package.json.cjs.js +110 -0
- package/dist/package.json.cjs.js.map +1 -0
- package/package.json +76 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @backstage/cli-module-test-jest
|
|
2
|
+
|
|
3
|
+
## 0.0.0-nightly-20260317031259
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 329f394: Initial release of the CLI module packages. Each module provides a set of commands that can be discovered automatically by `@backstage/cli` or executed standalone.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @backstage/cli-node@0.0.0-nightly-20260317031259
|
|
13
|
+
- @backstage/cli-common@0.0.0-nightly-20260317031259
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @backstage/cli-module-test-jest
|
|
2
|
+
|
|
3
|
+
CLI module that provides Jest-based testing commands for the Backstage CLI.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
| Command | Description |
|
|
8
|
+
| :------------- | :---------------------------------------------------------------- |
|
|
9
|
+
| `package test` | Run tests, forwarding arguments to Jest, defaulting to watch mode |
|
|
10
|
+
| `repo test` | Run tests, forwarding arguments to Jest, defaulting to watch mode |
|
|
11
|
+
|
|
12
|
+
## Documentation
|
|
13
|
+
|
|
14
|
+
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
|
|
15
|
+
- [Backstage Documentation](https://backstage.io/docs)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2024 The Backstage Authors
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
|
|
20
|
+
/* eslint-disable-next-line no-restricted-syntax */
|
|
21
|
+
const isLocal = require('node:fs').existsSync(
|
|
22
|
+
path.resolve(__dirname, '../src'),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (isLocal) {
|
|
26
|
+
require('@backstage/cli-node/config/nodeTransform.cjs');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { runCliModule } = require('@backstage/cli-node');
|
|
30
|
+
const cliModule = require(isLocal ? '../src/index' : '..').default;
|
|
31
|
+
const pkg = require('../package.json');
|
|
32
|
+
runCliModule({ module: cliModule, name: pkg.name, version: pkg.version });
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function getJestMajorVersion() {
|
|
18
|
+
const jestVersion = require('jest/package.json').version;
|
|
19
|
+
const majorVersion = parseInt(jestVersion.split('.')[0], 10);
|
|
20
|
+
return majorVersion;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getJestEnvironment() {
|
|
24
|
+
const majorVersion = getJestMajorVersion();
|
|
25
|
+
|
|
26
|
+
if (majorVersion >= 30) {
|
|
27
|
+
try {
|
|
28
|
+
require.resolve('@jest/environment-jsdom-abstract');
|
|
29
|
+
require.resolve('jsdom');
|
|
30
|
+
} catch {
|
|
31
|
+
throw new Error(
|
|
32
|
+
'Jest 30+ requires @jest/environment-jsdom-abstract and jsdom. ' +
|
|
33
|
+
'Please install them as dev dependencies.',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return require.resolve('./jest-environment-jsdom');
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
require.resolve('jest-environment-jsdom');
|
|
40
|
+
} catch {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'Jest 29 requires jest-environment-jsdom. ' +
|
|
43
|
+
'Please install it as a dev dependency.',
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return require.resolve('jest-environment-jsdom');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { getJestMajorVersion, getJestEnvironment };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const JSDOMEnvironment = require('@jest/environment-jsdom-abstract').default;
|
|
18
|
+
const jsdom = require('jsdom');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A custom JSDOM environment that extends the abstract base and applies
|
|
22
|
+
* fixes for Web API globals that are missing or incorrectly implemented
|
|
23
|
+
* in JSDOM.
|
|
24
|
+
*
|
|
25
|
+
* Based on https://github.com/mswjs/jest-fixed-jsdom
|
|
26
|
+
*/
|
|
27
|
+
class FixedJSDOMEnvironment extends JSDOMEnvironment {
|
|
28
|
+
constructor(config, context) {
|
|
29
|
+
super(config, context, jsdom);
|
|
30
|
+
|
|
31
|
+
// Fix Web API globals that JSDOM doesn't properly expose
|
|
32
|
+
this.global.TextDecoder = TextDecoder;
|
|
33
|
+
this.global.TextEncoder = TextEncoder;
|
|
34
|
+
this.global.TextDecoderStream = TextDecoderStream;
|
|
35
|
+
this.global.TextEncoderStream = TextEncoderStream;
|
|
36
|
+
this.global.ReadableStream = ReadableStream;
|
|
37
|
+
|
|
38
|
+
this.global.Blob = Blob;
|
|
39
|
+
this.global.Headers = Headers;
|
|
40
|
+
this.global.FormData = FormData;
|
|
41
|
+
this.global.Request = Request;
|
|
42
|
+
this.global.Response = Response;
|
|
43
|
+
this.global.fetch = fetch;
|
|
44
|
+
this.global.AbortController = AbortController;
|
|
45
|
+
this.global.AbortSignal = AbortSignal;
|
|
46
|
+
this.global.structuredClone = structuredClone;
|
|
47
|
+
this.global.URL = URL;
|
|
48
|
+
this.global.URLSearchParams = URLSearchParams;
|
|
49
|
+
|
|
50
|
+
this.global.BroadcastChannel = BroadcastChannel;
|
|
51
|
+
this.global.TransformStream = TransformStream;
|
|
52
|
+
this.global.WritableStream = WritableStream;
|
|
53
|
+
|
|
54
|
+
// Needed to ensure `e instanceof Error` works as expected with errors thrown from
|
|
55
|
+
// any of the native APIs above. Without this, the JSDOM `Error` is what the test
|
|
56
|
+
// code will use for comparison with `e`, which fails the instanceof check.
|
|
57
|
+
this.global.Error = Error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = FixedJSDOMEnvironment;
|
package/config/jest.js
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs-extra');
|
|
18
|
+
const path = require('node:path');
|
|
19
|
+
const crypto = require('node:crypto');
|
|
20
|
+
const glob = require('node:util').promisify(require('glob'));
|
|
21
|
+
const { version } = require('../package.json');
|
|
22
|
+
const paths = require('@backstage/cli-common').findPaths(process.cwd());
|
|
23
|
+
const {
|
|
24
|
+
getJestEnvironment,
|
|
25
|
+
getJestMajorVersion,
|
|
26
|
+
} = require('./getJestEnvironment');
|
|
27
|
+
|
|
28
|
+
const SRC_EXTS = ['ts', 'js', 'tsx', 'jsx', 'mts', 'cts', 'mjs', 'cjs'];
|
|
29
|
+
|
|
30
|
+
const FRONTEND_ROLES = [
|
|
31
|
+
'frontend',
|
|
32
|
+
'web-library',
|
|
33
|
+
'common-library',
|
|
34
|
+
'frontend-plugin',
|
|
35
|
+
'frontend-plugin-module',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const NODE_ROLES = [
|
|
39
|
+
'backend',
|
|
40
|
+
'cli',
|
|
41
|
+
'cli-module',
|
|
42
|
+
'node-library',
|
|
43
|
+
'backend-plugin',
|
|
44
|
+
'backend-plugin-module',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const envOptions = {
|
|
48
|
+
oldTests: Boolean(process.env.BACKSTAGE_OLD_TESTS),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
require.resolve('react-dom/client', {
|
|
53
|
+
paths: [paths.targetRoot],
|
|
54
|
+
});
|
|
55
|
+
process.env.HAS_REACT_DOM_CLIENT = true;
|
|
56
|
+
} catch {
|
|
57
|
+
/* ignored */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A list of config keys that are valid for project-level config.
|
|
62
|
+
* Jest will complain if we forward any other root configuration to the projects.
|
|
63
|
+
*
|
|
64
|
+
* @type {Array<keyof import('@jest/types').Config.ProjectConfig>}
|
|
65
|
+
*/
|
|
66
|
+
const projectConfigKeys = [
|
|
67
|
+
'automock',
|
|
68
|
+
'cache',
|
|
69
|
+
'cacheDirectory',
|
|
70
|
+
'clearMocks',
|
|
71
|
+
'collectCoverageFrom',
|
|
72
|
+
'coverageDirectory',
|
|
73
|
+
'coveragePathIgnorePatterns',
|
|
74
|
+
'cwd',
|
|
75
|
+
'dependencyExtractor',
|
|
76
|
+
'detectLeaks',
|
|
77
|
+
'detectOpenHandles',
|
|
78
|
+
'displayName',
|
|
79
|
+
'errorOnDeprecated',
|
|
80
|
+
'extensionsToTreatAsEsm',
|
|
81
|
+
'fakeTimers',
|
|
82
|
+
'filter',
|
|
83
|
+
'forceCoverageMatch',
|
|
84
|
+
'globalSetup',
|
|
85
|
+
'globalTeardown',
|
|
86
|
+
'globals',
|
|
87
|
+
'haste',
|
|
88
|
+
'id',
|
|
89
|
+
'injectGlobals',
|
|
90
|
+
'moduleDirectories',
|
|
91
|
+
'moduleFileExtensions',
|
|
92
|
+
'moduleNameMapper',
|
|
93
|
+
'modulePathIgnorePatterns',
|
|
94
|
+
'modulePaths',
|
|
95
|
+
'openHandlesTimeout',
|
|
96
|
+
'preset',
|
|
97
|
+
'prettierPath',
|
|
98
|
+
'resetMocks',
|
|
99
|
+
'resetModules',
|
|
100
|
+
'resolver',
|
|
101
|
+
'restoreMocks',
|
|
102
|
+
'rootDir',
|
|
103
|
+
'roots',
|
|
104
|
+
'runner',
|
|
105
|
+
'runtime',
|
|
106
|
+
'sandboxInjectedGlobals',
|
|
107
|
+
'setupFiles',
|
|
108
|
+
'setupFilesAfterEnv',
|
|
109
|
+
'skipFilter',
|
|
110
|
+
'skipNodeResolution',
|
|
111
|
+
'slowTestThreshold',
|
|
112
|
+
'snapshotResolver',
|
|
113
|
+
'snapshotSerializers',
|
|
114
|
+
'snapshotFormat',
|
|
115
|
+
'testEnvironment',
|
|
116
|
+
'testEnvironmentOptions',
|
|
117
|
+
'testMatch',
|
|
118
|
+
'testLocationInResults',
|
|
119
|
+
'testPathIgnorePatterns',
|
|
120
|
+
'testRegex',
|
|
121
|
+
'testRunner',
|
|
122
|
+
'transform',
|
|
123
|
+
'transformIgnorePatterns',
|
|
124
|
+
'watchPathIgnorePatterns',
|
|
125
|
+
'unmockedModulePathPatterns',
|
|
126
|
+
'workerIdleMemoryLimit',
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const transformIgnorePattern = [
|
|
130
|
+
'@material-ui',
|
|
131
|
+
'ajv',
|
|
132
|
+
'core-js',
|
|
133
|
+
'jest-.*',
|
|
134
|
+
'jsdom',
|
|
135
|
+
'knex',
|
|
136
|
+
'react',
|
|
137
|
+
'react-dom',
|
|
138
|
+
'highlight\\.js',
|
|
139
|
+
'prismjs',
|
|
140
|
+
'json-schema',
|
|
141
|
+
'react-use/lib',
|
|
142
|
+
'typescript',
|
|
143
|
+
].join('|');
|
|
144
|
+
|
|
145
|
+
// Provides additional config that's based on the role of the target package
|
|
146
|
+
function getRoleConfig(role, pkgJson) {
|
|
147
|
+
// Only Node.js package roles support native ESM modules, frontend and common
|
|
148
|
+
// packages are always transpiled to CommonJS.
|
|
149
|
+
const moduleOpts = NODE_ROLES.includes(role)
|
|
150
|
+
? {
|
|
151
|
+
module: {
|
|
152
|
+
ignoreDynamic: true,
|
|
153
|
+
exportInteropAnnotation: true,
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
: undefined;
|
|
157
|
+
|
|
158
|
+
const transform = {
|
|
159
|
+
'\\.(mjs|cjs|js)$': [
|
|
160
|
+
require.resolve('./jestSwcTransform'),
|
|
161
|
+
{
|
|
162
|
+
...moduleOpts,
|
|
163
|
+
jsc: {
|
|
164
|
+
parser: {
|
|
165
|
+
syntax: 'ecmascript',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
'\\.jsx$': [
|
|
171
|
+
require.resolve('./jestSwcTransform'),
|
|
172
|
+
{
|
|
173
|
+
jsc: {
|
|
174
|
+
parser: {
|
|
175
|
+
syntax: 'ecmascript',
|
|
176
|
+
jsx: true,
|
|
177
|
+
},
|
|
178
|
+
transform: {
|
|
179
|
+
react: {
|
|
180
|
+
runtime: 'automatic',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
'\\.(mts|cts|ts)$': [
|
|
187
|
+
require.resolve('./jestSwcTransform'),
|
|
188
|
+
{
|
|
189
|
+
...moduleOpts,
|
|
190
|
+
jsc: {
|
|
191
|
+
parser: {
|
|
192
|
+
syntax: 'typescript',
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
'\\.tsx$': [
|
|
198
|
+
require.resolve('./jestSwcTransform'),
|
|
199
|
+
{
|
|
200
|
+
jsc: {
|
|
201
|
+
parser: {
|
|
202
|
+
syntax: 'typescript',
|
|
203
|
+
tsx: true,
|
|
204
|
+
},
|
|
205
|
+
transform: {
|
|
206
|
+
react: {
|
|
207
|
+
runtime: 'automatic',
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
'\\.(bmp|gif|jpg|jpeg|png|ico|webp|frag|xml|svg|eot|woff|woff2|ttf)$':
|
|
214
|
+
require.resolve('./jestFileTransform.js'),
|
|
215
|
+
'\\.(yaml)$': require.resolve('./jestYamlTransform'),
|
|
216
|
+
};
|
|
217
|
+
if (FRONTEND_ROLES.includes(role)) {
|
|
218
|
+
return {
|
|
219
|
+
testEnvironment: getJestEnvironment(),
|
|
220
|
+
// The caching module loader is only used to speed up frontend tests,
|
|
221
|
+
// as it breaks real dynamic imports of ESM modules.
|
|
222
|
+
runtime: envOptions.oldTests
|
|
223
|
+
? undefined
|
|
224
|
+
: require.resolve('./jestCachingModuleLoader'),
|
|
225
|
+
transform,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
testEnvironment: require.resolve('jest-environment-node'),
|
|
230
|
+
moduleFileExtensions: [...SRC_EXTS, 'json', 'node'],
|
|
231
|
+
// Jest doesn't let us dynamically detect type=module per transformed file,
|
|
232
|
+
// so we have to assume that if the entry point is ESM, all TS files are
|
|
233
|
+
// ESM.
|
|
234
|
+
//
|
|
235
|
+
// This means you can't switch a package to type=module until all of its
|
|
236
|
+
// monorepo dependencies are also type=module or does not contain any .ts
|
|
237
|
+
// files.
|
|
238
|
+
extensionsToTreatAsEsm:
|
|
239
|
+
pkgJson.type === 'module' ? ['.ts', '.mts'] : ['.mts'],
|
|
240
|
+
transform,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function getProjectConfig(targetPath, extraConfig, extraOptions) {
|
|
245
|
+
const configJsPath = path.resolve(targetPath, 'jest.config.js');
|
|
246
|
+
const configTsPath = path.resolve(targetPath, 'jest.config.ts');
|
|
247
|
+
// If the package has it's own jest config, we use that instead.
|
|
248
|
+
if (await fs.pathExists(configJsPath)) {
|
|
249
|
+
return require(configJsPath);
|
|
250
|
+
} else if (await fs.pathExists(configTsPath)) {
|
|
251
|
+
return require(configTsPath);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Jest config can be defined both in the root package.json and within each package. The root config
|
|
255
|
+
// gets forwarded to us through the `extraConfig` parameter, while the package config is read here.
|
|
256
|
+
// If they happen to be the same the keys will simply override each other.
|
|
257
|
+
// The merging of the configs is shallow, meaning e.g. all transforms are replaced if new ones are defined.
|
|
258
|
+
const pkgJson = await fs.readJson(path.resolve(targetPath, 'package.json'));
|
|
259
|
+
|
|
260
|
+
const options = {
|
|
261
|
+
...extraConfig,
|
|
262
|
+
rootDir: path.resolve(targetPath, 'src'),
|
|
263
|
+
moduleNameMapper: {
|
|
264
|
+
'\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'),
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// A bit more opinionated
|
|
268
|
+
testMatch: [`**/*.test.{${SRC_EXTS.join(',')}}`],
|
|
269
|
+
|
|
270
|
+
transformIgnorePatterns: [`/node_modules/(?:${transformIgnorePattern})/`],
|
|
271
|
+
...getRoleConfig(pkgJson.backstage?.role, pkgJson),
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
options.setupFilesAfterEnv = options.setupFilesAfterEnv || [];
|
|
275
|
+
|
|
276
|
+
if (
|
|
277
|
+
extraOptions.rejectFrontendNetworkRequests &&
|
|
278
|
+
FRONTEND_ROLES.includes(pkgJson.backstage?.role)
|
|
279
|
+
) {
|
|
280
|
+
// By adding this first we ensure that it's possible to for example override
|
|
281
|
+
// fetch with a mock in a custom setup file
|
|
282
|
+
options.setupFilesAfterEnv.unshift(
|
|
283
|
+
require.resolve('./jestRejectNetworkRequests.js'),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
options.testEnvironment === getJestEnvironment() &&
|
|
289
|
+
getJestMajorVersion() < 30 // Only needed when not running the custom env for Jest 30+
|
|
290
|
+
) {
|
|
291
|
+
// FIXME https://github.com/jsdom/jsdom/issues/1724
|
|
292
|
+
options.setupFilesAfterEnv.unshift(require.resolve('cross-fetch/polyfill'));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Use src/setupTests.* as the default location for configuring test env
|
|
296
|
+
for (const ext of SRC_EXTS) {
|
|
297
|
+
if (fs.existsSync(path.resolve(targetPath, `src/setupTests.${ext}`))) {
|
|
298
|
+
options.setupFilesAfterEnv.push(`<rootDir>/setupTests.${ext}`);
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const config = Object.assign(options, pkgJson.jest);
|
|
304
|
+
|
|
305
|
+
// The config id is a cache key that lets us share the jest cache across projects.
|
|
306
|
+
// If no explicit id was configured, generated one based on the configuration.
|
|
307
|
+
if (!config.id) {
|
|
308
|
+
const configHash = crypto
|
|
309
|
+
.createHash('sha256')
|
|
310
|
+
.update(version)
|
|
311
|
+
.update(Buffer.alloc(1))
|
|
312
|
+
.update(JSON.stringify(config.transform).replaceAll(paths.targetRoot, ''))
|
|
313
|
+
.digest('hex');
|
|
314
|
+
config.id = `backstage_cli_${configHash}`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return config;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// This loads the root jest config, which in turn will either refer to a single
|
|
321
|
+
// configuration for the current package, or a collection of configurations for
|
|
322
|
+
// the target workspace packages
|
|
323
|
+
async function getRootConfig() {
|
|
324
|
+
const rootPkgJson = await fs.readJson(
|
|
325
|
+
paths.resolveTargetRoot('package.json'),
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const baseCoverageConfig = {
|
|
329
|
+
coverageDirectory: paths.resolveTarget('coverage'),
|
|
330
|
+
coverageProvider: envOptions.oldTests ? 'v8' : 'babel',
|
|
331
|
+
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx,mjs,cjs}', '!**/*.d.ts'],
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const { rejectFrontendNetworkRequests, ...rootOptions } =
|
|
335
|
+
rootPkgJson.jest ?? {};
|
|
336
|
+
const extraRootOptions = {
|
|
337
|
+
rejectFrontendNetworkRequests,
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const ws = rootPkgJson.workspaces;
|
|
341
|
+
const workspacePatterns = Array.isArray(ws) ? ws : ws?.packages;
|
|
342
|
+
|
|
343
|
+
// Check if we're running within a specific monorepo package. In that case just get the single project config.
|
|
344
|
+
if (!workspacePatterns || paths.targetRoot !== paths.targetDir) {
|
|
345
|
+
return getProjectConfig(
|
|
346
|
+
paths.targetDir,
|
|
347
|
+
{
|
|
348
|
+
...baseCoverageConfig,
|
|
349
|
+
...rootOptions,
|
|
350
|
+
},
|
|
351
|
+
extraRootOptions,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const globalRootConfig = { ...baseCoverageConfig };
|
|
356
|
+
const globalProjectConfig = {};
|
|
357
|
+
|
|
358
|
+
for (const [key, value] of Object.entries(rootOptions)) {
|
|
359
|
+
if (projectConfigKeys.includes(key)) {
|
|
360
|
+
globalProjectConfig[key] = value;
|
|
361
|
+
} else {
|
|
362
|
+
globalRootConfig[key] = value;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// If the target package is a workspace root, we find all packages in the
|
|
367
|
+
// workspace and load those in as separate jest projects instead.
|
|
368
|
+
const projectPaths = await Promise.all(
|
|
369
|
+
workspacePatterns.map(pattern =>
|
|
370
|
+
glob(path.join(paths.targetRoot, pattern)),
|
|
371
|
+
),
|
|
372
|
+
).then(_ => _.flat());
|
|
373
|
+
|
|
374
|
+
let projects = await Promise.all(
|
|
375
|
+
projectPaths.flat().map(async projectPath => {
|
|
376
|
+
const packagePath = path.resolve(projectPath, 'package.json');
|
|
377
|
+
if (!(await fs.pathExists(packagePath))) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// We check for the presence of "backstage-cli test" in the package test
|
|
382
|
+
// script to determine whether a given package should be tested
|
|
383
|
+
const packageData = await fs.readJson(packagePath);
|
|
384
|
+
const testScript = packageData.scripts && packageData.scripts.test;
|
|
385
|
+
const isSupportedTestScript =
|
|
386
|
+
testScript?.includes('backstage-cli test') ||
|
|
387
|
+
testScript?.includes('backstage-cli package test');
|
|
388
|
+
if (testScript && isSupportedTestScript) {
|
|
389
|
+
return await getProjectConfig(
|
|
390
|
+
projectPath,
|
|
391
|
+
{
|
|
392
|
+
...globalProjectConfig,
|
|
393
|
+
displayName: packageData.name,
|
|
394
|
+
},
|
|
395
|
+
extraRootOptions,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return undefined;
|
|
400
|
+
}),
|
|
401
|
+
).then(cs => cs.filter(Boolean));
|
|
402
|
+
|
|
403
|
+
const cache = global.__backstageCli_jestSuccessCache;
|
|
404
|
+
if (cache) {
|
|
405
|
+
projects = await cache.filterConfigs(projects, globalRootConfig);
|
|
406
|
+
}
|
|
407
|
+
const watchProjectFilter = global.__backstageCli_watchProjectFilter;
|
|
408
|
+
if (watchProjectFilter) {
|
|
409
|
+
projects = await watchProjectFilter.filter(projects);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
rootDir: paths.targetRoot,
|
|
414
|
+
projects,
|
|
415
|
+
testResultsProcessor: cache
|
|
416
|
+
? require.resolve('./jestCacheResultProcessor.cjs')
|
|
417
|
+
: undefined,
|
|
418
|
+
...globalRootConfig,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
module.exports = getRootConfig();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
module.exports = async results => {
|
|
18
|
+
const cache = global.__backstageCli_jestSuccessCache;
|
|
19
|
+
if (cache) {
|
|
20
|
+
await cache.reportResults(results);
|
|
21
|
+
}
|
|
22
|
+
return results;
|
|
23
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2022 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// 'jest-runtime' is included with jest and should be kept in sync with the installed jest version
|
|
18
|
+
// eslint-disable-next-line @backstage/no-undeclared-imports
|
|
19
|
+
const { default: JestRuntime } = require('jest-runtime');
|
|
20
|
+
|
|
21
|
+
module.exports = class CachingJestRuntime extends JestRuntime {
|
|
22
|
+
constructor(config, ...restArgs) {
|
|
23
|
+
super(config, ...restArgs);
|
|
24
|
+
this.allowLoadAsEsm = config.extensionsToTreatAsEsm.includes('.mts');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Unfortunately we need to use this unstable API to make sure that .js files
|
|
28
|
+
// are only loaded as modules where ESM is supported, i.e. Node.js packages.
|
|
29
|
+
unstable_shouldLoadAsEsm(path, ...restArgs) {
|
|
30
|
+
if (!this.allowLoadAsEsm) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return super.unstable_shouldLoadAsEsm(path, ...restArgs);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
process(src, filename) {
|
|
21
|
+
const assetFilename = JSON.stringify(path.basename(filename));
|
|
22
|
+
|
|
23
|
+
if (filename.match(/\.icon\.svg$/)) {
|
|
24
|
+
return {
|
|
25
|
+
code: `const React = require('react');
|
|
26
|
+
const SvgIcon = require('@material-ui/core/SvgIcon').default;
|
|
27
|
+
module.exports = {
|
|
28
|
+
__esModule: true,
|
|
29
|
+
default: props => React.createElement(SvgIcon, props, {
|
|
30
|
+
$$typeof: Symbol.for('react.element'),
|
|
31
|
+
type: 'svg',
|
|
32
|
+
ref: ref,
|
|
33
|
+
key: null,
|
|
34
|
+
props: Object.assign({}, props, {
|
|
35
|
+
children: ${assetFilename}
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
};`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { code: `module.exports = ${assetFilename};` };
|
|
43
|
+
},
|
|
44
|
+
};
|