playwright-slack-report 1.1.22 → 1.1.24

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.
package/README.md CHANGED
@@ -179,6 +179,8 @@ A function that returns a layout object, this configuration is optional. See se
179
179
  Same as **layout** above, but asynchronous in that it returns a promise.
180
180
  ### **maxNumberOfFailuresToShow**
181
181
  Limits the number of failures shown in the Slack message, defaults to 10.
182
+ ### **separateFlaky**
183
+ Don't consider flaky tests as passed, instead output them separately. Flaky tests are tests that passed but not on the first try. This defaults to `false` but it is highly recommended that you switch this on.
182
184
  ### **slackOAuthToken**
183
185
  Instead of providing an environment variable `SLACK_BOT_USER_OAUTH_TOKEN` you can specify the token in the config in the `slackOAuthToken` field.
184
186
  ### **slackLogLevel** (default LogLevel.DEBUG)
@@ -2,8 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateFailures = exports.generateBlocks = void 0;
4
4
  const generateBlocks = async (summaryResults, maxNumberOfFailures) => {
5
- const maxNumberOfFailureLength = 650;
6
- const fails = [];
7
5
  const meta = [];
8
6
  const header = {
9
7
  type: 'section',
@@ -16,35 +14,12 @@ const generateBlocks = async (summaryResults, maxNumberOfFailures) => {
16
14
  type: 'section',
17
15
  text: {
18
16
  type: 'mrkdwn',
19
- text: `✅ *${summaryResults.passed}* | ❌ *${summaryResults.failed}* | ⏩ *${summaryResults.skipped}*`,
17
+ text: `✅ *${summaryResults.passed}* | ❌ *${summaryResults.failed}* |${summaryResults.flaky !== undefined
18
+ ? ` 🟡 *${summaryResults.flaky}* | `
19
+ : ' '}⏩ *${summaryResults.skipped}*`,
20
20
  },
21
21
  };
22
- for (let i = 0; i < summaryResults.failures.length; i += 1) {
23
- const { failureReason, test } = summaryResults.failures[i];
24
- const formattedFailure = failureReason
25
- .substring(0, maxNumberOfFailureLength)
26
- .split('\n')
27
- .map((l) => `>${l}`)
28
- .join('\n');
29
- fails.push({
30
- type: 'section',
31
- text: {
32
- type: 'mrkdwn',
33
- text: `*${test}*
34
- \n${formattedFailure}`,
35
- },
36
- });
37
- if (i > maxNumberOfFailures) {
38
- fails.push({
39
- type: 'section',
40
- text: {
41
- type: 'mrkdwn',
42
- text: `*There are too many failures to display - ${fails.length} out of ${summaryResults.failures.length} failures shown*`,
43
- },
44
- });
45
- break;
46
- }
47
- }
22
+ const fails = await generateFailures(summaryResults, maxNumberOfFailures);
48
23
  if (summaryResults.meta) {
49
24
  for (let i = 0; i < summaryResults.meta.length; i += 1) {
50
25
  const { key, value } = summaryResults.meta[i];
@@ -57,21 +32,14 @@ const generateBlocks = async (summaryResults, maxNumberOfFailures) => {
57
32
  });
58
33
  }
59
34
  }
60
- return [
61
- header,
62
- summary,
63
- ...meta,
64
- {
65
- type: 'divider',
66
- },
67
- ...fails,
68
- ];
35
+ return [header, summary, ...meta, ...fails];
69
36
  };
70
37
  exports.generateBlocks = generateBlocks;
71
38
  const generateFailures = async (summaryResults, maxNumberOfFailures) => {
72
39
  const maxNumberOfFailureLength = 650;
73
40
  const fails = [];
74
- for (let i = 0; i < summaryResults.failures.length; i += 1) {
41
+ const numberOfFailuresToShow = Math.min(summaryResults.failures.length, maxNumberOfFailures);
42
+ for (let i = 0; i < numberOfFailuresToShow; i += 1) {
75
43
  const { failureReason, test } = summaryResults.failures[i];
76
44
  const formattedFailure = failureReason
77
45
  .substring(0, maxNumberOfFailureLength)
@@ -86,16 +54,15 @@ const generateFailures = async (summaryResults, maxNumberOfFailures) => {
86
54
  \n${formattedFailure}`,
87
55
  },
88
56
  });
89
- if (i > maxNumberOfFailures) {
90
- fails.push({
91
- type: 'section',
92
- text: {
93
- type: 'mrkdwn',
94
- text: `*There are too many failures to display - ${fails.length} out of ${summaryResults.failures.length} failures shown*`,
95
- },
96
- });
97
- break;
98
- }
57
+ }
58
+ if (summaryResults.failures.length > maxNumberOfFailures) {
59
+ fails.push({
60
+ type: 'section',
61
+ text: {
62
+ type: 'mrkdwn',
63
+ text: `*⚠️ There are too many failures to display - ${fails.length} out of ${summaryResults.failures.length} failures shown*`,
64
+ },
65
+ });
99
66
  }
100
67
  return [
101
68
  {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { failure, SummaryResults } from '.';
2
+ import { failure, flaky, pass, SummaryResults } from '.';
3
3
  export declare type testResult = {
4
4
  suiteName: string;
5
5
  name: string;
@@ -27,9 +27,14 @@ export declare type testSuite = {
27
27
  };
28
28
  export default class ResultsParser {
29
29
  private result;
30
- constructor();
30
+ private separateFlakyTests;
31
+ constructor(options?: {
32
+ separateFlakyTests: boolean;
33
+ });
31
34
  getParsedResults(): Promise<SummaryResults>;
32
35
  getFailures(): Promise<Array<failure>>;
36
+ getFlakes(): Promise<Array<flaky>>;
37
+ getPasses(): Promise<Array<pass>>;
33
38
  static getTestName(failedTest: any): any;
34
39
  updateResults(data: {
35
40
  testSuite: any;
@@ -50,4 +55,7 @@ export default class ResultsParser {
50
55
  projectName: string;
51
56
  browser: string;
52
57
  };
58
+ /** removes tests from the passed array that only passed on a retry (flaky).
59
+ * Does not modify param passed, returns a new passed array. */
60
+ doSeparateFlakyTests(passes: Array<pass>, flakes: Array<flaky>): pass[];
53
61
  }
@@ -8,14 +8,22 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  class ResultsParser {
10
10
  result;
11
- constructor() {
11
+ separateFlakyTests;
12
+ constructor(options = { separateFlakyTests: false }) {
12
13
  this.result = [];
14
+ this.separateFlakyTests = options.separateFlakyTests;
13
15
  }
14
16
  async getParsedResults() {
15
17
  const failures = await this.getFailures();
18
+ const flakes = await this.getFlakes();
19
+ let passes = await this.getPasses();
20
+ if (this.separateFlakyTests) {
21
+ passes = this.doSeparateFlakyTests(passes, flakes);
22
+ }
16
23
  const summary = {
17
- passed: 0,
24
+ passed: passes.length,
18
25
  failed: failures.length,
26
+ flaky: this.separateFlakyTests ? flakes.length : undefined,
19
27
  skipped: 0,
20
28
  failures,
21
29
  tests: [],
@@ -23,10 +31,7 @@ class ResultsParser {
23
31
  for (const suite of this.result) {
24
32
  summary.tests = summary.tests.concat(suite.testSuite.tests);
25
33
  for (const test of suite.testSuite.tests) {
26
- if (test.status === 'passed') {
27
- summary.passed += 1;
28
- }
29
- else if (test.status === 'skipped') {
34
+ if (test.status === 'skipped') {
30
35
  summary.skipped += 1;
31
36
  }
32
37
  }
@@ -50,6 +55,33 @@ class ResultsParser {
50
55
  }
51
56
  return failures;
52
57
  }
58
+ async getFlakes() {
59
+ const flaky = [];
60
+ for (const suite of this.result) {
61
+ for (const test of suite.testSuite.tests) {
62
+ if (test.status === 'passed' && test.retry > 0) {
63
+ flaky.push({
64
+ test: ResultsParser.getTestName(test),
65
+ retry: test.retry,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ return flaky;
71
+ }
72
+ async getPasses() {
73
+ const passes = [];
74
+ for (const suite of this.result) {
75
+ for (const test of suite.testSuite.tests) {
76
+ if (test.status === 'passed') {
77
+ passes.push({
78
+ test: ResultsParser.getTestName(test),
79
+ });
80
+ }
81
+ }
82
+ }
83
+ return passes;
84
+ }
53
85
  static getTestName(failedTest) {
54
86
  const testName = failedTest.name;
55
87
  if (failedTest.browser && failedTest.projectName) {
@@ -122,5 +154,17 @@ class ResultsParser {
122
154
  browser: '',
123
155
  };
124
156
  }
157
+ /** removes tests from the passed array that only passed on a retry (flaky).
158
+ * Does not modify param passed, returns a new passed array. */
159
+ doSeparateFlakyTests(passes, flakes) {
160
+ const _passes = new Map();
161
+ for (const pass of passes) {
162
+ _passes.set(pass.test, pass);
163
+ }
164
+ for (const flake of flakes) {
165
+ _passes.delete(flake.test);
166
+ }
167
+ return [..._passes.values()];
168
+ }
125
169
  }
126
170
  exports.default = ResultsParser;
@@ -15,6 +15,7 @@ declare class SlackReporter implements Reporter {
15
15
  private proxy;
16
16
  private browsers;
17
17
  private suite;
18
+ private separateFlaky;
18
19
  logs: string[];
19
20
  onBegin(fullConfig: FullConfig, suite: Suite): void;
20
21
  onTestEnd(test: TestCase, result: TestResult): void;
@@ -22,6 +22,7 @@ class SlackReporter {
22
22
  proxy;
23
23
  browsers = [];
24
24
  suite;
25
+ separateFlaky = false;
25
26
  logs = [];
26
27
  onBegin(fullConfig, suite) {
27
28
  this.suite = suite;
@@ -53,8 +54,11 @@ class SlackReporter {
53
54
  this.showInThread = slackReporterConfig.showInThread || false;
54
55
  this.slackLogLevel = slackReporterConfig.slackLogLevel || web_api_1.LogLevel.DEBUG;
55
56
  this.proxy = slackReporterConfig.proxy || undefined;
57
+ this.separateFlaky = slackReporterConfig.separateFlaky || false;
56
58
  }
57
- this.resultsParser = new ResultsParser_1.default();
59
+ this.resultsParser = new ResultsParser_1.default({
60
+ separateFlakyTests: this.separateFlaky,
61
+ });
58
62
  }
59
63
  // eslint-disable-next-line class-methods-use-this, no-unused-vars
60
64
  onTestEnd(test, result) {
@@ -108,12 +112,15 @@ class SlackReporter {
108
112
  // eslint-disable-next-line no-console
109
113
  console.log(JSON.stringify(result, null, 2));
110
114
  if (this.showInThread && resultSummary.failures.length > 0) {
111
- await slackClient.attachDetailsToThread({
112
- channelIds: this.slackChannels,
113
- ts: result[0].ts,
114
- summaryResults: resultSummary,
115
- maxNumberOfFailures: this.maxNumberOfFailuresToShow,
116
- });
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
+ }
117
124
  }
118
125
  }
119
126
  }
@@ -25,7 +25,7 @@ const generateCustomLayout = (summaryResults) => {
25
25
  type: 'section',
26
26
  text: {
27
27
  type: 'mrkdwn',
28
- text: '*There are too many failures to display, view the full results in BuildKite*',
28
+ text: '*⚠️ There are too many failures to display, view the full results in BuildKite*',
29
29
  },
30
30
  });
31
31
  break;
@@ -2,6 +2,7 @@
2
2
  export declare type SummaryResults = {
3
3
  passed: number;
4
4
  failed: number;
5
+ flaky: number | undefined;
5
6
  skipped: number;
6
7
  failures: Array<failure>;
7
8
  meta?: Array<{
@@ -30,3 +31,10 @@ export declare type failure = {
30
31
  test: string;
31
32
  failureReason: string;
32
33
  };
34
+ export declare type flaky = {
35
+ test: string;
36
+ retry: number;
37
+ };
38
+ export declare type pass = {
39
+ test: string;
40
+ };
package/package.json CHANGED
@@ -26,10 +26,11 @@
26
26
  "prettier": "prettier --write --loglevel warn \"**/**/*.ts\"",
27
27
  "pw": "nyc playwright test && nyc report --reporter=lcov",
28
28
  "build": "tsc -p ./tsconfig.json",
29
- "lint": "npx eslint . --ext .ts"
29
+ "lint": "npx eslint . --ext .ts",
30
+ "lint-fix": "npx eslint . --ext .ts --fix"
30
31
  },
31
32
  "name": "playwright-slack-report",
32
- "version": "1.1.22",
33
+ "version": "1.1.24",
33
34
  "main": "index.js",
34
35
  "types": "dist/index.d.ts",
35
36
  "repository": "git@github.com:ryanrosello-og/playwright-slack-report.git",