UNPKG

omni-dashboard-playwright-reporter

Version:

Playwright client for publishing test results to Omni Dashboard - Transform Your Test Automation with AI-Powered Insights

312 lines (311 loc) 14.1 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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.OmniPlaywrightClient = void 0; const test_1 = require("@playwright/test"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); class OmniPlaywrightClient { constructor(config) { this.config = config; } async getContext() { if (!this.context) { this.context = await test_1.request.newContext({ baseURL: this.config.baseUrl, extraHTTPHeaders: { 'x-api-key': this.config.apiKey, 'Content-Type': 'application/json' } }); } return this.context; } async createBuild(requestBody) { const url = `${this.config.baseUrl}/projects/${this.config.projectId}/builds`; // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Create Build URL:", url); console.log("Create Build Request Payload: ", JSON.stringify(requestBody, null, 2)); } try { const context = await this.getContext(); const response = await context.post(url, { data: requestBody }); const body = await response.json(); // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Create Build Response (%s) %s:", response.status(), JSON.stringify(body, null, 2)); } // Check if the response is successful if (!response.ok()) { const errorMessage = body?.error?.message || `API returned status ${response.status()}`; throw new Error(`Failed to create build: ${errorMessage}`); } const createBuildResponse = body; // Validate response structure if (!createBuildResponse.success || !createBuildResponse.data) { throw new Error(`Invalid response structure: ${JSON.stringify(body)}`); } return createBuildResponse; } catch (error) { console.error("Error creating build:", error.message); throw error; } } async completeBuild(requestBody) { const url = `${this.config.baseUrl}/projects/${this.config.projectId}/builds?build_id=${requestBody.build_id}`; // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Complete Build URL:", url); console.log("Complete Build Request Payload: ", JSON.stringify(requestBody, null, 2)); } try { const context = await this.getContext(); const response = await context.patch(url, { data: requestBody }); const body = await response.json(); // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Complete Build Response (%s) %s:", response.status(), JSON.stringify(body, null, 2)); } // Check if the response is successful if (!response.ok()) { const errorMessage = body?.error?.message || `API returned status ${response.status()}`; throw new Error(`Failed to complete build: ${errorMessage}`); } const completeBuildResponse = body; // Validate response structure if (!completeBuildResponse.success || !completeBuildResponse.data) { throw new Error(`Invalid response structure: ${JSON.stringify(body)}`); } return completeBuildResponse; } catch (error) { console.error("Error completing build:", error.message); throw error; } } async uploadTestCase(test, result, buildId) { const url = `${this.config.baseUrl}/projects/${this.config.projectId}/test-cases`; // Reduced logging for performance - only log URL, not full payload if (process.env.OMNI_DEBUG === 'true') { console.log("Upload TestCase URL:", url); } const payload = this.createTestCasePayload(test, result, buildId); // Skip expensive JSON.stringify in production - only log in debug mode if (process.env.OMNI_DEBUG === 'true') { console.log("Upload TestCase Request Payload: ", JSON.stringify(payload, null, 2)); } try { const context = await this.getContext(); const response = await context.post(url, { data: payload }); const body = await response.json(); // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Upload TestCase Response (%s) %s:", response.status(), JSON.stringify(body, null, 2)); } // Check if the response is successful if (!response.ok()) { const errorMessage = body?.error?.message || `API returned status ${response.status()}`; throw new Error(`Failed to upload test case: ${errorMessage}`); } const uploadTestCaseResponse = body; // Validate response structure if (!uploadTestCaseResponse.success || !uploadTestCaseResponse.data) { throw new Error(`Invalid response structure: ${JSON.stringify(body)}`); } return uploadTestCaseResponse; } catch (error) { console.error("In Upload TestCase. Error uploading test case:", error.message); throw error; } } mapStatus(status) { if (status === 'timedOut' || status === 'interrupted' || status === 'failed') return 'failed'; if (status === 'skipped') return 'skipped'; return 'passed'; } createTestCasePayload(test, result, buildId) { const title = test.title; const test_status = this.mapStatus(result.status); // Create steps from test steps const test_steps = result.steps.map((step, index) => ({ name: step.title, sequence_number: index + 1, duration: Math.max(0, step.duration || 0), // Ensure duration is never negative status: step.error ? 'failed' : 'passed', error_message: step.error?.message, stack_trace: step.error?.stack })); // Create stdout entries const stdout = [{ timestamp: new Date().toISOString(), level: test_status === 'passed' ? 'INFO' : 'ERROR', message: `${title} ${test_status}` }]; // Create screenshots entries const screenshots = result.attachments .filter((att) => att.contentType === 'image/png' && att.path) .map((att) => ({ name: att.path, timestamp: new Date().toISOString() })); // Create traces entries const traces = result.attachments .filter((att) => att.name === 'trace' && att.contentType === 'application/zip') .map((att) => ({ name: att.path })); // Create annotations from test annotations const annotations = result.annotations.map((ann) => ({ type: ann.type, description: ann.description })); const singleTestCase = { name: title, module: annotations.find((ann) => ann.type === 'module')?.description || 'Others', priority: annotations.find((ann) => ann.type === 'priority')?.description, tags: test.tags?.map((tag) => tag.replace(/^@/, '')) || [], status: test_status, duration: Math.max(0, result.duration || 0), // Ensure duration is never negative test_steps, stdout, screenshots, traces, annotations, error_message: result.error?.message, error_stack_trace: result.error?.stack }; return { build_id: buildId, test_cases: [singleTestCase] }; } async uploadScreenshots(test_case) { if (test_case.screenshots_s3_paths?.length > 0) { // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Screenshots to be uploaded:", test_case.screenshots_s3_paths.length); } // Use Promise.allSettled to continue even if some uploads fail await Promise.allSettled(test_case.screenshots_s3_paths.map(async (screenshot) => { if (process.env.OMNI_DEBUG === 'true') { console.log("Uploading Screenshot:", screenshot.local_path); } try { const filePath = path.resolve(screenshot.local_path); const fileData = await fs.promises.readFile(filePath); if (process.env.OMNI_DEBUG === 'true') { console.log("Upload URL:", screenshot.upload_url); } const response = await fetch(screenshot.upload_url, { method: 'PUT', headers: { 'Content-Type': 'image/png', 'Content-Length': fileData.length.toString() }, body: fileData }); if (process.env.OMNI_DEBUG === 'true') { console.log('Uploaded screenshot:', screenshot.name, 'Status:', response.status); } if (!response.ok) { const errorText = await response.text(); console.error('S3 upload failed:', errorText); throw new Error(`S3 upload failed with status ${response.status}: ${errorText}`); } } catch (uploadErr) { // Log error but don't throw - allow other screenshots to upload console.error('Failed to upload screenshot:', screenshot.name, uploadErr?.message || uploadErr); // Don't re-throw to prevent blocking other uploads } })); } } async uploadTraces(test_case) { if (test_case.traces_s3_paths?.length > 0) { // Reduced logging for performance if (process.env.OMNI_DEBUG === 'true') { console.log("Traces to be uploaded:", test_case.traces_s3_paths.length); } // Use Promise.allSettled to continue even if some uploads fail await Promise.allSettled(test_case.traces_s3_paths.map(async (trace) => { if (process.env.OMNI_DEBUG === 'true') { console.log("Uploading Trace:", trace.name); console.log("Uploading Trace Path:", trace.local_path); } try { const filePath = path.resolve(trace.local_path); const fileData = await fs.promises.readFile(filePath); if (process.env.OMNI_DEBUG === 'true') { console.log("Upload URL:", trace.upload_url); } const response = await fetch(trace.upload_url, { method: 'PUT', headers: { 'Content-Type': 'application/zip', 'Content-Length': fileData.length.toString() }, body: fileData }); if (process.env.OMNI_DEBUG === 'true') { console.log('Uploaded trace:', trace.name, 'Status:', response.status); } if (!response.ok) { const errorText = await response.text(); console.error('S3 upload failed:', errorText); throw new Error(`S3 upload failed with status ${response.status}: ${errorText}`); } } catch (uploadErr) { // Log error but don't throw - allow other traces to upload console.error('Failed to upload trace:', trace.name, uploadErr?.message || uploadErr); // Don't re-throw to prevent blocking other uploads } })); } } } exports.OmniPlaywrightClient = OmniPlaywrightClient;