diff --git a/src/help.ts b/src/help.ts index df98e8e..3ee00ed 100644 --- a/src/help.ts +++ b/src/help.ts @@ -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); @@ -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; } diff --git a/src/index.ts b/src/index.ts index 0611df0..9a9ffd4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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; @@ -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; @@ -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; @@ -98,13 +102,13 @@ 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) => { @@ -112,15 +116,15 @@ class XrayReporter implements Reporter { // 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); @@ -133,13 +137,13 @@ 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); } }); @@ -147,24 +151,27 @@ class XrayReporter implements Reporter { 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; } } @@ -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, '', @@ -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, @@ -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"; diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..007ed82 --- /dev/null +++ b/src/logger.ts @@ -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(); diff --git a/src/types/cloud.types.ts b/src/types/cloud.types.ts index e26735c..4bac309 100644 --- a/src/types/cloud.types.ts +++ b/src/types/cloud.types.ts @@ -36,7 +36,7 @@ export interface XrayTest { evidence?: XrayTestEvidence[]; steps?: XrayTestSteps[]; defects?: object; - comment: string; + comment: string | undefined; } export interface XrayTestSteps { diff --git a/src/xray.service.ts b/src/xray.service.ts index 91ba61e..e98b4b4 100644 --- a/src/xray.service.ts +++ b/src/xray.service.ts @@ -1,18 +1,18 @@ -import * as fs from 'node:fs'; -import { inspect } from 'node:util'; -import axios, { type Axios } from 'axios'; -import { blue, bold, green, red, white, yellow } from 'picocolors'; -import Help from './help'; -import type { XrayTestResult } from './types/cloud.types'; -import type { ExecInfo } from './types/execInfo.types'; -import type { XrayOptions } from './types/xray.types'; +import * as fs from "node:fs"; +import { inspect } from "node:util"; +import axios, { type Axios } from "axios"; +import { logger } from "./logger"; +import Help from "./help"; +import type { XrayTestResult } from "./types/cloud.types"; +import type { ExecInfo } from "./types/execInfo.types"; +import type { XrayOptions } from "./types/xray.types"; export class XrayService { private readonly jira: string; private readonly type: string; private readonly apiVersion: string; private readonly options: XrayOptions; - private requestUrl = ''; + private requestUrl = ""; private axios: Axios; private help: Help; private dryRun: boolean; @@ -41,9 +41,9 @@ export class XrayService { this.axios = axios; this.axios.defaults.headers.options = { - 'Cache-Control': 'no-cache', - Pragma: 'no-cache', - Expires: '0', + "Cache-Control": "no-cache", + Pragma: "no-cache", + Expires: "0", }; if (!this.dryRun) { @@ -67,7 +67,7 @@ export class XrayService { try { if (this.options.debug) { - fs.writeFileSync('xray-payload-debug.json', JSON.stringify(results)); + fs.writeFileSync("xray-payload-debug.json", JSON.stringify(results)); } } catch (error) { console.log(`Unable to write xray-payload-debug.json : ${(error as Error).message}`); @@ -75,15 +75,15 @@ export class XrayService { //console.log(results); for (const test of results.tests ?? []) { switch (test.status) { - case 'SKIPPED': + case "SKIPPED": skipped = skipped + 1; break; - case 'PASS': - case 'PASSED': + case "PASS": + case "PASSED": passed = passed + 1; break; - case 'FAIL': - case 'FAILED': + case "FAIL": + case "FAILED": if (this.isThereFlaky(results, test)) { flaky = flaky + 1; } else { @@ -96,107 +96,135 @@ export class XrayService { try { if (this.options.debug || this.options.dryRun) { - fs.writeFileSync('xray-payload.json', JSON.stringify(results)); + fs.writeFileSync("xray-payload.json", JSON.stringify(results)); } - const key = !this.dryRun ? await this.postResultToJira(URL, results) : 'Dry run'; + const key = !this.dryRun ? await this.postResultToJira(URL, results) : "Dry run"; - const action = this.options.testExecution !== undefined ? 'updated' : 'created'; + const action = this.options.testExecution !== undefined ? "updated" : "created"; // Results - console.log(`${bold(blue(' '))}`); - console.log(`${bold(blue('-------------------------------------'))}`); - console.log(`${bold(blue(' '))}`); + logger.separator(); if (this.dryRun) { - console.log(`${bold(green('😀 Successfully performed a Dry Run'))}`); + logger.sucess("😀 Successfully performed a Dry Run"); + //logger.console.log(`${bold(green("😀 Successfully performed a Dry Run"))}`); } else { - console.log(`${bold(green('😀 Successfully sending test results to Jira'))}`); + logger.sucess("😀 Successfully sending test results to Jira"); + //console.log(`${bold(green("😀 Successfully sending test results to Jira"))}`); } - console.log(`${bold(blue(' '))}`); + logger.logEmptyLine(); if (this.options.description !== undefined) { - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Description: ${this.options.description}`))}`); + logger.info(`Description: ${this.options.description}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Description: ${this.options.description}`))}`); } if (this.options.testEnvironments !== undefined) { - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Test environments: ${this.options.testEnvironments}`))}`); + logger.info(`Test environments: ${this.options.testEnvironments}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Test environments: ${this.options.testEnvironments}`))}`); } if (this.options.version !== undefined) { - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Version: ${this.options.version}`))}`); + logger.info(`Version: ${this.options.version}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Version: ${this.options.version}`))}`); } if (this.options.revision !== undefined) { - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Revision: ${this.options.revision}`))}`); + logger.info(`Revision: ${this.options.revision}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Revision: ${this.options.revision}`))}`); } if (execInfo.browserName !== undefined) { - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Browser: ${execInfo.testedBrowser}`))}`); + logger.info(`Browser: ${execInfo.browserName}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Browser: ${execInfo.testedBrowser}`))}`); } - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Test plan: ${this.options.testPlan}`))}`); + + logger.info(`Test plan: ${this.options.testPlan}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Test plan: ${this.options.testPlan}`))}`); + if (this.options.testExecution !== undefined) { - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Test execution: ${this.options.testExecution}`))}`); + logger.info(`Test execution: ${this.options.testExecution}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Test execution: ${this.options.testExecution}`))}`); } - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Test Duration: ${this.help.convertMsToTime(duration)}`))}`); - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Tests ran: ${total} (including reruns)`))}`); - console.log(`${bold(yellow('⏺ '))}${bold(green(`Tests passed: ${passed}`))}`); - console.log(`${bold(yellow('⏺ '))}${bold(red(`Tests failed: ${failed}`))}`); - console.log(`${bold(yellow('⏺ '))}${bold(yellow(`Flaky tests: ${flaky}`))}`); - console.log(`${bold(yellow('⏺ '))}${bold(white(`Skipped tests: ${skipped}`))}`); - console.log(`${bold(blue(' '))}`); - console.log(`${bold(blue('-------------------------------------'))}`); - console.log(`${bold(blue(' '))}`); - console.log(`${bold(yellow('⏺ '))}${bold(blue(`Test execution ${key} has been ${action}`))}`); + logger.info(`Test Duration: ${this.help.convertMsToTime(duration)}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Test Duration: ${this.help.convertMsToTime(duration)}`))}`); + logger.info(`Tests ran: ${total} (including reruns)`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Tests ran: ${total} (including reruns)`))}`); + logger.info(`Tests passed: ${passed}`); + //console.log(`${bold(yellow("⏺ "))}${bold(green(`Tests passed: ${passed}`))}`); + logger.info(`Tests failed: ${failed}`); + //console.log(`${bold(yellow("⏺ "))}${bold(red(`Tests failed: ${failed}`))}`); + logger.info(`Flaky tests: ${flaky}`); + //console.log(`${bold(yellow("⏺ "))}${bold(yellow(`Flaky tests: ${flaky}`))}`); + logger.info(`Skipped tests: ${skipped}`); + //console.log(`${bold(yellow("⏺ "))}${bold(white(`Skipped tests: ${skipped}`))}`); + logger.separator(); + //console.log(`${bold(blue(" "))}`); + //console.log(`${bold(blue("-------------------------------------"))}`); + //console.log(`${bold(blue(" "))}`); + logger.info(`Test execution ${key} has been ${action}`); + //console.log(`${bold(yellow("⏺ "))}${bold(blue(`Test execution ${key} has been ${action}`))}`); if (!this.dryRun) { - console.log(`${bold(blue('👇 Check out the test result'))}`); - console.log(`${bold(blue(`🔗 ${this.jira}browse/${key}`))}`); - console.log(`${bold(blue(' '))}`); + logger.info("👇 Check out the test result"); + //console.log(`${bold(blue("👇 Check out the test result"))}`); + logger.info(`🔗 ${this.jira}browse/${key}`); + //console.log(`${bold(blue(`🔗 ${this.jira}browse/${key}`))}`); + logger.logEmptyLine(); + //console.log(`${bold(blue(" "))}`); } if (this.runResult) writeRunResult(this.options.testPlan); - console.log(`${bold(blue('-------------------------------------'))}`); + logger.separator(); } catch (error) { - console.log(`${bold(blue(' '))}`); - console.log(`${bold(blue('-------------------------------------'))}`); - console.log(`${bold(blue(' '))}`); + logger.separator(); + //console.log(`${bold(blue(" "))}`); + //console.log(`${bold(blue("-------------------------------------"))}`); + //console.log(`${bold(blue(" "))}`); - let log = ''; - let msg = ''; + let log = ""; + let msg = ""; if (axios.isAxiosError(error) && !this.dryRun) { log = `Config: ${inspect(error.config)}\n\n`; if (error.response) { msg = inspect(error.response.data.error); - msg = msg.replace(/'/g, ''); + msg = msg.replace(/'/g, ""); log += `Status: ${error.response.status}\n`; log += `Headers: ${inspect(error.response.headers)}\n`; log += `Data: ${inspect(error.response.data)}\n`; } else if (error.request) { - msg = 'The request was made but no response was received'; + msg = "The request was made but no response was received"; log += `Error: ${inspect(error.toJSON())}\n`; } else { - msg = 'Something happened in setting up the request that triggered an error'; + msg = "Something happened in setting up the request that triggered an error"; log += `Error: ${inspect(error.message)}\n`; } } else { log = `Unknown error: ${error}\n`; } try { - fs.writeFileSync('playwright-xray-error.log', log); + fs.writeFileSync("playwright-xray-error.log", log); } catch (error) { - console.log(`Unable to write playwright-xray-error.log : ${(error as Error).message}`); + logger.error(`Unable to write playwright-xray-error.log : ${(error as Error).message}`); + //console.log(`Unable to write playwright-xray-error.log : ${(error as Error).message}`); } - const msgs = msg.split(';'); - console.log(`${bold(red('😞 Error sending test results to Jira'))}`); - console.log(`${bold(blue(' '))}`); + const msgs = msg.split(";"); + logger.error("😞 Error sending test results to Jira"); + //console.log(`${bold(red("😞 Error sending test results to Jira"))}`); + logger.logEmptyLine(); + //console.log(`${bold(blue(" "))}`); for (const m of msgs) { - console.log(`${bold(red(`⛔ ${m}`))}`); + logger.error(`⛔ ${m}`); + //console.log(`${bold(red(`⛔ ${m}`))}`); } - console.log(`${bold(blue(' '))}`); - console.log(`${bold(blue('👉 Check the "playwright-xray-error.log" file for more details'))}`); - console.log(`${bold(blue(' '))}`); - console.log(`${bold(blue('-------------------------------------'))}`); + logger.logEmptyLine(); + //console.log(`${bold(blue(" "))}`); + logger.info("👉 Check the 'playwright-xray-error.log' file for more details"); + //console.log(`${bold(blue('👉 Check the "playwright-xray-error.log" file for more details'))}`); + logger.separator(); + //console.log(`${bold(blue(" "))}`); + //console.log(`${bold(blue("-------------------------------------"))}`); } function writeRunResult(testPlan: string) { @@ -211,22 +239,23 @@ export class XrayService { skippedTests: skipped, }; try { - fs.writeFileSync('runresult.json', JSON.stringify(runResult)); + fs.writeFileSync("runresult.json", JSON.stringify(runResult)); } catch (error) { - console.log(`Unable to write runresult.json : ${(error as Error).message}`); + logger.error(`Unable to write runresult.json : ${(error as Error).message}`); + //console.log(`Unable to write runresult.json : ${(error as Error).message}`); } } } private initialzeJiraConnection(options: XrayOptions) { - let xray = ''; - let username = ''; - let password = ''; - let token = ''; + let xray = ""; + let username = ""; + let password = ""; + let token = ""; switch (this.type) { - case 'cloud': + case "cloud": // Set Xray Server URL - xray = options.cloud?.xrayUrl === undefined || !options.cloud?.xrayUrl ? 'https://xray.cloud.getxray.app/' : options.cloud.xrayUrl; + xray = options.cloud?.xrayUrl === undefined || !options.cloud?.xrayUrl ? "https://xray.cloud.getxray.app/" : options.cloud.xrayUrl; // Set Xray Credencials if (!options.cloud?.client_id || !options.cloud?.client_secret) { @@ -237,7 +266,7 @@ export class XrayService { password = options.cloud?.client_secret; // Set Request URL - this.requestUrl = new URL('api/v2', xray).toString(); + this.requestUrl = new URL("api/v2", xray).toString(); //Create Axios Instance with Auth axios @@ -249,7 +278,7 @@ export class XrayService { this.axios = axios.create({ baseURL: xray, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", Authorization: `Bearer ${request.data}`, }, }); @@ -260,7 +289,7 @@ export class XrayService { break; - case 'server': + case "server": // Set Xray Server URL if (!options.jira?.url) throw new Error('"host" option is missed. Please, provide it in the config'); xray = options.jira?.url; @@ -270,13 +299,13 @@ export class XrayService { token = options.server?.token; // Set Request URL - this.requestUrl = xray + (this.apiVersion !== '1.0' ? `rest/raven/${this.apiVersion}/api` : 'rest/raven/1.0'); + this.requestUrl = xray + (this.apiVersion !== "1.0" ? `rest/raven/${this.apiVersion}/api` : "rest/raven/1.0"); //Create Axios Instance with Auth this.axios = axios.create({ baseURL: xray, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); @@ -294,19 +323,19 @@ export class XrayService { }); if (response.status !== 200) throw new Error(`${response.status} - Failed to create test cycle`); let key = response.data.key; - if (this.options.jira.type === 'server') { + if (this.options.jira.type === "server") { key = response.data.testExecIssue.key; } return key; } private isThereFlaky(results: XrayTestResult, test: { status: string; testKey: string }) { - const flaky = results.tests?.find((item) => item.testKey.includes(test.testKey) && item.status.includes('PASSED')); + const flaky = results.tests?.find((item) => item.testKey.includes(test.testKey) && item.status.includes("PASSED")); if (flaky !== undefined) { const passed = results.tests?.at(results.tests.indexOf(flaky)); - if (passed !== undefined) passed.status = this.options.markFlakyWith === undefined ? 'PASSED' : this.options.markFlakyWith; + if (passed !== undefined) passed.status = this.options.markFlakyWith === undefined ? "PASSED" : this.options.markFlakyWith; - const duplicates = results.tests?.filter((item) => item.testKey.includes(test.testKey) && item.status.includes('FAILED')); + const duplicates = results.tests?.filter((item) => item.testKey.includes(test.testKey) && item.status.includes("FAILED")); if (duplicates) { for (const duplicate of duplicates) { results.tests?.splice(results.tests?.indexOf(duplicate), 1); @@ -318,7 +347,7 @@ export class XrayService { } private removeDuplicates(results: XrayTestResult, test: { status: string; testKey: string }) { - const duplicates = results.tests?.filter((item) => item.testKey.includes(test.testKey) && item.status.includes('FAILED')); + const duplicates = results.tests?.filter((item) => item.testKey.includes(test.testKey) && item.status.includes("FAILED")); duplicates?.pop(); if (duplicates) { for (const duplicate of duplicates) { diff --git a/tests/another-simple.test.ts b/tests/another-simple.test.ts index 2c10f4a..1bd1be6 100644 --- a/tests/another-simple.test.ts +++ b/tests/another-simple.test.ts @@ -1,25 +1,25 @@ -import { expect, test } from '@playwright/test'; +import { expect, test } from "@playwright/test"; -test('XRAYISSUE-1 | another test', async ({ page, browserName }) => { - test.skip(browserName === 'webkit'); +test("XRAYISSUE-1 | another test", async ({ page, browserName }) => { + test.skip(browserName === "webkit"); - await page.goto('https://playwright.dev/'); - const title = page.locator('.navbar__inner .navbar__title'); + await page.goto("https://playwright.dev/"); + const title = page.locator(".navbar__inner .navbar__title"); - if (browserName === 'firefox') { - await expect(title).toHaveText('Playright'); + if (browserName === "firefox") { + await expect(title).toHaveText("Playright"); } - await expect(title).toHaveText('Playwright'); + await expect(title).toHaveText("Playwright"); }); -test('XRAYISSUE-2 | another test', async ({ page, browserName }) => { - test.skip(browserName === 'webkit'); +test("XRAYISSUE-2 | another test", async ({ page, browserName }) => { + test.skip(browserName === "webkit"); - await page.goto('https://playwright.dev/'); - const title = page.locator('.navbar__inner .navbar__title'); + await page.goto("https://playwright.dev/"); + const title = page.locator(".navbar__inner .navbar__title"); - if (browserName === 'firefox') { - await expect(title).toHaveText('Playright 22'); + if (browserName === "firefox") { + await expect(title).toHaveText("Playright 22"); } - await expect(title).toHaveText('Playwright 22'); + await expect(title).toHaveText("Playwright 22"); }); diff --git a/tests/simple.test.ts b/tests/simple.test.ts index 19ea71e..266f973 100644 --- a/tests/simple.test.ts +++ b/tests/simple.test.ts @@ -1,7 +1,7 @@ -import { expect, test } from '@playwright/test'; +import { expect, test } from "@playwright/test"; -test('XRAYISSUE-1 | basic test', async ({ page }) => { - await page.goto('https://playwright.dev/'); - const title = page.locator('.navbar__inner .navbar__title'); - await expect(title).toHaveText('Playwright'); +test("XRAYISSUE-1 | basic test", async ({ page }) => { + await page.goto("https://playwright.dev/"); + const title = page.locator(".navbar__inner .navbar__title"); + await expect(title).toHaveText("Playwright"); }); diff --git a/tests/with-steps.test.ts b/tests/with-steps.test.ts index ff5de85..25c741e 100644 --- a/tests/with-steps.test.ts +++ b/tests/with-steps.test.ts @@ -1,28 +1,28 @@ -import { expect, test } from '@playwright/test'; +import { expect, test } from "@playwright/test"; -test('TES-49 | flaky test with playwright steps', async ({ page }) => { - await test.step('Step 1', async () => { +test("TES-49 | flaky test with playwright steps", async ({ page }) => { + await test.step("Step 1", async () => { expect(true).toBe(true); }); - await test.step('Step 2', async () => { + await test.step("Step 2", async () => { expect(true).toBe(true); }); - await page.goto('https://playwright.dev/'); - const title = page.locator('.navbar__inner .navbar__title'); - await expect(title).toHaveText('Playwright'); + await page.goto("https://playwright.dev/"); + const title = page.locator(".navbar__inner .navbar__title"); + await expect(title).toHaveText("Playwright"); - await test.step('Step 3', async () => { + await test.step("Step 3", async () => { const ran = Math.floor(Math.random() * 2); expect(ran).toBe(1); }); }); -test('TES-42 | test with - skip', async ({ page }) => { +test("TES-42 | test with - skip", async ({ page }) => { test.skip(); }); -test('Test without XRAY key - skip', async ({ page }) => { +test("Test without XRAY key - skip", async ({ page }) => { test.skip(); });