-
Notifications
You must be signed in to change notification settings - Fork 0
/
summaryReport.ts
166 lines (149 loc) · 5.25 KB
/
summaryReport.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import { FullResult, Reporter, TestCase, TestResult } from '@playwright/test/reporter';
import { NotificationSender } from './notificationSender';
import axios from 'axios';
import { RPconfig } from './../../playwright.config';
/**
* Calculates the duration between a start time and an end time.
* @param startTime - The start time in milliseconds.
* @param endTime - The end time in milliseconds.
* @returns The duration in milliseconds.
*/
function getDuration(startTime: number, endTime: number): number {
return endTime - startTime;
}
const RPClient = require('@reportportal/client-javascript');
/**
* Formats the duration in milliseconds into a string representation of minutes and seconds.
* @param duration - The duration in milliseconds.
* @returns The formatted duration string in the format "Xm Ys".
*/
function formatDuration(duration: number): string {
const minutes = Math.floor(duration / 60000);
const seconds = Math.floor((duration % 60000) / 1000);
return `${Math.max(minutes, 0)}m ${Math.abs(seconds)}s`;
}
/**
* Represents the summary result object.
*/
type SummaryResult = {
[key: string]: string;
};
/**
* Represents a custom report for test execution.
*/
class SummaryReport implements Reporter {
private testResults: SummaryResult = {};
private suiteStartTime: number = 0;
private suiteEndTime: number = 0;
private notificationSender: NotificationSender;
private notifications: Promise<void>[] = [];
private failCount = 0;
private passCount = 0;
private launchUrl;
constructor() {
this.notificationSender = new NotificationSender();
}
/**
* Sends a notification to a webhook URL with the specified summary and status.
* If the status is not 'Success', it sends a failure notification.
* @param webhookUrl The URL of the webhook to send the notification to.
* @param summary The summary of the notification.
* @param status The status of the notification.
* @returns A promise that resolves when the notification is sent.
*/
private async sendNotification(
webhookUrl: string,
summary: string,
status: string,
launchUrl: string,
): Promise<void> {
// Uncomment the following line to send a notification whenever a test finishes, regardless of whether it passed or failed.
// if (status !== 'Success')
this.notifications.push(this.notificationSender.sendSummaryNotification(webhookUrl, summary, status, launchUrl));
}
/**
* Called when a test ends.
* @param test - The test case.
* @param result - The test result.
*/
onTestEnd(test: TestCase, result: TestResult): void {
this.testResults[test.id] = test.outcome();
const startTime = result.startTime.getTime();
this.suiteStartTime = this.suiteStartTime === 0 ? startTime : Math.min(this.suiteStartTime, startTime);
if (result.status !== 'passed') {
this.failCount++;
} else {
this.passCount++;
}
}
/**
* Handles the end of the test execution.
* @param _result - The result of the test execution.
* @returns A promise that resolves when the handling is complete.
*/
async onEnd(_result: FullResult): Promise<void> {
await this.getLaunchURL();
let all = 0;
const outcome: any = {
skipped: 0,
expected: 0,
unexpected: 0,
flaky: 0,
};
for (const id in this.testResults) {
all++;
const status = this.testResults[id];
if (!outcome[status]) {
outcome[status] = 0;
}
outcome[status]++;
}
const duration = getDuration(this.suiteStartTime, this.suiteEndTime);
const durationString = formatDuration(duration);
const summary = `
Execution Summary:
✅ Total Test Cases: ${all}
✅ Passed: ${outcome['expected']}
❌ Failed: ${outcome['unexpected']}
⚠ Flaky: ${outcome['flaky']}
⏭ Skipped: ${outcome['skipped']}
⏱ Duration: ${durationString}
`;
await this.sendNotification(
'YOUR_WEBHOOK_URL',
summary,
this.failCount === 0 ? 'Success' : 'Fail',
this.launchUrl,
);
await Promise.all(this.notifications);
}
/**
* Retrieves the launch URL for the current RPClient configuration.
* @returns {Promise<void>} A promise that resolves once the launch URL is retrieved.
*/
private async getLaunchURL() {
const rpClient = new RPClient({
apiKey: RPconfig.apiKey,
endpoint: RPconfig.endpoint,
project: RPconfig.project,
});
try {
const response = await rpClient.checkConnect();
console.info('You have successfully connected to the Report Portal server.');
console.info(`You are using an account: ${response.fullName}`);
const launchesResponse = await axios.get(`${rpClient.config.endpoint}/${rpClient.config.project}/launch`, {
headers: {
Authorization: `Bearer ${rpClient.config.apiKey}`,
},
});
const launches = launchesResponse.data.content;
const latestLaunch = launches.sort((a, b) => b.id - a.id)[0];
const baseUrl = RPconfig.endpoint.replace('/api/v1', '');
this.launchUrl = `${baseUrl}/ui/#${rpClient.config.project}/launches/all/${latestLaunch.id}`;
console.info(`Launch URL: ${this.launchUrl}`);
} catch (error) {
console.info('Error connection to server');
}
}
}
export default SummaryReport;