Skip to content

Commit

Permalink
chore: rewrite gui server to typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
shadowusr committed Nov 20, 2023
1 parent 3dd3959 commit 6477f53
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 150 deletions.
16 changes: 0 additions & 16 deletions lib/gui/api/facade.js

This file was deleted.

16 changes: 16 additions & 0 deletions lib/gui/api/facade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import EventEmitter2 from 'eventemitter2';
import {GuiEvents} from '../constants';

export class ApiFacade extends EventEmitter2 {
events: GuiEvents;

static create<T extends ApiFacade>(this: new () => T): T {
return new this();
}

constructor() {
super();

this.events = GuiEvents;
}
}
21 changes: 0 additions & 21 deletions lib/gui/api/index.js

This file was deleted.

31 changes: 31 additions & 0 deletions lib/gui/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {ApiFacade} from './facade';
import Hermione from 'hermione';
import {Express} from 'express';

interface GuiFacade {
gui: ApiFacade;
}

export interface ServerReadyData {
url: string;
}

export class Api {
private _gui: ApiFacade;

static create<T extends Api>(this: new (hermione: Hermione & GuiFacade) => T, hermione: Hermione & GuiFacade): T {
return new this(hermione);
}

constructor(hermione: Hermione & GuiFacade) {
this._gui = hermione.gui = ApiFacade.create();
}

async initServer(server: Express): Promise<void> {
await this._gui.emitAsync(this._gui.events.SERVER_INIT, server);
}

async serverReady(data: ServerReadyData): Promise<void> {
await this._gui.emitAsync(this._gui.events.SERVER_READY, data);
}
}
14 changes: 9 additions & 5 deletions lib/gui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
}

Expand Down
86 changes: 46 additions & 40 deletions lib/gui/server.js → lib/gui/server.ts
Original file line number Diff line number Diff line change
@@ -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<ServerReadyData> => {
const {options, pluginConfig} = configs;
const app = App.create(paths, hermione, configs);
const server = express();
Expand All @@ -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) => {
Expand All @@ -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}`);
}
});

Expand All @@ -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}`);
}
});

Expand All @@ -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});
}
});

Expand All @@ -99,17 +105,17 @@ 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});
}
});

server.post('/find-equal-diffs', async (req, res) => {
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});
}
});

Expand All @@ -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;
});
Expand Down
Loading

0 comments on commit 6477f53

Please sign in to comment.