UNPKG

prettier-playwright-msteams-report

Version:
299 lines (298 loc) 12.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 (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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.processResults = void 0; const utils_1 = require("./utils"); const constants_1 = require("./constants"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const quickchart_js_1 = __importDefault(require("quickchart-js")); // Function to generate the donut chart function generateDonutChart(passed, failed, skipped, flaky) { const chart = new quickchart_js_1.default(); chart.setConfig({ type: 'outlabeledPie', data: { labels: ['Passed', 'Failed', 'Skipped', 'Flaky'], datasets: [{ backgroundColor: ['#4CAF50', '#F44336', '#8f8f8f', '#FF9800'], data: [passed, failed, skipped, flaky] }] }, options: { plugins: { legend: false, outlabels: { text: '%l %p', color: 'black', stretch: 35, font: { resizable: true, minSize: 12, maxSize: 18 } } } } }); return chart.getUrl(); } const processResults = (suite, options) => __awaiter(void 0, void 0, void 0, function* () { if (!options.webhookUrl) { console.error("No webhook URL provided"); return; } if (!(0, utils_1.validateWebhookUrl)(options.webhookUrl, options.webhookType)) { console.error("Invalid webhook URL"); return; } if (!suite) { console.error("No test suite found"); return; } if (options.shouldRun && !(options === null || options === void 0 ? void 0 : options.shouldRun(suite))) return; // Dynamically determine the report path const reportPath = path.join(process.cwd(), 'ctrf', 'ctrf-report.json'); const report = readCtrfReport(reportPath); const testResults = extractTestResults(report); const totalStatus = (0, utils_1.getTotalStatus)(suite.suites); const totalTests = suite.allTests().length; const isSuccess = totalStatus.failed === 0; if (isSuccess && !options.notifyOnSuccess) { if (!options.quiet) { console.log("No failed tests, skipping notification"); } return; } // Generate the donut chart const donutChartUrl = generateDonutChart(totalStatus.passed, totalStatus.failed, totalStatus.skipped, totalStatus.flaky); // Clone the base adaptive card and table const adaptiveCard = structuredClone(constants_1.BaseAdaptiveCard); const table = structuredClone(constants_1.BaseTable); // Populate the table with test results table.columns.push({ width: 1 }); table.rows.push((0, utils_1.createTableRow)("Type", "Total", "Percentage")); table.rows.push((0, utils_1.createTableRow)("✅ Passed", totalStatus.passed, `${((totalStatus.passed / totalTests) * 100).toFixed(1)}%`)); if (totalStatus.flaky) { table.rows.push((0, utils_1.createTableRow)("⚠️ Flaky", totalStatus.flaky, `${((totalStatus.flaky / totalTests) * 100).toFixed(1)}%`, { style: "warning" })); } table.rows.push((0, utils_1.createTableRow)("❌ Failed", totalStatus.failed, `${((totalStatus.failed / totalTests) * 100).toFixed(1)}%`, { style: "attention" })); table.rows.push((0, utils_1.createTableRow)("⏭️ Skipped", totalStatus.skipped, `${((totalStatus.skipped / totalTests) * 100).toFixed(1)}%`, { style: "accent" })); table.rows.push((0, utils_1.createTableRow)("Total tests", totalTests, "100.0%", { isSubtle: true, weight: "Bolder" })); // Add the table to the card const container = { type: "Container", items: [ { type: "TextBlock", size: "ExtraLarge", weight: "Bolder", text: options.title, }, { type: "TextBlock", size: "Large", weight: "Bolder", text: (0, utils_1.getNotificationTitle)(totalStatus), color: (0, utils_1.getNotificationColor)(totalStatus), }, table, ], bleed: true, backgroundImage: { url: (0, utils_1.getNotificationBackground)(totalStatus), fillMode: "RepeatHorizontally", }, style: "emphasis", // Set the background color to black }; // Group test details by status const groupedTestDetails = { passed: [], failed: [], skipped: [], flaky: [] }; testResults.forEach(test => { if (test.status === 'passed') groupedTestDetails.passed.push(test); else if (test.status === 'failed') groupedTestDetails.failed.push(test); else if (test.status === 'skipped') groupedTestDetails.skipped.push(test); else if (test.status === 'flaky') groupedTestDetails.flaky.push(test); }); // Add grouped test details to the container Object.keys(groupedTestDetails).forEach(status => { if (groupedTestDetails[status].length > 0) { container.items.push({ type: "TextBlock", text: `${status.charAt(0).toUpperCase() + status.slice(1)} Tests`, weight: "Bolder", size: "Medium" }); groupedTestDetails[status].forEach(test => { container.items.push({ type: "Container", items: [ { type: "TextBlock", text: `${test.title}`, weight: "Bolder", color: test.status === "passed" ? "Default" : (test.status === "failed" ? "Attention" : "Default") }, { type: "TextBlock", text: `${test.status === "passed" ? "✅" : (test.status === "failed" ? "❌" : "⏭️")} ${test.status} - ${test.duration}ms`, spacing: "None", color: test.status === "passed" ? "Default" : (test.status === "failed" ? "Attention" : "Default") } ], style: test.status === "failed" ? "attention" : (test.status === "skipped" ? "default" : "default") }); }); } }); // Add the container to the body adaptiveCard.body.push(container); // Add the donut chart to the adaptive card adaptiveCard.body.push({ type: "Image", url: donutChartUrl, size: "ExtraLarge", // Change size to ExtraLarge selectAction: { type: "Action.OpenUrl", url: donutChartUrl // Make the image clickable } }); // Check if we should ping on failure if (!isSuccess) { const mentionData = (0, utils_1.getMentions)(options.mentionOnFailure, options.mentionOnFailureText); if ((mentionData === null || mentionData === void 0 ? void 0 : mentionData.message) && mentionData.mentions.length > 0) { adaptiveCard.body.push({ type: "TextBlock", size: "Medium", text: mentionData.message, wrap: true, }); adaptiveCard.msteams.entities = mentionData.mentions.map((mention) => ({ type: "mention", text: `<at>${mention.email}</at>`, mentioned: { id: mention.email, name: mention.name, }, })); } } // Add action buttons if (options.linkToResultsUrl) { const linkToResultsUrl = typeof options.linkToResultsUrl === "string" ? options.linkToResultsUrl : options.linkToResultsUrl(); if (linkToResultsUrl) { adaptiveCard.actions.push({ type: "Action.OpenUrl", title: options.linkToResultsText, url: linkToResultsUrl, }); } } if (!isSuccess && options.linkTextOnFailure && options.linkUrlOnFailure) { const linkUrlOnFailure = typeof options.linkUrlOnFailure === "string" ? options.linkUrlOnFailure : options.linkUrlOnFailure(); if (linkUrlOnFailure) { adaptiveCard.actions.push({ type: "Action.OpenUrl", title: options.linkTextOnFailure, url: linkUrlOnFailure, }); } } if (options.webhookType === "powerautomate") { adaptiveCard.version = "1.4"; } const body = JSON.stringify({ type: "message", attachments: [ { contentType: "application/vnd.microsoft.card.adaptive", contentUrl: null, content: adaptiveCard, }, ], }); if (options.debug) { console.log("Sending the following message:"); console.log(body); } const response = yield fetch(options.webhookUrl, { method: "POST", headers: { "Content-Type": "application/json", }, body, }); if (response.ok) { if (!options.quiet) { console.log("Message sent successfully"); const responseText = yield response.text(); if (responseText !== "1") { console.log(responseText); } } } else { console.error("Failed to send message"); console.error(yield response.text()); } }); exports.processResults = processResults; function readCtrfReport(filePath) { const data = fs.readFileSync(filePath, 'utf8'); return JSON.parse(data); } function extractTestResults(report) { // Access the correct path to the tests if (!report.results || !report.results.tests) { console.error("report.results.tests is undefined"); return []; } return report.results.tests.map((test) => ({ title: test.name, status: test.status, duration: test.duration })); }