UNPKG

@applitools/eyes-playwright

Version:
208 lines (207 loc) 9.35 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; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InternalData = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const req_1 = require("@applitools/req"); const utils = __importStar(require("@applitools/utils")); const logger_1 = require("@applitools/logger"); const storage_blob_1 = require("@azure/storage-blob"); const playwrightPath = require.resolve('playwright'); const HtmlReporter = require(path.join(path.dirname(playwrightPath), '/lib/reporters/html')).default; const pluginsFile = require.resolve('../../dist/fixture/reportRenderer.js'); const styleFile = require.resolve('./reporterStyle.css'); const createInjectedScript = (testResultsMap) => ` <script>window.__icons = { visualTest: \`${fs.readFileSync(require.resolve('./images/visual-text.svg'), 'utf-8')}\`, link: \`${fs.readFileSync(require.resolve('./images/link.svg'), 'utf-8')}\`, }</script> <script> ${fs.readFileSync(pluginsFile, 'utf-8')} window.__testResultsMap = ${JSON.stringify(testResultsMap)}; window.__initEyesReport(); </script> <style type="text/css">${fs.readFileSync(styleFile, 'utf-8')}</style> `; class EyesReporter extends HtmlReporter { constructor(options) { super(options); this.applitoolsIdentifiers = []; } onTestEnd(test, result) { var _a; (_a = super.onTestEnd) === null || _a === void 0 ? void 0 : _a.call(this, test, result); const index = result.attachments.findIndex(a => a.name === 'applitoolsIdentifier'); if (index > -1) { const applitoolsIdentifier = result.attachments[index].body.toString(); this.applitoolsIdentifiers.push(applitoolsIdentifier); this.removeInternalIdAttachment(result); } } // TODO: let's remove this method! Removing the internal id from the test result is wrong, // it should be hidden in the HTML report and the test result should be read-only. removeInternalIdAttachment(result) { var _a, _b, _c; const index = result.attachments ? result.attachments.findIndex(a => (a === null || a === void 0 ? void 0 : a.name) === 'applitoolsIdentifier') : -1; if (index > -1) (_a = result.attachments) === null || _a === void 0 ? void 0 : _a.splice(index, 1); const index2 = result.attachments ? result.attachments.findIndex(a => !a) : -1; if (index2 > -1) (_c = (_b = result._endPayload) === null || _b === void 0 ? void 0 : _b.attachments) === null || _c === void 0 ? void 0 : _c.splice(index2, 1); if (result.steps) result.steps.forEach(step => this.removeInternalIdAttachment(step)); } async onEnd(result) { await super.onEnd(result); const testResultsMap = {}; for (const applitoolsIdentifier of this.applitoolsIdentifiers) { // Parse the identifier format: uuid|serverUrl|apiKey const [uuid, serverUrl, apiKey] = applitoolsIdentifier.split('|'); if (uuid && serverUrl && apiKey) { // Create a minimal eyes object for the consume call const eyes = { getServerUrl: () => serverUrl, getApiKey: () => apiKey, // No getLogger method - will use makeLogger() fallback }; const content = await exports.InternalData.consume(uuid, eyes); if (content) { testResultsMap[content.key] = content.data; } } } try { const filePath = path.join(this._outputFolder, 'index.html'); fs.appendFileSync(filePath, createInjectedScript(testResultsMap)); } catch (e) { // eslint-disable-next-line no-console console.error('Error modifying index.html:', e); } } } exports.default = EyesReporter; const getLogger = utils.general.cachify((settings) => { return settings.logger || (0, logger_1.makeLogger)({ level: 'info' }); }, ([settings]) => settings.serverUrl + settings.apiKey); const makeReporterRequest = (settings) => { var _a; const logger = getLogger(settings); const req = (0, req_1.makeReq)({ baseUrl: (_a = settings.serverUrl) !== null && _a !== void 0 ? _a : 'https://eyes.applitools.com', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'x-applitools-eyes-client': 'eyes-playwright-reporter', 'User-Agent': 'eyes-playwright-reporter', 'X-Eyes-Api-Key': settings.apiKey, }, retry: [ { limit: 3, timeout: 200, }, ], hooks: { beforeRequest: request => { var _a; logger.log(`Making request to ${request.request.url} with body: ${(_a = request.options) === null || _a === void 0 ? void 0 : _a.body}`); }, afterResponse: async (response) => { logger.log(`Received response from ${response.response.url} with status ${response.response.status} and body: ${await response.response.clone().text()}`); if (response.response.status >= 400) { logger.error(`Request to ${response.response.url} failed with status ${response.response.status}`, `and body: ${await response.response.clone().text()}`); throw new Error(`Request to ${response.response.url} failed with status ${response.response.status}`); } }, }, }); return { upload, download, }; async function upload(uuid, data) { const blobClient = new storage_blob_1.BlockBlobClient(settings.uploadUrl); const [response1, response2] = await Promise.all([ req(`/batch/${uuid}/report/${uuid}`, { method: 'POST', }), blobClient.upload(JSON.stringify(data), JSON.stringify(data).length), ]); return { respCreateUpload: await response1.json(), respUpload: JSON.stringify(response2), }; } async function download(uuid) { const response = await req(`/batch/${uuid}/report/${uuid}`, { method: 'GET', }); return response.json(); } }; exports.InternalData = { async write({ testInfo, data, eyes, uuid }) { var _a, _b; const logger = ((_a = eyes.getLogger) === null || _a === void 0 ? void 0 : _a.call(eyes)) || (0, logger_1.makeLogger)(); const uploadUrl = eyes._eyes.test.account.batchExecReportsUrl .replace('__batch_id__', uuid) .replace('__report_id__', uuid); const { upload } = makeReporterRequest({ serverUrl: (_b = eyes.getServerUrl()) !== null && _b !== void 0 ? _b : 'https://eyes.applitools.com', apiKey: eyes.getApiKey(), uploadUrl, logger: logger, }); const response = await upload(uuid, { key: `${testInfo.testId}--${testInfo.retry}`, data, }); logger.log('Data written successfully:', response); }, async consume(uuid, eyes) { var _a; const logger = ((_a = eyes.getLogger) === null || _a === void 0 ? void 0 : _a.call(eyes)) || (0, logger_1.makeLogger)(); const { download } = makeReporterRequest({ serverUrl: eyes.getServerUrl(), apiKey: eyes.getApiKey(), logger: logger, }); for (let retry = 0; retry < 3; retry++) { try { const response = await download(uuid); if (utils.types.has(response, 'status') && response.status !== 200) { throw new Error(`Failed to consume data for UUID ${uuid}: ${response.status} - ${JSON.stringify(response)}`); } return { key: response.key, data: response.data }; } catch (error) { logger.warn('Failed to consume data:', error, ". Perhaps the test doesn't include Applitools tests?"); } await utils.general.sleep(1000 * (retry + 1)); // the backend might need a few seconds to process the upload } }, };