cli: add option to reject network requets in frontend tests

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-10-18 23:40:52 +02:00
parent 6cbe4f999c
commit a7f97e40b3
7 changed files with 238 additions and 26 deletions
+46 -24
View File
@@ -23,6 +23,14 @@ const paths = require('@backstage/cli-common').findPaths(process.cwd());
const SRC_EXTS = ['ts', 'js', 'tsx', 'jsx', 'mts', 'cts', 'mjs', 'cjs'];
const FRONTEND_ROLES = [
'frontend',
'web-library',
'common-library',
'frontend-plugin',
'frontend-plugin-module',
];
const envOptions = {
oldTests: Boolean(process.env.BACKSTAGE_OLD_TESTS),
};
@@ -121,24 +129,13 @@ const transformIgnorePattern = [
// Provides additional config that's based on the role of the target package
function getRoleConfig(role) {
switch (role) {
case 'frontend':
case 'web-library':
case 'common-library':
case 'frontend-plugin':
case 'frontend-plugin-module':
return { testEnvironment: require.resolve('jest-environment-jsdom') };
case 'cli':
case 'backend':
case 'node-library':
case 'backend-plugin':
case 'backend-plugin-module':
default:
return { testEnvironment: require.resolve('jest-environment-node') };
if (FRONTEND_ROLES.includes(role)) {
return { testEnvironment: require.resolve('jest-environment-jsdom') };
}
return { testEnvironment: require.resolve('jest-environment-node') };
}
async function getProjectConfig(targetPath, extraConfig) {
async function getProjectConfig(targetPath, extraConfig, extraOptions) {
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.
@@ -232,6 +229,17 @@ async function getProjectConfig(targetPath, extraConfig) {
options.setupFilesAfterEnv = options.setupFilesAfterEnv || [];
if (
extraOptions.rejectFrontendNetworkRequests &&
FRONTEND_ROLES.includes(pkgJson.backstage?.role)
) {
// By adding this first we ensure that it's possible to for example override
// fetch with a mock in a custom setup file
options.setupFilesAfterEnv.unshift(
require.resolve('./jestRejectNetworkRequests.js'),
);
}
if (options.testEnvironment === require.resolve('jest-environment-jsdom')) {
// FIXME https://github.com/jsdom/jsdom/issues/1724
options.setupFilesAfterEnv.unshift(require.resolve('cross-fetch/polyfill'));
@@ -276,21 +284,31 @@ async function getRootConfig() {
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx,mjs,cjs}', '!**/*.d.ts'],
};
const { rejectFrontendNetworkRequests, ...rootOptions } =
rootPkgJson.jest ?? {};
const extraRootOptions = {
rejectFrontendNetworkRequests,
};
const workspacePatterns =
rootPkgJson.workspaces && rootPkgJson.workspaces.packages;
// Check if we're running within a specific monorepo package. In that case just get the single project config.
if (!workspacePatterns || paths.targetRoot !== paths.targetDir) {
return getProjectConfig(paths.targetDir, {
...baseCoverageConfig,
...(rootPkgJson.jest ?? {}),
});
return getProjectConfig(
paths.targetDir,
{
...baseCoverageConfig,
...rootOptions,
},
extraRootOptions,
);
}
const globalRootConfig = { ...baseCoverageConfig };
const globalProjectConfig = {};
for (const [key, value] of Object.entries(rootPkgJson.jest ?? {})) {
for (const [key, value] of Object.entries(rootOptions)) {
if (projectConfigKeys.includes(key)) {
globalProjectConfig[key] = value;
} else {
@@ -321,10 +339,14 @@ async function getRootConfig() {
testScript?.includes('backstage-cli test') ||
testScript?.includes('backstage-cli package test');
if (testScript && isSupportedTestScript) {
return await getProjectConfig(projectPath, {
...globalProjectConfig,
displayName: packageData.name,
});
return await getProjectConfig(
projectPath,
{
...globalProjectConfig,
displayName: packageData.name,
},
extraRootOptions,
);
}
return undefined;
@@ -0,0 +1,59 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const http = require('http');
const https = require('https');
const errorMessage = 'Network requests are not allowed in tests';
const origHttpAgent = http.globalAgent;
const origHttpsAgent = https.globalAgent;
const origFetch = global.fetch;
const origXMLHttpRequest = global.fetch;
http.globalAgent = new http.Agent({
lookup() {
throw new Error(errorMessage);
},
});
https.globalAgent = new https.Agent({
lookup() {
throw new Error(errorMessage);
},
});
if (global.fetch) {
global.fetch = async () => {
throw new Error(errorMessage);
};
}
if (global.XMLHttpRequest) {
global.XMLHttpRequest = class {
constructor() {
throw new Error(errorMessage);
}
};
}
// Reset overrides after each suite to make sure we don't pollute the test environment
afterAll(() => {
http.globalAgent = origHttpAgent;
https.globalAgent = origHttpsAgent;
global.fetch = origFetch;
global.XMLHttpRequest = origXMLHttpRequest;
});