From 7b021b0b9b12362d65197f35c4913176b798b2ae Mon Sep 17 00:00:00 2001 From: shadowusr Date: Thu, 17 Aug 2023 16:36:57 +0300 Subject: [PATCH 01/25] chore: rename static report builder to typescript --- lib/report-builder/{static.js => static.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/report-builder/{static.js => static.ts} (99%) diff --git a/lib/report-builder/static.js b/lib/report-builder/static.ts similarity index 99% rename from lib/report-builder/static.js rename to lib/report-builder/static.ts index fa0f12e03..2845b3bc9 100644 --- a/lib/report-builder/static.js +++ b/lib/report-builder/static.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const _ = require('lodash'); From d28ecdbdcb9a4da4beb5d681a17703030a693bff Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 09:22:34 +0300 Subject: [PATCH 02/25] chore: rename plugin-events to typescript --- lib/constants/{plugin-events.js => plugin-events.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/constants/{plugin-events.js => plugin-events.ts} (89%) diff --git a/lib/constants/plugin-events.js b/lib/constants/plugin-events.ts similarity index 89% rename from lib/constants/plugin-events.js rename to lib/constants/plugin-events.ts index 402d90172..8d15f5b5f 100644 --- a/lib/constants/plugin-events.js +++ b/lib/constants/plugin-events.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const getSyncEvents = () => ({ From ab721d9da20d9661f7bbd32fa730d669db5d75cc Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 09:31:10 +0300 Subject: [PATCH 03/25] chore: re-write plugin-events to typescript --- lib/constants/index.ts | 1 + lib/constants/plugin-events.ts | 19 +++++-------------- lib/plugin-api.js | 2 +- test/unit/lib/plugin-api.js | 2 +- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/constants/index.ts b/lib/constants/index.ts index 3dfdc50ac..c961136c6 100644 --- a/lib/constants/index.ts +++ b/lib/constants/index.ts @@ -1,5 +1,6 @@ export * from './database'; export * from './diff-modes'; export * from './paths'; +export * from './plugin-events'; export * from './test-statuses'; export * from './view-modes'; diff --git a/lib/constants/plugin-events.ts b/lib/constants/plugin-events.ts index 8d15f5b5f..077f3c216 100644 --- a/lib/constants/plugin-events.ts +++ b/lib/constants/plugin-events.ts @@ -1,14 +1,5 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; - -const getSyncEvents = () => ({ - DATABASE_CREATED: 'databaseCreated', - TEST_SCREENSHOTS_SAVED: 'testScreenshotsSaved', - REPORT_SAVED: 'reportSaved' -}); - -const events = getSyncEvents(); -Object.defineProperty(events, 'getSync', {value: getSyncEvents, enumerable: false}); - -module.exports = events; +export enum PluginEvents { + DATABASE_CREATED = 'databaseCreated', + TEST_SCREENSHOTS_SAVED = 'testScreenshotsSaved', + REPORT_SAVED = 'reportSaved' +} diff --git a/lib/plugin-api.js b/lib/plugin-api.js index d0c0becc3..98cc88c51 100644 --- a/lib/plugin-api.js +++ b/lib/plugin-api.js @@ -1,7 +1,7 @@ 'use strict'; const EventsEmitter2 = require('eventemitter2'); -const pluginEvents = require('./constants/plugin-events'); +const {PluginEvents: pluginEvents} = require('./constants/plugin-events'); const {downloadDatabases, mergeDatabases, getTestsTreeFromDatabase} = require('./db-utils/server'); module.exports = class HtmlReporter extends EventsEmitter2 { diff --git a/test/unit/lib/plugin-api.js b/test/unit/lib/plugin-api.js index 114cca00f..df096a0cf 100644 --- a/test/unit/lib/plugin-api.js +++ b/test/unit/lib/plugin-api.js @@ -1,7 +1,7 @@ 'use strict'; const PluginApi = require('lib/plugin-api'); -const PluginEvents = require('lib/constants/plugin-events'); +const {PluginEvents} = require('lib/constants/plugin-events'); describe('plugin api', () => { it('should store extra items', () => { From 639e3eed817cbd0516bf14531a938380f8fb8c1c Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 09:55:57 +0300 Subject: [PATCH 04/25] chore: re-write local-images-saver to typescript --- lib/local-images-saver.js | 11 ----------- lib/local-images-saver.ts | 10 ++++++++++ lib/plugin-api.js | 2 +- test/unit/lib/local-images-saver.js | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) delete mode 100644 lib/local-images-saver.js create mode 100644 lib/local-images-saver.ts diff --git a/lib/local-images-saver.js b/lib/local-images-saver.js deleted file mode 100644 index 466fbc61d..000000000 --- a/lib/local-images-saver.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const utils = require('./server-utils'); - -module.exports = { - saveImg: async (srcCurrPath, {destPath, reportDir}) => { - await utils.copyFileAsync(srcCurrPath, destPath, {reportDir}); - - return destPath; - } -}; diff --git a/lib/local-images-saver.ts b/lib/local-images-saver.ts new file mode 100644 index 000000000..dea4dc890 --- /dev/null +++ b/lib/local-images-saver.ts @@ -0,0 +1,10 @@ +import {copyFileAsync} from './server-utils'; +import type {ImagesSaver} from './types'; + +export const LocalImagesSaver: ImagesSaver = { + saveImg: async (srcCurrPath, {destPath, reportDir}) => { + await copyFileAsync(srcCurrPath, destPath, {reportDir}); + + return destPath; + } +}; diff --git a/lib/plugin-api.js b/lib/plugin-api.js index 98cc88c51..afb18563a 100644 --- a/lib/plugin-api.js +++ b/lib/plugin-api.js @@ -16,7 +16,7 @@ module.exports = class HtmlReporter extends EventsEmitter2 { this._values = { extraItems: {}, metaInfoExtenders: {}, - imagesSaver: require('./local-images-saver'), + imagesSaver: require('./local-images-saver').LocalImagesSaver, reportsSaver: null }; this._version = require('../package.json').version; diff --git a/test/unit/lib/local-images-saver.js b/test/unit/lib/local-images-saver.js index 9988a949c..6d25e6dd6 100644 --- a/test/unit/lib/local-images-saver.js +++ b/test/unit/lib/local-images-saver.js @@ -14,7 +14,7 @@ describe('local-images-saver', () => { imagesSaver = proxyquire('lib/local-images-saver', { './server-utils': utils - }); + }).LocalImagesSaver; }); afterEach(() => sandbox.restore()); From 6d22922390c410c6ae92e54b2d1bdbd633c32131 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 10:09:46 +0300 Subject: [PATCH 05/25] chore: re-write browser constants to typescript --- lib/constants/browser.js | 3 --- lib/constants/browser.ts | 3 +++ lib/constants/index.ts | 1 + lib/static/modules/reducers/browsers.js | 2 +- lib/tests-tree-builder/base.js | 4 ++-- lib/tests-tree-builder/static.js | 6 +++--- test/unit/lib/tests-tree-builder/base.js | 4 ++-- test/unit/lib/tests-tree-builder/static.js | 4 ++-- 8 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 lib/constants/browser.js create mode 100644 lib/constants/browser.ts diff --git a/lib/constants/browser.js b/lib/constants/browser.js deleted file mode 100644 index 221371b0b..000000000 --- a/lib/constants/browser.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.versions = { - UNKNOWN: 'unknown' -}; diff --git a/lib/constants/browser.ts b/lib/constants/browser.ts new file mode 100644 index 000000000..52f0e7ac3 --- /dev/null +++ b/lib/constants/browser.ts @@ -0,0 +1,3 @@ +export enum BrowserVersions { + UNKNOWN = 'unknown' +} diff --git a/lib/constants/index.ts b/lib/constants/index.ts index c961136c6..ba1ae9c1d 100644 --- a/lib/constants/index.ts +++ b/lib/constants/index.ts @@ -1,3 +1,4 @@ +export * from './browser'; export * from './database'; export * from './diff-modes'; export * from './paths'; diff --git a/lib/static/modules/reducers/browsers.js b/lib/static/modules/reducers/browsers.js index 83f1ce14f..924c1ed1f 100644 --- a/lib/static/modules/reducers/browsers.js +++ b/lib/static/modules/reducers/browsers.js @@ -1,6 +1,6 @@ import _ from 'lodash'; import actionNames from '../action-names'; -import {versions as BrowserVersions} from '../../../constants/browser'; +import {BrowserVersions} from '../../../constants/browser'; export default (state, action) => { switch (action.type) { diff --git a/lib/tests-tree-builder/base.js b/lib/tests-tree-builder/base.js index 621ff31f4..085dd483f 100644 --- a/lib/tests-tree-builder/base.js +++ b/lib/tests-tree-builder/base.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const {determineStatus} = require('../common-utils'); -const {versions: browserVersions} = require('../constants/browser'); +const {BrowserVersions} = require('../constants/browser'); module.exports = class ResultsTreeBuilder { static create() { @@ -41,7 +41,7 @@ module.exports = class ResultsTreeBuilder { addTestResult(testResult, formattedResult) { const {testPath, browserId: browserName, attempt} = formattedResult; const {imagesInfo} = testResult; - const {browserVersion = browserVersions.UNKNOWN} = testResult.metaInfo; + const {browserVersion = BrowserVersions.UNKNOWN} = testResult.metaInfo; const suiteId = this._buildId(testPath); const browserId = this._buildId(suiteId, browserName); diff --git a/lib/tests-tree-builder/static.js b/lib/tests-tree-builder/static.js index 55dddccc0..b4d96fdc1 100644 --- a/lib/tests-tree-builder/static.js +++ b/lib/tests-tree-builder/static.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const BaseTestsTreeBuilder = require('./base'); const testStatus = require('../constants/test-statuses'); -const {versions: browserVersions} = require('../constants/browser'); +const {BrowserVersions} = require('../constants/browser'); const {DB_COLUMN_INDEXES} = require('../constants/database'); module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { @@ -60,7 +60,7 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { _calcStats(testResult, {testId, browserId, browserName}) { const {status} = testResult; const {browserVersion} = testResult.metaInfo; - const version = browserVersion || browserVersions.UNKNOWN; + const version = browserVersion || BrowserVersions.UNKNOWN; if (!this._stats.perBrowser[browserName]) { this._stats.perBrowser[browserName] = {}; @@ -173,6 +173,6 @@ function addBrowserVersion(browsers, testResult) { browsers[browserId] = new Set(); } - const {browserVersion = browserVersions.UNKNOWN} = testResult.metaInfo; + const {browserVersion = BrowserVersions.UNKNOWN} = testResult.metaInfo; browsers[browserId].add(browserVersion); } diff --git a/test/unit/lib/tests-tree-builder/base.js b/test/unit/lib/tests-tree-builder/base.js index 5dd11291b..8cfd1bca3 100644 --- a/test/unit/lib/tests-tree-builder/base.js +++ b/test/unit/lib/tests-tree-builder/base.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const proxyquire = require('proxyquire'); const {FAIL, ERROR, SUCCESS} = require('lib/constants/test-statuses'); -const {versions: browserVersions} = require('lib/constants/browser'); +const {BrowserVersions} = require('lib/constants/browser'); describe('ResultsTreeBuilder', () => { const sandbox = sinon.sandbox.create(); @@ -132,7 +132,7 @@ describe('ResultsTreeBuilder', () => { name: 'b1', parentId: 's1', resultIds: ['s1 b1 0'], - version: browserVersions.UNKNOWN + version: BrowserVersions.UNKNOWN } ); }); diff --git a/test/unit/lib/tests-tree-builder/static.js b/test/unit/lib/tests-tree-builder/static.js index ccc6b84b7..81fb6d79e 100644 --- a/test/unit/lib/tests-tree-builder/static.js +++ b/test/unit/lib/tests-tree-builder/static.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const StaticResultsTreeBuilder = require('lib/tests-tree-builder/static'); const {SUCCESS} = require('lib/constants/test-statuses'); -const {versions: browserVersions} = require('lib/constants/browser'); +const {BrowserVersions} = require('lib/constants/browser'); describe('StaticResultsTreeBuilder', () => { const sandbox = sinon.sandbox.create(); @@ -150,7 +150,7 @@ describe('StaticResultsTreeBuilder', () => { const {browsers} = builder.build(rows); - assert.deepEqual(browsers, [{id: 'yabro', versions: [browserVersions.UNKNOWN]}]); + assert.deepEqual(browsers, [{id: 'yabro', versions: [BrowserVersions.UNKNOWN]}]); }); it('with a few versions for the same browser', () => { From 15579a4aeddc7ed476ee906d8530cdf5430e333c Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 10:11:28 +0300 Subject: [PATCH 06/25] chore: rename base tests tree builder to typescript --- lib/tests-tree-builder/{base.js => base.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/tests-tree-builder/{base.js => base.ts} (99%) diff --git a/lib/tests-tree-builder/base.js b/lib/tests-tree-builder/base.ts similarity index 99% rename from lib/tests-tree-builder/base.js rename to lib/tests-tree-builder/base.ts index 085dd483f..5feff7e85 100644 --- a/lib/tests-tree-builder/base.js +++ b/lib/tests-tree-builder/base.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const _ = require('lodash'); From 057feed6186ac27d9069ac12267ac9f33dac8bb3 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 16:56:40 +0300 Subject: [PATCH 07/25] chore: re-write base tests tree builder to typescript --- lib/common-utils.ts | 20 ++-- lib/test-adapter.ts | 6 +- lib/tests-tree-builder/base.ts | 145 +++++++++++++++++------ lib/tests-tree-builder/gui.js | 2 +- lib/tests-tree-builder/static.js | 2 +- lib/types.ts | 8 +- test/unit/lib/tests-tree-builder/base.js | 2 +- 7 files changed, 130 insertions(+), 55 deletions(-) diff --git a/lib/common-utils.ts b/lib/common-utils.ts index 5e114172e..37bce0fa3 100644 --- a/lib/common-utils.ts +++ b/lib/common-utils.ts @@ -2,14 +2,14 @@ import crypto from 'crypto'; import {pick} from 'lodash'; import url from 'url'; import axios, {AxiosRequestConfig} from 'axios'; -import {SUCCESS, FAIL, ERROR, SKIPPED, UPDATED, IDLE, RUNNING, QUEUED} from './constants/test-statuses'; +import {SUCCESS, FAIL, ERROR, SKIPPED, UPDATED, IDLE, RUNNING, QUEUED, TestStatus} from './constants'; import {UNCHECKED, INDETERMINATE, CHECKED} from './constants/checked-statuses'; export const getShortMD5 = (str: string): string => { return crypto.createHash('md5').update(str, 'ascii').digest('hex').substr(0, 7); }; -const statusPriority: string[] = [ +const statusPriority: TestStatus[] = [ // non-final RUNNING, QUEUED, @@ -19,15 +19,15 @@ const statusPriority: string[] = [ export const logger = pick(console, ['log', 'warn', 'error']); -export const isSuccessStatus = (status: string): boolean => status === SUCCESS; -export const isFailStatus = (status: string): boolean => status === FAIL; -export const isIdleStatus = (status: string): boolean => status === IDLE; -export const isRunningStatus = (status: string): boolean => status === RUNNING; -export const isErroredStatus = (status: string): boolean => status === ERROR; -export const isSkippedStatus = (status: string): boolean => status === SKIPPED; -export const isUpdatedStatus = (status: string): boolean => status === UPDATED; +export const isSuccessStatus = (status: TestStatus): boolean => status === SUCCESS; +export const isFailStatus = (status: TestStatus): boolean => status === FAIL; +export const isIdleStatus = (status: TestStatus): boolean => status === IDLE; +export const isRunningStatus = (status: TestStatus): boolean => status === RUNNING; +export const isErroredStatus = (status: TestStatus): boolean => status === ERROR; +export const isSkippedStatus = (status: TestStatus): boolean => status === SKIPPED; +export const isUpdatedStatus = (status: TestStatus): boolean => status === UPDATED; -export const determineStatus = (statuses: string[]): string | null => { +export const determineStatus = (statuses: TestStatus[]): TestStatus | null => { if (!statuses.length) { return SUCCESS; } diff --git a/lib/test-adapter.ts b/lib/test-adapter.ts index f8e030610..10123f1c6 100644 --- a/lib/test-adapter.ts +++ b/lib/test-adapter.ts @@ -18,7 +18,7 @@ import { HtmlReporterApi, ImageInfo, ImagesSaver, - SuitesRow, + RawSuitesRow, TestResult, ImageData, ImageInfoFull, ImageDiffError, AssertViewResult, ImageInfoError, @@ -121,14 +121,14 @@ export class TestAdapter { const suitePath = getSuitePath(this._testResult); const suitePathString = JSON.stringify(suitePath); - const imagesInfoResult = this._sqliteAdapter.query | undefined>({ + const imagesInfoResult = this._sqliteAdapter.query | undefined>({ select: DB_COLUMNS.IMAGES_INFO, where: `${DB_COLUMNS.SUITE_PATH} = ? AND ${DB_COLUMNS.NAME} = ?`, orderBy: DB_COLUMNS.TIMESTAMP, orderDescending: true }, suitePathString, browserName); - const imagesInfo: ImageInfoFull[] = imagesInfoResult && JSON.parse(imagesInfoResult[DB_COLUMNS.IMAGES_INFO as keyof SuitesRow]) || []; + const imagesInfo: ImageInfoFull[] = imagesInfoResult && JSON.parse(imagesInfoResult[DB_COLUMNS.IMAGES_INFO as keyof RawSuitesRow]) || []; return imagesInfo.find(info => info.stateName === stateName); } diff --git a/lib/tests-tree-builder/base.ts b/lib/tests-tree-builder/base.ts index 5feff7e85..42fd78afe 100644 --- a/lib/tests-tree-builder/base.ts +++ b/lib/tests-tree-builder/base.ts @@ -1,13 +1,82 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; - -const _ = require('lodash'); -const {determineStatus} = require('../common-utils'); -const {BrowserVersions} = require('../constants/browser'); - -module.exports = class ResultsTreeBuilder { - static create() { +import _ from 'lodash'; +import {determineStatus} from '../common-utils'; +import {TestStatus, BrowserVersions} from '../constants'; +import {TestAdapter} from '../test-adapter'; +import {ImageInfoFull, ParsedSuitesRow} from '../types'; + +type TreeResult = { + id: string; + parentId: string; + status: TestStatus; + imageIds: string[]; +} & Omit; + +interface TreeBrowser { + id: string; + name: string; + parentId: string; + resultIds: string[]; + version: string; +} + +interface TreeSuite { + status?: TestStatus; + id: string; + parentId: string | null; + name: string; + suitePath: string[]; + root: boolean; + suiteIds?: string[]; + browserIds?: string[]; +} + +type TreeImages = { + id: string; + parentId: string; +} & ImageInfoFull; + +export interface Tree { + suites: { + byId: Record, + allIds: string[], + allRootIds: string[] + }, + browsers: { + byId: Record, + allIds: string[] + }, + results: { + byId: Record, + allIds: string[] + }, + images: { + byId: Record, + allIds: string[] + } +} + +interface ResultPayload { + id: string; + parentId: string; + result: ParsedSuitesRow; +} + +interface BrowserPayload { + id: string; + name: string; + parentId: string; + version: string; +} + +interface ImagesPayload { + imagesInfo: ImageInfoFull[]; + parentId: string; +} + +export class BaseTestsTreeBuilder { + protected _tree: Tree; + + static create(this: new () => T): T { return new this(); } @@ -20,12 +89,12 @@ module.exports = class ResultsTreeBuilder { }; } - get tree() { + get tree(): Tree { return this._tree; } - sortTree() { - const sortChildSuites = (suiteId) => { + sortTree(): void { + const sortChildSuites = (suiteId: string): void => { const childSuite = this._tree.suites.byId[suiteId]; if (childSuite.suiteIds) { @@ -40,16 +109,16 @@ module.exports = class ResultsTreeBuilder { this._tree.suites.allRootIds.sort().forEach(sortChildSuites); } - addTestResult(testResult, formattedResult) { + addTestResult(testResult: ParsedSuitesRow, formattedResult: Pick): void { const {testPath, browserId: browserName, attempt} = formattedResult; const {imagesInfo} = testResult; - const {browserVersion = BrowserVersions.UNKNOWN} = testResult.metaInfo; + const {browserVersion = BrowserVersions.UNKNOWN} = testResult.metaInfo as {browserVersion: string}; const suiteId = this._buildId(testPath); const browserId = this._buildId(suiteId, browserName); - const testResultId = this._buildId(browserId, attempt); + const testResultId = this._buildId(browserId, attempt.toString()); const imageIds = imagesInfo - .map((image, i) => this._buildId(testResultId, image.stateName || `${image.status}_${i}`)); + .map((image: ImageInfoFull, i: number) => this._buildId(testResultId, image.stateName || `${image.status}_${i}`)); this._addSuites(testPath, browserId); this._addBrowser({id: browserId, parentId: suiteId, name: browserName, version: browserVersion}, testResultId, attempt); @@ -59,11 +128,11 @@ module.exports = class ResultsTreeBuilder { this._setStatusForBranch(testPath); } - _buildId(parentId = [], name = []) { - return [].concat(parentId, name).join(' '); + protected _buildId(parentId: string | string[] = [], name: string | string[] = []): string { + return ([] as string[]).concat(parentId, name).join(' '); } - _addSuites(testPath, browserId) { + protected _addSuites(testPath: string[], browserId: string): void { testPath.reduce((suites, name, ind, arr) => { const isRoot = ind === 0; const suitePath = isRoot ? [name] : arr.slice(0, ind + 1); @@ -71,7 +140,7 @@ module.exports = class ResultsTreeBuilder { if (!suites.byId[id]) { const parentId = isRoot ? null : this._buildId(suitePath.slice(0, -1)); - const suite = {id, parentId, name, suitePath, root: isRoot}; + const suite: TreeSuite = {id, parentId, name, suitePath, root: isRoot}; this._addSuite(suite); } @@ -87,7 +156,7 @@ module.exports = class ResultsTreeBuilder { }, this._tree.suites); } - _addSuite(suite) { + protected _addSuite(suite: TreeSuite): void { const {suites} = this._tree; suites.byId[suite.id] = suite; @@ -98,7 +167,7 @@ module.exports = class ResultsTreeBuilder { } } - _addNodeId(parentSuiteId, nodeId, {fieldName}) { + protected _addNodeId(parentSuiteId: string, nodeId: string, {fieldName}: {fieldName: 'browserIds' | 'suiteIds'}): void { const {suites} = this._tree; if (!suites.byId[parentSuiteId][fieldName]) { @@ -107,15 +176,15 @@ module.exports = class ResultsTreeBuilder { } if (!this._isNodeIdExists(parentSuiteId, nodeId, {fieldName})) { - suites.byId[parentSuiteId][fieldName].push(nodeId); + suites.byId[parentSuiteId][fieldName]?.push(nodeId); } } - _isNodeIdExists(parentSuiteId, nodeId, {fieldName}) { + protected _isNodeIdExists(parentSuiteId: string, nodeId: string, {fieldName}: {fieldName: 'browserIds' | 'suiteIds'}): boolean { return _.includes(this._tree.suites.byId[parentSuiteId][fieldName], nodeId); } - _addBrowser({id, parentId, name, version}, testResultId, attempt) { + protected _addBrowser({id, parentId, name, version}: BrowserPayload, testResultId: string, attempt: number): void { const {browsers} = this._tree; if (!browsers.byId[id]) { @@ -126,11 +195,11 @@ module.exports = class ResultsTreeBuilder { this._addResultIdToBrowser(id, testResultId, attempt); } - _addResultIdToBrowser(browserId, testResultId, attempt) { + protected _addResultIdToBrowser(browserId: string, testResultId: string, attempt: number): void { this._tree.browsers.byId[browserId].resultIds[attempt] = testResultId; } - _addResult({id, parentId, result}, imageIds) { + protected _addResult({id, parentId, result}: ResultPayload, imageIds: string[]): void { const resultWithoutImagesInfo = _.omit(result, 'imagesInfo'); if (!this._tree.results.byId[id]) { @@ -140,14 +209,14 @@ module.exports = class ResultsTreeBuilder { this._tree.results.byId[id] = {id, parentId, ...resultWithoutImagesInfo, imageIds}; } - _addImages(imageIds, {imagesInfo, parentId}) { + protected _addImages(imageIds: string[], {imagesInfo, parentId}: ImagesPayload): void { imageIds.forEach((id, ind) => { this._tree.images.byId[id] = {...imagesInfo[ind], id, parentId}; this._tree.images.allIds.push(id); }); } - _setStatusForBranch(testPath = []) { + protected _setStatusForBranch(testPath: string[] = []): void { const suiteId = this._buildId(testPath); if (!suiteId) { @@ -156,25 +225,25 @@ module.exports = class ResultsTreeBuilder { const suite = this._tree.suites.byId[suiteId]; - const resultStatuses = _.compact([].concat(suite.browserIds)) - .map((browserId) => { + const resultStatuses = _.compact(([] as (string | undefined)[]).concat(suite.browserIds)) + .map((browserId: string) => { const browser = this._tree.browsers.byId[browserId]; - const lastResultId = _.last(browser.resultIds); + const lastResultId = _.last(browser.resultIds) as string; return this._tree.results.byId[lastResultId].status; }); - const childrenSuiteStatuses = _.compact([].concat(suite.suiteIds)) - .map((childSuiteId) => this._tree.suites.byId[childSuiteId].status); + const childrenSuiteStatuses = _.compact(([] as (string | undefined)[]).concat(suite.suiteIds)) + .map((childSuiteId: string) => this._tree.suites.byId[childSuiteId].status); - const status = determineStatus([...resultStatuses, ...childrenSuiteStatuses]); + const status = determineStatus(_.compact([...resultStatuses, ...childrenSuiteStatuses])); // if newly determined status is the same as current status, do nothing if (suite.status === status) { return; } - suite.status = status; + suite.status = status || undefined; this._setStatusForBranch(testPath.slice(0, -1)); } -}; +} diff --git a/lib/tests-tree-builder/gui.js b/lib/tests-tree-builder/gui.js index 32423f361..41caace3b 100644 --- a/lib/tests-tree-builder/gui.js +++ b/lib/tests-tree-builder/gui.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -const BaseTestsTreeBuilder = require('./base'); +const {BaseTestsTreeBuilder} = require('./base'); const {UPDATED} = require('../constants/test-statuses'); const {isUpdatedStatus} = require('../common-utils'); diff --git a/lib/tests-tree-builder/static.js b/lib/tests-tree-builder/static.js index b4d96fdc1..3492385f2 100644 --- a/lib/tests-tree-builder/static.js +++ b/lib/tests-tree-builder/static.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -const BaseTestsTreeBuilder = require('./base'); +const {BaseTestsTreeBuilder} = require('./base'); const testStatus = require('../constants/test-statuses'); const {BrowserVersions} = require('../constants/browser'); const {DB_COLUMN_INDEXES} = require('../constants/database'); diff --git a/lib/types.ts b/lib/types.ts index f2c33bcce..459595375 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -112,10 +112,16 @@ export interface TestResult { parent: Suite; } -export interface SuitesRow { +export interface RawSuitesRow { imagesInfo: string; } +export interface ParsedSuitesRow { + status: TestStatus; + imagesInfo: ImageInfoFull[]; + metaInfo: Record; +} + export interface HtmlReporterApi { htmlReporter: HtmlReporter; } diff --git a/test/unit/lib/tests-tree-builder/base.js b/test/unit/lib/tests-tree-builder/base.js index 8cfd1bca3..ac6fa30d3 100644 --- a/test/unit/lib/tests-tree-builder/base.js +++ b/test/unit/lib/tests-tree-builder/base.js @@ -25,7 +25,7 @@ describe('ResultsTreeBuilder', () => { determineStatus = sandbox.stub().returns(SUCCESS); ResultsTreeBuilder = proxyquire('lib/tests-tree-builder/base', { '../common-utils': {determineStatus} - }); + }).BaseTestsTreeBuilder; builder = ResultsTreeBuilder.create(); }); From 3fa6add35a0015992ef9d662a9501b0d60b8cfda Mon Sep 17 00:00:00 2001 From: shadowusr Date: Fri, 18 Aug 2023 17:01:16 +0300 Subject: [PATCH 08/25] chore: rename static tests tree builder to typescript --- lib/tests-tree-builder/{static.js => static.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/tests-tree-builder/{static.js => static.ts} (99%) diff --git a/lib/tests-tree-builder/static.js b/lib/tests-tree-builder/static.ts similarity index 99% rename from lib/tests-tree-builder/static.js rename to lib/tests-tree-builder/static.ts index 3492385f2..6de8c6894 100644 --- a/lib/tests-tree-builder/static.js +++ b/lib/tests-tree-builder/static.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const _ = require('lodash'); From 0848c6909404c8e4d84b210a4fcd516d42df29e8 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 11:08:01 +0300 Subject: [PATCH 09/25] chore: re-write static tests tree builder to typescript --- lib/db-utils/server.js | 2 +- lib/static/modules/actions.js | 2 +- lib/test-adapter.ts | 6 +- lib/tests-tree-builder/static.ts | 82 ++++++++++++++-------- lib/types.ts | 26 ++++++- test/unit/lib/static/modules/actions.js | 2 +- test/unit/lib/tests-tree-builder/static.js | 24 +++---- 7 files changed, 95 insertions(+), 49 deletions(-) diff --git a/lib/db-utils/server.js b/lib/db-utils/server.js index 1530e7e61..04f096c70 100644 --- a/lib/db-utils/server.js +++ b/lib/db-utils/server.js @@ -8,7 +8,7 @@ const Database = require('better-sqlite3'); const chalk = require('chalk'); const NestedError = require('nested-error-stacks'); -const StaticTestsTreeBuilder = require('../tests-tree-builder/static'); +const {StaticTestsTreeBuilder} = require('../tests-tree-builder/static'); const commonSqliteUtils = require('./common'); const {isUrl, fetchFile, normalizeUrls, logger} = require('../common-utils'); diff --git a/lib/static/modules/actions.js b/lib/static/modules/actions.js index 73f1b2491..817afeaf4 100644 --- a/lib/static/modules/actions.js +++ b/lib/static/modules/actions.js @@ -1,7 +1,7 @@ import axios from 'axios'; import {isEmpty, difference} from 'lodash'; import {notify, dismissNotification as dismissNotify, POSITIONS} from 'reapop'; -import StaticTestsTreeBuilder from '../../tests-tree-builder/static'; +import {StaticTestsTreeBuilder} from '../../tests-tree-builder/static'; import actionNames from './action-names'; import {types as modalTypes} from '../components/modals'; import {QUEUED} from '../../constants/test-statuses'; diff --git a/lib/test-adapter.ts b/lib/test-adapter.ts index 10123f1c6..3b2c6d8cc 100644 --- a/lib/test-adapter.ts +++ b/lib/test-adapter.ts @@ -18,7 +18,7 @@ import { HtmlReporterApi, ImageInfo, ImagesSaver, - RawSuitesRow, + LabeledSuitesRow, TestResult, ImageData, ImageInfoFull, ImageDiffError, AssertViewResult, ImageInfoError, @@ -121,14 +121,14 @@ export class TestAdapter { const suitePath = getSuitePath(this._testResult); const suitePathString = JSON.stringify(suitePath); - const imagesInfoResult = this._sqliteAdapter.query | undefined>({ + const imagesInfoResult = this._sqliteAdapter.query | undefined>({ select: DB_COLUMNS.IMAGES_INFO, where: `${DB_COLUMNS.SUITE_PATH} = ? AND ${DB_COLUMNS.NAME} = ?`, orderBy: DB_COLUMNS.TIMESTAMP, orderDescending: true }, suitePathString, browserName); - const imagesInfo: ImageInfoFull[] = imagesInfoResult && JSON.parse(imagesInfoResult[DB_COLUMNS.IMAGES_INFO as keyof RawSuitesRow]) || []; + const imagesInfo: ImageInfoFull[] = imagesInfoResult && JSON.parse(imagesInfoResult[DB_COLUMNS.IMAGES_INFO as keyof LabeledSuitesRow]) || []; return imagesInfo.find(info => info.stateName === stateName); } diff --git a/lib/tests-tree-builder/static.ts b/lib/tests-tree-builder/static.ts index 6de8c6894..a25a5cd0f 100644 --- a/lib/tests-tree-builder/static.ts +++ b/lib/tests-tree-builder/static.ts @@ -1,14 +1,41 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; +import _ from 'lodash'; +import {BaseTestsTreeBuilder, Tree} from './base'; +import {BrowserVersions, DB_COLUMN_INDEXES, TestStatus} from '../constants'; +import {Attempt, ParsedSuitesRow, RawSuitesRow} from '../types'; + +interface Stats { + total: number; + passed: number; + failed: number; + skipped: number; + retries: number; +} + +type FinalStats = Stats & { + perBrowser: { + [browserName: string]: { + [browserVersion: string]: Stats + } + } +} -const _ = require('lodash'); -const {BaseTestsTreeBuilder} = require('./base'); -const testStatus = require('../constants/test-statuses'); -const {BrowserVersions} = require('../constants/browser'); -const {DB_COLUMN_INDEXES} = require('../constants/database'); +interface SkipItem { + browser: string; + suite: string; + comment: string; +} + +interface BrowserItem { + id: string; + versions: string[]; +} + +export class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { + protected _stats: FinalStats; + protected _skips: SkipItem[]; + protected _failedBrowserIds: { [key: string]: boolean }; + protected _passedBrowserIds: { [key: string]: boolean }; -module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { constructor() { super(); @@ -21,20 +48,19 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { this._passedBrowserIds = {}; } - build(rows = []) { - // in order to sync attempts between gui tree and static tree - const attemptsMap = new Map(); - const browsers = {}; + build(rows: RawSuitesRow[] = []): { tree: Tree; stats: FinalStats; skips: SkipItem[]; browsers: BrowserItem[] } { + const attemptsMap = new Map(); + const browsers: Record> = {}; for (const row of rows) { - const testPath = JSON.parse(row[DB_COLUMN_INDEXES.suitePath]); - const browserName = row[DB_COLUMN_INDEXES.name]; + const testPath: string[] = JSON.parse(row[DB_COLUMN_INDEXES.suitePath]); + const browserName: string = row[DB_COLUMN_INDEXES.name]; const testId = this._buildId(testPath); const browserId = this._buildId(testId, browserName); - attemptsMap.set(browserId, attemptsMap.has(browserId) ? attemptsMap.get(browserId) + 1 : 0); - const attempt = attemptsMap.get(browserId); + attemptsMap.set(browserId, attemptsMap.has(browserId) ? attemptsMap.get(browserId) as number + 1 : 0); + const attempt = attemptsMap.get(browserId) as number; const testResult = mkTestResult(row, {attempt}); const formattedResult = {browserId: browserName, testPath, attempt}; @@ -55,11 +81,11 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { }; } - _addResultIdToBrowser(browserId, testResultId) { + protected _addResultIdToBrowser(browserId: string, testResultId: string): void { this._tree.browsers.byId[browserId].resultIds.push(testResultId); } - _calcStats(testResult, {testId, browserId, browserName}) { + protected _calcStats(testResult: ParsedSuitesRow, {testId, browserId, browserName}: { testId: string; browserId: string; browserName: string }): void { const {status} = testResult; const {browserVersion} = testResult.metaInfo; const version = browserVersion || BrowserVersions.UNKNOWN; @@ -73,8 +99,8 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { } switch (status) { - case testStatus.FAIL: - case testStatus.ERROR: { + case TestStatus.FAIL: + case TestStatus.ERROR: { if (this._failedBrowserIds[browserId]) { this._stats.retries++; this._stats.perBrowser[browserName][version].retries++; @@ -89,7 +115,7 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { return; } - case testStatus.SUCCESS: { + case TestStatus.SUCCESS: { if (this._passedBrowserIds[browserId]) { this._stats.retries++; this._stats.perBrowser[browserName][version].retries++; @@ -117,7 +143,7 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { return; } - case testStatus.SKIPPED: { + case TestStatus.SKIPPED: { this._skips.push({ browser: browserName, suite: testId, @@ -139,9 +165,9 @@ module.exports = class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { } } } -}; +} -function initStats() { +function initStats(): Stats { return { total: 0, passed: 0, @@ -151,7 +177,7 @@ function initStats() { }; } -function mkTestResult(row, data = {}) { +function mkTestResult(row: RawSuitesRow, data: {attempt: number}): ParsedSuitesRow & Attempt { return { description: row[DB_COLUMN_INDEXES.description], imagesInfo: JSON.parse(row[DB_COLUMN_INDEXES.imagesInfo]), @@ -160,7 +186,7 @@ function mkTestResult(row, data = {}) { multipleTabs: Boolean(row[DB_COLUMN_INDEXES.multipleTabs]), name: row[DB_COLUMN_INDEXES.name], screenshot: Boolean(row[DB_COLUMN_INDEXES.screenshot]), - status: row[DB_COLUMN_INDEXES.status], + status: row[DB_COLUMN_INDEXES.status] as TestStatus, suiteUrl: row[DB_COLUMN_INDEXES.suiteUrl], skipReason: row[DB_COLUMN_INDEXES.skipReason], error: JSON.parse(row[DB_COLUMN_INDEXES.error]), @@ -168,7 +194,7 @@ function mkTestResult(row, data = {}) { }; } -function addBrowserVersion(browsers, testResult) { +function addBrowserVersion(browsers: Record>, testResult: ParsedSuitesRow): void { const browserId = testResult.name; if (!browsers[browserId]) { diff --git a/lib/types.ts b/lib/types.ts index 459595375..85a7ea6ef 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -112,14 +112,34 @@ export interface TestResult { parent: Suite; } -export interface RawSuitesRow { +export interface LabeledSuitesRow { imagesInfo: string; } +export type RawSuitesRow = LabeledSuitesRow[keyof LabeledSuitesRow]; + export interface ParsedSuitesRow { - status: TestStatus; + description: string | null; + error: { + message: string; + stack: string; + }; + history: unknown; imagesInfo: ImageInfoFull[]; - metaInfo: Record; + metaInfo: { + browserVersion?: string; + [key: string]: unknown; + }; + multipleTabs: boolean; + name: string; + screenshot: boolean; + skipReason: string; + status: TestStatus; + suiteUrl: string; +} + +export interface Attempt { + attempt: number; } export interface HtmlReporterApi { diff --git a/test/unit/lib/static/modules/actions.js b/test/unit/lib/static/modules/actions.js index 3e7f76281..b9a6526d0 100644 --- a/test/unit/lib/static/modules/actions.js +++ b/test/unit/lib/static/modules/actions.js @@ -3,7 +3,7 @@ import proxyquire from 'proxyquire'; import {POSITIONS} from 'reapop'; import {acceptOpened, undoAcceptImages, retryTest, runFailedTests} from 'lib/static/modules/actions'; import actionNames from 'lib/static/modules/action-names'; -import StaticTestsTreeBuilder from 'lib/tests-tree-builder/static'; +import {StaticTestsTreeBuilder} from 'lib/tests-tree-builder/static'; import {LOCAL_DATABASE_NAME} from 'lib/constants/database'; import {DiffModes} from 'lib/constants/diff-modes'; diff --git a/test/unit/lib/tests-tree-builder/static.js b/test/unit/lib/tests-tree-builder/static.js index 81fb6d79e..d80978ad3 100644 --- a/test/unit/lib/tests-tree-builder/static.js +++ b/test/unit/lib/tests-tree-builder/static.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -const StaticResultsTreeBuilder = require('lib/tests-tree-builder/static'); +const {StaticTestsTreeBuilder} = require('lib/tests-tree-builder/static'); const {SUCCESS} = require('lib/constants/test-statuses'); const {BrowserVersions} = require('lib/constants/browser'); @@ -65,10 +65,10 @@ describe('StaticResultsTreeBuilder', () => { }; beforeEach(() => { - sandbox.stub(StaticResultsTreeBuilder.prototype, 'addTestResult'); - sandbox.stub(StaticResultsTreeBuilder.prototype, 'sortTree'); + sandbox.stub(StaticTestsTreeBuilder.prototype, 'addTestResult'); + sandbox.stub(StaticTestsTreeBuilder.prototype, 'sortTree'); - builder = StaticResultsTreeBuilder.create(); + builder = StaticTestsTreeBuilder.create(); }); afterEach(() => sandbox.restore()); @@ -83,12 +83,12 @@ describe('StaticResultsTreeBuilder', () => { builder.build(rows); assert.calledWith( - StaticResultsTreeBuilder.prototype.addTestResult.firstCall, + StaticTestsTreeBuilder.prototype.addTestResult.firstCall, formatToTestResult(dataFromDb1, {attempt: 0}), {browserId: 'yabro', testPath: ['s1'], attempt: 0} ); assert.calledWith( - StaticResultsTreeBuilder.prototype.addTestResult.secondCall, + StaticTestsTreeBuilder.prototype.addTestResult.secondCall, formatToTestResult(dataFromDb2, {attempt: 0}), {browserId: 'yabro', testPath: ['s2'], attempt: 0} ); @@ -102,12 +102,12 @@ describe('StaticResultsTreeBuilder', () => { builder.build(rows); assert.calledWith( - StaticResultsTreeBuilder.prototype.addTestResult.firstCall, + StaticTestsTreeBuilder.prototype.addTestResult.firstCall, formatToTestResult(dataFromDb1, {attempt: 0}), {browserId: 'yabro', testPath: ['s1'], attempt: 0} ); assert.calledWith( - StaticResultsTreeBuilder.prototype.addTestResult.secondCall, + StaticTestsTreeBuilder.prototype.addTestResult.secondCall, formatToTestResult(dataFromDb1, {attempt: 1}), {browserId: 'yabro', testPath: ['s1'], attempt: 1} ); @@ -121,8 +121,8 @@ describe('StaticResultsTreeBuilder', () => { builder.build(rows); assert.callOrder( - StaticResultsTreeBuilder.prototype.addTestResult, - StaticResultsTreeBuilder.prototype.sortTree + StaticTestsTreeBuilder.prototype.addTestResult, + StaticTestsTreeBuilder.prototype.sortTree ); }); @@ -131,12 +131,12 @@ describe('StaticResultsTreeBuilder', () => { builder.build(rows); - assert.calledOnce(StaticResultsTreeBuilder.prototype.sortTree); + assert.calledOnce(StaticTestsTreeBuilder.prototype.sortTree); }); }); it('should return tests tree', () => { - sandbox.stub(StaticResultsTreeBuilder.prototype, 'tree').get(() => 'tree'); + sandbox.stub(StaticTestsTreeBuilder.prototype, 'tree').get(() => 'tree'); const {tree} = builder.build([]); From 1f22f1c01d5d43f31312a22d984b0a7d97c1d983 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 11:13:18 +0300 Subject: [PATCH 10/25] chore: rename db-utils/common to typescript --- lib/db-utils/{common.js => common.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/db-utils/{common.js => common.ts} (98%) diff --git a/lib/db-utils/common.js b/lib/db-utils/common.ts similarity index 98% rename from lib/db-utils/common.js rename to lib/db-utils/common.ts index 3b219ba50..b0cafa025 100644 --- a/lib/db-utils/common.js +++ b/lib/db-utils/common.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const _ = require('lodash'); From 7ae821e2c1bf0a220a16679924b99e5c344795d6 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 12:11:07 +0300 Subject: [PATCH 11/25] chore: re-write db-utils/common to typescript --- lib/db-utils/client.js | 1 + lib/db-utils/common.ts | 50 ++++++++++++++++++-------------- lib/db-utils/server.js | 1 + lib/sqlite-adapter.ts | 2 +- lib/test-adapter.ts | 2 +- lib/tests-tree-builder/static.ts | 20 ++++++------- lib/types.ts | 8 ++++- test/unit/lib/sqlite-adapter.js | 4 +-- 8 files changed, 51 insertions(+), 37 deletions(-) diff --git a/lib/db-utils/client.js b/lib/db-utils/client.js index 51da2ed59..d30668488 100644 --- a/lib/db-utils/client.js +++ b/lib/db-utils/client.js @@ -2,6 +2,7 @@ /* eslint-env browser */ const {isEmpty} = require('lodash'); +/** @type Record unknown> */ const commonSqliteUtils = require('./common'); const {fetchFile, normalizeUrls} = require('../common-utils'); diff --git a/lib/db-utils/common.ts b/lib/db-utils/common.ts index b0cafa025..99626ef7f 100644 --- a/lib/db-utils/common.ts +++ b/lib/db-utils/common.ts @@ -1,23 +1,32 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; +import _ from 'lodash'; +import {logger} from '../common-utils'; +import {DB_MAX_AVAILABLE_PAGE_SIZE, DB_SUITES_TABLE_NAME, SUITES_TABLE_COLUMNS, DB_COLUMN_INDEXES} from '../constants'; +import {DbUrlsJsonData, RawSuitesRow} from '../types'; +import type {Database, Statement} from 'better-sqlite3'; -const _ = require('lodash'); -const {logger} = require('../common-utils'); -const {DB_MAX_AVAILABLE_PAGE_SIZE, DB_SUITES_TABLE_NAME, SUITES_TABLE_COLUMNS, DB_COLUMN_INDEXES} = require('../constants/database'); +export const selectAllQuery = (tableName: string): string => `SELECT * FROM ${tableName}`; +export const selectAllSuitesQuery = (): string => selectAllQuery(DB_SUITES_TABLE_NAME); -exports.selectAllQuery = (tableName) => `SELECT * FROM ${tableName}`; -exports.selectAllSuitesQuery = () => exports.selectAllQuery(DB_SUITES_TABLE_NAME); - -exports.createTablesQuery = () => [ +export const createTablesQuery = (): string[] => [ createTableQuery(DB_SUITES_TABLE_NAME, SUITES_TABLE_COLUMNS) ]; -exports.compareDatabaseRowsByTimestamp = (row1, row2) => { - return row1[DB_COLUMN_INDEXES.timestamp] - row2[DB_COLUMN_INDEXES.timestamp]; +export const compareDatabaseRowsByTimestamp = (row1: RawSuitesRow, row2: RawSuitesRow): number => { + return (row1[DB_COLUMN_INDEXES.timestamp] as number) - (row2[DB_COLUMN_INDEXES.timestamp] as number); }; -exports.handleDatabases = async (dbJsonUrls, opts = {}) => { +export interface DbLoadResult { + url: string; status: string; data: null | unknown +} + +export interface HandleDatabasesOptions { + loadDbJsonUrl: (dbJsonUrl: string) => Promise<{data: DbUrlsJsonData; status: string}>; + formatData?: (dbJsonUrl: string, status: string) => DbLoadResult; + prepareUrls: (dbUrls: string[], baseUrls: string) => string[]; + loadDbUrl: (dbUrl: string, opts: unknown) => Promise; +} + +export const handleDatabases = async (dbJsonUrls: string[], opts: HandleDatabasesOptions): Promise<(string | DbLoadResult)[]> => { return _.flattenDeep( await Promise.all( dbJsonUrls.map(async dbJsonUrl => { @@ -30,34 +39,31 @@ exports.handleDatabases = async (dbJsonUrls, opts = {}) => { return opts.formatData ? opts.formatData(dbJsonUrl, currentJsonResponse.status) : []; } - // JSON format declare at lib/static/modules/actions.js const {dbUrls, jsonUrls} = currentJsonResponse.data; - - // paths from databaseUrls.json may be relative or absolute const preparedDbUrls = opts.prepareUrls(dbUrls, dbJsonUrl); const preparedDbJsonUrls = opts.prepareUrls(jsonUrls, dbJsonUrl); return await Promise.all([ - exports.handleDatabases(preparedDbJsonUrls, opts), - ...preparedDbUrls.map(dbUrl => opts.loadDbUrl(dbUrl, opts)) + handleDatabases(preparedDbJsonUrls, opts), + ...preparedDbUrls.map((dbUrl: string) => opts.loadDbUrl(dbUrl, opts)) ]); } catch (e) { logger.warn(`Error while downloading databases from ${dbJsonUrl}`, e); - return opts.formatData ? opts.formatData(dbJsonUrl) : []; + return opts.formatData ? opts.formatData(dbJsonUrl, 'error') : []; } }) ) ); }; -exports.mergeTables = ({db, dbPaths, getExistingTables = () => {}}) => { +export const mergeTables = ({db, dbPaths, getExistingTables = (): string[] => []}: { db: Database, dbPaths: string[], getExistingTables?: (getTablesStatement: Statement) => string[] }): void => { db.prepare(`PRAGMA page_size = ${DB_MAX_AVAILABLE_PAGE_SIZE}`).run(); for (const dbPath of dbPaths) { db.prepare(`ATTACH DATABASE '${dbPath}' AS attached`).run(); - const getTablesStatement = db.prepare(`SELECT name FROM attached.sqlite_master WHERE type='table'`); + const getTablesStatement = db.prepare(`SELECT name FROM attached.sqlite_master WHERE type='table'`); const tables = getExistingTables(getTablesStatement); for (const tableName of tables) { @@ -69,7 +75,7 @@ exports.mergeTables = ({db, dbPaths, getExistingTables = () => {}}) => { } }; -function createTableQuery(tableName, columns) { +function createTableQuery(tableName: string, columns: { name: string, type: string }[]): string { const formattedColumns = columns .map(({name, type}) => `${name} ${type}`) .join(', '); diff --git a/lib/db-utils/server.js b/lib/db-utils/server.js index 04f096c70..56450bb1a 100644 --- a/lib/db-utils/server.js +++ b/lib/db-utils/server.js @@ -9,6 +9,7 @@ const chalk = require('chalk'); const NestedError = require('nested-error-stacks'); const {StaticTestsTreeBuilder} = require('../tests-tree-builder/static'); +/** @type Record unknown> */ const commonSqliteUtils = require('./common'); const {isUrl, fetchFile, normalizeUrls, logger} = require('../common-utils'); diff --git a/lib/sqlite-adapter.ts b/lib/sqlite-adapter.ts index 7ec859500..14b8c6f66 100644 --- a/lib/sqlite-adapter.ts +++ b/lib/sqlite-adapter.ts @@ -9,7 +9,7 @@ import NestedError from 'nested-error-stacks'; import {getShortMD5} from './common-utils'; import {TestStatus} from './constants'; import {DB_SUITES_TABLE_NAME, SUITES_TABLE_COLUMNS, LOCAL_DATABASE_NAME, DATABASE_URLS_JSON_NAME} from './constants/database'; -import {createTablesQuery} from './db-utils/server'; +import {createTablesQuery} from './db-utils/common'; import {DbNotInitializedError} from './errors/db-not-initialized-error'; import type {HtmlReporterApi, ImageInfoFull} from './types'; diff --git a/lib/test-adapter.ts b/lib/test-adapter.ts index 3b2c6d8cc..b440524cc 100644 --- a/lib/test-adapter.ts +++ b/lib/test-adapter.ts @@ -128,7 +128,7 @@ export class TestAdapter { orderDescending: true }, suitePathString, browserName); - const imagesInfo: ImageInfoFull[] = imagesInfoResult && JSON.parse(imagesInfoResult[DB_COLUMNS.IMAGES_INFO as keyof LabeledSuitesRow]) || []; + const imagesInfo: ImageInfoFull[] = imagesInfoResult && JSON.parse(imagesInfoResult[DB_COLUMNS.IMAGES_INFO as keyof Pick]) || []; return imagesInfo.find(info => info.stateName === stateName); } diff --git a/lib/tests-tree-builder/static.ts b/lib/tests-tree-builder/static.ts index a25a5cd0f..24bb64e94 100644 --- a/lib/tests-tree-builder/static.ts +++ b/lib/tests-tree-builder/static.ts @@ -53,8 +53,8 @@ export class StaticTestsTreeBuilder extends BaseTestsTreeBuilder { const browsers: Record> = {}; for (const row of rows) { - const testPath: string[] = JSON.parse(row[DB_COLUMN_INDEXES.suitePath]); - const browserName: string = row[DB_COLUMN_INDEXES.name]; + const testPath: string[] = JSON.parse(row[DB_COLUMN_INDEXES.suitePath] as string); + const browserName = row[DB_COLUMN_INDEXES.name] as string; const testId = this._buildId(testPath); const browserId = this._buildId(testId, browserName); @@ -179,17 +179,17 @@ function initStats(): Stats { function mkTestResult(row: RawSuitesRow, data: {attempt: number}): ParsedSuitesRow & Attempt { return { - description: row[DB_COLUMN_INDEXES.description], - imagesInfo: JSON.parse(row[DB_COLUMN_INDEXES.imagesInfo]), - metaInfo: JSON.parse(row[DB_COLUMN_INDEXES.metaInfo]), - history: JSON.parse(row[DB_COLUMN_INDEXES.history]), + description: row[DB_COLUMN_INDEXES.description] as string | null, + imagesInfo: JSON.parse(row[DB_COLUMN_INDEXES.imagesInfo] as string), + metaInfo: JSON.parse(row[DB_COLUMN_INDEXES.metaInfo] as string), + history: JSON.parse(row[DB_COLUMN_INDEXES.history] as string), multipleTabs: Boolean(row[DB_COLUMN_INDEXES.multipleTabs]), - name: row[DB_COLUMN_INDEXES.name], + name: row[DB_COLUMN_INDEXES.name] as string, screenshot: Boolean(row[DB_COLUMN_INDEXES.screenshot]), status: row[DB_COLUMN_INDEXES.status] as TestStatus, - suiteUrl: row[DB_COLUMN_INDEXES.suiteUrl], - skipReason: row[DB_COLUMN_INDEXES.skipReason], - error: JSON.parse(row[DB_COLUMN_INDEXES.error]), + suiteUrl: row[DB_COLUMN_INDEXES.suiteUrl] as string, + skipReason: row[DB_COLUMN_INDEXES.skipReason] as string, + error: JSON.parse(row[DB_COLUMN_INDEXES.error] as string), ...data }; } diff --git a/lib/types.ts b/lib/types.ts index 85a7ea6ef..ed5c77a30 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -114,9 +114,10 @@ export interface TestResult { export interface LabeledSuitesRow { imagesInfo: string; + timestamp: number; } -export type RawSuitesRow = LabeledSuitesRow[keyof LabeledSuitesRow]; +export type RawSuitesRow = LabeledSuitesRow[keyof LabeledSuitesRow][]; export interface ParsedSuitesRow { description: string | null; @@ -170,3 +171,8 @@ export interface ReporterConfig { pluginsEnabled: boolean; yandexMetrika: { counterNumber: null | number }; } + +export interface DbUrlsJsonData { + dbUrls: string[]; + jsonUrls: string[]; +} diff --git a/test/unit/lib/sqlite-adapter.js b/test/unit/lib/sqlite-adapter.js index 2df7e1c82..82c69f741 100644 --- a/test/unit/lib/sqlite-adapter.js +++ b/test/unit/lib/sqlite-adapter.js @@ -78,7 +78,7 @@ describe('lib/sqlite-adapter', () => { getStub = sandbox.stub(); prepareStub = sandbox.stub(Database.prototype, 'prepare').returns({get: getStub}); sqliteAdapter = proxyquire('lib/sqlite-adapter', { - './db-utils/server': {createTablesQuery: () => []} + './db-utils/common': {createTablesQuery: () => []} }).SqliteAdapter.create({hermione, reportPath: 'test'}); await sqliteAdapter.init(); @@ -146,7 +146,7 @@ describe('lib/sqlite-adapter', () => { runStub = sandbox.stub(); prepareStub = sandbox.stub(Database.prototype, 'prepare').returns({run: runStub}); sqliteAdapter = proxyquire('lib/sqlite-adapter', { - './db-utils/server': {createTablesQuery: () => []} + './db-utils/common': {createTablesQuery: () => []} }).SqliteAdapter.create({hermione, reportPath: 'test'}); await sqliteAdapter.init(); From 571c775c38971abbac6a2fc57977394ba0291012 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 13:21:05 +0300 Subject: [PATCH 12/25] chore: rename db-utils/server to typescript --- lib/db-utils/{server.js => server.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/db-utils/{server.js => server.ts} (99%) diff --git a/lib/db-utils/server.js b/lib/db-utils/server.ts similarity index 99% rename from lib/db-utils/server.js rename to lib/db-utils/server.ts index 56450bb1a..9dcb677b9 100644 --- a/lib/db-utils/server.js +++ b/lib/db-utils/server.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const path = require('path'); From 6fc1559e0f1b261d0a457ebe38ab120b677a2d64 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 13:40:09 +0300 Subject: [PATCH 13/25] chore: re-write db-utils/server to typescript --- lib/db-utils/common.ts | 15 +++++---- lib/db-utils/server.ts | 72 +++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/lib/db-utils/common.ts b/lib/db-utils/common.ts index 99626ef7f..6183cc358 100644 --- a/lib/db-utils/common.ts +++ b/lib/db-utils/common.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import {logger} from '../common-utils'; import {DB_MAX_AVAILABLE_PAGE_SIZE, DB_SUITES_TABLE_NAME, SUITES_TABLE_COLUMNS, DB_COLUMN_INDEXES} from '../constants'; -import {DbUrlsJsonData, RawSuitesRow} from '../types'; +import {DbUrlsJsonData, RawSuitesRow, ReporterConfig} from '../types'; import type {Database, Statement} from 'better-sqlite3'; export const selectAllQuery = (tableName: string): string => `SELECT * FROM ${tableName}`; @@ -20,10 +20,11 @@ export interface DbLoadResult { } export interface HandleDatabasesOptions { - loadDbJsonUrl: (dbJsonUrl: string) => Promise<{data: DbUrlsJsonData; status: string}>; - formatData?: (dbJsonUrl: string, status: string) => DbLoadResult; + pluginConfig: ReporterConfig; + loadDbJsonUrl: (dbJsonUrl: string) => Promise<{data: DbUrlsJsonData | null; status?: string}>; + formatData?: (dbJsonUrl: string, status?: string) => DbLoadResult; prepareUrls: (dbUrls: string[], baseUrls: string) => string[]; - loadDbUrl: (dbUrl: string, opts: unknown) => Promise; + loadDbUrl: (dbUrl: string, opts: HandleDatabasesOptions) => Promise; } export const handleDatabases = async (dbJsonUrls: string[], opts: HandleDatabasesOptions): Promise<(string | DbLoadResult)[]> => { @@ -50,20 +51,20 @@ export const handleDatabases = async (dbJsonUrls: string[], opts: HandleDatabase } catch (e) { logger.warn(`Error while downloading databases from ${dbJsonUrl}`, e); - return opts.formatData ? opts.formatData(dbJsonUrl, 'error') : []; + return opts.formatData ? opts.formatData(dbJsonUrl) : []; } }) ) ); }; -export const mergeTables = ({db, dbPaths, getExistingTables = (): string[] => []}: { db: Database, dbPaths: string[], getExistingTables?: (getTablesStatement: Statement) => string[] }): void => { +export const mergeTables = ({db, dbPaths, getExistingTables = (): string[] => []}: { db: Database, dbPaths: string[], getExistingTables?: (getTablesStatement: Statement<[]>) => string[] }): void => { db.prepare(`PRAGMA page_size = ${DB_MAX_AVAILABLE_PAGE_SIZE}`).run(); for (const dbPath of dbPaths) { db.prepare(`ATTACH DATABASE '${dbPath}' AS attached`).run(); - const getTablesStatement = db.prepare(`SELECT name FROM attached.sqlite_master WHERE type='table'`); + const getTablesStatement = db.prepare<[]>(`SELECT name FROM attached.sqlite_master WHERE type='table'`); const tables = getExistingTables(getTablesStatement); for (const tableName of tables) { diff --git a/lib/db-utils/server.ts b/lib/db-utils/server.ts index 9dcb677b9..4d840f7ba 100644 --- a/lib/db-utils/server.ts +++ b/lib/db-utils/server.ts @@ -1,24 +1,23 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; - -const path = require('path'); -const crypto = require('crypto'); -const axios = require('axios'); -const fs = require('fs-extra'); -const Database = require('better-sqlite3'); -const chalk = require('chalk'); -const NestedError = require('nested-error-stacks'); - -const {StaticTestsTreeBuilder} = require('../tests-tree-builder/static'); -/** @type Record unknown> */ -const commonSqliteUtils = require('./common'); -const {isUrl, fetchFile, normalizeUrls, logger} = require('../common-utils'); - -const {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} = require('../constants/database'); - -function downloadDatabases(dbJsonUrls, opts) { - const loadDbJsonUrl = async (dbJsonUrl) => { +import path from 'path'; +import crypto from 'crypto'; +import axios from 'axios'; +import fs from 'fs-extra'; +import Database from 'better-sqlite3'; +import chalk from 'chalk'; +import NestedError from 'nested-error-stacks'; + +import {StaticTestsTreeBuilder} from '../tests-tree-builder/static'; +import * as commonSqliteUtils from './common'; +import {isUrl, fetchFile, normalizeUrls, logger} from '../common-utils'; +import {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} from '../constants'; +import {DbLoadResult, HandleDatabasesOptions} from './common'; +import {DbUrlsJsonData, RawSuitesRow, ReporterConfig} from '../types'; +import {Tree} from '../tests-tree-builder/base'; + +export * from './common'; + +export async function downloadDatabases(dbJsonUrls: string[], opts: HandleDatabasesOptions): Promise<(string | DbLoadResult)[]> { + const loadDbJsonUrl = async (dbJsonUrl: string): Promise<{data: DbUrlsJsonData | null}> => { if (isUrl(dbJsonUrl)) { return fetchFile(dbJsonUrl); } @@ -27,20 +26,20 @@ function downloadDatabases(dbJsonUrls, opts) { return {data}; }; - const prepareUrls = (urls, baseUrl) => isUrl(baseUrl) ? normalizeUrls(urls, baseUrl) : urls; - const loadDbUrl = (dbUrl, opts) => downloadSingleDatabase(dbUrl, opts); + const prepareUrls = (urls: string[], baseUrl: string): string[] => isUrl(baseUrl) ? normalizeUrls(urls, baseUrl) : urls; + const loadDbUrl = (dbUrl: string, opts: HandleDatabasesOptions): Promise => downloadSingleDatabase(dbUrl, opts); return commonSqliteUtils.handleDatabases(dbJsonUrls, {...opts, loadDbJsonUrl, prepareUrls, loadDbUrl}); } -async function mergeDatabases(srcDbPaths, reportPath) { +export async function mergeDatabases(srcDbPaths: string[], reportPath: string): Promise { try { const mainDatabaseUrls = path.resolve(reportPath, DATABASE_URLS_JSON_NAME); const mergedDbPath = path.resolve(reportPath, LOCAL_DATABASE_NAME); const mergedDb = new Database(mergedDbPath); commonSqliteUtils.mergeTables({db: mergedDb, dbPaths: srcDbPaths, getExistingTables: (statement) => { - return statement.all().map((table) => table.name); + return statement.all().map((table) => (table as {name: string}).name); }}); for (const dbPath of srcDbPaths) { @@ -50,31 +49,31 @@ async function mergeDatabases(srcDbPaths, reportPath) { await rewriteDatabaseUrls([mergedDbPath], mainDatabaseUrls, reportPath); mergedDb.close(); - } catch (err) { + } catch (err: any) { // eslint-disable-line @typescript-eslint/no-explicit-any throw new NestedError('Error while merging databases', err); } } -function getTestsTreeFromDatabase(dbPath) { +export function getTestsTreeFromDatabase(dbPath: string): Tree { try { const db = new Database(dbPath, {readonly: true, fileMustExist: true}); const testsTreeBuilder = StaticTestsTreeBuilder.create(); - const suitesRows = db.prepare(commonSqliteUtils.selectAllSuitesQuery()) + const suitesRows = (db.prepare(commonSqliteUtils.selectAllSuitesQuery()) .raw() - .all() + .all() as RawSuitesRow[]) .sort(commonSqliteUtils.compareDatabaseRowsByTimestamp); const {tree} = testsTreeBuilder.build(suitesRows); db.close(); return tree; - } catch (err) { + } catch (err: any) { // eslint-disable-line @typescript-eslint/no-explicit-any throw new NestedError('Error while getting data from database', err); } } -async function downloadSingleDatabase(dbUrl, {pluginConfig} = {}) { +async function downloadSingleDatabase(dbUrl: string, {pluginConfig}: {pluginConfig: ReporterConfig}): Promise { if (!isUrl(dbUrl)) { return path.resolve(pluginConfig.path, dbUrl); } @@ -100,7 +99,7 @@ async function downloadSingleDatabase(dbUrl, {pluginConfig} = {}) { return dest; } -function getUniqueFileNameForLink(link) { +function getUniqueFileNameForLink(link: string): string { const fileName = crypto .createHash('sha256') .update(link) @@ -110,7 +109,7 @@ function getUniqueFileNameForLink(link) { return `${fileName}${fileExt}`; } -async function rewriteDatabaseUrls(dbPaths, mainDatabaseUrls, reportPath) { +async function rewriteDatabaseUrls(dbPaths: string[], mainDatabaseUrls: string, reportPath: string): Promise { const dbUrls = dbPaths.map(p => path.relative(reportPath, p)); await fs.writeJson(mainDatabaseUrls, { @@ -118,10 +117,3 @@ async function rewriteDatabaseUrls(dbPaths, mainDatabaseUrls, reportPath) { jsonUrls: [] }); } - -module.exports = { - ...commonSqliteUtils, - downloadDatabases, - mergeDatabases, - getTestsTreeFromDatabase -}; From 214df655e00ed1b5b89d0c6e9e4396b332f3a2bc Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 14:15:56 +0300 Subject: [PATCH 14/25] chore: rename plugin-api to typescript --- lib/{plugin-api.js => plugin-api.ts} | 2 ++ 1 file changed, 2 insertions(+) rename lib/{plugin-api.js => plugin-api.ts} (98%) diff --git a/lib/plugin-api.js b/lib/plugin-api.ts similarity index 98% rename from lib/plugin-api.js rename to lib/plugin-api.ts index afb18563a..1cfa1b092 100644 --- a/lib/plugin-api.js +++ b/lib/plugin-api.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ +// @ts-nocheck 'use strict'; const EventsEmitter2 = require('eventemitter2'); From f95b7364ab99a821abe32addbb10d3cb61ef244c Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 14:51:43 +0300 Subject: [PATCH 15/25] chore: re-write plugin-api to typescript --- lib/plugin-adapter.js | 4 +- lib/plugin-api.ts | 70 +++++++++++++++----------- lib/server-utils.ts | 2 +- lib/test-adapter.ts | 2 +- lib/types.ts | 6 ++- test/unit/lib/plugin-adapter.js | 4 +- test/unit/lib/plugin-api.js | 12 ++--- test/unit/lib/report-builder/gui.js | 4 +- test/unit/lib/report-builder/static.js | 4 +- test/unit/lib/sqlite-adapter.js | 4 +- 10 files changed, 63 insertions(+), 49 deletions(-) diff --git a/lib/plugin-adapter.js b/lib/plugin-adapter.js index 61309f887..ece184bf9 100644 --- a/lib/plugin-adapter.js +++ b/lib/plugin-adapter.js @@ -6,7 +6,7 @@ const parseConfig = require('./config'); const StaticReportBuilder = require('./report-builder/static'); const utils = require('./server-utils'); const cliCommands = require('./cli-commands'); -const PluginApi = require('./plugin-api'); +const {HtmlReporter} = require('./plugin-api'); module.exports = class PluginAdapter { static create(hermione, opts) { @@ -23,7 +23,7 @@ module.exports = class PluginAdapter { } addApi() { - this._hermione.htmlReporter = PluginApi.create(this._config); + this._hermione.htmlReporter = HtmlReporter.create(this._config); return this; } diff --git a/lib/plugin-api.ts b/lib/plugin-api.ts index 1cfa1b092..42a9d17e5 100644 --- a/lib/plugin-api.ts +++ b/lib/plugin-api.ts @@ -1,86 +1,96 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; - -const EventsEmitter2 = require('eventemitter2'); -const {PluginEvents: pluginEvents} = require('./constants/plugin-events'); -const {downloadDatabases, mergeDatabases, getTestsTreeFromDatabase} = require('./db-utils/server'); - -module.exports = class HtmlReporter extends EventsEmitter2 { - static create(config) { +import EventsEmitter2 from 'eventemitter2'; +import {PluginEvents} from './constants'; +import {downloadDatabases, mergeDatabases, getTestsTreeFromDatabase} from './db-utils/server'; +import {LocalImagesSaver} from './local-images-saver'; +import {version} from '../package.json'; +import {ImagesSaver, ReporterConfig, ReportsSaver} from './types'; + +interface HtmlReporterValues { + extraItems: Record; + metaInfoExtenders: Record; + imagesSaver: ImagesSaver; + reportsSaver: ReportsSaver | null; +} + +export class HtmlReporter extends EventsEmitter2 { + protected _config: ReporterConfig; + protected _values: HtmlReporterValues; + protected _version: string; + + static create(this: new (config: ReporterConfig) => T, config: ReporterConfig): T { return new this(config); } - constructor(config) { + constructor(config: ReporterConfig) { super(); this._config = config; this._values = { extraItems: {}, metaInfoExtenders: {}, - imagesSaver: require('./local-images-saver').LocalImagesSaver, + imagesSaver: LocalImagesSaver, reportsSaver: null }; - this._version = require('../package.json').version; + this._version = version; } - get config() { + get config(): ReporterConfig { return this._config; } - get version() { + get version(): string { return this._version; } - get events() { - return pluginEvents; + get events(): typeof PluginEvents { + return PluginEvents; } - addExtraItem(key, value) { + addExtraItem(key: string, value: string): void { this._values.extraItems[key] = value; } - get extraItems() { + get extraItems(): Record { return this._values.extraItems; } - addMetaInfoExtender(key, value) { + addMetaInfoExtender(key: string, value: string): void { this._values.metaInfoExtenders[key] = value; } - get metaInfoExtenders() { + get metaInfoExtenders(): Record { return this._values.metaInfoExtenders; } - set imagesSaver(imagesSaver) { + set imagesSaver(imagesSaver: ImagesSaver) { this._values.imagesSaver = imagesSaver; } - get imagesSaver() { + get imagesSaver(): ImagesSaver { return this._values.imagesSaver; } - set reportsSaver(reportsSaver) { + set reportsSaver(reportsSaver: ReportsSaver) { this._values.reportsSaver = reportsSaver; } - get reportsSaver() { + get reportsSaver(): ReportsSaver | null { return this._values.reportsSaver; } - get values() { + get values(): HtmlReporterValues { return this._values; } - downloadDatabases(...args) { + downloadDatabases(...args: Parameters): ReturnType { return downloadDatabases(...args); } - mergeDatabases(...args) { + mergeDatabases(...args: Parameters): ReturnType { return mergeDatabases(...args); } - getTestsTreeFromDatabase(...args) { + getTestsTreeFromDatabase(...args: Parameters): ReturnType { return getTestsTreeFromDatabase(...args); } -}; +} diff --git a/lib/server-utils.ts b/lib/server-utils.ts index c7fb02073..fccc4d9b3 100644 --- a/lib/server-utils.ts +++ b/lib/server-utils.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import fs from 'fs-extra'; import {logger} from './common-utils'; import {UPDATED, RUNNING, IDLE, SKIPPED, IMAGES_PATH, TestStatus} from './constants'; -import type HtmlReporter from './plugin-api'; +import type {HtmlReporter} from './plugin-api'; import type {TestAdapter} from './test-adapter'; import {CustomGuiItem, HtmlReporterApi, ReporterConfig} from './types'; import type Hermione from 'hermione'; diff --git a/lib/test-adapter.ts b/lib/test-adapter.ts index b440524cc..b6ad0327a 100644 --- a/lib/test-adapter.ts +++ b/lib/test-adapter.ts @@ -26,7 +26,7 @@ import { } from './types'; import type {SqliteAdapter} from './sqlite-adapter'; import EventEmitter2 from 'eventemitter2'; -import type HtmlReporter from './plugin-api'; +import type {HtmlReporter} from './plugin-api'; import type * as Workers from './workers/worker'; interface PrepareTestResultData { diff --git a/lib/types.ts b/lib/types.ts index ed5c77a30..e4c9790dc 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,7 +1,7 @@ import type {LooksSameOptions, CoordBounds} from 'looks-same'; import type {default as Hermione} from 'hermione'; import {DiffMode, TestStatus, ViewMode} from './constants'; -import type HtmlReporter from './plugin-api'; +import type {HtmlReporter} from './plugin-api'; declare module 'tmp' { export const tmpdir: string; @@ -17,6 +17,10 @@ export interface ImagesSaver { saveImg: (localFilePath: string, options: {destPath: string; reportDir: string}) => string | Promise; } +export interface ReportsSaver { + saveReportData: (localDbPath: string, options: {destPath: string; reportDir: string}) => string | Promise; +} + export interface ErrorDetails { title: string; data: unknown; diff --git a/test/unit/lib/plugin-adapter.js b/test/unit/lib/plugin-adapter.js index e09205752..c47369d10 100644 --- a/test/unit/lib/plugin-adapter.js +++ b/test/unit/lib/plugin-adapter.js @@ -5,7 +5,7 @@ const _ = require('lodash'); const proxyquire = require('proxyquire'); const {logger} = require('lib/common-utils'); const StaticReportBuilder = require('lib/report-builder/static'); -const PluginApi = require('lib/plugin-api'); +const {HtmlReporter} = require('lib/plugin-api'); const {stubTool, stubConfig} = require('../utils'); const {GUI, MERGE_REPORTS, REMOVE_UNUSED_SCREENS} = require('lib/cli-commands'); @@ -133,7 +133,7 @@ describe('lib/plugin-adapter', () => { const plugin = toolReporter.create(tool, opts); assert.deepEqual(plugin.addApi(), plugin); - assert.instanceOf(tool.htmlReporter, PluginApi); + assert.instanceOf(tool.htmlReporter, HtmlReporter); }); it(`should not register command if hermione called via API`, () => { diff --git a/test/unit/lib/plugin-api.js b/test/unit/lib/plugin-api.js index df096a0cf..6d303aa81 100644 --- a/test/unit/lib/plugin-api.js +++ b/test/unit/lib/plugin-api.js @@ -1,11 +1,11 @@ 'use strict'; -const PluginApi = require('lib/plugin-api'); +const {HtmlReporter} = require('lib/plugin-api'); const {PluginEvents} = require('lib/constants/plugin-events'); describe('plugin api', () => { it('should store extra items', () => { - const pluginApi = PluginApi.create(); + const pluginApi = HtmlReporter.create(); pluginApi.addExtraItem('some', 'item'); @@ -13,7 +13,7 @@ describe('plugin api', () => { }); it('should store meta info extenders', () => { - const pluginApi = PluginApi.create(); + const pluginApi = HtmlReporter.create(); pluginApi.addMetaInfoExtender('name', 'value'); @@ -21,7 +21,7 @@ describe('plugin api', () => { }); it('should return all stored values', () => { - const pluginApi = PluginApi.create(); + const pluginApi = HtmlReporter.create(); pluginApi.addExtraItem('key1', 'value1'); pluginApi.addMetaInfoExtender('key2', 'value2'); @@ -38,14 +38,14 @@ describe('plugin api', () => { describe('should provide access to', () => { it('plugin events', () => { - const pluginApi = PluginApi.create(); + const pluginApi = HtmlReporter.create(); assert.deepEqual(pluginApi.events, PluginEvents); }); it('plugin config', () => { const pluginConfig = {path: 'some-path'}; - const pluginApi = PluginApi.create(pluginConfig); + const pluginApi = HtmlReporter.create(pluginConfig); assert.deepEqual(pluginApi.config, pluginConfig); }); diff --git a/test/unit/lib/report-builder/gui.js b/test/unit/lib/report-builder/gui.js index bda41d216..58d6a4ad5 100644 --- a/test/unit/lib/report-builder/gui.js +++ b/test/unit/lib/report-builder/gui.js @@ -7,7 +7,7 @@ const serverUtils = require('lib/server-utils'); const {TestAdapter} = require('lib/test-adapter'); const {SqliteAdapter} = require('lib/sqlite-adapter'); const GuiTestsTreeBuilder = require('lib/tests-tree-builder/gui'); -const PluginApi = require('lib/plugin-api'); +const {HtmlReporter} = require('lib/plugin-api'); const {SUCCESS, FAIL, ERROR, SKIPPED, IDLE, RUNNING, UPDATED} = require('lib/constants/test-statuses'); const {LOCAL_DATABASE_NAME} = require('lib/constants/database'); const {mkFormattedTest} = require('../../utils'); @@ -26,7 +26,7 @@ describe('GuiReportBuilder', () => { const browserConfigStub = {getAbsoluteUrl: toolConfig.getAbsoluteUrl}; const hermione = { forBrowser: sandbox.stub().returns(browserConfigStub), - htmlReporter: PluginApi.create() + htmlReporter: HtmlReporter.create() }; TestAdapter.create = (obj) => obj; diff --git a/test/unit/lib/report-builder/static.js b/test/unit/lib/report-builder/static.js index 37856f936..bb5d0d464 100644 --- a/test/unit/lib/report-builder/static.js +++ b/test/unit/lib/report-builder/static.js @@ -4,7 +4,7 @@ const fs = require('fs-extra'); const _ = require('lodash'); const Database = require('better-sqlite3'); const proxyquire = require('proxyquire'); -const PluginApi = require('lib/plugin-api'); +const {HtmlReporter} = require('lib/plugin-api'); const {SUCCESS, FAIL, ERROR, SKIPPED} = require('lib/constants/test-statuses'); const {LOCAL_DATABASE_NAME} = require('lib/constants/database'); const {mkFormattedTest} = require('../../utils'); @@ -24,7 +24,7 @@ describe('StaticReportBuilder', () => { hermione = { forBrowser: sandbox.stub().returns(browserConfigStub), on: sandbox.spy(), - htmlReporter: _.extend(PluginApi.create(), { + htmlReporter: _.extend(HtmlReporter.create(), { reportsSaver: { saveReportData: sandbox.stub() } diff --git a/test/unit/lib/sqlite-adapter.js b/test/unit/lib/sqlite-adapter.js index 82c69f741..3bf15e344 100644 --- a/test/unit/lib/sqlite-adapter.js +++ b/test/unit/lib/sqlite-adapter.js @@ -5,7 +5,7 @@ const proxyquire = require('proxyquire'); const Database = require('better-sqlite3'); const {SqliteAdapter} = require('lib/sqlite-adapter'); -const PluginApi = require('lib/plugin-api'); +const {HtmlReporter} = require('lib/plugin-api'); describe('lib/sqlite-adapter', () => { const sandbox = sinon.createSandbox(); @@ -18,7 +18,7 @@ describe('lib/sqlite-adapter', () => { }; beforeEach(() => { - hermione = {htmlReporter: PluginApi.create()}; + hermione = {htmlReporter: HtmlReporter.create()}; }); afterEach(() => { From d4f1a167cd3897e334fbae6ab7ef325914fba5c9 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 17:08:49 +0300 Subject: [PATCH 16/25] chore: re-write static report builder to typescript --- lib/plugin-adapter.js | 2 +- lib/report-builder/gui.js | 2 +- lib/report-builder/static.ts | 104 +++++++++++++------------ lib/sqlite-adapter.ts | 5 +- lib/test-adapter.ts | 2 + lib/types.ts | 3 +- test/unit/hermione.js | 5 +- test/unit/lib/plugin-adapter.js | 2 +- test/unit/lib/report-builder/static.js | 18 +++-- 9 files changed, 82 insertions(+), 61 deletions(-) diff --git a/lib/plugin-adapter.js b/lib/plugin-adapter.js index ece184bf9..f946e0824 100644 --- a/lib/plugin-adapter.js +++ b/lib/plugin-adapter.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const Promise = require('bluebird'); const parseConfig = require('./config'); -const StaticReportBuilder = require('./report-builder/static'); +const {StaticReportBuilder} = require('./report-builder/static'); const utils = require('./server-utils'); const cliCommands = require('./cli-commands'); const {HtmlReporter} = require('./plugin-api'); diff --git a/lib/report-builder/gui.js b/lib/report-builder/gui.js index acc99e6d2..b5f393fd7 100644 --- a/lib/report-builder/gui.js +++ b/lib/report-builder/gui.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const path = require('path'); -const StaticReportBuilder = require('./static'); +const {StaticReportBuilder} = require('./static'); const GuiTestsTreeBuilder = require('../tests-tree-builder/gui'); const {IDLE, RUNNING, SKIPPED, FAIL, SUCCESS, UPDATED} = require('../constants/test-statuses'); const {hasResultFails, hasNoRefImageErrors} = require('../static/modules/utils'); diff --git a/lib/report-builder/static.ts b/lib/report-builder/static.ts index 2845b3bc9..acb5a5042 100644 --- a/lib/report-builder/static.ts +++ b/lib/report-builder/static.ts @@ -1,26 +1,36 @@ -/* eslint-disable */ -// @ts-nocheck -'use strict'; +import _ from 'lodash'; +import path from 'path'; +import fs from 'fs-extra'; +import type {default as Hermione} from 'hermione'; + +import {IDLE, RUNNING, SKIPPED, FAIL, ERROR, SUCCESS, TestStatus, LOCAL_DATABASE_NAME} from '../constants'; +import {PreparedTestResult, SqliteAdapter} from '../sqlite-adapter'; +import {TestAdapter} from '../test-adapter'; +import {hasNoRefImageErrors} from '../static/modules/utils'; +import {hasImage, saveStaticFilesToReportDir, writeDatabaseUrlsFile} from '../server-utils'; +import {HtmlReporterApi, ReporterConfig, TestResult} from '../types'; -const _ = require('lodash'); -const path = require('path'); -const fs = require('fs-extra'); +const ignoredStatuses = [RUNNING, IDLE]; -const {IDLE, RUNNING, SKIPPED, FAIL, ERROR, SUCCESS} = require('../constants/test-statuses'); -const {LOCAL_DATABASE_NAME} = require('../constants/database'); -const {SqliteAdapter} = require('../sqlite-adapter'); -const {TestAdapter} = require('../test-adapter'); -const {hasNoRefImageErrors} = require('../static/modules/utils'); -const {hasImage, saveStaticFilesToReportDir, writeDatabaseUrlsFile} = require('../server-utils'); +interface StaticReportBuilderOptions { + reuse: boolean; +} -const ignoredStatuses = [RUNNING, IDLE]; +export class StaticReportBuilder { + protected _hermione: Hermione & HtmlReporterApi; + protected _pluginConfig: ReporterConfig; + protected _sqliteAdapter: SqliteAdapter; -module.exports = class StaticReportBuilder { - static create(...args) { - return new this(...args); + static create( + this: new (hermione: Hermione & HtmlReporterApi, pluginConfig: ReporterConfig, options?: Partial) => T, + hermione: Hermione & HtmlReporterApi, + pluginConfig: ReporterConfig, + options?: Partial + ): T { + return new this(hermione, pluginConfig, options); } - constructor(hermione, pluginConfig, {reuse = false} = {}) { + constructor(hermione: Hermione & HtmlReporterApi, pluginConfig: ReporterConfig, {reuse = false}: Partial = {}) { this._hermione = hermione; this._pluginConfig = pluginConfig; @@ -31,11 +41,11 @@ module.exports = class StaticReportBuilder { }); } - async init() { + async init(): Promise { await this._sqliteAdapter.init(); } - format(result, status) { + format(result: TestResult | TestAdapter, status: TestStatus): TestAdapter { result.timestamp = Date.now(); return result instanceof TestAdapter @@ -47,19 +57,17 @@ module.exports = class StaticReportBuilder { }); } - async saveStaticFiles() { + async saveStaticFiles(): Promise { const destPath = this._pluginConfig.path; - await Promise.all( - [ - saveStaticFilesToReportDir(this._hermione, this._pluginConfig, destPath), - writeDatabaseUrlsFile(destPath, [LOCAL_DATABASE_NAME]) - ] - ); + await Promise.all([ + saveStaticFilesToReportDir(this._hermione, this._pluginConfig, destPath), + writeDatabaseUrlsFile(destPath, [LOCAL_DATABASE_NAME]) + ]); } - addSkipped(result) { - const formattedResult = this.format(result); + addSkipped(result: TestResult | TestAdapter): TestAdapter { + const formattedResult = this.format(result, SKIPPED); return this._addTestResult(formattedResult, { status: SKIPPED, @@ -67,20 +75,20 @@ module.exports = class StaticReportBuilder { }); } - addSuccess(result) { - return this._addTestResult(this.format(result), {status: SUCCESS}); + addSuccess(result: TestResult | TestAdapter): TestAdapter { + return this._addTestResult(this.format(result, SUCCESS), {status: SUCCESS}); } - addFail(result) { - return this._addFailResult(this.format(result)); + addFail(result: TestResult | TestAdapter): TestAdapter { + return this._addFailResult(this.format(result, FAIL)); } - addError(result) { - return this._addErrorResult(this.format(result)); + addError(result: TestResult | TestAdapter): TestAdapter { + return this._addErrorResult(this.format(result, ERROR)); } - addRetry(result) { - const formattedResult = this.format(result); + addRetry(result: TestResult | TestAdapter): TestAdapter { + const formattedResult = this.format(result, FAIL); if (formattedResult.hasDiff()) { return this._addFailResult(formattedResult); @@ -89,15 +97,15 @@ module.exports = class StaticReportBuilder { } } - _addFailResult(formattedResult) { + protected _addFailResult(formattedResult: TestAdapter): TestAdapter { return this._addTestResult(formattedResult, {status: FAIL}); } - _addErrorResult(formattedResult) { + protected _addErrorResult(formattedResult: TestAdapter): TestAdapter { return this._addTestResult(formattedResult, {status: ERROR, error: formattedResult.error}); } - _addTestResult(formattedResult, props) { + protected _addTestResult(formattedResult: TestAdapter, props: {status: TestStatus} & Partial): TestAdapter { formattedResult.image = hasImage(formattedResult); const testResult = this._createTestResult(formattedResult, _.extend(props, { @@ -115,17 +123,17 @@ module.exports = class StaticReportBuilder { return formattedResult; } - _createTestResult(result, props) { + _createTestResult(result: TestAdapter, props: {status: TestStatus} & Partial): PreparedTestResult { const { browserId, suite, sessionId, description, history, - imagesInfo, screenshot, multipleTabs, errorDetails + imagesInfo = [], screenshot, multipleTabs, errorDetails } = result; const {baseHost, saveErrorDetails} = this._pluginConfig; - const suiteUrl = suite.getUrl({browserId, baseHost}); + const suiteUrl: string = suite.getUrl({browserId, baseHost}); const metaInfo = _.merge(_.cloneDeep(result.meta), {url: suite.fullUrl, file: suite.file, sessionId}); - const testResult = Object.assign({ + const testResult: PreparedTestResult = Object.assign({ suiteUrl, name: browserId, metaInfo, description, history, imagesInfo, screenshot: Boolean(screenshot), multipleTabs }, props); @@ -137,7 +145,7 @@ module.exports = class StaticReportBuilder { return testResult; } - _writeTestResultToDb(testResult, formattedResult) { + _writeTestResultToDb(testResult: PreparedTestResult, formattedResult: TestAdapter): void { const {suite} = formattedResult; const suiteName = formattedResult.state.name; const suitePath = suite.path.concat(suiteName); @@ -145,11 +153,11 @@ module.exports = class StaticReportBuilder { this._sqliteAdapter.write({testResult, suitePath, suiteName}); } - _deleteTestResultFromDb({where, orderBy, orderDescending, limit}, ...args) { - this._sqliteAdapter.delete({where, orderBy, orderDescending, limit}, ...args); + _deleteTestResultFromDb(...args: Parameters): void { + this._sqliteAdapter.delete(...args); } - async finalize() { + async finalize(): Promise { this._sqliteAdapter.close(); const reportsSaver = this._hermione.htmlReporter.reportsSaver; @@ -162,4 +170,4 @@ module.exports = class StaticReportBuilder { await fs.remove(src); } } -}; +} diff --git a/lib/sqlite-adapter.ts b/lib/sqlite-adapter.ts index 14b8c6f66..0d0502a2c 100644 --- a/lib/sqlite-adapter.ts +++ b/lib/sqlite-adapter.ts @@ -11,7 +11,7 @@ import {TestStatus} from './constants'; import {DB_SUITES_TABLE_NAME, SUITES_TABLE_COLUMNS, LOCAL_DATABASE_NAME, DATABASE_URLS_JSON_NAME} from './constants/database'; import {createTablesQuery} from './db-utils/common'; import {DbNotInitializedError} from './errors/db-not-initialized-error'; -import type {HtmlReporterApi, ImageInfoFull} from './types'; +import type {ErrorDetails, HtmlReporterApi, ImageInfoFull} from './types'; const debug = makeDebug('html-reporter:sqlite-adapter'); @@ -37,13 +37,14 @@ export interface PreparedTestResult { metaInfo: Record; history: string[]; description: unknown; - error: Error; + error?: {message?: string; stack?: string; stateName?: string}; skipReason?: string; imagesInfo: ImageInfoFull[]; screenshot: boolean; multipleTabs: boolean; status: TestStatus; timestamp?: number; + errorDetails?: ErrorDetails; } interface ParseTestResultParams { diff --git a/lib/test-adapter.ts b/lib/test-adapter.ts index b6ad0327a..c675b9c4e 100644 --- a/lib/test-adapter.ts +++ b/lib/test-adapter.ts @@ -91,6 +91,8 @@ export class TestAdapter { this._attempt = testsAttempts.get(this._testId) || 0; } + image?: boolean; + get suite(): SuiteAdapter { return this._suite; } diff --git a/lib/types.ts b/lib/types.ts index e4c9790dc..25b65f1ed 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -23,7 +23,7 @@ export interface ReportsSaver { export interface ErrorDetails { title: string; - data: unknown; + data?: unknown; filePath: string; } @@ -174,6 +174,7 @@ export interface ReporterConfig { plugins: PluginDescription[]; pluginsEnabled: boolean; yandexMetrika: { counterNumber: null | number }; + saveErrorDetails: boolean; } export interface DbUrlsJsonData { diff --git a/test/unit/hermione.js b/test/unit/hermione.js index 156133a8a..74af516f3 100644 --- a/test/unit/hermione.js +++ b/test/unit/hermione.js @@ -35,7 +35,8 @@ describe('lib/hermione', () => { './server-utils': utils }); - const StaticReportBuilder = proxyquire('lib/report-builder/static', { + const {StaticReportBuilder} = proxyquire('lib/report-builder/static', { + 'fs-extra': fs, '../server-utils': utils, '../sqlite-adapter': {SqliteAdapter}, '../test-adapter': {TestAdapter} @@ -43,7 +44,7 @@ describe('lib/hermione', () => { const PluginAdapter = proxyquire('lib/plugin-adapter', { './server-utils': utils, - './report-builder/static': StaticReportBuilder, + './report-builder/static': {StaticReportBuilder}, './plugin-api': proxyquire('lib/plugin-api', { './local-images-saver': proxyquire('lib/local-images-saver', { './server-utils': utils diff --git a/test/unit/lib/plugin-adapter.js b/test/unit/lib/plugin-adapter.js index c47369d10..16771822f 100644 --- a/test/unit/lib/plugin-adapter.js +++ b/test/unit/lib/plugin-adapter.js @@ -4,7 +4,7 @@ const {EventEmitter} = require('events'); const _ = require('lodash'); const proxyquire = require('proxyquire'); const {logger} = require('lib/common-utils'); -const StaticReportBuilder = require('lib/report-builder/static'); +const {StaticReportBuilder} = require('lib/report-builder/static'); const {HtmlReporter} = require('lib/plugin-api'); const {stubTool, stubConfig} = require('../utils'); const {GUI, MERGE_REPORTS, REMOVE_UNUSED_SCREENS} = require('lib/cli-commands'); diff --git a/test/unit/lib/report-builder/static.js b/test/unit/lib/report-builder/static.js index bb5d0d464..04e98f13d 100644 --- a/test/unit/lib/report-builder/static.js +++ b/test/unit/lib/report-builder/static.js @@ -1,6 +1,6 @@ 'use strict'; -const fs = require('fs-extra'); +const fsOriginal = require('fs-extra'); const _ = require('lodash'); const Database = require('better-sqlite3'); const proxyquire = require('proxyquire'); @@ -14,7 +14,14 @@ const TEST_DB_PATH = `${TEST_REPORT_PATH}/${LOCAL_DATABASE_NAME}`; describe('StaticReportBuilder', () => { const sandbox = sinon.sandbox.create(); - let hasImage, StaticReportBuilder, hermione; + let StaticReportBuilder, hermione; + + const fs = _.clone(fsOriginal); + + const originalUtils = proxyquire('lib/server-utils', { + 'fs-extra': fs + }); + const utils = _.clone(originalUtils); const mkStaticReportBuilder_ = async ({toolConfig = {}, pluginConfig} = {}) => { toolConfig = _.defaults(toolConfig, {getAbsoluteUrl: _.noop}); @@ -59,11 +66,12 @@ describe('StaticReportBuilder', () => { }; beforeEach(() => { - hasImage = sandbox.stub().returns(true); + sandbox.stub(utils, 'hasImage').returns(true); StaticReportBuilder = proxyquire('lib/report-builder/static', { - '../server-utils': {hasImage} - }); + 'fs-extra': fs, + '../server-utils': utils + }).StaticReportBuilder; }); afterEach(() => { From 676353a8aac468b6b13c42e569cc92d44f020008 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 21:09:23 +0300 Subject: [PATCH 17/25] chore: re-write create-workers to typescript --- hermione.js | 2 +- lib/gui/tool-runner/report-subscriber.js | 2 +- lib/workers/create-workers.js | 7 ------- lib/workers/create-workers.ts | 15 +++++++++++++++ 4 files changed, 17 insertions(+), 9 deletions(-) delete mode 100644 lib/workers/create-workers.js create mode 100644 lib/workers/create-workers.ts diff --git a/hermione.js b/hermione.js index 7c2032b21..00e1d6bee 100644 --- a/hermione.js +++ b/hermione.js @@ -3,7 +3,7 @@ const os = require('os'); const PQueue = require('p-queue'); const PluginAdapter = require('./lib/plugin-adapter'); -const createWorkers = require('./lib/workers/create-workers'); +const {createWorkers} = require('./lib/workers/create-workers'); let workers; diff --git a/lib/gui/tool-runner/report-subscriber.js b/lib/gui/tool-runner/report-subscriber.js index 03f487b06..696efee1b 100644 --- a/lib/gui/tool-runner/report-subscriber.js +++ b/lib/gui/tool-runner/report-subscriber.js @@ -5,7 +5,7 @@ const PQueue = require('p-queue'); const clientEvents = require('../constants/client-events'); const {RUNNING} = require('../../constants/test-statuses'); const {getSuitePath} = require('../../plugin-utils'); -const createWorkers = require('../../workers/create-workers'); +const {createWorkers} = require('../../workers/create-workers'); const {logError} = require('../../server-utils'); let workers; diff --git a/lib/workers/create-workers.js b/lib/workers/create-workers.js deleted file mode 100644 index dff0bc7b0..000000000 --- a/lib/workers/create-workers.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -module.exports = (runner) => { - const workerFilepath = require.resolve('./worker'); - - return runner.registerWorkers(workerFilepath, ['saveDiffTo']); -}; diff --git a/lib/workers/create-workers.ts b/lib/workers/create-workers.ts new file mode 100644 index 000000000..5407ee888 --- /dev/null +++ b/lib/workers/create-workers.ts @@ -0,0 +1,15 @@ +import type {EventEmitter} from 'events'; + +type MapOfMethods> = { + [K in T[number]]: (...args: Array) => Promise | unknown; +}; + +type RegisterWorkers> = EventEmitter & MapOfMethods; + +export const createWorkers = ( + runner: {registerWorkers: (workerFilePath: string, exportedMethods: string[]) => RegisterWorkers<['saveDiffTo']>} +): RegisterWorkers<['saveDiffTo']> => { + const workerFilepath = require.resolve('./worker'); + + return runner.registerWorkers(workerFilepath, ['saveDiffTo']); +}; From 72f998e82fd10ad7b59559434fcf3cd968bc9182 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sat, 19 Aug 2023 21:16:12 +0300 Subject: [PATCH 18/25] chore: re-write cli-commands/index to typescript --- lib/cli-commands/{index.js => index.ts} | 6 ++---- lib/plugin-adapter.js | 2 +- test/unit/lib/plugin-adapter.js | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) rename lib/cli-commands/{index.js => index.ts} (71%) diff --git a/lib/cli-commands/index.js b/lib/cli-commands/index.ts similarity index 71% rename from lib/cli-commands/index.js rename to lib/cli-commands/index.ts index 5ccabff27..f6ec574a7 100644 --- a/lib/cli-commands/index.js +++ b/lib/cli-commands/index.ts @@ -1,7 +1,5 @@ -'use strict'; - -module.exports = { +export const cliCommands = { GUI: 'gui', MERGE_REPORTS: 'merge-reports', REMOVE_UNUSED_SCREENS: 'remove-unused-screens' -}; +} as const; diff --git a/lib/plugin-adapter.js b/lib/plugin-adapter.js index f946e0824..204b08ea1 100644 --- a/lib/plugin-adapter.js +++ b/lib/plugin-adapter.js @@ -5,7 +5,7 @@ const Promise = require('bluebird'); const parseConfig = require('./config'); const {StaticReportBuilder} = require('./report-builder/static'); const utils = require('./server-utils'); -const cliCommands = require('./cli-commands'); +const {cliCommands} = require('./cli-commands'); const {HtmlReporter} = require('./plugin-api'); module.exports = class PluginAdapter { diff --git a/test/unit/lib/plugin-adapter.js b/test/unit/lib/plugin-adapter.js index 16771822f..0e0666d80 100644 --- a/test/unit/lib/plugin-adapter.js +++ b/test/unit/lib/plugin-adapter.js @@ -7,7 +7,7 @@ const {logger} = require('lib/common-utils'); const {StaticReportBuilder} = require('lib/report-builder/static'); const {HtmlReporter} = require('lib/plugin-api'); const {stubTool, stubConfig} = require('../utils'); -const {GUI, MERGE_REPORTS, REMOVE_UNUSED_SCREENS} = require('lib/cli-commands'); +const {GUI, MERGE_REPORTS, REMOVE_UNUSED_SCREENS} = require('lib/cli-commands').cliCommands; describe('lib/plugin-adapter', () => { const sandbox = sinon.createSandbox(); From 9581923e28ce7b99546a43d3c1e984ec149a1dd2 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sun, 20 Aug 2023 13:29:01 +0300 Subject: [PATCH 19/25] chore: re-write defaults constants to typescript --- lib/config/index.js | 2 +- lib/constants/defaults.js | 25 ------------------------- lib/constants/defaults.ts | 22 ++++++++++++++++++++++ lib/constants/index.ts | 1 + lib/static/modules/custom-queries.js | 4 ++-- lib/static/modules/default-state.js | 4 ++-- test/unit/lib/config/index.js | 2 +- 7 files changed, 29 insertions(+), 31 deletions(-) delete mode 100644 lib/constants/defaults.js create mode 100644 lib/constants/defaults.ts diff --git a/lib/config/index.js b/lib/config/index.js index 2dc859ade..17edd2ef3 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -5,7 +5,7 @@ const configParser = require('gemini-configparser'); const chalk = require('chalk'); const {logger} = require('../common-utils'); -const {config: configDefaults} = require('../constants/defaults'); +const {configDefaults} = require('../constants/defaults'); const {DiffModes} = require('../constants/diff-modes'); const saveFormats = require('../constants/save-formats'); const {assertCustomGui} = require('./custom-gui-asserts'); diff --git a/lib/constants/defaults.js b/lib/constants/defaults.js deleted file mode 100644 index 6bf6ec1ae..000000000 --- a/lib/constants/defaults.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -const {DiffModes} = require('./diff-modes'); -const {ViewMode} = require('./view-modes'); - -module.exports = { - CIRCLE_RADIUS: 150, - config: { - saveErrorDetails: false, - commandsWithShortHistory: [], - defaultView: ViewMode.ALL, - diffMode: DiffModes.THREE_UP.id, - baseHost: '', - lazyLoadOffset: null, - errorPatterns: [], - metaInfoBaseUrls: {}, - customGui: {}, - customScripts: [], - yandexMetrika: { - counterNumber: null - }, - pluginsEnabled: false, - plugins: [] - } -}; diff --git a/lib/constants/defaults.ts b/lib/constants/defaults.ts new file mode 100644 index 000000000..aa0477578 --- /dev/null +++ b/lib/constants/defaults.ts @@ -0,0 +1,22 @@ +import {DiffModes} from './diff-modes'; +import {ViewMode} from './view-modes'; + +export const CIRCLE_RADIUS = 150; + +export const configDefaults = { + saveErrorDetails: false, + commandsWithShortHistory: [], + defaultView: ViewMode.ALL, + diffMode: DiffModes.THREE_UP.id, + baseHost: '', + lazyLoadOffset: null, + errorPatterns: [], + metaInfoBaseUrls: {}, + customGui: {}, + customScripts: [], + yandexMetrika: { + counterNumber: null + }, + pluginsEnabled: false, + plugins: [] +} as const; diff --git a/lib/constants/index.ts b/lib/constants/index.ts index ba1ae9c1d..da44a2796 100644 --- a/lib/constants/index.ts +++ b/lib/constants/index.ts @@ -1,5 +1,6 @@ export * from './browser'; export * from './database'; +export * from './defaults'; export * from './diff-modes'; export * from './paths'; export * from './plugin-events'; diff --git a/lib/static/modules/custom-queries.js b/lib/static/modules/custom-queries.js index 1d996a50f..f31ee33bf 100644 --- a/lib/static/modules/custom-queries.js +++ b/lib/static/modules/custom-queries.js @@ -3,7 +3,7 @@ import {parseQuery, decodeBrowsers} from './query-params'; import {pick, has} from 'lodash'; import {ViewMode} from '../../constants/view-modes'; -import {config} from '../../constants/defaults'; +import {configDefaults} from '../../constants/defaults'; const allowedViewModes = new Set(Object.values(ViewMode)); @@ -13,7 +13,7 @@ export function getViewQuery(queryString) { query.filteredBrowsers = decodeBrowsers(query.filteredBrowsers); if (has(query, 'viewMode') && !allowedViewModes.has(query.viewMode)) { - query.viewMode = config.defaultView; + query.viewMode = configDefaults.defaultView; } return pick(query, [ diff --git a/lib/static/modules/default-state.js b/lib/static/modules/default-state.js index 2ac31c190..59d073bbc 100644 --- a/lib/static/modules/default-state.js +++ b/lib/static/modules/default-state.js @@ -1,12 +1,12 @@ 'use strict'; -const defaults = require('../../constants/defaults'); +const {configDefaults} = require('../../constants/defaults'); const {ViewMode} = require('../../constants/view-modes'); const {DiffModes} = require('../../constants/diff-modes'); const {EXPAND_ERRORS} = require('../../constants/expand-modes'); const {RESULT_KEYS} = require('../../constants/group-tests'); -export default Object.assign(defaults, { +export default Object.assign({config: configDefaults}, { gui: true, running: false, processing: false, diff --git a/test/unit/lib/config/index.js b/test/unit/lib/config/index.js index 82e980b73..279e9f219 100644 --- a/test/unit/lib/config/index.js +++ b/test/unit/lib/config/index.js @@ -2,7 +2,7 @@ const {isEmpty} = require('lodash'); const parseConfig = require('lib/config'); -const {config: configDefaults} = require('lib/constants/defaults'); +const {configDefaults} = require('lib/constants/defaults'); const {ViewMode} = require('lib/constants/view-modes'); const {DiffModes} = require('lib/constants/diff-modes'); const saveFormats = require('lib/constants/save-formats'); From 25fb2b351ef2cb6df265482c0b9febeaa4111796 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sun, 20 Aug 2023 13:34:46 +0300 Subject: [PATCH 20/25] chore: re-write save-formats constants to typescript --- lib/config/index.js | 6 +++--- lib/constants/save-formats.js | 5 ----- lib/constants/save-formats.ts | 3 +++ test/unit/lib/config/index.js | 6 +++--- 4 files changed, 9 insertions(+), 11 deletions(-) delete mode 100644 lib/constants/save-formats.js create mode 100644 lib/constants/save-formats.ts diff --git a/lib/config/index.js b/lib/config/index.js index 17edd2ef3..59086ec84 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -7,7 +7,7 @@ const chalk = require('chalk'); const {logger} = require('../common-utils'); const {configDefaults} = require('../constants/defaults'); const {DiffModes} = require('../constants/diff-modes'); -const saveFormats = require('../constants/save-formats'); +const {SaveFormat} = require('../constants/save-formats'); const {assertCustomGui} = require('./custom-gui-asserts'); const root = configParser.root; @@ -31,7 +31,7 @@ const assertBoolean = (name) => assertType(name, _.isBoolean, 'boolean'); const assertNumber = (name) => assertType(name, _.isNumber, 'number'); const assertSaveFormat = saveFormat => { - const formats = Object.values(saveFormats); + const formats = Object.values(SaveFormat); if (!_.isString(saveFormat)) { throw new Error(`"saveFormat" option must be string, but got ${typeof saveFormat}`); } @@ -152,7 +152,7 @@ const getParser = () => { validate: assertString('path') }), saveFormat: option({ - defaultValue: saveFormats.SQLITE, + defaultValue: SaveFormat.SQLITE, validate: assertSaveFormat }), saveErrorDetails: option({ diff --git a/lib/constants/save-formats.js b/lib/constants/save-formats.js deleted file mode 100644 index 42cfe215f..000000000 --- a/lib/constants/save-formats.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = { - SQLITE: 'sqlite' -}; diff --git a/lib/constants/save-formats.ts b/lib/constants/save-formats.ts new file mode 100644 index 000000000..c3614bf97 --- /dev/null +++ b/lib/constants/save-formats.ts @@ -0,0 +1,3 @@ +export enum SaveFormat { + SQLITE = 'sqlite' +} diff --git a/test/unit/lib/config/index.js b/test/unit/lib/config/index.js index 279e9f219..ab071dac2 100644 --- a/test/unit/lib/config/index.js +++ b/test/unit/lib/config/index.js @@ -5,7 +5,7 @@ const parseConfig = require('lib/config'); const {configDefaults} = require('lib/constants/defaults'); const {ViewMode} = require('lib/constants/view-modes'); const {DiffModes} = require('lib/constants/diff-modes'); -const saveFormats = require('lib/constants/save-formats'); +const {SaveFormat} = require('lib/constants/save-formats'); const SUPPORTED_CONTROL_TYPES = Object.values(require('lib/gui/constants/custom-gui-control-types')); const {logger} = require('lib/common-utils'); @@ -82,8 +82,8 @@ describe('config', () => { }); describe('"saveFormat" option', () => { - it(`should be ${saveFormats.SQLITE} by default`, () => { - assert.equal(parseConfig({}).saveFormat, saveFormats.SQLITE); + it(`should be ${SaveFormat.SQLITE} by default`, () => { + assert.equal(parseConfig({}).saveFormat, SaveFormat.SQLITE); }); }); From 36b35ad298a933ed7faae6ab9c4b091fc945c399 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Sun, 20 Aug 2023 13:45:17 +0300 Subject: [PATCH 21/25] chore: re-write custom-gui-asserts to typescript --- ...m-gui-asserts.js => custom-gui-asserts.ts} | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) rename lib/config/{custom-gui-asserts.js => custom-gui-asserts.ts} (55%) diff --git a/lib/config/custom-gui-asserts.js b/lib/config/custom-gui-asserts.ts similarity index 55% rename from lib/config/custom-gui-asserts.js rename to lib/config/custom-gui-asserts.ts index 0f41554de..884ed9346 100644 --- a/lib/config/custom-gui-asserts.js +++ b/lib/config/custom-gui-asserts.ts @@ -1,10 +1,9 @@ -'use strict'; +import {isUndefined, isArray, isEmpty, isFunction, isPlainObject, isString} from 'lodash'; +import CustomGuiControlTypes from '../gui/constants/custom-gui-control-types'; -const {isUndefined, isArray, isEmpty, isFunction, isPlainObject, isString} = require('lodash'); +const SUPPORTED_CONTROL_TYPES: string[] = Object.values(CustomGuiControlTypes); -const SUPPORTED_CONTROL_TYPES = Object.values(require('../gui/constants/custom-gui-control-types')); - -const assertSectionGroupType = (context, type) => { +const assertSectionGroupType = (context: string, type: unknown): void => { if (isUndefined(type)) { throw new Error(`${context} must contain field "type"`); } @@ -16,7 +15,7 @@ const assertSectionGroupType = (context, type) => { } }; -const assertSectionGroupControls = (context, controls) => { +const assertSectionGroupControls = (context: string, controls: unknown): void => { if (isUndefined(controls)) { throw new Error(`${context} must contain field "controls"`); } @@ -26,14 +25,14 @@ const assertSectionGroupControls = (context, controls) => { if (isEmpty(controls)) { throw new Error(`${context} must contain non-empty array in the field "controls"`); } - controls.forEach((control) => { + controls.forEach((control: unknown) => { if (!isPlainObject(control)) { throw new Error(`${context} must contain objects in the array "controls"`); } }); }; -const assertSectionGroupAction = (context, action) => { +const assertSectionGroupAction = (context: string, action: unknown): void => { if (isUndefined(action)) { throw new Error(`${context} must contain field "action"`); } @@ -42,34 +41,35 @@ const assertSectionGroupAction = (context, action) => { } }; -const assertSectionGroup = (sectionName, group, groupIndex) => { +const assertSectionGroup = (sectionName: string, group: unknown, groupIndex: number): void => { const context = `customGui["${sectionName}"][${groupIndex}]`; if (!isPlainObject(group)) { throw new Error(`${context} must be plain object, but got ${typeof group}`); } - assertSectionGroupType(context, group.type); - assertSectionGroupControls(context, group.controls); - assertSectionGroupAction(context, group.action); + const groupObj = group as Record; + + assertSectionGroupType(context, groupObj.type); + assertSectionGroupControls(context, groupObj.controls); + assertSectionGroupAction(context, groupObj.action); }; -const assertSection = (section, sectionName) => { +const assertSection = (section: unknown, sectionName: string): void => { if (!isArray(section)) { throw new Error(`customGui["${sectionName}"] must be an array, but got ${typeof section}`); } - section.forEach((group, groupIndex) => assertSectionGroup(sectionName, group, groupIndex)); + section.forEach((group: unknown, groupIndex: number) => assertSectionGroup(sectionName, group, groupIndex)); }; -const assertCustomGui = (customGui) => { +export const assertCustomGui = (customGui: unknown): void => { if (!isPlainObject(customGui)) { throw new Error(`"customGui" option must be plain object, but got ${typeof customGui}`); } - for (const sectionName in customGui) { - assertSection(customGui[sectionName], sectionName); - } -}; -module.exports = { - assertCustomGui + const customGuiObj = customGui as Record; + + for (const sectionName in customGuiObj) { + assertSection(customGuiObj[sectionName], sectionName); + } }; From dd01de8aca60e778216966ecb5052118cf751b6a Mon Sep 17 00:00:00 2001 From: shadowusr Date: Mon, 21 Aug 2023 17:25:08 +0300 Subject: [PATCH 22/25] chore: re-write config to typescript --- docs/en/html-reporter-events.md | 2 +- docs/ru/html-reporter-events.md | 2 +- lib/config/{index.js => index.ts} | 100 +++++++++++++++--------------- lib/constants/defaults.ts | 25 +++++--- lib/constants/diff-modes.ts | 2 + lib/constants/index.ts | 1 + lib/plugin-adapter.js | 2 +- lib/types.ts | 27 ++++++-- test/unit/lib/config/index.js | 2 +- test/unit/lib/plugin-adapter.js | 2 +- 10 files changed, 94 insertions(+), 71 deletions(-) rename lib/config/{index.js => index.ts} (65%) diff --git a/docs/en/html-reporter-events.md b/docs/en/html-reporter-events.md index b6cc98e07..eeb6029a7 100644 --- a/docs/en/html-reporter-events.md +++ b/docs/en/html-reporter-events.md @@ -29,7 +29,7 @@ A database instance is passed to the event handler. ### Usage example ```javascript -const parseConfig = require('./config'); +const {parseConfig} = require('./config'); module.exports = (hermione, opts) => { const pluginConfig = parseConfig(opts); diff --git a/docs/ru/html-reporter-events.md b/docs/ru/html-reporter-events.md index 299161af1..3b5ec2065 100644 --- a/docs/ru/html-reporter-events.md +++ b/docs/ru/html-reporter-events.md @@ -29,7 +29,7 @@ hermione.htmlReporter.on(hermione.htmlReporter.events.DATABASE_CREATED, (db) => ### Пример использования ```javascript -const parseConfig = require('./config'); +const {parseConfig} = require('./config'); module.exports = (hermione, opts) => { const pluginConfig = parseConfig(opts); diff --git a/lib/config/index.js b/lib/config/index.ts similarity index 65% rename from lib/config/index.js rename to lib/config/index.ts index 59086ec84..6e3ae9bb7 100644 --- a/lib/config/index.js +++ b/lib/config/index.ts @@ -1,47 +1,43 @@ -'use strict'; +import _ from 'lodash'; +import {root, section, option} from 'gemini-configparser'; +import chalk from 'chalk'; -const _ = require('lodash'); -const configParser = require('gemini-configparser'); -const chalk = require('chalk'); - -const {logger} = require('../common-utils'); -const {configDefaults} = require('../constants/defaults'); -const {DiffModes} = require('../constants/diff-modes'); -const {SaveFormat} = require('../constants/save-formats'); -const {assertCustomGui} = require('./custom-gui-asserts'); - -const root = configParser.root; -const section = configParser.section; -const option = configParser.option; +import {logger} from '../common-utils'; +import {configDefaults, DiffModeId, DiffModes, SaveFormat, ViewMode} from '../constants'; +import {assertCustomGui} from './custom-gui-asserts'; +import {ErrorPattern, PluginDescription, ReporterConfig, ReporterOptions} from '../types'; const ENV_PREFIX = 'html_reporter_'; const CLI_PREFIX = '--html-reporter-'; const ALLOWED_PLUGIN_DESCRIPTION_FIELDS = new Set(['name', 'component', 'point', 'position', 'config']); -const assertType = (name, validationFn, type) => { - return (v) => { +type TypePredicateFn = (value: unknown) => value is T; +type AssertionFn = (value: unknown) => asserts value is T; + +const assertType = (name: string, validationFn: (value: unknown) => value is T, type: string): AssertionFn => { + return (v: unknown): asserts v is T => { if (!validationFn(v)) { throw new Error(`"${name}" option must be ${type}, but got ${typeof v}`); } }; }; -const assertString = (name) => assertType(name, _.isString, 'string'); -const assertBoolean = (name) => assertType(name, _.isBoolean, 'boolean'); -const assertNumber = (name) => assertType(name, _.isNumber, 'number'); +const assertString = (name: string): AssertionFn => assertType(name, _.isString, 'string'); +const assertBoolean = (name: string): AssertionFn => assertType(name, _.isBoolean, 'boolean'); +const assertNumber = (name: string): AssertionFn => assertType(name, _.isNumber, 'number'); -const assertSaveFormat = saveFormat => { +const assertSaveFormat = (saveFormat: unknown): asserts saveFormat is SaveFormat => { const formats = Object.values(SaveFormat); if (!_.isString(saveFormat)) { throw new Error(`"saveFormat" option must be string, but got ${typeof saveFormat}`); } - if (!formats.includes(saveFormat)) { + if (!formats.includes(saveFormat as SaveFormat)) { throw new Error(`"saveFormat" must be "${formats.join('", "')}", but got "${saveFormat}"`); } }; -const assertErrorPatterns = (errorPatterns) => { +const assertErrorPatterns = (errorPatterns: unknown): asserts errorPatterns is (string | ErrorPattern)[] => { if (!_.isArray(errorPatterns)) { throw new Error(`"errorPatterns" option must be array, but got ${typeof errorPatterns}`); } @@ -59,51 +55,54 @@ const assertErrorPatterns = (errorPatterns) => { } }; -const assertMetaInfoBaseUrls = (metaInfoBaseUrls) => { +const assertMetaInfoBaseUrls = (metaInfoBaseUrls: unknown): asserts metaInfoBaseUrls is Record => { if (!_.isObject(metaInfoBaseUrls)) { throw new Error(`"metaInfoBaseUrls" option must be object, but got ${typeof metaInfoBaseUrls}`); } - for (const key in metaInfoBaseUrls) { + for (const keyStr in metaInfoBaseUrls) { + const key = keyStr as keyof typeof metaInfoBaseUrls; if (!_.isString(metaInfoBaseUrls[key])) { throw new Error(`Value of "${key}" in "metaInfoBaseUrls" option must be string, but got ${typeof metaInfoBaseUrls[key]}`); } } }; -const assertArrayOf = (itemsType, name, assertFn) => { - return (value) => { +const assertArrayOf = (itemsType: string, name: string, predicateFn: TypePredicateFn) => { + return (value: unknown): asserts value is T[] => { if (!_.isArray(value)) { throw new Error(`"${name}" option must be an array, but got ${typeof value}`); } for (const item of value) { - if (!assertFn(item)) { + if (!predicateFn(item)) { throw new Error(`"${name}" option must be an array of ${itemsType} but got ${typeof item} for one of items`); } } }; }; -const assertPluginDescription = (description) => { - if (!_.isPlainObject(description)) { +const assertPluginDescription = (description: unknown): description is PluginDescription => { + const maybeDescription = description as PluginDescription; + + if (!_.isPlainObject(maybeDescription)) { throw new Error(`plugin description expected to be an object but got ${typeof description}`); } - for (const field of ['name', 'component']) { - if (!description[field] || !_.isString(description[field])) { - throw new Error(`"plugins.${field}" option must be non-empty string but got ${typeof description[field]}`); + for (const field of ['name', 'component'] as const) { + if (!maybeDescription[field] || !_.isString(maybeDescription[field])) { + throw new Error(`"plugins.${field}" option must be non-empty string but got ${typeof maybeDescription[field]}`); } } - if (description.point && !_.isString(description.point)) { - throw new Error(`"plugins.point" option must be string but got ${typeof description.point}`); + if (maybeDescription.point && !_.isString(maybeDescription.point)) { + throw new Error(`"plugins.point" option must be string but got ${typeof maybeDescription.point}`); } - if (description.position && !['after', 'before', 'wrap'].includes(description.position)) { - throw new Error(`"plugins.position" option got an unexpected value "${description.position}"`); + if (maybeDescription.position && !['after', 'before', 'wrap'].includes(maybeDescription.position)) { + throw new Error(`"plugins.position" option got an unexpected value "${maybeDescription.position}"`); } - if (description.config && !_.isPlainObject(description.config)) { - throw new Error(`plugin configuration expected to be an object but got ${typeof description.config}`); + if (maybeDescription.config && !_.isPlainObject(maybeDescription.config)) { + throw new Error(`plugin configuration expected to be an object but got ${typeof maybeDescription.config}`); } _.forOwn(description, (value, key) => { @@ -115,19 +114,19 @@ const assertPluginDescription = (description) => { return true; }; -const assertDiffMode = (diffMode) => { +const assertDiffMode = (diffMode: unknown): asserts diffMode is DiffModeId => { if (!_.isString(diffMode)) { throw new Error(`"diffMode" option must be a string, but got ${typeof diffMode}`); } const availableValues = Object.values(DiffModes).map(v => v.id); - if (!availableValues.includes(diffMode)) { + if (!availableValues.includes(diffMode as DiffModeId)) { throw new Error(`"diffMode" must be one of "${availableValues.join('", "')}", but got "${diffMode}"`); } }; -const mapErrorPatterns = (errorPatterns) => { +const mapErrorPatterns = (errorPatterns: (string | ErrorPattern)[]): ErrorPattern[] => { return errorPatterns.map(patternInfo => { return _.isString(patternInfo) ? {name: patternInfo, pattern: patternInfo} @@ -135,12 +134,12 @@ const mapErrorPatterns = (errorPatterns) => { }); }; -const deprecationWarning = (name) => { +const deprecationWarning = (name: string): void => { logger.warn(chalk.red(`Warning: field "${name}" is deprecated and will be removed soon from html-reporter config.`)); }; -const getParser = () => { - return root(section({ +const getParser = (): ReturnType> => { + return root(section({ enabled: option({ defaultValue: true, parseEnv: JSON.parse, @@ -165,11 +164,11 @@ const getParser = () => { defaultValue: configDefaults.commandsWithShortHistory, validate: assertArrayOf('strings', 'commandsWithShortHistory', _.isString) }), - defaultView: option({ + defaultView: option({ defaultValue: configDefaults.defaultView, validate: assertString('defaultView') }), - diffMode: option({ + diffMode: option({ defaultValue: configDefaults.diffMode, validate: assertDiffMode }), @@ -181,7 +180,7 @@ const getParser = () => { defaultValue: configDefaults.lazyLoadOffset, validate: (value) => _.isNull(value) || deprecationWarning('lazyLoadOffset') }), - errorPatterns: option({ + errorPatterns: option<(string | ErrorPattern)[], ErrorPattern[]>({ defaultValue: configDefaults.errorPatterns, parseEnv: JSON.parse, validate: assertErrorPatterns, @@ -197,7 +196,7 @@ const getParser = () => { defaultValue: configDefaults.customGui, validate: assertCustomGui }), - customScripts: option({ + customScripts: option<(() => void)[]>({ defaultValue: configDefaults.customScripts, validate: assertArrayOf('functions', 'customScripts', _.isFunction) }), @@ -224,9 +223,10 @@ const getParser = () => { }), {envPrefix: ENV_PREFIX, cliPrefix: CLI_PREFIX}); }; -module.exports = (options) => { +export const parseConfig = (options: Partial): ReporterConfig => { const env = process.env; const argv = process.argv; - return getParser()({options, env, argv}); + // TODO: add support for different types of input and output in gemini-configparser + return getParser()({options: options as ReporterConfig, env, argv}); }; diff --git a/lib/constants/defaults.ts b/lib/constants/defaults.ts index aa0477578..9bb2d57da 100644 --- a/lib/constants/defaults.ts +++ b/lib/constants/defaults.ts @@ -1,22 +1,27 @@ import {DiffModes} from './diff-modes'; import {ViewMode} from './view-modes'; +import {ReporterConfig} from '../types'; +import {SaveFormat} from './save-formats'; export const CIRCLE_RADIUS = 150; -export const configDefaults = { - saveErrorDetails: false, +export const configDefaults: ReporterConfig = { + baseHost: '', commandsWithShortHistory: [], + customGui: {}, + customScripts: [], defaultView: ViewMode.ALL, diffMode: DiffModes.THREE_UP.id, - baseHost: '', - lazyLoadOffset: null, + enabled: false, errorPatterns: [], + lazyLoadOffset: null, metaInfoBaseUrls: {}, - customGui: {}, - customScripts: [], + path: '', + plugins: [], + pluginsEnabled: false, + saveErrorDetails: false, + saveFormat: SaveFormat.SQLITE, yandexMetrika: { counterNumber: null - }, - pluginsEnabled: false, - plugins: [] -} as const; + } +}; diff --git a/lib/constants/diff-modes.ts b/lib/constants/diff-modes.ts index 594d77006..af9176002 100644 --- a/lib/constants/diff-modes.ts +++ b/lib/constants/diff-modes.ts @@ -41,3 +41,5 @@ export const DiffModes = { export type DiffModes = typeof DiffModes; export type DiffMode = ValueOf; + +export type DiffModeId = DiffModes[keyof DiffModes]['id']; diff --git a/lib/constants/index.ts b/lib/constants/index.ts index da44a2796..be8296aa2 100644 --- a/lib/constants/index.ts +++ b/lib/constants/index.ts @@ -4,5 +4,6 @@ export * from './defaults'; export * from './diff-modes'; export * from './paths'; export * from './plugin-events'; +export * from './save-formats'; export * from './test-statuses'; export * from './view-modes'; diff --git a/lib/plugin-adapter.js b/lib/plugin-adapter.js index 204b08ea1..08200fdf6 100644 --- a/lib/plugin-adapter.js +++ b/lib/plugin-adapter.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const Promise = require('bluebird'); -const parseConfig = require('./config'); +const {parseConfig} = require('./config'); const {StaticReportBuilder} = require('./report-builder/static'); const utils = require('./server-utils'); const {cliCommands} = require('./cli-commands'); diff --git a/lib/types.ts b/lib/types.ts index 25b65f1ed..faa791fab 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,6 +1,6 @@ import type {LooksSameOptions, CoordBounds} from 'looks-same'; import type {default as Hermione} from 'hermione'; -import {DiffMode, TestStatus, ViewMode} from './constants'; +import {DiffModeId, SaveFormat, TestStatus, ViewMode} from './constants'; import type {HtmlReporter} from './plugin-api'; declare module 'tmp' { @@ -151,8 +151,17 @@ export interface HtmlReporterApi { htmlReporter: HtmlReporter; } +export interface ErrorPattern { + name: string; + pattern: string; +} + export interface PluginDescription { name: string; + component: string; + point?: string; + position?: 'after' | 'before' | 'wrap'; + config?: Record; } export interface CustomGuiItem { @@ -164,19 +173,25 @@ export interface CustomGuiItem { export interface ReporterConfig { baseHost: string; - defaultView: ViewMode; + commandsWithShortHistory: string[]; customGui: Record; - customScripts: object[]; - diffMode: DiffMode; - errorPatterns: object[]; + customScripts: (() => void)[]; + defaultView: ViewMode; + diffMode: DiffModeId; + enabled: boolean; + errorPatterns: ErrorPattern[]; + lazyLoadOffset: number | null; metaInfoBaseUrls: Record; path: string; plugins: PluginDescription[]; pluginsEnabled: boolean; - yandexMetrika: { counterNumber: null | number }; saveErrorDetails: boolean; + saveFormat: SaveFormat; + yandexMetrika: { counterNumber: null | number }; } +export type ReporterOptions = Omit & {errorPatterns: (string | ErrorPattern)[]}; + export interface DbUrlsJsonData { dbUrls: string[]; jsonUrls: string[]; diff --git a/test/unit/lib/config/index.js b/test/unit/lib/config/index.js index ab071dac2..17370f3b9 100644 --- a/test/unit/lib/config/index.js +++ b/test/unit/lib/config/index.js @@ -1,7 +1,7 @@ 'use strict'; const {isEmpty} = require('lodash'); -const parseConfig = require('lib/config'); +const {parseConfig} = require('lib/config'); const {configDefaults} = require('lib/constants/defaults'); const {ViewMode} = require('lib/constants/view-modes'); const {DiffModes} = require('lib/constants/diff-modes'); diff --git a/test/unit/lib/plugin-adapter.js b/test/unit/lib/plugin-adapter.js index 0e0666d80..660586815 100644 --- a/test/unit/lib/plugin-adapter.js +++ b/test/unit/lib/plugin-adapter.js @@ -76,7 +76,7 @@ describe('lib/plugin-adapter', () => { cliCommands[REMOVE_UNUSED_SCREENS] = sandbox.stub(); toolReporter = proxyquire('lib/plugin-adapter', { - './config': parseConfig, + './config': {parseConfig}, [`./cli-commands/${GUI}`]: cliCommands[GUI], [`./cli-commands/${MERGE_REPORTS}`]: cliCommands[MERGE_REPORTS], [`./cli-commands/${REMOVE_UNUSED_SCREENS}`]: cliCommands[REMOVE_UNUSED_SCREENS] From 9b2171586f0b8d49484cb67baab38ad4a067425e Mon Sep 17 00:00:00 2001 From: shadowusr Date: Tue, 22 Aug 2023 00:54:18 +0300 Subject: [PATCH 23/25] chore: re-write plugin-adapter to typescript --- hermione.js | 2 +- lib/{plugin-adapter.js => plugin-adapter.ts} | 59 +++++++++++--------- test/unit/hermione.js | 4 +- test/unit/lib/plugin-adapter.js | 2 +- 4 files changed, 38 insertions(+), 29 deletions(-) rename lib/{plugin-adapter.js => plugin-adapter.ts} (51%) diff --git a/hermione.js b/hermione.js index 00e1d6bee..0d9b70d6a 100644 --- a/hermione.js +++ b/hermione.js @@ -2,7 +2,7 @@ const os = require('os'); const PQueue = require('p-queue'); -const PluginAdapter = require('./lib/plugin-adapter'); +const {PluginAdapter} = require('./lib/plugin-adapter'); const {createWorkers} = require('./lib/workers/create-workers'); let workers; diff --git a/lib/plugin-adapter.js b/lib/plugin-adapter.ts similarity index 51% rename from lib/plugin-adapter.js rename to lib/plugin-adapter.ts index 08200fdf6..dfe710872 100644 --- a/lib/plugin-adapter.js +++ b/lib/plugin-adapter.ts @@ -1,51 +1,60 @@ -'use strict'; - -const _ = require('lodash'); -const Promise = require('bluebird'); -const {parseConfig} = require('./config'); -const {StaticReportBuilder} = require('./report-builder/static'); -const utils = require('./server-utils'); -const {cliCommands} = require('./cli-commands'); -const {HtmlReporter} = require('./plugin-api'); - -module.exports = class PluginAdapter { - static create(hermione, opts) { +import _ from 'lodash'; +import type Hermione from 'hermione'; +import {CommanderStatic} from '@gemini-testing/commander'; + +import {parseConfig} from './config'; +import {StaticReportBuilder} from './report-builder/static'; +import * as utils from './server-utils'; +import {cliCommands} from './cli-commands'; +import {HtmlReporter} from './plugin-api'; +import {HtmlReporterApi, ReporterConfig, ReporterOptions} from './types'; + +type PrepareFn = (hermione: Hermione & HtmlReporterApi, reportBuilder: StaticReportBuilder, config: ReporterConfig) => Promise; + +export class PluginAdapter { + protected _hermione: Hermione & HtmlReporterApi; + protected _config: ReporterConfig; + + static create( + this: new (hermione: Hermione & HtmlReporterApi, opts: Partial) => T, + hermione: Hermione & HtmlReporterApi, + opts: Partial + ): T { return new this(hermione, opts); } - constructor(hermione, opts) { + constructor(hermione: Hermione & HtmlReporterApi, opts: Partial) { this._hermione = hermione; this._config = parseConfig(opts); } - isEnabled() { + isEnabled(): boolean { return this._config.enabled; } - addApi() { + addApi(): this { this._hermione.htmlReporter = HtmlReporter.create(this._config); - return this; } - addCliCommands() { - _.values(cliCommands).forEach((command) => { - this._hermione.on(this._hermione.events.CLI, (commander) => { + addCliCommands(): this { + _.values(cliCommands).forEach((command: string) => { + this._hermione.on(this._hermione.events.CLI, (commander: CommanderStatic) => { + // eslint-disable-next-line @typescript-eslint/no-var-requires require(`./cli-commands/${command}`)(commander, this._config, this._hermione); - commander.prependListener(`command:${command}`, () => this._run = _.noop); + commander.prependListener(`command:${command}`, () => this._run = _.noop as typeof this._run); }); }); return this; } - init(prepareData) { + init(prepareData: PrepareFn): this { this._hermione.on(this._hermione.events.INIT, () => this._run(prepareData)); - return this; } - async _createStaticReportBuilder(prepareData) { + protected async _createStaticReportBuilder(prepareData: PrepareFn): Promise { const staticReportBuilder = StaticReportBuilder.create(this._hermione, this._config); await staticReportBuilder.init(); @@ -63,11 +72,11 @@ module.exports = class PluginAdapter { }); } - async _run(prepareData) { + protected async _run(prepareData: PrepareFn): Promise { const generateReport = this._createStaticReportBuilder(prepareData); this._hermione.on(this._hermione.events.RUNNER_END, () => generateReport.then(() => utils.logPathToHtmlReport(this._config)).catch(utils.logError) ); } -}; +} diff --git a/test/unit/hermione.js b/test/unit/hermione.js index 74af516f3..409013fc8 100644 --- a/test/unit/hermione.js +++ b/test/unit/hermione.js @@ -42,7 +42,7 @@ describe('lib/hermione', () => { '../test-adapter': {TestAdapter} }); - const PluginAdapter = proxyquire('lib/plugin-adapter', { + const {PluginAdapter} = proxyquire('lib/plugin-adapter', { './server-utils': utils, './report-builder/static': {StaticReportBuilder}, './plugin-api': proxyquire('lib/plugin-api', { @@ -53,7 +53,7 @@ describe('lib/hermione', () => { }); const HermioneReporter = proxyquire('../../hermione', { - './lib/plugin-adapter': PluginAdapter + './lib/plugin-adapter': {PluginAdapter} }); const events = { diff --git a/test/unit/lib/plugin-adapter.js b/test/unit/lib/plugin-adapter.js index 660586815..a2dd57201 100644 --- a/test/unit/lib/plugin-adapter.js +++ b/test/unit/lib/plugin-adapter.js @@ -80,7 +80,7 @@ describe('lib/plugin-adapter', () => { [`./cli-commands/${GUI}`]: cliCommands[GUI], [`./cli-commands/${MERGE_REPORTS}`]: cliCommands[MERGE_REPORTS], [`./cli-commands/${REMOVE_UNUSED_SCREENS}`]: cliCommands[REMOVE_UNUSED_SCREENS] - }); + }).PluginAdapter; }); afterEach(() => sandbox.restore()); From a2ea259b9084ee744abc4ddacdc41b190871ac66 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Tue, 22 Aug 2023 09:29:17 +0300 Subject: [PATCH 24/25] chore: re-write suite-adapter to typescript --- lib/report-builder/static.ts | 2 +- lib/suite-adapter.js | 71 ---------------------------------- lib/suite-adapter.ts | 70 +++++++++++++++++++++++++++++++++ lib/test-adapter.ts | 2 +- lib/types.ts | 15 ++++--- package-lock.json | 17 +++++++- package.json | 1 + test/unit/lib/suite-adapter.js | 2 +- 8 files changed, 99 insertions(+), 81 deletions(-) delete mode 100644 lib/suite-adapter.js create mode 100644 lib/suite-adapter.ts diff --git a/lib/report-builder/static.ts b/lib/report-builder/static.ts index acb5a5042..388c4bbb9 100644 --- a/lib/report-builder/static.ts +++ b/lib/report-builder/static.ts @@ -130,7 +130,7 @@ export class StaticReportBuilder { } = result; const {baseHost, saveErrorDetails} = this._pluginConfig; - const suiteUrl: string = suite.getUrl({browserId, baseHost}); + const suiteUrl: string = suite.getUrl({baseHost}); const metaInfo = _.merge(_.cloneDeep(result.meta), {url: suite.fullUrl, file: suite.file, sessionId}); const testResult: PreparedTestResult = Object.assign({ diff --git a/lib/suite-adapter.js b/lib/suite-adapter.js deleted file mode 100644 index fb386b170..000000000 --- a/lib/suite-adapter.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -const _ = require('lodash'); -const path = require('path'); -const url = require('url'); -const Uri = require('urijs'); - -const {getSuitePath} = require('./plugin-utils'); - -const wrapLinkByTag = (text) => { - return text.replace(/https?:\/\/[^\s]*/g, (url) => { - return `${url}`; - }); -}; - -function getSkipComment(suite) { - return suite.skipReason || suite.parent && getSkipComment(suite.parent); -} - -module.exports = class SuiteAdapter { - static create(suite = {}, config = {}) { - return new this(suite, config); - } - - constructor(suite, config) { - this._suite = suite; - this._config = config; - } - - _wrapSkipComment(skipComment) { - return skipComment ? wrapLinkByTag(skipComment) : 'Unknown reason'; - } - - _configureUrl(url, baseHost) { - return _.isEmpty(baseHost) - ? url - : Uri(baseHost).resource(url).href(); - } - - get skipComment() { - const skipComment = getSkipComment(this._suite); - - return this._wrapSkipComment(skipComment); - } - - get fullName() { - return this._suite.fullTitle(); - } - - get path() { - return getSuitePath(this._suite.parent); - } - - get file() { - return path.relative(process.cwd(), this._suite.file); - } - - getUrl(opts = {}) { - const url = _.get(this, '_suite.meta.url', ''); - - return this._configureUrl(url, opts.baseHost); - } - - get fullUrl() { - const baseUrl = this.getUrl(); - - return baseUrl - ? url.parse(baseUrl).path - : ''; - } -}; diff --git a/lib/suite-adapter.ts b/lib/suite-adapter.ts new file mode 100644 index 000000000..f7feb2e81 --- /dev/null +++ b/lib/suite-adapter.ts @@ -0,0 +1,70 @@ +import _ from 'lodash'; +import path from 'path'; +import url from 'url'; +import Uri from 'urijs'; +import {getSuitePath} from './plugin-utils'; +import {Suite, TestResult} from './types'; + +const wrapLinkByTag = (text: string): string => { + return text.replace(/https?:\/\/[^\s]*/g, (url) => { + return `${url}`; + }); +}; + +function getSkipComment(suite: TestResult | Suite): string | null | undefined { + return suite.skipReason || suite.parent && getSkipComment(suite.parent); +} + +export class SuiteAdapter { + protected _suite: TestResult; + + static create(suite: TestResult): SuiteAdapter { + return new this(suite); + } + + constructor(suite: TestResult) { + this._suite = suite; + } + + protected _wrapSkipComment(skipComment: string | null | undefined): string { + return skipComment ? wrapLinkByTag(skipComment) : 'Unknown reason'; + } + + protected _configureUrl(url: string, baseHost: string): string { + return _.isEmpty(baseHost) + ? url + : Uri(baseHost).resource(url).href(); + } + + get skipComment(): string { + const skipComment = getSkipComment(this._suite); + + return this._wrapSkipComment(skipComment); + } + + get fullName(): string { + return this._suite.fullTitle(); + } + + get path(): string[] { + return getSuitePath(this._suite.parent); + } + + get file(): string { + return path.relative(process.cwd(), this._suite.file); + } + + getUrl(opts: { baseHost?: string } = {}): string { + const url = _.get(this, '_suite.meta.url', '') as string; + + return this._configureUrl(url, opts.baseHost || ''); + } + + get fullUrl(): string { + const baseUrl = this.getUrl(); + + return baseUrl + ? url.parse(baseUrl).path || '' + : ''; + } +} diff --git a/lib/test-adapter.ts b/lib/test-adapter.ts index c675b9c4e..ccda49982 100644 --- a/lib/test-adapter.ts +++ b/lib/test-adapter.ts @@ -5,7 +5,7 @@ import tmp from 'tmp'; import crypto from 'crypto'; import type {default as Hermione} from 'hermione'; -import SuiteAdapter from './suite-adapter'; +import {SuiteAdapter} from './suite-adapter'; import {DB_COLUMNS} from './constants/database'; import {getSuitePath} from './plugin-utils'; import {getCommandsHistory} from './history-utils'; diff --git a/lib/types.ts b/lib/types.ts index faa791fab..53f3ce38a 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -7,7 +7,15 @@ declare module 'tmp' { export const tmpdir: string; } -export interface Suite { +interface ConfigurableTestObject { + browserId: string; + browserVersion?: string; + id: string; + file: string; + skipReason: string; +} + +export interface Suite extends ConfigurableTestObject { readonly root: boolean; readonly title: string; parent: Suite | null; @@ -93,7 +101,7 @@ export interface ImageDiffError { export type AssertViewResult = ImageDiffError; -export interface TestResult { +export interface TestResult extends ConfigurableTestObject { assertViewResults: AssertViewResult[]; description?: string; err?: { @@ -103,11 +111,8 @@ export interface TestResult { details: ErrorDetails }; fullTitle(): string; - id: string; title: string; meta: Record - browserId: string; - browserVersion?: string; sessionId: string; timestamp: number; imagesInfo: ImageInfoFull[]; diff --git a/package-lock.json b/package-lock.json index 4a6e2c62c..0d3a60c24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "html-reporter", - "version": "9.10.3", + "version": "9.10.3-hello", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "html-reporter", - "version": "9.10.3", + "version": "9.10.3-hello", "license": "MIT", "dependencies": { "@babel/runtime": "^7.22.5", @@ -54,6 +54,7 @@ "@types/nested-error-stacks": "^2.1.0", "@types/opener": "^1.4.0", "@types/tmp": "^0.1.0", + "@types/urijs": "^1.19.19", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "app-module-path": "^2.2.0", @@ -4623,6 +4624,12 @@ "node": ">=0.10.0" } }, + "node_modules/@types/urijs": { + "version": "1.19.19", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz", + "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==", + "dev": true + }, "node_modules/@types/webpack": { "version": "4.41.33", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz", @@ -35371,6 +35378,12 @@ } } }, + "@types/urijs": { + "version": "1.19.19", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.19.tgz", + "integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==", + "dev": true + }, "@types/webpack": { "version": "4.41.33", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.33.tgz", diff --git a/package.json b/package.json index 94a6e52ff..eca5127bb 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "@types/nested-error-stacks": "^2.1.0", "@types/opener": "^1.4.0", "@types/tmp": "^0.1.0", + "@types/urijs": "^1.19.19", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "app-module-path": "^2.2.0", diff --git a/test/unit/lib/suite-adapter.js b/test/unit/lib/suite-adapter.js index a5843b6b8..0b1138dab 100644 --- a/test/unit/lib/suite-adapter.js +++ b/test/unit/lib/suite-adapter.js @@ -1,6 +1,6 @@ 'use strict'; -const SuiteAdapter = require('lib/suite-adapter'); +const {SuiteAdapter} = require('lib/suite-adapter'); describe('suite adapter', () => { it('should return suite skip reason', () => { From 6d04d3ebbf07b356e945fc2890aeab21fa436b02 Mon Sep 17 00:00:00 2001 From: shadowusr Date: Tue, 22 Aug 2023 09:56:02 +0300 Subject: [PATCH 25/25] chore: fix cliCommands imports --- lib/cli-commands/gui.js | 4 +++- lib/cli-commands/merge-reports.js | 4 +++- lib/cli-commands/remove-unused-screens/index.js | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/cli-commands/gui.js b/lib/cli-commands/gui.js index f10f74091..155489e5b 100644 --- a/lib/cli-commands/gui.js +++ b/lib/cli-commands/gui.js @@ -1,8 +1,10 @@ 'use strict'; +const {cliCommands} = require('.'); const runGui = require('../gui').default; const Api = require('../gui/api'); -const {GUI: commandName} = require('./'); + +const {GUI: commandName} = cliCommands; module.exports = (program, pluginConfig, hermione) => { // must be executed here because it adds `gui` field in `gemini` and `hermione tool`, diff --git a/lib/cli-commands/merge-reports.js b/lib/cli-commands/merge-reports.js index 3701478c1..d7349a2b4 100644 --- a/lib/cli-commands/merge-reports.js +++ b/lib/cli-commands/merge-reports.js @@ -1,9 +1,11 @@ 'use strict'; -const {MERGE_REPORTS: commandName} = require('./'); +const {cliCommands} = require('.'); const mergeReports = require('../merge-reports'); const {logError} = require('../server-utils'); +const {MERGE_REPORTS: commandName} = cliCommands; + module.exports = (program, pluginConfig, hermione) => { program .command(`${commandName} [paths...]`) diff --git a/lib/cli-commands/remove-unused-screens/index.js b/lib/cli-commands/remove-unused-screens/index.js index b564699b3..9f5186b4e 100644 --- a/lib/cli-commands/remove-unused-screens/index.js +++ b/lib/cli-commands/remove-unused-screens/index.js @@ -8,11 +8,13 @@ const chalk = require('chalk'); const filesize = require('filesize'); const Promise = require('bluebird'); -const {REMOVE_UNUSED_SCREENS: commandName} = require('..'); +const {cliCommands} = require('..'); const {getTestsFromFs, findScreens, askQuestion, identifyOutdatedScreens, identifyUnusedScreens, removeScreens} = require('./utils'); const {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} = require('../../constants/database'); const {logger} = require('../../common-utils'); +const {REMOVE_UNUSED_SCREENS: commandName} = cliCommands; + // TODO: remove hack after add ability to add controllers from plugin in silent mode function proxyHermione() { const proxyHandler = {