@testsmith/testblocks 0.9.1 → 0.9.2
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/dist/cli/executor.js +1 -0
- package/dist/cli/index.js +9 -2
- package/dist/cli/reporters/HTMLReporter.js +21 -8
- package/dist/cli/reporters/JUnitReporter.js +16 -8
- package/dist/cli/reporters/types.d.ts +1 -0
- package/dist/client/assets/{index-BCKY2YTp.js → index-CVn_B7zc.js} +71 -71
- package/dist/client/assets/{index-BCKY2YTp.js.map → index-CVn_B7zc.js.map} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/core/blocks/playwright/assertions.js +10 -20
- package/dist/core/blocks/playwright/interactions.js +7 -14
- package/dist/core/blocks/playwright/navigation.js +2 -4
- package/dist/core/blocks/playwright/retrieval.js +3 -6
- package/dist/core/blocks/playwright/utils.d.ts +4 -0
- package/dist/core/blocks/playwright/utils.js +8 -0
- package/dist/core/types.d.ts +1 -0
- package/dist/server/executor.js +3 -0
- package/dist/server/globals.d.ts +4 -0
- package/dist/server/globals.js +7 -0
- package/dist/server/index.js +4 -2
- package/dist/server/startServer.js +6 -4
- package/package.json +1 -1
package/dist/cli/executor.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -208,6 +208,7 @@ program
|
|
|
208
208
|
const testDir = path.dirname(files[0]);
|
|
209
209
|
globalsPath = findGlobalsFile(testDir) || globalsPath;
|
|
210
210
|
}
|
|
211
|
+
let globalTimeout;
|
|
211
212
|
if (fs.existsSync(globalsPath)) {
|
|
212
213
|
try {
|
|
213
214
|
const globalsContent = fs.readFileSync(globalsPath, 'utf-8');
|
|
@@ -218,6 +219,9 @@ program
|
|
|
218
219
|
if (globals.procedures && typeof globals.procedures === 'object') {
|
|
219
220
|
globalProcedures = globals.procedures;
|
|
220
221
|
}
|
|
222
|
+
if (typeof globals.timeout === 'number') {
|
|
223
|
+
globalTimeout = globals.timeout;
|
|
224
|
+
}
|
|
221
225
|
}
|
|
222
226
|
catch (e) {
|
|
223
227
|
console.warn(`Warning: Could not load globals from ${globalsPath}: ${e.message}`);
|
|
@@ -271,10 +275,13 @@ program
|
|
|
271
275
|
if (options.baseUrl) {
|
|
272
276
|
variables.baseUrl = options.baseUrl;
|
|
273
277
|
}
|
|
278
|
+
// Determine timeout: CLI option takes precedence, then globals.json, then default
|
|
279
|
+
const cliTimeout = parseInt(options.timeout, 10);
|
|
280
|
+
const effectiveTimeout = options.timeout !== '30000' ? cliTimeout : (globalTimeout ?? cliTimeout);
|
|
274
281
|
// Create executor options
|
|
275
282
|
const executorOptions = {
|
|
276
283
|
headless: !options.headed,
|
|
277
|
-
timeout:
|
|
284
|
+
timeout: effectiveTimeout,
|
|
278
285
|
baseUrl: options.baseUrl,
|
|
279
286
|
variables,
|
|
280
287
|
procedures: globalProcedures,
|
|
@@ -447,7 +454,7 @@ program
|
|
|
447
454
|
'test:ci': 'testblocks run tests/**/*.testblocks.json -r console,html,junit -o reports',
|
|
448
455
|
},
|
|
449
456
|
devDependencies: {
|
|
450
|
-
'@testsmith/testblocks': '^0.9.
|
|
457
|
+
'@testsmith/testblocks': '^0.9.2',
|
|
451
458
|
},
|
|
452
459
|
};
|
|
453
460
|
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
|
|
@@ -49,8 +49,9 @@ class HTMLReporter {
|
|
|
49
49
|
onTestFileComplete(file, testFile, results) {
|
|
50
50
|
this.allResults.push({ file, testFile, results });
|
|
51
51
|
const passed = results.filter(r => r.status === 'passed').length;
|
|
52
|
-
const
|
|
53
|
-
|
|
52
|
+
const skipped = results.filter(r => r.status === 'skipped').length;
|
|
53
|
+
const failed = results.filter(r => r.status !== 'passed' && r.status !== 'skipped').length;
|
|
54
|
+
console.log(` ${passed} passed, ${failed} failed, ${skipped} skipped\n`);
|
|
54
55
|
}
|
|
55
56
|
onComplete(allResults) {
|
|
56
57
|
if (!fs.existsSync(this.outputDir)) {
|
|
@@ -60,11 +61,12 @@ class HTMLReporter {
|
|
|
60
61
|
const outputPath = path.join(this.outputDir, `report-${timestamp}.html`);
|
|
61
62
|
const totalTests = allResults.reduce((sum, r) => sum + r.results.length, 0);
|
|
62
63
|
const passed = allResults.reduce((sum, r) => sum + r.results.filter(t => t.status === 'passed').length, 0);
|
|
63
|
-
const
|
|
64
|
+
const skipped = allResults.reduce((sum, r) => sum + r.results.filter(t => t.status === 'skipped').length, 0);
|
|
65
|
+
const failed = allResults.reduce((sum, r) => sum + r.results.filter(t => t.status !== 'passed' && t.status !== 'skipped').length, 0);
|
|
64
66
|
const totalDuration = allResults.reduce((sum, r) => sum + r.results.reduce((s, t) => s + t.duration, 0), 0);
|
|
65
67
|
const html = generateHTMLReport({
|
|
66
68
|
timestamp: new Date().toISOString(),
|
|
67
|
-
summary: { totalTests, passed, failed, duration: totalDuration },
|
|
69
|
+
summary: { totalTests, passed, failed, skipped, duration: totalDuration },
|
|
68
70
|
testFiles: this.allResults,
|
|
69
71
|
});
|
|
70
72
|
fs.writeFileSync(outputPath, html);
|
|
@@ -81,19 +83,24 @@ function generateHTMLReport(data) {
|
|
|
81
83
|
let actualTests = 0;
|
|
82
84
|
let actualPassed = 0;
|
|
83
85
|
let actualFailed = 0;
|
|
86
|
+
let actualSkipped = 0;
|
|
84
87
|
for (const { results } of testFiles) {
|
|
85
88
|
for (const result of results) {
|
|
86
89
|
if (!result.isLifecycle) {
|
|
87
90
|
actualTests++;
|
|
88
91
|
if (result.status === 'passed')
|
|
89
92
|
actualPassed++;
|
|
93
|
+
else if (result.status === 'skipped')
|
|
94
|
+
actualSkipped++;
|
|
90
95
|
else
|
|
91
96
|
actualFailed++;
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
// Pass rate excludes skipped tests from calculation
|
|
101
|
+
const runTests = actualTests - actualSkipped;
|
|
102
|
+
const passRate = runTests > 0
|
|
103
|
+
? ((actualPassed / runTests) * 100).toFixed(1)
|
|
97
104
|
: '0';
|
|
98
105
|
let html = `<!DOCTYPE html>
|
|
99
106
|
<html lang="en">
|
|
@@ -123,6 +130,10 @@ ${getHTMLStyles()}
|
|
|
123
130
|
<div class="value">${actualFailed}</div>
|
|
124
131
|
<div class="label">Failed</div>
|
|
125
132
|
</div>
|
|
133
|
+
<div class="summary-card skipped">
|
|
134
|
+
<div class="value">${actualSkipped}</div>
|
|
135
|
+
<div class="label">Skipped</div>
|
|
136
|
+
</div>
|
|
126
137
|
<div class="summary-card">
|
|
127
138
|
<div class="value">${passRate}%</div>
|
|
128
139
|
<div class="label">Pass Rate</div>
|
|
@@ -150,12 +161,13 @@ function renderTestFile(file, testFile, results) {
|
|
|
150
161
|
const lifecycleResults = results.filter(r => r.isLifecycle);
|
|
151
162
|
const testResults = results.filter(r => !r.isLifecycle);
|
|
152
163
|
const filePassed = testResults.filter(r => r.status === 'passed').length;
|
|
153
|
-
const
|
|
164
|
+
const fileSkipped = testResults.filter(r => r.status === 'skipped').length;
|
|
165
|
+
const fileFailed = testResults.filter(r => r.status !== 'passed' && r.status !== 'skipped').length;
|
|
154
166
|
let html = `
|
|
155
167
|
<div class="test-file">
|
|
156
168
|
<div class="test-file-header">
|
|
157
169
|
<span>${(0, utils_1.escapeHtml)(testFile.name)}</span>
|
|
158
|
-
<span class="test-file-path">${(0, utils_1.escapeHtml)(file)} • ${filePassed} passed, ${fileFailed} failed</span>
|
|
170
|
+
<span class="test-file-path">${(0, utils_1.escapeHtml)(file)} • ${filePassed} passed, ${fileFailed} failed, ${fileSkipped} skipped</span>
|
|
159
171
|
</div>
|
|
160
172
|
`;
|
|
161
173
|
// Render lifecycle hooks (beforeAll)
|
|
@@ -336,6 +348,7 @@ function getHTMLStyles() {
|
|
|
336
348
|
.summary-card .label { font-size: 14px; color: var(--color-text-secondary); }
|
|
337
349
|
.summary-card.passed .value { color: var(--color-passed); }
|
|
338
350
|
.summary-card.failed .value { color: var(--color-failed); }
|
|
351
|
+
.summary-card.skipped .value { color: var(--color-skipped); }
|
|
339
352
|
.test-file {
|
|
340
353
|
background: var(--color-surface);
|
|
341
354
|
border: 1px solid var(--color-border);
|
|
@@ -49,8 +49,9 @@ class JUnitReporter {
|
|
|
49
49
|
onTestFileComplete(file, testFile, results) {
|
|
50
50
|
this.allResults.push({ file, testFile, results });
|
|
51
51
|
const passed = results.filter(r => r.status === 'passed').length;
|
|
52
|
-
const
|
|
53
|
-
|
|
52
|
+
const skipped = results.filter(r => r.status === 'skipped').length;
|
|
53
|
+
const failed = results.filter(r => r.status !== 'passed' && r.status !== 'skipped').length;
|
|
54
|
+
console.log(` ${passed} passed, ${failed} failed, ${skipped} skipped\n`);
|
|
54
55
|
}
|
|
55
56
|
onComplete(allResults) {
|
|
56
57
|
if (!fs.existsSync(this.outputDir)) {
|
|
@@ -63,7 +64,8 @@ class JUnitReporter {
|
|
|
63
64
|
summary: {
|
|
64
65
|
totalTests: allResults.reduce((sum, r) => sum + r.results.length, 0),
|
|
65
66
|
passed: allResults.reduce((sum, r) => sum + r.results.filter(t => t.status === 'passed').length, 0),
|
|
66
|
-
failed: allResults.reduce((sum, r) => sum + r.results.filter(t => t.status !== 'passed').length, 0),
|
|
67
|
+
failed: allResults.reduce((sum, r) => sum + r.results.filter(t => t.status !== 'passed' && t.status !== 'skipped').length, 0),
|
|
68
|
+
skipped: allResults.reduce((sum, r) => sum + r.results.filter(t => t.status === 'skipped').length, 0),
|
|
67
69
|
duration: allResults.reduce((sum, r) => sum + r.results.reduce((s, t) => s + t.duration, 0), 0),
|
|
68
70
|
},
|
|
69
71
|
testFiles: this.allResults,
|
|
@@ -79,19 +81,25 @@ exports.JUnitReporter = JUnitReporter;
|
|
|
79
81
|
function generateJUnitXML(data) {
|
|
80
82
|
const { testFiles } = data;
|
|
81
83
|
const totalTests = testFiles.reduce((sum, f) => sum + f.results.length, 0);
|
|
82
|
-
const failures = testFiles.reduce((sum, f) => sum + f.results.filter(t => t.status !== 'passed').length, 0);
|
|
84
|
+
const failures = testFiles.reduce((sum, f) => sum + f.results.filter(t => t.status !== 'passed' && t.status !== 'skipped').length, 0);
|
|
85
|
+
const skipped = testFiles.reduce((sum, f) => sum + f.results.filter(t => t.status === 'skipped').length, 0);
|
|
83
86
|
const totalTime = testFiles.reduce((sum, f) => sum + f.results.reduce((s, t) => s + t.duration, 0), 0) / 1000;
|
|
84
87
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
85
|
-
xml += `<testsuites tests="${totalTests}" failures="${failures}" time="${totalTime.toFixed(3)}">\n`;
|
|
88
|
+
xml += `<testsuites tests="${totalTests}" failures="${failures}" skipped="${skipped}" time="${totalTime.toFixed(3)}">\n`;
|
|
86
89
|
for (const { file, testFile, results } of testFiles) {
|
|
87
90
|
const suiteTests = results.length;
|
|
88
|
-
const suiteFailures = results.filter(r => r.status !== 'passed').length;
|
|
91
|
+
const suiteFailures = results.filter(r => r.status !== 'passed' && r.status !== 'skipped').length;
|
|
92
|
+
const suiteSkipped = results.filter(r => r.status === 'skipped').length;
|
|
89
93
|
const suiteTime = results.reduce((s, t) => s + t.duration, 0) / 1000;
|
|
90
|
-
xml += ` <testsuite name="${(0, utils_1.escapeXml)(testFile.name)}" tests="${suiteTests}" failures="${suiteFailures}" time="${suiteTime.toFixed(3)}" file="${(0, utils_1.escapeXml)(file)}">\n`;
|
|
94
|
+
xml += ` <testsuite name="${(0, utils_1.escapeXml)(testFile.name)}" tests="${suiteTests}" failures="${suiteFailures}" skipped="${suiteSkipped}" time="${suiteTime.toFixed(3)}" file="${(0, utils_1.escapeXml)(file)}">\n`;
|
|
91
95
|
for (const result of results) {
|
|
92
96
|
const testTime = result.duration / 1000;
|
|
93
97
|
xml += ` <testcase name="${(0, utils_1.escapeXml)(result.testName)}" classname="${(0, utils_1.escapeXml)(testFile.name)}" time="${testTime.toFixed(3)}">\n`;
|
|
94
|
-
if (result.status
|
|
98
|
+
if (result.status === 'skipped') {
|
|
99
|
+
const message = result.error?.message || 'Test skipped';
|
|
100
|
+
xml += ` <skipped message="${(0, utils_1.escapeXml)(message)}"/>\n`;
|
|
101
|
+
}
|
|
102
|
+
else if (result.status !== 'passed' && result.error) {
|
|
95
103
|
xml += ` <failure message="${(0, utils_1.escapeXml)(result.error.message)}">\n`;
|
|
96
104
|
xml += `${(0, utils_1.escapeXml)(result.error.stack || result.error.message)}\n`;
|
|
97
105
|
xml += ` </failure>\n`;
|