prettier-playwright-msteams-report
Version:
A modified version of the Playwright MS Teams Messager
299 lines (298 loc) • 12.1 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 __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
}));
}