diff --git a/frontend/__mocks__/localStorage.ts b/frontend/__mocks__/localStorage.ts index be5c3407c66..e920671557d 100644 --- a/frontend/__mocks__/localStorage.ts +++ b/frontend/__mocks__/localStorage.ts @@ -2,10 +2,10 @@ let _localStorage = {}; (window as any).localStorage = (window as any).sessionStorage = { setItem(key, value) { - return Object.assign(_localStorage, {[key]: value}); + Object.assign(_localStorage, { [key]: value} ); }, getItem(key) { - return _localStorage[key]; + return _localStorage[key] || null; }, clear() { _localStorage = {}; diff --git a/frontend/__mocks__/matchMedia.js b/frontend/__mocks__/matchMedia.js index 00d3b2c5b88..6db4442d249 100644 --- a/frontend/__mocks__/matchMedia.js +++ b/frontend/__mocks__/matchMedia.js @@ -1 +1 @@ -window.matchMedia = () => ({matches: true}); +window.matchMedia = () => ({ matches: true }); diff --git a/frontend/__mocks__/requestAnimationFrame.js b/frontend/__mocks__/requestAnimationFrame.js deleted file mode 100644 index 99c95de591c..00000000000 --- a/frontend/__mocks__/requestAnimationFrame.js +++ /dev/null @@ -1,4 +0,0 @@ -// https://github.com/facebook/jest/issues/4545 -window.requestAnimationFrame = (callback) => { - setTimeout(callback, 0); -}; diff --git a/frontend/before-tests.js b/frontend/before-tests.js index f59b16c1a26..03f87dc4f0a 100644 --- a/frontend/before-tests.js +++ b/frontend/before-tests.js @@ -3,11 +3,12 @@ import { configure } from 'enzyme'; import * as Adapter from 'enzyme-adapter-react-16'; +import './setup-jsdom'; +import 'url-search-params-polyfill'; + // http://airbnb.io/enzyme/docs/installation/index.html#working-with-react-16 -configure({adapter: new Adapter()}); +configure({ adapter: new Adapter() }); window.SERVER_FLAGS = { basePath: '/', }; - -require('url-search-params-polyfill'); diff --git a/frontend/package.json b/frontend/package.json index 31f6e345df4..3b6808883a7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -43,8 +43,8 @@ "/node_modules/(?!lodash-es|@console)" ], "testRegex": "/__tests__/.*\\.spec\\.(ts|tsx|js|jsx)$", + "testURL": "http://localhost", "setupFiles": [ - "./__mocks__/requestAnimationFrame.js", "./__mocks__/localStorage.ts", "./__mocks__/matchMedia.js", "./before-tests.js" diff --git a/frontend/packages/console-plugin-sdk/src/codegen/plugin-resolver.ts b/frontend/packages/console-plugin-sdk/src/codegen/plugin-resolver.ts index 8f044dbeba7..878299fd364 100644 --- a/frontend/packages/console-plugin-sdk/src/codegen/plugin-resolver.ts +++ b/frontend/packages/console-plugin-sdk/src/codegen/plugin-resolver.ts @@ -11,6 +11,9 @@ const consoleAppName = '@console/app'; // glob that matches all the monorepo packages const consolePkgGlob = 'packages/*/package.json'; +// env. variable used to override the active plugin list +const consolePluginOverrideEnvVar = 'CONSOLE_PLUGINS'; + /** * Return `true` if the given package represents a Console plugin. */ @@ -40,14 +43,19 @@ export const readPackages = (packageFiles: string[]) => { }; }; +const sortPluginPackages: PluginPackageFilter = (appPackage, pluginPackages) => { + // if appPackage is in the list, make sure it's the first element + return _.sortBy(pluginPackages, (pkg) => (appPackage === pkg ? 0 : 1)); +}; + export const filterActivePluginPackages: PluginPackageFilter = (appPackage, pluginPackages) => { // include dependencies of the appPackage or the appPackage itself - const list = pluginPackages.filter( - (pkg) => appPackage === pkg || appPackage.dependencies[pkg.name] === pkg.version, + return sortPluginPackages( + appPackage, + pluginPackages.filter( + (pkg) => appPackage === pkg || appPackage.dependencies[pkg.name] === pkg.version, + ), ); - - // if appPackage is in the list, make sure it's the first element - return _.sortBy(list, (pkg) => (appPackage === pkg ? 0 : 1)); }; /** @@ -59,7 +67,21 @@ export const resolvePluginPackages = ( ) => { const packageFiles = glob.sync(consolePkgGlob, { cwd: monorepoRootDir, absolute: true }); const { appPackage, pluginPackages } = readPackages(packageFiles); - return appPackage ? pluginFilter(appPackage, pluginPackages) : []; + + if (!appPackage) { + throw new Error(`Cannot detect Console application package ${consoleAppName}`); + } + + // provide the ability to override the active plugin list + if (_.isString(process.env[consolePluginOverrideEnvVar])) { + const pluginPackageNames = process.env[consolePluginOverrideEnvVar].split(','); + return sortPluginPackages( + appPackage, + pluginPackages.filter((pkg) => pluginPackageNames.includes(pkg.name)), + ); + } + + return pluginFilter(appPackage, pluginPackages); }; export type Package = readPkg.NormalizedPackageJson; diff --git a/frontend/packages/dev-console/src/components/topology/shapes/__tests__/BaseNode.spec.tsx b/frontend/packages/dev-console/src/components/topology/shapes/__tests__/BaseNode.spec.tsx index c0072d9dcaf..566fae148cf 100644 --- a/frontend/packages/dev-console/src/components/topology/shapes/__tests__/BaseNode.spec.tsx +++ b/frontend/packages/dev-console/src/components/topology/shapes/__tests__/BaseNode.spec.tsx @@ -38,7 +38,7 @@ describe('BaseNode', () => { .find('.odc-base-node__bg') .first() .props().filter, - ).toBe('url(blank#BaseNodeDropShadowFilterId)'); + ).toBe('url(/#BaseNodeDropShadowFilterId)'); wrapper.setState({ hover: true }); expect( @@ -46,7 +46,7 @@ describe('BaseNode', () => { .find('.odc-base-node__bg') .first() .props().filter, - ).toBe('url(blank#BaseNodeDropShadowFilterId--hover)'); + ).toBe('url(/#BaseNodeDropShadowFilterId--hover)'); }); it('should show long labels when selected', () => { diff --git a/frontend/plugin-stats.ts b/frontend/plugin-stats.ts index 08112511161..07cb8bb21d0 100644 --- a/frontend/plugin-stats.ts +++ b/frontend/plugin-stats.ts @@ -1,5 +1,8 @@ /* eslint-env node */ +import './setup-jsdom'; +import './__mocks__/matchMedia'; + import { resolvePluginPackages, loadActivePlugins, diff --git a/frontend/public/reducers/ui.ts b/frontend/public/reducers/ui.ts index 93f948a5010..c1e5ceb986a 100644 --- a/frontend/public/reducers/ui.ts +++ b/frontend/public/reducers/ui.ts @@ -30,7 +30,7 @@ export function getDefaultPerspective() { activePerspective = defaultPerspective.properties.id; } } - return activePerspective; + return activePerspective || undefined; } export default (state: UIState, action: UIAction): UIState => { diff --git a/frontend/setup-jsdom.js b/frontend/setup-jsdom.js new file mode 100644 index 00000000000..7a0aefc2f34 --- /dev/null +++ b/frontend/setup-jsdom.js @@ -0,0 +1,27 @@ +/* eslint-env node */ + +// https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md + +import { JSDOM } from 'jsdom'; + +const jsdom = new JSDOM(''); +const { window } = jsdom; + +function copyProps(src, target) { + Object.defineProperties(target, { + ...Object.getOwnPropertyDescriptors(src), + ...Object.getOwnPropertyDescriptors(target), + }); +} + +global.window = window; +global.document = window.document; +global.navigator = { + userAgent: 'node.js', +}; +global.requestAnimationFrame = (callback) => setTimeout(callback, 0); +global.cancelAnimationFrame = (id) => { + clearTimeout(id); +}; + +copyProps(window, global);