{key}:
@@ -74,17 +73,17 @@ class MetaInfoContent extends Component {
};
getExtraMetaInfo = () => {
- const {testName, apiValues: {extraItems, metaInfoExtenders}} = this.props;
+ const { testName, apiValues: { extraItems, metaInfoExtenders } } = this.props;
return omitBy(mapValues(metaInfoExtenders, (extender) => {
const stringifiedFn = extender.startsWith('return') ? extender : `return ${extender}`;
- return new Function(stringifiedFn)()({testName}, extraItems);
+ return new Function(stringifiedFn)()({ testName }, extraItems);
}), isEmpty);
};
render() {
- const {result, metaInfoBaseUrls, baseHost} = this.props;
+ const { result, metaInfoBaseUrls, baseHost } = this.props;
const serializedMetaValues = serializeMetaValues(result.metaInfo);
const extraMetaInfo = this.getExtraMetaInfo();
@@ -93,7 +92,7 @@ class MetaInfoContent extends Component {
...extraMetaInfo
};
if (result.suiteUrl) {
- formattedMetaInfo.url = mkLinkToUrl(getUrlWithBase(result.suiteUrl, baseHost), result.metaInfo.url);
+ formattedMetaInfo.url = mkTextWithClipboardButton(result.metaInfo.url, getUrlWithBase(result.suiteUrl, baseHost));
}
return metaToElements(formattedMetaInfo, metaInfoBaseUrls);
@@ -101,7 +100,7 @@ class MetaInfoContent extends Component {
}
export default connect(
- ({tree, config: {metaInfoBaseUrls}, apiValues, view}, {resultId}) => {
+ ({ tree, config: { metaInfoBaseUrls }, apiValues, view }, { resultId }) => {
const result = tree.results.byId[resultId];
const browser = tree.browsers.byId[result.parentId];
diff --git a/lib/static/components/section/title/browser.jsx b/lib/static/components/section/title/browser.jsx
index 1810a0484..c61917a90 100644
--- a/lib/static/components/section/title/browser.jsx
+++ b/lib/static/components/section/title/browser.jsx
@@ -3,7 +3,7 @@ import ClipboardButton from 'react-clipboard.js';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
-import {get} from 'lodash';
+import {get, escapeRegExp} from 'lodash';
import * as actions from '../../../modules/actions';
import {appendQuery} from '../../../modules/query-params';
import {ViewMode} from '../../../../constants/view-modes';
@@ -15,8 +15,8 @@ import Bullet from '../../bullet';
const BrowserTitle = (props) => {
const getTestUrl = () => {
return appendQuery(window.location.href, {
- browser: props.browserName,
- testNameFilter: props.testName,
+ browser: escapeRegExp(props.browserName),
+ testNameFilter: escapeRegExp(props.testName),
strictMatchFilter: true,
retryIndex: props.retryIndex,
viewModes: ViewMode.ALL,
diff --git a/lib/static/gui.jsx b/lib/static/gui.jsx
index 2b310da92..41240faf0 100644
--- a/lib/static/gui.jsx
+++ b/lib/static/gui.jsx
@@ -3,12 +3,18 @@ import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from './modules/store';
import Gui from './components/gui';
+import {ThemeProvider} from '@gravity-ui/uikit';
+
+import '@gravity-ui/uikit/styles/fonts.css';
+import '@gravity-ui/uikit/styles/styles.css';
const rootEl = document.getElementById('app');
ReactDOM.render(
-
-
- ,
+
+
+
+
+ ,
rootEl
);
diff --git a/lib/static/index.jsx b/lib/static/index.jsx
index 49429f695..c9bc4ecca 100644
--- a/lib/static/index.jsx
+++ b/lib/static/index.jsx
@@ -3,12 +3,18 @@ import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from './modules/store';
import Report from './components/report';
+import {ThemeProvider} from '@gravity-ui/uikit';
+
+import '@gravity-ui/uikit/styles/fonts.css';
+import '@gravity-ui/uikit/styles/styles.css';
const rootEl = document.getElementById('app');
ReactDOM.render(
-
-
- ,
+
+
+
+
+ ,
rootEl
);
diff --git a/lib/static/styles.css b/lib/static/styles.css
index 66b016d58..fce08b877 100644
--- a/lib/static/styles.css
+++ b/lib/static/styles.css
@@ -530,7 +530,7 @@ details[open] > .details__summary:before {
.meta-info__item {
display: flex;
word-wrap: break-word;
- margin-bottom: 5px;
+ align-items: center;
}
.meta-info__item:last-child {
@@ -546,6 +546,7 @@ details[open] > .details__summary:before {
.meta-info__item-value {
display: grid;
grid-template-columns: repeat(2, auto);
+ align-items: center;
}
.custom-icon_view-local{
@@ -555,6 +556,18 @@ details[open] > .details__summary:before {
text-overflow: ellipsis;
}
+.copy-button {
+ margin-left: 2px;
+}
+
+.meta-info__item .copy-button {
+ opacity: 0;
+}
+
+.meta-info__item:hover .copy-button {
+ opacity: 1;
+}
+
.error {
background: #f0f2f5;
padding: 10px;
diff --git a/lib/test-adapter/hermione.ts b/lib/test-adapter/hermione.ts
deleted file mode 100644
index f8e1d70e2..000000000
--- a/lib/test-adapter/hermione.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export {
- TestplaneTestAdapter as HermioneTestAdapter,
- TestplaneTestAdapterOptions as HermioneTestAdapterOptions,
- getStatus
-} from './testplane';
diff --git a/lib/test-attempt-manager.ts b/lib/test-attempt-manager.ts
index 2b65263e5..98ffcf839 100644
--- a/lib/test-attempt-manager.ts
+++ b/lib/test-attempt-manager.ts
@@ -1,4 +1,4 @@
-import {ReporterTestResult} from './test-adapter';
+import {ReporterTestResult} from './adapters/test-result';
import {IDLE, RUNNING, TestStatus} from './constants';
type TestSpec = Pick
diff --git a/lib/tests-tree-builder/base.ts b/lib/tests-tree-builder/base.ts
index 8da495b23..f45c45b76 100644
--- a/lib/tests-tree-builder/base.ts
+++ b/lib/tests-tree-builder/base.ts
@@ -1,9 +1,9 @@
import _ from 'lodash';
import {determineFinalStatus} from '../common-utils';
import {BrowserVersions, PWT_TITLE_DELIMITER, TESTPLANE_TITLE_DELIMITER, TestStatus, ToolName} from '../constants';
-import {ReporterTestResult} from '../test-adapter';
+import {ReporterTestResult} from '../adapters/test-result';
import {ErrorDetails, ImageInfoFull} from '../types';
-import {TreeTestResultTransformer} from '../test-adapter/transformers/tree';
+import {TreeTestResultTransformer} from '../adapters/test-result/transformers/tree';
import {DbTestResult} from '../sqlite-client';
export type BaseTreeTestResult = Omit & {
diff --git a/lib/tests-tree-builder/static.ts b/lib/tests-tree-builder/static.ts
index a45707829..501886680 100644
--- a/lib/tests-tree-builder/static.ts
+++ b/lib/tests-tree-builder/static.ts
@@ -1,8 +1,8 @@
import _ from 'lodash';
import {BaseTestsTreeBuilder, BaseTestsTreeBuilderOptions, Tree} from './base';
import {BrowserVersions, DB_COLUMN_INDEXES, TestStatus} from '../constants';
-import {ReporterTestResult} from '../test-adapter';
-import {SqliteTestAdapter} from '../test-adapter/sqlite';
+import {ReporterTestResult} from '../adapters/test-result';
+import {SqliteTestResultAdapter} from '../adapters/test-result/sqlite';
import {getTitleDelimiter} from '../common-utils';
import {RawSuitesRow} from '../types';
@@ -70,7 +70,7 @@ export class StaticTestsTreeBuilder extends BaseTestsTreeBuilder {
attemptsMap.set(browserId, attemptsMap.has(browserId) ? attemptsMap.get(browserId) as number + 1 : 0);
const attempt = attemptsMap.get(browserId) as number;
- const formattedResult = new SqliteTestAdapter(row, attempt, {titleDelimiter: getTitleDelimiter(this._toolName)});
+ const formattedResult = new SqliteTestResultAdapter(row, attempt, {titleDelimiter: getTitleDelimiter(this._toolName)});
addBrowserVersion(browsers, formattedResult);
diff --git a/lib/types.ts b/lib/types.ts
index 0f01aa735..a62068095 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -41,6 +41,13 @@ export interface ImageFile {
size: ImageSize;
}
+export interface RefImageFile extends ImageFile {
+ /**
+ * @note defined if testplane >= 8.13.0
+ */
+ relativePath?: string;
+}
+
export interface ImageBuffer {
buffer: Buffer;
}
@@ -59,7 +66,10 @@ export interface DiffOptions extends LooksSameOptions {
export interface TestError {
name: string;
message: string;
- snippet?: string; // defined if testplane >= 8.11.0
+ /**
+ * @note defined if testplane >= 8.11.0
+ */
+ snippet?: string;
stack?: string;
stateName?: string;
details?: ErrorDetails
@@ -69,27 +79,37 @@ export interface TestError {
export interface ImageInfoDiff {
status: TestStatus.FAIL;
stateName: string;
- // Ref image is absent in pwt test results
- refImg?: ImageFile;
+ /**
+ * @note Ref image is absent in pwt test results
+ */
+ refImg?: RefImageFile;
diffClusters?: CoordBounds[];
expectedImg: ImageFile;
actualImg: ImageFile;
diffImg?: ImageFile | ImageBuffer;
diffOptions: DiffOptions;
- differentPixels?: number; // defined if hermione >= 8.2.0
- diffRatio?: number; // defined if hermione >= 8.2.0
+ /**
+ * @note defined if hermione >= 8.2.0
+ */
+ differentPixels?: number;
+ /**
+ * @note defined if hermione >= 8.2.0
+ */
+ diffRatio?: number;
}
interface AssertViewSuccess {
stateName: string;
- refImg: ImageFile;
+ refImg: RefImageFile;
}
export interface ImageInfoSuccess {
status: TestStatus.SUCCESS;
stateName: string;
- // Ref image may be absent in pwt test results
- refImg?: ImageFile;
+ /**
+ * @note Ref image is absent in pwt test results
+ */
+ refImg?: RefImageFile;
diffClusters?: CoordBounds[];
expectedImg: ImageFile;
actualImg?: ImageFile;
@@ -109,15 +129,17 @@ export interface ImageInfoNoRef {
status: TestStatus.ERROR;
error?: TestError;
stateName: string;
- // Ref image may be absent in pwt test results
- refImg?: ImageFile;
+ /**
+ * @note Ref image is absent in pwt test results
+ */
+ refImg?: RefImageFile;
actualImg: ImageFile;
}
export interface ImageInfoUpdated {
status: TestStatus.UPDATED;
stateName: string;
- refImg: ImageFile;
+ refImg: RefImageFile;
actualImg: ImageFile;
expectedImg: ImageFile;
}
diff --git a/package-lock.json b/package-lock.json
index b8a1ec017..148e195a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "html-reporter",
- "version": "9.19.0",
+ "version": "10.3.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "html-reporter",
- "version": "9.18.1",
+ "version": "10.3.1",
"license": "MIT",
"workspaces": [
"test/func/fixtures/*",
@@ -18,7 +18,7 @@
"@gemini-testing/sql.js": "^2.0.0",
"ansi-html-community": "^0.0.8",
"axios": "1.6.3",
- "better-sqlite3": "^8.5.0",
+ "better-sqlite3": "^10.0.0",
"bluebird": "^3.5.3",
"body-parser": "^1.18.2",
"chalk": "^4.1.2",
@@ -46,6 +46,9 @@
"tmp": "^0.1.0",
"worker-farm": "^1.7.0"
},
+ "bin": {
+ "html-reporter": "bin/html-reporter"
+ },
"devDependencies": {
"@babel/core": "^7.22.5",
"@babel/plugin-transform-runtime": "^7.22.5",
@@ -53,7 +56,8 @@
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@gemini-testing/commander": "^2.15.3",
- "@playwright/test": "^1.37.1",
+ "@gravity-ui/uikit": "^6.20.0",
+ "@playwright/test": "^1.44.1",
"@swc/core": "^1.3.64",
"@types/better-sqlite3": "^7.6.4",
"@types/bluebird": "^3.5.3",
@@ -161,10 +165,10 @@
"webpack-merge": "^4.1.1"
},
"engines": {
- "node": ">= 14"
+ "node": ">= 18"
},
"peerDependencies": {
- "hermione": ">=6.0.0",
+ "hermione": ">=8.0.0",
"testplane": "*"
},
"peerDependenciesMeta": {
@@ -2893,6 +2897,12 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "node_modules/@bem-react/classname": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@bem-react/classname/-/classname-1.6.0.tgz",
+ "integrity": "sha512-SFBwUHMcb7TFFK5ld88+JhecoEun3/kHZ6KvLDjj3w5hv/tfRV8mtGHA8N42uMctXLF4bPEcr96xwXXcRFuweg==",
+ "dev": true
+ },
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -3121,6 +3131,85 @@
"resolved": "https://registry.npmjs.org/@gemini-testing/sql.js/-/sql.js-2.0.0.tgz",
"integrity": "sha512-FoslR6S5cxObp0fNtiFkQ6TvGZ5sd7+KomY7Hm3sH51uAHxyzJSQGvpsaiw5dmO/HHYEBF/bJMXHVWEQGfot7A=="
},
+ "node_modules/@gravity-ui/i18n": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/i18n/-/i18n-1.5.1.tgz",
+ "integrity": "sha512-ZvaQtRUf4Yl9zi0+SMzjlDeHp9+p5IXkNu2k6RtW04c+RYKA1jX+umeKNwzft4iR3+KxDlpLX2trTFEW6W7HKQ==",
+ "dev": true
+ },
+ "node_modules/@gravity-ui/icons": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/icons/-/icons-2.10.0.tgz",
+ "integrity": "sha512-xS0G4+TM7cD2cCKS4wVc01c4lLe/OreKjm4sHwrOtJWH4EawaRbpkuwtgUDcUvY2EryIcI6lgV+8o714m6lcyQ==",
+ "dev": true,
+ "peerDependencies": {
+ "react": "*"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@gravity-ui/uikit": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/uikit/-/uikit-6.20.0.tgz",
+ "integrity": "sha512-ngeFZH0CgF+6cNwWTrtwecYkb1Rb7TWFg3eUg87TGLvXiEemepJuwLvM4t9WX3yZFzFCzIoHML2h/SM6VXhjyQ==",
+ "dev": true,
+ "dependencies": {
+ "@bem-react/classname": "^1.6.0",
+ "@gravity-ui/i18n": "^1.3.0",
+ "@gravity-ui/icons": "^2.8.1",
+ "@popperjs/core": "^2.11.8",
+ "blueimp-md5": "^2.19.0",
+ "focus-trap": "^7.5.4",
+ "lodash": "^4.17.21",
+ "rc-slider": "^10.5.0",
+ "react-beautiful-dnd": "^13.1.1",
+ "react-copy-to-clipboard": "^5.1.0",
+ "react-popper": "^2.3.0",
+ "react-transition-group": "^4.4.5",
+ "react-virtualized-auto-sizer": "^1.0.21",
+ "react-window": "^1.8.10",
+ "tabbable": "^6.2.0",
+ "tslib": "^2.6.2"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@gravity-ui/uikit/node_modules/react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "dev": true,
+ "dependencies": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/@gravity-ui/uikit/node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
@@ -3419,12 +3508,12 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
- "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
+ "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
"dev": true,
"dependencies": {
- "playwright": "1.38.1"
+ "playwright": "1.44.1"
},
"bin": {
"playwright": "cli.js"
@@ -3433,6 +3522,16 @@
"node": ">=16"
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@puppeteer/browsers": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz",
@@ -7280,6 +7379,7 @@
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "deprecated": "Use your platform's native atob() and btoa() methods instead",
"dev": true
},
"node_modules/abort-controller": {
@@ -7330,9 +7430,9 @@
}
},
"node_modules/acorn-globals/node_modules/acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
+ "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -7342,10 +7442,13 @@
}
},
"node_modules/acorn-globals/node_modules/acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "version": "8.3.3",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
+ "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
"dev": true,
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
"engines": {
"node": ">=0.4.0"
}
@@ -8504,13 +8607,13 @@
"dev": true
},
"node_modules/better-sqlite3": {
- "version": "8.5.0",
- "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.5.0.tgz",
- "integrity": "sha512-vbPcv/Hx5WYdyNg/NbcfyaBZyv9s/NVbxb7yCeC5Bq1pVocNxeL2tZmSu3Rlm4IEOTjYdGyzWQgyx0OSdORBzw==",
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-10.0.0.tgz",
+ "integrity": "sha512-rOz0JY8bt9oMgrFssP7GnvA5R3yln73y/NizzWqy3WlFth8Ux8+g4r/N9fjX97nn4X1YX6MTER2doNpTu5pqiA==",
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.5.0",
- "prebuild-install": "^7.1.0"
+ "prebuild-install": "^7.1.1"
}
},
"node_modules/big-integer": {
@@ -8630,6 +8733,12 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
+ "node_modules/blueimp-md5": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==",
+ "dev": true
+ },
"node_modules/bn.js": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
@@ -11464,6 +11573,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "dev": true,
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
"node_modules/copyfiles": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz",
@@ -11931,6 +12049,15 @@
"urix": "^0.1.0"
}
},
+ "node_modules/css-box-model": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
+ "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+ "dev": true,
+ "dependencies": {
+ "tiny-invariant": "^1.0.6"
+ }
+ },
"node_modules/css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
@@ -13341,6 +13468,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
"integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "deprecated": "Use your platform's native DOMException instead",
"dev": true,
"dependencies": {
"webidl-conversions": "^7.0.0"
@@ -15427,6 +15555,15 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
+ "node_modules/focus-trap": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz",
+ "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==",
+ "dev": true,
+ "dependencies": {
+ "tabbable": "^6.2.0"
+ }
+ },
"node_modules/follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
@@ -19129,9 +19266,9 @@
}
},
"node_modules/jsdom/node_modules/acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
+ "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -19164,37 +19301,25 @@
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
- "node_modules/jsdom/node_modules/punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "node_modules/jsdom/node_modules/ws": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"dev": true,
"engines": {
- "node": ">=6"
- }
- },
- "node_modules/jsdom/node_modules/tough-cookie": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
- "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
- "dev": true,
- "dependencies": {
- "psl": "^1.1.33",
- "punycode": "^2.1.1",
- "universalify": "^0.2.0",
- "url-parse": "^1.5.3"
+ "node": ">=10.0.0"
},
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/jsdom/node_modules/universalify": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
- "dev": true,
- "engines": {
- "node": ">= 4.0.0"
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
}
},
"node_modules/jsesc": {
@@ -20036,6 +20161,12 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/memoize-one": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
+ "dev": true
+ },
"node_modules/memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -21330,9 +21461,9 @@
}
},
"node_modules/nwsapi": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
- "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz",
+ "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
"dev": true
},
"node_modules/nyc": {
@@ -22398,12 +22529,12 @@
}
},
"node_modules/playwright": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
- "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
+ "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
"dev": true,
"dependencies": {
- "playwright-core": "1.38.1"
+ "playwright-core": "1.44.1"
},
"bin": {
"playwright": "cli.js"
@@ -22416,9 +22547,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
- "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
+ "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -23408,9 +23539,9 @@
"dev": true
},
"node_modules/psl": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
- "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
"node_modules/public-encrypt": {
@@ -23579,6 +23710,12 @@
"performance-now": "^2.1.0"
}
},
+ "node_modules/raf-schd": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
+ "dev": true
+ },
"node_modules/railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
@@ -23670,21 +23807,44 @@
"react-dom": ">=16.9.0"
}
},
- "node_modules/rc-resize-observer/node_modules/rc-util": {
- "version": "5.15.0",
- "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.15.0.tgz",
- "integrity": "sha512-8RI8sjOCXD3FhD3dzQNBQetpGol6BBd3sHQ/8jSGk9NPT0CH3JGtBfPODnASyE7AdDpCFQMOmgcp9CBs3S/1hg==",
+ "node_modules/rc-slider": {
+ "version": "10.6.2",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.6.2.tgz",
+ "integrity": "sha512-FjkoFjyvUQWcBo1F3RgSglky3ar0+qHLM41PlFVYB4Bj3RD8E/Mv7kqMouLFBU+3aFglMzzctAIWRwajEuueSw==",
"dev": true,
"dependencies": {
- "@babel/runtime": "^7.12.5",
- "react-is": "^16.12.0",
- "shallowequal": "^1.1.0"
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
}
},
+ "node_modules/rc-util": {
+ "version": "5.43.0",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz",
+ "integrity": "sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "dev": true
+ },
"node_modules/react": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
@@ -23699,6 +23859,25 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-beautiful-dnd": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
+ "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.9.2",
+ "css-box-model": "^1.2.0",
+ "memoize-one": "^5.1.1",
+ "raf-schd": "^4.0.2",
+ "react-redux": "^7.2.0",
+ "redux": "^4.0.4",
+ "use-memo-one": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.5 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-checkbox-tree": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/react-checkbox-tree/-/react-checkbox-tree-1.8.0.tgz",
@@ -23727,6 +23906,19 @@
"react": ">=15.5.0"
}
},
+ "node_modules/react-copy-to-clipboard": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
+ "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
+ "dev": true,
+ "dependencies": {
+ "copy-to-clipboard": "^3.3.1",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": "^15.3.0 || 16 || 17 || 18"
+ }
+ },
"node_modules/react-dom": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
@@ -23775,6 +23967,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "dev": true
+ },
"node_modules/react-hotkeys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-hotkeys/-/react-hotkeys-2.0.0.tgz",
@@ -24086,6 +24284,33 @@
"react-dom": "^15.3.0 || ^16.0.0-alpha"
}
},
+ "node_modules/react-virtualized-auto-sizer": {
+ "version": "1.0.24",
+ "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz",
+ "integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==",
+ "dev": true,
+ "peerDependencies": {
+ "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0",
+ "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-window": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
+ "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.0.0",
+ "memoize-one": ">=3.1.1 <6"
+ },
+ "engines": {
+ "node": ">8.0.0"
+ },
+ "peerDependencies": {
+ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/read-only-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
@@ -27078,6 +27303,12 @@
"acorn-node": "^1.2.0"
}
},
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
+ "dev": true
+ },
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -27776,6 +28007,12 @@
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"dev": true
},
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "dev": true
+ },
"node_modules/tmp": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
@@ -27858,6 +28095,12 @@
"node": ">=8.0"
}
},
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "dev": true
+ },
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
@@ -27866,6 +28109,39 @@
"node": ">=0.6"
}
},
+ "node_modules/tough-cookie": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tough-cookie/node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tough-cookie/node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/tr46": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
@@ -27879,9 +28155,9 @@
}
},
"node_modules/tr46/node_modules/punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"engines": {
"node": ">=6"
@@ -28016,9 +28292,9 @@
}
},
"node_modules/tslib": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
- "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -28757,6 +29033,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/use-memo-one": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+ "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
+ "dev": true,
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/userhome": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz",
@@ -33420,6 +33705,12 @@
}
}
},
+ "@bem-react/classname": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@bem-react/classname/-/classname-1.6.0.tgz",
+ "integrity": "sha512-SFBwUHMcb7TFFK5ld88+JhecoEun3/kHZ6KvLDjj3w5hv/tfRV8mtGHA8N42uMctXLF4bPEcr96xwXXcRFuweg==",
+ "dev": true
+ },
"@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@@ -33588,6 +33879,67 @@
"resolved": "https://registry.npmjs.org/@gemini-testing/sql.js/-/sql.js-2.0.0.tgz",
"integrity": "sha512-FoslR6S5cxObp0fNtiFkQ6TvGZ5sd7+KomY7Hm3sH51uAHxyzJSQGvpsaiw5dmO/HHYEBF/bJMXHVWEQGfot7A=="
},
+ "@gravity-ui/i18n": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/i18n/-/i18n-1.5.1.tgz",
+ "integrity": "sha512-ZvaQtRUf4Yl9zi0+SMzjlDeHp9+p5IXkNu2k6RtW04c+RYKA1jX+umeKNwzft4iR3+KxDlpLX2trTFEW6W7HKQ==",
+ "dev": true
+ },
+ "@gravity-ui/icons": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/icons/-/icons-2.10.0.tgz",
+ "integrity": "sha512-xS0G4+TM7cD2cCKS4wVc01c4lLe/OreKjm4sHwrOtJWH4EawaRbpkuwtgUDcUvY2EryIcI6lgV+8o714m6lcyQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "@gravity-ui/uikit": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/uikit/-/uikit-6.20.0.tgz",
+ "integrity": "sha512-ngeFZH0CgF+6cNwWTrtwecYkb1Rb7TWFg3eUg87TGLvXiEemepJuwLvM4t9WX3yZFzFCzIoHML2h/SM6VXhjyQ==",
+ "dev": true,
+ "requires": {
+ "@bem-react/classname": "^1.6.0",
+ "@gravity-ui/i18n": "^1.3.0",
+ "@gravity-ui/icons": "^2.8.1",
+ "@popperjs/core": "^2.11.8",
+ "blueimp-md5": "^2.19.0",
+ "focus-trap": "^7.5.4",
+ "lodash": "^4.17.21",
+ "rc-slider": "^10.5.0",
+ "react-beautiful-dnd": "^13.1.1",
+ "react-copy-to-clipboard": "^5.1.0",
+ "react-popper": "^2.3.0",
+ "react-transition-group": "^4.4.5",
+ "react-virtualized-auto-sizer": "^1.0.21",
+ "react-window": "^1.8.10",
+ "tabbable": "^6.2.0",
+ "tslib": "^2.6.2"
+ },
+ "dependencies": {
+ "react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "dev": true,
+ "requires": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ }
+ },
+ "react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ }
+ }
+ }
+ },
"@humanwhocodes/config-array": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
@@ -33813,14 +34165,20 @@
"optional": true
},
"@playwright/test": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
- "integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
+ "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
"dev": true,
"requires": {
- "playwright": "1.38.1"
+ "playwright": "1.44.1"
}
},
+ "@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "dev": true
+ },
"@puppeteer/browsers": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz",
@@ -36785,16 +37143,19 @@
},
"dependencies": {
"acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
+ "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"dev": true
},
"acorn-walk": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
- "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
- "dev": true
+ "version": "8.3.3",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
+ "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.11.0"
+ }
}
}
},
@@ -37674,12 +38035,12 @@
"dev": true
},
"better-sqlite3": {
- "version": "8.5.0",
- "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.5.0.tgz",
- "integrity": "sha512-vbPcv/Hx5WYdyNg/NbcfyaBZyv9s/NVbxb7yCeC5Bq1pVocNxeL2tZmSu3Rlm4IEOTjYdGyzWQgyx0OSdORBzw==",
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-10.0.0.tgz",
+ "integrity": "sha512-rOz0JY8bt9oMgrFssP7GnvA5R3yln73y/NizzWqy3WlFth8Ux8+g4r/N9fjX97nn4X1YX6MTER2doNpTu5pqiA==",
"requires": {
"bindings": "^1.5.0",
- "prebuild-install": "^7.1.0"
+ "prebuild-install": "^7.1.1"
}
},
"big-integer": {
@@ -37761,6 +38122,12 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
+ "blueimp-md5": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==",
+ "dev": true
+ },
"bn.js": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
@@ -40050,6 +40417,15 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true
},
+ "copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "dev": true,
+ "requires": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
"copyfiles": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz",
@@ -40432,6 +40808,15 @@
}
}
},
+ "css-box-model": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
+ "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+ "dev": true,
+ "requires": {
+ "tiny-invariant": "^1.0.6"
+ }
+ },
"css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
@@ -43092,6 +43477,15 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
+ "focus-trap": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz",
+ "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==",
+ "dev": true,
+ "requires": {
+ "tabbable": "^6.2.0"
+ }
+ },
"follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
@@ -45895,9 +46289,9 @@
},
"dependencies": {
"acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
+ "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"dev": true
},
"entities": {
@@ -45915,29 +46309,12 @@
"entities": "^4.4.0"
}
},
- "punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
- "dev": true
- },
- "tough-cookie": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
- "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
+ "ws": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"dev": true,
- "requires": {
- "psl": "^1.1.33",
- "punycode": "^2.1.1",
- "universalify": "^0.2.0",
- "url-parse": "^1.5.3"
- }
- },
- "universalify": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
- "dev": true
+ "requires": {}
}
}
},
@@ -46629,6 +47006,12 @@
"fs-monkey": "^1.0.4"
}
},
+ "memoize-one": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+ "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
+ "dev": true
+ },
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -47639,9 +48022,9 @@
"dev": true
},
"nwsapi": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
- "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz",
+ "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
"dev": true
},
"nyc": {
@@ -48469,19 +48852,19 @@
}
},
"playwright": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
- "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
+ "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
- "playwright-core": "1.38.1"
+ "playwright-core": "1.44.1"
}
},
"playwright-core": {
- "version": "1.38.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
- "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
+ "version": "1.44.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
+ "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
"dev": true
},
"playwright-fixture-report": {
@@ -49245,9 +49628,9 @@
"dev": true
},
"psl": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
- "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
"public-encrypt": {
@@ -49373,6 +49756,12 @@
"performance-now": "^2.1.0"
}
},
+ "raf-schd": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+ "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==",
+ "dev": true
+ },
"railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
@@ -49446,18 +49835,34 @@
"classnames": "^2.2.1",
"rc-util": "^5.0.0",
"resize-observer-polyfill": "^1.5.1"
+ }
+ },
+ "rc-slider": {
+ "version": "10.6.2",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.6.2.tgz",
+ "integrity": "sha512-FjkoFjyvUQWcBo1F3RgSglky3ar0+qHLM41PlFVYB4Bj3RD8E/Mv7kqMouLFBU+3aFglMzzctAIWRwajEuueSw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.36.0"
+ }
+ },
+ "rc-util": {
+ "version": "5.43.0",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.43.0.tgz",
+ "integrity": "sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^18.2.0"
},
"dependencies": {
- "rc-util": {
- "version": "5.15.0",
- "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.15.0.tgz",
- "integrity": "sha512-8RI8sjOCXD3FhD3dzQNBQetpGol6BBd3sHQ/8jSGk9NPT0CH3JGtBfPODnASyE7AdDpCFQMOmgcp9CBs3S/1hg==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.12.5",
- "react-is": "^16.12.0",
- "shallowequal": "^1.1.0"
- }
+ "react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "dev": true
}
}
},
@@ -49472,6 +49877,21 @@
"prop-types": "^15.6.2"
}
},
+ "react-beautiful-dnd": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
+ "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.9.2",
+ "css-box-model": "^1.2.0",
+ "memoize-one": "^5.1.1",
+ "raf-schd": "^4.0.2",
+ "react-redux": "^7.2.0",
+ "redux": "^4.0.4",
+ "use-memo-one": "^1.1.1"
+ }
+ },
"react-checkbox-tree": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/react-checkbox-tree/-/react-checkbox-tree-1.8.0.tgz",
@@ -49494,6 +49914,16 @@
"prop-types": "^15.5.0"
}
},
+ "react-copy-to-clipboard": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
+ "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==",
+ "dev": true,
+ "requires": {
+ "copy-to-clipboard": "^3.3.1",
+ "prop-types": "^15.8.1"
+ }
+ },
"react-dom": {
"version": "16.14.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
@@ -49532,6 +49962,12 @@
}
}
},
+ "react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "dev": true
+ },
"react-hotkeys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-hotkeys/-/react-hotkeys-2.0.0.tgz",
@@ -49784,6 +50220,23 @@
"react-lifecycles-compat": "^3.0.4"
}
},
+ "react-virtualized-auto-sizer": {
+ "version": "1.0.24",
+ "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz",
+ "integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==",
+ "dev": true,
+ "requires": {}
+ },
+ "react-window": {
+ "version": "1.8.10",
+ "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
+ "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "memoize-one": ">=3.1.1 <6"
+ }
+ },
"read-only-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
@@ -52188,6 +52641,12 @@
"acorn-node": "^1.2.0"
}
},
+ "tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
+ "dev": true
+ },
"tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -52736,6 +53195,12 @@
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"dev": true
},
+ "tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "dev": true
+ },
"tmp": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
@@ -52802,11 +53267,43 @@
"is-number": "^7.0.0"
}
},
+ "toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "dev": true
+ },
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
+ "tough-cookie": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true
+ },
+ "universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true
+ }
+ }
+ },
"tr46": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
@@ -52817,9 +53314,9 @@
},
"dependencies": {
"punycode": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
- "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true
}
}
@@ -52905,9 +53402,9 @@
}
},
"tslib": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
- "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
},
"tsutils": {
"version": "3.21.0",
@@ -53462,6 +53959,13 @@
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
"dev": true
},
+ "use-memo-one": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+ "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
+ "dev": true,
+ "requires": {}
+ },
"userhome": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.0.tgz",
diff --git a/package.json b/package.json
index c5e6b0f62..5c66a0dd7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "html-reporter",
- "version": "9.19.0",
+ "version": "10.3.2",
"description": "Html-reporter and GUI for viewing and managing results of a tests run. Currently supports Testplane and Hermione.",
"files": [
"build"
@@ -17,10 +17,12 @@
"copy-static": "copyfiles 'lib/static/icons/*' .npmignore build",
"check-types": "tsc --project tsconfig.spec.json",
"coverage": "nyc npm run test-unit",
- "e2e:build-browsers": "docker build -f test/func/docker/Dockerfile -t html-reporter-browsers:0.0.1 --network host test/func/docker",
+ "browsers:build:local": "PLATFORM=$([ $(node -e 'console.log(process.arch)') = 'arm64' ] && echo linux/arm64 || echo linux/amd64) npm run browsers:build:single-platform",
+ "browsers:build:single-platform": "docker build -f test/func/docker/Dockerfile --platform $PLATFORM -t yinfra/html-reporter-browsers test/func/docker --load",
+ "browsers:build-and-push": "docker buildx build -t yinfra/html-reporter-browsers --platform linux/amd64,linux/arm64 test/func/docker --push",
+ "browsers:launch": "docker run -it --rm --network=host $(which colima >/dev/null || echo --add-host=host.docker.internal:0.0.0.0) yinfra/html-reporter-browsers",
"e2e:build-packages": "npm run --workspace=test/func/packages --if-present build",
"e2e:generate-fixtures": "npm run --workspace=test/func/fixtures generate",
- "e2e:launch-browsers": "docker run -it --rm --network=host --add-host=host.docker.internal:0.0.0.0 html-reporter-browsers:0.0.1",
"e2e:test": "npm run --workspace=test/func/tests test",
"e2e": "npm run e2e:build-packages && npm run e2e:generate-fixtures ; npm run e2e:test",
"lint": "eslint .",
@@ -48,7 +50,7 @@
"url": "https://github.com/gemini-testing/html-reporter/issues"
},
"engines": {
- "node": ">= 14"
+ "node": ">= 18"
},
"keywords": [
"testplane",
@@ -56,10 +58,13 @@
"plugin",
"html-reporter"
],
+ "bin": {
+ "html-reporter": "./bin/html-reporter"
+ },
"homepage": "https://github.com/gemini-testing/html-reporter#readme",
"license": "MIT",
"peerDependencies": {
- "hermione": ">=6.0.0",
+ "hermione": ">=8.0.0",
"testplane": "*"
},
"peerDependenciesMeta": {
@@ -75,7 +80,7 @@
"@gemini-testing/sql.js": "^2.0.0",
"ansi-html-community": "^0.0.8",
"axios": "1.6.3",
- "better-sqlite3": "^8.5.0",
+ "better-sqlite3": "^10.0.0",
"bluebird": "^3.5.3",
"body-parser": "^1.18.2",
"chalk": "^4.1.2",
@@ -110,7 +115,8 @@
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@gemini-testing/commander": "^2.15.3",
- "@playwright/test": "^1.37.1",
+ "@gravity-ui/uikit": "^6.20.0",
+ "@playwright/test": "^1.44.1",
"@swc/core": "^1.3.64",
"@types/better-sqlite3": "^7.6.4",
"@types/bluebird": "^3.5.3",
diff --git a/playwright.ts b/playwright.ts
index 3efc83ab1..9f7c42e5a 100644
--- a/playwright.ts
+++ b/playwright.ts
@@ -12,7 +12,7 @@ import {ReporterConfig, TestSpecByPath} from './lib/types';
import {parseConfig} from './lib/config';
import {PluginEvents, ToolName, UNKNOWN_ATTEMPT} from './lib/constants';
import {RegisterWorkers} from './lib/workers/create-workers';
-import {PlaywrightTestAdapter} from './lib/test-adapter/playwright';
+import {PlaywrightTestResultAdapter} from './lib/adapters/test-result/playwright';
import {SqliteClient} from './lib/sqlite-client';
import {SqliteImageStore} from './lib/image-store';
import {ImagesInfoSaver} from './lib/images-info-saver';
@@ -31,7 +31,9 @@ class MyReporter implements Reporter {
protected _initPromise: Promise;
constructor(opts: Partial) {
- this._config = parseConfig(_.omit(opts, ['configDir']));
+ const reporterOpts = _.omitBy(opts, (_value, key) => key === 'configDir' || key.startsWith('_'));
+
+ this._config = parseConfig(reporterOpts);
this._htmlReporter = HtmlReporter.create(this._config, {toolName: ToolName.Playwright});
this._staticReportBuilder = null;
this._workerFarm = workerFarm(require.resolve('./lib/workers/worker'), ['saveDiffTo']);
@@ -68,7 +70,7 @@ class MyReporter implements Reporter {
const staticReportBuilder = this._staticReportBuilder as StaticReportBuilder;
- const formattedResult = new PlaywrightTestAdapter(test, result, UNKNOWN_ATTEMPT);
+ const formattedResult = new PlaywrightTestResultAdapter(test, result, UNKNOWN_ATTEMPT);
await staticReportBuilder.addTestResult(formattedResult);
});
diff --git a/test/func/common.testplane.conf.js b/test/func/common.testplane.conf.js
index ee3e07390..0ffee03fa 100644
--- a/test/func/common.testplane.conf.js
+++ b/test/func/common.testplane.conf.js
@@ -13,7 +13,7 @@ module.exports.getCommonConfig = (projectDir) => ({
desiredCapabilities: {
browserName: 'chrome',
'goog:chromeOptions': {
- args: ['headless', 'no-sandbox', 'hide-scrollbars'],
+ args: ['headless', 'no-sandbox', 'hide-scrollbars', 'disable-dev-shm-usage'],
binary: CHROME_BINARY_PATH
}
},
diff --git a/test/func/docker/Dockerfile b/test/func/docker/Dockerfile
index e65354f82..412272c94 100644
--- a/test/func/docker/Dockerfile
+++ b/test/func/docker/Dockerfile
@@ -1,15 +1,28 @@
-FROM cimg/node:16.20-browsers
+FROM node:18-slim
-ENV CHROME_VERSION=116
+ARG DEBIAN_FRONTEND=noninteractive
-USER circleci
+ARG BUILDPLATFORM
-COPY --chown=circleci browser-utils browser-utils
-WORKDIR browser-utils
+ENV CHROMIUM_VERSION="126.0.6478.126-1~deb12u1"
+ENV ARM_CHROMEDRIVER_URL="https://github.com/electron/electron/releases/download/v31.1.0/chromedriver-v31.1.0-linux-arm64.zip"
+ENV ARM_CHROMEDRIVER_RELATIVE_PATH=""
+ENV AMD_CHROMEDRIVER_URL="https://storage.googleapis.com/chrome-for-testing-public/126.0.6478.126/linux64/chromedriver-linux64.zip"
+ENV AMD_CHROMEDRIVER_RELATIVE_PATH="/chromedriver-linux64"
-RUN npm ci && npm run install-chromium
+RUN if [ "$BUILDPLATFORM" = "linux/arm64" ]; then export RELATIVE_PATH="$ARM_CHROMEDRIVER_RELATIVE_PATH"; export URL="$ARM_CHROMEDRIVER_URL"; \
+ else export RELATIVE_PATH="$AMD_CHROMEDRIVER_RELATIVE_PATH"; export URL="$AMD_CHROMEDRIVER_URL"; fi && \
+ apt-get update -y && \
+ apt-get install -y wget unzip git && \
+ wget "$URL" -O "chromedriver.zip" && \
+ unzip "chromedriver" && \
+ if [ "$RELATIVE_PATH" != "" ]; then mv "$RELATIVE_PATH/chromedriver" "/chromedriver"; fi && \
+ apt update -y && \
+ apt install libnss3 -y && \
+ rm chromedriver.zip && \
+ (rm chromedriver.debug || true) && \
+ apt-get install chromium="$CHROMIUM_VERSION" -y
-RUN npm install selenium-standalone@9.1.1 -g && \
- selenium-standalone install --drivers.chrome.version=$CHROME_VERSION
+LABEL com.circleci.preserve-entrypoint=true
-ENTRYPOINT selenium-standalone start --drivers.chrome.version=$CHROME_VERSION
+ENTRYPOINT ./chromedriver --port=4444 --whitelisted-ips=""
diff --git a/test/func/fixtures/plugins/screens/eea1754/chrome/header.png b/test/func/fixtures/plugins/screens/eea1754/chrome/header.png
index bf8ebcf91..236088d2e 100644
Binary files a/test/func/fixtures/plugins/screens/eea1754/chrome/header.png and b/test/func/fixtures/plugins/screens/eea1754/chrome/header.png differ
diff --git a/test/func/tests/screens/c0db305/chrome/details summary.png b/test/func/tests/screens/c0db305/chrome/details summary.png
index cbf9826f5..9b727d925 100644
Binary files a/test/func/tests/screens/c0db305/chrome/details summary.png and b/test/func/tests/screens/c0db305/chrome/details summary.png differ
diff --git a/test/func/tests/utils.js b/test/func/tests/utils.js
index 980516b61..f918d6591 100644
--- a/test/func/tests/utils.js
+++ b/test/func/tests/utils.js
@@ -34,7 +34,7 @@ const hideScreenshots = async (browser) => {
const runGui = async (projectDir) => {
return new Promise((resolve, reject) => {
- const child = childProcess.spawn('npm', ['run', 'gui'], {cwd: projectDir});
+ const child = childProcess.spawn('npm', ['run', 'gui', '--', '--no-open'], {cwd: projectDir});
let processKillTimeoutId = setTimeout(() => {
treeKill(child.pid).then(() => {
diff --git a/test/func/utils/constants.js b/test/func/utils/constants.js
index a5c675abb..cbeb6b47e 100644
--- a/test/func/utils/constants.js
+++ b/test/func/utils/constants.js
@@ -1,6 +1,6 @@
module.exports = {
- GRID_URL: 'http://localhost:4444/wd/hub/',
- CHROME_BINARY_PATH: '/home/circleci/browsers/chrome-linux/chrome',
+ GRID_URL: 'http://127.0.0.1:4444/',
+ CHROME_BINARY_PATH: '/usr/bin/chromium',
PORTS: {
testplane: {
server: 8083,
diff --git a/test/unit/lib/test-adapter/playwright.ts b/test/unit/lib/adapters/test-result/playwright.ts
similarity index 76%
rename from test/unit/lib/test-adapter/playwright.ts
rename to test/unit/lib/adapters/test-result/playwright.ts
index fa1afac99..139d72880 100644
--- a/test/unit/lib/test-adapter/playwright.ts
+++ b/test/unit/lib/adapters/test-result/playwright.ts
@@ -7,14 +7,14 @@ import {
ImageTitleEnding,
PlaywrightAttachment,
PwtTestStatus
-} from 'lib/test-adapter/playwright';
+} from 'lib/adapters/test-result/playwright';
import {ErrorName} from 'lib/errors';
import {ERROR, FAIL, TestStatus, UNKNOWN_ATTEMPT} from 'lib/constants';
import {ImageInfoDiff, ImageInfoNoRef} from 'lib/types';
-describe('PlaywrightTestAdapter', () => {
+describe('PlaywrightTestResultAdapter', () => {
let sandbox: sinon.SinonSandbox;
- let PlaywrightTestAdapter: typeof import('lib/test-adapter/playwright').PlaywrightTestAdapter;
+ let PlaywrightTestResultAdapter: typeof import('lib/adapters/test-result/playwright').PlaywrightTestResultAdapter;
let imageSizeStub: sinon.SinonStub;
const createAttachment = (path: string): PlaywrightAttachment => ({
@@ -46,9 +46,9 @@ describe('PlaywrightTestAdapter', () => {
imageSizeStub = sinon.stub().returns({height: 100, width: 200});
- PlaywrightTestAdapter = proxyquire('lib/test-adapter/playwright', {
+ PlaywrightTestResultAdapter = proxyquire('lib/adapters/test-result/playwright', {
'image-size': imageSizeStub
- }).PlaywrightTestAdapter;
+ }).PlaywrightTestResultAdapter;
});
afterEach(() => {
@@ -57,7 +57,7 @@ describe('PlaywrightTestAdapter', () => {
describe('attempt', () => {
it('should return suite attempt', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase({titlePath: sinon.stub().returns(['another-title'])}), mkTestResult(), 3);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase({titlePath: sinon.stub().returns(['another-title'])}), mkTestResult(), 3);
assert.equal(adapter.attempt, 3);
});
@@ -65,7 +65,7 @@ describe('PlaywrightTestAdapter', () => {
describe('browserId', () => {
it('should return browserId', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
assert.equal(adapter.browserId, 'some-browser');
});
@@ -73,7 +73,7 @@ describe('PlaywrightTestAdapter', () => {
describe('error', () => {
it('should return undefined if there are no errors', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({errors: []}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({errors: []}), UNKNOWN_ATTEMPT);
const {error} = adapter;
@@ -83,7 +83,7 @@ describe('PlaywrightTestAdapter', () => {
it('should return an error with name NO_REF_IMAGE for snapshot missing errors', () => {
const errorMessage = 'A snapshot doesn\'t exist: image-name.png.';
const errors = [{message: errorMessage}];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
const {error} = adapter;
@@ -94,7 +94,7 @@ describe('PlaywrightTestAdapter', () => {
it('should return an error with name IMAGE_DIFF for screenshot comparison failures', () => {
const errorMessage = 'Screenshot comparison failed';
const errors = [{message: errorMessage}];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
const {error} = adapter;
@@ -106,7 +106,7 @@ describe('PlaywrightTestAdapter', () => {
const errorMessage = 'Some error occurred';
const errorStack = 'Error: Some error occurred at some-file.ts:10:15';
const errors = [{message: errorMessage, stack: errorStack}];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
const {error} = adapter;
@@ -118,7 +118,7 @@ describe('PlaywrightTestAdapter', () => {
{message: 'First error', stack: 'Error: First error at some-file.ts:5:10'},
{message: 'Second error', stack: 'Error: Second error at another-file.ts:15:20'}
];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({errors}), UNKNOWN_ATTEMPT);
const expectedMessage = JSON.stringify(errors.map(err => err.message));
const expectedStack = JSON.stringify(errors.map(err => err.stack));
@@ -131,7 +131,7 @@ describe('PlaywrightTestAdapter', () => {
describe('file', () => {
it('should return file path', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
assert.strictEqual(adapter.file, 'test-file-path');
});
@@ -139,7 +139,7 @@ describe('PlaywrightTestAdapter', () => {
describe('fullName', () => {
it('should return fullName', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
assert.strictEqual(adapter.fullName, 'describe › test');
});
@@ -151,7 +151,7 @@ describe('PlaywrightTestAdapter', () => {
{title: 'Step1', duration: 100},
{title: 'Step2', duration: 200}
];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({steps} as any), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({steps} as any), UNKNOWN_ATTEMPT);
const expectedHistory = ['Step1 <- 100ms\n', 'Step2 <- 200ms\n'];
assert.deepEqual(adapter.history, expectedHistory);
@@ -160,7 +160,7 @@ describe('PlaywrightTestAdapter', () => {
describe('id', () => {
it('should return id', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult(), 0);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult(), 0);
assert.strictEqual(adapter.id, 'describe test some-browser 0');
});
@@ -168,7 +168,7 @@ describe('PlaywrightTestAdapter', () => {
describe('imageDir', () => {
it('should return imageDir', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
assert.strictEqual(adapter.imageDir, '4050de5');
});
@@ -184,7 +184,7 @@ describe('PlaywrightTestAdapter', () => {
{name: 'screenshot', path: 'test-results/test-name-1.png', contentType: 'image/png'}
];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({attachments, errors}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({attachments, errors}), UNKNOWN_ATTEMPT);
assert.equal(adapter.imagesInfo.length, 2);
assert.deepEqual(adapter.imagesInfo.find(info => (info as ImageInfoDiff).stateName === undefined), {
@@ -209,7 +209,7 @@ describe('PlaywrightTestAdapter', () => {
{name: 'screenshot', path: 'test-results/test-name-1.png', contentType: 'image/png'}
];
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({attachments, errors}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({attachments, errors}), UNKNOWN_ATTEMPT);
assert.equal(adapter.imagesInfo.length, 2);
assert.deepEqual(adapter.imagesInfo.find(info => (info as ImageInfoNoRef).stateName === undefined), {
@@ -231,31 +231,31 @@ describe('PlaywrightTestAdapter', () => {
describe('status', () => {
it('should return SUCCESS for PASSED PwtTestStatus', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.PASSED}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.PASSED}), UNKNOWN_ATTEMPT);
assert.equal(adapter.status, TestStatus.SUCCESS);
});
it('should return FAIL for FAILED PwtTestStatus', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.FAILED}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.FAILED}), UNKNOWN_ATTEMPT);
assert.equal(adapter.status, TestStatus.FAIL);
});
it('should return FAIL for TIMED_OUT PwtTestStatus', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.TIMED_OUT}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.TIMED_OUT}), UNKNOWN_ATTEMPT);
assert.equal(adapter.status, TestStatus.FAIL);
});
it('should return FAIL for INTERRUPTED PwtTestStatus', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.INTERRUPTED}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.INTERRUPTED}), UNKNOWN_ATTEMPT);
assert.equal(adapter.status, TestStatus.FAIL);
});
it('should return SKIPPED for any other PwtTestStatus', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.SKIPPED}), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult({status: PwtTestStatus.SKIPPED}), UNKNOWN_ATTEMPT);
assert.equal(adapter.status, TestStatus.SKIPPED);
});
@@ -263,7 +263,7 @@ describe('PlaywrightTestAdapter', () => {
describe('testPath', () => {
it('should return testPath', () => {
- const adapter = new PlaywrightTestAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
+ const adapter = new PlaywrightTestResultAdapter(mkTestCase(), mkTestResult(), UNKNOWN_ATTEMPT);
assert.deepEqual(adapter.testPath, ['describe', 'test']);
});
diff --git a/test/unit/lib/test-adapter/testplane.ts b/test/unit/lib/adapters/test-result/testplane.ts
similarity index 74%
rename from test/unit/lib/test-adapter/testplane.ts
rename to test/unit/lib/adapters/test-result/testplane.ts
index 3ac134533..2144558e0 100644
--- a/test/unit/lib/test-adapter/testplane.ts
+++ b/test/unit/lib/adapters/test-result/testplane.ts
@@ -6,17 +6,18 @@ import tmpOriginal from 'tmp';
import {TestStatus} from 'lib/constants/test-statuses';
import {ERROR_DETAILS_PATH} from 'lib/constants/paths';
-import {ReporterTestResult} from 'lib/test-adapter';
-import {TestplaneTestAdapter, TestplaneTestAdapterOptions} from 'lib/test-adapter/testplane';
+import {TestplaneTestResultAdapter, TestplaneTestResultAdapterOptions} from 'lib/adapters/test-result/testplane';
import {TestplaneTestResult} from 'lib/types';
import * as originalUtils from 'lib/server-utils';
import * as originalCommonUtils from 'lib/common-utils';
-import * as originalTestAdapterUtils from 'lib/test-adapter/utils';
+import * as originalTestAdapterUtils from 'lib/adapters/test-result/utils';
-describe('TestplaneTestAdapter', () => {
+import type {ReporterTestResult} from 'lib/adapters/test-result';
+
+describe('TestplaneTestResultAdapter', () => {
const sandbox = sinon.sandbox.create();
- let TestplaneTestAdapter: new (testResult: TestplaneTestResult, options: TestplaneTestAdapterOptions) => ReporterTestResult;
+ let TestplaneTestResultAdapter: new (testResult: TestplaneTestResult, options: TestplaneTestResultAdapterOptions) => ReporterTestResult;
let getCommandsHistory: sinon.SinonStub;
let getSuitePath: sinon.SinonStub;
let utils: sinon.SinonStubbedInstance;
@@ -28,8 +29,8 @@ describe('TestplaneTestAdapter', () => {
const mkTestplaneTestResultAdapter = (
testResult: TestplaneTestResult,
{status = TestStatus.SUCCESS}: {status?: TestStatus} = {}
- ): TestplaneTestAdapter => {
- return new TestplaneTestAdapter(testResult, {status, attempt: 0}) as TestplaneTestAdapter;
+ ): TestplaneTestResultAdapter => {
+ return new TestplaneTestResultAdapter(testResult, {status, attempt: 0}) as TestplaneTestResultAdapter;
};
const mkTestResult_ = (result: Partial): TestplaneTestResult => _.defaults(result, {
@@ -51,20 +52,19 @@ describe('TestplaneTestAdapter', () => {
const originalCommonUtils = proxyquire('lib/common-utils', {});
commonUtils = _.clone(originalCommonUtils);
- const originalTestAdapterUtils = proxyquire('lib/test-adapter/utils', {
- '../../server-utils': utils,
- '../../common-utils': commonUtils
+ const originalTestAdapterUtils = proxyquire('lib/adapters/test-result/utils', {
+ '../../../common-utils': commonUtils
});
testAdapterUtils = _.clone(originalTestAdapterUtils);
- TestplaneTestAdapter = proxyquire('lib/test-adapter/testplane', {
+ TestplaneTestResultAdapter = proxyquire('lib/adapters/test-result/testplane', {
tmp,
'fs-extra': fs,
- '../plugin-utils': {getSuitePath},
- '../history-utils': {getCommandsHistory},
+ '../../plugin-utils': {getSuitePath},
+ '../../history-utils': {getCommandsHistory},
'../server-utils': utils,
'./utils': testAdapterUtils
- }).TestplaneTestAdapter;
+ }).TestplaneTestResultAdapter;
sandbox.stub(utils, 'getCurrentPath').returns('');
sandbox.stub(utils, 'getDiffPath').returns('');
sandbox.stub(utils, 'getReferencePath').returns('');
@@ -89,9 +89,9 @@ describe('TestplaneTestAdapter', () => {
} as any
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.error, {
+ assert.deepEqual(TestplaneTestResultAdapter.error, {
message: 'some-message',
stack: 'some-stack',
stateName: 'some-test',
@@ -112,17 +112,17 @@ describe('TestplaneTestAdapter', () => {
} as any
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.history, ['some-history']);
+ assert.deepEqual(TestplaneTestResultAdapter.history, ['some-history']);
});
it('should return test state', () => {
const testResult = mkTestResult_({title: 'some-test'});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.state, {name: 'some-test'});
+ assert.deepEqual(TestplaneTestResultAdapter.state, {name: 'some-test'});
});
describe('error details', () => {
@@ -140,9 +140,9 @@ describe('TestplaneTestAdapter', () => {
});
getDetailsFileName.returns('md5-bro-n-time');
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.errorDetails, {
+ assert.deepEqual(TestplaneTestResultAdapter.errorDetails, {
title: 'some-title',
data: {foo: 'bar'},
filePath: `${ERROR_DETAILS_PATH}/md5-bro-n-time`
@@ -152,9 +152,9 @@ describe('TestplaneTestAdapter', () => {
it('should have "error details" title if no title is given', () => {
const testResult = mkTestResult_({err: {details: {}} as any});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.propertyVal(testplaneTestAdapter.errorDetails, 'title', 'error details');
+ assert.propertyVal(TestplaneTestResultAdapter.errorDetails, 'title', 'error details');
});
it('should be memoized', () => {
@@ -164,10 +164,10 @@ describe('TestplaneTestAdapter', () => {
details: {title: 'some-title', data: {foo: 'bar'}}
} as any
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- const firstErrDetails = testplaneTestAdapter.errorDetails;
- const secondErrDetails = testplaneTestAdapter.errorDetails;
+ const firstErrDetails = TestplaneTestResultAdapter.errorDetails;
+ const secondErrDetails = TestplaneTestResultAdapter.errorDetails;
assert.calledOnce(extractErrorDetails);
assert.deepEqual(firstErrDetails, secondErrDetails);
@@ -189,29 +189,29 @@ describe('TestplaneTestAdapter', () => {
details: {data: {foo: 'bar'}}
} as any
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
// we need to get errorDetails to trigger getDetailsFileName to be called
- testplaneTestAdapter.errorDetails;
+ TestplaneTestResultAdapter.errorDetails;
- assert.calledWith(getDetailsFileName, 'abcdef', 'bro', testplaneTestAdapter.attempt);
+ assert.calledWith(getDetailsFileName, 'abcdef', 'bro', TestplaneTestResultAdapter.attempt);
});
});
it('should return image dir', () => {
const testResult = mkTestResult_({id: 'some-id'});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.imageDir, 'some-id');
+ assert.deepEqual(TestplaneTestResultAdapter.imageDir, 'some-id');
});
it('should return description', () => {
const testResult = mkTestResult_({description: 'some-description'});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.description, 'some-description');
+ assert.deepEqual(TestplaneTestResultAdapter.description, 'some-description');
});
describe('timestamp', () => {
@@ -219,9 +219,9 @@ describe('TestplaneTestAdapter', () => {
const testResult = mkTestResult_({
timestamp: 100500
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.strictEqual(testplaneTestAdapter.timestamp, 100500);
+ assert.strictEqual(TestplaneTestResultAdapter.timestamp, 100500);
});
});
@@ -243,9 +243,9 @@ describe('TestplaneTestAdapter', () => {
}]
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.imagesInfo, [
+ assert.deepEqual(TestplaneTestResultAdapter.imagesInfo, [
{
status: TestStatus.FAIL,
stateName: 'some-state',
@@ -273,9 +273,9 @@ describe('TestplaneTestAdapter', () => {
}]
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.imagesInfo, [
+ assert.deepEqual(TestplaneTestResultAdapter.imagesInfo, [
{
status: TestStatus.ERROR,
stateName: 'some-state',
@@ -301,9 +301,9 @@ describe('TestplaneTestAdapter', () => {
} as TestplaneTestResult['assertViewResults'][number]]
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.imagesInfo, [
+ assert.deepEqual(TestplaneTestResultAdapter.imagesInfo, [
{
status: TestStatus.UPDATED,
stateName: 'some-state',
@@ -323,9 +323,9 @@ describe('TestplaneTestAdapter', () => {
}]
});
- const testplaneTestAdapter = mkTestplaneTestResultAdapter(testResult);
+ const TestplaneTestResultAdapter = mkTestplaneTestResultAdapter(testResult);
- assert.deepEqual(testplaneTestAdapter.imagesInfo, [
+ assert.deepEqual(TestplaneTestResultAdapter.imagesInfo, [
{
status: TestStatus.SUCCESS,
stateName: 'some-state',
diff --git a/test/unit/lib/adapters/tool/testplane/index.ts b/test/unit/lib/adapters/tool/testplane/index.ts
new file mode 100644
index 000000000..a69dd1f3e
--- /dev/null
+++ b/test/unit/lib/adapters/tool/testplane/index.ts
@@ -0,0 +1,433 @@
+import Testplane, {type TestCollection} from 'testplane';
+import proxyquire from 'proxyquire';
+import sinon, {SinonStub} from 'sinon';
+import P from 'bluebird';
+import type {CommanderStatic} from '@gemini-testing/commander';
+
+import {HtmlReporter} from '../../../../../../lib/plugin-api';
+import {ToolName} from '../../../../../../lib/constants';
+import {GuiApi} from '../../../../../../lib/gui/api';
+import {GuiReportBuilder} from '../../../../../../lib/report-builder/gui';
+import {EventSource} from '../../../../../../lib/gui/event-source';
+
+import {stubTool, stubConfig, stubTestCollection} from '../../../../utils';
+import type {ReporterConfig} from '../../../../../../lib/types';
+import type {TestSpec} from '../../../../../../lib/adapters/tool/types';
+
+describe('lib/adapters/tool/testplane/index', () => {
+ const sandbox = sinon.sandbox.create();
+ let TestplaneToolAdapter: typeof import('../../../../../../lib/adapters/tool/testplane').TestplaneToolAdapter;
+ let parseConfigStub: SinonStub;
+ let createTestRunnerStub: SinonStub;
+ let handleTestResultsStub: SinonStub;
+
+ beforeEach(() => {
+ sandbox.stub(Testplane, 'create').returns(stubTool());
+ sandbox.stub(HtmlReporter, 'create').returns({});
+ sandbox.stub(GuiApi, 'create').returns({});
+
+ parseConfigStub = sandbox.stub();
+ createTestRunnerStub = sandbox.stub();
+ handleTestResultsStub = sandbox.stub();
+
+ TestplaneToolAdapter = proxyquire('../../../../../../lib/adapters/tool/testplane', {
+ '../../../config': {parseConfig: parseConfigStub},
+ './runner': {createTestRunner: createTestRunnerStub},
+ './test-results-handler': {handleTestResults: handleTestResultsStub}
+ }).TestplaneToolAdapter;
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+
+ delete process.env['html_reporter_enabled'];
+ });
+
+ describe('constructor', () => {
+ describe('used from cli', () => {
+ it('should disable html reporter plugin in order to not generate static report', () => {
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane});
+
+ assert.equal(process.env['html_reporter_enabled'], 'false');
+ });
+
+ it('should create testplane instance with path to config file', () => {
+ const configPath = '/some/config/path';
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane, configPath});
+
+ assert.calledOnceWith(Testplane.create as SinonStub, configPath);
+ });
+
+ [ToolName.Testplane, 'hermione'].forEach(toolName => {
+ it(`should parse reporter opts from "html-reporter/${toolName}" plugin in config`, () => {
+ const pluginOpts = {
+ enabled: true,
+ path: 'hermione-report'
+ };
+ const config = stubConfig({plugins: {
+ [`html-reporter/${toolName}`]: pluginOpts
+ }});
+ const testplane = stubTool(config);
+ (Testplane.create as SinonStub).returns(testplane);
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane});
+
+ assert.calledOnceWith(parseConfigStub, pluginOpts);
+ });
+ });
+
+ it('should use default opts if html-reporter plugin is not found in config', () => {
+ const config = stubConfig({plugins: {}});
+ const testplane = stubTool(config);
+ (Testplane.create as SinonStub).returns(testplane);
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane});
+
+ assert.calledOnceWith(parseConfigStub, {});
+ });
+
+ it('should init htmlReporter instance with parsed reporter config', () => {
+ const reporterConfig = {path: 'some/path'} as ReporterConfig;
+ parseConfigStub.returns(reporterConfig);
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane});
+
+ assert.calledOnceWith(HtmlReporter.create as SinonStub, reporterConfig, {toolName: ToolName.Testplane});
+ });
+
+ it('should set "htmlReporter field in testplane to use from other plugins', () => {
+ const testplane = stubTool();
+ const htmlReporter = sinon.createStubInstance(HtmlReporter);
+ (Testplane.create as SinonStub).returns(testplane);
+ (HtmlReporter.create as SinonStub).returns(htmlReporter);
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane});
+
+ assert.equal(testplane.htmlReporter, htmlReporter);
+ });
+ });
+
+ describe('used from plugin', () => {
+ it('should use passed testplane instance', () => {
+ const reporterConfig = {path: 'some/path'} as ReporterConfig;
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: stubTool(), reporterConfig});
+
+ assert.notCalled(Testplane.create as SinonStub);
+ });
+
+ it('should init htmlReporter instance with passed reporter config', () => {
+ const reporterConfig = {path: 'some/path'} as ReporterConfig;
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: stubTool(), reporterConfig});
+
+ assert.calledOnceWith(HtmlReporter.create as SinonStub, reporterConfig, {toolName: ToolName.Testplane});
+ });
+
+ it('should set "htmlReporter field in testplane to use from other plugins', () => {
+ const testplane = stubTool();
+ const htmlReporter = sinon.createStubInstance(HtmlReporter);
+ (HtmlReporter.create as SinonStub).returns(htmlReporter);
+
+ TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ assert.equal(testplane.htmlReporter, htmlReporter);
+ });
+ });
+ });
+
+ describe('initGuiApi', () => {
+ it('should set "gui" field in testplane to use from other plugins', () => {
+ const testplane = stubTool();
+ const gui = {};
+ (GuiApi.create as SinonStub).returns({gui});
+
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+ toolAdapter.initGuiApi();
+
+ assert.equal(testplane.gui, gui);
+ });
+ });
+
+ describe('readTests', () => {
+ it('should correctly pass "paths" to the tests', async () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ await toolAdapter.readTests(['foo', 'bar'], {} as CommanderStatic);
+
+ assert.calledOnceWith(testplane.readTests, ['foo', 'bar']);
+ });
+
+ it('should correctly pass cli options', async () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+ const cliTool = {grep: 'foo', set: 'bar', browser: 'yabro'} as unknown as CommanderStatic;
+
+ await toolAdapter.readTests([], cliTool);
+
+ assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
+ grep: cliTool.grep,
+ sets: cliTool.set,
+ browsers: cliTool.browser
+ }));
+ });
+
+ describe('"replMode" option', () => {
+ it('should be disabled by default', async () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ await toolAdapter.readTests([], {} as CommanderStatic);
+
+ assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
+ replMode: {
+ enabled: false,
+ beforeTest: false,
+ onFail: false
+ }
+ }));
+ });
+
+ it('should be enabled when specify "repl" flag', async () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ await toolAdapter.readTests([], {repl: true} as unknown as CommanderStatic);
+
+ assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
+ replMode: {
+ enabled: true,
+ beforeTest: false,
+ onFail: false
+ }
+ }));
+ });
+
+ it('should be enabled when specify "beforeTest" flag', async () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ await toolAdapter.readTests([], {replBeforeTest: true} as unknown as CommanderStatic);
+
+ assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
+ replMode: {
+ enabled: true,
+ beforeTest: true,
+ onFail: false
+ }
+ }));
+ });
+
+ it('should be enabled when specify "onFail" flag', async () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ await toolAdapter.readTests([], {replOnFail: true} as unknown as CommanderStatic);
+
+ assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
+ replMode: {
+ enabled: true,
+ beforeTest: false,
+ onFail: true
+ }
+ }));
+ });
+ });
+ });
+
+ describe('run', () => {
+ let collection: TestCollection;
+ let runner: {run: SinonStub};
+
+ const run_ = async (testplane: Testplane, collection: TestCollection, tests: TestSpec[], cliTool: CommanderStatic): Promise => {
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+
+ await toolAdapter.run(collection, tests, cliTool);
+
+ const runHandler = runner.run.firstCall.args[0];
+ await runHandler(collection);
+ };
+
+ beforeEach(() => {
+ collection = stubTestCollection() as TestCollection;
+ runner = {run: sandbox.stub().resolves()};
+
+ createTestRunnerStub.withArgs(collection, []).returns(runner);
+ });
+
+ it('should run testplane with passed opts', async () => {
+ const testplane = stubTool();
+ const tests = [] as TestSpec[];
+ const cliTool = {grep: /some-grep/, set: 'some-set', browser: 'yabro', devtools: true} as unknown as CommanderStatic;
+
+ await run_(testplane, collection, tests, cliTool);
+
+ assert.calledOnceWith(testplane.run, collection, sinon.match({
+ grep: cliTool.grep,
+ sets: cliTool.set,
+ browsers: cliTool.browser,
+ devtools: cliTool.devtools
+ }));
+ });
+
+ describe('"replMode" option', () => {
+ it('should be disabled by default', async () => {
+ const testplane = stubTool();
+ const tests = [] as TestSpec[];
+ const cliTool = {} as unknown as CommanderStatic;
+
+ await run_(testplane, collection, tests, cliTool);
+
+ assert.calledOnceWith(testplane.run, collection, sinon.match({
+ replMode: {
+ enabled: false,
+ beforeTest: false,
+ onFail: false
+ }
+ }));
+ });
+
+ it('should be enabled when specify "repl" flag', async () => {
+ const testplane = stubTool();
+ const tests = [] as TestSpec[];
+ const cliTool = {repl: true} as unknown as CommanderStatic;
+
+ await run_(testplane, collection, tests, cliTool);
+
+ assert.calledOnceWith(testplane.run, collection, sinon.match({
+ replMode: {
+ enabled: true,
+ beforeTest: false,
+ onFail: false
+ }
+ }));
+ });
+
+ it('should be enabled when specify "beforeTest" flag', async () => {
+ const testplane = stubTool();
+ const tests = [] as TestSpec[];
+ const cliTool = {replBeforeTest: true} as unknown as CommanderStatic;
+
+ await run_(testplane, collection, tests, cliTool);
+
+ assert.calledOnceWith(testplane.run, collection, sinon.match({
+ replMode: {
+ enabled: true,
+ beforeTest: true,
+ onFail: false
+ }
+ }));
+ });
+
+ it('should be enabled when specify "onFail" flag', async () => {
+ const testplane = stubTool();
+ const tests = [] as TestSpec[];
+ const cliTool = {replOnFail: true} as unknown as CommanderStatic;
+
+ await run_(testplane, collection, tests, cliTool);
+
+ assert.calledOnceWith(testplane.run, collection, sinon.match({
+ replMode: {
+ enabled: true,
+ beforeTest: false,
+ onFail: true
+ }
+ }));
+ });
+ });
+ });
+
+ describe('updateReference', () => {
+ it('should emit "UPDATE_REFERENCE" event with passed options', () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+ const onUpdateReference = sinon.spy();
+ const updateOpts = {
+ refImg: {path: '/ref/path', size: {height: 100, width: 200}},
+ state: 'plain'
+ };
+
+ testplane.on(testplane.events.UPDATE_REFERENCE, onUpdateReference);
+ toolAdapter.updateReference(updateOpts);
+
+ assert.calledOnceWith(onUpdateReference, updateOpts);
+ });
+ });
+
+ describe('handleTestResults', () => {
+ it('should call test results handler with correct args', () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+ const reportBuilder = {} as GuiReportBuilder;
+ const eventSource = {} as EventSource;
+
+ toolAdapter.handleTestResults(reportBuilder, eventSource);
+
+ assert.calledOnceWith(handleTestResultsStub, testplane, reportBuilder, eventSource);
+ });
+ });
+
+ describe('halt', () => {
+ it('should halt testplane', () => {
+ const testplane = stubTool();
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: {} as ReporterConfig});
+ const error = new Error('o.O');
+ const timeout = 100500;
+
+ toolAdapter.halt(error, timeout);
+
+ assert.calledOnceWith(testplane.halt, error, timeout);
+ });
+ });
+
+ describe('initGuiHandler', () => {
+ it('should initialize each group of controls if initialize-function is available', async () => {
+ const initializeSpy1 = sinon.spy();
+ const initializeSpy2 = sinon.spy();
+
+ const initialize1 = sinon.stub().callsFake(() => P.delay(5).then(initializeSpy1));
+ const initialize2 = sinon.stub().callsFake(() => P.delay(10).then(initializeSpy2));
+
+ const ctx1 = {initialize: initialize1};
+ const ctx2 = {initialize: initialize2};
+
+ const reporterConfig = {
+ customGui: {'section-1': [ctx1], 'section-2': [ctx2]}
+ } as unknown as ReporterConfig;
+ const testplane = stubTool();
+
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig});
+
+ await toolAdapter.initGuiHandler();
+
+ assert.calledOnceWith(initialize1, {testplane, hermione: testplane, ctx: ctx1});
+ assert.calledOnceWith(initialize2, {testplane, hermione: testplane, ctx: ctx2});
+ assert.callOrder(initializeSpy1, initializeSpy2);
+ });
+ });
+
+ describe('runCustomGuiAction', () => {
+ it('should run action for specified controls', async () => {
+ const actionSpy = sinon.spy();
+ const action = sinon.stub().callsFake(() => P.delay(10).then(actionSpy));
+
+ const control = {};
+ const ctx = {controls: [control], action};
+ const reporterConfig = {customGui: {'section': [ctx]}} as unknown as ReporterConfig;
+ const testplane = stubTool();
+
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig});
+
+ await toolAdapter.runCustomGuiAction({
+ sectionName: 'section',
+ groupIndex: 0,
+ controlIndex: 0
+ });
+
+ assert.calledOnceWith(action, {testplane, hermione: testplane, ctx, control});
+ assert.calledOnce(actionSpy);
+ });
+ });
+});
diff --git a/test/unit/lib/gui/tool-runner/report-subsciber.js b/test/unit/lib/adapters/tool/testplane/test-results-handler.js
similarity index 86%
rename from test/unit/lib/gui/tool-runner/report-subsciber.js
rename to test/unit/lib/adapters/tool/testplane/test-results-handler.js
index caf3fc665..f68ef9b6a 100644
--- a/test/unit/lib/gui/tool-runner/report-subsciber.js
+++ b/test/unit/lib/adapters/tool/testplane/test-results-handler.js
@@ -3,14 +3,14 @@
const {EventEmitter} = require('events');
const Promise = require('bluebird');
const _ = require('lodash');
-const {subscribeOnToolEvents} = require('lib/gui/tool-runner/report-subscriber');
+const {handleTestResults} = require('lib/adapters/tool/testplane/test-results-handler');
const {GuiReportBuilder} = require('lib/report-builder/gui');
const {ClientEvents} = require('lib/gui/constants');
const {stubTool, stubConfig} = require('test/unit/utils');
-const {TestplaneTestAdapter} = require('lib/test-adapter/testplane');
+const {TestplaneTestResultAdapter} = require('lib/adapters/test-result/testplane');
const {UNKNOWN_ATTEMPT} = require('lib/constants');
-describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
+describe('lib/adapters/tool/testplane/test-results-handler', () => {
const sandbox = sinon.createSandbox();
let reportBuilder;
let client;
@@ -37,7 +37,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
reportBuilder.addTestResult.callsFake(_.identity);
sandbox.stub(GuiReportBuilder, 'create').returns(reportBuilder);
- sandbox.stub(TestplaneTestAdapter.prototype, 'id').value('some-id');
+ sandbox.stub(TestplaneTestResultAdapter.prototype, 'id').value('some-id');
client = new EventEmitter();
sandbox.spy(client, 'emit');
@@ -49,7 +49,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
it('should emit "END" event for client', () => {
const testplane = mkTestplane_();
- subscribeOnToolEvents(testplane, reportBuilder, client);
+ handleTestResults(testplane, reportBuilder, client);
return testplane.emitAsync(testplane.events.RUNNER_END)
.then(() => assert.calledOnceWith(client.emit, ClientEvents.END));
@@ -62,7 +62,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
reportBuilder.addTestResult.callsFake(() => Promise.delay(100).then(mediator).then(() => ({id: 'some-id'})));
- subscribeOnToolEvents(testplane, reportBuilder, client);
+ handleTestResults(testplane, reportBuilder, client);
testplane.emit(testplane.events.TEST_FAIL, testResult);
await testplane.emitAsync(testplane.events.RUNNER_END);
@@ -78,7 +78,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
reportBuilder.addTestResult.resolves({id: 'some-id'});
reportBuilder.getTestBranch.withArgs('some-id').returns('test-tree-branch');
- subscribeOnToolEvents(testplane, reportBuilder, client);
+ handleTestResults(testplane, reportBuilder, client);
testplane.emit(testplane.events.TEST_BEGIN, testResult);
await testplane.emitAsync(testplane.events.RUNNER_END);
@@ -91,7 +91,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
const testplane = mkTestplane_();
const testResult = mkTestplaneTestResult();
- subscribeOnToolEvents(testplane, reportBuilder, client);
+ handleTestResults(testplane, reportBuilder, client);
await testplane.emitAsync(testplane.events.TEST_PENDING, testResult);
await testplane.emitAsync(testplane.events.RUNNER_END);
@@ -108,7 +108,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
reportBuilder.getTestBranch.withArgs('some-id').returns('test-tree-branch');
- subscribeOnToolEvents(testplane, reportBuilder, client);
+ handleTestResults(testplane, reportBuilder, client);
await testplane.emitAsync(testplane.events.TEST_PENDING, testResult);
await testplane.emitAsync(testplane.events.RUNNER_END);
@@ -123,7 +123,7 @@ describe('lib/gui/tool-runner/testplane/report-subscriber', () => {
reportBuilder.getTestBranch.withArgs('some-id').returns('test-tree-branch');
- subscribeOnToolEvents(testplane, reportBuilder, client);
+ handleTestResults(testplane, reportBuilder, client);
testplane.emit(testplane.events.TEST_FAIL, testResult);
await testplane.emitAsync(testplane.events.RUNNER_END);
diff --git a/test/unit/lib/cli-commands/remove-unused-screens/index.js b/test/unit/lib/cli/commands/remove-unused-screens/index.js
similarity index 84%
rename from test/unit/lib/cli-commands/remove-unused-screens/index.js
rename to test/unit/lib/cli/commands/remove-unused-screens/index.js
index fc904d9a5..d20ef9b93 100644
--- a/test/unit/lib/cli-commands/remove-unused-screens/index.js
+++ b/test/unit/lib/cli/commands/remove-unused-screens/index.js
@@ -1,15 +1,14 @@
'use strict';
const path = require('path');
-const _ = require('lodash');
const fs = require('fs-extra');
const proxyquire = require('proxyquire');
const chalk = require('chalk');
const {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} = require('lib/constants/database');
-const {stubTool} = require('test/unit/utils');
const {logger} = require('lib/common-utils');
+const {stubToolAdapter, stubReporterConfig} = require('test/unit/utils');
-describe('lib/cli-commands/remove-unused-screens', () => {
+describe('lib/cli/commands/remove-unused-screens', () => {
const sandbox = sinon.sandbox.create();
let actionPromise, removeUnusedScreens, getTestsFromFs, findScreens, askQuestion;
let identifyOutdatedScreens, identifyUnusedScreens, removeScreens, filesizeMock;
@@ -25,17 +24,13 @@ describe('lib/cli-commands/remove-unused-screens', () => {
})
});
- const mkPluginCfg_ = (opts) => _.defaults(opts, {path: 'default-path'});
+ const mkToolAdapter_ = ({htmlReporter, reporterConfig} = {}) => {
+ const toolAdapter = stubToolAdapter({reporterConfig, htmlReporter});
- const mkTestplane_ = (htmlReporter) => {
- const testplane = stubTool();
+ toolAdapter.htmlReporter.downloadDatabases = sandbox.stub().resolves(['/default-path/sqlite.db']);
+ toolAdapter.htmlReporter.mergeDatabases = sandbox.stub().resolves();
- testplane.htmlReporter = htmlReporter || {
- downloadDatabases: sandbox.stub().resolves(['/default-path/sqlite.db']),
- mergeDatabases: sandbox.stub().resolves()
- };
-
- return testplane;
+ return toolAdapter;
};
const mkTestsTreeFromFs_ = (opts = {}) => {
@@ -44,10 +39,9 @@ describe('lib/cli-commands/remove-unused-screens', () => {
const removeUnusedScreens_ = async ({
program = mkProgram_({pattern: ['default/pattern'], skipQuestions: false}),
- pluginConfig = mkPluginCfg_(),
- testplane = mkTestplane_()
+ toolAdapter = mkToolAdapter_()
} = {}) => {
- removeUnusedScreens(program, pluginConfig, testplane);
+ removeUnusedScreens(program, toolAdapter);
await actionPromise;
};
@@ -71,7 +65,7 @@ describe('lib/cli-commands/remove-unused-screens', () => {
filesizeMock = sandbox.stub().returns('12345');
- removeUnusedScreens = proxyquire('lib/cli-commands/remove-unused-screens', {
+ removeUnusedScreens = proxyquire('lib/cli/commands/remove-unused-screens', {
ora: () => ({start: sandbox.stub(), succeed: sandbox.stub()}),
filesize: filesizeMock,
'./utils': {getTestsFromFs, findScreens, askQuestion, identifyOutdatedScreens, identifyUnusedScreens, removeScreens}
@@ -101,11 +95,11 @@ describe('lib/cli-commands/remove-unused-screens', () => {
});
it('should get tests tree from fs', async () => {
- const testplane = mkTestplane_();
+ const toolAdapter = mkToolAdapter_();
- await removeUnusedScreens_({testplane});
+ await removeUnusedScreens_({toolAdapter});
- assert.calledOnceWith(getTestsFromFs, testplane);
+ assert.calledOnceWith(getTestsFromFs, toolAdapter);
});
it('should inform user about the number of tests read', async () => {
@@ -346,49 +340,49 @@ describe('lib/cli-commands/remove-unused-screens', () => {
});
it('should not handle unused images if user say "no"', async () => {
- const testplane = mkTestplane_();
+ const toolAdapter = mkToolAdapter_();
const program = mkProgram_({pattern: ['some/pattern'], skipQuestions: false});
askQuestion.withArgs(sinon.match({name: 'identifyUnused'}), program.options).resolves(false);
- await removeUnusedScreens_({testplane, program});
+ await removeUnusedScreens_({toolAdapter, program});
- assert.notCalled(testplane.htmlReporter.downloadDatabases);
+ assert.notCalled(toolAdapter.htmlReporter.downloadDatabases);
});
describe('if user say "yes"', () => {
- let testplane, program, pluginConfig;
+ let toolAdapter, program, reporterConfig;
beforeEach(() => {
- testplane = mkTestplane_();
+ reporterConfig = stubReporterConfig({path: './testplane-report'});
+ toolAdapter = mkToolAdapter_({reporterConfig});
program = mkProgram_({pattern: ['some/pattern'], skipQuestions: false});
- pluginConfig = mkPluginCfg_({path: './testplane-report'});
askQuestion.withArgs(sinon.match({name: 'identifyUnused'}), program.options).resolves(true);
});
it('should inform user if report with the result of test run is missing on fs', async () => {
- fs.pathExists.withArgs(pluginConfig.path).resolves(false);
+ fs.pathExists.withArgs(reporterConfig.path).resolves(false);
- await removeUnusedScreens_({program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
- assert.calledWith(logger.error, sinon.match(`Can't find html-report in "${pluginConfig.path}" folder`));
+ assert.calledWith(logger.error, sinon.match(`Can't find html-report in "${reporterConfig.path}" folder`));
assert.calledOnceWith(process.exit, 1);
});
describe('download databases', () => {
it('should download from main databaseUrls.json', async () => {
- const mainDatabaseUrls = path.resolve(pluginConfig.path, DATABASE_URLS_JSON_NAME);
+ const mainDatabaseUrls = path.resolve(reporterConfig.path, DATABASE_URLS_JSON_NAME);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
- assert.calledOnceWith(testplane.htmlReporter.downloadDatabases, [mainDatabaseUrls], {pluginConfig});
+ assert.calledOnceWith(toolAdapter.htmlReporter.downloadDatabases, [mainDatabaseUrls], {pluginConfig: reporterConfig});
});
it(`should inform user if databases were not loaded from "${DATABASE_URLS_JSON_NAME}"`, async () => {
- const mainDatabaseUrls = path.resolve(pluginConfig.path, DATABASE_URLS_JSON_NAME);
- testplane.htmlReporter.downloadDatabases.resolves([]);
+ const mainDatabaseUrls = path.resolve(reporterConfig.path, DATABASE_URLS_JSON_NAME);
+ toolAdapter.htmlReporter.downloadDatabases.resolves([]);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
assert.calledWith(logger.error, sinon.match(`Databases were not loaded from "${mainDatabaseUrls}" file`));
assert.calledOnceWith(process.exit, 1);
@@ -397,32 +391,32 @@ describe('lib/cli-commands/remove-unused-screens', () => {
describe('merge databases', () => {
it('should not merge databases if downloaded only main database', async () => {
- const mainDatabasePath = path.resolve(pluginConfig.path, LOCAL_DATABASE_NAME);
- testplane.htmlReporter.downloadDatabases.resolves([mainDatabasePath]);
+ const mainDatabasePath = path.resolve(reporterConfig.path, LOCAL_DATABASE_NAME);
+ toolAdapter.htmlReporter.downloadDatabases.resolves([mainDatabasePath]);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
- assert.notCalled(testplane.htmlReporter.mergeDatabases);
+ assert.notCalled(toolAdapter.htmlReporter.mergeDatabases);
});
it('should merge source databases to main', async () => {
- const srcDb1 = path.resolve(pluginConfig.path, 'sqlite_1.db');
- const srcDb2 = path.resolve(pluginConfig.path, 'sqlite_2.db');
- const mainDatabasePath = path.resolve(pluginConfig.path, LOCAL_DATABASE_NAME);
- testplane.htmlReporter.downloadDatabases.resolves([mainDatabasePath, srcDb1, srcDb2]);
+ const srcDb1 = path.resolve(reporterConfig.path, 'sqlite_1.db');
+ const srcDb2 = path.resolve(reporterConfig.path, 'sqlite_2.db');
+ const mainDatabasePath = path.resolve(reporterConfig.path, LOCAL_DATABASE_NAME);
+ toolAdapter.htmlReporter.downloadDatabases.resolves([mainDatabasePath, srcDb1, srcDb2]);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
- assert.calledOnceWith(testplane.htmlReporter.mergeDatabases, [srcDb1, srcDb2], pluginConfig.path);
+ assert.calledOnceWith(toolAdapter.htmlReporter.mergeDatabases, [srcDb1, srcDb2], reporterConfig.path);
});
it('should infrom user about how much databases were merged', async () => {
- const srcDb1 = path.resolve(pluginConfig.path, 'sqlite_1.db');
- const srcDb2 = path.resolve(pluginConfig.path, 'sqlite_2.db');
- const mainDatabasePath = path.resolve(pluginConfig.path, LOCAL_DATABASE_NAME);
- testplane.htmlReporter.downloadDatabases.resolves([mainDatabasePath, srcDb1, srcDb2]);
+ const srcDb1 = path.resolve(reporterConfig.path, 'sqlite_1.db');
+ const srcDb2 = path.resolve(reporterConfig.path, 'sqlite_2.db');
+ const mainDatabasePath = path.resolve(reporterConfig.path, LOCAL_DATABASE_NAME);
+ toolAdapter.htmlReporter.downloadDatabases.resolves([mainDatabasePath, srcDb1, srcDb2]);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
assert.calledWith(
logger.log,
@@ -436,11 +430,11 @@ describe('lib/cli-commands/remove-unused-screens', () => {
byId: {a: {}, b: {}, c: {}}
});
getTestsFromFs.resolves(testsTreeFromFs);
- const mergedDbPath = path.resolve(pluginConfig.path, LOCAL_DATABASE_NAME);
+ const mergedDbPath = path.resolve(reporterConfig.path, LOCAL_DATABASE_NAME);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
- assert.calledOnceWith(identifyUnusedScreens, testsTreeFromFs, {testplane, mergedDbPath});
+ assert.calledOnceWith(identifyUnusedScreens, testsTreeFromFs, {toolAdapter, mergedDbPath});
});
it('should not throw if unused screen does not exist on fs', async () => {
@@ -451,7 +445,7 @@ describe('lib/cli-commands/remove-unused-screens', () => {
accessError.code = 'ENOENT';
fs.access.withArgs('/root/broId/unusedTestId/2.png').rejects(accessError);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
assert.calledOnceWith(logger.warn, sinon.match('Screen by path: "/root/broId/unusedTestId/2.png" is not found in your file system'));
assert.calledWith(logger.log, `Found ${chalk.green('0')} unused reference images out of ${chalk.bold('2')}`);
@@ -461,7 +455,7 @@ describe('lib/cli-commands/remove-unused-screens', () => {
findScreens.resolves(['/root/usedTestId/img.png', '/root/unusedTestId/img.png']);
identifyUnusedScreens.returns(['/root/unusedTestId/img.png']);
- await removeUnusedScreens_({testplane, program, pluginConfig});
+ await removeUnusedScreens_({toolAdapter, program});
assert.calledWith(logger.log, `Found ${chalk.red('1')} unused reference images out of ${chalk.bold('2')}`);
});
diff --git a/test/unit/lib/cli-commands/remove-unused-screens/utils.js b/test/unit/lib/cli/commands/remove-unused-screens/utils.js
similarity index 86%
rename from test/unit/lib/cli-commands/remove-unused-screens/utils.js
rename to test/unit/lib/cli/commands/remove-unused-screens/utils.js
index 830a499d9..3dc922349 100644
--- a/test/unit/lib/cli-commands/remove-unused-screens/utils.js
+++ b/test/unit/lib/cli/commands/remove-unused-screens/utils.js
@@ -6,9 +6,9 @@ const proxyquire = require('proxyquire');
const inquirer = require('inquirer');
const {SUCCESS, ERROR} = require('lib/constants/test-statuses');
-const {stubTool, stubConfig, mkState} = require('test/unit/utils');
+const {stubToolAdapter, stubConfig, mkState} = require('test/unit/utils');
-describe('lib/cli-commands/remove-unused-screens/utils', () => {
+describe('lib/cli/commands/remove-unused-screens/utils', () => {
const sandbox = sinon.sandbox.create();
let utils, fgMock;
@@ -36,14 +36,15 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
};
};
- const mkTestplane_ = (testCollection = mkTestCollection_(), config = stubConfig(), htmlReporter) => {
- const testplane = stubTool(config);
- testplane.readTests.resolves(testCollection);
- testplane.htmlReporter = htmlReporter || {
+ const mkToolAdapter_ = (testCollection = mkTestCollection_(), config = stubConfig(), htmlReporter) => {
+ const toolAdapter = stubToolAdapter({config});
+
+ toolAdapter.readTests.resolves(testCollection);
+ toolAdapter.htmlReporter = htmlReporter || {
getTestsTreeFromDatabase: sandbox.stub().returns(mkTestsTreeFromDb_())
};
- return testplane;
+ return toolAdapter;
};
beforeEach(() => {
@@ -52,7 +53,7 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
fgMock = sandbox.stub().resolves([]);
- utils = proxyquire('lib/cli-commands/remove-unused-screens/utils', {
+ utils = proxyquire('lib/cli/commands/remove-unused-screens/utils', {
'fast-glob': fgMock
});
});
@@ -66,12 +67,12 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
getScreenshotPath = sandbox.stub().returns('/default/path/*.png');
});
- it('should read all testplane tests silently', async () => {
- const testplane = mkTestplane_();
+ it('should read all tests silently', async () => {
+ const toolAdapter = mkToolAdapter_();
- await utils.getTestsFromFs(testplane);
+ await utils.getTestsFromFs(toolAdapter);
- assert.calledOnceWith(testplane.readTests, [], {silent: true});
+ assert.calledOnceWith(toolAdapter.readTests, [], {silent: true});
});
it('should add test tests tree with its screen info', async () => {
@@ -82,9 +83,9 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
fgMock.withArgs('/ref/path/*.png').resolves(['/ref/path/1.png', '/ref/path/2.png']);
- const testplane = mkTestplane_(testCollection, config);
+ const toolAdapter = mkToolAdapter_(testCollection, config);
- const tests = await utils.getTestsFromFs(testplane);
+ const tests = await utils.getTestsFromFs(toolAdapter);
assert.lengthOf(Object.keys(tests.byId), 1);
assert.deepEqual(
@@ -106,9 +107,9 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
.withArgs(test2, '*').returns('/ref/path-2/*.png');
const config = stubConfig({browsers: {bro1: {getScreenshotPath}, bro2: {getScreenshotPath}}});
- const testplane = mkTestplane_(testCollection, config);
+ const toolAdapter = mkToolAdapter_(testCollection, config);
- const tests = await utils.getTestsFromFs(testplane);
+ const tests = await utils.getTestsFromFs(toolAdapter);
assert.deepEqual(tests.screenPatterns, ['/ref/path-1/*.png', '/ref/path-2/*.png']);
});
@@ -122,9 +123,9 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
bro1: {getScreenshotPath}, bro2: {getScreenshotPath},
bro3: {getScreenshotPath}, bro4: {getScreenshotPath}
}});
- const testplane = mkTestplane_(testCollection, config);
+ const toolAdapter = mkToolAdapter_(testCollection, config);
- const tests = await utils.getTestsFromFs(testplane);
+ const tests = await utils.getTestsFromFs(toolAdapter);
assert.equal(tests.count, 2);
});
@@ -136,9 +137,9 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
const config = stubConfig({browsers: {
bro1: {getScreenshotPath}, bro2: {getScreenshotPath}, bro3: {getScreenshotPath}
}});
- const testplane = mkTestplane_(testCollection, config);
+ const toolAdapter = mkToolAdapter_(testCollection, config);
- const tests = await utils.getTestsFromFs(testplane);
+ const tests = await utils.getTestsFromFs(toolAdapter);
assert.deepEqual(tests.browserIds, new Set(['bro1', 'bro2', 'bro3']));
});
@@ -261,8 +262,8 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
const imagesById = mkImage({id: 'img-1', stateName: 'a'});
const dbTree = mkTestsTreeFromDb_({browsersById, resultsById, imagesById});
- const testplane = mkTestplane_();
- testplane.htmlReporter.getTestsTreeFromDatabase.withArgs('/report/sqlite.db').returns(dbTree);
+ const toolAdapter = mkToolAdapter_();
+ toolAdapter.htmlReporter.getTestsTreeFromDatabase.withArgs('/report/sqlite.db').returns(dbTree);
const fsTestsTree = mkTestsTreeFromFs_({
byId: {
@@ -272,7 +273,7 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
const unusedScreens = utils.identifyUnusedScreens(
fsTestsTree,
- {testplane, mergedDbPath: '/report/sqlite.db'}
+ {toolAdapter, mergedDbPath: '/report/sqlite.db'}
);
assert.deepEqual(unusedScreens, ['/test1/b.png']);
@@ -284,8 +285,8 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
const imagesById = mkImage({id: 'img-1', stateName: 'a'});
const dbTree = mkTestsTreeFromDb_({browsersById, resultsById, imagesById});
- const testplane = mkTestplane_();
- testplane.htmlReporter.getTestsTreeFromDatabase.withArgs('/report/sqlite.db').returns(dbTree);
+ const toolAdapter = mkToolAdapter_();
+ toolAdapter.htmlReporter.getTestsTreeFromDatabase.withArgs('/report/sqlite.db').returns(dbTree);
const fsTestsTree = mkTestsTreeFromFs_({
byId: {
@@ -295,7 +296,7 @@ describe('lib/cli-commands/remove-unused-screens/utils', () => {
const unusedScreens = utils.identifyUnusedScreens(
fsTestsTree,
- {testplane, mergedDbPath: '/report/sqlite.db'}
+ {toolAdapter, mergedDbPath: '/report/sqlite.db'}
);
assert.isEmpty(unusedScreens);
diff --git a/test/unit/lib/gui/api/index.js b/test/unit/lib/gui/api/index.js
index 87e5ee07a..252e7f180 100644
--- a/test/unit/lib/gui/api/index.js
+++ b/test/unit/lib/gui/api/index.js
@@ -2,34 +2,28 @@
const EventEmitter2 = require('eventemitter2');
const {GuiEvents} = require('lib/gui/constants/gui-events');
-const {Api} = require('lib/gui/api');
-const {stubTool} = require('../../../utils');
+const {GuiApi} = require('lib/gui/api');
-describe('lig/gui/api', () => {
+describe('lib/gui/api', () => {
describe('constructor', () => {
- it('should extend tool with gui api', () => {
- const tool = stubTool();
+ it('should init gui api', () => {
+ const api = GuiApi.create();
- Api.create(tool);
-
- assert.instanceOf(tool.gui, EventEmitter2);
+ assert.instanceOf(api.gui, EventEmitter2);
});
it('should add events to gui api', () => {
- const tool = stubTool();
-
- Api.create(tool);
+ const api = GuiApi.create();
- assert.deepEqual(tool.gui.events, GuiEvents);
+ assert.deepEqual(api.gui.events, GuiEvents);
});
});
describe('initServer', () => {
it('should emit "SERVER_INIT" event through gui api', () => {
- const tool = stubTool();
- const api = Api.create(tool);
+ const api = GuiApi.create();
const onServerInit = sinon.spy().named('onServerInit');
- tool.gui.on(GuiEvents.SERVER_INIT, onServerInit);
+ api.gui.on(GuiEvents.SERVER_INIT, onServerInit);
api.initServer({foo: 'bar'});
@@ -39,10 +33,9 @@ describe('lig/gui/api', () => {
describe('serverReady', () => {
it('should emit "SERVER_READY" event through gui api', () => {
- const tool = stubTool();
- const api = Api.create(tool);
+ const api = GuiApi.create();
const onServerReady = sinon.spy().named('onServerReady');
- tool.gui.on(GuiEvents.SERVER_READY, onServerReady);
+ api.gui.on(GuiEvents.SERVER_READY, onServerReady);
api.serverReady({url: 'http://my.server'});
diff --git a/test/unit/lib/gui/server.js b/test/unit/lib/gui/server.js
index 7050f1f6e..6bbec02b8 100644
--- a/test/unit/lib/gui/server.js
+++ b/test/unit/lib/gui/server.js
@@ -3,7 +3,7 @@
const _ = require('lodash');
const proxyquire = require('proxyquire');
const {App} = require('lib/gui/app');
-const {stubTool} = require('../../utils');
+const {stubToolAdapter} = require('../../utils');
describe('lib/gui/server', () => {
const sandbox = sinon.createSandbox();
@@ -23,19 +23,12 @@ describe('lib/gui/server', () => {
static: sandbox.stub()
});
- const mkApi_ = () => ({
- initServer: sandbox.stub(),
- serverReady: sandbox.stub()
- });
-
const startServer = (opts = {}) => {
opts = _.defaults(opts, {
paths: [],
- tool: stubTool(),
- guiApi: mkApi_(),
- configs: {
- options: {hostname: 'localhost', port: '4444'},
- pluginConfig: {path: 'default-path'}
+ toolAdapter: stubToolAdapter(),
+ cli: {
+ options: {hostname: 'localhost', port: '4444'}
}
});
@@ -71,32 +64,36 @@ describe('lib/gui/server', () => {
});
it('should init server from api', async () => {
- const guiApi = mkApi_();
+ const toolAdapter = stubToolAdapter();
+ const {guiApi} = toolAdapter;
- await startServer({guiApi});
+ await startServer({toolAdapter});
assert.calledOnceWith(guiApi.initServer, expressStub);
assert.calledOnceWith(guiApi.serverReady, {url: 'http://localhost:4444'});
});
it('should init server only after body is parsed', async () => {
- const guiApi = mkApi_();
+ const toolAdapter = stubToolAdapter();
+ const {guiApi} = toolAdapter;
- await startServer({guiApi});
+ await startServer({toolAdapter});
assert.callOrder(bodyParserStub.json, guiApi.initServer, guiApi.serverReady);
});
it('should init server before any static middleware starts', async () => {
- const guiApi = mkApi_();
+ const toolAdapter = stubToolAdapter();
+ const {guiApi} = toolAdapter;
- await startServer({guiApi});
+ await startServer({toolAdapter});
assert.callOrder(guiApi.initServer, staticMiddleware, guiApi.serverReady);
});
it('should properly complete app working', async () => {
sandbox.stub(process, 'kill');
+ sandbox.stub(process, 'exit');
await startServer();
@@ -106,28 +103,24 @@ describe('lib/gui/server', () => {
});
it('should correctly set json replacer', async () => {
- const guiApi = mkApi_();
+ const toolAdapter = stubToolAdapter();
- await startServer({guiApi});
+ await startServer({toolAdapter});
assert.calledOnceWith(expressStub.set, 'json replacer', sinon.match.func);
});
it('should try to attach plugins middleware on startup', async () => {
- const pluginConfig = {
+ const reporterConfig = {
path: 'test-path',
plugins: [
{name: 'test-plugin', component: 'TestComponent'}
]
};
+ const toolAdapter = stubToolAdapter({reporterConfig});
- await startServer({
- configs: {
- options: {hostname: 'localhost', port: '4444'},
- pluginConfig
- }
- });
+ await startServer({toolAdapter});
- assert.calledOnceWith(initPluginRoutesStub, RouterStub, pluginConfig);
+ assert.calledOnceWith(initPluginRoutesStub, RouterStub, reporterConfig);
});
});
diff --git a/test/unit/lib/gui/tool-runner/index.js b/test/unit/lib/gui/tool-runner/index.js
index 63b2ccbe8..9488cc353 100644
--- a/test/unit/lib/gui/tool-runner/index.js
+++ b/test/unit/lib/gui/tool-runner/index.js
@@ -7,7 +7,7 @@ const proxyquire = require('proxyquire');
const {GuiReportBuilder} = require('lib/report-builder/gui');
const {LOCAL_DATABASE_NAME} = require('lib/constants/database');
const {logger} = require('lib/common-utils');
-const {stubTool, stubConfig, mkImagesInfo, mkState, mkSuite} = require('test/unit/utils');
+const {stubToolAdapter, stubConfig, stubReporterConfig, mkImagesInfo, mkState, mkSuite} = require('test/unit/utils');
const {SqliteClient} = require('lib/sqlite-client');
const {PluginEvents, TestStatus, UPDATED} = require('lib/constants');
const {Cache} = require('lib/cache');
@@ -16,12 +16,10 @@ describe('lib/gui/tool-runner/index', () => {
const sandbox = sinon.createSandbox();
let reportBuilder;
let ToolGuiReporter;
- let subscribeOnToolEvents;
- let testplane;
+ let toolAdapter;
let getTestsTreeFromDatabase;
let looksSame;
let toolRunnerUtils;
- let createTestRunner;
let getReferencePath;
let reporterHelpers;
@@ -40,37 +38,21 @@ describe('lib/gui/tool-runner/index', () => {
}));
};
- const mkToolCliOpts_ = (globalCliOpts = {name: () => 'testplane'}, guiCliOpts = {}) => {
- return {program: globalCliOpts, options: guiCliOpts};
- };
- const mkPluginConfig_ = (config = {}) => {
- const pluginConfig = _.defaults(config, {path: 'default-path'});
- return {pluginConfig};
- };
-
- const mkTestplane_ = (config, testsTree) => {
- const testplane = stubTool(config, {UPDATE_REFERENCE: 'updateReference'});
- sandbox.stub(testplane, 'emit');
- testplane.readTests.resolves(mkTestCollection_(testsTree));
-
- return testplane;
- };
-
- const initGuiReporter = (testplane, opts = {}) => {
+ const initGuiReporter = (opts = {}) => {
opts = _.defaults(opts, {
+ toolAdapter: stubToolAdapter(),
paths: [],
- configs: {}
+ cli: {
+ tool: {},
+ options: {}
+ }
});
- const configs = _.defaults(opts.configs, mkToolCliOpts_(), mkPluginConfig_());
-
- return ToolGuiReporter.create(opts.paths, testplane, configs);
+ return ToolGuiReporter.create(opts);
};
beforeEach(() => {
- testplane = stubTool();
-
- createTestRunner = sandbox.stub();
+ toolAdapter = stubToolAdapter();
toolRunnerUtils = {
findTestResult: sandbox.stub(),
@@ -81,7 +63,6 @@ describe('lib/gui/tool-runner/index', () => {
reportBuilder.addTestResult.callsFake(_.identity);
reportBuilder.provideAttempt.callsFake(_.identity);
- subscribeOnToolEvents = sandbox.stub().named('reportSubscriber').resolves();
looksSame = sandbox.stub().named('looksSame').resolves({equal: true});
sandbox.stub(GuiReportBuilder, 'create').returns(reportBuilder);
@@ -99,7 +80,7 @@ describe('lib/gui/tool-runner/index', () => {
fileExists: sandbox.stub(),
deleteFile: sandbox.stub()
},
- './test-adapter/utils': {
+ './adapters/test-result/utils': {
copyAndUpdate: sandbox.stub().callsFake(_.assign)
}
});
@@ -107,8 +88,6 @@ describe('lib/gui/tool-runner/index', () => {
ToolGuiReporter = proxyquire(`lib/gui/tool-runner`, {
'looks-same': looksSame,
- './runner': {createTestRunner},
- './report-subscriber': {subscribeOnToolEvents},
'./utils': toolRunnerUtils,
'../../sqlite-client': {SqliteClient: {create: () => sandbox.createStubInstance(SqliteClient)}},
'../../db-utils/server': {getTestsTreeFromDatabase},
@@ -116,6 +95,8 @@ describe('lib/gui/tool-runner/index', () => {
}).ToolRunner;
sandbox.stub(logger, 'warn');
+
+ sandbox.stub(process, 'cwd').returns('/ref/cwd');
});
afterEach(() => sandbox.restore());
@@ -123,9 +104,9 @@ describe('lib/gui/tool-runner/index', () => {
describe('initialize', () => {
it('should set values added through api', () => {
const htmlReporter = {emit: sandbox.stub(), values: {foo: 'bar'}, config: {}, imagesSaver: {}};
- testplane = stubTool(stubConfig(), {}, {}, htmlReporter);
+ toolAdapter = stubToolAdapter({htmlReporter});
- const gui = initGuiReporter(testplane);
+ const gui = initGuiReporter({toolAdapter});
return gui.initialize()
.then(() => assert.calledWith(reportBuilder.setApiValues, {foo: 'bar'}));
@@ -133,109 +114,34 @@ describe('lib/gui/tool-runner/index', () => {
describe('correctly pass options to "readTests" method', () => {
it('should pass "paths" option', () => {
- const gui = initGuiReporter(testplane, {paths: ['foo', 'bar']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo', 'bar']});
return gui.initialize()
- .then(() => assert.calledOnceWith(testplane.readTests, ['foo', 'bar']));
+ .then(() => assert.calledOnceWith(toolAdapter.readTests, ['foo', 'bar']));
});
- it('should pass "grep", "sets" and "browsers" options', () => {
- const grep = 'foo';
- const set = 'bar';
- const browser = 'yabro';
+ it('should pass cli options', () => {
+ const cliTool = {grep: 'foo', set: 'bar', browser: 'yabro'};
- const gui = initGuiReporter(testplane, {
- configs: {
- program: {grep, set, browser}
+ const gui = initGuiReporter({
+ toolAdapter,
+ cli: {
+ tool: cliTool,
+ options: {}
}
});
return gui.initialize()
.then(() => {
- assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({grep, sets: set, browsers: browser}));
- });
- });
-
- describe('"replMode" option', () => {
- it('should be disabled by default', async () => {
- const gui = initGuiReporter(testplane, {
- configs: {
- program: {}
- }
- });
-
- await gui.initialize();
-
- assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
- replMode: {
- enabled: false,
- beforeTest: false,
- onFail: false
- }
- }));
- });
-
- it('should be enabled when specify "repl" flag', async () => {
- const gui = initGuiReporter(testplane, {
- configs: {
- program: {repl: true}
- }
- });
-
- await gui.initialize();
-
- assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
- replMode: {
- enabled: true,
- beforeTest: false,
- onFail: false
- }
- }));
- });
-
- it('should be enabled when specify "beforeTest" flag', async () => {
- const gui = initGuiReporter(testplane, {
- configs: {
- program: {replBeforeTest: true}
- }
- });
-
- await gui.initialize();
-
- assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
- replMode: {
- enabled: true,
- beforeTest: true,
- onFail: false
- }
- }));
- });
-
- it('should be enabled when specify "onFail" flag', async () => {
- const gui = initGuiReporter(testplane, {
- configs: {
- program: {replOnFail: true}
- }
+ assert.calledOnceWith(toolAdapter.readTests, sinon.match.any, cliTool);
});
-
- await gui.initialize();
-
- assert.calledOnceWith(testplane.readTests, sinon.match.any, sinon.match({
- replMode: {
- enabled: true,
- beforeTest: false,
- onFail: true
- }
- }));
- });
});
});
it('should not add disabled test to report', () => {
- const testplane = stubTool();
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_({disabled: true})}));
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_({disabled: true})}));
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
return gui.initialize()
.then(() => {
@@ -244,10 +150,9 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should not add silently skipped test to report', () => {
- const testplane = stubTool();
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_({silentSkip: true})}));
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_({silentSkip: true})}));
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
return gui.initialize()
.then(() => {
@@ -256,12 +161,10 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should not add test from silently skipped suite to report', () => {
- const testplane = stubTool();
const silentlySkippedSuite = mkSuite({silentSkip: true});
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_({parent: silentlySkippedSuite})}));
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_({parent: silentlySkippedSuite})}));
-
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
return gui.initialize()
.then(() => {
@@ -270,132 +173,169 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should add skipped test to report', () => {
- const testplane = stubTool();
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_({pending: true})}));
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_({pending: true})}));
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
return gui.initialize()
.then(() => assert.calledOnce(reportBuilder.addTestResult));
});
it('should add idle test to report', () => {
- const testplane = stubTool();
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_()}));
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_()}));
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
return gui.initialize()
.then(() => assert.calledOnce(reportBuilder.addTestResult));
});
- it('should subscribe on events before read tests', () => {
- const testplane = stubTool();
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_()}));
+ it('should handle test results before read tests', () => {
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_()}));
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
return gui.initialize()
- .then(() => assert.callOrder(subscribeOnToolEvents, testplane.readTests));
+ .then(() => assert.callOrder(toolAdapter.handleTestResults, toolAdapter.readTests));
});
it('should initialize report builder after read tests for the correct order of events', async () => {
- const testplane = stubTool();
- testplane.readTests.resolves(mkTestCollection_({bro: stubTest_()}));
- const gui = initGuiReporter(testplane, {paths: ['foo']});
+ toolAdapter.readTests.resolves(mkTestCollection_({bro: stubTest_()}));
+ const gui = initGuiReporter({toolAdapter, paths: ['foo']});
await gui.initialize();
- assert.callOrder(testplane.readTests, testplane.htmlReporter.emit);
- assert.calledOnceWith(testplane.htmlReporter.emit, PluginEvents.DATABASE_CREATED, sinon.match.any);
+ assert.callOrder(toolAdapter.readTests, toolAdapter.htmlReporter.emit);
+ assert.calledOnceWith(toolAdapter.htmlReporter.emit, PluginEvents.DATABASE_CREATED, sinon.match.any);
});
});
describe('updateReferenceImage', () => {
- describe('should emit "UPDATE_REFERENCE" event', () => {
- it('should emit "UPDATE_REFERENCE" event with state and reference data', async () => {
- const testRefUpdateData = [{
- id: 'some-id',
- fullTitle: () => 'some-title',
- browserId: 'yabro',
- suite: {path: ['suite1']},
- state: {},
- metaInfo: {},
- imagesInfo: [{
+ it('should update reference for one image', async () => {
+ const testRefUpdateData = [{
+ id: 'some-id',
+ fullTitle: () => 'some-title',
+ browserId: 'yabro',
+ suite: {path: ['suite1']},
+ state: {},
+ metaInfo: {},
+ imagesInfo: [{
+ status: UPDATED,
+ stateName: 'plain1',
+ actualImg: {
+ size: {height: 100, width: 200}
+ }
+ }]
+ }];
+
+ const getScreenshotPath = sandbox.stub().returns('/ref/path1');
+ const config = stubConfig({
+ browsers: {yabro: {getScreenshotPath}}
+ });
+
+ const testCollection = mkTestCollection_({'some-title.yabro': testRefUpdateData[0]});
+ const toolAdapter = stubToolAdapter({config, testCollection});
+
+ const gui = initGuiReporter({toolAdapter});
+ await gui.initialize();
+
+ await gui.updateReferenceImage(testRefUpdateData);
+
+ assert.calledOnceWith(toolAdapter.updateReference, {
+ refImg: {path: '/ref/path1', relativePath: '../path1', size: {height: 100, width: 200}},
+ state: 'plain1'
+ });
+ });
+
+ it('should update reference for each image', async () => {
+ const tests = [{
+ id: 'some-id',
+ fullTitle: () => 'some-title',
+ browserId: 'yabro',
+ suite: {path: ['suite1']},
+ state: {},
+ metaInfo: {},
+ imagesInfo: [
+ {
status: UPDATED,
stateName: 'plain1',
actualImg: {
size: {height: 100, width: 200}
}
- }]
- }];
-
- const getScreenshotPath = sandbox.stub().returns('/ref/path1');
- const config = stubConfig({
- browsers: {yabro: {getScreenshotPath}}
- });
- const testplane = mkTestplane_(config, {'some-title.yabro': testRefUpdateData[0]});
- const gui = initGuiReporter(testplane, {pluginConfig: {path: 'report-path'}});
- await gui.initialize();
+ },
+ {
+ status: UPDATED,
+ stateName: 'plain2',
+ actualImg: {
+ size: {height: 200, width: 300}
+ }
+ }
+ ]
+ }];
- await gui.updateReferenceImage(testRefUpdateData);
+ const getScreenshotPath = sandbox.stub()
+ .onFirstCall().returns('/ref/path1')
+ .onSecondCall().returns('/ref/path2');
- assert.calledOnceWith(testplane.emit, 'updateReference', {
- refImg: {path: '/ref/path1', size: {height: 100, width: 200}},
- state: 'plain1'
- });
+ const config = stubConfig({
+ browsers: {yabro: {getScreenshotPath}}
});
- it('for each image info', async () => {
- const tests = [{
- id: 'some-id',
- fullTitle: () => 'some-title',
- browserId: 'yabro',
- suite: {path: ['suite1']},
- state: {},
- metaInfo: {},
- imagesInfo: [
- {
- status: UPDATED,
- stateName: 'plain1',
- actualImg: {
- size: {height: 100, width: 200}
- }
- },
- {
- status: UPDATED,
- stateName: 'plain2',
- actualImg: {
- size: {height: 200, width: 300}
- }
- }
- ]
- }];
+ const testCollection = mkTestCollection_({'some-title.yabro': tests[0]});
+ const toolAdapter = stubToolAdapter({config, testCollection});
- const getScreenshotPath = sandbox.stub()
- .onFirstCall().returns('/ref/path1')
- .onSecondCall().returns('/ref/path2');
+ const gui = initGuiReporter({toolAdapter});
+ await gui.initialize();
- const config = stubConfig({
- browsers: {yabro: {getScreenshotPath}}
- });
+ await gui.updateReferenceImage(tests);
- const testplane = mkTestplane_(config, {'some-title.yabro': tests[0]});
- const gui = initGuiReporter(testplane);
- await gui.initialize();
+ assert.calledTwice(toolAdapter.updateReference);
+ assert.calledWith(toolAdapter.updateReference.firstCall, {
+ refImg: {path: '/ref/path1', relativePath: '../path1', size: {height: 100, width: 200}},
+ state: 'plain1'
+ });
+ assert.calledWith(toolAdapter.updateReference.secondCall, {
+ refImg: {path: '/ref/path2', relativePath: '../path2', size: {height: 200, width: 300}},
+ state: 'plain2'
+ });
+ });
- await gui.updateReferenceImage(tests);
+ it('should determine status based on the latest result', async () => {
+ const testRefUpdateData = [{
+ id: 'some-id',
+ fullTitle: () => 'some-title',
+ browserId: 'yabro',
+ suite: {path: ['suite1']},
+ state: {},
+ metaInfo: {},
+ imagesInfo: [{
+ status: UPDATED,
+ stateName: 'plain1',
+ actualImg: {
+ size: {height: 100, width: 200}
+ }
+ }]
+ }];
- assert.calledTwice(testplane.emit);
- assert.calledWith(testplane.emit.firstCall, 'updateReference', {
- refImg: {path: '/ref/path1', size: {height: 100, width: 200}},
- state: 'plain1'
- });
- assert.calledWith(testplane.emit.secondCall, 'updateReference', {
- refImg: {path: '/ref/path2', size: {height: 200, width: 300}},
- state: 'plain2'
- });
+ const getScreenshotPath = sandbox.stub().returns('/ref/path1');
+ const config = stubConfig({
+ browsers: {yabro: {getScreenshotPath}}
});
+
+ const testCollection = mkTestCollection_({'some-title.yabro': testRefUpdateData[0]});
+ const toolAdapter = stubToolAdapter({config, testCollection});
+
+ reportBuilder.getLatestAttempt.withArgs({fullName: 'some-title', browserId: 'yabro'}).returns(100500);
+ reportBuilder.getUpdatedReferenceTestStatus.withArgs(sinon.match({attempt: 100500})).returns(TestStatus.UPDATED);
+
+ const gui = initGuiReporter({toolAdapter});
+ await gui.initialize();
+
+ reportBuilder.addTestResult.reset();
+
+ await gui.updateReferenceImage(testRefUpdateData);
+
+ assert.calledOnceWith(reportBuilder.addTestResult, sinon.match.any, {status: TestStatus.UPDATED});
});
});
@@ -429,8 +369,11 @@ describe('lib/gui/tool-runner/index', () => {
const config = stubConfig({
browsers: {yabro: {getScreenshotPath}}
});
- const testplane = mkTestplane_(config, {'some-title.yabro': tests[0]});
- const gui = initGuiReporter(testplane);
+
+ const testCollection = mkTestCollection_({'some-title.yabro': tests[0]});
+ const toolAdapter = stubToolAdapter({config, testCollection});
+
+ const gui = initGuiReporter({toolAdapter});
await gui.initialize();
return {gui, tests};
@@ -477,8 +420,11 @@ describe('lib/gui/tool-runner/index', () => {
let compareOpts;
beforeEach(() => {
- testplane = stubTool(stubConfig({tolerance: 100500, antialiasingTolerance: 500100}));
- testplane.readTests.resolves(mkTestCollection_());
+ toolAdapter = stubToolAdapter({
+ config: stubConfig({tolerance: 100500, antialiasingTolerance: 500100}),
+ reporterConfig: stubReporterConfig({path: 'report_path'})
+ });
+ toolAdapter.readTests.resolves(mkTestCollection_());
compareOpts = {
tolerance: 100500,
@@ -491,7 +437,7 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should stop comparison on first diff in reference images', async () => {
- const gui = initGuiReporter(testplane, {configs: mkPluginConfig_({path: 'report_path'})});
+ const gui = initGuiReporter({toolAdapter});
const refImagesInfo = mkImagesInfo({expectedImg: {path: 'ref-path-1'}});
const comparedImagesInfo = [mkImagesInfo({expectedImg: {path: 'ref-path-2'}})];
@@ -513,7 +459,7 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should stop comparison on diff in actual images', async () => {
- const gui = initGuiReporter(testplane, {configs: mkPluginConfig_({path: 'report_path'})});
+ const gui = initGuiReporter({toolAdapter});
const refImagesInfo = mkImagesInfo({actualImg: {path: 'act-path-1'}});
const comparedImagesInfo = [mkImagesInfo({actualImg: {path: 'act-path-2'}})];
@@ -536,7 +482,7 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should compare each diff cluster', async () => {
- const gui = initGuiReporter(testplane, {configs: mkPluginConfig_({path: 'report_path'})});
+ const gui = initGuiReporter({toolAdapter});
const refImagesInfo = mkImagesInfo({
diffClusters: [
{left: 0, top: 0, right: 5, bottom: 5},
@@ -559,7 +505,7 @@ describe('lib/gui/tool-runner/index', () => {
});
it('should return all found image ids with equal diffs', async () => {
- const gui = initGuiReporter(testplane);
+ const gui = initGuiReporter({toolAdapter});
const refImagesInfo = {...mkImagesInfo(), id: 'selected-img-1'};
const comparedImagesInfo = [
{...mkImagesInfo(), id: 'compared-img-2'},
@@ -576,89 +522,24 @@ describe('lib/gui/tool-runner/index', () => {
});
describe('run', () => {
- let runner;
- let collection;
-
- beforeEach(() => {
- runner = {run: sandbox.stub().resolves()};
- collection = mkTestCollection_();
- createTestRunner.withArgs(collection).returns(runner);
- testplane.readTests.resolves(collection);
- });
-
- describe('should run testplane with', () => {
- const run_ = async (globalCliOpts = {}) => {
- const configs = {...mkPluginConfig_(), ...mkToolCliOpts_(globalCliOpts)};
- const gui = ToolGuiReporter.create([], testplane, configs);
-
- await gui.initialize();
- await gui.run();
-
- const runHandler = runner.run.firstCall.args[0];
- runHandler(collection);
- };
-
- it('passed opts', async () => {
- await run_({grep: /some-grep/, set: 'some-set', browser: 'yabro', devtools: true});
+ it('should run tool with passed opts', async () => {
+ const cliTool = {grep: /some-grep/, set: 'some-set', browser: 'yabro', devtools: true};
+ const collection = mkTestCollection_();
+ toolAdapter.readTests.resolves(collection);
- assert.calledOnceWith(testplane.run, collection, sinon.match({grep: /some-grep/, sets: 'some-set', browsers: 'yabro', devtools: true}));
- });
-
- describe('"replMode" option', () => {
- it('should be disabled by default', async () => {
- await run_();
-
- assert.calledOnceWith(testplane.run, collection, sinon.match({
- replMode: {
- enabled: false,
- beforeTest: false,
- onFail: false
- }
- }));
- });
-
- it('should be enabled when specify "repl" flag', async () => {
- await run_({repl: true});
-
- assert.calledOnceWith(testplane.run, collection, sinon.match({
- replMode: {
- enabled: true,
- beforeTest: false,
- onFail: false
- }
- }));
- });
+ const gui = initGuiReporter({toolAdapter, cli: {tool: cliTool, options: {}}});
+ const tests = [];
- it('should be enabled when specify "beforeTest" flag', async () => {
- await run_({replBeforeTest: true});
-
- assert.calledOnceWith(testplane.run, collection, sinon.match({
- replMode: {
- enabled: true,
- beforeTest: true,
- onFail: false
- }
- }));
- });
-
- it('should be enabled when specify "onFail" flag', async () => {
- await run_({replOnFail: true});
+ await gui.initialize();
+ await gui.run(tests);
- assert.calledOnceWith(testplane.run, collection, sinon.match({
- replMode: {
- enabled: true,
- beforeTest: false,
- onFail: true
- }
- }));
- });
- });
+ assert.calledOnceWith(toolAdapter.run, collection, tests, cliTool);
});
});
- describe('finalize testplane', () => {
+ describe('finalize tool', () => {
it('should call reportBuilder.finalize', async () => {
- const gui = initGuiReporter(testplane);
+ const gui = initGuiReporter({toolAdapter});
await gui.initialize();
gui.finalize();
@@ -670,9 +551,11 @@ describe('lib/gui/tool-runner/index', () => {
describe('reuse tests tree from database', () => {
let gui;
let dbPath;
+ let toolAdapter;
beforeEach(() => {
- gui = initGuiReporter(testplane, {configs: mkPluginConfig_({path: 'report_path'})});
+ toolAdapter = stubToolAdapter({reporterConfig: stubReporterConfig({path: 'report_path'})});
+ gui = initGuiReporter({toolAdapter});
dbPath = path.resolve('report_path', LOCAL_DATABASE_NAME);
sandbox.stub(fs, 'pathExists').withArgs(dbPath).resolves(false);
@@ -716,8 +599,7 @@ describe('lib/gui/tool-runner/index', () => {
it('"autoRun" from gui options', async () => {
const guiOpts = {autoRun: true};
- const configs = {...mkPluginConfig_(), ...mkToolCliOpts_({}, guiOpts)};
- const gui = ToolGuiReporter.create([], testplane, configs);
+ const gui = initGuiReporter({toolAdapter, cli: {options: guiOpts}});
await gui.initialize();
diff --git a/test/unit/lib/images-info-saver.ts b/test/unit/lib/images-info-saver.ts
index 69708dda7..d28fdc81d 100644
--- a/test/unit/lib/images-info-saver.ts
+++ b/test/unit/lib/images-info-saver.ts
@@ -1,7 +1,7 @@
import * as fsOriginal from 'fs-extra';
import {ImagesInfoSaver as ImagesInfoSaverOriginal} from 'lib/images-info-saver';
import {Writable} from 'type-fest';
-import {ReporterTestResult} from 'lib/test-adapter';
+import {ReporterTestResult} from 'lib/adapters/test-result';
import {
ImageBase64,
ImageBuffer,
diff --git a/test/unit/lib/merge-reports/index.js b/test/unit/lib/merge-reports/index.js
index b9d26c807..2c61ca32d 100644
--- a/test/unit/lib/merge-reports/index.js
+++ b/test/unit/lib/merge-reports/index.js
@@ -2,17 +2,17 @@
const _ = require('lodash');
const proxyquire = require('proxyquire');
-const {stubTool, stubConfig} = require('../../utils');
+const {stubToolAdapter} = require('../../utils');
const originalServerUtils = require('lib/server-utils');
describe('lib/merge-reports', () => {
const sandbox = sinon.sandbox.create();
let htmlReporter, serverUtils, mergeReports, axiosStub;
- const execMergeReports_ = async ({pluginConfig = stubConfig(), testplane = stubTool(stubConfig()), paths = [], opts = {}}) => {
+ const execMergeReports_ = async ({toolAdapter = stubToolAdapter(), paths = [], opts = {}}) => {
opts = _.defaults(opts, {destination: 'default-dest-report/path'});
- await mergeReports(pluginConfig, testplane, paths, opts);
+ await mergeReports(toolAdapter, paths, opts);
};
beforeEach(() => {
@@ -71,11 +71,10 @@ describe('lib/merge-reports', () => {
});
describe('should send headers to request json urls', () => {
- let pluginConfig, testplane, paths, destPath;
+ let toolAdapter, paths, destPath;
beforeEach(() => {
- pluginConfig = stubConfig();
- testplane = stubTool(pluginConfig, {}, {}, htmlReporter);
+ toolAdapter = stubToolAdapter({htmlReporter});
paths = ['src-report/path-1.json'];
destPath = 'dest-report/path';
});
@@ -87,7 +86,7 @@ describe('lib/merge-reports', () => {
it('from environment variable', async () => {
process.env['html_reporter_headers'] = '{"foo":"bar","baz":"qux"}';
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers: []}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers: []}});
assert.calledOnceWith(
axiosStub.get,
@@ -103,7 +102,7 @@ describe('lib/merge-reports', () => {
it('from cli option', async () => {
const headers = ['foo=bar', 'baz=qux'];
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers}});
assert.calledOnceWith(
axiosStub.get,
@@ -121,7 +120,7 @@ describe('lib/merge-reports', () => {
const headers = ['foo=123', 'abc=def'];
axiosStub.get.withArgs('src-report/path-1.json').resolves({data: {jsonUrls: ['src-report/path-2.json'], dbUrls: []}});
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers}});
assert.calledTwice(axiosStub.get);
assert.calledWith(
@@ -146,20 +145,18 @@ describe('lib/merge-reports', () => {
});
it('should merge reports', async () => {
- const pluginConfig = stubConfig();
- const testplane = stubTool(pluginConfig, {}, {}, htmlReporter);
+ const toolAdapter = stubToolAdapter({htmlReporter});
const paths = ['src-report/path-1.json', 'src-report/path-2.db'];
const destPath = 'dest-report/path';
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers: []}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers: []}});
- assert.calledOnceWith(serverUtils.saveStaticFilesToReportDir, testplane.htmlReporter, pluginConfig, destPath);
+ assert.calledOnceWith(serverUtils.saveStaticFilesToReportDir, toolAdapter.htmlReporter, toolAdapter.reporterConfig, destPath);
assert.calledOnceWith(serverUtils.writeDatabaseUrlsFile, destPath, paths);
});
it('should resolve json urls while merging reports', async () => {
- const pluginConfig = stubConfig();
- const testplane = stubTool(pluginConfig, {}, {}, htmlReporter);
+ const toolAdapter = stubToolAdapter({htmlReporter});
const paths = ['src-report/path-1.json'];
const destPath = 'dest-report/path';
@@ -168,44 +165,42 @@ describe('lib/merge-reports', () => {
axiosStub.get.withArgs('src-report/path-3.json').resolves({data: {jsonUrls: ['src-report/path-4.json'], dbUrls: ['path-3.db']}});
axiosStub.get.withArgs('src-report/path-4.json').resolves({data: {jsonUrls: [], dbUrls: ['path-4.db']}});
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers: []}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers: []}});
assert.calledOnceWith(serverUtils.writeDatabaseUrlsFile, destPath, ['path-1.db', 'path-2.db', 'path-3.db', 'path-4.db']);
});
it('should normalize urls while merging reports', async () => {
- const pluginConfig = stubConfig();
- const testplane = stubTool(pluginConfig, {}, {}, htmlReporter);
+ const toolAdapter = stubToolAdapter({htmlReporter});
const paths = ['src-report/path-1.json'];
const destPath = 'dest-report/path';
axiosStub.get.withArgs('src-report/path-1.json').resolves({data: {jsonUrls: ['https://foo.bar/path-2.json']}});
axiosStub.get.withArgs('https://foo.bar/path-2.json').resolves({data: {jsonUrls: [], dbUrls: ['sqlite.db']}});
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers: []}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers: []}});
assert.calledOnceWith(serverUtils.writeDatabaseUrlsFile, destPath, ['https://foo.bar/sqlite.db']);
});
it('should fallback to json url while merging reports', async () => {
- const pluginConfig = stubConfig();
- const testplane = stubTool(pluginConfig, {}, {}, htmlReporter);
+ const toolAdapter = stubToolAdapter({htmlReporter});
const paths = ['src-report/path-1.json'];
const destPath = 'dest-report/path';
axiosStub.get.rejects();
- await execMergeReports_({pluginConfig, testplane, paths, opts: {destPath, headers: []}});
+ await execMergeReports_({toolAdapter, paths, opts: {destPath, headers: []}});
assert.calledOnceWith(serverUtils.writeDatabaseUrlsFile, destPath, ['src-report/path-1.json']);
});
it('should emit REPORT_SAVED event', async () => {
- const testplane = stubTool({}, {}, {}, htmlReporter);
+ const toolAdapter = stubToolAdapter({htmlReporter});
const destPath = 'dest-report/path';
- await execMergeReports_({pluginConfig: {}, testplane, paths: [''], opts: {destPath, headers: []}});
+ await execMergeReports_({toolAdapter, paths: [''], opts: {destPath, headers: []}});
- assert.calledOnceWith(testplane.htmlReporter.emitAsync, 'reportSaved', {reportPath: destPath});
+ assert.calledOnceWith(toolAdapter.htmlReporter.emitAsync, 'reportSaved', {reportPath: destPath});
});
});
diff --git a/test/unit/lib/report-builder/gui.js b/test/unit/lib/report-builder/gui.js
index cbd0204b4..1ad578f0f 100644
--- a/test/unit/lib/report-builder/gui.js
+++ b/test/unit/lib/report-builder/gui.js
@@ -4,7 +4,7 @@ const fs = require('fs-extra');
const _ = require('lodash');
const proxyquire = require('proxyquire');
const serverUtils = require('lib/server-utils');
-const {TestplaneTestAdapter} = require('lib/test-adapter/testplane');
+const {TestplaneTestResultAdapter} = require('lib/adapters/test-result/testplane');
const {SqliteClient} = require('lib/sqlite-client');
const {GuiTestsTreeBuilder} = require('lib/tests-tree-builder/gui');
const {HtmlReporter} = require('lib/plugin-api');
@@ -33,7 +33,7 @@ describe('GuiReportBuilder', () => {
htmlReporter
};
- TestplaneTestAdapter.create = (obj) => obj;
+ TestplaneTestResultAdapter.create = (obj) => obj;
dbClient = await SqliteClient.create({htmlReporter, reportPath: TEST_REPORT_PATH});
imagesInfoSaver = sinon.createStubInstance(ImagesInfoSaver);
@@ -100,7 +100,7 @@ describe('GuiReportBuilder', () => {
}).StaticReportBuilder
},
'../server-utils': {hasImage, deleteFile},
- '../test-adapter/utils': {copyAndUpdate}
+ '../adapters/test-result/utils': {copyAndUpdate}
}).GuiReportBuilder;
sandbox.stub(GuiTestsTreeBuilder, 'create').returns(Object.create(GuiTestsTreeBuilder.prototype));
diff --git a/test/unit/lib/server-utils.js b/test/unit/lib/server-utils.js
index 41c77f982..89116dd5f 100644
--- a/test/unit/lib/server-utils.js
+++ b/test/unit/lib/server-utils.js
@@ -1,8 +1,6 @@
'use strict';
const path = require('path');
-
-const Promise = require('bluebird');
const _ = require('lodash');
const proxyquire = require('proxyquire');
const sinon = require('sinon');
@@ -242,51 +240,6 @@ describe('server-utils', () => {
});
});
- describe('initializeCustomGui', () => {
- it('should initialize each group of controls if initialize-function is available', async () => {
- const initializeSpy1 = sinon.spy().named('initialize-1');
- const initializeSpy2 = sinon.spy().named('initialize-2');
-
- const initialize1 = sinon.stub().callsFake(() => Promise.delay(5).then(initializeSpy1));
- const initialize2 = sinon.stub().callsFake(() => Promise.delay(10).then(initializeSpy2));
-
- const ctx1 = {initialize: initialize1};
- const ctx2 = {initialize: initialize2};
-
- const pluginConfig = {
- customGui: {'section-1': [ctx1], 'section-2': [ctx2]}
- };
- const testplane = {};
-
- await utils.initializeCustomGui(testplane, pluginConfig);
-
- assert.calledOnceWith(initialize1, {testplane, hermione: testplane, ctx: ctx1});
- assert.calledOnceWith(initialize2, {testplane, hermione: testplane, ctx: ctx2});
-
- assert.callOrder(initializeSpy1, initializeSpy2);
- });
- });
-
- describe('runCustomGuiAction', () => {
- it('should run action for specified controls', async () => {
- const actionSpy = sinon.spy().named('action');
- const action = sinon.stub().callsFake(() => Promise.delay(10).then(actionSpy));
- const control = {};
- const ctx = {controls: [control], action};
- const pluginConfig = {customGui: {'section': [ctx]}};
- const testplane = {};
-
- await utils.runCustomGuiAction(testplane, pluginConfig, {
- sectionName: 'section',
- groupIndex: 0,
- controlIndex: 0
- });
-
- assert.calledOnceWith(action, {testplane, hermione: testplane, ctx, control});
- assert.calledOnce(actionSpy);
- });
- });
-
describe('forEachPlugin', () => {
it('should call the callback for each plugin only once', () => {
const plugins = [
diff --git a/test/unit/lib/static/components/controls/run-button.jsx b/test/unit/lib/static/components/controls/run-button.jsx
index 4557cde22..d296925a1 100644
--- a/test/unit/lib/static/components/controls/run-button.jsx
+++ b/test/unit/lib/static/components/controls/run-button.jsx
@@ -5,10 +5,12 @@ import {mkConnectedComponent, mkState} from '../utils';
describe('', () => {
const sandbox = sinon.sandbox.create();
- let RunButton, useLocalStorageStub, actionsStub, selectorsStub;
+ let RunButton, useLocalStorageStub, actionsStub, selectorsStub, writeValueStub;
beforeEach(() => {
+ writeValueStub = sandbox.stub();
useLocalStorageStub = sandbox.stub().returns([true]);
+ useLocalStorageStub.withArgs('RunMode', 'Failed').returns(['All', writeValueStub]);
actionsStub = {
runAllTests: sandbox.stub().returns({type: 'some-type'}),
runFailedTests: sandbox.stub().returns({type: 'some-type'}),
@@ -69,6 +71,7 @@ describe('', () => {
});
it('should call "runFailedTests" action on "Run failed tests" click', () => {
+ useLocalStorageStub.withArgs('RunMode', 'Failed').returns(['Failed', () => {}]);
const failedTests = [{testName: 'suite test', browserName: 'yabro'}];
const state = mkState({initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}});
selectorsStub.getFailedTests.withArgs(state).returns(failedTests);
@@ -81,6 +84,7 @@ describe('', () => {
});
it('should call "retrySuite" action on "Run checked tests" click', () => {
+ useLocalStorageStub.withArgs('RunMode', 'Failed').returns(['Checked', () => {}]);
const checkedTests = [{testName: 'suite test', browserName: 'yabro'}];
const state = mkState({initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}});
selectorsStub.getCheckedTests.withArgs(state).returns(checkedTests);
@@ -110,33 +114,46 @@ describe('', () => {
assert.equal(component.find('button').text(), 'Run all tests');
});
- it('should be "Run checked tests" if there are checked tests', () => {
+ it('should switch to "Run checked tests" if there are checked tests', () => {
selectorsStub.getCheckedTests.returns([{testName: 'testName', browserName: 'browserName'}]);
const component = mkConnectedComponent(, {
initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}
});
- assert.equal(component.find('button').text(), 'Run checked tests');
+ assert.calledWith(writeValueStub, 'Checked');
+ });
+ });
+
+ describe('localStorage', () => {
+ it('should save "Run all tests" if picked', () => {
+ selectorsStub.getCheckedTests.returns([{testName: 'testName', browserName: 'browserName'}]);
+ selectorsStub.getFailedTests.returns([{testName: 'testName', browserName: 'browserName'}]);
+ const component = mkConnectedComponent(, {
+ initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}
+ });
+
+ component.find({children: 'All'}).simulate('click');
+ assert.calledWith(writeValueStub, 'All');
});
- it('should be "Run failed tests" if picked', () => {
+ it('should save "Run failed tests" if picked', () => {
selectorsStub.getFailedTests.returns([{testName: 'testName', browserName: 'browserName'}]);
const component = mkConnectedComponent(, {
initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}
});
component.find({children: 'Failed'}).simulate('click');
- assert.equal(component.find('button').text(), 'Run failed tests');
+ assert.calledOnceWith(writeValueStub, 'Failed');
});
- it('should be "Run checked tests" if picked', () => {
+ it('should save "Run checked tests" if picked', () => {
selectorsStub.getCheckedTests.returns([{testName: 'testName', browserName: 'browserName'}]);
const component = mkConnectedComponent(, {
initialState: {tree: {suites: {allRootIds: ['suite']}}, processing: false}
});
component.find({children: 'Checked'}).simulate('click');
- assert.equal(component.find('button').text(), 'Run checked tests');
+ assert.calledWith(writeValueStub, 'Checked');
});
});
diff --git a/test/unit/lib/static/components/suites.jsx b/test/unit/lib/static/components/suites.jsx
index 1b371642d..a3bde3e13 100644
--- a/test/unit/lib/static/components/suites.jsx
+++ b/test/unit/lib/static/components/suites.jsx
@@ -41,6 +41,9 @@ describe('', () => {
});
it('should render few section common components', () => {
+ if (!global.SVGElement) {
+ global.SVGElement = HTMLElement; // Without this line test throws an error "ReferenceError: SVGElement is not defined"
+ }
getVisibleRootSuiteIds.returns(['suite-id-1', 'suite-id-2']);
const component = mkSuitesComponent();
diff --git a/test/unit/lib/static/components/utils.jsx b/test/unit/lib/static/components/utils.jsx
index a89aa61aa..a9cdd1990 100644
--- a/test/unit/lib/static/components/utils.jsx
+++ b/test/unit/lib/static/components/utils.jsx
@@ -3,6 +3,7 @@ import _ from 'lodash';
import configureStore from 'redux-mock-store';
import {Provider} from 'react-redux';
import defaultState from 'lib/static/modules/default-state';
+import { ThemeProvider } from '@gravity-ui/uikit';
exports.mkState = ({initialState} = {}) => {
return _.defaultsDeep(initialState, defaultState);
@@ -17,7 +18,7 @@ exports.mkStore = ({initialState, state} = {}) => {
exports.mkConnectedComponent = (Component, state) => {
const store = exports.mkStore(state);
- return mount({Component});
+ return mount({Component});
};
exports.mkImg_ = (opts = {}) => {
diff --git a/test/unit/testplane.js b/test/unit/testplane.js
index 2781e6bbb..5dd31ab86 100644
--- a/test/unit/testplane.js
+++ b/test/unit/testplane.js
@@ -19,7 +19,7 @@ describe('lib/testplane', () => {
const sandbox = sinon.createSandbox();
let testplane;
let cacheExpectedPaths = new Map(), cacheAllImages = new Map(), cacheDiffImages = new Map();
- let fs, originalUtils, utils, SqliteClient, ImagesInfoSaver, TestAdapter, StaticReportBuilder, HtmlReporter, runHtmlReporter;
+ let fs, originalUtils, utils, SqliteClient, ImagesInfoSaver, TestResultAdapter, StaticReportBuilder, HtmlReporter, runHtmlReporter;
let program;
@@ -115,15 +115,15 @@ describe('lib/testplane', () => {
'./server-utils': utils
}).ImagesInfoSaver;
- TestAdapter = proxyquire('lib/test-adapter', {
+ TestResultAdapter = proxyquire('lib/adapters/test-result', {
'fs-extra': fs,
'./server-utils': utils
- }).TestAdapter;
+ }).TestResultAdapter;
StaticReportBuilder = proxyquire('lib/report-builder/static', {
'fs-extra': fs,
'../server-utils': utils,
- '../test-adapter': {TestAdapter},
+ '../adapters/test-result': {TestResultAdapter},
'../images-info-saver': {ImagesInfoSaver}
}).StaticReportBuilder;
diff --git a/test/unit/utils.js b/test/unit/utils.js
index b77989de7..1aec99eb0 100644
--- a/test/unit/utils.js
+++ b/test/unit/utils.js
@@ -14,6 +14,10 @@ function stubConfig(config = {}) {
return Object.assign(config, browserConfigs);
}
+function stubReporterConfig(opts = {}) {
+ return _.defaults(opts, {path: 'default-path'});
+}
+
const stubTestCollection = (testsTree = {}) => {
return {
eachTest: (cb) => {
@@ -31,6 +35,7 @@ function stubTool(config = stubConfig(), events = {}, errors = {}, htmlReporter)
tool.run = sinon.stub().resolves(false);
tool.readTests = sinon.stub().resolves(stubTestCollection());
+ tool.halt = sinon.stub();
tool.htmlReporter = htmlReporter || sinon.createStubInstance(HtmlReporter);
_.defaultsDeep(tool.htmlReporter, {
emitAsync: sinon.stub(),
@@ -46,6 +51,29 @@ function stubTool(config = stubConfig(), events = {}, errors = {}, htmlReporter)
return tool;
}
+function stubToolAdapter({
+ config = stubConfig(), reporterConfig = stubReporterConfig(), testCollection = stubTestCollection(), htmlReporter
+} = {}) {
+ const toolAdapter = {
+ config,
+ reporterConfig,
+ htmlReporter: htmlReporter || sinon.createStubInstance(HtmlReporter),
+ run: sinon.stub().resolves(false),
+ readTests: sinon.stub().resolves(testCollection),
+ updateReference: sinon.stub(),
+ handleTestResults: sinon.stub(),
+ guiApi: {
+ initServer: sinon.stub(),
+ serverReady: sinon.stub()
+ }
+ };
+
+ sinon.stub(toolAdapter.htmlReporter, 'imagesSaver').value({saveImg: sinon.stub()});
+ sinon.stub(toolAdapter.htmlReporter, 'config').value({});
+
+ return toolAdapter;
+}
+
function mkSuite(opts = {}) {
return _.defaults(opts, {
name: _.last(opts.suitePath) || 'default-suite',
@@ -153,8 +181,10 @@ class ImageDiffError extends Error {
module.exports = {
stubConfig,
+ stubReporterConfig,
stubTestCollection,
stubTool,
+ stubToolAdapter,
mkSuite,
mkState,
mkBrowserResult,
diff --git a/testplane.ts b/testplane.ts
index 070eb23cc..c4d8bc762 100644
--- a/testplane.ts
+++ b/testplane.ts
@@ -6,19 +6,19 @@ import _ from 'lodash';
import PQueue from 'p-queue';
import {CommanderStatic} from '@gemini-testing/commander';
-import {cliCommands} from './lib/cli-commands';
+import {TestplaneToolAdapter} from './lib/adapters/tool/testplane';
+import {commands as cliCommands} from './lib/cli';
import {parseConfig} from './lib/config';
import {ToolName} from './lib/constants';
-import {HtmlReporter} from './lib/plugin-api';
import {StaticReportBuilder} from './lib/report-builder/static';
import {formatTestResult, logPathToHtmlReport, logError, getExpectedCacheKey} from './lib/server-utils';
import {SqliteClient} from './lib/sqlite-client';
-import {HtmlReporterApi, ReporterOptions, TestSpecByPath} from './lib/types';
+import {ReporterOptions, TestSpecByPath} from './lib/types';
import {createWorkers, CreateWorkersRunner} from './lib/workers/create-workers';
import {SqliteImageStore} from './lib/image-store';
import {Cache} from './lib/cache';
import {ImagesInfoSaver} from './lib/images-info-saver';
-import {getStatus} from './lib/test-adapter/testplane';
+import {getStatus} from './lib/adapters/test-result/testplane';
export default (testplane: Testplane, opts: Partial): void => {
if (testplane.isWorker()) {
@@ -31,9 +31,8 @@ export default (testplane: Testplane, opts: Partial): void => {
return;
}
- const htmlReporter = HtmlReporter.create(config, {toolName: ToolName.Testplane});
-
- (testplane as Testplane & HtmlReporterApi).htmlReporter = htmlReporter;
+ const toolAdapter = TestplaneToolAdapter.create({toolName: ToolName.Testplane, tool: testplane, reporterConfig: config});
+ const {htmlReporter} = toolAdapter;
let isCliCommandLaunched = false;
let handlingTestResults: Promise;
@@ -54,7 +53,7 @@ export default (testplane: Testplane, opts: Partial): void => {
testplane.on(testplane.events.CLI, (commander: CommanderStatic) => {
_.values(cliCommands).forEach((command: string) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
- require(path.resolve(__dirname, 'lib/cli-commands', command))(commander, config, testplane);
+ require(path.resolve(__dirname, 'lib/cli/commands', command))(commander, toolAdapter);
commander.prependListener(`command:${command}`, () => {
isCliCommandLaunched = true;