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
- 1256
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
- // Tests by file
113
- markdown += `## Test Files\n\n`;
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
- // Create file summary table
116
- markdown += `| File | Status | Tests | Duration |\n`;
117
- markdown += `| ---- | ------ | ----- | -------- |\n`;
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
- const fileTests = suite.specs.reduce(
121
- (count, spec) => count + spec.tests.length,
122
- 0,
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[0];
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
- // Check if all tests passed (including self-healed)
135
- const allPassed = suite.specs.every((spec) => spec.ok);
136
- // Check if any test self-healed
137
- let hasSelfHealed = false;
138
- suite.specs.forEach((spec) => {
139
- spec.tests.forEach((test) => {
140
- if (
141
- test.annotations &&
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
- // Determine status icon
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
- // Add null checking for results
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 \n`;
179
- markdown += `**Duration**: N/A \n`;
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
- 1256
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
- // Tests by file
113
- markdown += `## Test Files\n\n`;
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
- // Create file summary table
116
- markdown += `| File | Status | Tests | Duration |\n`;
117
- markdown += `| ---- | ------ | ----- | -------- |\n`;
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
- const fileTests = suite.specs.reduce(
121
- (count, spec) => count + spec.tests.length,
122
- 0,
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[0];
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
- // Check if all tests passed (including self-healed)
135
- const allPassed = suite.specs.every((spec) => spec.ok);
136
- // Check if any test self-healed
137
- let hasSelfHealed = false;
138
- suite.specs.forEach((spec) => {
139
- spec.tests.forEach((test) => {
140
- if (
141
- test.annotations &&
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
- // Determine status icon
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
- // Add null checking for results
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 \n`;
179
- markdown += `**Duration**: N/A \n`;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "2.23.2",
3
+ "version": "2.23.3",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",