UNPKG

html-reporter

Version:

Html-reporter and GUI for viewing and managing results of a tests run. Currently supports Testplane and Hermione.

267 lines 11.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.start = void 0; const path_1 = __importDefault(require("path")); const express_1 = __importDefault(require("express")); const signal_exit_1 = require("signal-exit"); const bluebird_1 = __importDefault(require("bluebird")); const body_parser_1 = __importDefault(require("body-parser")); const http_codes_1 = require("http-codes"); const app_1 = require("./app"); const constants_1 = require("./constants"); const common_utils_1 = require("../common-utils"); const plugins_1 = require("./routes/plugins"); const constants_2 = require("../constants"); const server_utils_1 = require("../server-utils"); const constants_3 = require("../constants"); const originalBrowserConfigs = new Map(); const start = async (args) => { const { toolAdapter } = args; const { reporterConfig, guiApi } = toolAdapter; if (!guiApi) { throw new Error('Gui API must be initialized before starting gui server'); } const app = app_1.App.create(args); const server = (0, express_1.default)(); server.use(body_parser_1.default.json({ limit: constants_1.MAX_REQUEST_SIZE })); await guiApi.initServer(server); // allow plugins to precede default server routes server.use((0, plugins_1.initPluginsRoutes)(express_1.default.Router(), reporterConfig)); server.use(express_1.default.static(path_1.default.join(__dirname, '../static'), { index: 'gui.html' })); server.use(express_1.default.static(path_1.default.join(process.cwd(), reporterConfig.path))); server.get('/', (_req, res) => res.sendFile(path_1.default.join(__dirname, '../static', 'gui.html'))); server.get('/new-ui', (_req, res) => res.sendFile(path_1.default.join(__dirname, '../static', 'new-ui-gui.html'))); server.get('/ui-mode', (_req, res) => { try { const uiMode = reporterConfig.uiMode || null; res.json({ uiMode }); } catch (e) { res.json({ uiMode: null }); console.error(`Error while getting UI config. You may report this at: ${constants_3.NEW_ISSUE_LINK}`); console.error(e); } }); server.get('/events', (_req, res) => { res.writeHead(http_codes_1.OK, { 'Content-Type': 'text/event-stream' }); app.addClient(res); }); server.set('json replacer', (_key, val) => { return typeof val === 'function' ? val.toString() : val; }); server.get('/init', async (_req, res) => { try { if (toolAdapter.toolName === constants_2.ToolName.Testplane) { await toolAdapter.initGuiHandler(); } res.json(app.data); } catch (e) { const error = e; if (!app.data) { throw new Error(`Failed to initialize custom GUI ${error.message}`); } res.json({ ...app.data, customGuiError: { response: { status: http_codes_1.INTERNAL_SERVER_ERROR, data: `Error while trying to initialize custom GUI: ${error.message}` } } }); } }); server.post('/update-time-travel-settings', (req, res) => { try { if (toolAdapter.toolName !== constants_2.ToolName.Testplane) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).json({ error: { message: 'Time travel configuration is only supported for Testplane' } }); return; } const { useRecommendedSettings } = req.body; const tpAdapter = toolAdapter; const tpConfig = tpAdapter.config; const TimeTravelMode = (0, server_utils_1.getTimeTravelModeEnumSafe)(); if (!TimeTravelMode) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).json({ error: { message: 'Time Travel is not supported in this version of Testplane' } }); return; } if (useRecommendedSettings) { for (const browserId of tpConfig.browserIds) { const browserConfig = tpConfig.getBrowserConfig(browserId); if (!originalBrowserConfigs.has(browserId)) { originalBrowserConfigs.set(browserId, { timeTravel: browserConfig.timeTravel, saveHistoryMode: browserConfig.saveHistoryMode }); } browserConfig.timeTravel = { mode: TimeTravelMode.On }; browserConfig.saveHistoryMode = 'all'; } } else { for (const browserId of tpConfig.browserIds) { const browserConfig = tpConfig.getBrowserConfig(browserId); const originalConfig = originalBrowserConfigs.get(browserId); if (originalConfig && originalConfig.timeTravel && originalConfig.saveHistoryMode) { browserConfig.timeTravel = originalConfig.timeTravel; browserConfig.saveHistoryMode = originalConfig.saveHistoryMode; } } } res.status(http_codes_1.OK).json({ data: { browserFeatures: tpAdapter.browserFeatures } }); } catch (e) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).json({ error: { message: `Error updating time travel config: ${e.message}` } }); } }); server.post('/run', (req, res) => { try { // do not wait for completion so that response does not hang and browser does not restart it by timeout app.run(req.body); res.sendStatus(http_codes_1.OK); } catch (e) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).send(`Error while trying to run tests: ${e.message}`); } }); server.post('/run-custom-gui-action', async ({ body: payload }, res) => { try { if (toolAdapter.toolName === constants_2.ToolName.Testplane) { await toolAdapter.runCustomGuiAction(payload); } res.sendStatus(http_codes_1.OK); } catch (e) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).send(`Error while running custom gui action: ${e.message}`); } }); server.post('/reference-data-to-update', (req, res) => { try { const data = app.getTestsDataToUpdateRefs(req.body); res.json(data); } catch (error) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).send({ error: error.message }); } }); server.post('/update-reference', (req, res) => { app.updateReferenceImage(req.body) .then((updatedTests) => res.json(updatedTests)) .catch(({ message }) => res.status(http_codes_1.INTERNAL_SERVER_ERROR).send({ error: message })); }); server.post('/undo-accept-images', (req, res) => { app.undoAcceptImages(req.body) .then((updated) => res.json(updated)) .catch(({ message }) => res.status(http_codes_1.INTERNAL_SERVER_ERROR).send({ error: message })); }); server.post('/get-find-equal-diffs-data', (req, res) => { try { const data = app.getImageDataToFindEqualDiffs(req.body); res.json(data); } catch (e) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).send({ error: e.message }); } }); server.post('/find-equal-diffs', async (req, res) => { try { const result = await app.findEqualDiffs(req.body); res.json(result); } catch (e) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).send({ error: e.message }); } }); server.get('/running-test-data', async (req, res) => { if (toolAdapter.toolName !== constants_2.ToolName.Testplane) { res.status(500).json({ error: `Getting running test data supports only in Testplane tool` }); return; } try { const { testPath, browserId } = req.query; const { getSnapshotHashWithoutAttempt, snapshotsInProgress } = await Promise.resolve().then(() => __importStar(require('../adapters/event-handling/testplane/snapshots'))); if (!testPath || !browserId) { res.status(400).json({ error: `Missing one of the required GET parameters: testPath or browserId. Received testPath: ${testPath}, browserId: ${browserId}` }); } let parsedTestPath; try { parsedTestPath = JSON.parse(testPath); if (!Array.isArray(parsedTestPath)) { throw new Error('testPath must be a JSON string with an array'); } } catch (error) { res.status(400).json({ error: 'Invalid testPath format' }); return; } const context = { testPath: parsedTestPath, browserId: browserId }; const snapshotKey = getSnapshotHashWithoutAttempt(context); const snapshots = snapshotsInProgress[snapshotKey] || []; res.json({ rrwebSnapshots: snapshots }); } catch (error) { res.status(500).json({ error: `Error while getting running test data: ${error.message}` }); } }); (0, signal_exit_1.onExit)(() => { app.finalize(); common_utils_1.logger.log('server shutting down'); }); server.post('/stop', (_req, res) => { try { // pass 0 to prevent terminating testplane process toolAdapter.halt(new Error('Tests were stopped by the user'), 0); res.sendStatus(http_codes_1.OK); } catch (e) { res.status(http_codes_1.INTERNAL_SERVER_ERROR).send(`Error while stopping tests: ${e.message}`); } }); await app.initialize(); const { port, hostname } = args.cli.options; await bluebird_1.default.fromCallback((callback) => { const httpServer = server.listen(port, hostname, callback); httpServer.keepAliveTimeout = constants_1.KEEP_ALIVE_TIMEOUT; httpServer.headersTimeout = constants_1.HEADERS_TIMEOUT; }); const data = { url: `http://${hostname}:${port}` }; await guiApi.serverReady(data); return data; }; exports.start = start; //# sourceMappingURL=server.js.map