testblocks 0.1.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.
- package/LICENSE +21 -0
- package/README.md +333 -0
- package/dist/cli/executor.d.ts +32 -0
- package/dist/cli/executor.js +517 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +411 -0
- package/dist/cli/reporters.d.ts +62 -0
- package/dist/cli/reporters.js +451 -0
- package/dist/client/assets/index-4hbFPUhP.js +2087 -0
- package/dist/client/assets/index-4hbFPUhP.js.map +1 -0
- package/dist/client/assets/index-Dnk1ti7l.css +1 -0
- package/dist/client/index.html +25 -0
- package/dist/core/blocks/api.d.ts +2 -0
- package/dist/core/blocks/api.js +610 -0
- package/dist/core/blocks/data-driven.d.ts +2 -0
- package/dist/core/blocks/data-driven.js +245 -0
- package/dist/core/blocks/index.d.ts +15 -0
- package/dist/core/blocks/index.js +71 -0
- package/dist/core/blocks/lifecycle.d.ts +2 -0
- package/dist/core/blocks/lifecycle.js +199 -0
- package/dist/core/blocks/logic.d.ts +2 -0
- package/dist/core/blocks/logic.js +357 -0
- package/dist/core/blocks/playwright.d.ts +2 -0
- package/dist/core/blocks/playwright.js +764 -0
- package/dist/core/blocks/procedures.d.ts +5 -0
- package/dist/core/blocks/procedures.js +321 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +44 -0
- package/dist/core/plugins.d.ts +66 -0
- package/dist/core/plugins.js +118 -0
- package/dist/core/types.d.ts +153 -0
- package/dist/core/types.js +2 -0
- package/dist/server/codegenManager.d.ts +54 -0
- package/dist/server/codegenManager.js +259 -0
- package/dist/server/codegenParser.d.ts +17 -0
- package/dist/server/codegenParser.js +598 -0
- package/dist/server/executor.d.ts +37 -0
- package/dist/server/executor.js +672 -0
- package/dist/server/globals.d.ts +85 -0
- package/dist/server/globals.js +273 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +361 -0
- package/dist/server/plugins.d.ts +55 -0
- package/dist/server/plugins.js +206 -0
- package/package.json +103 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dataDrivenBlocks = void 0;
|
|
4
|
+
// Data-Driven Testing Blocks
|
|
5
|
+
exports.dataDrivenBlocks = [
|
|
6
|
+
// Define test data inline
|
|
7
|
+
{
|
|
8
|
+
type: 'data_define',
|
|
9
|
+
category: 'Data',
|
|
10
|
+
color: '#00897B',
|
|
11
|
+
tooltip: 'Define test data sets for data-driven testing',
|
|
12
|
+
inputs: [
|
|
13
|
+
{ name: 'DATA_JSON', type: 'field', fieldType: 'text', default: '[{"name": "test1", "value": 1}]' },
|
|
14
|
+
],
|
|
15
|
+
output: { type: 'Array' },
|
|
16
|
+
execute: async (params, context) => {
|
|
17
|
+
const json = resolveVariables(params.DATA_JSON, context);
|
|
18
|
+
return JSON.parse(json);
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
// Load data from variable
|
|
22
|
+
{
|
|
23
|
+
type: 'data_from_variable',
|
|
24
|
+
category: 'Data',
|
|
25
|
+
color: '#00897B',
|
|
26
|
+
tooltip: 'Get test data from a variable',
|
|
27
|
+
inputs: [
|
|
28
|
+
{ name: 'NAME', type: 'field', fieldType: 'text', required: true },
|
|
29
|
+
],
|
|
30
|
+
output: { type: 'Array' },
|
|
31
|
+
execute: async (params, context) => {
|
|
32
|
+
const name = params.NAME;
|
|
33
|
+
return context.variables.get(name) || [];
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
// Get current data value
|
|
37
|
+
{
|
|
38
|
+
type: 'data_get_current',
|
|
39
|
+
category: 'Data',
|
|
40
|
+
color: '#00897B',
|
|
41
|
+
tooltip: 'Get a value from the current data set',
|
|
42
|
+
inputs: [
|
|
43
|
+
{ name: 'KEY', type: 'field', fieldType: 'text', required: true },
|
|
44
|
+
],
|
|
45
|
+
output: { type: ['String', 'Number', 'Boolean', 'Object', 'Array'] },
|
|
46
|
+
execute: async (params, context) => {
|
|
47
|
+
const key = params.KEY;
|
|
48
|
+
if (!context.currentData) {
|
|
49
|
+
throw new Error('No data set available. This block must be used inside a data-driven test.');
|
|
50
|
+
}
|
|
51
|
+
const value = context.currentData.values[key];
|
|
52
|
+
if (value === undefined) {
|
|
53
|
+
context.logger.warn(`Data key "${key}" not found in current data set`);
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// Get current iteration index
|
|
59
|
+
{
|
|
60
|
+
type: 'data_get_index',
|
|
61
|
+
category: 'Data',
|
|
62
|
+
color: '#00897B',
|
|
63
|
+
tooltip: 'Get the current data iteration index (0-based)',
|
|
64
|
+
inputs: [],
|
|
65
|
+
output: { type: 'Number' },
|
|
66
|
+
execute: async (params, context) => {
|
|
67
|
+
return context.dataIndex ?? 0;
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// Get current data set name
|
|
71
|
+
{
|
|
72
|
+
type: 'data_get_name',
|
|
73
|
+
category: 'Data',
|
|
74
|
+
color: '#00897B',
|
|
75
|
+
tooltip: 'Get the name of the current data set',
|
|
76
|
+
inputs: [],
|
|
77
|
+
output: { type: 'String' },
|
|
78
|
+
execute: async (params, context) => {
|
|
79
|
+
return context.currentData?.name ?? `Iteration ${(context.dataIndex ?? 0) + 1}`;
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
// For each data - iterate over data array
|
|
83
|
+
{
|
|
84
|
+
type: 'data_foreach',
|
|
85
|
+
category: 'Data',
|
|
86
|
+
color: '#00897B',
|
|
87
|
+
tooltip: 'Run steps for each item in a data set',
|
|
88
|
+
inputs: [
|
|
89
|
+
{ name: 'DATA', type: 'value', check: 'Array', required: true },
|
|
90
|
+
{ name: 'ITEM_VAR', type: 'field', fieldType: 'text', default: 'item' },
|
|
91
|
+
{ name: 'INDEX_VAR', type: 'field', fieldType: 'text', default: 'index' },
|
|
92
|
+
{ name: 'DO', type: 'statement' },
|
|
93
|
+
],
|
|
94
|
+
previousStatement: true,
|
|
95
|
+
nextStatement: true,
|
|
96
|
+
execute: async (params, _context) => {
|
|
97
|
+
const data = params.DATA;
|
|
98
|
+
const itemVar = params.ITEM_VAR;
|
|
99
|
+
const indexVar = params.INDEX_VAR;
|
|
100
|
+
return {
|
|
101
|
+
dataLoop: true,
|
|
102
|
+
data,
|
|
103
|
+
itemVar,
|
|
104
|
+
indexVar,
|
|
105
|
+
statement: 'DO',
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
// Create data row
|
|
110
|
+
{
|
|
111
|
+
type: 'data_row',
|
|
112
|
+
category: 'Data',
|
|
113
|
+
color: '#00897B',
|
|
114
|
+
tooltip: 'Create a single data row with key-value pairs',
|
|
115
|
+
inputs: [
|
|
116
|
+
{ name: 'NAME', type: 'field', fieldType: 'text', default: '' },
|
|
117
|
+
{ name: 'JSON', type: 'field', fieldType: 'text', default: '{}' },
|
|
118
|
+
],
|
|
119
|
+
output: { type: 'Object' },
|
|
120
|
+
execute: async (params, context) => {
|
|
121
|
+
const name = params.NAME;
|
|
122
|
+
const json = resolveVariables(params.JSON, context);
|
|
123
|
+
return {
|
|
124
|
+
name: name || undefined,
|
|
125
|
+
values: JSON.parse(json),
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
// Data table - create multiple rows at once
|
|
130
|
+
{
|
|
131
|
+
type: 'data_table',
|
|
132
|
+
category: 'Data',
|
|
133
|
+
color: '#00897B',
|
|
134
|
+
tooltip: 'Create a data table with headers and rows',
|
|
135
|
+
inputs: [
|
|
136
|
+
{ name: 'HEADERS', type: 'field', fieldType: 'text', default: 'username, password, expected' },
|
|
137
|
+
{ name: 'ROWS', type: 'field', fieldType: 'text', default: 'user1, pass1, true\nuser2, pass2, false' },
|
|
138
|
+
],
|
|
139
|
+
output: { type: 'Array' },
|
|
140
|
+
execute: async (params, context) => {
|
|
141
|
+
const headersStr = params.HEADERS;
|
|
142
|
+
const rowsStr = resolveVariables(params.ROWS, context);
|
|
143
|
+
const headers = headersStr.split(',').map(h => h.trim());
|
|
144
|
+
const rows = rowsStr.split('\n').filter(r => r.trim());
|
|
145
|
+
return rows.map((row, index) => {
|
|
146
|
+
const values = row.split(',').map(v => {
|
|
147
|
+
const trimmed = v.trim();
|
|
148
|
+
// Try to parse as JSON for numbers, booleans, etc.
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(trimmed);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return trimmed;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
const obj = {};
|
|
157
|
+
headers.forEach((header, i) => {
|
|
158
|
+
obj[header] = values[i];
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
name: `Row ${index + 1}`,
|
|
162
|
+
values: obj,
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
// CSV-style data
|
|
168
|
+
{
|
|
169
|
+
type: 'data_csv',
|
|
170
|
+
category: 'Data',
|
|
171
|
+
color: '#00897B',
|
|
172
|
+
tooltip: 'Parse CSV-style data (first row as headers)',
|
|
173
|
+
inputs: [
|
|
174
|
+
{ name: 'CSV', type: 'field', fieldType: 'text', default: 'name,value\ntest1,100\ntest2,200' },
|
|
175
|
+
],
|
|
176
|
+
output: { type: 'Array' },
|
|
177
|
+
execute: async (params, context) => {
|
|
178
|
+
const csv = resolveVariables(params.CSV, context);
|
|
179
|
+
const lines = csv.split('\n').filter(l => l.trim());
|
|
180
|
+
if (lines.length < 2) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
const headers = lines[0].split(',').map(h => h.trim());
|
|
184
|
+
return lines.slice(1).map((line, index) => {
|
|
185
|
+
const values = line.split(',').map(v => {
|
|
186
|
+
const trimmed = v.trim();
|
|
187
|
+
try {
|
|
188
|
+
return JSON.parse(trimmed);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return trimmed;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const obj = {};
|
|
195
|
+
headers.forEach((header, i) => {
|
|
196
|
+
obj[header] = values[i];
|
|
197
|
+
});
|
|
198
|
+
return {
|
|
199
|
+
name: `Row ${index + 1}`,
|
|
200
|
+
values: obj,
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
// Generate range of numbers
|
|
206
|
+
{
|
|
207
|
+
type: 'data_range',
|
|
208
|
+
category: 'Data',
|
|
209
|
+
color: '#00897B',
|
|
210
|
+
tooltip: 'Generate a range of numbers as data',
|
|
211
|
+
inputs: [
|
|
212
|
+
{ name: 'START', type: 'field', fieldType: 'number', default: 1 },
|
|
213
|
+
{ name: 'END', type: 'field', fieldType: 'number', default: 10 },
|
|
214
|
+
{ name: 'STEP', type: 'field', fieldType: 'number', default: 1 },
|
|
215
|
+
{ name: 'VAR_NAME', type: 'field', fieldType: 'text', default: 'n' },
|
|
216
|
+
],
|
|
217
|
+
output: { type: 'Array' },
|
|
218
|
+
execute: async (params, _context) => {
|
|
219
|
+
const start = params.START;
|
|
220
|
+
const end = params.END;
|
|
221
|
+
const step = params.STEP;
|
|
222
|
+
const varName = params.VAR_NAME;
|
|
223
|
+
const result = [];
|
|
224
|
+
for (let i = start; i <= end; i += step) {
|
|
225
|
+
result.push({
|
|
226
|
+
name: `${varName}=${i}`,
|
|
227
|
+
values: { [varName]: i },
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
// Helper function
|
|
235
|
+
function resolveVariables(text, context) {
|
|
236
|
+
return text.replace(/\$\{(\w+)\}/g, (_, varName) => {
|
|
237
|
+
// First check current data
|
|
238
|
+
if (context.currentData?.values[varName] !== undefined) {
|
|
239
|
+
return String(context.currentData.values[varName]);
|
|
240
|
+
}
|
|
241
|
+
// Then check variables
|
|
242
|
+
const value = context.variables.get(varName);
|
|
243
|
+
return value !== undefined ? String(value) : '';
|
|
244
|
+
});
|
|
245
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { apiBlocks } from './api';
|
|
2
|
+
export { playwrightBlocks } from './playwright';
|
|
3
|
+
export { logicBlocks } from './logic';
|
|
4
|
+
export { lifecycleBlocks } from './lifecycle';
|
|
5
|
+
export { dataDrivenBlocks } from './data-driven';
|
|
6
|
+
export { procedureBlocks, registerProcedure, getProcedure, clearProcedures } from './procedures';
|
|
7
|
+
import { BlockDefinition } from '../types';
|
|
8
|
+
export declare const builtInBlocks: BlockDefinition[];
|
|
9
|
+
export declare const blockRegistry: Map<string, BlockDefinition>;
|
|
10
|
+
export declare function registerBlock(block: BlockDefinition): void;
|
|
11
|
+
export declare function registerBlocks(blocks: BlockDefinition[]): void;
|
|
12
|
+
export declare function getBlock(type: string): BlockDefinition | undefined;
|
|
13
|
+
export declare function getAllBlocks(): BlockDefinition[];
|
|
14
|
+
export declare function getBlocksByCategory(category: string): BlockDefinition[];
|
|
15
|
+
export declare function getCategories(): string[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.blockRegistry = exports.builtInBlocks = exports.clearProcedures = exports.getProcedure = exports.registerProcedure = exports.procedureBlocks = exports.dataDrivenBlocks = exports.lifecycleBlocks = exports.logicBlocks = exports.playwrightBlocks = exports.apiBlocks = void 0;
|
|
4
|
+
exports.registerBlock = registerBlock;
|
|
5
|
+
exports.registerBlocks = registerBlocks;
|
|
6
|
+
exports.getBlock = getBlock;
|
|
7
|
+
exports.getAllBlocks = getAllBlocks;
|
|
8
|
+
exports.getBlocksByCategory = getBlocksByCategory;
|
|
9
|
+
exports.getCategories = getCategories;
|
|
10
|
+
var api_1 = require("./api");
|
|
11
|
+
Object.defineProperty(exports, "apiBlocks", { enumerable: true, get: function () { return api_1.apiBlocks; } });
|
|
12
|
+
var playwright_1 = require("./playwright");
|
|
13
|
+
Object.defineProperty(exports, "playwrightBlocks", { enumerable: true, get: function () { return playwright_1.playwrightBlocks; } });
|
|
14
|
+
var logic_1 = require("./logic");
|
|
15
|
+
Object.defineProperty(exports, "logicBlocks", { enumerable: true, get: function () { return logic_1.logicBlocks; } });
|
|
16
|
+
var lifecycle_1 = require("./lifecycle");
|
|
17
|
+
Object.defineProperty(exports, "lifecycleBlocks", { enumerable: true, get: function () { return lifecycle_1.lifecycleBlocks; } });
|
|
18
|
+
var data_driven_1 = require("./data-driven");
|
|
19
|
+
Object.defineProperty(exports, "dataDrivenBlocks", { enumerable: true, get: function () { return data_driven_1.dataDrivenBlocks; } });
|
|
20
|
+
var procedures_1 = require("./procedures");
|
|
21
|
+
Object.defineProperty(exports, "procedureBlocks", { enumerable: true, get: function () { return procedures_1.procedureBlocks; } });
|
|
22
|
+
Object.defineProperty(exports, "registerProcedure", { enumerable: true, get: function () { return procedures_1.registerProcedure; } });
|
|
23
|
+
Object.defineProperty(exports, "getProcedure", { enumerable: true, get: function () { return procedures_1.getProcedure; } });
|
|
24
|
+
Object.defineProperty(exports, "clearProcedures", { enumerable: true, get: function () { return procedures_1.clearProcedures; } });
|
|
25
|
+
const api_2 = require("./api");
|
|
26
|
+
const playwright_2 = require("./playwright");
|
|
27
|
+
const logic_2 = require("./logic");
|
|
28
|
+
const lifecycle_2 = require("./lifecycle");
|
|
29
|
+
const data_driven_2 = require("./data-driven");
|
|
30
|
+
const procedures_2 = require("./procedures");
|
|
31
|
+
// All built-in blocks
|
|
32
|
+
exports.builtInBlocks = [
|
|
33
|
+
...api_2.apiBlocks,
|
|
34
|
+
...playwright_2.playwrightBlocks,
|
|
35
|
+
...logic_2.logicBlocks,
|
|
36
|
+
...lifecycle_2.lifecycleBlocks,
|
|
37
|
+
...data_driven_2.dataDrivenBlocks,
|
|
38
|
+
...procedures_2.procedureBlocks,
|
|
39
|
+
];
|
|
40
|
+
// Block registry for runtime lookup
|
|
41
|
+
exports.blockRegistry = new Map();
|
|
42
|
+
// Initialize registry with built-in blocks
|
|
43
|
+
exports.builtInBlocks.forEach(block => {
|
|
44
|
+
exports.blockRegistry.set(block.type, block);
|
|
45
|
+
});
|
|
46
|
+
// Function to register custom blocks (for plugins)
|
|
47
|
+
function registerBlock(block) {
|
|
48
|
+
exports.blockRegistry.set(block.type, block);
|
|
49
|
+
}
|
|
50
|
+
// Function to register multiple blocks
|
|
51
|
+
function registerBlocks(blocks) {
|
|
52
|
+
blocks.forEach(block => registerBlock(block));
|
|
53
|
+
}
|
|
54
|
+
// Get a block definition by type
|
|
55
|
+
function getBlock(type) {
|
|
56
|
+
return exports.blockRegistry.get(type);
|
|
57
|
+
}
|
|
58
|
+
// Get all registered blocks
|
|
59
|
+
function getAllBlocks() {
|
|
60
|
+
return Array.from(exports.blockRegistry.values());
|
|
61
|
+
}
|
|
62
|
+
// Get blocks by category
|
|
63
|
+
function getBlocksByCategory(category) {
|
|
64
|
+
return getAllBlocks().filter(block => block.category === category);
|
|
65
|
+
}
|
|
66
|
+
// Get all categories
|
|
67
|
+
function getCategories() {
|
|
68
|
+
const categories = new Set();
|
|
69
|
+
getAllBlocks().forEach(block => categories.add(block.category));
|
|
70
|
+
return Array.from(categories);
|
|
71
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lifecycleBlocks = void 0;
|
|
4
|
+
// Lifecycle and Hook Blocks
|
|
5
|
+
exports.lifecycleBlocks = [
|
|
6
|
+
// Test Case - container for test steps
|
|
7
|
+
{
|
|
8
|
+
type: 'test_case',
|
|
9
|
+
category: 'Tests',
|
|
10
|
+
color: '#1E88E5',
|
|
11
|
+
tooltip: 'A test case containing steps to execute',
|
|
12
|
+
inputs: [
|
|
13
|
+
{ name: 'NAME', type: 'field', fieldType: 'multiline', default: 'Test Case' },
|
|
14
|
+
{ name: 'STEPS', type: 'statement' },
|
|
15
|
+
],
|
|
16
|
+
execute: async (params, _context) => {
|
|
17
|
+
// Marker block - steps are executed by the executor
|
|
18
|
+
return { testCase: true, name: params.NAME, statement: 'STEPS' };
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
// Data-Driven Test Case - runs multiple times with different data
|
|
22
|
+
{
|
|
23
|
+
type: 'test_case_data_driven',
|
|
24
|
+
category: 'Tests',
|
|
25
|
+
color: '#1565C0',
|
|
26
|
+
tooltip: 'A data-driven test case that runs for each row of data (CSV format: header row, then data rows)',
|
|
27
|
+
inputs: [
|
|
28
|
+
{ name: 'NAME', type: 'field', fieldType: 'text', default: 'Data-Driven Test' },
|
|
29
|
+
{ name: 'DATA', type: 'field', fieldType: 'multiline', default: 'username,password,expected\nuser1,pass1,true\nuser2,pass2,false' },
|
|
30
|
+
{ name: 'STEPS', type: 'statement' },
|
|
31
|
+
],
|
|
32
|
+
execute: async (params, _context) => {
|
|
33
|
+
// Marker block - actual data parsing and execution handled by executor
|
|
34
|
+
const csvData = params.DATA;
|
|
35
|
+
const rows = csvData.trim().split('\n').map(row => row.split(',').map(cell => cell.trim()));
|
|
36
|
+
if (rows.length < 2) {
|
|
37
|
+
return { testCase: true, name: params.NAME, dataDriven: true, data: [], statement: 'STEPS' };
|
|
38
|
+
}
|
|
39
|
+
const headers = rows[0];
|
|
40
|
+
const data = rows.slice(1).map((row, index) => {
|
|
41
|
+
const values = {};
|
|
42
|
+
headers.forEach((header, i) => {
|
|
43
|
+
let value = row[i] || '';
|
|
44
|
+
// Try to parse as boolean or number
|
|
45
|
+
if (value === 'true')
|
|
46
|
+
value = true;
|
|
47
|
+
else if (value === 'false')
|
|
48
|
+
value = false;
|
|
49
|
+
else if (!isNaN(Number(value)) && value !== '')
|
|
50
|
+
value = Number(value);
|
|
51
|
+
values[header] = value;
|
|
52
|
+
});
|
|
53
|
+
return { name: `Row ${index + 1}`, values };
|
|
54
|
+
});
|
|
55
|
+
return { testCase: true, name: params.NAME, dataDriven: true, data, statement: 'STEPS' };
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// Before All - runs once before all tests
|
|
59
|
+
{
|
|
60
|
+
type: 'lifecycle_before_all',
|
|
61
|
+
category: 'Lifecycle',
|
|
62
|
+
color: '#8E24AA',
|
|
63
|
+
tooltip: 'Steps to run once before all tests in the suite',
|
|
64
|
+
inputs: [
|
|
65
|
+
{ name: 'DO', type: 'statement' },
|
|
66
|
+
],
|
|
67
|
+
execute: async (_params, _context) => {
|
|
68
|
+
// Marker block - actual execution handled by executor
|
|
69
|
+
return { lifecycle: 'beforeAll', statement: 'DO' };
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
// After All - runs once after all tests
|
|
73
|
+
{
|
|
74
|
+
type: 'lifecycle_after_all',
|
|
75
|
+
category: 'Lifecycle',
|
|
76
|
+
color: '#8E24AA',
|
|
77
|
+
tooltip: 'Steps to run once after all tests in the suite',
|
|
78
|
+
inputs: [
|
|
79
|
+
{ name: 'DO', type: 'statement' },
|
|
80
|
+
],
|
|
81
|
+
execute: async (_params, _context) => {
|
|
82
|
+
return { lifecycle: 'afterAll', statement: 'DO' };
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
// Before Each - runs before each test
|
|
86
|
+
{
|
|
87
|
+
type: 'lifecycle_before_each',
|
|
88
|
+
category: 'Lifecycle',
|
|
89
|
+
color: '#8E24AA',
|
|
90
|
+
tooltip: 'Steps to run before each test',
|
|
91
|
+
inputs: [
|
|
92
|
+
{ name: 'DO', type: 'statement' },
|
|
93
|
+
],
|
|
94
|
+
execute: async (_params, _context) => {
|
|
95
|
+
return { lifecycle: 'beforeEach', statement: 'DO' };
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
// After Each - runs after each test
|
|
99
|
+
{
|
|
100
|
+
type: 'lifecycle_after_each',
|
|
101
|
+
category: 'Lifecycle',
|
|
102
|
+
color: '#8E24AA',
|
|
103
|
+
tooltip: 'Steps to run after each test',
|
|
104
|
+
inputs: [
|
|
105
|
+
{ name: 'DO', type: 'statement' },
|
|
106
|
+
],
|
|
107
|
+
execute: async (_params, _context) => {
|
|
108
|
+
return { lifecycle: 'afterEach', statement: 'DO' };
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
// Setup - alias for beforeEach with clearer naming
|
|
112
|
+
{
|
|
113
|
+
type: 'lifecycle_setup',
|
|
114
|
+
category: 'Lifecycle',
|
|
115
|
+
color: '#8E24AA',
|
|
116
|
+
tooltip: 'Setup steps to run before the test',
|
|
117
|
+
inputs: [
|
|
118
|
+
{ name: 'DESCRIPTION', type: 'field', fieldType: 'text', default: 'Setup' },
|
|
119
|
+
{ name: 'DO', type: 'statement' },
|
|
120
|
+
],
|
|
121
|
+
previousStatement: true,
|
|
122
|
+
nextStatement: true,
|
|
123
|
+
execute: async (params, context) => {
|
|
124
|
+
context.logger.info(`Setup: ${params.DESCRIPTION}`);
|
|
125
|
+
return { lifecycle: 'setup', statement: 'DO' };
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
// Teardown - alias for afterEach with clearer naming
|
|
129
|
+
{
|
|
130
|
+
type: 'lifecycle_teardown',
|
|
131
|
+
category: 'Lifecycle',
|
|
132
|
+
color: '#8E24AA',
|
|
133
|
+
tooltip: 'Teardown steps to run after the test',
|
|
134
|
+
inputs: [
|
|
135
|
+
{ name: 'DESCRIPTION', type: 'field', fieldType: 'text', default: 'Teardown' },
|
|
136
|
+
{ name: 'DO', type: 'statement' },
|
|
137
|
+
],
|
|
138
|
+
previousStatement: true,
|
|
139
|
+
nextStatement: true,
|
|
140
|
+
execute: async (params, context) => {
|
|
141
|
+
context.logger.info(`Teardown: ${params.DESCRIPTION}`);
|
|
142
|
+
return { lifecycle: 'teardown', statement: 'DO' };
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
// Cleanup on failure - only runs if test fails
|
|
146
|
+
{
|
|
147
|
+
type: 'lifecycle_on_failure',
|
|
148
|
+
category: 'Lifecycle',
|
|
149
|
+
color: '#C62828',
|
|
150
|
+
tooltip: 'Steps to run only if the test fails',
|
|
151
|
+
inputs: [
|
|
152
|
+
{ name: 'DO', type: 'statement' },
|
|
153
|
+
],
|
|
154
|
+
previousStatement: true,
|
|
155
|
+
nextStatement: true,
|
|
156
|
+
execute: async (_params, _context) => {
|
|
157
|
+
return { lifecycle: 'onFailure', statement: 'DO' };
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
// Skip test conditionally
|
|
161
|
+
{
|
|
162
|
+
type: 'lifecycle_skip_if',
|
|
163
|
+
category: 'Lifecycle',
|
|
164
|
+
color: '#757575',
|
|
165
|
+
tooltip: 'Skip the rest of the test if condition is true',
|
|
166
|
+
inputs: [
|
|
167
|
+
{ name: 'CONDITION', type: 'value', check: 'Boolean', required: true },
|
|
168
|
+
{ name: 'REASON', type: 'field', fieldType: 'text', default: 'Condition not met' },
|
|
169
|
+
],
|
|
170
|
+
previousStatement: true,
|
|
171
|
+
nextStatement: true,
|
|
172
|
+
execute: async (params, _context) => {
|
|
173
|
+
const condition = params.CONDITION;
|
|
174
|
+
const reason = params.REASON;
|
|
175
|
+
if (condition) {
|
|
176
|
+
throw { skip: true, reason };
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
// Retry on failure
|
|
181
|
+
{
|
|
182
|
+
type: 'lifecycle_retry',
|
|
183
|
+
category: 'Lifecycle',
|
|
184
|
+
color: '#FF6F00',
|
|
185
|
+
tooltip: 'Retry steps on failure',
|
|
186
|
+
inputs: [
|
|
187
|
+
{ name: 'TIMES', type: 'field', fieldType: 'number', default: 3 },
|
|
188
|
+
{ name: 'DELAY', type: 'field', fieldType: 'number', default: 1000 },
|
|
189
|
+
{ name: 'DO', type: 'statement' },
|
|
190
|
+
],
|
|
191
|
+
previousStatement: true,
|
|
192
|
+
nextStatement: true,
|
|
193
|
+
execute: async (params, _context) => {
|
|
194
|
+
const times = params.TIMES;
|
|
195
|
+
const delay = params.DELAY;
|
|
196
|
+
return { retry: true, times, delay, statement: 'DO' };
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
];
|