donobu 2.23.2 → 2.23.3
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1257
|
|
@@ -66,99 +66,84 @@ function generateMarkdown(jsonData) {
|
|
|
66
66
|
|
|
67
67
|
// Create report header
|
|
68
68
|
let markdown = `# Playwright Test Report\n\n`;
|
|
69
|
-
|
|
70
|
-
// Summary section
|
|
69
|
+
// Tests by file
|
|
71
70
|
markdown += `## Summary\n\n`;
|
|
72
|
-
markdown += `- **Start Time**: ${formatDate(stats.startTime)}\n`;
|
|
73
|
-
markdown += `- **Duration**: ${formatDuration(stats.duration)}\n`;
|
|
74
|
-
markdown += `- **Tests**: ${stats.expected + stats.unexpected}\n`;
|
|
75
|
-
markdown += `- **Passed**: ${stats.expected}\n`;
|
|
76
|
-
markdown += `- **Self-Healed**: ${selfHealedCount}\n`;
|
|
77
|
-
markdown += `- **Failed**: ${stats.unexpected - selfHealedCount}\n`;
|
|
78
|
-
markdown += `- **Skipped**: ${stats.skipped}\n`;
|
|
79
|
-
markdown += `- **Flaky**: ${stats.flaky}\n\n`;
|
|
80
|
-
|
|
81
|
-
// Create pass rate and visual indicator
|
|
82
|
-
const totalTests = stats.expected + stats.unexpected;
|
|
83
|
-
const passRate =
|
|
84
|
-
totalTests > 0 ? Math.round((stats.expected / totalTests) * 100) : 0;
|
|
85
|
-
const selfHealedRate =
|
|
86
|
-
totalTests > 0 ? Math.round((selfHealedCount / totalTests) * 100) : 0;
|
|
87
|
-
const failRate =
|
|
88
|
-
totalTests > 0
|
|
89
|
-
? Math.round(((stats.unexpected - selfHealedCount) / totalTests) * 100)
|
|
90
|
-
: 0;
|
|
91
|
-
|
|
92
|
-
markdown += `### Test Results\n\n`;
|
|
93
|
-
markdown += `- **Passed**: ${passRate}%\n`;
|
|
94
|
-
markdown += `- **Self-Healed**: ${selfHealedRate}%\n`;
|
|
95
|
-
markdown += `- **Failed**: ${failRate}%\n\n`;
|
|
96
|
-
|
|
97
|
-
// Create visual pass/self-healed/fail bar
|
|
98
|
-
const passChar = '🟢';
|
|
99
|
-
const selfHealedChar = '❤️🩹';
|
|
100
|
-
const failChar = '🔴';
|
|
101
|
-
|
|
102
|
-
const passCount = stats.expected;
|
|
103
|
-
const failCount = stats.unexpected - selfHealedCount;
|
|
104
|
-
|
|
105
|
-
const statusBar =
|
|
106
|
-
passChar.repeat(passCount) +
|
|
107
|
-
selfHealedChar.repeat(selfHealedCount) +
|
|
108
|
-
failChar.repeat(failCount);
|
|
109
|
-
|
|
110
|
-
markdown += `${statusBar}\n\n`;
|
|
111
71
|
|
|
112
|
-
//
|
|
113
|
-
markdown +=
|
|
72
|
+
// Create file summary table with status counts
|
|
73
|
+
markdown += `| File | Passed | Self-Healed | Failed | Timed Out | Skipped | Interrupted | Duration |\n`;
|
|
74
|
+
markdown += `| - | - | - | - | - | - | - | - |\n`;
|
|
114
75
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
// Track totals for summary row
|
|
77
|
+
let totalPassed = 0;
|
|
78
|
+
let totalFailed = 0;
|
|
79
|
+
let totalTimedOut = 0;
|
|
80
|
+
let totalSkipped = 0;
|
|
81
|
+
let totalInterrupted = 0;
|
|
82
|
+
let totalSelfHealed = 0;
|
|
83
|
+
let totalDuration = 0;
|
|
118
84
|
|
|
119
85
|
suites.forEach((suite) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
86
|
+
// Count tests by status for this file
|
|
87
|
+
let passed = 0;
|
|
88
|
+
let failed = 0;
|
|
89
|
+
let timedOut = 0;
|
|
90
|
+
let skipped = 0;
|
|
91
|
+
let interrupted = 0;
|
|
92
|
+
let selfHealed = 0;
|
|
93
|
+
|
|
124
94
|
const fileDuration = suite.specs.reduce(
|
|
125
95
|
(total, spec) =>
|
|
126
96
|
total +
|
|
127
97
|
spec.tests.reduce((testTotal, test) => {
|
|
128
|
-
const result = test.results && test.results
|
|
98
|
+
const result = test.results && test.results.at(-1);
|
|
99
|
+
const isSelfHealed =
|
|
100
|
+
test.annotations &&
|
|
101
|
+
test.annotations.some((a) => a.type === 'self-healed');
|
|
102
|
+
|
|
103
|
+
if (result) {
|
|
104
|
+
if (isSelfHealed) {
|
|
105
|
+
selfHealed++;
|
|
106
|
+
} else {
|
|
107
|
+
switch (result.status) {
|
|
108
|
+
case 'passed':
|
|
109
|
+
passed++;
|
|
110
|
+
break;
|
|
111
|
+
case 'failed':
|
|
112
|
+
failed++;
|
|
113
|
+
break;
|
|
114
|
+
case 'timedOut':
|
|
115
|
+
timedOut++;
|
|
116
|
+
break;
|
|
117
|
+
case 'skipped':
|
|
118
|
+
skipped++;
|
|
119
|
+
break;
|
|
120
|
+
case 'interrupted':
|
|
121
|
+
interrupted++;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
129
127
|
return testTotal + (result?.duration || 0);
|
|
130
128
|
}, 0),
|
|
131
129
|
0,
|
|
132
130
|
);
|
|
133
131
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
test.annotations.some((a) => a.type === 'self-healed')
|
|
143
|
-
) {
|
|
144
|
-
hasSelfHealed = true;
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
});
|
|
132
|
+
// Add to totals
|
|
133
|
+
totalPassed += passed;
|
|
134
|
+
totalFailed += failed;
|
|
135
|
+
totalTimedOut += timedOut;
|
|
136
|
+
totalSkipped += skipped;
|
|
137
|
+
totalInterrupted += interrupted;
|
|
138
|
+
totalSelfHealed += selfHealed;
|
|
139
|
+
totalDuration += fileDuration;
|
|
148
140
|
|
|
149
|
-
|
|
150
|
-
let status;
|
|
151
|
-
if (allPassed) {
|
|
152
|
-
status = '✅';
|
|
153
|
-
} else if (hasSelfHealed) {
|
|
154
|
-
status = '❤️🩹';
|
|
155
|
-
} else {
|
|
156
|
-
status = '❌';
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
markdown += `| ${suite.file} | ${status} | ${fileTests} | ${formatDuration(fileDuration)} |\n`;
|
|
141
|
+
markdown += `| ${suite.file} | ${passed ? passed + ' ✅' : ''} | ${selfHealed ? selfHealed + ' ❤️🩹' : ''} | ${failed ? failed + ' ❌' : ''} | ${timedOut ? timedOut + ' ⏰' : ''} | ${skipped ? skipped + ' ⏭️' : ''} | ${interrupted ? interrupted + ' ⚡' : ''} | ${formatDuration(fileDuration)} |\n`;
|
|
160
142
|
});
|
|
161
143
|
|
|
144
|
+
// Add totals row
|
|
145
|
+
markdown += `| **TOTAL** | **${totalPassed + ' ✅'}** | **${totalSelfHealed + ' ❤️🩹'}** | **${totalFailed + ' ❌'}** | **${totalTimedOut + ' ⏰'}** | **${totalSkipped + ' ⏭️'}** | **${totalInterrupted + ' ⚡'}** | **${formatDuration(totalDuration)}** |\n`;
|
|
146
|
+
|
|
162
147
|
markdown += `\n`;
|
|
163
148
|
|
|
164
149
|
// Generate test details sections
|
|
@@ -170,13 +155,12 @@ function generateMarkdown(jsonData) {
|
|
|
170
155
|
markdown += `### ${spec.title}\n\n`;
|
|
171
156
|
|
|
172
157
|
spec.tests.forEach((test) => {
|
|
173
|
-
|
|
174
|
-
const result = test.results && test.results[0];
|
|
158
|
+
const result = test.results && test.results.at(-1);
|
|
175
159
|
|
|
176
160
|
// Skip tests without results
|
|
177
161
|
if (!result) {
|
|
178
|
-
markdown += `**Status**: ⚠️ No Results
|
|
179
|
-
markdown += `**Duration**: N/A
|
|
162
|
+
markdown += `**Status**: ⚠️ No Results\n`;
|
|
163
|
+
markdown += `**Duration**: N/A\n`;
|
|
180
164
|
markdown += `**Objective**: Test skipped or no results available\n`;
|
|
181
165
|
markdown += `---\n\n`;
|
|
182
166
|
return;
|
|
@@ -194,8 +178,12 @@ function generateMarkdown(jsonData) {
|
|
|
194
178
|
status = '❌ Failed (❤️🩹 Self-Healed)';
|
|
195
179
|
} else if (result.status === 'failed') {
|
|
196
180
|
status = '❌ Failed';
|
|
181
|
+
} else if (result.status === 'timedOut') {
|
|
182
|
+
status = '⏰ Timed Out';
|
|
197
183
|
} else if (result.status === 'skipped') {
|
|
198
184
|
status = '⏭️ Skipped';
|
|
185
|
+
} else if (result.status === 'interrupted') {
|
|
186
|
+
status = '⚡ Interrupted';
|
|
199
187
|
} else {
|
|
200
188
|
status = `⚠️ ${result.status || 'Unknown'}`;
|
|
201
189
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1257
|
|
@@ -66,99 +66,84 @@ function generateMarkdown(jsonData) {
|
|
|
66
66
|
|
|
67
67
|
// Create report header
|
|
68
68
|
let markdown = `# Playwright Test Report\n\n`;
|
|
69
|
-
|
|
70
|
-
// Summary section
|
|
69
|
+
// Tests by file
|
|
71
70
|
markdown += `## Summary\n\n`;
|
|
72
|
-
markdown += `- **Start Time**: ${formatDate(stats.startTime)}\n`;
|
|
73
|
-
markdown += `- **Duration**: ${formatDuration(stats.duration)}\n`;
|
|
74
|
-
markdown += `- **Tests**: ${stats.expected + stats.unexpected}\n`;
|
|
75
|
-
markdown += `- **Passed**: ${stats.expected}\n`;
|
|
76
|
-
markdown += `- **Self-Healed**: ${selfHealedCount}\n`;
|
|
77
|
-
markdown += `- **Failed**: ${stats.unexpected - selfHealedCount}\n`;
|
|
78
|
-
markdown += `- **Skipped**: ${stats.skipped}\n`;
|
|
79
|
-
markdown += `- **Flaky**: ${stats.flaky}\n\n`;
|
|
80
|
-
|
|
81
|
-
// Create pass rate and visual indicator
|
|
82
|
-
const totalTests = stats.expected + stats.unexpected;
|
|
83
|
-
const passRate =
|
|
84
|
-
totalTests > 0 ? Math.round((stats.expected / totalTests) * 100) : 0;
|
|
85
|
-
const selfHealedRate =
|
|
86
|
-
totalTests > 0 ? Math.round((selfHealedCount / totalTests) * 100) : 0;
|
|
87
|
-
const failRate =
|
|
88
|
-
totalTests > 0
|
|
89
|
-
? Math.round(((stats.unexpected - selfHealedCount) / totalTests) * 100)
|
|
90
|
-
: 0;
|
|
91
|
-
|
|
92
|
-
markdown += `### Test Results\n\n`;
|
|
93
|
-
markdown += `- **Passed**: ${passRate}%\n`;
|
|
94
|
-
markdown += `- **Self-Healed**: ${selfHealedRate}%\n`;
|
|
95
|
-
markdown += `- **Failed**: ${failRate}%\n\n`;
|
|
96
|
-
|
|
97
|
-
// Create visual pass/self-healed/fail bar
|
|
98
|
-
const passChar = '🟢';
|
|
99
|
-
const selfHealedChar = '❤️🩹';
|
|
100
|
-
const failChar = '🔴';
|
|
101
|
-
|
|
102
|
-
const passCount = stats.expected;
|
|
103
|
-
const failCount = stats.unexpected - selfHealedCount;
|
|
104
|
-
|
|
105
|
-
const statusBar =
|
|
106
|
-
passChar.repeat(passCount) +
|
|
107
|
-
selfHealedChar.repeat(selfHealedCount) +
|
|
108
|
-
failChar.repeat(failCount);
|
|
109
|
-
|
|
110
|
-
markdown += `${statusBar}\n\n`;
|
|
111
71
|
|
|
112
|
-
//
|
|
113
|
-
markdown +=
|
|
72
|
+
// Create file summary table with status counts
|
|
73
|
+
markdown += `| File | Passed | Self-Healed | Failed | Timed Out | Skipped | Interrupted | Duration |\n`;
|
|
74
|
+
markdown += `| - | - | - | - | - | - | - | - |\n`;
|
|
114
75
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
// Track totals for summary row
|
|
77
|
+
let totalPassed = 0;
|
|
78
|
+
let totalFailed = 0;
|
|
79
|
+
let totalTimedOut = 0;
|
|
80
|
+
let totalSkipped = 0;
|
|
81
|
+
let totalInterrupted = 0;
|
|
82
|
+
let totalSelfHealed = 0;
|
|
83
|
+
let totalDuration = 0;
|
|
118
84
|
|
|
119
85
|
suites.forEach((suite) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
86
|
+
// Count tests by status for this file
|
|
87
|
+
let passed = 0;
|
|
88
|
+
let failed = 0;
|
|
89
|
+
let timedOut = 0;
|
|
90
|
+
let skipped = 0;
|
|
91
|
+
let interrupted = 0;
|
|
92
|
+
let selfHealed = 0;
|
|
93
|
+
|
|
124
94
|
const fileDuration = suite.specs.reduce(
|
|
125
95
|
(total, spec) =>
|
|
126
96
|
total +
|
|
127
97
|
spec.tests.reduce((testTotal, test) => {
|
|
128
|
-
const result = test.results && test.results
|
|
98
|
+
const result = test.results && test.results.at(-1);
|
|
99
|
+
const isSelfHealed =
|
|
100
|
+
test.annotations &&
|
|
101
|
+
test.annotations.some((a) => a.type === 'self-healed');
|
|
102
|
+
|
|
103
|
+
if (result) {
|
|
104
|
+
if (isSelfHealed) {
|
|
105
|
+
selfHealed++;
|
|
106
|
+
} else {
|
|
107
|
+
switch (result.status) {
|
|
108
|
+
case 'passed':
|
|
109
|
+
passed++;
|
|
110
|
+
break;
|
|
111
|
+
case 'failed':
|
|
112
|
+
failed++;
|
|
113
|
+
break;
|
|
114
|
+
case 'timedOut':
|
|
115
|
+
timedOut++;
|
|
116
|
+
break;
|
|
117
|
+
case 'skipped':
|
|
118
|
+
skipped++;
|
|
119
|
+
break;
|
|
120
|
+
case 'interrupted':
|
|
121
|
+
interrupted++;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
129
127
|
return testTotal + (result?.duration || 0);
|
|
130
128
|
}, 0),
|
|
131
129
|
0,
|
|
132
130
|
);
|
|
133
131
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
test.annotations.some((a) => a.type === 'self-healed')
|
|
143
|
-
) {
|
|
144
|
-
hasSelfHealed = true;
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
});
|
|
132
|
+
// Add to totals
|
|
133
|
+
totalPassed += passed;
|
|
134
|
+
totalFailed += failed;
|
|
135
|
+
totalTimedOut += timedOut;
|
|
136
|
+
totalSkipped += skipped;
|
|
137
|
+
totalInterrupted += interrupted;
|
|
138
|
+
totalSelfHealed += selfHealed;
|
|
139
|
+
totalDuration += fileDuration;
|
|
148
140
|
|
|
149
|
-
|
|
150
|
-
let status;
|
|
151
|
-
if (allPassed) {
|
|
152
|
-
status = '✅';
|
|
153
|
-
} else if (hasSelfHealed) {
|
|
154
|
-
status = '❤️🩹';
|
|
155
|
-
} else {
|
|
156
|
-
status = '❌';
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
markdown += `| ${suite.file} | ${status} | ${fileTests} | ${formatDuration(fileDuration)} |\n`;
|
|
141
|
+
markdown += `| ${suite.file} | ${passed ? passed + ' ✅' : ''} | ${selfHealed ? selfHealed + ' ❤️🩹' : ''} | ${failed ? failed + ' ❌' : ''} | ${timedOut ? timedOut + ' ⏰' : ''} | ${skipped ? skipped + ' ⏭️' : ''} | ${interrupted ? interrupted + ' ⚡' : ''} | ${formatDuration(fileDuration)} |\n`;
|
|
160
142
|
});
|
|
161
143
|
|
|
144
|
+
// Add totals row
|
|
145
|
+
markdown += `| **TOTAL** | **${totalPassed + ' ✅'}** | **${totalSelfHealed + ' ❤️🩹'}** | **${totalFailed + ' ❌'}** | **${totalTimedOut + ' ⏰'}** | **${totalSkipped + ' ⏭️'}** | **${totalInterrupted + ' ⚡'}** | **${formatDuration(totalDuration)}** |\n`;
|
|
146
|
+
|
|
162
147
|
markdown += `\n`;
|
|
163
148
|
|
|
164
149
|
// Generate test details sections
|
|
@@ -170,13 +155,12 @@ function generateMarkdown(jsonData) {
|
|
|
170
155
|
markdown += `### ${spec.title}\n\n`;
|
|
171
156
|
|
|
172
157
|
spec.tests.forEach((test) => {
|
|
173
|
-
|
|
174
|
-
const result = test.results && test.results[0];
|
|
158
|
+
const result = test.results && test.results.at(-1);
|
|
175
159
|
|
|
176
160
|
// Skip tests without results
|
|
177
161
|
if (!result) {
|
|
178
|
-
markdown += `**Status**: ⚠️ No Results
|
|
179
|
-
markdown += `**Duration**: N/A
|
|
162
|
+
markdown += `**Status**: ⚠️ No Results\n`;
|
|
163
|
+
markdown += `**Duration**: N/A\n`;
|
|
180
164
|
markdown += `**Objective**: Test skipped or no results available\n`;
|
|
181
165
|
markdown += `---\n\n`;
|
|
182
166
|
return;
|
|
@@ -194,8 +178,12 @@ function generateMarkdown(jsonData) {
|
|
|
194
178
|
status = '❌ Failed (❤️🩹 Self-Healed)';
|
|
195
179
|
} else if (result.status === 'failed') {
|
|
196
180
|
status = '❌ Failed';
|
|
181
|
+
} else if (result.status === 'timedOut') {
|
|
182
|
+
status = '⏰ Timed Out';
|
|
197
183
|
} else if (result.status === 'skipped') {
|
|
198
184
|
status = '⏭️ Skipped';
|
|
185
|
+
} else if (result.status === 'interrupted') {
|
|
186
|
+
status = '⚡ Interrupted';
|
|
199
187
|
} else {
|
|
200
188
|
status = `⚠️ ${result.status || 'Unknown'}`;
|
|
201
189
|
}
|