Skip to content

Commit

Permalink
Fixes #37637 - Test plugins from foreman core
Browse files Browse the repository at this point in the history
  • Loading branch information
MariaAga committed Jul 18, 2024
1 parent 4af58fb commit 3be135a
Show file tree
Hide file tree
Showing 13 changed files with 396 additions and 11 deletions.
5 changes: 4 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"noopener",
"noreferrer",
"nowrap",
"npx",
"num",
"numpad",
"operatingsystem",
Expand Down Expand Up @@ -159,6 +160,7 @@
"textarea",
"textfield",
"tfm",
"theforeman",
"timepicker",
"timerdelay",
"timeseries",
Expand Down Expand Up @@ -192,6 +194,7 @@
"minLength": 3
}
],
"@theforeman/rules/require-ouiaid": ["error"]
"@theforeman/rules/require-ouiaid": ["error"],
"import/no-extraneous-dependencies": "off",
}
}
7 changes: 5 additions & 2 deletions .github/workflows/plugins_react_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ concurrency:
group: ${{ github.ref_name }}-${{ github.workflow }}
cancel-in-progress: true

env:
BUNDLE_WITHOUT: "console:development:journald:libvirt"

jobs:
setup_matrix:
name: Setup matrix
Expand Down Expand Up @@ -72,5 +75,5 @@ jobs:
run: npm ci --no-audit --legacy-peer-deps
working-directory: ${{ github.workspace }}/projects/plugin
- name: Run ${{ matrix.plugin }} tests
run: npm test
working-directory: ${{ github.workspace }}/projects/plugin
run: npm run test:plugins $(echo ${{ matrix.plugin }} | awk -F'/' '{print $NF}')
working-directory: ${{ github.workspace }}/projects/foreman
4 changes: 4 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
presets: [require.resolve('@theforeman/builder/babel')],
plugins: [require.resolve('babel-plugin-dynamic-import-node')],
};
1 change: 1 addition & 0 deletions package-exclude.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"EXCLUDE_NPM_PACKAGES": [
"@apollo/react-testing",
"@sheerun/mutationobserver-shim",
"@theforeman/env",
"@theforeman/eslint-plugin-foreman",
Expand Down
23 changes: 21 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
"lint:custom": "eslint ./webpack",
"foreman-js:link": "./script/npm_link_foreman_js.sh",
"postlint": "./script/npm_lint_plugins.js",
"test": "tfm-test",
"test": "npx jest --setupFilesAfterEnv ./global_test_setup.js ./core_test_setup.js --testPathIgnorePatterns '/node_modules/' '<rootDir>/.+fixtures.+' --config ./webpack/jest.config.js ",
"test:watch": "tfm-test --watchAll",
"test:current": "tfm-test --watch",
"test:plugins": "./script/npm_test_plugin.js",
"publish-coverage": "tfm-publish-coverage",
"postinstall": "./script/npm_install_plugins.js",
"analyze": "./script/webpack-analyze"
Expand All @@ -29,28 +30,45 @@
"react-intl": "^2.8.0"
},
"devDependencies": {
"@apollo/react-testing": "^4.0.0",
"@babel/core": "^7.7.0",
"@testing-library/jest-dom": "^5.3.0",
"@testing-library/react": "^10.0.2",
"@testing-library/react-hooks": "^3.4.2",
"@theforeman/builder": "^13.1.0",
"@theforeman/eslint-plugin-foreman": "^13.1.0",
"@theforeman/eslint-plugin-rules": "^13.1.0",
"@theforeman/test": "^13.1.0",
"@theforeman/vendor-core": "^13.1.0",
"@theforeman/vendor-dev": "^13.1.0",
"@types/jest": "<27.0.0",
"argv-parse": "^1.0.1",
"axios-mock-adapter": "^1.1.7",
"babel-eslint": "^10.0.0",
"babel-jest": "^26.3.0",
"babel-loader": "^8.0.0",
"buffer": "^5.7.1",
"cheerio": "1.0.0-rc.10",
"compression-webpack-plugin": "^10.0.0",
"cross-env": "^5.2.0",
"css-loader": "^6.8.1",
"dotenv": "^5.0.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.4.3",
"eslint": "^6.7.2",
"eslint-plugin-spellcheck": "0.0.17",
"graphql": "^15.5.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.4.0",
"jest-prop-type-error": "^1.1.0",
"jest-svg-transformer": "^1.0.0",
"jest-transform-graphql": "^2.1.0",
"path-browserify": "^1.0.1",
"prettier": "^1.19.1",
"pretty-format": "26.6.2",
"react-dnd-test-backend": "^9.4.0",
"react-redux-test-utils": "^0.2.0",
"react-test-renderer": "^17.0.1",
"redux-mock-store": "^1.2.2",
"sass": "~1.60.0",
"sass-loader": "^13.3.2",
Expand All @@ -59,6 +77,7 @@
"stylelint-config-standard": "^18.0.0",
"tabbable": "~5.2.0",
"victory-core": "~36.8.6",
"victory-legend": "~36.8.6",
"victory-pie": "~36.8.6",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0",
Expand Down
138 changes: 138 additions & 0 deletions script/npm_test_plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env node
/* eslint-disable import/no-dynamic-require */
/* eslint-disable no-console */
/* eslint-disable no-var */

/* This script is used to run tests for all plugins that have a `lint` script defined in their package.json
To run tests for an individual plugin, pass the plugin name as the first argument to the script
For example, to run tests for the `foreman-tasks` plugin, run: `npm run test-plugin foreman-tasks`
To pass arguments to jest, pass them after the plugin name like so: `npm run test-plugin foreman-tasks -- --debug`
*/

var fs = require('fs');
var path = require('path');
var lodash = require('lodash');
var childProcess = require('child_process');
var { packageJsonDirs } = require('./plugin_webpack_directories');

const passedArgs = process.argv.slice(2);
const coreConfigPath = path.resolve(__dirname, '../webpack/jest.config.js');
const coreConfig = require(coreConfigPath);

function runChildProcess(args, pluginPath) {
return new Promise((resolve, reject) => {
const child = childProcess.spawn('npx', args, {
shell: true,
});
// this is needed to make sure the output is not cut
let stdoutBuffer = '';
child.stdout.on('data', data => {
stdoutBuffer += data.toString();
const lines = stdoutBuffer.split('\n');
stdoutBuffer = lines.pop();
});

let stderrBuffer = `${pluginPath}: \n`;
child.stderr.on('data', data => {
stderrBuffer += data.toString();
const lines = stderrBuffer.split('\n');
stderrBuffer = lines.pop();
lines.forEach(line => console.error(line));
});
child.on('close', code => {
if (stdoutBuffer) console.log(stdoutBuffer);
if (stderrBuffer) console.error(stderrBuffer);
if (code === 0) {
resolve();
} else {
reject(new Error(`Child process exited with code ${code}`));
}
});
});
}
const runTests = async () => {
var dirs = packageJsonDirs();
function pluginDefinesLint(pluginPath) {
var packageHasNodeModules = fs.existsSync(`${pluginPath}/node_modules`); // skip gems
var packageData = JSON.parse(fs.readFileSync(`${pluginPath}/package.json`));

return (
packageHasNodeModules && packageData.scripts && packageData.scripts.lint
);
}
if (passedArgs[0] && passedArgs[0][0] !== '-') {
dirs = dirs.filter(dir => dir.endsWith(passedArgs[0]));
passedArgs.shift();
}
function customizer(objValue, srcValue) {
if (lodash.isArray(objValue)) {
return lodash.uniq(objValue.concat(srcValue));
}
return undefined;
}
// eslint-disable-next-line no-unused-vars
for (const pluginPath of dirs) {
if (pluginDefinesLint(pluginPath)) {
const testSetupFiles = [
path.resolve(__dirname, '../webpack/global_test_setup.js'),
];
const testSetupPath = path.join(pluginPath, 'webpack', 'test_setup.js');
if (fs.existsSync(testSetupPath)) {
testSetupFiles.unshift(testSetupPath);
}
const pluginConfigPath = path.join(pluginPath, 'jest.config.js');
const combinedConfigPath = path.join(
pluginPath,
'combined.jest.config.js'
);

if (fs.existsSync(pluginConfigPath)) {
// eslint-disable-next-line global-require
const pluginConfig = require(pluginConfigPath);

const combinedConfig = lodash.mergeWith(
pluginConfig,
{
...coreConfig,
setupFilesAfterEnv: [
path.resolve(__dirname, '../webpack/global_test_setup.js'),
],
},
customizer
);
combinedConfig.snapshotSerializers = coreConfig.snapshotSerializers;
fs.writeFileSync(
combinedConfigPath,
`module.exports = ${JSON.stringify(combinedConfig, null, 2)};`,
'utf8'
);
}
const pluginConfigOverride = fs.existsSync(pluginConfigPath);
const configPath = pluginConfigOverride
? combinedConfigPath
: coreConfigPath;
const corePath = path.resolve(__dirname, '../');
const args = [
'jest',
`${pluginPath}/webpack`,
'--roots',
pluginPath,
corePath,
`--config=${configPath}`,
pluginConfigOverride
? ''
: `--setupFilesAfterEnv ${testSetupFiles.join(' ')}`,
'--color',
...passedArgs,
];

// eslint-disable-next-line no-await-in-loop
await runChildProcess(args, pluginPath); // Run every plugin test in a separate process
if (fs.existsSync(combinedConfigPath)) {
fs.unlinkSync(combinedConfigPath);
}
}
}
};

runTests();
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ import * as actions from '../TemplateGeneratorActions';
jest.mock('file-saver');
jest.mock('../../../redux/API');

beforeEach(() => {
API.post.mockImplementation(async () => scheduleResponse);
API.get.mockImplementation(async () => noContentResponse);
});

describe('TemplateGeneratorActions', () => {
beforeAll(() => {
jest.useFakeTimers();
});
beforeEach(() => {
API.post.mockClear();
API.get.mockClear();

API.post.mockImplementation(async () => scheduleResponse);
API.get.mockImplementation(async () => noContentResponse);
});

describe('generateTemplate', () => {
Expand All @@ -55,7 +56,6 @@ describe('TemplateGeneratorActions', () => {
API.get
.mockImplementationOnce(async () => noContentResponse)
.mockImplementationOnce(async () => generatedReportResponse);

runActionInDepth(() => actions.generateTemplate(), 3).then(callTree => {
const successAction = callTree[1][1][1];
expect(successAction).toHaveProperty('type', TEMPLATE_GENERATE_SUCCESS);
Expand Down
7 changes: 7 additions & 0 deletions webpack/test_setup.js → webpack/core_test_setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ ace.config.set('themePath', '');

jest.mock('jed');
jest.mock('./assets/javascripts/react_app/Root/Context/ForemanContext', () => ({
getForemanContext: () => ({
context: { metadata: { version: 'mocked_version' } },
}),
useForemanContext: () => ({ metadata: { version: 'mocked_version' } }),
useForemanSetContext: () => {},
useForemanVersion: () => 'mocked_version',
useForemanSettings: () => ({ perPage: 5 }),
useForemanDocUrl: () => '/url',
useForemanLocation: () => ({ title: 'location' }),
useForemanOrganization: () => ({ title: 'organization' }),
useForemanUser: () => ({ login: 'user' }),
getHostsPageUrl: displayNewHostsPage =>
displayNewHostsPage ? '/new/hosts' : '/hosts',
useForemanHostsPageUrl: () => '/hosts',
}));
jest.mock('./assets/javascripts/react_app/common/I18n');
jest.mock('./assets/javascripts/foreman_tools', () => ({
Expand Down
19 changes: 19 additions & 0 deletions webpack/global_test_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// eslint-disable-next-line import/no-unresolved, import/extensions
import 'core-js/shim';
// eslint-disable-next-line import/no-extraneous-dependencies
import 'regenerator-runtime/runtime';

const { configure } = require('./theforeman-test');
const Adapter = require('enzyme-adapter-react-16');

configure({ adapter: new Adapter() });

// https://github.com/facebook/jest/issues/6121
// eslint-disable-next-line no-console
const { error } = console;
// eslint-disable-next-line no-console
console.error = (message, ...args) => {
error.apply(console, args); // keep default behaviour
const err = message instanceof Error ? message : new Error(message);
throw err;
};
Loading

0 comments on commit 3be135a

Please sign in to comment.