Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions src/help.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import dayjs from 'dayjs';
import { XrayCloudStatus } from './types/cloud.types';
import { XrayServerStatus } from './types/server.types';
import dayjs from "dayjs";
import { XrayCloudStatus } from "./types/cloud.types";
import { XrayServerStatus } from "./types/server.types";

class Help {
public jiraType = '';
public jiraType = "";
constructor(jiraType: string) {
this.jiraType = jiraType;
}

convertPwStatusToXray(status: string): string {
switch (this.jiraType) {
case 'cloud':
case "cloud":
return XrayCloudStatus[status];
case 'server':
case "server":
return XrayServerStatus[status];
default:
return '';
return "";
}
}

getFormatData(date: Date) {
if (this.jiraType === 'cloud') {
if (this.jiraType === "cloud") {
return date.toISOString();
}
const d = dayjs(date);
Expand All @@ -36,11 +36,11 @@ class Help {
minutes = minutes % 60;
hours = hours % 24;

let out = '';
let out = "";

out += hours.toString() !== '0' ? `${hours.toString()}h ` : '';
out += minutes.toString() !== '0' ? `${minutes.toString()}m ` : '';
out += seconds.toString() !== '0' ? `${seconds.toString()}s ` : '';
out += hours.toString() !== "0" ? `${hours.toString()}h ` : "";
out += minutes.toString() !== "0" ? `${minutes.toString()}m ` : "";
out += seconds.toString() !== "0" ? `${seconds.toString()}s ` : "";

return out;
}
Expand Down
106 changes: 57 additions & 49 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import type { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
import { blue, bold, green, red, white, yellow } from 'picocolors';
import type { XrayTest, XrayTestEvidence, XrayTestResult, XrayTestSteps } from './types/cloud.types';
import type { XrayOptions } from './types/xray.types';
import * as fs from "node:fs";
import * as path from "node:path";
import type { FullConfig, Reporter, Suite, TestCase, TestResult, TestStep } from "@playwright/test/reporter";
import type { XrayTest, XrayTestEvidence, XrayTestResult, XrayTestSteps } from "./types/cloud.types";
import type { XrayOptions } from "./types/xray.types";

import Help from './help';
import type { ExecInfo } from './types/execInfo.types';
import { XrayService } from './xray.service';
import Help from "./help";
import type { ExecInfo } from "./types/execInfo.types";
import { XrayService } from "./xray.service";
import { logger } from "./logger";

class XrayReporter implements Reporter {
private xrayService!: XrayService;
Expand All @@ -22,7 +22,7 @@ class XrayReporter implements Reporter {
private uploadScreenShot: boolean | undefined;
private uploadTrace: boolean | undefined;
private uploadVideo: boolean | undefined;
private stepCategories = ['expect', 'pw:api', 'test.step'];
private stepCategories = ["expect", "pw:api", "test.step"];

constructor(options: XrayOptions) {
this.options = options;
Expand All @@ -49,45 +49,49 @@ class XrayReporter implements Reporter {
tests: [] as XrayTest[],
};
this.testResults = testResults;
console.log(`${bold(blue('-------------------------------------'))}`);
console.log(`${bold(blue(' '))}`);
logger.separator();
//console.log(`${bold(blue("-------------------------------------"))}`);
//console.log(`${bold(blue(" "))}`);
if (this.options.summary !== undefined) testResults.info.summary = this.options.summary;
this.execInfo = {
browserName: '',
browserName: "",
};
}

onBegin(config: FullConfig, suite: Suite) {
config.projects.forEach((p, index) => {
this.execInfo.browserName += index > 0 ? ', ' : '';
this.execInfo.browserName += index > 0 ? ", " : "";
this.execInfo.browserName += p.name.charAt(0).toUpperCase() + p.name.slice(1);
});
if (this.options.dryRun) {
console.log(`${bold(yellow('⏺ '))}${bold(blue(`Starting a Dry Run with ${suite.allTests().length} tests`))}`);
logger.info(`Starting a Dry Run with ${suite.allTests().length} tests`);
//console.log(`${bold(yellow("⏺ "))}${bold(blue(`Starting a Dry Run with ${suite.allTests().length} tests`))}`);
} else {
console.log(`${bold(yellow('⏺ '))}${bold(blue(`Starting the run with ${suite.allTests().length} tests`))}`);
logger.info(`Starting the run with ${suite.allTests().length} tests`);
//console.log(`${bold(yellow("⏺ "))}${bold(blue(`Starting the run with ${suite.allTests().length} tests`))}`);
}

console.log(`${bold(blue(' '))}`);
logger.logEmptyLine();
//console.log(`${bold(blue(" "))}`);
}

async onTestBegin(test: TestCase) {
if (this.execInfo.testedBrowser === undefined) {
this.execInfo.testedBrowser = test.parent.parent?.title;
console.log(
`${bold(yellow('⏺ '))}${bold(blue(`The following test execution will be imported & reported: ${this.execInfo.testedBrowser}`))}`,
);
logger.info(`The following test execution will be imported & reported: ${this.execInfo.testedBrowser}`);
/* console.log(
`${bold(yellow("⏺ "))}${bold(blue(`The following test execution will be imported & reported: ${this.execInfo.testedBrowser}`))}`,
);*/
}
}
async onTestEnd(testCase: TestCase, result: TestResult) {
const testCaseId = testCase.title.match(this.testCaseKeyPattern);
const testCode: string = testCaseId?.[1] ?? '';
const testCode: string = testCaseId?.[1] ?? "";
const projectId = JSON.stringify(testCase.parent.project()).match(/__projectId":"(.*)"/)?.[1];
if (this.execInfo.testedBrowser !== projectId) {
return;
}

if (testCode !== '') {
if (testCode !== "") {
// @ts-ignore
const finishTime = new Date(result.startTime.getTime() + result.duration);
this.totalDuration = this.totalDuration + result.duration;
Expand All @@ -98,29 +102,29 @@ class XrayReporter implements Reporter {
start: this.help.getFormatData(result.startTime),
finish: this.help.getFormatData(finishTime),
steps: [] as XrayTestSteps[],
comment: '',
comment: "",
};

// Set Test Error
const pwStepsExists = result.steps.some((step) => step.category.includes('test.step'));
const pwStepsExists = result.steps.some((step) => step.category.includes("test.step"));
if (result.errors.length > 0 && !pwStepsExists) {
xrayTestData.comment = this.stripAnsi(JSON.stringify(result.errors).replace(/\\\\/g, '\\'));
xrayTestData.comment = this.stripAnsi(JSON.stringify(result.errors).replace(/\\\\/g, "\\"));
} else {
await Promise.all(
result.steps.map(async (step) => {
if (this.stepCategories.some((type) => type.includes(step.category))) {
// Add Step to request
const errorMessage = this.stripAnsi(step.error?.stack?.valueOf() as string);
const received = errorMessage ? this.receivedRegEx.exec(errorMessage) : null;
let dataReceived = '';
let dataReceived = "";
if (received?.[1] !== undefined) {
dataReceived = received?.[1];
}

const xrayTestStep: XrayTestSteps = {
status:
typeof step.error === 'object' ? this.help.convertPwStatusToXray('failed') : this.help.convertPwStatusToXray('passed'),
comment: typeof step.error === 'object' ? errorMessage : '',
typeof step.error === "object" ? this.help.convertPwStatusToXray("failed") : this.help.convertPwStatusToXray("passed"),
comment: typeof step.error === "object" ? errorMessage : "",
actualResult: dataReceived,
};
xrayTestData.steps?.push(xrayTestStep);
Expand All @@ -133,38 +137,41 @@ class XrayReporter implements Reporter {
const evidences: XrayTestEvidence[] = [];
if (result.attachments.length > 0) {
result.attachments.map(async (attach) => {
if (attach.name.includes('screenshot') && this.uploadScreenShot) {
if (attach.name.includes("screenshot") && this.uploadScreenShot) {
await this.addEvidence(attach, evidences);
}
if (attach.name.includes('trace') && this.uploadTrace) {
if (attach.name.includes("trace") && this.uploadTrace) {
await this.addEvidence(attach, evidences);
}
if (attach.name.includes('video') && this.uploadVideo) {
if (attach.name.includes("video") && this.uploadVideo) {
await this.addEvidence(attach, evidences);
}
});
}

xrayTestData.evidence = evidences;
this.testResults.tests?.push(xrayTestData);
let projectID = '';
let projectID = "";
const tst: string = JSON.stringify(testCase.parent.project()).match(/__projectIdd":"(.*)"/)?.[1] as string;
if (tst !== undefined) {
projectID = `${tst.charAt(0).toUpperCase() + tst.slice(1)} | `;
}

switch (this.help.convertPwStatusToXray(result.status)) {
case 'PASS':
case 'PASSED':
console.log(`${bold(green(`✅ ${projectID}${testCase.title}`))}`);
case "PASS":
case "PASSED":
logger.info(`✅ ${projectID}${testCase.title}`);
//console.log(`${bold(green(`✅ ${projectID}${testCase.title}`))}`);
break;
case 'FAIL':
case 'FAILED':
console.log(`${bold(red(`⛔ ${projectID}${testCase.title}`))}`);
case "FAIL":
case "FAILED":
logger.error(`⛔ ${projectID}${testCase.title}`);
//console.log(`${bold(red(`⛔ ${projectID}${testCase.title}`))}`);
break;
case 'SKIPPED':
case 'ABORTED':
console.log(`${bold(white(`🚫 ${projectID}${testCase.title}`))}`);
case "SKIPPED":
case "ABORTED":
logger.warn(`🚫 ${projectID}${testCase.title}`);
// console.log(`${bold(white(`🚫 ${projectID}${testCase.title}`))}`);
break;
}
}
Expand All @@ -177,9 +184,9 @@ class XrayReporter implements Reporter {
const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)';
const pattern = [
`[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?${ST})`,
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
].join('|');
let errorMessage = step.replace(new RegExp(pattern, 'g'), '');
let errorMessage = step.replace(new RegExp(pattern, "g"), "");
errorMessage = errorMessage.replace(
/(\\u001b)(8|7|H|>|\[(\?\d+(h|l)|[0-2]?(K|J)|\d*(A|B|C|D\D|E|F|G|g|i|m|n|S|s|T|u)|1000D\d+|\d*;\d*(f|H|r|m)|\d+;\d+;\d+m))/g,
'',
Expand All @@ -197,10 +204,10 @@ class XrayReporter implements Reporter {
evidences: XrayTestEvidence[],
) {
if (!attach.path) {
throw new Error('Attachment path is undefined');
throw new Error("Attachment path is undefined");
}
const filename = path.basename(attach.path);
const attachData = fs.readFileSync(attach.path, { encoding: 'base64' });
const attachData = fs.readFileSync(attach.path, { encoding: "base64" });
const evid: XrayTestEvidence = {
data: attachData,
filename: filename,
Expand All @@ -215,13 +222,14 @@ class XrayReporter implements Reporter {
new Date(new Date(this.testResults?.info?.startDate ?? new Date()).getTime() + this.totalDuration),
);

if (typeof this.testResults !== 'undefined' && typeof this.testResults.tests !== 'undefined' && this.testResults.tests.length > 0) {
if (typeof this.testResults !== "undefined" && typeof this.testResults.tests !== "undefined" && this.testResults.tests.length > 0) {
await this.xrayService.createRun(this.testResults, this.execInfo);
} else {
console.log(`There are no tests with such ${this.testCaseKeyPattern} key pattern`);
logger.error(`There are no tests with such ${this.testCaseKeyPattern} key pattern`);
//console.log(`There are no tests with such ${this.testCaseKeyPattern} key pattern`);
}
}
}

export default XrayReporter;
export * from './types/xray.types';
export * from "./types/xray.types";
35 changes: 35 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { blue, bold, green, red, white, yellow, cyan } from "picocolors";

class CustomLogger {
log(...args: unknown[]) {
console.log(cyan("[INFO]"), ...args);
}

warn(...args: unknown[]) {
console.log(yellow("[WARN]"), ...args);
}

error(...args: unknown[]) {
console.log(red("[ERROR]"), ...args);
}

logEmptyLine() {
console.log("\n\n");
}

separator() {
console.log(`${bold(blue(" "))}`);
console.log(`${bold(blue("-------------------------------------"))}`);
console.log(`${bold(blue(" "))}`);
}

info(message: string) {
console.log(`${bold(yellow("⏺ "))}${bold(blue(message))}`);
}

sucess(message: string) {
console.log(`${bold(green("✔ "))}${bold(blue(message))}`);
}
}

export const logger = new CustomLogger();
2 changes: 1 addition & 1 deletion src/types/cloud.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface XrayTest {
evidence?: XrayTestEvidence[];
steps?: XrayTestSteps[];
defects?: object;
comment: string;
comment: string | undefined;
}

export interface XrayTestSteps {
Expand Down
Loading
Loading