test: Migration to jest (#935)

* jest initialised

* refactor: `connect()` and `openApp()`

* test: integrate to jest

* chore: renaming jests test commands

* chore: adding override rule for non `commands/serve` directories to skip jest rules
This commit is contained in:
Ahmad Mirzaei
2022-09-26 11:02:32 +02:00
committed by GitHub
parent dcebeb157c
commit 5990d85a95
11 changed files with 1486 additions and 143 deletions

View File

@@ -1,13 +1,14 @@
{ {
"root": true, "root": true,
"env": { "env": {
"browser": true "browser": true,
"jest/globals": true
}, },
"parserOptions": { "parserOptions": {
"sourceType": "module" "sourceType": "module"
}, },
"extends": ["airbnb", "prettier"], "extends": ["airbnb", "prettier"],
"plugins": ["prettier"], "plugins": ["prettier", "jest"],
"rules": { "rules": {
"max-len": 0, "max-len": 0,
"no-plusplus": 0, "no-plusplus": 0,
@@ -18,12 +19,24 @@
"react/prop-types": 0, "react/prop-types": 0,
"react/no-deprecated": 0, "react/no-deprecated": 0,
"import/no-extraneous-dependencies": [2, { "devDependencies": true }], "import/no-extraneous-dependencies": [2, { "devDependencies": true }],
"import/no-dynamic-require": 0 "import/no-dynamic-require": 0,
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
}, },
"globals": { "globals": {
"__NEBULA_DEV__": false "__NEBULA_DEV__": false
}, },
"overrides": [ "overrides": [
{
"files": ["apis/**/*", "packages/**/*", "commands/create/**/*", "commands/sense/src/**/*"],
"rules": {
"jest/valid-expect": 0,
"jest/no-identical-title": 0
}
},
{ {
"files": ["scripts/**/*", "**/apis/*/scripts/**/*"], "files": ["scripts/**/*", "**/apis/*/scripts/**/*"],
"rules": { "rules": {

View File

@@ -2,15 +2,7 @@ module.exports = {
env: { env: {
test: { test: {
presets: [['@babel/preset-env', { targets: { node: 'current' } }]], presets: [['@babel/preset-env', { targets: { node: 'current' } }]],
plugins: [ plugins: ['@babel/plugin-transform-react-jsx'],
['@babel/plugin-transform-react-jsx'],
[
'istanbul',
{
exclude: ['**/test/**', '**/__test__/**', '**/dist/**'],
},
],
],
}, },
}, },
}; };

View File

@@ -21,7 +21,7 @@ import Step from '@mui/material/Step';
import StepLabel from '@mui/material/StepLabel'; import StepLabel from '@mui/material/StepLabel';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import { info as connectionInfo, connect } from '../connect'; import { getConnectionInfo, connect } from '../connect';
import storageFn from '../storage'; import storageFn from '../storage';
const storage = storageFn({}); const storage = storageFn({});
@@ -268,7 +268,7 @@ export default function Hub() {
}; };
useEffect(() => { useEffect(() => {
connectionInfo.then((i) => { getConnectionInfo().then((i) => {
if (i.enigma.appId) { if (i.enigma.appId) {
window.location.href = `/dev/${window.location.search}`; window.location.href = `/dev/${window.location.search}`;
return; return;

View File

@@ -61,51 +61,37 @@ const parseEngineURL = (url) => {
}; };
}; };
const connectionInfo = fetch('/info') const getConnectionInfo = () =>
.then((response) => response.json()) fetch('/info')
.then(async (n) => { .then((response) => response.json())
let info = n; .then(async (n) => {
if (params.engine_url) { let info = n;
info = { if (params.engine_url) {
info = {
...info,
...parseEngineURL(params.engine_url),
};
} else if (params.app) {
info = {
...info,
enigma: {
...info.enigma,
appId: params.app,
},
};
}
if (params['qlik-web-integration-id']) {
info.webIntegrationId = params['qlik-web-integration-id'];
}
if (info.invalid) {
return info;
}
const rootPath = `${info.enigma.secure ? 'https' : 'http'}://${info.enigma.host}`;
return {
...info, ...info,
...parseEngineURL(params.engine_url), rootPath,
}; };
} else if (params.app) { });
info = {
...info,
enigma: {
...info.enigma,
appId: params.app,
},
};
}
if (params['qlik-web-integration-id']) {
info.webIntegrationId = params['qlik-web-integration-id'];
}
if (info.invalid) {
return info;
}
const rootPath = `${info.enigma.secure ? 'https' : 'http'}://${info.enigma.host}`;
return {
...info,
rootPath,
};
});
let headers;
const getHeaders = (authInstance) => {
if (!authInstance) return 401;
headers = {
'qlik-web-integration-id': authInstance.config.webIntegrationId,
'qlik-csrf-token': authInstance.config.csrfToken,
};
return headers;
};
const defaultConfig = {
secure: false,
};
const getAuthInstance = ({ webIntegrationId, host }) => { const getAuthInstance = ({ webIntegrationId, host }) => {
const authInstance = new Auth({ const authInstance = new Auth({
@@ -118,69 +104,73 @@ const getAuthInstance = ({ webIntegrationId, host }) => {
return authInstance; return authInstance;
}; };
let connection; const connect = async () => {
const connect = () => { try {
if (!connection) { const {
connection = connectionInfo.then(({ webIntegrationId, enigma: enigmaInfo, enigma: { host } }) => { webIntegrationId,
if (webIntegrationId) { enigma: enigmaInfo,
const authInstance = getAuthInstance({ webIntegrationId, host }); enigma: { host },
if (!headers) headers = getHeaders(authInstance); } = await getConnectionInfo();
if (headers === 401) return { status: 401 };
return {
getDocList: async () => {
const url = `/items?resourceType=app&limit=30&sort=-updatedAt`;
const { data = [] } = await (await authInstance.rest(url)).json();
return data.map((d) => ({
qDocId: d.resourceId,
qTitle: d.name,
}));
},
getConfiguration: async () => ({}),
};
}
const url = SenseUtilities.buildUrl({
...defaultConfig,
...enigmaInfo,
});
return enigma
.create({
schema: qixSchema,
url,
})
.open();
});
}
return connection;
};
const openApp = (id) =>
connectionInfo.then(async ({ webIntegrationId, enigma: enigmaInfo, enigma: { host } }) => {
let urlParams = {};
if (webIntegrationId) { if (webIntegrationId) {
const authInstance = getAuthInstance({ webIntegrationId, host }); const authInstance = getAuthInstance({ webIntegrationId, host });
if (!headers) headers = getHeaders(authInstance); return {
urlParams = { ...headers }; getDocList: async () => {
const url = `/items?resourceType=app&limit=30&sort=-updatedAt`;
const { data = [] } = await (await authInstance.rest(url)).json();
return data.map((d) => ({
qDocId: d.resourceId,
qTitle: d.name,
}));
},
getConfiguration: async () => ({}),
};
}
const url = SenseUtilities.buildUrl({
secure: false,
...enigmaInfo,
});
return enigma
.create({
schema: qixSchema,
url,
})
.open();
} catch (error) {
throw new Error('Failed to return enigma instance');
}
};
const openApp = async (id) => {
try {
const {
webIntegrationId,
enigma: enigmaInfo,
enigma: { host },
} = await getConnectionInfo();
if (webIntegrationId) {
const authInstance = getAuthInstance({ webIntegrationId, host });
const url = await authInstance.generateWebsocketUrl(id); const url = await authInstance.generateWebsocketUrl(id);
const enigmaGlobal = await enigma.create({ schema: qixSchema, url }).open(); const enigmaGlobal = await enigma.create({ schema: qixSchema, url }).open();
return enigmaGlobal.openDoc(id); return enigmaGlobal.openDoc(id);
} }
const url = SenseUtilities.buildUrl({ const url = SenseUtilities.buildUrl({
...defaultConfig, secure: false,
...enigmaInfo, ...enigmaInfo,
urlParams, urlParams: {},
appId: id, appId: id,
}); });
return enigma return enigma
.create({ schema: qixSchema, url }) .create({ schema: qixSchema, url })
.open() .open()
.then((global) => global.openDoc(id)); .then((global) => global.openDoc(id));
}); } catch (error) {
throw new Error('Failed to open app!');
}
};
export { connect, openApp, params, connectionInfo as info }; export { connect, openApp, params, getConnectionInfo, getAuthInstance };

View File

@@ -3,10 +3,10 @@ import ReactDOM from 'react-dom';
import App from './components/App'; import App from './components/App';
import { openApp, info } from './connect'; import { openApp, getConnectionInfo } from './connect';
import initiateWatch from './hot'; import initiateWatch from './hot';
info.then(($) => { getConnectionInfo().then(($) => {
if (!$.enigma.appId) { if (!$.enigma.appId) {
window.location.href = `/${window.location.search}`; window.location.href = `/${window.location.search}`;
} }

View File

@@ -2,7 +2,7 @@
import { embed } from '@nebula.js/stardust'; import { embed } from '@nebula.js/stardust';
import snapshooter from '@nebula.js/snapshooter/client'; import snapshooter from '@nebula.js/snapshooter/client';
import { openApp, params, info as serverInfo } from './connect'; import { openApp, params, getConnectionInfo } from './connect';
import initiateWatch from './hot'; import initiateWatch from './hot';
import renderFixture from './render-fixture'; import renderFixture from './render-fixture';
@@ -31,7 +31,7 @@ const nuke = async ({ app, supernova: { name }, themes, theme, language }) => {
}; };
async function renderWithEngine() { async function renderWithEngine() {
const info = await serverInfo; const info = await getConnectionInfo();
initiateWatch(info); initiateWatch(info);
if (!info.enigma.appId) { if (!info.enigma.appId) {
location.href = location.origin; //eslint-disable-line location.href = location.origin; //eslint-disable-line
@@ -79,7 +79,7 @@ async function renderWithEngine() {
} }
async function renderSnapshot() { async function renderSnapshot() {
const info = await serverInfo; const info = await getConnectionInfo();
const { themes, supernova } = info; const { themes, supernova } = info;
initiateWatch(info); initiateWatch(info);
const element = document.querySelector('#chart-container'); const element = document.querySelector('#chart-container');

View File

@@ -1,7 +1,7 @@
import { embed } from '@nebula.js/stardust'; import { embed } from '@nebula.js/stardust';
import EnigmaMocker from '@nebula.js/enigma-mocker'; import EnigmaMocker from '@nebula.js/enigma-mocker';
import extend from 'extend'; import extend from 'extend';
import { info as getServerInfo } from './connect'; import { getConnectionInfo } from './connect';
import { getModule } from './hot'; import { getModule } from './hot';
const getDefaultGenericObject = ({ type }) => ({ const getDefaultGenericObject = ({ type }) => ({
@@ -101,7 +101,7 @@ function getQId(genericObjects = []) {
const renderFixture = async (params) => { const renderFixture = async (params) => {
const element = document.querySelector('#chart-container'); const element = document.querySelector('#chart-container');
const serverInfo = await getServerInfo; const serverInfo = await getConnectionInfo();
const fixture = await getFixture(params.fixture); const fixture = await getFixture(params.fixture);
const { type, load, genericObjects, instanceConfig, snConfig } = await getOptions({ fixture, params, serverInfo }); const { type, load, genericObjects, instanceConfig, snConfig } = await getOptions({ fixture, params, serverInfo });
const mockedApp = await EnigmaMocker.fromGenericObjects(genericObjects); const mockedApp = await EnigmaMocker.fromGenericObjects(genericObjects);

16
jest.config.js Normal file
View File

@@ -0,0 +1,16 @@
module.exports = {
clearMocks: true,
testEnvironment: 'jest-environment-jsdom',
testRegex: ['commands/serve/web/.+\\.(test|spec)\\.[jt]sx?$'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
collectCoverageFrom: [
'commands/serve/web/**/*.{js,jsx}',
'!commands/serve/web/**/*.spec.{js,jsx}',
'!commands/serve/web/**/*.test.{js,jsx}',
'!commands/serve/web/**/__tests__/**/*',
'!**/dist/**',
'!**/node_modules/**',
],
coverageReporters: ['json', 'lcov', 'text-summary', 'clover'],
};

2
jest.setup.js Normal file
View File

@@ -0,0 +1,2 @@
import '@testing-library/jest-dom';
import 'whatwg-fetch';

View File

@@ -15,6 +15,9 @@
"build-storybook": "build-storybook", "build-storybook": "build-storybook",
"spec": "lerna run spec --stream --concurrency 99 && lerna run ts --stream --concurrency 99 ", "spec": "lerna run spec --stream --concurrency 99 && lerna run ts --stream --concurrency 99 ",
"test": "yarn run test:unit", "test": "yarn run test:unit",
"test:jest": "jest",
"test:jest:watch": "jest --watch",
"test:jest:coverage": "jest --coverage",
"mashup": "node scripts/start-mashup.js", "mashup": "node scripts/start-mashup.js",
"test:mashup": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/mashup/**/*.int.js'", "test:mashup": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/mashup/**/*.int.js'",
"test:integration": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/integration/**/*.int.js'", "test:integration": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/integration/**/*.int.js'",
@@ -36,8 +39,8 @@
"@babel/cli": "7.18.10", "@babel/cli": "7.18.10",
"@babel/core": "7.18.13", "@babel/core": "7.18.13",
"@babel/helper-plugin-utils": "7.18.9", "@babel/helper-plugin-utils": "7.18.9",
"@babel/plugin-transform-react-jsx": "7.18.10",
"@babel/plugin-proposal-object-rest-spread": "7.18.9", "@babel/plugin-proposal-object-rest-spread": "7.18.9",
"@babel/plugin-transform-react-jsx": "7.18.10",
"@babel/preset-env": "7.18.10", "@babel/preset-env": "7.18.10",
"@babel/preset-react": "7.18.6", "@babel/preset-react": "7.18.6",
"@commitlint/cli": "17.1.2", "@commitlint/cli": "17.1.2",
@@ -46,9 +49,9 @@
"@rollup/plugin-json": "4.1.0", "@rollup/plugin-json": "4.1.0",
"@rollup/plugin-node-resolve": "13.3.0", "@rollup/plugin-node-resolve": "13.3.0",
"@rollup/plugin-replace": "4.0.0", "@rollup/plugin-replace": "4.0.0",
"@storybook/addon-docs": "6.5.10",
"@storybook/addon-controls": "6.5.10",
"@storybook/addon-actions": "6.5.10", "@storybook/addon-actions": "6.5.10",
"@storybook/addon-controls": "6.5.10",
"@storybook/addon-docs": "6.5.10",
"@storybook/addon-essentials": "6.5.10", "@storybook/addon-essentials": "6.5.10",
"@storybook/addon-interactions": "6.5.10", "@storybook/addon-interactions": "6.5.10",
"@storybook/addon-links": "6.5.10", "@storybook/addon-links": "6.5.10",
@@ -56,6 +59,7 @@
"@storybook/manager-webpack5": "6.5.10", "@storybook/manager-webpack5": "6.5.10",
"@storybook/react": "6.5.10", "@storybook/react": "6.5.10",
"@storybook/testing-library": "0.0.13", "@storybook/testing-library": "0.0.13",
"@testing-library/jest-dom": "^5.16.5",
"babel-loader": "8.2.5", "babel-loader": "8.2.5",
"babel-plugin-istanbul": "6.1.1", "babel-plugin-istanbul": "6.1.1",
"body-parser": "1.20.0", "body-parser": "1.20.0",
@@ -65,6 +69,7 @@
"eslint-config-airbnb": "19.0.4", "eslint-config-airbnb": "19.0.4",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "2.26.0",
"eslint-plugin-jest": "^27.0.4",
"eslint-plugin-jsx-a11y": "6.6.1", "eslint-plugin-jsx-a11y": "6.6.1",
"eslint-plugin-mocha": "10.1.0", "eslint-plugin-mocha": "10.1.0",
"eslint-plugin-prettier": "4.2.1", "eslint-plugin-prettier": "4.2.1",
@@ -72,6 +77,8 @@
"eslint-plugin-storybook": "0.6.4", "eslint-plugin-storybook": "0.6.4",
"express": "4.18.1", "express": "4.18.1",
"husky": "8.0.1", "husky": "8.0.1",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.0.3",
"jimp": "0.16.1", "jimp": "0.16.1",
"lerna": "5.5.0", "lerna": "5.5.0",
"lint-staged": "13.0.3", "lint-staged": "13.0.3",
@@ -86,6 +93,7 @@
"rollup-plugin-dependency-flow": "0.3.0", "rollup-plugin-dependency-flow": "0.3.0",
"rollup-plugin-sass": "1.2.13", "rollup-plugin-sass": "1.2.13",
"rollup-plugin-terser": "7.0.2", "rollup-plugin-terser": "7.0.2",
"whatwg-fetch": "^3.6.2",
"yargs": "17.5.1" "yargs": "17.5.1"
}, },
"resolutions": { "resolutions": {

1380
yarn.lock

File diff suppressed because it is too large Load Diff