diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 0f541f19ff3..00000000000
--- a/.eslintrc
+++ /dev/null
@@ -1,200 +0,0 @@
-{
- "plugins": [
- "@theforeman/foreman",
- "spellcheck",
- "@theforeman/rules"
- ],
- "extends": "plugin:@theforeman/foreman/core",
- "rules": {
- "spellcheck/spell-checker": [
- "warn",
- {
- "comments": false,
- "identifiers": false,
- "strings": true,
- "lang": "en_US",
- "ignoreRequire": false,
- "skipWords": [
- "2xl",
- "45vh",
- "4xl",
- "ampm",
- "Ansible",
- "ascii",
- "auditable",
- "Autocompletion",
- "autogenerated",
- "bool",
- "bootable",
- "Borderless",
- "btns",
- "candlepin",
- "centos",
- "checkbox",
- "clearbutton",
- "clearfix",
- "comms",
- "consts",
- "cpu",
- "csrf",
- "csv",
- "datacenter",
- "datastore",
- "datastores",
- "datepicker",
- "datetime",
- "datetimepicker",
- "debian",
- "decrement", // should be removed once https://github.com/aotaduy/eslint-plugin-spellcheck/issues/67 is resolved.
- "devs",
- "dgettext",
- "dngettext",
- "donut",
- "dow",
- "dropdown",
- "dropdowns",
- "ec2",
- "erb",
- "fieldset",
- "fnc",
- "formatter",
- "fqdn",
- "func",
- "gettext",
- "glyphicon",
- "gpg",
- "graphql",
- "gridster",
- "hostdetails",
- "hostgroup",
- "hostgroups",
- "href",
- "hypervisor",
- "i386",
- "internets",
- "ip6",
- "IPv4",
- "IPv6",
- "javascript",
- "javascripts",
- "jed",
- "katello",
- "keybind",
- "keydown",
- "keypress",
- "klasses",
- "labelledby",
- "lang",
- "ldap",
- "loc",
- "locs",
- "lsi",
- "matcher",
- "menuitem",
- "monokai",
- "mousedown",
- "mouseup",
- "msg",
- "nailgun",
- "nat",
- "navitem",
- "netgroups",
- "networksurl",
- "ngettext",
- "nic",
- "nfs",
- "nonpersistent",
- "noopener",
- "noreferrer",
- "nowrap",
- "num",
- "numpad",
- "operatingsystem",
- "operatingsystems",
- "orderable",
- "orgs",
- "ownfield",
- "paravirtual",
- "patternfly",
- "pficon",
- "poolsurl",
- "popstate",
- "posinset",
- "pqr",
- "ptable",
- "puppetclass",
- "puppetclasses",
- "pxe_loader",
- "Pv",
- "qcow2",
- "rbt",
- "readonly",
- "redhat",
- "redux",
- "refetches",
- "rendering",
- "repo",
- "resize",
- "rex",
- "rhel",
- "safemode",
- "sbs",
- "scrollable",
- "scsi",
- "securityfailure",
- "setsize",
- "sizex",
- "sizey",
- "Solaris",
- "sparc",
- "storages",
- "stringified",
- "subcomponent",
- "subcomponents",
- "subnav",
- "subnet",
- "subnets",
- "substate",
- "svg",
- "testoption",
- "testoption",
- "textarea",
- "textfield",
- "tfm",
- "timepicker",
- "timerdelay",
- "timeseries",
- "tlv",
- "tooltip",
- "turbolinks",
- "twipsy",
- "txt",
- "typeahead",
- "ubuntu",
- "uncheck",
- "unencrypted",
- "unmount",
- "unordered",
- "unprocessable",
- "unselect",
- "unstyled",
- "virtualization",
- "vms",
- "vmware",
- "vnc",
- "vnic",
- "webpack",
- "wget",
- "wss",
- "x86_64",
- "xml",
- "xpi",
- "xyz",
- "yaml"
- ],
- "minLength": 3
- }
- ],
- "@theforeman/rules/require-ouiaid": ["error"]
- }
-}
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000000..f3452815619
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,21 @@
+const lintCoreConfig = require('./script/lint/lint_core_config.js');
+const lintGenericConfig = require('./script/lint/lint_generic_config.js');
+
+const combinedConfig = {
+ ...lintCoreConfig,
+ ...lintGenericConfig,
+ rules: {
+ ...lintCoreConfig.rules,
+ ...lintGenericConfig.rules,
+ },
+ plugins: [
+ ...(lintCoreConfig.plugins || []),
+ ...(lintGenericConfig.plugins || []),
+ ],
+ extends: [
+ ...(lintCoreConfig.extends || []),
+ ...(lintGenericConfig.extends || []),
+ ],
+};
+
+module.exports = combinedConfig;
diff --git a/.github/workflows/foreman.yml b/.github/workflows/foreman.yml
index 7a5c845604d..07862bb1d39 100644
--- a/.github/workflows/foreman.yml
+++ b/.github/workflows/foreman.yml
@@ -93,6 +93,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
+ - name: use npm 8
+ run: npm i -g npm@8 --registry=https://registry.npmjs.org
- name: Setup NPM Cache
uses: actions/cache@v4
with:
diff --git a/.github/workflows/js_tests.yml b/.github/workflows/js_tests.yml
index 7501009fb3e..5d8980fd24f 100644
--- a/.github/workflows/js_tests.yml
+++ b/.github/workflows/js_tests.yml
@@ -46,14 +46,14 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
+ - name: use npm 8
+ run: npm i -g npm@8 --registry=https://registry.npmjs.org
- name: Generate npm dependencies package-lock
run: npm install --package-lock-only --no-audit
- name: Install npm dependencies
run: npm ci --no-audit
- name: Run linter
run: npm run lint
- - name: Run custom eslint rules Spellcheck (only warnings) and missing ouia-ids
- run: npm run lint:custom
- name: Run tests
run: npm run test
- name: Publish Coveralls
diff --git a/.github/workflows/plugins_react_tests.yml b/.github/workflows/plugins_react_tests.yml
index 7d0f7184b7c..ce1d1baeb42 100644
--- a/.github/workflows/plugins_react_tests.yml
+++ b/.github/workflows/plugins_react_tests.yml
@@ -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
@@ -45,11 +48,13 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- # We could update the postinstall action for foreman to look for an environment variable for plugin webpack dirs
- # before kicking off the ruby script to find them, this would eliminate the ruby dep and running `npm install` in plugins.
- - uses: ruby/setup-ruby@v1
+ - name: use npm 8
+ run: npm i -g npm@8 --registry=https://registry.npmjs.org
+ - name: "Set up Ruby ${{ matrix.ruby }}"
+ uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
+ bundler-cache: true
- name: Checkout Foreman
uses: actions/checkout@v4
with:
@@ -65,12 +70,29 @@ jobs:
with:
repository: ${{ matrix.plugin }}
path: ${{ github.workspace }}/projects/plugin
+ - name: store plugin name
+ run: echo "PLUGIN_NAME=$(echo ${{ matrix.plugin }} | awk -F'/' '{print $NF}')" >> "${GITHUB_ENV}"
+ - name: Set up plugin in Foreman
+ run: |
+ echo "gemspec name: '$PLUGIN_NAME', path: '${{ github.workspace }}/projects/plugin'" > "bundler.d/$PLUGIN_NAME.local.rb"
+ if [ -d $PLUGIN_NAME/gemfile.d ] ; then
+ cat $PLUGIN_NAME/gemfile.d/*.rb >> bundler.d/$PLUGIN_NAME.local.rb
+ fi
+ working-directory: ${{ github.workspace }}/projects/foreman
- name: Generate ${{ matrix.plugin }} npm dependencies package-lock
run: npm install --package-lock-only --no-audit --legacy-peer-deps
working-directory: ${{ github.workspace }}/projects/plugin
- name: Install ${{ matrix.plugin }} npm dependencies
run: npm ci --no-audit --legacy-peer-deps
working-directory: ${{ github.workspace }}/projects/plugin
+ - run: sudo apt-get update
+ - run: sudo apt-get -qq -y install build-essential libcurl4-openssl-dev zlib1g-dev libpq-dev libvirt-dev
+ - name: Install gems
+ run: bundle install
+ working-directory: ${{ github.workspace }}/projects/foreman
+ - name: Run plugin webpack dir to test
+ run: ./script/plugin_webpack_directories.rb
+ working-directory: ${{ github.workspace }}/projects/foreman
- 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
diff --git a/.npmrc b/.npmrc
index d5831dd5188..b6f27f13595 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,2 +1 @@
engine-strict=true
-legacy-peer-deps=true
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 25babb1b055..582ed1bb9c0 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -80,7 +80,7 @@ function onContentLoad() {
password_caps_lock_hint();
tfm.i18n.intl.ready.then(function() {
- var tz = jstz.determine();
+ var tz = tfm.jstz.determine();
$.cookie('timezone', tz.name(), {
path: '/',
secure: location.protocol === 'https:',
diff --git a/app/assets/javascripts/host_edit.js b/app/assets/javascripts/host_edit.js
index 7c2f18ab8b6..19eeb147e13 100644
--- a/app/assets/javascripts/host_edit.js
+++ b/app/assets/javascripts/host_edit.js
@@ -402,12 +402,12 @@ function serializeForm() {
}
function subnet_contains(network, cidr, ip) {
- if (!ip || 0 === ip.length || !ipaddr.isValid(ip)) {
+ if (!ip || 0 === ip.length || !tfm.ipaddr.isValid(ip)) {
return;
}
- var addr = ipaddr.parse(ip);
- var range = ipaddr.parse(network);
+ var addr = tfm.ipaddr.parse(ip);
+ var range = tfm.ipaddr.parse(network);
return addr.match(range, cidr);
}
diff --git a/app/assets/javascripts/subnets.js b/app/assets/javascripts/subnets.js
index 19db0e569cd..943e67bd430 100644
--- a/app/assets/javascripts/subnets.js
+++ b/app/assets/javascripts/subnets.js
@@ -69,7 +69,7 @@ function subnetMaskChanged(field) {
}
if ($('input[id^=subnet_type_]:checked').val() === 'Subnet::Ipv4') {
try {
- var cidr = ipaddr.IPv4.parse(mask).prefixLengthFromSubnetMask();
+ var cidr = tfm.ipaddr.IPv4.parse(mask).prefixLengthFromSubnetMask();
} catch (err) {
var cidr = '';
}
diff --git a/app/helpers/reactjs_helper.rb b/app/helpers/reactjs_helper.rb
index a09381c2372..6afdc4f5ec4 100644
--- a/app/helpers/reactjs_helper.rb
+++ b/app/helpers/reactjs_helper.rb
@@ -36,13 +36,8 @@ def get_webpack_chunk(name, extension)
data['assetsByChunkName'][name]&.find { |value| value.end_with?(".#{extension}") }
end
- def get_webpack_foreman_vendor_js
- foreman_vendor_js = get_webpack_chunk('foreman-vendor', 'js')
- javascript_include_tag("/webpack/#{foreman_vendor_js}")
- end
-
def get_webpack_foreman_vendor_css
- foreman_vendor_css = get_webpack_chunk('foreman-vendor', 'css')
+ foreman_vendor_css = get_webpack_chunk('vendorStyles', 'css')
stylesheet_link_tag("/webpack/#{foreman_vendor_css}")
end
diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb
index 7099e4d458f..e94f1e5df54 100644
--- a/app/views/layouts/base.html.erb
+++ b/app/views/layouts/base.html.erb
@@ -36,10 +36,10 @@
- <%= get_webpack_foreman_vendor_js %>
+
<%= javascript_include_tag("/webpack/#{get_webpack_chunk('vendor', 'js')}") %>
- <%= javascript_include_tag("/webpack/#{get_webpack_chunk('bundle', 'js')}") %>
<%= javascript_include_tag("/webpack/#{get_webpack_chunk('reactExports', 'js')}") %>
+ <%= javascript_include_tag("/webpack/#{get_webpack_chunk('bundle', 'js')}") %>
<%= javascript_include_tag 'application' %>
<%= webpacked_plugins_with_global_js %>
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 00000000000..773205911e8
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: [require.resolve('@theforeman/builder/babel')],
+};
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 17c8186432e..cac3f52b391 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -4,9 +4,10 @@
var path = require('path');
var webpack = require('webpack');
const dotenv = require('dotenv');
+const root = path.resolve(__dirname, '..');
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
dotenv.config();
-var ForemanVendorPlugin = require('@theforeman/vendor')
- .WebpackForemanVendorPlugin;
var StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
var vendorEntry = require('./webpack.vendor');
var fs = require('fs');
@@ -15,6 +16,18 @@ var pluginUtils = require('../script/plugin_webpack_directories');
var { generateExportsFile }= require('../webpack/assets/javascripts/exportAll');
var CompressionPlugin = require('compression-webpack-plugin');
+const packageJsonPath = path.resolve(__dirname,'..', 'package.json');
+const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
+const dependencies = packageJson.dependencies || {};
+const devDependencies = packageJson.devDependencies || {};
+const allDependencies = { ...dependencies, ...devDependencies };
+const shared = isPlugin => Object.keys(allDependencies).map(dep => ({
+ [dep]: {
+ eager: !isPlugin, // core should load all dependencies eagerly so they will be available for plugins
+ singleton: true,
+ requiredVersion: allDependencies[dep],
+ }}));
+
class AddRuntimeRequirement {
// to avoid "webpackRequire.l is not a function" error
// enables use of webpack require inside promise new promise
@@ -79,11 +92,18 @@ const commonConfig = function() {
os: require.resolve('os-browserify'),
},
alias: {
+ 'patternfly-react$': path.resolve(__dirname,'..', 'node_modules/patternfly-react/dist/js/index.js'), // to avoid circular dependency in dist/esm
+ '/node_modules/jquery': path.resolve(__dirname, '..', 'webpack/assets/javascripts/jquery.js'),
+ 'jquery': path.resolve(__dirname, '..', 'webpack/assets/javascripts/jquery.js'),
foremanReact: path.join(
__dirname,
'../webpack/assets/javascripts/react_app'
),
+ 'react/jsx-runtime': 'react/jsx-runtime.js', // for react-dnd
+ 'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js', // for react-dnd
+
},
+
},
resolveLoader: {
modules: [path.resolve(__dirname, '..', 'node_modules')],
@@ -117,9 +137,6 @@ const commonConfig = function() {
],
},
plugins: [
- new ForemanVendorPlugin({
- mode,
- }),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(mode),
@@ -160,13 +177,18 @@ const coreConfig = function() {
}
config.entry = {
+ /* keep bundle entry files and reactExports seperate to avoid late loading issues of mixed files, import in react_app only from react_app and node_modules */
bundle: { import: bundleEntry, dependOn: ['vendor', 'reactExports'] },
- vendor: vendorEntry,
- reactExports: path.join(
+ reactExports: {
+ import: path.join(
__dirname,
'..',
'webpack/assets/javascripts/all_react_app_exports.js'
- ),
+ ),
+ dependOn: 'vendor',
+ },
+ vendor: vendorEntry,
+ vendorStyles: path.join(__dirname, '..', 'webpack/assets/javascripts/react_app/common/scss/vendor-core.scss'),
};
config.output = {
path: path.join(__dirname, '..', 'public', 'webpack'),
@@ -179,9 +201,12 @@ const coreConfig = function() {
};
var plugins = config.plugins;
+ plugins.push(
+ new MiniCssExtractPlugin());
plugins.push(
new ModuleFederationPlugin({
name: 'foremanReact',
+ shared: shared(false),
})
);
plugins.push(
@@ -191,8 +216,10 @@ const coreConfig = function() {
);
config.plugins = plugins;
var rules = config.module.rules;
+
rules.push({
test: /\.(sa|sc|c)ss$/,
+ exclude: /vendor-core/i,
use: [
{
loader: 'style-loader',
@@ -205,6 +232,14 @@ const coreConfig = function() {
'sass-loader',
],
});
+ rules.push({
+ test: /vendor-core/i,
+ use: [
+ MiniCssExtractPlugin.loader,
+ 'css-loader',
+ 'sass-loader',
+ ],
+ });
config.module.rules = rules;
return config;
};
@@ -276,8 +311,10 @@ const pluginConfig = function(plugin) {
name: pluginName,
filename: pluginName + '_remoteEntry.js',
exposes: pluginEntries,
+ shared: shared(true),
})
);
+
config.plugins = plugins;
var rules = config.module.rules;
rules.push({
diff --git a/config/webpack.vendor.js b/config/webpack.vendor.js
index d6f31b32227..25f29718e8c 100644
--- a/config/webpack.vendor.js
+++ b/config/webpack.vendor.js
@@ -1 +1,122 @@
-module.exports = ['react-intl', 'intl'];
+/* eslint-disable */
+
+module.exports = [
+ 'intl',
+ /**
+ * React related
+ */
+ '@apollo/client',
+ '@apollo/client/link/batch-http',
+ '@reduxjs/toolkit',
+ 'core-js/shim',
+ 'regenerator-runtime/runtime',
+ 'formik',
+ 'rc-input-number',
+ 'react',
+ 'react-ace',
+ 'react-dom',
+ 'react-dnd',
+ 'react-dnd-html5-backend',
+ 'react-debounce-input',
+ 'react-diff-view',
+ 'react-ellipsis-with-tooltip',
+ 'react-onclickoutside',
+ 'react-password-strength',
+ 'react-router-dom',
+ 'react-router-bootstrap',
+ 'react-loading-skeleton',
+ 'react-redux',
+ 'redux',
+ 'redux-logger',
+ 'redux-thunk',
+ 'reselect',
+ 'prop-types',
+ 'classnames',
+ 'seamless-immutable',
+ 'connected-react-router',
+ 'react-helmet',
+ 'react-intl',
+
+ /**
+ * Patternfly related
+ */
+ 'patternfly-react',
+ 'patternfly-react-extensions',
+ '@patternfly/react-core',
+ '@patternfly/react-icons',
+ '@patternfly/react-table',
+ '@patternfly/react-tokens',
+ '@patternfly/react-styles',
+ '@patternfly/react-charts',
+ // '@redhat-cloud-services/frontend-components',
+
+ /**
+ * ace-builds related
+ */
+ 'ace-builds',
+ 'ace-builds/src-noconflict/ace',
+ 'ace-builds/src-noconflict/ext-language_tools',
+ 'ace-builds/src-noconflict/mode-ruby',
+ 'ace-builds/src-noconflict/mode-json',
+ 'ace-builds/src-noconflict/mode-sh',
+ 'ace-builds/src-noconflict/mode-html_ruby',
+ 'ace-builds/src-noconflict/mode-xml',
+ 'ace-builds/src-noconflict/mode-yaml',
+ 'ace-builds/src-noconflict/theme-github',
+ 'ace-builds/src-noconflict/theme-monokai',
+ 'ace-builds/src-noconflict/keybinding-vim',
+ 'ace-builds/src-noconflict/keybinding-emacs',
+ 'ace-builds/src-min-noconflict/ext-searchbox',
+
+ /**
+ * UUID
+ */
+ 'uuid',
+ 'uuid/v1',
+ 'uuid/v3',
+ 'uuid/v4',
+ 'uuid/v5',
+
+ 'jstz',
+ 'diff',
+ /**
+ * Custom modules
+ */
+ // {
+ // name: 'jquery',
+ // path: '@theforeman/vendor-core/lib/customModules/jquery.js',
+ // },
+ // {
+ // name: 'jstz',
+ // window: 'jstz',
+ // },
+ // {
+ // name: 'ipaddr.js',
+ // window: 'ipaddr',
+ // },
+ // {
+ // name: 'diff',
+ // window: 'diff',
+ // },
+
+ /**
+ * Other packages
+ */
+ 'history',
+ 'number_helpers',
+ 'lodash',
+ 'axios',
+ 'file-saver',
+ 'humanize-duration',
+ 'unidiff',
+ 'urijs',
+ 'yup',
+ 'select2',
+ 'multiselect',
+ '@novnc/novnc/core/rfb',
+ '@novnc/novnc',
+
+ // '@spice-project/spice-html5',
+ // '@webcomponents/webcomponentsjs/webcomponents-bundle',
+ // '@webcomponents/webcomponentsjs/custom-elements-es5-adapter',
+];
diff --git a/developer_docs/adding-dependencies.asciidoc b/developer_docs/adding-dependencies.asciidoc
deleted file mode 100644
index 9cbaeea321b..00000000000
--- a/developer_docs/adding-dependencies.asciidoc
+++ /dev/null
@@ -1,32 +0,0 @@
-[[adding-js-dependencies]]
-= NPM dependencies
-:toc: right
-:toclevels: 5
-
-## Using/Adding/updating NPM dependencies
-
-Foreman manage npm dependencies with a seperate project called `@theforeman/vendor` which responsible to deliver 3rd-party modules to foreman and its plugins.
-Foreman and its plugins consumes `@theforeman/vendor` project from `npm` in development and from `rpm` in production.
-
-`@theforeman/vendor` lives inside a monorepo together with other foreman javascript tools in a project called https://github.com/theforeman/foreman-js[foreman-js]
-
-
-https://github.com/theforeman/foreman-js/tree/master/packages/vendor[Read more about @theforeman/vendor]
-
-## Consuming `foreman-js` projects from source (locally)
-
-Clone, install, build and link the `foreman-js` project to foreman:
-
-```sh
-git clone git@github.com:theforeman/foreman-js.git
-cd foreman-js
-npm install
-npm run build
-npm run link -- --location ../foreman
-```
-
-To read to full documentation, please go to `foreman-js` contribution guide:
-https://github.com/theforeman/foreman-js/blob/master/CONTRIBUTING.md
-
-**NOTICE: You must remove `./node_modules/@theforeman` before running `npm install`.
-If not, it will destroy your `foreman-js` copy.**
diff --git a/package-exclude.json b/package-exclude.json
index 3b9dabad042..747d37d055f 100644
--- a/package-exclude.json
+++ b/package-exclude.json
@@ -1,5 +1,6 @@
{
"EXCLUDE_NPM_PACKAGES": [
+ "@apollo/react-testing",
"@sheerun/mutationobserver-shim",
"@theforeman/env",
"@theforeman/eslint-plugin-foreman",
diff --git a/package.json b/package.json
index 7ae5676471f..0de2a5bfbe4 100644
--- a/package.json
+++ b/package.json
@@ -5,52 +5,141 @@
"description": "Foreman isn't really a node module, these are just dependencies needed to build the webpack bundle. 'dependencies' are the asset libraries in use and 'devDependencies' are used for the build process.",
"private": true,
"engines": {
- "node": ">=14.0.0 <21.0.0"
+ "node": ">=14.0.0 <21.0.0",
+ "npm": ">=8.0.0"
},
"scripts": {
- "lint": "tfm-lint",
- "lint:custom": "eslint ./webpack",
+ "lint": "eslint ./webpack ./script",
+ "prelint": "node ./script/lint/link-eslint-plugin.js",
"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/' '/.+fixtures.+' --config ./webpack/jest.config.js",
"test:watch": "tfm-test --watchAll",
"test:current": "tfm-test --watch",
+ "test:plugins": "./script/npm_test_plugin.js",
+ "lint:plugins": "./script/npm_lint_plugins.js",
"publish-coverage": "tfm-publish-coverage",
"postinstall": "./script/npm_install_plugins.js",
"analyze": "./script/webpack-analyze"
},
"dependencies": {
+ "@apollo/client": "^3.3.7",
"@module-federation/utilities": "^1.7.0",
- "@theforeman/vendor": "^13.1.0",
+ "@novnc/novnc": "1.3",
+ "@patternfly/patternfly": "^4.171.1",
+ "@patternfly/react-charts": "~6.94.15",
+ "@patternfly/react-core": "~4.271.2",
+ "@patternfly/react-icons": "~4.93.3",
+ "@patternfly/react-styles": "~4.92.3",
+ "@patternfly/react-table": "~4.112.6",
+ "@patternfly/react-tokens": "~4.94.3",
+ "@reduxjs/toolkit": "^1.6.0",
+ "@spice-project/spice-html5": "^0.2.1",
+ "@webcomponents/webcomponentsjs": "^2.2.10",
+ "ace-builds": "^1.4.13",
+ "axios": "^0.21.1",
+ "bootstrap-sass": "^3.3.7",
+ "classnames": "^2.2.5",
+ "connected-react-router": "6.6.1",
+ "core-js": "^2.5.7",
+ "datatables.net": "~1.10.12",
+ "datatables.net-bs": "~1.10.12",
+ "diff": "^4.0.1",
+ "dsmorse-gridster": "^0.8.0",
+ "file-saver": "^2.0.1",
+ "formik": "^1.5.8",
+ "graphql": "^15.5.0",
"graphql-tag": "^2.11.0",
+ "history": "^4.7.2",
+ "humanize-duration": "3.27.0",
"intl": "~1.2.5",
+ "ipaddr.js": "~1.2.0",
"jed": "^1.1.1",
+ "jquery": "~2.2.4",
+ "jquery-ujs": "~1.2.0",
+ "jquery.cookie": "~1.4.1",
+ "jstz": "~1.0.7",
+ "lodash": "^4.17.14",
+ "multiselect": "~0.9.12",
+ "number_helpers": "^0.1.1",
"os-browserify": "^0.3.0",
- "react-intl": "^2.8.0"
+ "patternfly": "^3.58.0",
+ "patternfly-react": "^2.39.17",
+ "patternfly-react-extensions": "^3.0.12",
+ "prop-types": "^15.6.0",
+ "rc-input-number": "^6.0.0",
+ "react": "^16.9.0",
+ "react-ace": "^9.5.0",
+ "react-debounce-input": "^3.2.0",
+ "react-diff-view": "^2.4.5",
+ "react-dnd": "^14.0.2",
+ "react-dnd-html5-backend": "^14.0.0",
+ "react-dom": "^16.8.1",
+ "react-ellipsis-with-tooltip": "^1.0.8",
+ "react-helmet": "^6.1.0",
+ "react-intl": "^2.8.0",
+ "react-loading-skeleton": "^1.1.2",
+ "react-onclickoutside": "^6.6.2",
+ "react-password-strength": "^2.4.0",
+ "react-redux": "^7.1.0",
+ "react-router-bootstrap": "^0.25.0",
+ "react-router-dom": "^5.1.2",
+ "redux": "^4.0.4",
+ "redux-logger": "^2.8.1",
+ "redux-thunk": "^2.2.0",
+ "regenerator-runtime": "^0.13.3",
+ "reselect": "^3.0.1",
+ "sanitize-html": "2.3.2",
+ "seamless-immutable": "^7.1.2",
+ "select2": "~3.5.2-browserify",
+ "unidiff": "^1.0.0",
+ "urijs": "^1.19.4",
+ "uuid": "^3.3.2",
+ "yup": "^0.29.3"
},
"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",
+ "eslint-plugin-patternfly-react": "0.2.0",
+ "eslint-plugin-jquery": "^1.5.1",
+ "eslint-plugin-promise": "^4.2.1",
+ "eslint-plugin-react-hooks": "^2.1.1",
"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",
+ "jsx-ast-utils": "^3.3.3",
"path-browserify": "^1.0.1",
"prettier": "^1.19.1",
"pretty-format": "26.6.2",
"react-dnd-test-backend": "^9.4.0",
+ "react-test-renderer": "^16.9.0",
+ "mini-css-extract-plugin": "^2.9.1",
"redux-mock-store": "^1.2.2",
"sass": "~1.60.0",
"sass-loader": "^13.3.2",
@@ -58,11 +147,30 @@
"stylelint": "^9.3.0",
"stylelint-config-standard": "^18.0.0",
"tabbable": "~5.2.0",
- "victory-core": "~36.8.6",
- "victory-pie": "~36.8.6",
+ "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",
"webpack-cli": "^5.0.1",
"webpack-stats-plugin": "^1.0.3"
+ },
+ "overrides": {
+ "victory-core": "36.8.6",
+ "victory-pie": "36.8.6",
+ "victory-area": "36.8.6",
+ "victory-axis": "36.8.6",
+ "victory-bar": "36.8.6",
+ "victory-chart": "36.8.6",
+ "victory-create-container": "36.8.6",
+ "victory-cursor-container": "36.8.6",
+ "victory-group": "36.8.6",
+ "victory-legend": "36.8.6",
+ "victory-line": "36.8.6",
+ "victory-scatter": "36.8.6",
+ "victory-stack": "36.8.6",
+ "victory-tooltip": "36.8.6",
+ "victory-voronoi-container": "36.8.6",
+ "victory-zoom-container": "36.8.6"
}
}
diff --git a/script/lint/@foreman/eslint-plugin-custom/index.js b/script/lint/@foreman/eslint-plugin-custom/index.js
new file mode 100644
index 00000000000..337fc704241
--- /dev/null
+++ b/script/lint/@foreman/eslint-plugin-custom/index.js
@@ -0,0 +1,7 @@
+const requireOuiaidRule = require('./require-ouiaid');
+
+module.exports = {
+ rules: {
+ 'require-ouiaid': requireOuiaidRule,
+ },
+};
diff --git a/script/lint/@foreman/eslint-plugin-custom/require-ouiaid.js b/script/lint/@foreman/eslint-plugin-custom/require-ouiaid.js
new file mode 100644
index 00000000000..2254b202a99
--- /dev/null
+++ b/script/lint/@foreman/eslint-plugin-custom/require-ouiaid.js
@@ -0,0 +1,84 @@
+const getProp = require('jsx-ast-utils/getProp');
+
+module.exports = {
+ create(context) {
+ const patternflyImports = new Set();
+ const options = context.options.length
+ ? context.options
+ : [
+ 'Alert',
+ 'Breadcrumb',
+ 'Button',
+ 'Card',
+ 'Checkbox',
+ 'Chip',
+ 'ChipGroup',
+ 'ContextSelector',
+ 'Dropdown',
+ 'DropdownItem',
+ 'DropdownSeparator',
+ 'DropdownToggle',
+ 'DropdownToggleCheckbox',
+ 'FormSelect',
+ 'Menu',
+ 'Modal',
+ 'ModalBoxCloseButton',
+ 'ModalContent',
+ 'Nav',
+ 'NavExpandable',
+ 'NavItem',
+ 'OptionsMenu',
+ 'Pagination',
+ 'Radio',
+ 'RowWrapper',
+ 'Select',
+ 'Switch',
+ 'TabButton',
+ 'TabContent',
+ 'Tab',
+ 'Tabs',
+ 'Text',
+ 'TextInput',
+ 'Title',
+ 'Toolbar',
+ 'Table',
+ 'TableComposable',
+ 'Tr',
+ ];
+
+ function addPatternflyImport(node) {
+ if (
+ node.type === 'ImportDeclaration' &&
+ node.source.value.startsWith('@patternfly/react')
+ ) {
+ node.specifiers.forEach(specifier => {
+ if (specifier.type === 'ImportSpecifier') {
+ patternflyImports.add(specifier.local.name);
+ }
+ });
+ }
+ }
+
+ function checkPatternflyComponent(node) {
+ if (!options.includes(node.name.name)) {
+ return;
+ }
+ if (
+ node.type === 'JSXOpeningElement' &&
+ patternflyImports.has(node.name.name)
+ ) {
+ const ouiaIdProp = getProp(node.attributes, 'ouiaId');
+ if (!ouiaIdProp) {
+ context.report({
+ node,
+ message: `ouiaId property is missing in PatternFly component '${node.name.name}'`,
+ });
+ }
+ }
+ }
+ return {
+ ImportDeclaration: addPatternflyImport,
+ JSXOpeningElement: checkPatternflyComponent,
+ };
+ },
+};
diff --git a/script/lint/link-eslint-plugin.js b/script/lint/link-eslint-plugin.js
new file mode 100644
index 00000000000..c196235f9ad
--- /dev/null
+++ b/script/lint/link-eslint-plugin.js
@@ -0,0 +1,33 @@
+/* eslint-disable no-console */
+const fs = require('fs');
+const path = require('path');
+
+function linkEslintPlugin(runPath = process.cwd()) {
+ // instead of creating an npm package for the custom eslint plugin, we symlink it
+ // eslint will only search for plugins in node_modules, so we need to symlink it there
+ const sourceDir = path.join(__dirname, '@foreman');
+ const destinationDir = path.join(runPath, 'node_modules', '@foreman');
+ function createSymlink() {
+ fs.symlink(sourceDir, destinationDir, 'dir', err => {
+ if (err) {
+ console.error('Error creating symlink:', err);
+ }
+ });
+ }
+
+ // Check if the symlink exists and remove it if it does
+ fs.lstat(destinationDir, (err, stats) => {
+ if (!err && stats.isSymbolicLink()) {
+ fs.unlink(destinationDir, unlinkErr => {
+ if (unlinkErr) {
+ console.error('Error removing existing symlink:', unlinkErr);
+ return;
+ }
+ createSymlink();
+ });
+ } else {
+ createSymlink();
+ }
+ });
+}
+linkEslintPlugin();
diff --git a/script/lint/lint_core_config.js b/script/lint/lint_core_config.js
new file mode 100644
index 00000000000..c1b3d84e16b
--- /dev/null
+++ b/script/lint/lint_core_config.js
@@ -0,0 +1,218 @@
+module.exports = {
+ plugins: ['spellcheck', '@foreman/custom'],
+ rules: {
+ 'spellcheck/spell-checker': [
+ 'warn',
+ {
+ comments: false,
+ identifiers: false,
+ strings: true,
+ lang: 'en_US',
+ ignoreRequire: false,
+ skipWords: [
+ '2xl',
+ '45vh',
+ '4xl',
+ 'ampm',
+ 'Ansible',
+ 'ascii',
+ 'auditable',
+ 'Autocompletion',
+ 'autogenerated',
+ 'axios',
+ 'bool',
+ 'bootable',
+ 'Borderless',
+ 'btns',
+ 'candlepin',
+ 'centos',
+ 'checkbox',
+ 'clearbutton',
+ 'clearfix',
+ 'comms',
+ 'Composable',
+ 'consts',
+ 'cpu',
+ 'csrf',
+ 'csv',
+ 'datacenter',
+ 'datastore',
+ 'datastores',
+ 'datepicker',
+ 'datetime',
+ 'datetimepicker',
+ 'debian',
+ 'decrement', // should be removed once https://github.com/aotaduy/eslint-plugin-spellcheck/issues/67 is resolved.
+ 'devs',
+ 'dgettext',
+ 'dngettext',
+ 'donut',
+ 'dow',
+ 'dropdown',
+ 'dropdowns',
+ 'ec2',
+ 'erb',
+ 'fieldset',
+ 'fnc',
+ 'formatter',
+ 'fqdn',
+ 'func',
+ 'gettext',
+ 'glyphicon',
+ 'gpg',
+ 'graphql',
+ 'gridster',
+ 'hostdetails',
+ 'hostgroup',
+ 'hostgroups',
+ 'href',
+ 'hypervisor',
+ 'i386',
+ 'internets',
+ 'ip6',
+ 'IPv4',
+ 'IPv6',
+ 'javascript',
+ 'javascripts',
+ 'jed',
+ 'katello',
+ 'keybind',
+ 'keydown',
+ 'keypress',
+ 'klasses',
+ 'labelledby',
+ 'lang',
+ 'ldap',
+ 'loc',
+ 'locs',
+ 'lsi',
+ 'matcher',
+ 'menuitem',
+ 'monokai',
+ 'mousedown',
+ 'mouseup',
+ 'msg',
+ 'nailgun',
+ 'nat',
+ 'navitem',
+ 'netgroups',
+ 'networksurl',
+ 'ngettext',
+ 'nic',
+ 'nfs',
+ 'nonpersistent',
+ 'noopener',
+ 'noreferrer',
+ 'nowrap',
+ 'npx',
+ 'num',
+ 'numpad',
+ 'operatingsystem',
+ 'operatingsystems',
+ 'orderable',
+ 'orgs',
+ 'ouia',
+ 'ouiaid',
+ 'ownfield',
+ 'paravirtual',
+ 'patternfly',
+ 'pficon',
+ 'poolsurl',
+ 'popstate',
+ 'posinset',
+ 'pqr',
+ 'ptable',
+ 'puppetclass',
+ 'puppetclasses',
+ 'pxe_loader',
+ 'Pv',
+ 'qcow2',
+ 'rbt',
+ 'readonly',
+ 'redhat',
+ 'redux',
+ 'refetches',
+ 'rendering',
+ 'repo',
+ 'resize',
+ 'rex',
+ 'rhel',
+ 'safemode',
+ 'sbs',
+ 'scrollable',
+ 'scsi',
+ 'securityfailure',
+ 'setsize',
+ 'sizex',
+ 'sizey',
+ 'Solaris',
+ 'sparc',
+ 'storages',
+ 'stringified',
+ 'subcomponent',
+ 'subcomponents',
+ 'subnav',
+ 'subnet',
+ 'subnets',
+ 'substate',
+ 'svg',
+ 'symlink',
+ 'Symlink',
+ 'testoption',
+ 'testoption',
+ 'textarea',
+ 'textfield',
+ 'tfm',
+ 'theforeman',
+ 'timepicker',
+ 'timerdelay',
+ 'timeseries',
+ 'tlv',
+ 'tooltip',
+ 'turbolinks',
+ 'twipsy',
+ 'txt',
+ 'typeahead',
+ 'ubuntu',
+ 'uncheck',
+ 'unencrypted',
+ 'unmount',
+ 'unordered',
+ 'unprocessable',
+ 'unselect',
+ 'unstyled',
+ 'utf8',
+ 'virtualization',
+ 'vms',
+ 'vmware',
+ 'vnc',
+ 'vnic',
+ 'webpack',
+ 'wget',
+ 'wss',
+ 'x86_64',
+ 'xml',
+ 'xpi',
+ 'xyz',
+ 'yaml',
+ ],
+ minLength: 3,
+ },
+ ],
+
+ 'import/no-extraneous-dependencies': 'off',
+ 'import/no-unresolved': [
+ 'error',
+ {
+ ignore: ['foremanReact/.*'],
+ },
+ ],
+ 'import/extensions': [
+ 'error',
+ {
+ ignore: ['foremanReact/.*'],
+ },
+ ],
+ '@foreman/custom/require-ouiaid': 'error',
+ },
+};
diff --git a/script/lint/lint_generic_config.js b/script/lint/lint_generic_config.js
new file mode 100644
index 00000000000..f0bd8c4eaa4
--- /dev/null
+++ b/script/lint/lint_generic_config.js
@@ -0,0 +1,66 @@
+module.exports = {
+ plugins: [
+ 'patternfly-react',
+ 'promise',
+ 'jquery',
+ 'react-hooks',
+ // '@foreman/custom',
+ ],
+ extends: [
+ 'plugin:patternfly-react/recommended',
+ require.resolve('@theforeman/vendor-dev/eslint.extends.js'),
+ 'plugin:jquery/deprecated',
+ ],
+ rules: {
+ 'react-hooks/rules-of-hooks': 'error',
+ 'react-hooks/exhaustive-deps': 'warn',
+ 'max-lines': [
+ 'error',
+ {
+ max: 300,
+ skipBlankLines: true,
+ skipComments: true,
+ },
+ ],
+ 'no-restricted-syntax': [
+ 'error',
+ {
+ selector: 'ForInStatement',
+ message:
+ 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.',
+ },
+ {
+ selector: 'LabeledStatement',
+ message:
+ 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
+ },
+ {
+ selector: 'WithStatement',
+ message:
+ '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
+ },
+ ],
+ 'promise/prefer-await-to-then': 'error',
+ 'prettier/prettier': [
+ 'error',
+ {
+ singleQuote: true,
+ trailingComma: 'es5',
+ },
+ ],
+ 'import/no-unresolved': [
+ 'error',
+ {
+ ignore: ['foremanReact/.*'],
+ },
+ ],
+ 'import/extensions': [
+ 'error',
+ {
+ ignore: ['foremanReact/.*'],
+ },
+ ],
+ 'import/no-extraneous-dependencies': 'off',
+ // '@foreman/custom/require-ouiaid': 'error', TODO: uncomment when the plugins are ready
+ },
+};
diff --git a/script/npm_lint_plugins.js b/script/npm_lint_plugins.js
index d98421123db..b181d42e81b 100755
--- a/script/npm_lint_plugins.js
+++ b/script/npm_lint_plugins.js
@@ -1,20 +1,63 @@
#!/usr/bin/env node
/* eslint-disable no-var */
+/* eslint-disable no-console */
var fs = require('fs');
-var childProcess = require('child_process');
-var { packageJsonDirs } = require('./plugin_webpack_directories');
+const { spawn, execSync } = require('child_process');
+const path = require('path');
+
+var { packageJsonDirsObject } = require('./plugin_webpack_directories');
+
+var dirs = packageJsonDirsObject();
+var dirsKeys = Object.keys(dirs);
+var passedArgs = process.argv.slice(2);
function pluginDefinesLint(pluginPath) {
var packageData = JSON.parse(fs.readFileSync(`${pluginPath}/package.json`));
return packageData.scripts && packageData.scripts.lint;
}
+if (passedArgs[0] && passedArgs[0][0] !== '-') {
+ // if the argument is --debug and not a plugin name npm test:plugins katello --debug
+ dirsKeys = dirsKeys.filter(dir => dir.endsWith(passedArgs[0]));
+ passedArgs.shift();
+}
+try {
+ const scriptPath = path.join(__dirname, 'lint', 'link-eslint-plugin.js');
+ execSync(`node ${scriptPath}`, { stdio: 'inherit' });
+} catch (error) {
+ console.error(`Error: ${error.message}`);
+}
+dirsKeys.forEach(dirsKey => {
+ const pluginPath = dirs[dirsKey];
+ const pluginLintScript = pluginDefinesLint(pluginPath);
+ if (pluginLintScript?.includes('tfm-lint')) {
+ const eslintConfigPath = path.join(
+ __dirname,
+ 'lint',
+ '/lint_generic_config.js'
+ );
+ const eslint = spawn(
+ 'npx',
+ ['eslint', path.join(pluginPath, 'webpack'), '-c', eslintConfigPath],
+ {
+ cwd: path.join(__dirname, '..'),
+ stdio: 'inherit',
+ }
+ );
+ eslint.on('error', error => {
+ console.error(`Error: ${error.message}`);
+ });
-packageJsonDirs().forEach(pluginPath => {
- if (pluginDefinesLint(pluginPath)) {
- childProcess.spawn('npm', ['run', 'lint'], {
+ eslint.on('close', code => {
+ if (code !== 0) {
+ console.error(`ESLint process exited with code ${code}`);
+ }
+ });
+ } else if (pluginLintScript.length) {
+ // Dont run foreman config lint for plugins with custom lint
+ spawn('npm', ['run', 'lint'], {
env: process.env,
cwd: pluginPath,
stdio: 'inherit',
diff --git a/script/npm_test_plugin.js b/script/npm_test_plugin.js
new file mode 100755
index 00000000000..cf3f59a7a3a
--- /dev/null
+++ b/script/npm_test_plugin.js
@@ -0,0 +1,140 @@
+#!/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 { packageJsonDirsObject } = 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 = packageJsonDirsObject();
+ var dirsKeys = Object.keys(dirs);
+ 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] !== '-') {
+ // if the argument is --debug and not a plugin name npm test:plugins katello --debug
+ dirsKeys = dirsKeys.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 dirsKey of dirsKeys) {
+ const pluginPath = dirs[dirsKey];
+ 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',
+ '--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();
diff --git a/script/plugin_webpack_directories.js b/script/plugin_webpack_directories.js
index 08d006e9c10..32bbed9a7aa 100644
--- a/script/plugin_webpack_directories.js
+++ b/script/plugin_webpack_directories.js
@@ -15,6 +15,19 @@ var sanitizeWebpackDirs = pluginDirs => {
return splitDirs.length > 2 ? splitDirs[1] : pluginDirs;
};
+var pluginPathObject = file => pluginsObj => {
+ var paths = {};
+ Object.keys(pluginsObj.plugins).forEach(entryKey => {
+ if (!entryKey.includes(':')) {
+ const pluginPath = pluginsObj.plugins[entryKey].root;
+ if (fs.existsSync(path.join(pluginPath, file))) {
+ paths[entryKey] = pluginPath;
+ }
+ }
+ });
+ return paths;
+};
+
// Get paths that have a specific file or folder
var pluginPath = file => pluginsObj => {
var paths = [];
@@ -53,11 +66,16 @@ var getPluginDirs = stderr =>
var packageJsonDirs = stderr =>
pluginPath('package.json')(getPluginDirs(stderr)).map(path.dirname);
+// Get plugin paths with the plugin name, dont assume the plugin path will have the plugin name
+var packageJsonDirsObject = stderr =>
+ pluginPathObject('package.json')(getPluginDirs(stderr));
+
module.exports = {
getPluginDirs,
pluginNodeModules: pluginPath('node_modules'),
aliasPlugins,
packageJsonDirs,
+ packageJsonDirsObject,
sanitizeWebpackDirs,
pluginPath,
};
diff --git a/webpack/assets/javascripts/bundle.js b/webpack/assets/javascripts/bundle.js
index 397994b337c..045cc5a8969 100644
--- a/webpack/assets/javascripts/bundle.js
+++ b/webpack/assets/javascripts/bundle.js
@@ -1,5 +1,7 @@
import 'core-js/shim';
import 'regenerator-runtime/runtime';
+import jstz from 'jstz';
+import ipaddr from 'ipaddr.js';
import compute from './foreman_compute_resource';
import componentRegistry from './react_app/components/componentRegistry';
@@ -56,4 +58,6 @@ window.tfm = Object.assign(window.tfm || {}, {
componentRegistry,
store,
lookupKeys,
+ jstz,
+ ipaddr,
});
diff --git a/webpack/assets/javascripts/exportAll.js b/webpack/assets/javascripts/exportAll.js
index d56a6497401..8062a11bbf6 100644
--- a/webpack/assets/javascripts/exportAll.js
+++ b/webpack/assets/javascripts/exportAll.js
@@ -19,9 +19,11 @@ function generateExports(directoryPath, exportFileContent = '') {
!dirent.name.endsWith('.test.js') &&
!dirent.name.endsWith('.fixtures.js') &&
!dirent.name.endsWith('mockRequests.js') &&
+ !dirent.name.endsWith('jquery.js') &&
+ !dirent.name.endsWith('jquery.js') &&
!fileNameWithoutExtension.includes('TestHelper') &&
!fileNameWithoutExtension.includes('testHelper') &&
- !fileNameWithoutExtension.includes('APITestSetup')
+ !fileNameWithoutExtension.includes('vendor-core.scss')
) {
let relativeFilePath = path.relative(
__dirname,
@@ -51,7 +53,8 @@ function generateExports(directoryPath, exportFileContent = '') {
const generateExportsFile = () => {
let exportFileContent = generateExports(path.join(__dirname, 'react_app'));
- exportFileContent = `/* eslint-disable */\n// This file is autogenerated by the webpack/assets/javascripts/exportAll.js script\n// Please do not modify this file directly\n\n${exportFileContent}`;
+ exportFileContent = `/* eslint-disable */\n// This file is autogenerated by the webpack/assets/javascripts/exportAll.js script\n// Please do not modify this file directly\n\n
+ console.log('all_react_export');\n\n${exportFileContent}`;
fs.writeFileSync(
path.join(__dirname, 'all_react_app_exports.js'),
diff --git a/webpack/assets/javascripts/jquery.js b/webpack/assets/javascripts/jquery.js
new file mode 100644
index 00000000000..be613eadabc
--- /dev/null
+++ b/webpack/assets/javascripts/jquery.js
@@ -0,0 +1,15 @@
+// to avoid webpack alias loop
+const jquery = require('../../../node_modules/jquery');
+
+window.$ = jquery;
+window.jQuery = jquery;
+window.jquery = jquery;
+
+module.exports = jquery;
+
+require('jquery.cookie');
+require('jquery-ujs');
+require('multiselect');
+require('select2');
+require('datatables.net-bs');
+require('dsmorse-gridster/dist/jquery.dsmorse-gridster');
diff --git a/webpack/assets/javascripts/react_app/common/HOC.test.js b/webpack/assets/javascripts/react_app/common/HOC.test.js
index ab6d9614251..6727eff09f2 100644
--- a/webpack/assets/javascripts/react_app/common/HOC.test.js
+++ b/webpack/assets/javascripts/react_app/common/HOC.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { callOnMount, withRenderHandler, callOnPopState } from './HOC';
diff --git a/webpack/assets/javascripts/react_app/common/IntegrationTestHelper.js b/webpack/assets/javascripts/react_app/common/IntegrationTestHelper.js
index 501f64966cc..6625f928b3f 100644
--- a/webpack/assets/javascripts/react_app/common/IntegrationTestHelper.js
+++ b/webpack/assets/javascripts/react_app/common/IntegrationTestHelper.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import { applyMiddleware, combineReducers, createStore } from 'redux';
import thunk from 'redux-thunk';
diff --git a/webpack/assets/javascripts/react_app/common/helpers.js b/webpack/assets/javascripts/react_app/common/helpers.js
index f9853e6b069..e5ad1a91ca9 100644
--- a/webpack/assets/javascripts/react_app/common/helpers.js
+++ b/webpack/assets/javascripts/react_app/common/helpers.js
@@ -202,6 +202,13 @@ export const formatDateTime = date => {
// generates an absolute, needed in case of running Foreman from a subpath
export const foremanUrl = path => `${window.URL_PREFIX}${path}`;
+export const visit = url => {
+ window.location.href = url;
+};
+
+export const reloadPage = () => {
+ window.location.reload();
+};
export default {
isoCompatibleDate,
bindMethods,
@@ -222,4 +229,6 @@ export default {
formatDateTime,
foremanUrl,
getWikiURL,
+ visit,
+ reloadPage,
};
diff --git a/webpack/assets/javascripts/react_app/common/scss/mixins.scss b/webpack/assets/javascripts/react_app/common/scss/mixins.scss
new file mode 100644
index 00000000000..f106521f856
--- /dev/null
+++ b/webpack/assets/javascripts/react_app/common/scss/mixins.scss
@@ -0,0 +1,21 @@
+// Mixins - Bootstrap overrides
+// -------------------
+@mixin box-shadow($shadow...) {
+ -webkit-box-shadow: $shadow; // iOS <4.3 & Android <4.1
+ box-shadow: $shadow;
+}
+
+// Form control outline
+@mixin form-control-outline($color: $input-border-focus){
+ $color-rgba: rgba(red($color), green($color), blue($color), .6);
+ &:focus {
+ border-color: $color;
+ outline: 0 !important;
+ @include box-shadow(unquote("inset 0 1px 1px rgba(3, 3, 3, 0.075), 0 0 8px #{$color-rgba}"));
+ }
+}
+/**
+ Third Party mixins imports here
+*/
+@import '~patternfly/dist/sass/patternfly/bootstrap-mixin-overrides';
+@import '~patternfly/dist/sass/patternfly/mixins';
diff --git a/webpack/assets/javascripts/react_app/common/scss/vendor-core.scss b/webpack/assets/javascripts/react_app/common/scss/vendor-core.scss
new file mode 100644
index 00000000000..a4c432652c5
--- /dev/null
+++ b/webpack/assets/javascripts/react_app/common/scss/vendor-core.scss
@@ -0,0 +1,22 @@
+/**
+ Third Party libraries imports here
+*/
+@import '../variables';
+@import './mixins';
+
+@import '~multiselect/css/multi-select.css';
+@import '~react-diff-view/style/index.css';
+@import '~select2/select2-bootstrap.css';
+@import '~select2/select2.css';
+@import "~dsmorse-gridster/dist/jquery.gridster";
+@import "~datatables.net-bs/css/dataTables.bootstrap.css";
+// @import "~@redhat-cloud-services/frontend-components/index.css";
+
+// patternfly v3
+@import '~patternfly-react/dist/sass/_patternfly-react.scss';
+@import '~patternfly-react-extensions/dist/sass/_select.scss';
+@import '~patternfly/dist/sass/patternfly/_loading-state';
+
+// patternfly v4
+@import '~@patternfly/patternfly/patternfly';
+@import '~@patternfly/patternfly/patternfly-addons';
diff --git a/webpack/assets/javascripts/react_app/common/testHelpers.js b/webpack/assets/javascripts/react_app/common/testHelpers.js
index ecb0c97268d..5b63970ef16 100644
--- a/webpack/assets/javascripts/react_app/common/testHelpers.js
+++ b/webpack/assets/javascripts/react_app/common/testHelpers.js
@@ -1,7 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
-
-jest.useFakeTimers();
+import { shallow } from 'enzyme';
export default {
mockStorage: () => {
@@ -80,6 +78,7 @@ const resolveDispatch = async (action, depth) => {
if (depth && typeof action === 'function') {
const dispatch = jest.fn();
await action(dispatch);
+ jest.useFakeTimers();
jest.runOnlyPendingTimers();
return Promise.all(
diff --git a/webpack/assets/javascripts/react_app/common/urlHelpers.js b/webpack/assets/javascripts/react_app/common/urlHelpers.js
index 8d15d9dda87..de5e54227f7 100644
--- a/webpack/assets/javascripts/react_app/common/urlHelpers.js
+++ b/webpack/assets/javascripts/react_app/common/urlHelpers.js
@@ -1,6 +1,6 @@
import URI from 'urijs';
-import { visit } from '../../foreman_navigation';
+import { visit } from './helpers';
/**
* Build a url from given controller, action and id
diff --git a/webpack/assets/javascripts/react_app/common/urlHelpers.test.js b/webpack/assets/javascripts/react_app/common/urlHelpers.test.js
index efd5e446164..34ffab09bcb 100644
--- a/webpack/assets/javascripts/react_app/common/urlHelpers.test.js
+++ b/webpack/assets/javascripts/react_app/common/urlHelpers.test.js
@@ -1,5 +1,5 @@
import { mockWindowLocation } from './testHelpers';
-import { visit } from '../../foreman_navigation';
+import { visit } from './helpers';
import {
urlBuilder,
urlWithSearch,
diff --git a/webpack/assets/javascripts/react_app/common/variables.scss b/webpack/assets/javascripts/react_app/common/variables.scss
index f7485b1fc2b..37f34808fe3 100644
--- a/webpack/assets/javascripts/react_app/common/variables.scss
+++ b/webpack/assets/javascripts/react_app/common/variables.scss
@@ -1,3 +1,22 @@
-@import '~@theforeman/vendor/scss/variables.scss';
+/**
+ Third Party variables imports here
+*/
+// patternfly v3
+$font-path: '~patternfly/dist/fonts/';
+$img-path: '~patternfly/dist/img/';
+$icon-font-path: '~patternfly/dist/fonts/';
-$header-max-width: calc(#{$pf-global--breakpoint--lg} + 70px); //TODO move into @theforeman/vendor/scss/variables
+// patternfly v4
+$pf-global--font-path: '~@patternfly/patternfly/assets/fonts';
+$fa-font-path: '~@patternfly/patternfly/assets/fonts/webfonts';
+$pf-global--fonticon-path: '~@patternfly/patternfly/assets/pficon';
+$pf-global--image-path: '~@patternfly/patternfly/assets/images';
+
+// patternfly v3
+@import '~bootstrap-sass/assets/stylesheets/bootstrap/variables';
+@import '~patternfly/dist/sass/patternfly/variables';
+
+// patternfly v4
+@import '~@patternfly/patternfly/base/patternfly-variables';
+
+$header-max-width: calc(#{$pf-global--breakpoint--lg} + 70px);
diff --git a/webpack/assets/javascripts/react_app/components/BreadcrumbBar/__tests__/BreadcrumbBar.test.js b/webpack/assets/javascripts/react_app/components/BreadcrumbBar/__tests__/BreadcrumbBar.test.js
index a86dfd49f0d..ddf0d33a9fb 100644
--- a/webpack/assets/javascripts/react_app/components/BreadcrumbBar/__tests__/BreadcrumbBar.test.js
+++ b/webpack/assets/javascripts/react_app/components/BreadcrumbBar/__tests__/BreadcrumbBar.test.js
@@ -1,6 +1,5 @@
import React from 'react';
import { render, fireEvent, screen, act } from '@testing-library/react';
-import { mount } from '@theforeman/test';
import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers';
diff --git a/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.test.js b/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.test.js
index b81fbcecfb5..c26c8334c76 100644
--- a/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.test.js
+++ b/webpack/assets/javascripts/react_app/components/ChartBox/ChartBox.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import ChartBox from './ChartBox';
import { classFunctionUnitTest } from '../../common/testHelpers';
diff --git a/webpack/assets/javascripts/react_app/components/ConfigReports/ConfigReports.test.js b/webpack/assets/javascripts/react_app/components/ConfigReports/ConfigReports.test.js
index 1a17f7e0cc1..48ac9b38a97 100644
--- a/webpack/assets/javascripts/react_app/components/ConfigReports/ConfigReports.test.js
+++ b/webpack/assets/javascripts/react_app/components/ConfigReports/ConfigReports.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import ConfigReports from './ConfigReports';
diff --git a/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/__tests__/DiffModal.test.js b/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/__tests__/DiffModal.test.js
index 5fcbeb65bd1..56e809898fe 100644
--- a/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/__tests__/DiffModal.test.js
+++ b/webpack/assets/javascripts/react_app/components/ConfigReports/DiffModal/__tests__/DiffModal.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { testComponentSnapshotsWithFixtures } from '../../../../common/testHelpers';
import DiffModal from '../DiffModal';
diff --git a/webpack/assets/javascripts/react_app/components/ConfirmModal/integration.test.js b/webpack/assets/javascripts/react_app/components/ConfirmModal/integration.test.js
index 479bf90bb00..52ed9ecaed7 100644
--- a/webpack/assets/javascripts/react_app/components/ConfirmModal/integration.test.js
+++ b/webpack/assets/javascripts/react_app/components/ConfirmModal/integration.test.js
@@ -1,6 +1,6 @@
import React from 'react';
import { Provider } from 'react-redux';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { Button } from '@patternfly/react-core';
import store from '../../redux';
import ConfirmModal, { openConfirmModal } from './index';
diff --git a/webpack/assets/javascripts/react_app/components/Editor/__tests__/Editor.test.js b/webpack/assets/javascripts/react_app/components/Editor/__tests__/Editor.test.js
index 2e0d6d67e67..30513634beb 100644
--- a/webpack/assets/javascripts/react_app/components/Editor/__tests__/Editor.test.js
+++ b/webpack/assets/javascripts/react_app/components/Editor/__tests__/Editor.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers';
import Editor from '../Editor';
import { editorOptions } from '../Editor.fixtures';
diff --git a/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorNavbar.test.js b/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorNavbar.test.js
index 527243a1f92..04ca650440c 100644
--- a/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorNavbar.test.js
+++ b/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorNavbar.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { testComponentSnapshotsWithFixtures } from '../../../../common/testHelpers';
import EditorNavbar from '../EditorNavbar';
diff --git a/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorOptions.test.js b/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorOptions.test.js
index de6300d14f0..d7b31e68a5a 100644
--- a/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorOptions.test.js
+++ b/webpack/assets/javascripts/react_app/components/Editor/components/__tests__/EditorOptions.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { testComponentSnapshotsWithFixtures } from '../../../../common/testHelpers';
import EditorOptions from '../EditorOptions';
diff --git a/webpack/assets/javascripts/react_app/components/ExternalLogout/__tests__/ExternalLogout.test.js b/webpack/assets/javascripts/react_app/components/ExternalLogout/__tests__/ExternalLogout.test.js
index d63b7c29888..eab80f58f7e 100644
--- a/webpack/assets/javascripts/react_app/components/ExternalLogout/__tests__/ExternalLogout.test.js
+++ b/webpack/assets/javascripts/react_app/components/ExternalLogout/__tests__/ExternalLogout.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import ExternalLogout from '../ExternalLogout';
import { props } from '../ExternalLogout.fixtures';
diff --git a/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/SubmitOrCancel/SubmitOrCancel.test.js b/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/SubmitOrCancel/SubmitOrCancel.test.js
index abaf2ab3882..e32de925747 100644
--- a/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/SubmitOrCancel/SubmitOrCancel.test.js
+++ b/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/SubmitOrCancel/SubmitOrCancel.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import SubmitOrCancel from './SubmitOrCancel';
diff --git a/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalFooter.test.js b/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalFooter.test.js
index 8a41647362d..05c5699d75a 100644
--- a/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalFooter.test.js
+++ b/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalFooter.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import { Button, Modal } from 'patternfly-react';
import ForemanModalFooter from '../ForemanModalFooter';
import * as ModalContext from '../../ForemanModalHooks'; // so enzyme test works
diff --git a/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalHeader.test.js b/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalHeader.test.js
index 1f3ce92b434..3c6f2c83393 100644
--- a/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalHeader.test.js
+++ b/webpack/assets/javascripts/react_app/components/ForemanModal/subcomponents/__tests__/ForemanModalHeader.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import { Modal } from 'patternfly-react';
import ForemanModalHeader from '../ForemanModalHeader';
import * as ModalContext from '../../ForemanModalHooks'; // so enzyme test works
diff --git a/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/actions.js b/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/actions.js
index 5c161a7e11e..85a374b3454 100644
--- a/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/actions.js
+++ b/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/actions.js
@@ -1,7 +1,6 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
-import { visit } from '../../../../foreman_navigation';
-import { foremanUrl } from '../../../common/helpers';
+import { visit, foremanUrl } from '../../../common/helpers';
import { sprintf, translate as __ } from '../../../common/I18n';
import { openConfirmModal } from '../../ConfirmModal';
import { APIActions } from '../../../redux/API';
diff --git a/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/index.js b/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/index.js
index 4570a2f249d..f5f307a505f 100644
--- a/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/index.js
+++ b/webpack/assets/javascripts/react_app/components/HostDetails/ActionsBar/index.js
@@ -18,10 +18,9 @@ import {
BuildIcon,
TerminalIcon,
} from '@patternfly/react-icons';
-import { visit } from '../../../../foreman_navigation';
import { translate as __ } from '../../../common/I18n';
import { selectKebabItems } from './Selectors';
-import { foremanUrl } from '../../../common/helpers';
+import { visit, foremanUrl } from '../../../common/helpers';
import { cancelBuild, deleteHost, isHostTurnOn } from './actions';
import { useForemanSettings } from '../../../Root/Context/ForemanContext';
import BuildModal from './BuildModal';
diff --git a/webpack/assets/javascripts/react_app/components/HostStatuses/index.js b/webpack/assets/javascripts/react_app/components/HostStatuses/index.js
index 3849896ca11..afd5dd7886d 100644
--- a/webpack/assets/javascripts/react_app/components/HostStatuses/index.js
+++ b/webpack/assets/javascripts/react_app/components/HostStatuses/index.js
@@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import { useSelector } from 'react-redux';
import { PageSection, Grid, GridItem, Title } from '@patternfly/react-core';
-import { foremanUrl } from '../../../foreman_tools';
+import { foremanUrl } from '../../common/helpers';
import { useAPI } from '../../common/hooks/API/APIHooks';
import Status from './Status';
import StatusSkeleton from './Status/StatusSkeleton';
diff --git a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js
index e6d04bff2a8..be546dc3372 100644
--- a/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js
+++ b/webpack/assets/javascripts/react_app/components/HostsIndex/BulkActions/bulkDelete.js
@@ -1,7 +1,6 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
-import { visit } from '../../../../foreman_navigation';
-import { foremanUrl } from '../../../common/helpers';
+import { visit, foremanUrl } from '../../../common/helpers';
import { sprintf, translate as __ } from '../../../common/I18n';
import { openConfirmModal } from '../../ConfirmModal';
import { APIActions } from '../../../redux/API';
diff --git a/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.scss b/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.scss
index fee44434dbc..7b6b05a1fb1 100644
--- a/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.scss
+++ b/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.scss
@@ -1,4 +1,4 @@
-@import "~@theforeman/vendor/scss/variables";
+@import "../../../../common/variables";
@keyframes blink {
0% {
diff --git a/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.test.js b/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.test.js
index d5132cdac71..2f79b894c50 100644
--- a/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.test.js
+++ b/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIcon.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import ImpersonateIcon from './ImpersonateIcon';
diff --git a/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIconActions.js b/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIconActions.js
index 0a8e01050fe..cca7c5d4c2f 100644
--- a/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIconActions.js
+++ b/webpack/assets/javascripts/react_app/components/Layout/components/ImpersonateIcon/ImpersonateIconActions.js
@@ -1,5 +1,5 @@
import { API } from '../../../../redux/API';
-import { foremanUrl } from '../../../../../foreman_tools';
+import { foremanUrl } from '../../../../common/helpers';
import { addToast } from '../../../ToastsList';
diff --git a/webpack/assets/javascripts/react_app/components/Layout/components/TaxonomySwitcher/TaxonomyDropdown.scss b/webpack/assets/javascripts/react_app/components/Layout/components/TaxonomySwitcher/TaxonomyDropdown.scss
index 5f7e814d201..2f8ea071f87 100644
--- a/webpack/assets/javascripts/react_app/components/Layout/components/TaxonomySwitcher/TaxonomyDropdown.scss
+++ b/webpack/assets/javascripts/react_app/components/Layout/components/TaxonomySwitcher/TaxonomyDropdown.scss
@@ -1,4 +1,4 @@
-@import '~@theforeman/vendor/scss/variables';
+@import '../../../../common/variables.scss';
.pf-c-masthead .pf-c-toolbar {
.pf-c-context-selector__menu-search {
diff --git a/webpack/assets/javascripts/react_app/components/Layout/components/Toolbar/HeaderToolbar.test.js b/webpack/assets/javascripts/react_app/components/Layout/components/Toolbar/HeaderToolbar.test.js
index 6c029c077d8..3896865c879 100644
--- a/webpack/assets/javascripts/react_app/components/Layout/components/Toolbar/HeaderToolbar.test.js
+++ b/webpack/assets/javascripts/react_app/components/Layout/components/Toolbar/HeaderToolbar.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import { hasTaxonomiesMock } from '../../Layout.fixtures';
import { noop } from '../../../../common/helpers';
diff --git a/webpack/assets/javascripts/react_app/components/LoginPage/LoginPage.scss b/webpack/assets/javascripts/react_app/components/LoginPage/LoginPage.scss
index c0567e34b7c..5827565ddaa 100644
--- a/webpack/assets/javascripts/react_app/components/LoginPage/LoginPage.scss
+++ b/webpack/assets/javascripts/react_app/components/LoginPage/LoginPage.scss
@@ -1,4 +1,4 @@
-@import '~@theforeman/vendor/scss/variables';
+@import '../../common/variables.scss';
$caption_font_weight: 600;
$background_image: url('../LoginPage/background.svg');
diff --git a/webpack/assets/javascripts/react_app/components/MemoryAllocationInput/__tests__/MemoryAllocationInput.test.js b/webpack/assets/javascripts/react_app/components/MemoryAllocationInput/__tests__/MemoryAllocationInput.test.js
index 556e25a393b..9ad986357ef 100644
--- a/webpack/assets/javascripts/react_app/components/MemoryAllocationInput/__tests__/MemoryAllocationInput.test.js
+++ b/webpack/assets/javascripts/react_app/components/MemoryAllocationInput/__tests__/MemoryAllocationInput.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import { MEGABYTES } from '../constants';
import MemoryAllocationInput from '../';
diff --git a/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/__tests__/Bookmarks.test.js b/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/__tests__/Bookmarks.test.js
index 0f3f43cf44a..e5cacded619 100644
--- a/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/__tests__/Bookmarks.test.js
+++ b/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/__tests__/Bookmarks.test.js
@@ -1,4 +1,3 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
import React from 'react';
import { Provider } from 'react-redux';
import store from '../../../../redux';
diff --git a/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/bookmarks.scss b/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/bookmarks.scss
index b41b45aad74..5fdad1912c7 100644
--- a/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/bookmarks.scss
+++ b/webpack/assets/javascripts/react_app/components/PF4/Bookmarks/bookmarks.scss
@@ -1,4 +1,4 @@
-@import '~@theforeman/vendor/scss/variables';
+@import '../../../common/variables.scss';
.bookmarks-dropdown-item {
word-break: break-word;
diff --git a/webpack/assets/javascripts/react_app/components/PF4/DocumentationLink/DocumentationLink.test.js b/webpack/assets/javascripts/react_app/components/PF4/DocumentationLink/DocumentationLink.test.js
index 7cee3c80b15..905392e36bf 100644
--- a/webpack/assets/javascripts/react_app/components/PF4/DocumentationLink/DocumentationLink.test.js
+++ b/webpack/assets/javascripts/react_app/components/PF4/DocumentationLink/DocumentationLink.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import Link from './index';
diff --git a/webpack/assets/javascripts/react_app/components/PasswordStrength/PasswordStrength.scss b/webpack/assets/javascripts/react_app/components/PasswordStrength/PasswordStrength.scss
index db05e547a3c..a133d6176cf 100644
--- a/webpack/assets/javascripts/react_app/components/PasswordStrength/PasswordStrength.scss
+++ b/webpack/assets/javascripts/react_app/components/PasswordStrength/PasswordStrength.scss
@@ -1,5 +1,5 @@
-@import "~@theforeman/vendor/scss/variables";
@import '../../common/colors.scss';
+@import '../../common/variables.scss';
.ReactPasswordStrength {
.ReactPasswordStrength-input {
diff --git a/webpack/assets/javascripts/react_app/components/PasswordStrength/__tests__/PasswordStrength.test.js b/webpack/assets/javascripts/react_app/components/PasswordStrength/__tests__/PasswordStrength.test.js
index 71d49e69a78..718a094bcaf 100644
--- a/webpack/assets/javascripts/react_app/components/PasswordStrength/__tests__/PasswordStrength.test.js
+++ b/webpack/assets/javascripts/react_app/components/PasswordStrength/__tests__/PasswordStrength.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { testComponentSnapshotsWithFixtures } from '../../../common/testHelpers';
diff --git a/webpack/assets/javascripts/react_app/components/SearchBar/SearchBar.scss b/webpack/assets/javascripts/react_app/components/SearchBar/SearchBar.scss
index 1d3c880ace0..9a82d5fa94b 100644
--- a/webpack/assets/javascripts/react_app/components/SearchBar/SearchBar.scss
+++ b/webpack/assets/javascripts/react_app/components/SearchBar/SearchBar.scss
@@ -1,4 +1,4 @@
-@import '~@theforeman/vendor/scss/variables';
+@import '../../common//variables.scss';
.autocomplete-search {
width: 100%;
diff --git a/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsReducer.test.js b/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsReducer.test.js
index 90c20d94482..ae91beb47aa 100644
--- a/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsReducer.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsReducer.test.js
@@ -1,4 +1,4 @@
-import { testReducerSnapshotWithFixtures } from '@theforeman/test';
+import { testReducerSnapshotWithFixtures } from 'foremanReact/common/testHelpers';
import { default as reducer, initialState } from '../SettingRecordsReducer';
diff --git a/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsSelectors.test.js b/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsSelectors.test.js
index 706b83f1a14..9f32648a377 100644
--- a/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsSelectors.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingRecords/__tests__/SettingRecordsSelectors.test.js
@@ -1,4 +1,4 @@
-import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
+import { testSelectorsSnapshotWithFixtures } from 'foremanReact/common/testHelpers';
import {
selectSettings,
diff --git a/webpack/assets/javascripts/react_app/components/SettingUpdateModal/SettingUpdateModal.test.js b/webpack/assets/javascripts/react_app/components/SettingUpdateModal/SettingUpdateModal.test.js
index 605749ac0e6..d359f0f10db 100644
--- a/webpack/assets/javascripts/react_app/components/SettingUpdateModal/SettingUpdateModal.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingUpdateModal/SettingUpdateModal.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import { arraySetting } from '../SettingRecords/__tests__/SettingRecords.fixtures';
diff --git a/webpack/assets/javascripts/react_app/components/SettingUpdateModal/components/SettingValueField.test.js b/webpack/assets/javascripts/react_app/components/SettingUpdateModal/components/SettingValueField.test.js
index c76bf679b65..978be53736d 100644
--- a/webpack/assets/javascripts/react_app/components/SettingUpdateModal/components/SettingValueField.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingUpdateModal/components/SettingValueField.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import SettingValueField from './SettingValueField';
diff --git a/webpack/assets/javascripts/react_app/components/SettingsTable/__tests__/SettingsTable.test.js b/webpack/assets/javascripts/react_app/components/SettingsTable/__tests__/SettingsTable.test.js
index 68799862484..9cf4dea3c16 100644
--- a/webpack/assets/javascripts/react_app/components/SettingsTable/__tests__/SettingsTable.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingsTable/__tests__/SettingsTable.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import { groupedSettings } from '../../SettingRecords/__tests__/SettingRecords.fixtures';
diff --git a/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingCell.test.js b/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingCell.test.js
index e6cd0b73c83..d766a4b7ee3 100644
--- a/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingCell.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingCell.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import {
rootPass,
diff --git a/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingName.test.js b/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingName.test.js
index 19c16153c59..fca1311af45 100644
--- a/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingName.test.js
+++ b/webpack/assets/javascripts/react_app/components/SettingsTable/components/__tests__/SettingName.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import {
rootPass,
diff --git a/webpack/assets/javascripts/react_app/components/TemplateGenerator/__tests__/TemplateGeneratorActions.test.js b/webpack/assets/javascripts/react_app/components/TemplateGenerator/__tests__/TemplateGeneratorActions.test.js
index a3554f42fdb..05e8fba8f40 100644
--- a/webpack/assets/javascripts/react_app/components/TemplateGenerator/__tests__/TemplateGeneratorActions.test.js
+++ b/webpack/assets/javascripts/react_app/components/TemplateGenerator/__tests__/TemplateGeneratorActions.test.js
@@ -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', () => {
@@ -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);
diff --git a/webpack/assets/javascripts/react_app/components/ToastsList/__tests__/integration.test.js b/webpack/assets/javascripts/react_app/components/ToastsList/__tests__/integration.test.js
index 92c109172ed..d3a7448a5f0 100644
--- a/webpack/assets/javascripts/react_app/components/ToastsList/__tests__/integration.test.js
+++ b/webpack/assets/javascripts/react_app/components/ToastsList/__tests__/integration.test.js
@@ -1,6 +1,6 @@
import React from 'react';
import { Provider } from 'react-redux';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import store from '../../../redux';
import ToastsList, { addToast, deleteToast } from '../index'
diff --git a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js
index 45c04779b24..3c97cdd4237 100644
--- a/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/ActionButtons/ActionButtons.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import { ActionButtons } from './ActionButtons';
import { buttons } from './ActionButtons.fixtures';
diff --git a/webpack/assets/javascripts/react_app/components/common/Alert/AlertBody.test.js b/webpack/assets/javascripts/react_app/components/common/Alert/AlertBody.test.js
index 27031e8690e..e7dce609455 100644
--- a/webpack/assets/javascripts/react_app/components/common/Alert/AlertBody.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/Alert/AlertBody.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import AlertBody from './AlertBody';
diff --git a/webpack/assets/javascripts/react_app/components/common/Alert/AlertLink.test.js b/webpack/assets/javascripts/react_app/components/common/Alert/AlertLink.test.js
index faf02a01cde..c2a759c04cd 100644
--- a/webpack/assets/javascripts/react_app/components/common/Alert/AlertLink.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/Alert/AlertLink.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import AlertLink from './AlertLink';
diff --git a/webpack/assets/javascripts/react_app/components/common/ComponentWrapper/ComponentWrapper.test.js b/webpack/assets/javascripts/react_app/components/common/ComponentWrapper/ComponentWrapper.test.js
index eb2c9f6d045..858281cda00 100644
--- a/webpack/assets/javascripts/react_app/components/common/ComponentWrapper/ComponentWrapper.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/ComponentWrapper/ComponentWrapper.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import componentRegistry from '../../componentRegistry';
import ComponentWrapper from './ComponentWrapper';
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateInput.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateInput.test.js
index 98c4b6ab193..2a4a564196e 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateInput.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DateInput.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow, mount } from '@theforeman/test';
+import { shallow, mount } from 'enzyme';
import DateInput from './DateInput';
test('DateInput is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Day.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Day.test.js
index 962e50a3c7e..ccee6ef4d7d 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Day.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Day.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import Day from './Day';
test('Day is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeView.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeView.test.js
index 051d8a830f4..a45612aec9e 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeView.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeView.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow, mount } from '@theforeman/test';
+import { shallow, mount } from 'enzyme';
import DecadeView from './DecadeView';
test('DecadeView is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewHeader.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewHeader.test.js
index d193691add4..0fba60bfb15 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewHeader.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewHeader.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import { DecadeViewHeader } from './DecadeViewHeader';
test('DecadeViewHeader is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewTable.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewTable.test.js
index 20a72bae885..74dbd93fa9b 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewTable.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/DecadeViewTable.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import { DecadeViewTable } from './DecadeViewTable';
test('DecadeViewTable is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Header.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Header.test.js
index cc50df728a5..f88d12f8786 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Header.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/Header.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import Header from './Header';
test('Header is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/MonthView.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/MonthView.test.js
index ca5bf935ed3..23703b6dddb 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/MonthView.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/MonthView.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import MonthView from './MonthView';
test('MonthView is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/TodayButton.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/TodayButton.test.js
index c8a2a7aa963..0d7a4358a93 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/TodayButton.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/TodayButton.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import TodayButton from './TodayButton';
const mockedDate = new Date('2/21/2019 , 3:22:31 PM');
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/YearView.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/YearView.test.js
index 5cd7c691551..92382a3f846 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/YearView.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateComponents/YearView.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import YearView from './YearView';
test('YearView is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DatePicker.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DatePicker.test.js
index 3c8964fcbc7..971498ad680 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DatePicker.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DatePicker.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import DatePicker from './DatePicker';
describe('DatePicker', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateTimePicker.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateTimePicker.test.js
index 5b8e713b3e4..b9a0435edd1 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateTimePicker.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/DateTimePicker.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import DateTimePicker from './DateTimePicker';
describe('DateTimePicker', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeClock.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeClock.test.js
index 766a2f3e501..b98ac585b97 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeClock.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeClock.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow, mount } from '@theforeman/test';
+import { shallow, mount } from 'enzyme';
import PickTimeClock from './PickTimeClock';
import { MINUTE, HOUR } from './TimeConstants';
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeTable.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeTable.test.js
index cc0f79e9fda..9cbd183db0c 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeTable.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/PickTimeTable.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import PickTimeTable from './PickTimeTable';
import { MINUTE, HOUR } from './TimeConstants';
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/TimeInput.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/TimeInput.test.js
index ff463a6bc5b..221fe4803e3 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/TimeInput.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimeComponents/TimeInput.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow, mount } from '@theforeman/test';
+import { shallow, mount } from 'enzyme';
import TimeInput from './TimeInput';
test('TimeInput is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimePicker.test.js b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimePicker.test.js
index e4511a84ebb..11c1d4aec25 100644
--- a/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimePicker.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DateTimePicker/TimePicker.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import TimePicker from './TimePicker';
test('TimePicker is working properly', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/DocumentationLink/DocumentationLink.test.js b/webpack/assets/javascripts/react_app/components/common/DocumentationLink/DocumentationLink.test.js
index b47e90902bf..aff30ed02ec 100644
--- a/webpack/assets/javascripts/react_app/components/common/DocumentationLink/DocumentationLink.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/DocumentationLink/DocumentationLink.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import Link from './index';
diff --git a/webpack/assets/javascripts/react_app/components/common/Fill/__tests__/FillReducer.test.js b/webpack/assets/javascripts/react_app/components/common/Fill/__tests__/FillReducer.test.js
index 4f19db384f1..3eda8463764 100644
--- a/webpack/assets/javascripts/react_app/components/common/Fill/__tests__/FillReducer.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/Fill/__tests__/FillReducer.test.js
@@ -1,4 +1,4 @@
-import { testReducerSnapshotWithFixtures } from '@theforeman/test';
+import { testReducerSnapshotWithFixtures } from 'foremanReact/common/testHelpers';
import Immutable from 'seamless-immutable';
import reducer from '../FillReducer';
import { REMOVE_FILLED_COMPONENT } from '../FillConstants';
diff --git a/webpack/assets/javascripts/react_app/components/common/Loader/Loader.test.js b/webpack/assets/javascripts/react_app/components/common/Loader/Loader.test.js
index 67981863262..370f1d7966c 100644
--- a/webpack/assets/javascripts/react_app/components/common/Loader/Loader.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/Loader/Loader.test.js
@@ -1,4 +1,4 @@
-import { shallow, mount } from '@theforeman/test';
+import { shallow, mount } from 'enzyme';
import React from 'react';
import { STATUS } from '../../../constants';
diff --git a/webpack/assets/javascripts/react_app/components/common/MessageBox/MessageBox.test.js b/webpack/assets/javascripts/react_app/components/common/MessageBox/MessageBox.test.js
index a695222ba9f..3887b509b9a 100644
--- a/webpack/assets/javascripts/react_app/components/common/MessageBox/MessageBox.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/MessageBox/MessageBox.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import MessageBox from './index';
diff --git a/webpack/assets/javascripts/react_app/components/common/ModalProgressBar/__tests__/ModalProgressBar.test.js b/webpack/assets/javascripts/react_app/components/common/ModalProgressBar/__tests__/ModalProgressBar.test.js
index 6573b1ecd70..acdd062e93c 100644
--- a/webpack/assets/javascripts/react_app/components/common/ModalProgressBar/__tests__/ModalProgressBar.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/ModalProgressBar/__tests__/ModalProgressBar.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import ModalProgressBar from '../ModalProgressBar';
describe('ModalProgressBar', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/RedirectCancelButton/RedirectCancelButton.test.js b/webpack/assets/javascripts/react_app/components/common/RedirectCancelButton/RedirectCancelButton.test.js
index 211a92a5fbc..f6a523afb8d 100644
--- a/webpack/assets/javascripts/react_app/components/common/RedirectCancelButton/RedirectCancelButton.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/RedirectCancelButton/RedirectCancelButton.test.js
@@ -1,6 +1,6 @@
import React from 'react';
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import RedirectCancelButton from './RedirectCancelButton';
diff --git a/webpack/assets/javascripts/react_app/components/common/SearchInput/SearchInput.test.js b/webpack/assets/javascripts/react_app/components/common/SearchInput/SearchInput.test.js
index 74a4fa0a154..d2788a082b3 100644
--- a/webpack/assets/javascripts/react_app/components/common/SearchInput/SearchInput.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/SearchInput/SearchInput.test.js
@@ -1,4 +1,4 @@
-import { shallow, mount } from '@theforeman/test';
+import { shallow, mount } from 'enzyme';
import React from 'react';
import SearchInput from './';
diff --git a/webpack/assets/javascripts/react_app/components/common/SkeletonLoader/SkeletonLoader.test.js b/webpack/assets/javascripts/react_app/components/common/SkeletonLoader/SkeletonLoader.test.js
index 18957adc4ba..0b37bf7713b 100644
--- a/webpack/assets/javascripts/react_app/components/common/SkeletonLoader/SkeletonLoader.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/SkeletonLoader/SkeletonLoader.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import SkeletonLoader from '.';
import { STATUS } from '../../../constants';
const fixtures = {
diff --git a/webpack/assets/javascripts/react_app/components/common/charts/BarChart/BarChart.test.js b/webpack/assets/javascripts/react_app/components/common/charts/BarChart/BarChart.test.js
index 847f6cfbca1..560dbaa9555 100644
--- a/webpack/assets/javascripts/react_app/components/common/charts/BarChart/BarChart.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/charts/BarChart/BarChart.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import BarChart from './';
import * as chartService from '../../../../../services/charts/BarChartService';
diff --git a/webpack/assets/javascripts/react_app/components/common/charts/DonutChart/DonutChart.test.js b/webpack/assets/javascripts/react_app/components/common/charts/DonutChart/DonutChart.test.js
index f82921cb624..33bda555cf8 100644
--- a/webpack/assets/javascripts/react_app/components/common/charts/DonutChart/DonutChart.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/charts/DonutChart/DonutChart.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import { mockData, emptyData } from './DonutChart.fixtures';
import DonutChart from './';
diff --git a/webpack/assets/javascripts/react_app/components/common/charts/LineChart/LineChart.test.js b/webpack/assets/javascripts/react_app/components/common/charts/LineChart/LineChart.test.js
index 9262129308d..df6e5b5751a 100644
--- a/webpack/assets/javascripts/react_app/components/common/charts/LineChart/LineChart.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/charts/LineChart/LineChart.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import { data, timeseriesData } from './LineChart.fixtures';
import LineChart from './index';
diff --git a/webpack/assets/javascripts/react_app/components/common/dates/IsoDate.test.js b/webpack/assets/javascripts/react_app/components/common/dates/IsoDate.test.js
index 8e48da64bb5..5afa3a6e377 100644
--- a/webpack/assets/javascripts/react_app/components/common/dates/IsoDate.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/dates/IsoDate.test.js
@@ -1,6 +1,6 @@
/* eslint-disable promise/prefer-await-to-then */
// Configure Enzyme
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import React from 'react';
import IsoDate from './IsoDate';
import { i18nProviderWrapperFactory } from '../../../common/i18nProviderWrapperFactory';
diff --git a/webpack/assets/javascripts/react_app/components/common/dates/LongDateTime.test.js b/webpack/assets/javascripts/react_app/components/common/dates/LongDateTime.test.js
index c4f096b10e9..8c5096f24b6 100644
--- a/webpack/assets/javascripts/react_app/components/common/dates/LongDateTime.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/dates/LongDateTime.test.js
@@ -1,6 +1,6 @@
/* eslint-disable promise/prefer-await-to-then */
// Configure Enzyme
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import React from 'react';
import LongDateTime from './LongDateTime';
import { i18nProviderWrapperFactory } from '../../../common/i18nProviderWrapperFactory';
diff --git a/webpack/assets/javascripts/react_app/components/common/dates/RelativeDateTime.test.js b/webpack/assets/javascripts/react_app/components/common/dates/RelativeDateTime.test.js
index 8a8b0f406d9..5bb5c2d8449 100644
--- a/webpack/assets/javascripts/react_app/components/common/dates/RelativeDateTime.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/dates/RelativeDateTime.test.js
@@ -1,6 +1,6 @@
/* eslint-disable promise/prefer-await-to-then */
// Configure Enzyme
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import React from 'react';
import RelativeDateTime from './RelativeDateTime';
import { i18nProviderWrapperFactory } from '../../../common/i18nProviderWrapperFactory';
diff --git a/webpack/assets/javascripts/react_app/components/common/dates/ShortDateTime.test.js b/webpack/assets/javascripts/react_app/components/common/dates/ShortDateTime.test.js
index d1f9c23e95d..ede7e7f046a 100644
--- a/webpack/assets/javascripts/react_app/components/common/dates/ShortDateTime.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/dates/ShortDateTime.test.js
@@ -1,6 +1,6 @@
/* eslint-disable promise/prefer-await-to-then */
// Configure Enzyme
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import React from 'react';
import ShortDateTime from './ShortDateTime';
import { i18nProviderWrapperFactory } from '../../../common/i18nProviderWrapperFactory';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/Actions.test.js b/webpack/assets/javascripts/react_app/components/common/forms/Actions.test.js
index c59a8f79871..631e557dc73 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/Actions.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/Actions.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import Actions from './Actions';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/CommonForm.test.js b/webpack/assets/javascripts/react_app/components/common/forms/CommonForm.test.js
index 499d62a5050..7ced1ba0862 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/CommonForm.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/CommonForm.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import { FieldLevelHelp } from 'patternfly-react';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/CounterInput/__tests__/CounterInput.test.js b/webpack/assets/javascripts/react_app/components/common/forms/CounterInput/__tests__/CounterInput.test.js
index c4c5f7ad739..0804dd8ae32 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/CounterInput/__tests__/CounterInput.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/CounterInput/__tests__/CounterInput.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import CounterInput from '../';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/DateTime/DateTimeOverrides.scss b/webpack/assets/javascripts/react_app/components/common/forms/DateTime/DateTimeOverrides.scss
index 31c6f3a6c93..4fd50975cce 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/DateTime/DateTimeOverrides.scss
+++ b/webpack/assets/javascripts/react_app/components/common/forms/DateTime/DateTimeOverrides.scss
@@ -1,4 +1,4 @@
-@import "~@theforeman/vendor/scss/variables";
+@import "../../../../common/variables.scss";
$screen-md: 992px !default;
$screen-md-min: $screen-md !default;
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.test.js b/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.test.js
index 69376501234..9ffe1032921 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/ForemanForm.test.js
@@ -1,7 +1,7 @@
import {
testComponentSnapshotsWithFixtures,
testSelectorsSnapshotWithFixtures,
-} from '@theforeman/test';
+} from 'foremanReact/common/testHelpers';
import * as Yup from 'yup';
import { prepareErrors } from '../../../../redux/actions/common/forms';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/integration.test.js b/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/integration.test.js
index 8f2aac4a75e..0cb262d0b42 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/integration.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/ForemanForm/integration.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { IntegrationTestHelper } from '@theforeman/test';
+import IntegrationTestHelper from 'foremanReact/common/IntegrationTestHelper';
import { submitForm } from '../../../../redux/actions/common/forms';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/Form.test.js b/webpack/assets/javascripts/react_app/components/common/forms/Form.test.js
index 104c6bca864..7e119ef5704 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/Form.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/Form.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import Form from './Form';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/NumericInput.scss b/webpack/assets/javascripts/react_app/components/common/forms/NumericInput.scss
index 1e06824d4cd..305999c1489 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/NumericInput.scss
+++ b/webpack/assets/javascripts/react_app/components/common/forms/NumericInput.scss
@@ -1,5 +1,5 @@
-@import "~@theforeman/vendor/scss/variables";
-@import "~@theforeman/vendor/scss/mixins";
+@import "../../../common/variables";
+@import "../../../common/scss/mixins";
.foreman-numeric-input {
position: relative;
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/__tests__/OrderableSelect.test.js b/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/__tests__/OrderableSelect.test.js
index 8e6d3370bee..c56603d92b5 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/__tests__/OrderableSelect.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/__tests__/OrderableSelect.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { DndProvider } from 'react-dnd';
import TestBackend from 'react-dnd-test-backend/dist/cjs/TestBackend';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/index.js b/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/index.js
index 533ab841ae3..fc39637c90b 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/index.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/OrderableSelect/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import { DndProvider } from 'react-dnd';
-import HTML5Backend from 'react-dnd-html5-backend';
+import { HTML5Backend } from 'react-dnd-html5-backend';
import OrderableSelect from './OrderableSelect';
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/RadioButtonGroup/RadioButtonGroup.test.js b/webpack/assets/javascripts/react_app/components/common/forms/RadioButtonGroup/RadioButtonGroup.test.js
index fa976d91cad..465df746196 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/RadioButtonGroup/RadioButtonGroup.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/RadioButtonGroup/RadioButtonGroup.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import RadioButtonGroup from './RadioButtonGroup';
const radios = [
diff --git a/webpack/assets/javascripts/react_app/components/common/forms/Select.test.js b/webpack/assets/javascripts/react_app/components/common/forms/Select.test.js
index 34bee4e78c6..962810eaaf4 100644
--- a/webpack/assets/javascripts/react_app/components/common/forms/Select.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/forms/Select.test.js
@@ -1,16 +1,15 @@
import 'select2';
-import { mount } from '@theforeman/test';
+import { mount } from 'enzyme';
import React from 'react';
import Select from './Select';
jest.unmock('jquery');
-beforeEach(() => {
- document.body.innerHTML = '\n \n
';
-});
-
describe('Select', () => {
+ beforeEach(() => {
+ document.body.innerHTML = '\n \n
';
+ });
it('onChange called exactly once even after update', () => {
const options = { one: '1', two: '2' };
const onChangeMock = jest.fn();
diff --git a/webpack/assets/javascripts/react_app/components/common/table/actions/selectionActions.test.js b/webpack/assets/javascripts/react_app/components/common/table/actions/selectionActions.test.js
index 1a86916c2e1..a0062be8fc6 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/actions/selectionActions.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/actions/selectionActions.test.js
@@ -1,4 +1,4 @@
-import { testActionSnapshotWithFixtures } from '@theforeman/test';
+import { testActionSnapshotWithFixtures } from 'foremanReact/common/testHelpers';
import {
selectPage,
selectAllRows,
diff --git a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/DeleteButton.test.js b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/DeleteButton.test.js
index 6f3d3cf0451..74f77175333 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/DeleteButton.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/DeleteButton.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import DeleteButton from '../DeleteButton';
const baseProps = {
diff --git a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/HostsCountCell.test.js b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/HostsCountCell.test.js
index 9abb4b68c56..39b2dbe2319 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/HostsCountCell.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/HostsCountCell.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import HostsCountCell from '../HostsCountCell';
describe('HostsCountCell', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/NameCell.test.js b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/NameCell.test.js
index 16abf01ce5b..97e1c4aff30 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/NameCell.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/NameCell.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import NameCell from '../NameCell';
const fixtures = {
diff --git a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/SortableHeader.test.js b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/SortableHeader.test.js
index c71bc99a951..ef99b250276 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/SortableHeader.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/SortableHeader.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import SortalbeHeader from '../SortableHeader';
describe('SortalbeHeader', () => {
diff --git a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionCell.test.js b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionCell.test.js
index 33cc20505fa..036069e352a 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionCell.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionCell.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import TableSelectionCell from '../TableSelectionCell';
diff --git a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionHeaderCell.test.js b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionHeaderCell.test.js
index c1156239134..a8670b93e09 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionHeaderCell.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/components/__tests__/TableSelectionHeaderCell.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import TableSelectionHeaderCell from '../TableSelectionHeaderCell';
diff --git a/webpack/assets/javascripts/react_app/components/common/table/reducers/selectionReducer.test.js b/webpack/assets/javascripts/react_app/components/common/table/reducers/selectionReducer.test.js
index 1969d7b5739..9ebd4dfb038 100644
--- a/webpack/assets/javascripts/react_app/components/common/table/reducers/selectionReducer.test.js
+++ b/webpack/assets/javascripts/react_app/components/common/table/reducers/selectionReducer.test.js
@@ -1,5 +1,5 @@
import Immutable from 'seamless-immutable';
-import { testReducerSnapshotWithFixtures } from '@theforeman/test';
+import { testReducerSnapshotWithFixtures } from 'foremanReact/common/testHelpers';
import {
SELECT_ROWS,
UNSELECT_ROWS,
diff --git a/webpack/assets/javascripts/react_app/components/hosts/powerStatus/__tests__/PowerStatus.test.js b/webpack/assets/javascripts/react_app/components/hosts/powerStatus/__tests__/PowerStatus.test.js
index 9d675419380..b7d77304916 100644
--- a/webpack/assets/javascripts/react_app/components/hosts/powerStatus/__tests__/PowerStatus.test.js
+++ b/webpack/assets/javascripts/react_app/components/hosts/powerStatus/__tests__/PowerStatus.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import PowerStatus from '../PowerStatus';
import {
pendingProps,
diff --git a/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/controller.test.js b/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/controller.test.js
index 17047e500aa..1104d403c1d 100644
--- a/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/controller.test.js
+++ b/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/controller.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import { props } from './controller.fixtures';
diff --git a/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/disk/disk.test.js b/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/disk/disk.test.js
index 684aa44e7fb..91357e83526 100644
--- a/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/disk/disk.test.js
+++ b/webpack/assets/javascripts/react_app/components/hosts/storage/vmware/controller/disk/disk.test.js
@@ -1,4 +1,4 @@
-import { shallow } from '@theforeman/test';
+import { shallow } from 'enzyme';
import React from 'react';
import { props } from './disk.fixtures';
diff --git a/webpack/assets/javascripts/react_app/mockRequests.js b/webpack/assets/javascripts/react_app/mockRequests.js
index 954b7cb8518..19cd5b61721 100644
--- a/webpack/assets/javascripts/react_app/mockRequests.js
+++ b/webpack/assets/javascripts/react_app/mockRequests.js
@@ -1,5 +1,5 @@
import axios from 'axios';
-import { MockAdapter } from '@theforeman/test';
+import MockAdapter from 'axios-mock-adapter';
export const mock = () => new MockAdapter(axios);
const methods = {
diff --git a/webpack/assets/javascripts/react_app/redux/API/API.js b/webpack/assets/javascripts/react_app/redux/API/API.js
index cf766e7039c..1a21cf3df54 100644
--- a/webpack/assets/javascripts/react_app/redux/API/API.js
+++ b/webpack/assets/javascripts/react_app/redux/API/API.js
@@ -1,6 +1,6 @@
import axios from 'axios';
import './APITestSetup';
-import { foremanUrl } from '../../../foreman_tools';
+import { foremanUrl } from '../../common/helpers';
const getcsrfToken = () => {
const token = document.querySelector('meta[name="csrf-token"]');
diff --git a/webpack/assets/javascripts/react_app/redux/actions/notifications/index.js b/webpack/assets/javascripts/react_app/redux/actions/notifications/index.js
index 7b340e4bf95..9373901024b 100644
--- a/webpack/assets/javascripts/react_app/redux/actions/notifications/index.js
+++ b/webpack/assets/javascripts/react_app/redux/actions/notifications/index.js
@@ -10,7 +10,7 @@ import {
} from '../../consts';
import * as sessionStorage from '../../../components/notifications/NotificationDrawerSessionStorage';
import { API, get } from '../../API';
-import { reloadPage } from '../../../../foreman_navigation';
+import { reloadPage } from '../../../common/helpers';
import {
stopInterval,
withInterval,
diff --git a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageActions.js b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageActions.js
index c98599ca324..84ea051c9e5 100644
--- a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageActions.js
+++ b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageActions.js
@@ -1,4 +1,4 @@
-import { foremanUrl } from '../../../../foreman_tools';
+import { foremanUrl } from '../../../common/helpers';
import { get, post } from '../../../redux/API';
import {
diff --git a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageHelpers.js b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageHelpers.js
index 44849b41ade..e81b3adb28e 100644
--- a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageHelpers.js
+++ b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/RegistrationCommandsPageHelpers.js
@@ -2,7 +2,7 @@
import React from 'react';
import { FormSelectOption } from '@patternfly/react-core';
-import { foremanUrl } from '../../../../foreman_tools';
+import { foremanUrl } from '../../../common/helpers';
import { sprintf, translate as __ } from '../../../common/I18n';
// Form helpers
diff --git a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/__tests__/helpers.test.js b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/__tests__/helpers.test.js
index 5f7134be1e2..d7ed1810840 100644
--- a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/__tests__/helpers.test.js
+++ b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/__tests__/helpers.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { shallow, render } from '@theforeman/test';
+import { shallow, render } from 'enzyme'
import { FormSelectOption } from '@patternfly/react-core';
import { emptyOption, validatedOS, osHelperText } from '../RegistrationCommandsPageHelpers'
diff --git a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/components/Actions.js b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/components/Actions.js
index c77c78cf51c..e76aada4e7d 100644
--- a/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/components/Actions.js
+++ b/webpack/assets/javascripts/react_app/routes/RegistrationCommands/RegistrationCommandsPage/components/Actions.js
@@ -5,7 +5,7 @@ import { Link } from 'react-router-dom';
import { ActionGroup, Button } from '@patternfly/react-core';
import { translate as __ } from '../../../../common/I18n';
-import { foremanUrl } from '../../../../../foreman_tools';
+import { foremanUrl } from '../../../../common/helpers';
const Actions = ({ isLoading, isGenerating, handleSubmit, invalidFields }) => (
<>
diff --git a/webpack/assets/javascripts/react_app/routes/RoutingService.js b/webpack/assets/javascripts/react_app/routes/RoutingService.js
index 9f463dcc070..ac7bd65ea68 100644
--- a/webpack/assets/javascripts/react_app/routes/RoutingService.js
+++ b/webpack/assets/javascripts/react_app/routes/RoutingService.js
@@ -1,6 +1,6 @@
import { Route } from 'react-router-dom';
import React from 'react';
-import { visit } from '../../foreman_navigation';
+import { visit } from '../common/helpers';
import { addGlobalFill } from '../components/common/Fill/GlobalFill';
let currentPath = window.location.href;
diff --git a/webpack/assets/javascripts/react_app/routes/__test__/Routes.test.js b/webpack/assets/javascripts/react_app/routes/__test__/Routes.test.js
index 386cb612b81..413e2cd5d3c 100644
--- a/webpack/assets/javascripts/react_app/routes/__test__/Routes.test.js
+++ b/webpack/assets/javascripts/react_app/routes/__test__/Routes.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import AppSwitcher from '../';
import { children } from './ForemanSwitcher.fixtures'
diff --git a/webpack/assets/javascripts/react_app/routes/common/EmptyPage/RedirectedEmptyPage.js b/webpack/assets/javascripts/react_app/routes/common/EmptyPage/RedirectedEmptyPage.js
index d1f1b7014c8..1407501d96f 100644
--- a/webpack/assets/javascripts/react_app/routes/common/EmptyPage/RedirectedEmptyPage.js
+++ b/webpack/assets/javascripts/react_app/routes/common/EmptyPage/RedirectedEmptyPage.js
@@ -4,7 +4,7 @@ import { Button } from '@patternfly/react-core';
import { useHistory } from 'react-router-dom';
import { SearchIcon } from '@patternfly/react-icons';
import { translate as __ } from '../../../common/I18n';
-import { visit } from '../../../../foreman_navigation';
+import { visit } from '../../../common/helpers';
import { EmptyStatePattern } from '../../../components/common/EmptyState';
const RedirectedEmptyPage = ({ location: { state = {} } }) => {
diff --git a/webpack/assets/javascripts/react_app/routes/common/PageLayout/components/ExportButton/ExportButton.test.js b/webpack/assets/javascripts/react_app/routes/common/PageLayout/components/ExportButton/ExportButton.test.js
index d9178abcb93..903bf388f25 100644
--- a/webpack/assets/javascripts/react_app/routes/common/PageLayout/components/ExportButton/ExportButton.test.js
+++ b/webpack/assets/javascripts/react_app/routes/common/PageLayout/components/ExportButton/ExportButton.test.js
@@ -1,4 +1,4 @@
-import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
+import { testComponentSnapshotsWithFixtures } from 'foremanReact/common/testHelpers';
import ExportButton from './ExportButton';
diff --git a/webpack/assets/javascripts/services/charts/LineChartService.test.js b/webpack/assets/javascripts/services/charts/LineChartService.test.js
index 60d51c31a9a..55962aca57d 100644
--- a/webpack/assets/javascripts/services/charts/LineChartService.test.js
+++ b/webpack/assets/javascripts/services/charts/LineChartService.test.js
@@ -1,4 +1,4 @@
-import { testActionSnapshotWithFixtures } from '@theforeman/test';
+import { testActionSnapshotWithFixtures } from 'foremanReact/common/testHelpers';
import { getLineChartConfig } from './LineChartService';
import {
diff --git a/webpack/test_setup.js b/webpack/core_test_setup.js
similarity index 59%
rename from webpack/test_setup.js
rename to webpack/core_test_setup.js
index c7158276891..6940d453554 100644
--- a/webpack/test_setup.js
+++ b/webpack/core_test_setup.js
@@ -6,16 +6,34 @@ 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', () => ({
foremanUrl: url => url,
}));
jest.mock('./assets/javascripts/foreman_navigation');
+
+jest.mock('./assets/javascripts/react_app/common/helpers', () => {
+ const helpers = jest.requireActual(
+ './assets/javascripts/react_app/common/helpers'
+ );
+ return {
+ ...helpers,
+ visit: jest.fn(),
+ reloadPage: jest.fn(),
+ };
+});
diff --git a/webpack/global_test_setup.js b/webpack/global_test_setup.js
new file mode 100644
index 00000000000..de6724309e2
--- /dev/null
+++ b/webpack/global_test_setup.js
@@ -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('enzyme');
+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;
+};
diff --git a/webpack/jest.config.js b/webpack/jest.config.js
new file mode 100644
index 00000000000..b1c5e8a4b80
--- /dev/null
+++ b/webpack/jest.config.js
@@ -0,0 +1,79 @@
+/* eslint-disable spellcheck/spell-checker */
+const fs = require('fs');
+const path = require('path');
+
+const nodeModules = path.resolve(__dirname, '..', 'node_modules');
+const packageJsonPath = path.resolve(__dirname, '..', 'package.json');
+const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
+const vendorCorePackageJsonPath = path.resolve(
+ nodeModules,
+ '@theforeman/vendor-core',
+ 'package.json'
+);
+const vendorCorePackageJson = JSON.parse(
+ fs.readFileSync(vendorCorePackageJsonPath, 'utf8')
+);
+
+const dependencies = {
+ ...packageJson.dependencies,
+ ...packageJson.devDependencies,
+ ...vendorCorePackageJson.dependencies,
+ '@apollo/client/testing': '@apollo/client/testing',
+}; // Use shared dependencies from foreman node_modules and not plugin node_modules to avoid jest errors due to multiple instances of same package
+
+const moduleNameMapper = {};
+Object.keys(dependencies).forEach(dep => {
+ moduleNameMapper[`^${dep}$`] = path.resolve(nodeModules, dep);
+});
+
+const foremanReactFull = path.resolve(
+ __dirname,
+ 'assets/javascripts/react_app'
+);
+const foremanTest = path.resolve(__dirname, 'theforeman_test_dependencies.js');
+
+module.exports = {
+ verbose: true,
+ logHeapUsage: true,
+ maxWorkers: 2,
+ collectCoverage: true,
+ coverageReporters: ['lcov'],
+ coverageDirectory: `../coverage`,
+ setupFiles: [require.resolve('jest-prop-type-error')],
+ testRegex: '.*\\.test\\.js$',
+ testPathIgnorePatterns: [
+ '/node_modules/',
+ '/foreman/',
+ '/.+fixtures.+',
+ 'foreman/webpack', // dont test foreman core in plugins
+ ],
+ moduleDirectories: [
+ `node_modules`,
+ `/node_modules/@theforeman/vendor-core/node_modules`,
+ `node_modules/@theforeman/vendor-core/node_modules`,
+ '/node_modules',
+ ],
+ transform: {
+ '^.+\\.js?$': 'babel-jest',
+ '\\.(gql|graphql)$': require.resolve('jest-transform-graphql'), // for graphql-tag
+ },
+ snapshotSerializers: [require.resolve('enzyme-to-json/serializer')],
+ moduleNameMapper: {
+ '^.+\\.(png|gif|css|scss)$': 'identity-obj-proxy',
+ ...moduleNameMapper,
+ '^dnd-core$': `${nodeModules}/dnd-core/dist/cjs`,
+ '^react-dnd$': `${nodeModules}/react-dnd/dist/cjs`,
+ '^react-dnd-html5-backend$': `${nodeModules}/react-dnd-html5-backend/dist/cjs`,
+ '^react-dnd-touch-backend$': `${nodeModules}/react-dnd-touch-backend/dist/cjs`,
+ '^react-dnd-test-backend$': `${nodeModules}/react-dnd-test-backend/dist/cjs`,
+ '^react-dnd-test-utils$': `${nodeModules}/react-dnd-test-utils/dist/cjs`,
+ '^foremanReact(.*)$': `${foremanReactFull}/$1`,
+ '^@theforeman/test$': foremanTest,
+ '^victory(.*)$': `${nodeModules}/victory$1`,
+ },
+ globals: {
+ __testing__: true,
+ URL_PREFIX: '',
+ },
+ resolver: require.resolve('./resolveNodeModule'),
+};
diff --git a/webpack/resolveNodeModule.js b/webpack/resolveNodeModule.js
new file mode 100644
index 00000000000..30f3d22b315
--- /dev/null
+++ b/webpack/resolveNodeModule.js
@@ -0,0 +1,62 @@
+const { modules } = require('@theforeman/vendor-core');
+
+const isRequestedByVendorCore = currentFileDirectory =>
+ (currentFileDirectory.includes('foreman-js/packages/vendor-core') ||
+ currentFileDirectory.includes('@theforeman/vendor-core')) &&
+ !currentFileDirectory.includes(
+ 'foreman-js/packages/vendor-core/node_modules'
+ ) &&
+ !currentFileDirectory.includes('@theforeman/vendor-core/node_modules');
+
+const getModuleToResolve = ({ sourcePath, currentFileDirectory }) => {
+ // map the custom foreman js module to the correct path
+ const requestPath = sourcePath === '.' ? './index' : sourcePath;
+ const sourcePathSplit = sourcePath.split('/');
+ const name = sourcePathSplit[sourcePathSplit.length - 1];
+ const vendorModule = modules.find(m => m.name === name);
+ const requestedByVendorCore = isRequestedByVendorCore(currentFileDirectory);
+
+ const shouldResolveCustomVendorModule = modules.find(
+ m => m.name === name && m.hasCustomPath && !requestedByVendorCore
+ );
+
+ return shouldResolveCustomVendorModule ? vendorModule.path : requestPath;
+};
+
+/**
+ * resolve a import/require of a node module
+ * this method should be calld by the jest-resolver
+ * to resolve every require statement
+ * This cannot be done with changing the paths with moduleNameMapper
+ */
+
+const resolveNodeModule = (sourcePath, currentFile) => {
+ const { basedir, rootDir } = currentFile;
+ const moduleToResolve = getModuleToResolve({
+ sourcePath,
+ currentFileDirectory: basedir,
+ });
+ if (sourcePath.includes('theforeman_test_dependencies')) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ 'import from @theforeman/test is deprecated, please remove the package and import from enzyme, axios-mock-adapter, foremanReact/testHelpers, foremanReact/common/IntegrationTestHelper directly instead.'
+ );
+ }
+ let results;
+ try {
+ results = require.resolve(moduleToResolve, {
+ paths: [basedir],
+ });
+ } catch (error) {
+ results = currentFile.defaultResolver(moduleToResolve, currentFile);
+ }
+
+ return rootDir
+ ? results.replace(
+ /.*\/foreman-js\/packages/,
+ `${rootDir}/node_modules/@theforeman`
+ )
+ : results;
+};
+
+module.exports = resolveNodeModule;
diff --git a/webpack/theforeman_test_dependencies.js b/webpack/theforeman_test_dependencies.js
new file mode 100644
index 00000000000..eb747effd53
--- /dev/null
+++ b/webpack/theforeman_test_dependencies.js
@@ -0,0 +1,36 @@
+// replaces @theforeman/test.js
+import { shallow, mount, render, configure } from 'enzyme';
+import MockAdapter from 'axios-mock-adapter';
+
+import {
+ mockWindowLocation,
+ classFunctionUnitTest,
+ shallowRenderComponentWithFixtures,
+ testComponentSnapshotsWithFixtures,
+ runActionInDepth,
+ testActionSnapshot,
+ testActionSnapshotWithFixtures,
+ testReducerSnapshotWithFixtures,
+ testSelectorsSnapshotWithFixtures,
+ initMockStore,
+} from './assets/javascripts/react_app/common/testHelpers';
+import IntegrationTestHelper from './assets/javascripts/react_app/common/IntegrationTestHelper';
+
+export {
+ mockWindowLocation,
+ classFunctionUnitTest,
+ shallowRenderComponentWithFixtures,
+ testComponentSnapshotsWithFixtures,
+ runActionInDepth,
+ testActionSnapshot,
+ testActionSnapshotWithFixtures,
+ testReducerSnapshotWithFixtures,
+ testSelectorsSnapshotWithFixtures,
+ initMockStore,
+ IntegrationTestHelper,
+ shallow,
+ mount,
+ render,
+ configure,
+ MockAdapter,
+};