From 6d0a7b3ba02da6544584aaccf3d40a2001c50ba1 Mon Sep 17 00:00:00 2001 From: shadowusr <58862284+shadowusr@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:30:10 +0300 Subject: [PATCH] feat: re-write the rest of the GUI logic to typescript (#520) * chore: rename tool-runner/utils to typescript * chore: rewrite tool-runner/utils to typescript * chore: rewrite reporter-helpers to typescript * chore: rename tool-runner/index to typescript * chore: re-write tool-runner to typescript * test: fix build and e2e tests * fix: use ensure props instead of assertion * chore: use node 18 for development * test: fix unit tests --- .nvmrc | 2 +- lib/gui/app.js | 2 +- lib/gui/index.ts | 20 +- lib/gui/tool-runner/index.js | 295 ------------ lib/gui/tool-runner/index.ts | 381 +++++++++++++++ lib/gui/tool-runner/runner/all-test-runner.ts | 3 +- lib/gui/tool-runner/runner/index.ts | 3 +- lib/gui/tool-runner/runner/runner.ts | 5 +- .../runner/specific-test-runner.ts | 3 +- lib/gui/tool-runner/utils.js | 112 ----- lib/gui/tool-runner/utils.ts | 115 +++++ lib/image-handler.ts | 9 +- lib/report-builder/gui.ts | 2 +- lib/reporter-helpers.js | 43 -- lib/reporter-helpers.ts | 52 ++ package-lock.json | 455 +++++++++++++----- package.json | 4 +- test/func/fixtures/hermione-eye/package.json | 2 +- test/func/fixtures/hermione-gui/package.json | 2 +- test/func/fixtures/hermione/package.json | 2 +- test/func/fixtures/plugins/package.json | 2 +- test/unit/lib/gui/app.js | 2 +- test/unit/lib/gui/tool-runner/index.js | 4 +- webpack.common.js | 2 + 24 files changed, 915 insertions(+), 607 deletions(-) delete mode 100644 lib/gui/tool-runner/index.js create mode 100644 lib/gui/tool-runner/index.ts delete mode 100644 lib/gui/tool-runner/utils.js create mode 100644 lib/gui/tool-runner/utils.ts delete mode 100644 lib/reporter-helpers.js create mode 100644 lib/reporter-helpers.ts diff --git a/.nvmrc b/.nvmrc index b6a7d89c6..3c032078a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16 +18 diff --git a/lib/gui/app.js b/lib/gui/app.js index 427b17e38..ae546c869 100644 --- a/lib/gui/app.js +++ b/lib/gui/app.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -const ToolRunner = require('./tool-runner'); +const {ToolRunner} = require('./tool-runner'); module.exports = class App { static create(paths, hermione, configs) { diff --git a/lib/gui/index.ts b/lib/gui/index.ts index f3938212a..31ab42468 100644 --- a/lib/gui/index.ts +++ b/lib/gui/index.ts @@ -1,20 +1,30 @@ +import type {CommanderStatic} from '@gemini-testing/commander'; import chalk from 'chalk'; import opener from 'opener'; + import server from './server'; import {logger} from '../common-utils'; import * as utils from '../server-utils'; +import {ReporterConfig} from '../types'; const {logError} = utils; +export interface GuiCliOptions { + autoRun: boolean; + open: unknown; +} + +export interface GuiConfigs { + options: GuiCliOptions; + program: CommanderStatic; + pluginConfig: ReporterConfig; +} + interface ServerArgs { paths: string[]; hermione: unknown; guiApi: unknown; - configs: { - options: { - open: unknown, - } - }; + configs: GuiConfigs; } export default (args: ServerArgs): void => { diff --git a/lib/gui/tool-runner/index.js b/lib/gui/tool-runner/index.js deleted file mode 100644 index 9b7a6b98f..000000000 --- a/lib/gui/tool-runner/index.js +++ /dev/null @@ -1,295 +0,0 @@ -'use strict'; - -const _ = require('lodash'); -const fs = require('fs-extra'); -const path = require('path'); -const chalk = require('chalk'); -const Promise = require('bluebird'); -const looksSame = require('looks-same'); - -const {createTestRunner} = require('./runner'); -const {subscribeOnToolEvents} = require('./report-subscriber'); -const {GuiReportBuilder} = require('../../report-builder/gui'); -const {EventSource} = require('../event-source'); -const {logger} = require('../../common-utils'); -const reporterHelper = require('../../reporter-helpers'); -const {UPDATED, SKIPPED, IDLE} = require('../../constants/test-statuses'); -const {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} = require('../../constants/database'); -const {getShortMD5} = require('../../common-utils'); -const {formatId, mkFullTitle, mergeDatabasesForReuse, filterByEqualDiffSizes} = require('./utils'); -const {getTestsTreeFromDatabase} = require('../../db-utils/server'); -const {formatTestResult} = require('../../server-utils'); -const {ToolName} = require('../../constants'); - -module.exports = class ToolRunner { - static create(paths, hermione, configs) { - return new this(paths, hermione, configs); - } - - constructor(paths, hermione, {program: globalOpts, pluginConfig, options: guiOpts}) { - this._testFiles = [].concat(paths); - this._hermione = hermione; - this._tree = null; - this._collection = null; - - this._globalOpts = globalOpts; - this._guiOpts = guiOpts; - this._reportPath = pluginConfig.path; - this._pluginConfig = pluginConfig; - - this._eventSource = new EventSource(); - this._reportBuilder = null; - - this._tests = {}; - } - - get config() { - return this._hermione.config; - } - - get tree() { - return this._tree; - } - - async initialize() { - await mergeDatabasesForReuse(this._reportPath); - - this._reportBuilder = GuiReportBuilder.create(this._hermione.htmlReporter, this._pluginConfig, {reuse: true}); - this._subscribeOnEvents(); - - this._collection = await this._readTests(); - - await this._reportBuilder.init(); - await this._reportBuilder.saveStaticFiles(); - - this._reportBuilder.setApiValues(this._hermione.htmlReporter.values); - await this._handleRunnableCollection(); - } - - async _readTests() { - const {grep, set: sets, browser: browsers} = this._globalOpts; - - return await this._hermione.readTests(this._testFiles, {grep, sets, browsers}); - } - - finalize() { - return this._reportBuilder.finalize(); - } - - addClient(connection) { - this._eventSource.addConnection(connection); - } - - sendClientEvent(event, data) { - this._eventSource.emit(event, data); - } - - getTestsDataToUpdateRefs(imageIds) { - return this._reportBuilder.getTestsDataToUpdateRefs(imageIds); - } - - getImageDataToFindEqualDiffs(imageIds) { - const [selectedImage, ...comparedImages] = this._reportBuilder.getImageDataToFindEqualDiffs(imageIds); - - const imagesWithEqualBrowserName = comparedImages.filter((image) => image.browserName === selectedImage.browserName); - const imagesWithEqualDiffSizes = filterByEqualDiffSizes(imagesWithEqualBrowserName, selectedImage.diffClusters); - - return _.isEmpty(imagesWithEqualDiffSizes) ? [] : [selectedImage].concat(imagesWithEqualDiffSizes); - } - - updateReferenceImage(tests) { - return Promise.map(tests, (test) => { - const updateResult = this._prepareTestResult(test); - const formattedResult = formatTestResult(updateResult, UPDATED, this._reportBuilder); - const failResultId = formattedResult.id; - const updateAttempt = this._reportBuilder.getUpdatedAttempt(formattedResult); - - formattedResult.attempt = updateAttempt; - updateResult.attempt = updateAttempt; - - return Promise.map(updateResult.imagesInfo, (imageInfo) => { - const {stateName} = imageInfo; - - return reporterHelper.updateReferenceImage(formattedResult, this._reportPath, stateName) - .then(() => { - const result = _.extend(updateResult, {refImg: imageInfo.expectedImg}); - this._emitUpdateReference(result, stateName); - }); - }) - .then(() => { - this._reportBuilder.addUpdated(formatTestResult(updateResult, UPDATED, this._reportBuilder), failResultId); - return this._reportBuilder.getTestBranch(formattedResult.id); - }); - }); - } - - async undoAcceptImages(tests) { - const updatedImages = [], removedResults = []; - - await Promise.map(tests, async (test) => { - const updateResult = this._prepareTestResult(test); - const formattedResult = formatTestResult(updateResult, UPDATED, this._reportBuilder); - - formattedResult.attempt = updateResult.attempt; - - await Promise.map(updateResult.imagesInfo, async (imageInfo) => { - const {stateName} = imageInfo; - const { - updatedImage, - removedResult, - previousExpectedPath, - shouldRemoveReference, - shouldRevertReference - } = await this._reportBuilder.undoAcceptImage(formattedResult, stateName); - - updatedImage && updatedImages.push(updatedImage); - removedResult && removedResults.push(removedResult); - - if (shouldRemoveReference) { - await reporterHelper.removeReferenceImage(formattedResult, stateName); - } - - if (shouldRevertReference) { - await reporterHelper.revertReferenceImage(removedResult, formattedResult, stateName); - } - - if (previousExpectedPath) { - this._reportBuilder.imageHandler.updateCacheExpectedPath(updateResult, stateName, previousExpectedPath); - } - }); - }); - - return {updatedImages, removedResults}; - } - - async findEqualDiffs(images) { - const [selectedImage, ...comparedImages] = images; - const {tolerance, antialiasingTolerance} = this.config; - const compareOpts = {tolerance, antialiasingTolerance, stopOnFirstFail: true, shouldCluster: false}; - const equalImages = await Promise.filter(comparedImages, async (image) => { - try { - await Promise.mapSeries(image.diffClusters, async (diffCluster, i) => { - const refComparisonRes = await looksSame( - {source: this._resolveImgPath(selectedImage.expectedImg.path), boundingBox: selectedImage.diffClusters[i]}, - {source: this._resolveImgPath(image.expectedImg.path), boundingBox: diffCluster}, - compareOpts - ); - - if (!refComparisonRes.equal) { - return Promise.reject(false); - } - - const actComparisonRes = await looksSame( - {source: this._resolveImgPath(selectedImage.actualImg.path), boundingBox: selectedImage.diffClusters[i]}, - {source: this._resolveImgPath(image.actualImg.path), boundingBox: diffCluster}, - compareOpts - ); - - if (!actComparisonRes.equal) { - return Promise.reject(false); - } - }); - - return true; - } catch (err) { - return err === false ? err : Promise.reject(err); - } - }); - - return equalImages.map((image) => image.id); - } - - run(tests = []) { - const {grep, set: sets, browser: browsers} = this._globalOpts; - - return createTestRunner(this._collection, tests) - .run((collection) => this._hermione.run(collection, {grep, sets, browsers})); - } - - async _handleRunnableCollection() { - this._collection.eachTest((test, browserId) => { - if (test.disabled || this._isSilentlySkipped(test)) { - return; - } - - // TODO: remove toString after publish major version - const testId = formatId(test.id.toString(), browserId); - this._tests[testId] = _.extend(test, {browserId}); - - test.pending - ? this._reportBuilder.addSkipped(formatTestResult(test, SKIPPED, this._reportBuilder)) - : this._reportBuilder.addIdle(formatTestResult(test, IDLE, this._reportBuilder)); - }); - - await this._fillTestsTree(); - } - - _isSilentlySkipped({silentSkip, parent}) { - return silentSkip || parent && this._isSilentlySkipped(parent); - } - - _subscribeOnEvents() { - subscribeOnToolEvents(this._hermione, this._reportBuilder, this._eventSource, this._reportPath); - } - - _prepareTestResult(test) { - const {browserId, attempt} = test; - const fullTitle = mkFullTitle(test); - const testId = formatId(getShortMD5(fullTitle), browserId); - const rawTest = this._tests[testId]; - const {sessionId, url} = test.metaInfo; - const assertViewResults = []; - - const imagesInfo = test.imagesInfo.map((imageInfo) => { - const {stateName, actualImg} = imageInfo; - const path = this._hermione.config.browsers[browserId].getScreenshotPath(rawTest, stateName); - const refImg = {path, size: actualImg.size}; - - assertViewResults.push({stateName, refImg, currImg: actualImg}); - - return _.extend(imageInfo, {expectedImg: refImg}); - }); - - const res = _.merge({}, rawTest, {assertViewResults, imagesInfo, sessionId, attempt, meta: {url}, updated: true}); - - // _.merge can't fully clone test object since hermione@7+ - // TODO: use separate object to represent test results. Do not extend test object with test results - return rawTest && rawTest.clone - ? Object.assign(rawTest.clone(), res) - : res; - } - - _emitUpdateReference({refImg}, state) { - this._hermione.emit( - this._hermione.events.UPDATE_REFERENCE, - {refImg, state} - ); - } - - async _fillTestsTree() { - const {autoRun} = this._guiOpts; - const testsTree = await this._loadDataFromDatabase(); - - if (!_.isEmpty(testsTree)) { - this._reportBuilder.reuseTestsTree(testsTree); - } - - this._tree = {...this._reportBuilder.getResult(), autoRun}; - } - - async _loadDataFromDatabase() { - const dbPath = path.resolve(this._reportPath, LOCAL_DATABASE_NAME); - - if (await fs.pathExists(dbPath)) { - return getTestsTreeFromDatabase(ToolName.Hermione, dbPath); - } - - logger.warn(chalk.yellow(`Nothing to reuse in ${this._reportPath}: can not load data from ${DATABASE_URLS_JSON_NAME}`)); - - return {}; - } - - _resolveImgPath(imgPath) { - return path.resolve(process.cwd(), this._pluginConfig.path, imgPath); - } -}; diff --git a/lib/gui/tool-runner/index.ts b/lib/gui/tool-runner/index.ts new file mode 100644 index 000000000..1b0d5756f --- /dev/null +++ b/lib/gui/tool-runner/index.ts @@ -0,0 +1,381 @@ +import path from 'path'; + +import {CommanderStatic} from '@gemini-testing/commander'; +import chalk from 'chalk'; +import fs from 'fs-extra'; +import type Hermione from 'hermione'; +import type {TestCollection, Test as HermioneTest, Config as HermioneConfig} from 'hermione'; +import _ from 'lodash'; +import looksSame, {CoordBounds} from 'looks-same'; + +import {createTestRunner} from './runner'; +import {subscribeOnToolEvents} from './report-subscriber'; +import {GuiReportBuilder, GuiReportBuilderResult} from '../../report-builder/gui'; +import {EventSource} from '../event-source'; +import {logger, getShortMD5} from '../../common-utils'; +import * as reporterHelper from '../../reporter-helpers'; +import {UPDATED, SKIPPED, IDLE, TestStatus, ToolName, DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} from '../../constants'; +import {formatId, mkFullTitle, mergeDatabasesForReuse, filterByEqualDiffSizes} from './utils'; +import {getTestsTreeFromDatabase} from '../../db-utils/server'; +import {formatTestResult} from '../../server-utils'; +import { + AssertViewResult, + HermioneTestResult, + HtmlReporterApi, + ImageData, + ImageInfoFail, + ReporterConfig +} from '../../types'; +import {GuiCliOptions, GuiConfigs} from '../index'; +import {Tree, TreeImage} from '../../tests-tree-builder/base'; +import {TestSpec} from './runner/runner'; +import {Response} from 'express'; +import {TestBranch, TestEqualDiffsData, TestRefUpdateData} from '../../tests-tree-builder/gui'; +import {ReporterTestResult} from '../../test-adapter'; +import {ImagesInfoFormatter} from '../../image-handler'; + +type ToolRunnerArgs = [paths: string[], hermione: Hermione & HtmlReporterApi, configs: GuiConfigs]; + +type ToolRunnerTree = GuiReportBuilderResult & Pick; + +interface HermioneTestExtended extends HermioneTest { + assertViewResults: {stateName: string, refImg: ImageData, currImg: ImageData}; + attempt: number; + imagesInfo: Pick[]; +} + +type HermioneTestPlain = Pick; + +export interface UndoAcceptImagesResult { + updatedImages: TreeImage[]; + removedResults: string[]; +} + +// TODO: get rid of this function. It allows to format raw test, but is type-unsafe. +const formatTestResultUnsafe = (test: HermioneTest | HermioneTestExtended | HermioneTestPlain, status: TestStatus, {imageHandler}: {imageHandler: ImagesInfoFormatter}): ReporterTestResult => { + return formatTestResult(test as HermioneTestResult, status, {imageHandler}); +}; + +export class ToolRunner { + private _testFiles: string[]; + private _hermione: Hermione & HtmlReporterApi; + private _tree: ToolRunnerTree | null; + protected _collection: TestCollection | null; + private _globalOpts: CommanderStatic; + private _guiOpts: GuiCliOptions; + private _reportPath: string; + private _pluginConfig: ReporterConfig; + private _eventSource: EventSource; + protected _reportBuilder: GuiReportBuilder | null; + private _tests: Record; + + static create(this: new (...args: ToolRunnerArgs) => T, ...args: ToolRunnerArgs): T { + return new this(...args); + } + + constructor(...[paths, hermione, {program: globalOpts, pluginConfig, options: guiOpts}]: ToolRunnerArgs) { + this._testFiles = ([] as string[]).concat(paths); + this._hermione = hermione; + this._tree = null; + this._collection = null; + + this._globalOpts = globalOpts; + this._guiOpts = guiOpts; + this._reportPath = pluginConfig.path; + this._pluginConfig = pluginConfig; + + this._eventSource = new EventSource(); + this._reportBuilder = null; + + this._tests = {}; + } + + get config(): HermioneConfig { + return this._hermione.config; + } + + get tree(): ToolRunnerTree | null { + return this._tree; + } + + async initialize(): Promise { + await mergeDatabasesForReuse(this._reportPath); + + this._reportBuilder = GuiReportBuilder.create(this._hermione.htmlReporter, this._pluginConfig, {reuse: true}); + this._subscribeOnEvents(); + + this._collection = await this._readTests(); + + await this._reportBuilder.init(); + await this._reportBuilder.saveStaticFiles(); + + this._reportBuilder.setApiValues(this._hermione.htmlReporter.values); + await this._handleRunnableCollection(); + } + + async _readTests(): Promise { + const {grep, set: sets, browser: browsers} = this._globalOpts; + + return this._hermione.readTests(this._testFiles, {grep, sets, browsers}); + } + + protected _ensureReportBuilder(): GuiReportBuilder { + if (!this._reportBuilder) { + throw new Error('ToolRunner has to be initialized before usage'); + } + + return this._reportBuilder; + } + + protected _ensureTestCollection(): TestCollection { + if (!this._collection) { + throw new Error('ToolRunner has to be initialized before usage'); + } + + return this._collection; + } + + async finalize(): Promise { + return this._ensureReportBuilder().finalize(); + } + + addClient(connection: Response): void { + this._eventSource.addConnection(connection); + } + + sendClientEvent(event: string, data: unknown): void { + this._eventSource.emit(event, data); + } + + getTestsDataToUpdateRefs(imageIds: string[]): TestRefUpdateData[] { + return this._ensureReportBuilder().getTestsDataToUpdateRefs(imageIds); + } + + getImageDataToFindEqualDiffs(imageIds: string[]): TestEqualDiffsData[] { + const [selectedImage, ...comparedImages] = this._ensureReportBuilder().getImageDataToFindEqualDiffs(imageIds); + + const imagesWithEqualBrowserName = comparedImages.filter((image) => image.browserName === selectedImage.browserName); + const imagesWithEqualDiffSizes = filterByEqualDiffSizes(imagesWithEqualBrowserName, (selectedImage as ImageInfoFail).diffClusters); + + return _.isEmpty(imagesWithEqualDiffSizes) ? [] : [selectedImage].concat(imagesWithEqualDiffSizes); + } + + async updateReferenceImage(tests: TestRefUpdateData[]): Promise { + const reportBuilder = this._ensureReportBuilder(); + + return Promise.all(tests.map(async (test): Promise => { + const updateResult = this._prepareTestResult(test); + const formattedResult = formatTestResultUnsafe(updateResult, UPDATED, reportBuilder); + const failResultId = formattedResult.id; + const updateAttempt = reportBuilder.getUpdatedAttempt(formattedResult); + + formattedResult.attempt = updateAttempt; + updateResult.attempt = updateAttempt; + + await Promise.all(updateResult.imagesInfo.map(async (imageInfo) => { + const {stateName} = imageInfo; + + await reporterHelper.updateReferenceImage(formattedResult, this._reportPath, stateName); + + const result = _.extend(updateResult, {refImg: imageInfo.expectedImg}); + this._emitUpdateReference(result, stateName); + })); + + reportBuilder.addUpdated(formatTestResultUnsafe(updateResult, UPDATED, reportBuilder), failResultId); + + return reportBuilder.getTestBranch(formattedResult.id); + })); + } + + async undoAcceptImages(tests: TestRefUpdateData[]): Promise { + const updatedImages: TreeImage[] = [], removedResults: string[] = []; + const reportBuilder = this._ensureReportBuilder(); + + await Promise.all(tests.map(async (test) => { + const updateResult = this._prepareTestResult(test); + const formattedResult = formatTestResultUnsafe(updateResult, UPDATED, reportBuilder); + + formattedResult.attempt = updateResult.attempt; + + await Promise.all(updateResult.imagesInfo.map(async (imageInfo) => { + const {stateName} = imageInfo; + + const undoResultData = reportBuilder.undoAcceptImage(formattedResult, stateName); + if (undoResultData === null) { + return; + } + + const { + updatedImage, + removedResult, + previousExpectedPath, + shouldRemoveReference, + shouldRevertReference + } = undoResultData; + + updatedImage && updatedImages.push(updatedImage); + removedResult && removedResults.push(removedResult); + + if (shouldRemoveReference) { + await reporterHelper.removeReferenceImage(formattedResult, stateName); + } + + if (shouldRevertReference && removedResult) { + await reporterHelper.revertReferenceImage(removedResult, formattedResult, stateName); + } + + if (previousExpectedPath && (updateResult as HermioneTest).fullTitle) { + reportBuilder.imageHandler.updateCacheExpectedPath({ + fullName: (updateResult as HermioneTest).fullTitle(), + browserId: (updateResult as HermioneTest).browserId + }, stateName, previousExpectedPath); + } + })); + })); + + return {updatedImages, removedResults}; + } + + async findEqualDiffs(images: TestEqualDiffsData[]): Promise { + const [selectedImage, ...comparedImages] = images as (ImageInfoFail & {diffClusters: CoordBounds[]})[]; + const {tolerance, antialiasingTolerance} = this.config; + const compareOpts = {tolerance, antialiasingTolerance, stopOnFirstFail: true, shouldCluster: false}; + + const comparisons = await Promise.all(comparedImages.map(async (image) => { + for (let i = 0; i < image.diffClusters.length; i++) { + const diffCluster = image.diffClusters[i]; + + try { + const refComparisonRes = await looksSame( + {source: this._resolveImgPath(selectedImage.expectedImg.path), boundingBox: selectedImage.diffClusters[i]}, + {source: this._resolveImgPath(image.expectedImg.path), boundingBox: diffCluster}, + compareOpts + ); + + if (!refComparisonRes.equal) { + return false; + } + + const actComparisonRes = await looksSame( + {source: this._resolveImgPath(selectedImage.actualImg.path), boundingBox: selectedImage.diffClusters[i]}, + {source: this._resolveImgPath(image.actualImg.path), boundingBox: diffCluster}, + compareOpts + ); + + if (!actComparisonRes.equal) { + return false; + } + } catch (err) { + if (err !== false) { + throw err; + } + return false; + } + } + + return image; + })); + + return comparisons.filter(Boolean).map(image => (image as TestEqualDiffsData).id); + } + + async run(tests: TestSpec[] = []): Promise { + const {grep, set: sets, browser: browsers} = this._globalOpts; + + return createTestRunner(this._ensureTestCollection(), tests) + .run((collection) => this._hermione.run(collection, {grep, sets, browsers})); + } + + protected async _handleRunnableCollection(): Promise { + const reportBuilder = this._ensureReportBuilder(); + + this._ensureTestCollection().eachTest((test, browserId) => { + if (test.disabled || this._isSilentlySkipped(test)) { + return; + } + + // TODO: remove toString after publish major version + const testId = formatId(test.id.toString(), browserId); + this._tests[testId] = _.extend(test, {browserId}); + + test.pending + ? reportBuilder.addSkipped(formatTestResultUnsafe(test, SKIPPED, reportBuilder)) + : reportBuilder.addIdle(formatTestResultUnsafe(test, IDLE, reportBuilder)); + }); + + await this._fillTestsTree(); + } + + protected _isSilentlySkipped({silentSkip, parent}: HermioneTest): boolean { + return silentSkip || parent && this._isSilentlySkipped(parent); + } + + protected _subscribeOnEvents(): void { + subscribeOnToolEvents(this._hermione, this._ensureReportBuilder(), this._eventSource, this._reportPath); + } + + protected _prepareTestResult(test: TestRefUpdateData): HermioneTestExtended | HermioneTestPlain { + const {browserId, attempt} = test; + const fullTitle = mkFullTitle(test); + const testId = formatId(getShortMD5(fullTitle), browserId); + const rawTest = this._tests[testId]; + const {sessionId, url} = test.metaInfo; + const assertViewResults: AssertViewResult[] = []; + + const imagesInfo = test.imagesInfo + .filter(({stateName, actualImg}) => Boolean(stateName) && Boolean(actualImg)) + .map((imageInfo) => { + const {stateName, actualImg} = imageInfo as {stateName: string, actualImg: ImageData}; + const path = this._hermione.config.browsers[browserId].getScreenshotPath(rawTest, stateName); + const refImg = {path, size: actualImg.size}; + + assertViewResults.push({stateName, refImg, currImg: actualImg}); + + return _.extend(imageInfo, {expectedImg: refImg}); + }); + + const res = _.merge({}, rawTest, {assertViewResults, imagesInfo, sessionId, attempt, meta: {url}, updated: true}); + + // _.merge can't fully clone test object since hermione@7+ + // TODO: use separate object to represent test results. Do not extend test object with test results + return rawTest && rawTest.clone + ? Object.assign(rawTest.clone(), res) + : res; + } + + protected _emitUpdateReference({refImg}: {refImg: ImageData}, state: string): void { + this._hermione.emit( + this._hermione.events.UPDATE_REFERENCE, + {refImg, state} + ); + } + + async _fillTestsTree(): Promise { + const reportBuilder = this._ensureReportBuilder(); + + const {autoRun} = this._guiOpts; + const testsTree = await this._loadDataFromDatabase(); + + if (!_.isEmpty(testsTree)) { + reportBuilder.reuseTestsTree(testsTree); + } + + this._tree = {...reportBuilder.getResult(), autoRun}; + } + + protected async _loadDataFromDatabase(): Promise { + const dbPath = path.resolve(this._reportPath, LOCAL_DATABASE_NAME); + + if (await fs.pathExists(dbPath)) { + return getTestsTreeFromDatabase(ToolName.Hermione, dbPath); + } + + logger.warn(chalk.yellow(`Nothing to reuse in ${this._reportPath}: can not load data from ${DATABASE_URLS_JSON_NAME}`)); + + return null; + } + + protected _resolveImgPath(imgPath: string): string { + return path.resolve(process.cwd(), this._pluginConfig.path, imgPath); + } +} diff --git a/lib/gui/tool-runner/runner/all-test-runner.ts b/lib/gui/tool-runner/runner/all-test-runner.ts index f181eb145..4800dddfd 100644 --- a/lib/gui/tool-runner/runner/all-test-runner.ts +++ b/lib/gui/tool-runner/runner/all-test-runner.ts @@ -1,4 +1,5 @@ -import {BaseRunner, TestCollection} from './runner'; +import type {TestCollection} from 'hermione'; +import {BaseRunner} from './runner'; export class AllTestRunner extends BaseRunner { override run(runHandler: (testCollection: TestCollection) => U): U { diff --git a/lib/gui/tool-runner/runner/index.ts b/lib/gui/tool-runner/runner/index.ts index df29746d1..f82cb8c20 100644 --- a/lib/gui/tool-runner/runner/index.ts +++ b/lib/gui/tool-runner/runner/index.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; +import type {TestCollection} from 'hermione'; -import {TestCollection, TestRunner, TestSpec} from './runner'; +import {TestRunner, TestSpec} from './runner'; import {AllTestRunner} from './all-test-runner'; import {SpecificTestRunner} from './specific-test-runner'; diff --git a/lib/gui/tool-runner/runner/runner.ts b/lib/gui/tool-runner/runner/runner.ts index a197f06fd..87e6a124a 100644 --- a/lib/gui/tool-runner/runner/runner.ts +++ b/lib/gui/tool-runner/runner/runner.ts @@ -1,7 +1,4 @@ -import Hermione from 'hermione'; -import {AsyncReturnType} from 'type-fest'; - -export type TestCollection = AsyncReturnType +import type {TestCollection} from 'hermione'; export interface TestRunner { run(handler: (testCollection: TestCollection) => U): U; diff --git a/lib/gui/tool-runner/runner/specific-test-runner.ts b/lib/gui/tool-runner/runner/specific-test-runner.ts index 9eba480d6..ff665af43 100644 --- a/lib/gui/tool-runner/runner/specific-test-runner.ts +++ b/lib/gui/tool-runner/runner/specific-test-runner.ts @@ -1,4 +1,5 @@ -import {BaseRunner, TestCollection, TestSpec} from './runner'; +import type {TestCollection} from 'hermione'; +import {BaseRunner, TestSpec} from './runner'; export class SpecificTestRunner extends BaseRunner { private _tests: TestSpec[]; diff --git a/lib/gui/tool-runner/utils.js b/lib/gui/tool-runner/utils.js deleted file mode 100644 index 7a16572be..000000000 --- a/lib/gui/tool-runner/utils.js +++ /dev/null @@ -1,112 +0,0 @@ -'use strict'; - -const _ = require('lodash'); -const path = require('path'); -const fs = require('fs-extra'); -const chalk = require('chalk'); -const Database = require('better-sqlite3'); - -const {logger} = require('../../common-utils'); -const {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} = require('../../constants/database'); -const {mergeTables} = require('../../db-utils/server'); - -exports.formatId = (hash, browserId) => `${hash}/${browserId}`; - -exports.mkFullTitle = ({suite, state}) => { - return suite.path.length > 0 - ? `${suite.path.join(' ')} ${state.name}` - : state.name; -}; - -// 'databaseUrls.json' may contain many databases urls but html-reporter at gui mode can work with single databases file. -// all databases should be local files. -exports.mergeDatabasesForReuse = async (reportPath) => { - const dbUrlsJsonPath = path.resolve(reportPath, DATABASE_URLS_JSON_NAME); - const mergedDbPath = path.resolve(reportPath, LOCAL_DATABASE_NAME); - - if (!await fs.pathExists(dbUrlsJsonPath)) { - return; - } - - const {dbUrls = []} = await fs.readJson(dbUrlsJsonPath); - - const dbPaths = dbUrls - .filter(u => u !== LOCAL_DATABASE_NAME) - .map(u => path.resolve(reportPath, u)); - - if (!dbPaths.length) { - return; - } - - logger.warn(chalk.yellow(`Merge databases to ${LOCAL_DATABASE_NAME}`)); - - const mergedDatabase = new Database(mergedDbPath); - mergeTables({db: mergedDatabase, dbPaths, getExistingTables: (statement) => { - return statement.all().map((table) => table.name); - }}); - mergedDatabase.close(); - - await Promise.all(dbPaths.map(p => fs.remove(p))); -}; - -exports.filterByEqualDiffSizes = (imagesInfo, refDiffClusters) => { - if (_.isEmpty(refDiffClusters)) { - return []; - } - - const refDiffSizes = refDiffClusters.map(getDiffClusterSizes); - - return _.filter(imagesInfo, (imageInfo) => { - const imageDiffSizes = imageInfo.diffClusters.map(getDiffClusterSizes); - const equal = compareDiffSizes(imageDiffSizes, refDiffSizes); - - if (!equal) { - return false; - } - - if (!_.isEqual(imageDiffSizes, refDiffSizes)) { - imageInfo.diffClusters = reorderClustersByEqualSize(imageInfo.diffClusters, imageDiffSizes, refDiffSizes); - } - - return true; - }); -}; - -function getDiffClusterSizes(diffCluster) { - return { - width: diffCluster.right - diffCluster.left + 1, - height: diffCluster.bottom - diffCluster.top + 1 - }; -} - -function compareDiffSizes(diffSizes1, diffSizes2) { - if (diffSizes1.length !== diffSizes2.length) { - return false; - } - - return diffSizes1.every((diffSize) => { - const foundIndex = _.findIndex(diffSizes2, diffSize); - - if (foundIndex < 0) { - return false; - } - - diffSizes2 = diffSizes2.filter((v, ind) => ind !== foundIndex); - - return true; - }); -} - -function reorderClustersByEqualSize(diffClusters1, diffSizes1, diffSizes2) { - return diffClusters1.reduce((acc, cluster, i) => { - if (diffSizes1[i] !== diffSizes2[i]) { - const foundIndex = _.findIndex(diffSizes2, diffSizes1[i]); - diffSizes2 = diffSizes2.filter((v, ind) => ind !== foundIndex); - acc[foundIndex] = cluster; - } else { - acc[i] = cluster; - } - - return acc; - }, []); -} diff --git a/lib/gui/tool-runner/utils.ts b/lib/gui/tool-runner/utils.ts new file mode 100644 index 000000000..c0732e838 --- /dev/null +++ b/lib/gui/tool-runner/utils.ts @@ -0,0 +1,115 @@ +import _ from 'lodash'; +import path from 'path'; +import fs from 'fs-extra'; +import chalk from 'chalk'; +import Database from 'better-sqlite3'; +import type {CoordBounds} from 'looks-same'; + +import {logger} from '../../common-utils'; +import {DATABASE_URLS_JSON_NAME, LOCAL_DATABASE_NAME} from '../../constants'; +import {mergeTables} from '../../db-utils/server'; +import {TestEqualDiffsData, TestRefUpdateData} from '../../tests-tree-builder/gui'; +import {ImageInfoFail, ImageSize} from '../../types'; + +export const formatId = (hash: string, browserId: string): string => `${hash}/${browserId}`; + +export const mkFullTitle = ({suite, state}: Pick): string => { + return suite.path.length > 0 ? `${suite.path.join(' ')} ${state.name}` : state.name; +}; + +export const mergeDatabasesForReuse = async (reportPath: string): Promise => { + const dbUrlsJsonPath = path.resolve(reportPath, DATABASE_URLS_JSON_NAME); + const mergedDbPath = path.resolve(reportPath, LOCAL_DATABASE_NAME); + + if (!await fs.pathExists(dbUrlsJsonPath)) { + return; + } + + const {dbUrls = []}: {dbUrls: string[]} = await fs.readJson(dbUrlsJsonPath); + + const dbPaths = dbUrls + .filter(u => u !== LOCAL_DATABASE_NAME) + .map(u => path.resolve(reportPath, u)); + + if (!dbPaths.length) { + return; + } + + logger.warn(chalk.yellow(`Merge databases to ${LOCAL_DATABASE_NAME}`)); + + const mergedDatabase = new Database(mergedDbPath); + mergeTables({ + db: mergedDatabase, + dbPaths, + getExistingTables: (statement: Database.Statement) => { + return statement.all().map((table) => (table as {name: string}).name); + } + }); + mergedDatabase.close(); + + await Promise.all(dbPaths.map(p => fs.remove(p))); +}; + +export const filterByEqualDiffSizes = (imagesInfo: TestEqualDiffsData[], refDiffClusters?: CoordBounds[]): TestEqualDiffsData[] => { + if (!refDiffClusters || _.isEmpty(refDiffClusters)) { + return []; + } + + const refDiffSizes = refDiffClusters.map(getDiffClusterSizes); + + return _.filter(imagesInfo, (imageInfo) => { + const imageInfoFail = imageInfo as ImageInfoFail; + + const imageDiffSizes = imageInfoFail.diffClusters?.map(getDiffClusterSizes) ?? []; + const equal = compareDiffSizes(imageDiffSizes, refDiffSizes); + + if (!equal) { + return false; + } + + if (!_.isEqual(imageDiffSizes, refDiffSizes)) { + imageInfoFail.diffClusters = reorderClustersByEqualSize(imageInfoFail.diffClusters ?? [], imageDiffSizes, refDiffSizes); + } + + return true; + }); +}; + +function getDiffClusterSizes(diffCluster: CoordBounds): ImageSize { + return { + width: diffCluster.right - diffCluster.left + 1, + height: diffCluster.bottom - diffCluster.top + 1 + }; +} + +function compareDiffSizes(diffSizes1: ImageSize[], diffSizes2: ImageSize[]): boolean { + if (diffSizes1.length !== diffSizes2.length) { + return false; + } + + return diffSizes1.every((diffSize) => { + const foundIndex = _.findIndex(diffSizes2, diffSize); + + if (foundIndex < 0) { + return false; + } + + diffSizes2 = diffSizes2.filter((_v, ind) => ind !== foundIndex); + + return true; + }); +} + +function reorderClustersByEqualSize(diffClusters1: CoordBounds[], diffSizes1: ImageSize[], diffSizes2: ImageSize[]): CoordBounds[] { + return diffClusters1.reduce((acc, cluster, i) => { + if (diffSizes1[i] !== diffSizes2[i]) { + const foundIndex = _.findIndex(diffSizes2, diffSizes1[i]); + diffSizes2 = diffSizes2.filter((_v, ind) => ind !== foundIndex); + acc[foundIndex] = cluster; + } else { + acc[i] = cluster; + } + + return acc; + }, [] as CoordBounds[]); +} diff --git a/lib/image-handler.ts b/lib/image-handler.ts index aa988d54a..791d07a40 100644 --- a/lib/image-handler.ts +++ b/lib/image-handler.ts @@ -42,6 +42,11 @@ export interface ImageHandlerOptions { reportPath: string; } +interface TestSpec { + fullName: string; + browserId: string; +} + export class ImageHandler extends EventEmitter2 implements ImagesInfoFormatter { private _imageStore: ImageStore; private _imagesSaver: ImagesSaver; @@ -235,7 +240,7 @@ export class ImageHandler extends EventEmitter2 implements ImagesInfoFormatter { this._imagesSaver = newImagesSaver; } - updateCacheExpectedPath(testResult: ReporterTestResultPlain, stateName: string, expectedPath: string): void { + updateCacheExpectedPath(testResult: TestSpec, stateName: string, expectedPath: string): void { const key = this._getExpectedKey(testResult, stateName); if (expectedPath) { @@ -245,7 +250,7 @@ export class ImageHandler extends EventEmitter2 implements ImagesInfoFormatter { } } - private _getExpectedKey(testResult: ReporterTestResultPlain, stateName?: string): string { + private _getExpectedKey(testResult: TestSpec, stateName?: string): string { const shortTestId = getShortMD5(mkTestId(testResult.fullName, testResult.browserId)); return shortTestId + '#' + stateName; diff --git a/lib/report-builder/gui.ts b/lib/report-builder/gui.ts index 9f5a91a68..e7b42ef22 100644 --- a/lib/report-builder/gui.ts +++ b/lib/report-builder/gui.ts @@ -21,7 +21,7 @@ interface UndoAcceptImageResult { shouldRevertReference: boolean; } -interface GuiReportBuilderResult { +export interface GuiReportBuilderResult { tree: Tree; skips: SkipItem[]; config: ConfigForStaticFile & {customGui: ReporterConfig['customGui']}; diff --git a/lib/reporter-helpers.js b/lib/reporter-helpers.js deleted file mode 100644 index 3dc583b1f..000000000 --- a/lib/reporter-helpers.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -const path = require('path'); -const tmp = require('tmp'); -const Promise = require('bluebird'); -const {getShortMD5} = require('./common-utils'); -const utils = require('./server-utils'); -const {ImageHandler} = require('./image-handler'); - -const mkReferenceHash = (testId, stateName) => getShortMD5(`${testId}#${stateName}`); - -exports.updateReferenceImage = async (testResult, reportPath, stateName) => { - const currImg = ImageHandler.getCurrImg(testResult.assertViewResults, stateName) ?? {}; - - const src = currImg.path - ? path.resolve(reportPath, currImg.path) - : utils.getCurrentAbsolutePath(testResult, reportPath, stateName); - - const referencePath = ImageHandler.getRefImg(testResult.assertViewResults, stateName)?.path; - - if (utils.fileExists(referencePath)) { - const referenceId = mkReferenceHash(testResult.id, stateName); - const oldReferencePath = path.resolve(tmp.tmpdir, referenceId); - await utils.copyFileAsync(referencePath, oldReferencePath); - } - - return Promise.all([ - utils.copyFileAsync(src, referencePath), - utils.copyFileAsync(src, utils.getReferenceAbsolutePath(testResult, reportPath, stateName)) - ]); -}; - -exports.revertReferenceImage = (referenceId, testResult, stateName) => { - const referenceHash = mkReferenceHash(referenceId, stateName); - const oldReferencePath = path.resolve(tmp.tmpdir, referenceHash); - const referencePath = ImageHandler.getRefImg(testResult.assertViewResults, stateName)?.path; - - return utils.copyFileAsync(oldReferencePath, referencePath); -}; - -exports.removeReferenceImage = (testResult, stateName) => { - return utils.deleteFile(ImageHandler.getRefImg(testResult.assertViewResults, stateName)?.path); -}; diff --git a/lib/reporter-helpers.ts b/lib/reporter-helpers.ts new file mode 100644 index 000000000..f0d278a41 --- /dev/null +++ b/lib/reporter-helpers.ts @@ -0,0 +1,52 @@ +import * as path from 'path'; +import * as tmp from 'tmp'; +import {getShortMD5} from './common-utils'; +import * as utils from './server-utils'; +import {ImageHandler} from './image-handler'; +import {ReporterTestResult} from './test-adapter'; + +const mkReferenceHash = (testId: string, stateName: string): string => getShortMD5(`${testId}#${stateName}`); + +export const updateReferenceImage = async (testResult: ReporterTestResult, reportPath: string, stateName: string): Promise => { + const currImg = ImageHandler.getCurrImg(testResult.assertViewResults, stateName); + + const src = currImg?.path + ? path.resolve(reportPath, currImg.path) + : utils.getCurrentAbsolutePath(testResult, reportPath, stateName); + + // TODO: get rid of type assertion + const referencePath = ImageHandler.getRefImg(testResult.assertViewResults, stateName)?.path as string; + + if (utils.fileExists(referencePath)) { + const referenceId = mkReferenceHash(testResult.id, stateName); + const oldReferencePath = path.resolve(tmp.tmpdir, referenceId); + await utils.copyFileAsync(referencePath, oldReferencePath); + } + + return Promise.all([ + utils.copyFileAsync(src, referencePath), + utils.copyFileAsync(src, utils.getReferenceAbsolutePath(testResult, reportPath, stateName)) + ]); +}; + +export const revertReferenceImage = async (referenceId: string, testResult: ReporterTestResult, stateName: string): Promise => { + const referenceHash = mkReferenceHash(referenceId, stateName); + const oldReferencePath = path.resolve(tmp.tmpdir, referenceHash); + const referencePath = ImageHandler.getRefImg(testResult.assertViewResults, stateName)?.path; + + if (!referencePath) { + return; + } + + return utils.copyFileAsync(oldReferencePath, referencePath); +}; + +export const removeReferenceImage = async (testResult: ReporterTestResult, stateName: string): Promise => { + const imagePath = ImageHandler.getRefImg(testResult.assertViewResults, stateName)?.path; + + if (!imagePath) { + return; + } + + return utils.deleteFile(imagePath); +}; diff --git a/package-lock.json b/package-lock.json index 4500ba7f4..471f03cae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,6 +72,7 @@ "@typescript-eslint/parser": "^5.60.0", "app-module-path": "^2.2.0", "babel-loader": "^9.1.3", + "buffer": "^6.0.3", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "classnames": "^2.2.5", @@ -87,8 +88,9 @@ "eslint": "^8.43.0", "eslint-config-gemini-testing": "^2.8.0", "eslint-plugin-react": "^7.32.2", + "events": "^3.3.0", "fork-ts-checker-webpack-plugin": "^9.0.0", - "hermione": "^8.0.0-beta.2", + "hermione": "^8.0.0-beta.3", "hermione-global-hook": "^1.0.1", "hermione-test-repeater": "^0.0.8", "html-react-parser": "^0.4.0", @@ -6979,8 +6981,7 @@ "version": "1.6.4", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", - "dev": true, - "optional": true + "dev": true }, "node_modules/babel-loader": { "version": "9.1.3", @@ -7708,6 +7709,17 @@ "pako": "~0.2.0" } }, + "node_modules/browserify/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "node_modules/browserify/node_modules/concat-stream": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", @@ -7722,6 +7734,15 @@ "typedarray": "~0.0.5" } }, + "node_modules/browserify/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/browserify/node_modules/path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -7791,14 +7812,27 @@ } }, "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, "node_modules/buffer-crc32": { @@ -8590,6 +8624,18 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", @@ -8605,6 +8651,26 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -11316,9 +11382,9 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { "node": ">=8" } @@ -14596,12 +14662,12 @@ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "engines": { - "node": ">=0.4.x" + "node": ">=0.8.x" } }, "node_modules/eventsource": { @@ -15047,8 +15113,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.0.tgz", "integrity": "sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw==", - "dev": true, - "optional": true + "dev": true }, "node_modules/fast-glob": { "version": "3.2.12", @@ -16787,15 +16852,15 @@ } }, "node_modules/hermione": { - "version": "8.0.0-beta.2", - "resolved": "https://registry.npmjs.org/hermione/-/hermione-8.0.0-beta.2.tgz", - "integrity": "sha512-KOgcvP3YuSWijV4PtjgBH7hGctbbAQB4P/Hw1OoZxpV8woEErNlPhz+fWo81+rN7EbVlM9jb6YIsutY7UtNzvw==", + "version": "8.0.0-beta.3", + "resolved": "https://registry.npmjs.org/hermione/-/hermione-8.0.0-beta.3.tgz", + "integrity": "sha512-zY6HL3FDFjlHtiIdr8CzJHLOY6UKZvekrLhaozTnFS8iyBz/l7X9B8uIYblILf5AOXfa2ZO/VVIbf1ry9Km4Rw==", "dev": true, "dependencies": { "@gemini-testing/commander": "2.15.3", "@types/mocha": "^10.0.1", - "@wdio/globals": "^8.5.7", - "@wdio/types": "^8.4.0", + "@wdio/globals": "^8.10.7", + "@wdio/types": "^8.10.4", "@wdio/utils": "^7.26.0", "aliasify": "^1.9.0", "bluebird": "^3.5.1", @@ -16811,7 +16876,7 @@ "glob-extra": "^5.0.2", "inherit": "^2.2.2", "lodash": "^4.17.21", - "looks-same": "^8.2.1", + "looks-same": "^9.0.0", "micromatch": "^4.0.5", "mocha": "^10.2.0", "plugins-loader": "^1.2.0", @@ -16822,6 +16887,7 @@ "uglifyify": "^3.0.4", "urijs": "^1.19.11", "url-join": "^4.0.1", + "uuid": "^9.0.1", "webdriverio": "8.13.4", "worker-farm": "^1.7.0", "yallist": "^3.1.1" @@ -16830,7 +16896,7 @@ "hermione": "bin/hermione" }, "engines": { - "node": ">= 16.0.0" + "node": ">= 18.0.0" }, "peerDependencies": { "ts-node": ">=10.5.0" @@ -17724,6 +17790,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hermione/node_modules/looks-same": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/looks-same/-/looks-same-9.0.0.tgz", + "integrity": "sha512-8UI1PxtatkOJ8xLSsd0Pl2CwUKu0L6KqmF4MYqXJXny81lEv41TrjdOX+EeKpbKVa1R1Ffn0ROP6HglZ5Ex1fg==", + "dev": true, + "dependencies": { + "color-diff": "^1.1.0", + "fs-extra": "^8.1.0", + "js-graph-algorithms": "1.0.18", + "lodash": "^4.17.3", + "nested-error-stacks": "^2.1.0", + "parse-color": "^1.0.0", + "sharp": "0.32.6" + }, + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/hermione/node_modules/looks-same/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/hermione/node_modules/looks-same/node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/hermione/node_modules/looks-same/node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, "node_modules/hermione/node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -17796,6 +17928,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/hermione/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, "node_modules/hermione/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -18108,6 +18246,17 @@ "node": ">=8" } }, + "node_modules/hermione/node_modules/tar-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", + "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/hermione/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -18146,10 +18295,14 @@ } }, "node_modules/hermione/node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -24680,8 +24833,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true, - "optional": true + "dev": true }, "node_modules/quick-lru": { "version": "1.1.0", @@ -26240,38 +26392,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/sharp/node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/sharp/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/sharp/node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/sharp/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -27527,7 +27647,6 @@ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz", "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==", "dev": true, - "optional": true, "dependencies": { "fast-fifo": "^1.1.0", "queue-tick": "^1.0.1" @@ -31031,15 +31150,6 @@ "ajv": "^6.9.1" } }, - "node_modules/webpack/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -36883,8 +36993,7 @@ "version": "1.6.4", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", - "dev": true, - "optional": true + "dev": true }, "babel-loader": { "version": "9.1.3", @@ -37356,6 +37465,17 @@ "xtend": "^4.0.0" }, "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "concat-stream": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", @@ -37367,6 +37487,12 @@ "typedarray": "~0.0.5" } }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "dev": true + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -37529,14 +37655,13 @@ } }, "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, "buffer-crc32": { @@ -38147,6 +38272,25 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + } + } + }, "color-convert": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", @@ -38162,6 +38306,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -40302,9 +40455,9 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" }, "detect-node": { "version": "2.1.0", @@ -42786,9 +42939,9 @@ "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "eventsource": { @@ -43175,8 +43328,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.0.tgz", "integrity": "sha512-IgfweLvEpwyA4WgiQe9Nx6VV2QkML2NkvZnk1oKnIzXgXdWxuhF7zw4DvLTPZJn6PIUneiAXPF24QmoEqHTjyw==", - "dev": true, - "optional": true + "dev": true }, "fast-glob": { "version": "3.2.12", @@ -44518,15 +44670,15 @@ "dev": true }, "hermione": { - "version": "8.0.0-beta.2", - "resolved": "https://registry.npmjs.org/hermione/-/hermione-8.0.0-beta.2.tgz", - "integrity": "sha512-KOgcvP3YuSWijV4PtjgBH7hGctbbAQB4P/Hw1OoZxpV8woEErNlPhz+fWo81+rN7EbVlM9jb6YIsutY7UtNzvw==", + "version": "8.0.0-beta.3", + "resolved": "https://registry.npmjs.org/hermione/-/hermione-8.0.0-beta.3.tgz", + "integrity": "sha512-zY6HL3FDFjlHtiIdr8CzJHLOY6UKZvekrLhaozTnFS8iyBz/l7X9B8uIYblILf5AOXfa2ZO/VVIbf1ry9Km4Rw==", "dev": true, "requires": { "@gemini-testing/commander": "2.15.3", "@types/mocha": "^10.0.1", - "@wdio/globals": "^8.5.7", - "@wdio/types": "^8.4.0", + "@wdio/globals": "^8.10.7", + "@wdio/types": "^8.10.4", "@wdio/utils": "^7.26.0", "aliasify": "^1.9.0", "bluebird": "^3.5.1", @@ -44542,7 +44694,7 @@ "glob-extra": "^5.0.2", "inherit": "^2.2.2", "lodash": "^4.17.21", - "looks-same": "^8.2.1", + "looks-same": "^9.0.0", "micromatch": "^4.0.5", "mocha": "^10.2.0", "plugins-loader": "^1.2.0", @@ -44553,6 +44705,7 @@ "uglifyify": "^3.0.4", "urijs": "^1.19.11", "url-join": "^4.0.1", + "uuid": "^9.0.1", "webdriverio": "8.13.4", "worker-farm": "^1.7.0", "yallist": "^3.1.1" @@ -45180,6 +45333,61 @@ "p-locate": "^6.0.0" } }, + "looks-same": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/looks-same/-/looks-same-9.0.0.tgz", + "integrity": "sha512-8UI1PxtatkOJ8xLSsd0Pl2CwUKu0L6KqmF4MYqXJXny81lEv41TrjdOX+EeKpbKVa1R1Ffn0ROP6HglZ5Ex1fg==", + "dev": true, + "requires": { + "color-diff": "^1.1.0", + "fs-extra": "^8.1.0", + "js-graph-algorithms": "1.0.18", + "lodash": "^4.17.3", + "nested-error-stacks": "^2.1.0", + "parse-color": "^1.0.0", + "sharp": "0.32.6" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "dev": true, + "requires": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + } + } + }, "lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -45230,6 +45438,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, "node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -45430,6 +45644,17 @@ "strip-ansi": "^6.0.1" } }, + "tar-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", + "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "dev": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -45449,9 +45674,9 @@ "dev": true }, "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true }, "webdriver": { @@ -50544,8 +50769,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true, - "optional": true + "dev": true }, "quick-lru": { "version": "1.1.0", @@ -51837,32 +52061,6 @@ "tunnel-agent": "^0.6.0" }, "dependencies": { - "color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -52882,7 +53080,6 @@ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.1.tgz", "integrity": "sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==", "dev": true, - "optional": true, "requires": { "fast-fifo": "^1.1.0", "queue-tick": "^1.0.1" @@ -55035,12 +55232,6 @@ "dev": true, "requires": {} }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", diff --git a/package.json b/package.json index 776ea1038..8b10631dd 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "@typescript-eslint/parser": "^5.60.0", "app-module-path": "^2.2.0", "babel-loader": "^9.1.3", + "buffer": "^6.0.3", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "classnames": "^2.2.5", @@ -134,8 +135,9 @@ "eslint": "^8.43.0", "eslint-config-gemini-testing": "^2.8.0", "eslint-plugin-react": "^7.32.2", + "events": "^3.3.0", "fork-ts-checker-webpack-plugin": "^9.0.0", - "hermione": "^8.0.0-beta.2", + "hermione": "^8.0.0-beta.3", "hermione-global-hook": "^1.0.1", "hermione-test-repeater": "^0.0.8", "html-react-parser": "^0.4.0", diff --git a/test/func/fixtures/hermione-eye/package.json b/test/func/fixtures/hermione-eye/package.json index 97f9d44c4..9d042881d 100644 --- a/test/func/fixtures/hermione-eye/package.json +++ b/test/func/fixtures/hermione-eye/package.json @@ -6,6 +6,6 @@ "scripts": { "clean": "rm -rf report", "generate": "npx hermione", - "gui": "npx hermione gui --port $(../../utils/get-port.js hermione-eye gui)" + "gui": "npx hermione gui --hostname 0.0.0.0 --port $(../../utils/get-port.js hermione-eye gui)" } } diff --git a/test/func/fixtures/hermione-gui/package.json b/test/func/fixtures/hermione-gui/package.json index 8eb621e62..7fe717d59 100644 --- a/test/func/fixtures/hermione-gui/package.json +++ b/test/func/fixtures/hermione-gui/package.json @@ -5,6 +5,6 @@ "scripts": { "clean": "rm -rf report", "generate": "npx hermione --grep 'tests to run'", - "gui": "npx hermione gui --port $(../../utils/get-port.js hermione-gui gui)" + "gui": "npx hermione gui --hostname 0.0.0.0 --port $(../../utils/get-port.js hermione-gui gui)" } } diff --git a/test/func/fixtures/hermione/package.json b/test/func/fixtures/hermione/package.json index 6942db35f..8a3cc326a 100644 --- a/test/func/fixtures/hermione/package.json +++ b/test/func/fixtures/hermione/package.json @@ -5,6 +5,6 @@ "scripts": { "clean": "rm -rf report", "generate": "npx hermione", - "gui": "npx hermione gui --port 8001" + "gui": "npx hermione gui --hostname 0.0.0.0 --port 8001" } } diff --git a/test/func/fixtures/plugins/package.json b/test/func/fixtures/plugins/package.json index e68ca4877..4b9b3a1e1 100644 --- a/test/func/fixtures/plugins/package.json +++ b/test/func/fixtures/plugins/package.json @@ -5,6 +5,6 @@ "scripts": { "clean": "rm -rf report", "generate": "npx hermione", - "gui": "npx hermione gui --port 8002" + "gui": "npx hermione gui --hostname 0.0.0.0 --port 8002" } } diff --git a/test/unit/lib/gui/app.js b/test/unit/lib/gui/app.js index 32bf5e403..86079422f 100644 --- a/test/unit/lib/gui/app.js +++ b/test/unit/lib/gui/app.js @@ -4,7 +4,7 @@ const _ = require('lodash'); const proxyquire = require('proxyquire'); const Promise = require('bluebird'); -const ToolRunner = require('lib/gui/tool-runner'); +const {ToolRunner} = require('lib/gui/tool-runner'); const {stubTool, stubConfig} = require('../../utils'); describe('lib/gui/app', () => { diff --git a/test/unit/lib/gui/tool-runner/index.js b/test/unit/lib/gui/tool-runner/index.js index 624eb54c8..ccf975cfe 100644 --- a/test/unit/lib/gui/tool-runner/index.js +++ b/test/unit/lib/gui/tool-runner/index.js @@ -98,7 +98,7 @@ describe('lib/gui/tool-runner/index', () => { removeReferenceImage, revertReferenceImage } - }); + }).ToolRunner; sandbox.stub(reportBuilder, 'imageHandler').value({updateCacheExpectedPath: sinon.stub()}); sandbox.stub(logger, 'warn'); @@ -312,7 +312,7 @@ describe('lib/gui/tool-runner/index', () => { const mkUndoTestData_ = async (stubResult, {stateName = 'plain'} = {}) => { reportBuilder.undoAcceptImage.withArgs(sinon.match({ fullName: 'some-title' - }), 'plain').resolves(stubResult); + }), 'plain').returns(stubResult); const tests = [{ id: 'some-id', fullTitle: () => 'some-title', diff --git a/webpack.common.js b/webpack.common.js index 15f58363a..c4de4521d 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -20,7 +20,9 @@ module.exports = { resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'], fallback: { + buffer: require.resolve('buffer'), crypto: require.resolve('crypto-browserify'), + events: require.resolve('events'), path: require.resolve('path-browserify'), stream: require.resolve('stream-browserify') }