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
JavaScript
;
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