playwright-slack-report-burak 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ /// <reference types="node" />
2
+ import { failure, flaky, pass, skipper, SummaryResults } from '.';
3
+ export declare type testResult = {
4
+ suiteName: string;
5
+ name: string;
6
+ browser?: string;
7
+ projectName: string;
8
+ endedAt: string;
9
+ reason: string;
10
+ retry: number;
11
+ retries: number;
12
+ startedAt: string;
13
+ status: 'passed' | 'failed' | 'timedOut' | 'skipped';
14
+ attachments?: {
15
+ body: string | undefined | Buffer;
16
+ contentType: string;
17
+ name: string;
18
+ path: string;
19
+ }[];
20
+ };
21
+ export declare type testSuite = {
22
+ testSuite: {
23
+ title: string;
24
+ tests: testResult[];
25
+ testRunId?: number;
26
+ };
27
+ };
28
+ export default class ResultsParser {
29
+ private result;
30
+ private separateFlakyTests;
31
+ constructor(options?: {
32
+ separateFlakyTests: boolean;
33
+ });
34
+ getParsedResults(): Promise<SummaryResults>;
35
+ getFailures(): Promise<Array<failure>>;
36
+ getFlakes(): Promise<Array<flaky>>;
37
+ getPasses(): Promise<Array<pass>>;
38
+ getSkippers(): Promise<Array<skipper>>;
39
+ static getTestName(failedTest: any): any;
40
+ updateResults(data: {
41
+ testSuite: any;
42
+ }): void;
43
+ addTestResult(suiteName: any, testCase: any, projectBrowserMapping: any): void;
44
+ safelyDetermineFailure(result: {
45
+ errors: any[];
46
+ error: {
47
+ message: string;
48
+ stack: string;
49
+ };
50
+ }): string;
51
+ cleanseReason(rawReaseon: string): string;
52
+ determineBrowser(projectName: string, browserMappings: {
53
+ projectName: string;
54
+ browser: string;
55
+ }[]): {
56
+ projectName: string;
57
+ browser: string;
58
+ };
59
+ /** removes tests from the passed array that only passed on a retry (flaky).
60
+ * Does not modify param passed, returns a new passed array. */
61
+ doSeparateFlakyTests(passes: Array<pass>, flakes: Array<flaky>): pass[];
62
+ }
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ /* eslint-disable no-shadow */
3
+ /* eslint-disable no-underscore-dangle */
4
+ /* eslint-disable import/extensions */
5
+ /* eslint-disable no-control-regex */
6
+ /* eslint-disable class-methods-use-this */
7
+ /* eslint-disable no-param-reassign */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ class ResultsParser {
10
+ result;
11
+ separateFlakyTests;
12
+ constructor(options = { separateFlakyTests: false }) {
13
+ this.result = [];
14
+ this.separateFlakyTests = options.separateFlakyTests;
15
+ }
16
+ async getParsedResults() {
17
+ const failures = await this.getFailures();
18
+ const flakes = await this.getFlakes();
19
+ let passes = await this.getPasses();
20
+ const skippers = await this.getSkippers();
21
+ if (this.separateFlakyTests) {
22
+ passes = this.doSeparateFlakyTests(passes, flakes);
23
+ }
24
+ const summary = {
25
+ passed: passes.length,
26
+ failed: failures.length,
27
+ flaky: this.separateFlakyTests ? flakes.length : undefined,
28
+ skipped: skippers.length,
29
+ failures,
30
+ tests: [],
31
+ };
32
+ for (const suite of this.result) {
33
+ summary.tests = summary.tests.concat(suite.testSuite.tests);
34
+ for (const test of suite.testSuite.tests) {
35
+ if (test.status === 'skipped') {
36
+ summary.skipped += 1;
37
+ }
38
+ }
39
+ }
40
+ return summary;
41
+ }
42
+ async getFailures() {
43
+ const failures = [];
44
+ for (const suite of this.result) {
45
+ for (const test of suite.testSuite.tests) {
46
+ if (test.status === 'failed' || test.status === 'timedOut') {
47
+ // only flag as failed if the last attempt has failed
48
+ if (test.retries === test.retry) {
49
+ failures.push({
50
+ test: ResultsParser.getTestName(test),
51
+ failureReason: test.reason,
52
+ });
53
+ }
54
+ }
55
+ }
56
+ }
57
+ return failures;
58
+ }
59
+ async getFlakes() {
60
+ const flaky = [];
61
+ for (const suite of this.result) {
62
+ for (const test of suite.testSuite.tests) {
63
+ if (test.status === 'passed' && test.retry > 0) {
64
+ flaky.push({
65
+ test: ResultsParser.getTestName(test),
66
+ retry: test.retry,
67
+ });
68
+ }
69
+ }
70
+ }
71
+ return flaky;
72
+ }
73
+ async getPasses() {
74
+ const passes = [];
75
+ for (const suite of this.result) {
76
+ for (const test of suite.testSuite.tests) {
77
+ if (test.status === 'passed') {
78
+ passes.push({
79
+ test: ResultsParser.getTestName(test),
80
+ });
81
+ }
82
+ }
83
+ }
84
+ return passes;
85
+ }
86
+
87
+ async getSkippers() {
88
+ const skippers = [];
89
+ for (const suite of this.result) {
90
+ for (const test of suite.testSuite.tests) {
91
+ if (test.status === 'skipped') {
92
+ skippers.push({
93
+ test: ResultsParser.getTestName(test),
94
+ });
95
+ }
96
+ }
97
+ }
98
+ return skippers;
99
+ }
100
+
101
+
102
+ static getTestName(failedTest) {
103
+ const testName = failedTest.name;
104
+ if (failedTest.browser && failedTest.projectName) {
105
+ if (failedTest.browser === failedTest.projectName) {
106
+ return `${testName} [${failedTest.browser}]`;
107
+ }
108
+ return `${testName} [Project Name: ${failedTest.projectName}] using ${failedTest.browser}`;
109
+ }
110
+ return testName;
111
+ }
112
+ updateResults(data) {
113
+ if (data.testSuite.tests.length > 0) {
114
+ this.result.push(data);
115
+ }
116
+ }
117
+ addTestResult(suiteName, testCase, projectBrowserMapping) {
118
+ const testResults = [];
119
+ const projectSettings = this.determineBrowser(testCase._projectId, projectBrowserMapping);
120
+ for (const result of testCase.results) {
121
+ testResults.push({
122
+ suiteName,
123
+ name: testCase.title,
124
+ status: result.status,
125
+ browser: projectSettings.browser,
126
+ projectName: projectSettings.projectName,
127
+ retry: result.retry,
128
+ retries: testCase.retries,
129
+ startedAt: new Date(result.startTime).toISOString(),
130
+ endedAt: new Date(new Date(result.startTime).getTime() + result.duration).toISOString(),
131
+ reason: this.safelyDetermineFailure(result),
132
+ attachments: result.attachments,
133
+ });
134
+ }
135
+ this.updateResults({
136
+ testSuite: {
137
+ title: suiteName,
138
+ tests: testResults,
139
+ },
140
+ });
141
+ }
142
+ safelyDetermineFailure(result) {
143
+ if (result.errors.length > 0) {
144
+ const fullError = result.errors
145
+ .map((e) => `${e.message}\r\n${e.stack ? e.stack : ''}\r\n`)
146
+ .join();
147
+ return this.cleanseReason(fullError);
148
+ }
149
+ return `${this.cleanseReason(result.error?.message)} \n ${this.cleanseReason(result.error?.stack)}`;
150
+ }
151
+ cleanseReason(rawReaseon) {
152
+ // eslint-disable-next-line prefer-regex-literals
153
+ const ansiRegex = new RegExp('([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))', 'g');
154
+ const ansiCleansed = rawReaseon ? rawReaseon.replace(ansiRegex, '') : '';
155
+ const logsStripped = ansiCleansed
156
+ .replace(/============================================================\n/g, '')
157
+ .replace(/============================================================\r\n/g, '')
158
+ .replace(/=========================== logs ===========================\n/g, '');
159
+ return logsStripped;
160
+ }
161
+ determineBrowser(projectName, browserMappings) {
162
+ const browserMapping = browserMappings.find((mapping) => mapping.projectName === projectName);
163
+ if (browserMapping) {
164
+ return {
165
+ projectName: browserMapping.projectName,
166
+ browser: browserMapping.browser,
167
+ };
168
+ }
169
+ return {
170
+ projectName: '',
171
+ browser: '',
172
+ };
173
+ }
174
+ /** removes tests from the passed array that only passed on a retry (flaky).
175
+ * Does not modify param passed, returns a new passed array. */
176
+ doSeparateFlakyTests(passes, flakes) {
177
+ const _passes = new Map();
178
+ for (const pass of passes) {
179
+ _passes.set(pass.test, pass);
180
+ }
181
+ for (const flake of flakes) {
182
+ _passes.delete(flake.test);
183
+ }
184
+ return [..._passes.values()];
185
+ }
186
+ }
187
+ exports.default = ResultsParser;
@@ -0,0 +1,37 @@
1
+ import { WebClient, KnownBlock, Block, ChatPostMessageResponse, LogLevel } from '@slack/web-api';
2
+ import { SummaryResults } from '.';
3
+ export declare type additionalInfo = Array<{
4
+ key: string;
5
+ value: string;
6
+ }>;
7
+ export default class SlackClient {
8
+ private slackWebClient;
9
+ constructor(slackClient: WebClient);
10
+ sendMessage({ options, }: {
11
+ options: {
12
+ channelIds: Array<string>;
13
+ customLayout: Function | undefined;
14
+ customLayoutAsync: Function | undefined;
15
+ fakeRequest?: Function;
16
+ maxNumberOfFailures: number;
17
+ slackOAuthToken?: string;
18
+ slackLogLevel?: LogLevel;
19
+ disableUnfurl?: boolean;
20
+ summaryResults: SummaryResults;
21
+ showInThread: boolean;
22
+ };
23
+ }): Promise<Array<{
24
+ channel: string;
25
+ outcome: string;
26
+ ts: string;
27
+ }>>;
28
+ attachDetailsToThread({ channelIds, ts, summaryResults, maxNumberOfFailures, disableUnfurl, fakeRequest, }: {
29
+ channelIds: Array<string>;
30
+ ts: string;
31
+ summaryResults: SummaryResults;
32
+ maxNumberOfFailures: number;
33
+ disableUnfurl?: boolean;
34
+ fakeRequest?: Function;
35
+ }): Promise<any[]>;
36
+ static doPostRequest(slackWebClient: WebClient, channel: string, blocks: Array<KnownBlock | Block>, unfurl: boolean, threadTimestamp?: string): Promise<ChatPostMessageResponse>;
37
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const LayoutGenerator_1 = require("./LayoutGenerator");
4
+ class SlackClient {
5
+ slackWebClient;
6
+ constructor(slackClient) {
7
+ this.slackWebClient = slackClient;
8
+ }
9
+ async sendMessage({ options, }) {
10
+ let blocks;
11
+ if (options.customLayout) {
12
+ blocks = options.customLayout(options.summaryResults);
13
+ }
14
+ else if (options.customLayoutAsync) {
15
+ blocks = await options.customLayoutAsync(options.summaryResults);
16
+ }
17
+ else if (options.showInThread) {
18
+ const modifiedOptions = JSON.parse(JSON.stringify(options));
19
+ modifiedOptions.summaryResults.failures = [];
20
+ blocks = await (0, LayoutGenerator_1.generateBlocks)(modifiedOptions.summaryResults, options.maxNumberOfFailures);
21
+ }
22
+ else {
23
+ blocks = await (0, LayoutGenerator_1.generateBlocks)(options.summaryResults, options.maxNumberOfFailures);
24
+ }
25
+ if (!options.channelIds) {
26
+ throw new Error(`Channel ids [${options.channelIds}] is not valid`);
27
+ }
28
+ const result = [];
29
+ const unfurl = !options.disableUnfurl;
30
+ for (const channel of options.channelIds) {
31
+ let chatResponse;
32
+ try {
33
+ // under test
34
+ if (options.fakeRequest) {
35
+ chatResponse = await options.fakeRequest();
36
+ }
37
+ else {
38
+ // send request for reals
39
+ chatResponse = await SlackClient.doPostRequest(this.slackWebClient, channel, blocks, unfurl);
40
+ }
41
+ if (chatResponse.ok) {
42
+ result.push({
43
+ channel,
44
+ outcome: `✅ Message sent to ${channel}`,
45
+ ts: chatResponse.ts,
46
+ });
47
+ // eslint-disable-next-line no-console
48
+ console.log(`✅ Message sent to ${channel}`);
49
+ }
50
+ else {
51
+ result.push({
52
+ channel,
53
+ outcome: `❌ Message not sent to ${channel} \r\n ${JSON.stringify(chatResponse, null, 2)}`,
54
+ });
55
+ }
56
+ }
57
+ catch (error) {
58
+ result.push({
59
+ channel,
60
+ outcome: `❌ Message not sent to ${channel} \r\n ${error.message}`,
61
+ });
62
+ }
63
+ }
64
+ return result;
65
+ }
66
+ async attachDetailsToThread({ channelIds, ts, summaryResults, maxNumberOfFailures, disableUnfurl, fakeRequest, }) {
67
+ const result = [];
68
+ const blocks = await (0, LayoutGenerator_1.generateFailures)(summaryResults, maxNumberOfFailures);
69
+ for (const channel of channelIds) {
70
+ // under test
71
+ let chatResponse;
72
+ if (fakeRequest) {
73
+ chatResponse = await fakeRequest();
74
+ }
75
+ else {
76
+ chatResponse = await SlackClient.doPostRequest(this.slackWebClient, channel, blocks, disableUnfurl, ts);
77
+ }
78
+ if (chatResponse.ok) {
79
+ // eslint-disable-next-line no-console
80
+ console.log(`✅ Message sent to ${channel} within thread ${ts}`);
81
+ result.push({
82
+ channel,
83
+ outcome: `✅ Message sent to ${channel} within thread ${ts}`,
84
+ ts: chatResponse.ts,
85
+ });
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+ static async doPostRequest(slackWebClient, channel, blocks, unfurl, threadTimestamp) {
91
+ const chatResponse = await slackWebClient.chat.postMessage({
92
+ channel,
93
+ text: ' ',
94
+ unfurl_link: unfurl,
95
+ blocks,
96
+ thread_ts: threadTimestamp,
97
+ });
98
+ return chatResponse;
99
+ }
100
+ }
101
+ exports.default = SlackClient;
@@ -0,0 +1,30 @@
1
+ import { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
2
+ declare class SlackReporter implements Reporter {
3
+ private customLayout;
4
+ private customLayoutAsync;
5
+ private maxNumberOfFailuresToShow;
6
+ private showInThread;
7
+ private meta;
8
+ private resultsParser;
9
+ private sendResults;
10
+ private slackChannels;
11
+ private slackLogLevel;
12
+ private slackOAuthToken;
13
+ private slackWebHookUrl;
14
+ private disableUnfurl;
15
+ private proxy;
16
+ private browsers;
17
+ private suite;
18
+ private separateFlaky;
19
+ logs: string[];
20
+ onBegin(fullConfig: FullConfig, suite: Suite): void;
21
+ onTestEnd(test: TestCase, result: TestResult): void;
22
+ onEnd(): Promise<void>;
23
+ preChecks(): {
24
+ okToProceed: boolean;
25
+ message?: string;
26
+ };
27
+ log(message: string | undefined): void;
28
+ printsToStdio(): boolean;
29
+ }
30
+ export default SlackReporter;
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const web_api_1 = require("@slack/web-api");
4
+ const https_proxy_agent_1 = require("https-proxy-agent");
5
+ const webhook_1 = require("@slack/webhook");
6
+ const ResultsParser_1 = require("./ResultsParser");
7
+ const SlackClient_1 = require("./SlackClient");
8
+ const SlackWebhookClient_1 = require("./SlackWebhookClient");
9
+ class SlackReporter {
10
+ customLayout;
11
+ customLayoutAsync;
12
+ maxNumberOfFailuresToShow;
13
+ showInThread;
14
+ meta = [];
15
+ resultsParser;
16
+ sendResults = 'on-failure';
17
+ slackChannels = [];
18
+ slackLogLevel;
19
+ slackOAuthToken;
20
+ slackWebHookUrl;
21
+ disableUnfurl;
22
+ proxy;
23
+ browsers = [];
24
+ suite;
25
+ separateFlaky = false;
26
+ logs = [];
27
+ onBegin(fullConfig, suite) {
28
+ this.suite = suite;
29
+ this.logs = [];
30
+ const slackReporterConfig = fullConfig.reporter.filter((f) => f[0].toLowerCase().includes('slackreporter'))[0][1];
31
+ if (fullConfig.projects.length === 0) {
32
+ this.browsers = [];
33
+ }
34
+ else {
35
+ // eslint-disable-next-line max-len
36
+ this.browsers = fullConfig.projects.map((obj) => ({
37
+ projectName: obj.name,
38
+ browser: obj.use.browserName
39
+ ? obj.use.browserName
40
+ : obj.use.defaultBrowserType,
41
+ }));
42
+ }
43
+ if (slackReporterConfig) {
44
+ this.meta = slackReporterConfig.meta || [];
45
+ this.sendResults = slackReporterConfig.sendResults || 'always';
46
+ this.customLayout = slackReporterConfig.layout;
47
+ this.customLayoutAsync = slackReporterConfig.layoutAsync;
48
+ this.slackChannels = slackReporterConfig.channels;
49
+ this.maxNumberOfFailuresToShow
50
+ = slackReporterConfig.maxNumberOfFailuresToShow || 10;
51
+ this.slackOAuthToken = slackReporterConfig.slackOAuthToken || undefined;
52
+ this.slackWebHookUrl = slackReporterConfig.slackWebHookUrl || undefined;
53
+ this.disableUnfurl = slackReporterConfig.disableUnfurl || false;
54
+ this.showInThread = slackReporterConfig.showInThread || false;
55
+ this.slackLogLevel = slackReporterConfig.slackLogLevel || web_api_1.LogLevel.DEBUG;
56
+ this.proxy = slackReporterConfig.proxy || undefined;
57
+ this.separateFlaky = slackReporterConfig.separateFlaky || false;
58
+ }
59
+ this.resultsParser = new ResultsParser_1.default({
60
+ separateFlakyTests: this.separateFlaky,
61
+ });
62
+ }
63
+ // eslint-disable-next-line class-methods-use-this, no-unused-vars
64
+ onTestEnd(test, result) {
65
+ this.resultsParser.addTestResult(test.parent.title, test, this.browsers);
66
+ }
67
+ async onEnd() {
68
+ const { okToProceed, message } = this.preChecks();
69
+ if (!okToProceed) {
70
+ this.log(message);
71
+ return;
72
+ }
73
+ const resultSummary = await this.resultsParser.getParsedResults();
74
+ resultSummary.meta = this.meta;
75
+ const maxRetry = Math.max(...resultSummary.tests.map((o) => o.retry));
76
+ if (this.sendResults === 'on-failure'
77
+ && resultSummary.tests.filter((z) => (z.status === 'failed' || z.status === 'timedOut')
78
+ && z.retry === maxRetry).length === 0) {
79
+ this.log('⏩ Slack reporter - no failures found');
80
+ return;
81
+ }
82
+ const agent = this.proxy ? new https_proxy_agent_1.HttpsProxyAgent(this.proxy) : undefined;
83
+ if (this.slackWebHookUrl) {
84
+ const webhook = new webhook_1.IncomingWebhook(this.slackWebHookUrl, { agent });
85
+ const slackWebhookClient = new SlackWebhookClient_1.default(webhook);
86
+ const webhookResult = await slackWebhookClient.sendMessage({
87
+ customLayout: this.customLayout,
88
+ customLayoutAsync: this.customLayoutAsync,
89
+ maxNumberOfFailures: this.maxNumberOfFailuresToShow,
90
+ disableUnfurl: this.disableUnfurl,
91
+ summaryResults: resultSummary,
92
+ });
93
+ // eslint-disable-next-line no-console
94
+ console.log(JSON.stringify(webhookResult, null, 2));
95
+ }
96
+ else {
97
+ const slackClient = new SlackClient_1.default(new web_api_1.WebClient(this.slackOAuthToken || process.env.SLACK_BOT_USER_OAUTH_TOKEN, {
98
+ logLevel: this.slackLogLevel || web_api_1.LogLevel.DEBUG,
99
+ agent,
100
+ }));
101
+ const result = await slackClient.sendMessage({
102
+ options: {
103
+ channelIds: this.slackChannels,
104
+ customLayout: this.customLayout,
105
+ customLayoutAsync: this.customLayoutAsync,
106
+ maxNumberOfFailures: this.maxNumberOfFailuresToShow,
107
+ disableUnfurl: this.disableUnfurl,
108
+ summaryResults: resultSummary,
109
+ showInThread: this.showInThread,
110
+ },
111
+ });
112
+ // eslint-disable-next-line no-console
113
+ console.log(JSON.stringify(result, null, 2));
114
+ if (this.showInThread && resultSummary.failures.length > 0) {
115
+ for (let i = 0; i < result.length; i += 1) {
116
+ // eslint-disable-next-line no-await-in-loop
117
+ await slackClient.attachDetailsToThread({
118
+ channelIds: [result[i].channel],
119
+ ts: result[i].ts,
120
+ summaryResults: resultSummary,
121
+ maxNumberOfFailures: this.maxNumberOfFailuresToShow,
122
+ });
123
+ }
124
+ }
125
+ }
126
+ }
127
+ preChecks() {
128
+ if (this.sendResults === 'off') {
129
+ return { okToProceed: false, message: '❌ Slack reporter is disabled' };
130
+ }
131
+ if (!this.slackWebHookUrl
132
+ && !this.slackOAuthToken
133
+ && !process.env.SLACK_BOT_USER_OAUTH_TOKEN) {
134
+ return {
135
+ okToProceed: false,
136
+ message: '❌ Neither slack webhook url, slackOAuthToken nor process.env.SLACK_BOT_USER_OAUTH_TOKEN were found',
137
+ };
138
+ }
139
+ if (this.slackWebHookUrl
140
+ && (process.env.SLACK_BOT_USER_OAUTH_TOKEN || this.slackOAuthToken)) {
141
+ return {
142
+ okToProceed: false,
143
+ message: '❌ You can only enable a single option, either provide a slack webhook url, slackOAuthToken or process.env.SLACK_BOT_USER_OAUTH_TOKEN were found',
144
+ };
145
+ }
146
+ if (!this.sendResults
147
+ || !['always', 'on-failure', 'off'].includes(this.sendResults)) {
148
+ return {
149
+ okToProceed: false,
150
+ message: "❌ \"sendResults\" is not valid. Expecting one of ['always', 'on-failure', 'off'].",
151
+ };
152
+ }
153
+ if (!this.sendResults || this.slackChannels?.length === 0) {
154
+ return {
155
+ okToProceed: false,
156
+ message: '❌ Slack channel(s) was not provided in the config',
157
+ };
158
+ }
159
+ if (this.customLayout && typeof this.customLayout !== 'function') {
160
+ return {
161
+ okToProceed: false,
162
+ message: '❌ Custom layout is not a function',
163
+ };
164
+ }
165
+ if (this.customLayoutAsync
166
+ && typeof this.customLayoutAsync !== 'function') {
167
+ return {
168
+ okToProceed: false,
169
+ message: '❌ customLayoutAsync is not a function',
170
+ };
171
+ }
172
+ if (this.meta && !Array.isArray(this.meta)) {
173
+ return { okToProceed: false, message: '❌ Meta is not an array' };
174
+ }
175
+ return { okToProceed: true };
176
+ }
177
+ log(message) {
178
+ // eslint-disable-next-line no-console
179
+ console.log(message);
180
+ if (message) {
181
+ this.logs.push(message);
182
+ }
183
+ }
184
+ // eslint-disable-next-line class-methods-use-this
185
+ printsToStdio() {
186
+ return false;
187
+ }
188
+ }
189
+ exports.default = SlackReporter;
@@ -0,0 +1,15 @@
1
+ import { IncomingWebhook } from '@slack/webhook';
2
+ import { SummaryResults } from '.';
3
+ export default class SlackWebhookClient {
4
+ private webhook;
5
+ constructor(webhook: IncomingWebhook);
6
+ sendMessage({ customLayout, customLayoutAsync, maxNumberOfFailures, summaryResults, disableUnfurl, }: {
7
+ customLayout: Function | undefined;
8
+ customLayoutAsync: Function | undefined;
9
+ maxNumberOfFailures: number;
10
+ summaryResults: SummaryResults;
11
+ disableUnfurl: boolean;
12
+ }): Promise<{
13
+ outcome: string;
14
+ }>;
15
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const LayoutGenerator_1 = require("./LayoutGenerator");
4
+ class SlackWebhookClient {
5
+ webhook;
6
+ constructor(webhook) {
7
+ this.webhook = webhook;
8
+ }
9
+ async sendMessage({ customLayout, customLayoutAsync, maxNumberOfFailures, summaryResults, disableUnfurl, }) {
10
+ let blocks;
11
+ if (customLayout) {
12
+ blocks = customLayout(summaryResults);
13
+ }
14
+ else if (customLayoutAsync) {
15
+ blocks = await customLayoutAsync(summaryResults);
16
+ }
17
+ else {
18
+ blocks = await (0, LayoutGenerator_1.generateBlocks)(summaryResults, maxNumberOfFailures);
19
+ }
20
+ let result;
21
+ try {
22
+ result = await this.webhook.send({
23
+ blocks,
24
+ unfurl_links: !disableUnfurl,
25
+ });
26
+ }
27
+ catch (error) {
28
+ return {
29
+ outcome: `error: ${JSON.stringify(error, null, 2)}`,
30
+ };
31
+ }
32
+ if (result && result.text === 'ok') {
33
+ return {
34
+ outcome: result.text,
35
+ };
36
+ }
37
+ return {
38
+ outcome: '😵 Failed to send webhook message, ensure your webhook url is valid',
39
+ };
40
+ }
41
+ }
42
+ exports.default = SlackWebhookClient;
@@ -0,0 +1,4 @@
1
+ import { Block, KnownBlock } from '@slack/types';
2
+ import { SummaryResults } from '..';
3
+ declare const generateCustomLayout: (summaryResults: SummaryResults) => Array<KnownBlock | Block>;
4
+ export default generateCustomLayout;