diff --git a/lib/cli-commands/gui.js b/lib/cli-commands/gui.js index 155489e5b..fb7e0d59d 100644 --- a/lib/cli-commands/gui.js +++ b/lib/cli-commands/gui.js @@ -2,7 +2,7 @@ const {cliCommands} = require('.'); const runGui = require('../gui').default; -const Api = require('../gui/api'); +const {Api} = require('../gui/api'); const {GUI: commandName} = cliCommands; diff --git a/lib/gui/api/facade.js b/lib/gui/api/facade.js deleted file mode 100644 index 2856e22f3..000000000 --- a/lib/gui/api/facade.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const EventEmitter2 = require('eventemitter2'); -const {GuiEvents} = require('../constants/gui-events'); - -module.exports = class ApiFacade extends EventEmitter2 { - static create() { - return new ApiFacade(); - } - - constructor() { - super(); - - this.events = GuiEvents; - } -}; diff --git a/lib/gui/api/facade.ts b/lib/gui/api/facade.ts new file mode 100644 index 000000000..4250a558b --- /dev/null +++ b/lib/gui/api/facade.ts @@ -0,0 +1,16 @@ +import EventEmitter2 from 'eventemitter2'; +import {GuiEvents} from '../constants'; + +export class ApiFacade extends EventEmitter2 { + events: GuiEvents; + + static create(this: new () => T): T { + return new this(); + } + + constructor() { + super(); + + this.events = GuiEvents; + } +} diff --git a/lib/gui/api/index.js b/lib/gui/api/index.js deleted file mode 100644 index 178a9f79c..000000000 --- a/lib/gui/api/index.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -const ApiFacade = require('./facade'); - -module.exports = class Api { - static create(hermione) { - return new Api(hermione); - } - - constructor(hermione) { - this._gui = hermione.gui = ApiFacade.create(); - } - - async initServer(server) { - await this._gui.emitAsync(this._gui.events.SERVER_INIT, server); - } - - async serverReady(data) { - await this._gui.emitAsync(this._gui.events.SERVER_READY, data); - } -}; diff --git a/lib/gui/api/index.ts b/lib/gui/api/index.ts new file mode 100644 index 000000000..06ae26609 --- /dev/null +++ b/lib/gui/api/index.ts @@ -0,0 +1,29 @@ +import {ApiFacade} from './facade'; +import Hermione from 'hermione'; +import {Express} from 'express'; + +export interface ServerReadyData { + url: string; +} + +type HermioneWithGui = Hermione & { gui: ApiFacade }; + +export class Api { + private _gui: ApiFacade; + + static create(this: new (hermione: HermioneWithGui) => T, hermione: HermioneWithGui): T { + return new this(hermione); + } + + constructor(hermione: HermioneWithGui) { + this._gui = hermione.gui = ApiFacade.create(); + } + + async initServer(server: Express): Promise { + await this._gui.emitAsync(this._gui.events.SERVER_INIT, server); + } + + async serverReady(data: ServerReadyData): Promise { + await this._gui.emitAsync(this._gui.events.SERVER_READY, data); + } +} diff --git a/lib/gui/app.js b/lib/gui/app.js deleted file mode 100644 index ae546c869..000000000 --- a/lib/gui/app.js +++ /dev/null @@ -1,86 +0,0 @@ -'use strict'; - -const _ = require('lodash'); -const {ToolRunner} = require('./tool-runner'); - -module.exports = class App { - static create(paths, hermione, configs) { - return new this(paths, hermione, configs); - } - - constructor(paths, hermione, configs) { - const {pluginConfig} = configs; - - this._toolRunner = ToolRunner.create(paths, hermione, configs); - this._pluginConfig = pluginConfig; - - this._browserConfigs = []; - this._retryCache = {}; - } - - async initialize() { - return await this._toolRunner.initialize(); - } - - finalize() { - this._toolRunner.finalize(); - } - - run(tests) { - return _.isEmpty(tests) - ? this._toolRunner.run() - : this._runWithoutRetries(tests); - } - - _runWithoutRetries(tests) { - if (_.isEmpty(this._browserConfigs)) { - this._browserConfigs = _.map(this._toolRunner.config.getBrowserIds(), (id) => this._toolRunner.config.forBrowser(id)); - } - - this._disableRetries(); - - return this._toolRunner.run(tests) - .finally(() => this._restoreRetries()); - } - - getTestsDataToUpdateRefs(imageIds = []) { - return this._toolRunner.getTestsDataToUpdateRefs(imageIds); - } - - getImageDataToFindEqualDiffs(imageIds = []) { - return this._toolRunner.getImageDataToFindEqualDiffs(imageIds); - } - - updateReferenceImage(failedTests = []) { - return this._toolRunner.updateReferenceImage(failedTests); - } - - undoAcceptImages(imageIds) { - return this._toolRunner.undoAcceptImages(imageIds); - } - - async findEqualDiffs(data) { - return this._toolRunner.findEqualDiffs(data); - } - - addClient(connection) { - this._toolRunner.addClient(connection); - } - - get data() { - return this._toolRunner.tree; - } - - _disableRetries() { - this._browserConfigs.forEach((broConfig) => { - this._retryCache[broConfig.id] = broConfig.retry; - broConfig.retry = 0; - }); - } - - _restoreRetries() { - this._browserConfigs.forEach((broConfig) => { - broConfig.retry = this._retryCache[broConfig.id]; - }); - } -}; diff --git a/lib/gui/app.ts b/lib/gui/app.ts new file mode 100644 index 000000000..b642682aa --- /dev/null +++ b/lib/gui/app.ts @@ -0,0 +1,96 @@ +import {Response} from 'express'; +import _ from 'lodash'; + +import {ToolRunner, ToolRunnerTree, UndoAcceptImagesResult} from './tool-runner'; +import {HtmlReporterApi} from '../types'; +import Hermione, {Config} from 'hermione'; +import {GuiConfigs} from './index'; +import {TestSpec} from './tool-runner/runner/runner'; +import {TestBranch, TestEqualDiffsData, TestRefUpdateData} from '../tests-tree-builder/gui'; + +type BrowserConfig = ReturnType; + +type AppArgs = [paths: string[], hermione: Hermione & HtmlReporterApi, configs: GuiConfigs]; + +export class App { + private _toolRunner: ToolRunner; + private _browserConfigs: BrowserConfig[]; + private _retryCache: Record; + + static create(this: new (...args: AppArgs) => T, ...args: AppArgs): T { + return new this(...args); + } + + constructor(...[paths, hermione, configs]: AppArgs) { + this._toolRunner = ToolRunner.create(paths, hermione, configs); + + this._browserConfigs = []; + this._retryCache = {}; + } + + get data(): ToolRunnerTree | null { + return this._toolRunner.tree; + } + + async initialize(): Promise { + return await this._toolRunner.initialize(); + } + + async finalize(): Promise { + return this._toolRunner.finalize(); + } + + async run(tests: TestSpec[]): Promise { + return _.isEmpty(tests) + ? this._toolRunner.run() + : this._runWithoutRetries(tests); + } + + private async _runWithoutRetries(tests: TestSpec[]): Promise { + if (_.isEmpty(this._browserConfigs)) { + this._browserConfigs = _.map(this._toolRunner.config.getBrowserIds(), (id) => this._toolRunner.config.forBrowser(id)); + } + + this._disableRetries(); + + return this._toolRunner.run(tests) + .finally(() => this._restoreRetries()); + } + + getTestsDataToUpdateRefs(imageIds: string[] = []): TestRefUpdateData[] { + return this._toolRunner.getTestsDataToUpdateRefs(imageIds); + } + + getImageDataToFindEqualDiffs(imageIds: string[] = []): TestEqualDiffsData[] { + return this._toolRunner.getImageDataToFindEqualDiffs(imageIds); + } + + async updateReferenceImage(failedTests: TestRefUpdateData[] = []): Promise { + return this._toolRunner.updateReferenceImage(failedTests); + } + + async undoAcceptImages(imageIds: TestRefUpdateData[]): Promise { + return this._toolRunner.undoAcceptImages(imageIds); + } + + async findEqualDiffs(data: TestEqualDiffsData[]): Promise { + return this._toolRunner.findEqualDiffs(data); + } + + addClient(connection: Response): void { + this._toolRunner.addClient(connection); + } + + private _disableRetries(): void { + this._browserConfigs.forEach((broConfig) => { + this._retryCache[broConfig.id] = broConfig.retry; + broConfig.retry = 0; + }); + } + + private _restoreRetries(): void { + this._browserConfigs.forEach((broConfig) => { + broConfig.retry = this._retryCache[broConfig.id]; + }); + } +} diff --git a/lib/gui/index.ts b/lib/gui/index.ts index 31ab42468..cae5f8a49 100644 --- a/lib/gui/index.ts +++ b/lib/gui/index.ts @@ -2,16 +2,20 @@ import type {CommanderStatic} from '@gemini-testing/commander'; import chalk from 'chalk'; import opener from 'opener'; -import server from './server'; +import * as server from './server'; import {logger} from '../common-utils'; import * as utils from '../server-utils'; -import {ReporterConfig} from '../types'; +import {HtmlReporterApi, ReporterConfig} from '../types'; +import Hermione from 'hermione'; +import {Api} from './api'; const {logError} = utils; export interface GuiCliOptions { autoRun: boolean; open: unknown; + port: number; + hostname: string; } export interface GuiConfigs { @@ -20,10 +24,10 @@ export interface GuiConfigs { pluginConfig: ReporterConfig; } -interface ServerArgs { +export interface ServerArgs { paths: string[]; - hermione: unknown; - guiApi: unknown; + hermione: Hermione & HtmlReporterApi; + guiApi: Api; configs: GuiConfigs; } diff --git a/lib/gui/routes/plugins.js b/lib/gui/routes/plugins.ts similarity index 70% rename from lib/gui/routes/plugins.js rename to lib/gui/routes/plugins.ts index be5ce6eee..38eac1c23 100644 --- a/lib/gui/routes/plugins.js +++ b/lib/gui/routes/plugins.ts @@ -1,22 +1,21 @@ -'use strict'; +import {Router} from 'express'; +import {INTERNAL_SERVER_ERROR, BAD_REQUEST} from 'http-codes'; -const {Router} = require('express'); -const {INTERNAL_SERVER_ERROR, BAD_REQUEST} = require('http-codes'); - -const { +import { isUnexpectedPlugin, getPluginClientScriptPath, getPluginMiddleware, forEachPlugin, logError -} = require('../../server-utils'); +} from '../../server-utils'; +import {ReporterConfig} from '../../types'; -module.exports = function(router, pluginConfig) { +export const initPluginsRoutes = (router: Router, pluginConfig: ReporterConfig): Router => { if (!pluginConfig.pluginsEnabled) { return router; } - router.get(`/plugins/:pluginName/plugin.js`, (req, res) => { + router.get<{pluginName: string}>(`/plugins/:pluginName/plugin.js`, (req, res) => { if (isUnexpectedPlugin(pluginConfig.plugins, req.params.pluginName)) { res.status(BAD_REQUEST).send({error: `Unexpected plugin "${req.params.pluginName}" requested.`}); return; @@ -42,12 +41,12 @@ module.exports = function(router, pluginConfig) { initPluginMiddleware(pluginRouter); router.use(`/plugin-routes/${pluginName}`, pluginRouter); - } catch (err) { - logError(err); + } catch (err: unknown) { + logError(err as Error); } }); - router.use('/plugin-routes/:pluginName', (req, res) => { + router.use<{pluginName: string}>('/plugin-routes/:pluginName', (req, res) => { res.status(BAD_REQUEST).send({error: `No middleware is registered for "${req.params.pluginName}".`}); }); diff --git a/lib/gui/server.js b/lib/gui/server.ts similarity index 60% rename from lib/gui/server.js rename to lib/gui/server.ts index 84e795216..63eb0fd85 100644 --- a/lib/gui/server.js +++ b/lib/gui/server.ts @@ -1,19 +1,19 @@ -'use strict'; - -const path = require('path'); -const express = require('express'); -const onExit = require('signal-exit'); -const Promise = require('bluebird'); -const bodyParser = require('body-parser'); -const {INTERNAL_SERVER_ERROR, OK} = require('http-codes'); - -const App = require('./app'); -const {MAX_REQUEST_SIZE, KEEP_ALIVE_TIMEOUT, HEADERS_TIMEOUT} = require('./constants'); -const {initializeCustomGui, runCustomGuiAction} = require('../server-utils'); -const {logger} = require('../common-utils'); -const initPluginsRoutes = require('./routes/plugins'); - -exports.start = async ({paths, hermione, guiApi, configs}) => { +import path from 'path'; +import express from 'express'; +import {onExit} from 'signal-exit'; +import BluebirdPromise from 'bluebird'; +import bodyParser from 'body-parser'; +import {INTERNAL_SERVER_ERROR, OK} from 'http-codes'; + +import {App} from './app'; +import {MAX_REQUEST_SIZE, KEEP_ALIVE_TIMEOUT, HEADERS_TIMEOUT} from './constants'; +import {initializeCustomGui, runCustomGuiAction} from '../server-utils'; +import {logger} from '../common-utils'; +import {initPluginsRoutes} from './routes/plugins'; +import {ServerArgs} from './index'; +import {ServerReadyData} from './api'; + +export const start = async ({paths, hermione, guiApi, configs}: ServerArgs): Promise => { const {options, pluginConfig} = configs; const app = App.create(paths, hermione, configs); const server = express(); @@ -28,31 +28,37 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { server.use(express.static(path.join(__dirname, '../static'), {index: 'gui.html'})); server.use(express.static(path.join(process.cwd(), pluginConfig.path))); - server.get('/', (req, res) => res.sendFile(path.join(__dirname, '../static', 'gui.html'))); + server.get('/', (_req, res) => res.sendFile(path.join(__dirname, '../static', 'gui.html'))); - server.get('/events', (req, res) => { + server.get('/events', (_req, res) => { res.writeHead(OK, {'Content-Type': 'text/event-stream'}); app.addClient(res); }); - server.set('json replacer', (key, val) => { + server.set('json replacer', (_key: string, val: unknown) => { return typeof val === 'function' ? val.toString() : val; }); - server.get('/init', async (req, res) => { + server.get('/init', async (_req, res) => { try { await initializeCustomGui(hermione, pluginConfig); - } catch (e) { - app.data.customGuiError = { - response: { - status: INTERNAL_SERVER_ERROR, - data: `Error while trying to initialize custom GUI: ${e.message}` + res.json(app.data); + } catch (e: unknown) { + const error = e as Error; + if (!app.data) { + throw new Error(`Failed to initialize custom GUI ${error.message}`); + } + res.json({ + ...app.data, + customGuiError: { + response: { + status: INTERNAL_SERVER_ERROR, + data: `Error while trying to initialize custom GUI: ${error.message}` + } } - }; + }); } - - res.json(app.data); }); server.post('/run', (req, res) => { @@ -61,7 +67,7 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { app.run(req.body); res.sendStatus(OK); } catch (e) { - res.status(INTERNAL_SERVER_ERROR).send(`Error while trying to run tests: ${e.message}`); + res.status(INTERNAL_SERVER_ERROR).send(`Error while trying to run tests: ${(e as Error).message}`); } }); @@ -70,7 +76,7 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { await runCustomGuiAction(hermione, pluginConfig, payload); res.sendStatus(OK); } catch (e) { - res.status(INTERNAL_SERVER_ERROR).send(`Error while running custom gui action: ${e.message}`); + res.status(INTERNAL_SERVER_ERROR).send(`Error while running custom gui action: ${(e as Error).message}`); } }); @@ -79,7 +85,7 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { const data = app.getTestsDataToUpdateRefs(req.body); res.json(data); } catch (error) { - res.status(INTERNAL_SERVER_ERROR).send({error: error.message}); + res.status(INTERNAL_SERVER_ERROR).send({error: (error as Error).message}); } }); @@ -99,8 +105,8 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { try { const data = app.getImageDataToFindEqualDiffs(req.body); res.json(data); - } catch (error) { - res.status(INTERNAL_SERVER_ERROR).send({error: error.message}); + } catch (e) { + res.status(INTERNAL_SERVER_ERROR).send({error: (e as Error).message}); } }); @@ -108,8 +114,8 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { try { const result = await app.findEqualDiffs(req.body); res.json(result); - } catch ({message}) { - res.status(INTERNAL_SERVER_ERROR).send({error: message}); + } catch (e) { + res.status(INTERNAL_SERVER_ERROR).send({error: (e as Error).message}); } }); @@ -118,21 +124,21 @@ exports.start = async ({paths, hermione, guiApi, configs}) => { logger.log('server shutting down'); }); - server.post('/stop', (req, res) => { + server.post('/stop', (_req, res) => { try { // pass 0 to prevent terminating hermione process - hermione.halt('Tests were stopped by the user', 0); + hermione.halt(new Error('Tests were stopped by the user'), 0); res.sendStatus(OK); } catch (e) { - res.status(INTERNAL_SERVER_ERROR).send(`Error while stopping tests: ${e.message}`); + res.status(INTERNAL_SERVER_ERROR).send(`Error while stopping tests: ${(e as Error).message}`); } }); await app.initialize(); const {port, hostname} = options; - await Promise.fromCallback((callback) => { - const httpServer = server.listen(port, hostname, callback); + await BluebirdPromise.fromCallback((callback) => { + const httpServer = server.listen(port, hostname, callback as () => void); httpServer.keepAliveTimeout = KEEP_ALIVE_TIMEOUT; httpServer.headersTimeout = HEADERS_TIMEOUT; }); diff --git a/lib/gui/tool-runner/index.ts b/lib/gui/tool-runner/index.ts index 1b0d5756f..6769e3e65 100644 --- a/lib/gui/tool-runner/index.ts +++ b/lib/gui/tool-runner/index.ts @@ -36,7 +36,7 @@ import {ImagesInfoFormatter} from '../../image-handler'; type ToolRunnerArgs = [paths: string[], hermione: Hermione & HtmlReporterApi, configs: GuiConfigs]; -type ToolRunnerTree = GuiReportBuilderResult & Pick; +export type ToolRunnerTree = GuiReportBuilderResult & Pick; interface HermioneTestExtended extends HermioneTest { assertViewResults: {stateName: string, refImg: ImageData, currImg: ImageData}; diff --git a/lib/server-utils.ts b/lib/server-utils.ts index 94f3b09f2..cf60419a2 100644 --- a/lib/server-utils.ts +++ b/lib/server-utils.ts @@ -12,6 +12,7 @@ import type Hermione from 'hermione'; import crypto from 'crypto'; import {ImageHandler, ImagesInfoFormatter} from './image-handler'; import {HermioneTestAdapter} from './test-adapter'; +import {Router} from 'express'; const DATA_FILE_NAME = 'data.js'; @@ -270,7 +271,11 @@ export function getPluginClientScriptPath(pluginName: string): string | null { } } -export function getPluginMiddleware(pluginName: string): unknown | null { +interface PluginMiddleware { + (pluginRouter: Router): unknown; +} + +export function getPluginMiddleware(pluginName: string): PluginMiddleware | null { try { return require(`${pluginName}/middleware.js`); } catch (e: any) { // eslint-disable-line @typescript-eslint/no-explicit-any diff --git a/package-lock.json b/package-lock.json index 471f03cae..aad6de3c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "ora": "^5.4.1", "p-queue": "^5.0.0", "qs": "^6.9.1", - "signal-exit": "^3.0.2", + "signal-exit": "^4.1.0", "strip-ansi": "^6.0.1", "tmp": "^0.1.0", "worker-farm": "^1.7.0" @@ -60,6 +60,7 @@ "@types/enzyme": "^3.10.13", "@types/express": "4.16", "@types/fs-extra": "^7.0.0", + "@types/http-codes": "^1.0.4", "@types/json-stringify-safe": "^5.0.2", "@types/lodash": "^4.14.195", "@types/nested-error-stacks": "^2.1.0", @@ -4355,6 +4356,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "node_modules/@types/http-codes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/http-codes/-/http-codes-1.0.4.tgz", + "integrity": "sha512-LGWkImWfjqxGYPXa+uHNuugnpG2bvjkWfPCpm1E9xb6Te/N3E8vr7dIUuC82JgCny+dl3lQ5domuXkdyHyUvsQ==", + "dev": true + }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -5384,19 +5391,6 @@ "node": ">=8" } }, - "node_modules/@wdio/config/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@wdio/config/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -11223,6 +11217,12 @@ "node": ">=6" } }, + "node_modules/default-gateway/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/default-require-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", @@ -12848,20 +12848,6 @@ "node": ">=8" } }, - "node_modules/devtools/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/devtools/node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -14737,6 +14723,12 @@ "yallist": "^2.1.2" } }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/execa/node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -15458,6 +15450,12 @@ "yallist": "^2.1.2" } }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/foreground-child/node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -18220,18 +18218,6 @@ "node": ">=8" } }, - "node_modules/hermione/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/hermione/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -21389,6 +21375,12 @@ "node": ">=0.10.0" } }, + "node_modules/loud-rejection/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -22853,6 +22845,12 @@ "node": ">=4" } }, + "node_modules/nyc/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -25829,6 +25827,11 @@ "node": ">=8" } }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -26482,9 +26485,15 @@ } }, "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/simple-concat": { "version": "1.0.1", @@ -26947,6 +26956,12 @@ "which": "^1.3.0" } }, + "node_modules/spawn-wrap/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -31488,6 +31503,12 @@ "signal-exit": "^3.0.2" } }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", @@ -34978,6 +34999,12 @@ "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, + "@types/http-codes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/http-codes/-/http-codes-1.0.4.tgz", + "integrity": "sha512-LGWkImWfjqxGYPXa+uHNuugnpG2bvjkWfPCpm1E9xb6Te/N3E8vr7dIUuC82JgCny+dl3lQ5domuXkdyHyUvsQ==", + "dev": true + }, "@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -35754,13 +35781,6 @@ "dev": true, "optional": true }, - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "optional": true - }, "strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -40324,6 +40344,12 @@ "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true } } }, @@ -41544,14 +41570,6 @@ "optional": true, "peer": true }, - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "optional": true, - "peer": true - }, "socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -43002,6 +43020,12 @@ "yallist": "^2.1.2" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -43616,6 +43640,12 @@ "yallist": "^2.1.2" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -45627,12 +45657,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -48080,6 +48104,14 @@ "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + } } }, "lower-case": { @@ -49246,6 +49278,12 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true } } }, @@ -51593,6 +51631,13 @@ "requires": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + } } }, "ret": { @@ -52135,9 +52180,9 @@ } }, "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" }, "simple-concat": { "version": "1.0.1", @@ -52507,6 +52552,14 @@ "rimraf": "^2.6.2", "signal-exit": "^3.0.2", "which": "^1.3.0" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + } } }, "spdx-correct": { @@ -55988,6 +56041,14 @@ "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", "signal-exit": "^3.0.2" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + } } }, "ws": { diff --git a/package.json b/package.json index 8b10631dd..97a059029 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "ora": "^5.4.1", "p-queue": "^5.0.0", "qs": "^6.9.1", - "signal-exit": "^3.0.2", + "signal-exit": "^4.1.0", "strip-ansi": "^6.0.1", "tmp": "^0.1.0", "worker-farm": "^1.7.0" @@ -107,6 +107,7 @@ "@types/enzyme": "^3.10.13", "@types/express": "4.16", "@types/fs-extra": "^7.0.0", + "@types/http-codes": "^1.0.4", "@types/json-stringify-safe": "^5.0.2", "@types/lodash": "^4.14.195", "@types/nested-error-stacks": "^2.1.0", diff --git a/test/unit/lib/gui/api/index.js b/test/unit/lib/gui/api/index.js index 8ba58af75..87e5ee07a 100644 --- a/test/unit/lib/gui/api/index.js +++ b/test/unit/lib/gui/api/index.js @@ -2,7 +2,7 @@ const EventEmitter2 = require('eventemitter2'); const {GuiEvents} = require('lib/gui/constants/gui-events'); -const Api = require('lib/gui/api'); +const {Api} = require('lib/gui/api'); const {stubTool} = require('../../../utils'); describe('lig/gui/api', () => { diff --git a/test/unit/lib/gui/app.js b/test/unit/lib/gui/app.js index 86079422f..61218fca3 100644 --- a/test/unit/lib/gui/app.js +++ b/test/unit/lib/gui/app.js @@ -48,7 +48,7 @@ describe('lib/gui/app', () => { App = proxyquire('lib/gui/app', { 'looks-same': looksSame - }); + }).App; sandbox.stub(ToolRunner, 'create').returns(toolRunner); }); diff --git a/test/unit/lib/gui/routes/plugins.js b/test/unit/lib/gui/routes/plugins.js index 4fe400b17..5f77b6fea 100644 --- a/test/unit/lib/gui/routes/plugins.js +++ b/test/unit/lib/gui/routes/plugins.js @@ -32,7 +32,7 @@ describe('lib/gui/routes/plugins', () => { return router; } } - }); + }).initPluginsRoutes; }); afterEach(() => sandbox.restore()); diff --git a/test/unit/lib/gui/server.js b/test/unit/lib/gui/server.js index 46f249848..7050f1f6e 100644 --- a/test/unit/lib/gui/server.js +++ b/test/unit/lib/gui/server.js @@ -2,7 +2,7 @@ const _ = require('lodash'); const proxyquire = require('proxyquire'); -const App = require('lib/gui/app'); +const {App} = require('lib/gui/app'); const {stubTool} = require('../../utils'); describe('lib/gui/server', () => { @@ -60,47 +60,49 @@ describe('lib/gui/server', () => { Router: () => RouterStub }), 'body-parser': bodyParserStub, - 'signal-exit': sandbox.stub().yields(), + 'signal-exit': {onExit: sandbox.stub().yields()}, '../common-utils': {logger: {log: sandbox.stub()}}, - './routes/plugins': initPluginRoutesStub + './routes/plugins': {initPluginsRoutes: initPluginRoutesStub} }); }); - afterEach(() => sandbox.restore()); + afterEach(() => { + sandbox.restore(); + }); - it('should init server from api', () => { + it('should init server from api', async () => { const guiApi = mkApi_(); - return startServer({guiApi}) - .then(() => { - assert.calledOnceWith(guiApi.initServer, expressStub); - assert.calledOnceWith(guiApi.serverReady, {url: 'http://localhost:4444'}); - }); + await startServer({guiApi}); + + assert.calledOnceWith(guiApi.initServer, expressStub); + assert.calledOnceWith(guiApi.serverReady, {url: 'http://localhost:4444'}); }); - it('should init server only after body is parsed', () => { + it('should init server only after body is parsed', async () => { const guiApi = mkApi_(); - return startServer({guiApi}) - .then(() => assert.callOrder(bodyParserStub.json, guiApi.initServer, guiApi.serverReady)); + await startServer({guiApi}); + + assert.callOrder(bodyParserStub.json, guiApi.initServer, guiApi.serverReady); }); - it('should init server before any static middleware starts', () => { + it('should init server before any static middleware starts', async () => { const guiApi = mkApi_(); - return startServer({guiApi}) - .then(() => assert.callOrder(guiApi.initServer, staticMiddleware, guiApi.serverReady)); + await startServer({guiApi}); + + assert.callOrder(guiApi.initServer, staticMiddleware, guiApi.serverReady); }); - it('should properly complete app working', () => { + it('should properly complete app working', async () => { sandbox.stub(process, 'kill'); - return startServer() - .then(() => { - process.emit('SIGTERM'); + await startServer(); + + process.emit('SIGTERM'); - assert.calledOnce(App.prototype.finalize); - }); + assert.calledOnce(App.prototype.finalize); }); it('should correctly set json replacer', async () => {