@testsmith/testblocks 0.6.0 → 0.7.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.
Files changed (71) hide show
  1. package/dist/cli/executor.d.ts +4 -1
  2. package/dist/cli/executor.js +101 -5
  3. package/dist/cli/index.js +78 -3
  4. package/dist/cli/reporters/ConsoleReporter.d.ts +12 -0
  5. package/dist/cli/reporters/ConsoleReporter.js +39 -0
  6. package/dist/cli/reporters/HTMLReporter.d.ts +19 -0
  7. package/dist/cli/reporters/HTMLReporter.js +506 -0
  8. package/dist/cli/reporters/JSONReporter.d.ts +15 -0
  9. package/dist/cli/reporters/JSONReporter.js +80 -0
  10. package/dist/cli/reporters/JUnitReporter.d.ts +19 -0
  11. package/dist/cli/reporters/JUnitReporter.js +105 -0
  12. package/dist/cli/reporters/index.d.ts +17 -0
  13. package/dist/cli/reporters/index.js +31 -0
  14. package/dist/cli/reporters/types.d.ts +28 -0
  15. package/dist/cli/reporters/types.js +2 -0
  16. package/dist/cli/reporters/utils.d.ts +31 -0
  17. package/dist/cli/reporters/utils.js +136 -0
  18. package/dist/cli/reporters.d.ts +13 -62
  19. package/dist/cli/reporters.js +16 -719
  20. package/dist/client/assets/index-Boo8ZrY_.js +2195 -0
  21. package/dist/client/assets/{index-dXniUrbi.js.map → index-Boo8ZrY_.js.map} +1 -1
  22. package/dist/client/assets/index-OxNH9dW-.css +1 -0
  23. package/dist/client/index.html +2 -2
  24. package/dist/core/blocks/api.js +3 -6
  25. package/dist/core/blocks/assertions.d.ts +31 -0
  26. package/dist/core/blocks/assertions.js +72 -0
  27. package/dist/core/blocks/index.d.ts +1 -0
  28. package/dist/core/blocks/index.js +6 -1
  29. package/dist/core/blocks/lifecycle.js +5 -3
  30. package/dist/core/blocks/logic.js +2 -3
  31. package/dist/core/blocks/playwright/assertions.d.ts +5 -0
  32. package/dist/core/blocks/playwright/assertions.js +321 -0
  33. package/dist/core/blocks/playwright/index.d.ts +17 -0
  34. package/dist/core/blocks/playwright/index.js +49 -0
  35. package/dist/core/blocks/playwright/interactions.d.ts +5 -0
  36. package/dist/core/blocks/playwright/interactions.js +191 -0
  37. package/dist/core/blocks/playwright/navigation.d.ts +5 -0
  38. package/dist/core/blocks/playwright/navigation.js +133 -0
  39. package/dist/core/blocks/playwright/retrieval.d.ts +5 -0
  40. package/dist/core/blocks/playwright/retrieval.js +144 -0
  41. package/dist/core/blocks/playwright/types.d.ts +65 -0
  42. package/dist/core/blocks/playwright/types.js +5 -0
  43. package/dist/core/blocks/playwright/utils.d.ts +26 -0
  44. package/dist/core/blocks/playwright/utils.js +137 -0
  45. package/dist/core/blocks/playwright.d.ts +13 -2
  46. package/dist/core/blocks/playwright.js +14 -761
  47. package/dist/core/executor/BaseTestExecutor.d.ts +60 -0
  48. package/dist/core/executor/BaseTestExecutor.js +297 -0
  49. package/dist/core/executor/index.d.ts +1 -0
  50. package/dist/core/executor/index.js +5 -0
  51. package/dist/core/index.d.ts +1 -0
  52. package/dist/core/index.js +4 -0
  53. package/dist/core/types.d.ts +12 -0
  54. package/dist/core/utils/blocklyParser.d.ts +18 -0
  55. package/dist/core/utils/blocklyParser.js +84 -0
  56. package/dist/core/utils/dataLoader.d.ts +9 -0
  57. package/dist/core/utils/dataLoader.js +117 -0
  58. package/dist/core/utils/index.d.ts +2 -0
  59. package/dist/core/utils/index.js +12 -0
  60. package/dist/core/utils/logger.d.ts +14 -0
  61. package/dist/core/utils/logger.js +48 -0
  62. package/dist/core/utils/variableResolver.d.ts +24 -0
  63. package/dist/core/utils/variableResolver.js +92 -0
  64. package/dist/server/executor.d.ts +6 -0
  65. package/dist/server/executor.js +207 -47
  66. package/dist/server/globals.d.ts +6 -1
  67. package/dist/server/globals.js +7 -0
  68. package/dist/server/startServer.js +15 -0
  69. package/package.json +1 -1
  70. package/dist/client/assets/index-dXniUrbi.js +0 -2193
  71. package/dist/client/assets/index-oTTttNKd.css +0 -1
@@ -0,0 +1,60 @@
1
+ import { Browser, Page, BrowserContext } from 'playwright';
2
+ import { TestFile, TestStep, StepResult, ExecutionContext, Logger, Plugin, ProcedureDefinition } from '../types';
3
+ export interface ExecutorOptions {
4
+ headless?: boolean;
5
+ timeout?: number;
6
+ baseUrl?: string;
7
+ variables?: Record<string, unknown>;
8
+ plugins?: Plugin[];
9
+ testIdAttribute?: string;
10
+ baseDir?: string;
11
+ }
12
+ /**
13
+ * Base class for test execution providing shared functionality
14
+ * for both server and CLI executors
15
+ */
16
+ export declare abstract class BaseTestExecutor {
17
+ protected options: ExecutorOptions;
18
+ protected browser: Browser | null;
19
+ protected browserContext: BrowserContext | null;
20
+ protected page: Page | null;
21
+ protected plugins: Map<string, Plugin>;
22
+ protected procedures: Map<string, ProcedureDefinition>;
23
+ constructor(options?: ExecutorOptions);
24
+ /**
25
+ * Initialize browser for test execution
26
+ */
27
+ initialize(): Promise<void>;
28
+ /**
29
+ * Cleanup browser resources
30
+ */
31
+ cleanup(): Promise<void>;
32
+ /**
33
+ * Check if a test file requires browser initialization
34
+ */
35
+ protected requiresBrowser(testFile: TestFile): boolean;
36
+ /**
37
+ * Register custom blocks from procedure definitions
38
+ */
39
+ protected registerCustomBlocksFromProcedures(procedures: Record<string, ProcedureDefinition>): void;
40
+ /**
41
+ * Execute a single step
42
+ */
43
+ protected runStep(step: TestStep, context: ExecutionContext): Promise<StepResult>;
44
+ /**
45
+ * Resolve step parameters, executing nested blocks
46
+ */
47
+ protected resolveParams(params: Record<string, unknown>, context: ExecutionContext): Promise<Record<string, unknown>>;
48
+ /**
49
+ * Create a logger instance
50
+ */
51
+ protected createLogger(): Logger;
52
+ /**
53
+ * Resolve variable defaults from globals format
54
+ */
55
+ protected resolveVariableDefaults(vars?: Record<string, unknown>): Record<string, unknown>;
56
+ /**
57
+ * Extract steps from Blockly state
58
+ */
59
+ protected extractStepsFromBlocklyState(state: unknown): TestStep[];
60
+ }
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseTestExecutor = void 0;
4
+ const playwright_1 = require("playwright");
5
+ const blocks_1 = require("../blocks");
6
+ const blocklyParser_1 = require("../utils/blocklyParser");
7
+ const variableResolver_1 = require("../utils/variableResolver");
8
+ /**
9
+ * Base class for test execution providing shared functionality
10
+ * for both server and CLI executors
11
+ */
12
+ class BaseTestExecutor {
13
+ constructor(options = {}) {
14
+ this.browser = null;
15
+ this.browserContext = null;
16
+ this.page = null;
17
+ this.plugins = new Map();
18
+ this.procedures = new Map();
19
+ this.options = {
20
+ headless: true,
21
+ timeout: 30000,
22
+ ...options,
23
+ };
24
+ if (options.plugins) {
25
+ options.plugins.forEach(plugin => {
26
+ this.plugins.set(plugin.name, plugin);
27
+ });
28
+ }
29
+ }
30
+ /**
31
+ * Initialize browser for test execution
32
+ */
33
+ async initialize() {
34
+ if (this.options.testIdAttribute) {
35
+ playwright_1.selectors.setTestIdAttribute(this.options.testIdAttribute);
36
+ }
37
+ this.browser = await playwright_1.chromium.launch({
38
+ headless: this.options.headless,
39
+ });
40
+ this.browserContext = await this.browser.newContext();
41
+ this.page = await this.browserContext.newPage();
42
+ if (this.options.timeout) {
43
+ this.page.setDefaultTimeout(this.options.timeout);
44
+ }
45
+ }
46
+ /**
47
+ * Cleanup browser resources
48
+ */
49
+ async cleanup() {
50
+ if (this.page)
51
+ await this.page.close();
52
+ if (this.browserContext)
53
+ await this.browserContext.close();
54
+ if (this.browser)
55
+ await this.browser.close();
56
+ this.page = null;
57
+ this.browserContext = null;
58
+ this.browser = null;
59
+ }
60
+ /**
61
+ * Check if a test file requires browser initialization
62
+ */
63
+ requiresBrowser(testFile) {
64
+ const hasWebStep = (steps) => {
65
+ return steps.some(step => step.type.startsWith('web_'));
66
+ };
67
+ const hasWebStepInState = (state) => {
68
+ const steps = blocklyParser_1.BlocklyParser.extractSteps(state);
69
+ return hasWebStep(steps);
70
+ };
71
+ // Check lifecycle hooks
72
+ if (testFile.beforeAll && hasWebStepInState(testFile.beforeAll))
73
+ return true;
74
+ if (testFile.afterAll && hasWebStepInState(testFile.afterAll))
75
+ return true;
76
+ if (testFile.beforeEach && hasWebStepInState(testFile.beforeEach))
77
+ return true;
78
+ if (testFile.afterEach && hasWebStepInState(testFile.afterEach))
79
+ return true;
80
+ // Check all tests
81
+ for (const test of testFile.tests) {
82
+ if (hasWebStepInState(test.steps))
83
+ return true;
84
+ if (test.beforeEach && hasWebStepInState(test.beforeEach))
85
+ return true;
86
+ if (test.afterEach && hasWebStepInState(test.afterEach))
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ /**
92
+ * Register custom blocks from procedure definitions
93
+ */
94
+ registerCustomBlocksFromProcedures(procedures) {
95
+ Object.entries(procedures).forEach(([name, proc]) => {
96
+ (0, blocks_1.registerProcedure)(name, proc);
97
+ this.procedures.set(name, proc);
98
+ if (!proc.steps || proc.steps.length === 0)
99
+ return;
100
+ const blockType = `custom_${proc.name.toLowerCase().replace(/\s+/g, '_')}`;
101
+ if ((0, blocks_1.getBlock)(blockType))
102
+ return;
103
+ const blockDef = {
104
+ type: blockType,
105
+ category: 'Custom',
106
+ color: '#607D8B',
107
+ tooltip: proc.description || `Custom block: ${proc.name}`,
108
+ inputs: (proc.params || []).map(param => ({
109
+ name: param.name.toUpperCase(),
110
+ type: 'field',
111
+ fieldType: param.type === 'number' ? 'number' : 'text',
112
+ default: param.default,
113
+ })),
114
+ previousStatement: true,
115
+ nextStatement: true,
116
+ execute: async (params, context) => {
117
+ context.logger.info(`Executing custom block: ${proc.name}`);
118
+ (proc.params || []).forEach(p => {
119
+ const paramKey = p.name.toUpperCase();
120
+ let value = params[paramKey];
121
+ if (value !== undefined) {
122
+ if (typeof value === 'string') {
123
+ value = variableResolver_1.VariableResolver.resolve(value, context);
124
+ }
125
+ context.variables.set(p.name, value);
126
+ context.logger.debug(`Set procedure param: ${p.name} = "${value}"`);
127
+ }
128
+ });
129
+ return {
130
+ customBlock: true,
131
+ name: proc.name,
132
+ steps: proc.steps,
133
+ };
134
+ },
135
+ };
136
+ (0, blocks_1.registerBlock)(blockDef);
137
+ });
138
+ }
139
+ /**
140
+ * Execute a single step
141
+ */
142
+ async runStep(step, context) {
143
+ const startTime = Date.now();
144
+ // Run beforeStep hooks
145
+ for (const plugin of this.plugins.values()) {
146
+ if (plugin.hooks?.beforeStep) {
147
+ await plugin.hooks.beforeStep(context, step);
148
+ }
149
+ }
150
+ let status = 'passed';
151
+ let output;
152
+ let error;
153
+ try {
154
+ const blockDef = (0, blocks_1.getBlock)(step.type);
155
+ if (!blockDef) {
156
+ throw new Error(`Unknown block type: ${step.type}`);
157
+ }
158
+ const resolvedParams = await this.resolveParams(step.params, context);
159
+ output = await blockDef.execute(resolvedParams, context);
160
+ // Handle custom blocks that return steps to execute
161
+ if (output && typeof output === 'object' && 'customBlock' in output) {
162
+ const customOutput = output;
163
+ for (const childStep of customOutput.steps) {
164
+ const childResult = await this.runStep(childStep, context);
165
+ if (childResult.status === 'failed' || childResult.status === 'error') {
166
+ status = childResult.status;
167
+ error = childResult.error;
168
+ break;
169
+ }
170
+ }
171
+ }
172
+ // Handle compound actions
173
+ if (output && typeof output === 'object' && 'compoundAction' in output) {
174
+ const compoundOutput = output;
175
+ for (const childStep of compoundOutput.steps) {
176
+ const childResult = await this.runStep(childStep, context);
177
+ if (childResult.status === 'failed' || childResult.status === 'error') {
178
+ status = childResult.status;
179
+ error = childResult.error;
180
+ break;
181
+ }
182
+ }
183
+ }
184
+ // Handle procedure calls
185
+ if (output && typeof output === 'object' && 'procedureCall' in output) {
186
+ const procOutput = output;
187
+ for (const [argName, argValue] of Object.entries(procOutput.args)) {
188
+ let resolvedValue = argValue;
189
+ if (typeof argValue === 'string') {
190
+ resolvedValue = variableResolver_1.VariableResolver.resolve(argValue, context);
191
+ }
192
+ context.variables.set(argName, resolvedValue);
193
+ }
194
+ if (procOutput.procedure.steps) {
195
+ for (const childStep of procOutput.procedure.steps) {
196
+ const childResult = await this.runStep(childStep, context);
197
+ if (childResult.status === 'failed' || childResult.status === 'error') {
198
+ status = childResult.status;
199
+ error = childResult.error;
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ catch (err) {
207
+ status = 'failed';
208
+ error = {
209
+ message: err.message,
210
+ stack: err.stack,
211
+ };
212
+ }
213
+ // Capture screenshot on failure for web tests
214
+ let screenshot;
215
+ if (status === 'failed' && step.type.startsWith('web_') && context.page) {
216
+ try {
217
+ const page = context.page;
218
+ const buffer = await page.screenshot({ fullPage: true });
219
+ screenshot = buffer.toString('base64');
220
+ }
221
+ catch {
222
+ // Ignore screenshot errors
223
+ }
224
+ }
225
+ const result = {
226
+ stepId: step.id,
227
+ stepType: step.type,
228
+ status,
229
+ duration: Date.now() - startTime,
230
+ output,
231
+ error,
232
+ screenshot,
233
+ };
234
+ // Run afterStep hooks
235
+ for (const plugin of this.plugins.values()) {
236
+ if (plugin.hooks?.afterStep) {
237
+ await plugin.hooks.afterStep(context, step, result);
238
+ }
239
+ }
240
+ return result;
241
+ }
242
+ /**
243
+ * Resolve step parameters, executing nested blocks
244
+ */
245
+ async resolveParams(params, context) {
246
+ const resolved = {};
247
+ for (const [key, value] of Object.entries(params)) {
248
+ if (value && typeof value === 'object' && 'type' in value && 'id' in value) {
249
+ // This is a connected block - execute it to get the value
250
+ const nestedStep = value;
251
+ const blockDef = (0, blocks_1.getBlock)(nestedStep.type);
252
+ if (blockDef) {
253
+ const nestedParams = await this.resolveParams(nestedStep.params || {}, context);
254
+ resolved[key] = await blockDef.execute(nestedParams, context);
255
+ }
256
+ }
257
+ else {
258
+ resolved[key] = value;
259
+ }
260
+ }
261
+ return resolved;
262
+ }
263
+ /**
264
+ * Create a logger instance
265
+ */
266
+ createLogger() {
267
+ return {
268
+ info: (message, data) => {
269
+ console.log(`[INFO] ${message}`, data !== undefined ? data : '');
270
+ },
271
+ warn: (message, data) => {
272
+ console.warn(`[WARN] ${message}`, data !== undefined ? data : '');
273
+ },
274
+ error: (message, data) => {
275
+ console.error(`[ERROR] ${message}`, data !== undefined ? data : '');
276
+ },
277
+ debug: (message, data) => {
278
+ if (process.env.DEBUG) {
279
+ console.debug(`[DEBUG] ${message}`, data !== undefined ? data : '');
280
+ }
281
+ },
282
+ };
283
+ }
284
+ /**
285
+ * Resolve variable defaults from globals format
286
+ */
287
+ resolveVariableDefaults(vars) {
288
+ return (0, variableResolver_1.resolveVariableDefaults)(vars);
289
+ }
290
+ /**
291
+ * Extract steps from Blockly state
292
+ */
293
+ extractStepsFromBlocklyState(state) {
294
+ return blocklyParser_1.BlocklyParser.extractSteps(state);
295
+ }
296
+ }
297
+ exports.BaseTestExecutor = BaseTestExecutor;
@@ -0,0 +1 @@
1
+ export { BaseTestExecutor, type ExecutorOptions } from './BaseTestExecutor';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseTestExecutor = void 0;
4
+ var BaseTestExecutor_1 = require("./BaseTestExecutor");
5
+ Object.defineProperty(exports, "BaseTestExecutor", { enumerable: true, get: function () { return BaseTestExecutor_1.BaseTestExecutor; } });
@@ -1,5 +1,6 @@
1
1
  export * from './types';
2
2
  export * from './blocks';
3
3
  export * from './plugins';
4
+ export * from './utils';
4
5
  export { builtInBlocks, blockRegistry, registerBlock, registerBlocks, getBlock, getAllBlocks, getBlocksByCategory, getCategories, } from './blocks';
5
6
  export { registerPlugin, getPlugin, getAllPlugins, getAllPluginBlocks, unregisterPlugin, createPlugin, createBlock, createActionBlock, createValueBlock, createAssertionBlock, } from './plugins';
@@ -21,6 +21,10 @@ __exportStar(require("./types"), exports);
21
21
  __exportStar(require("./blocks"), exports);
22
22
  // Plugins
23
23
  __exportStar(require("./plugins"), exports);
24
+ // Utilities (browser-safe only)
25
+ __exportStar(require("./utils"), exports);
26
+ // Note: BaseTestExecutor is in ./executor but uses Playwright (Node.js only)
27
+ // Import it directly from '@/core/executor' in server/CLI code
24
28
  // Re-export specific items for convenience
25
29
  var blocks_1 = require("./blocks");
26
30
  Object.defineProperty(exports, "builtInBlocks", { enumerable: true, get: function () { return blocks_1.builtInBlocks; } });
@@ -46,6 +46,7 @@ export interface TestCase {
46
46
  afterEach?: TestStep[];
47
47
  steps: TestStep[];
48
48
  tags?: string[];
49
+ softAssertions?: boolean;
49
50
  }
50
51
  export interface TestDataSet {
51
52
  name?: string;
@@ -81,6 +82,14 @@ export interface BlockInput {
81
82
  export interface BlockOutput {
82
83
  type: string | string[];
83
84
  }
85
+ export interface SoftAssertionError {
86
+ message: string;
87
+ stepId?: string;
88
+ stepType?: string;
89
+ expected?: unknown;
90
+ actual?: unknown;
91
+ timestamp: string;
92
+ }
84
93
  export interface ExecutionContext {
85
94
  variables: Map<string, unknown>;
86
95
  results: TestResult[];
@@ -94,6 +103,8 @@ export interface ExecutionContext {
94
103
  currentData?: TestDataSet;
95
104
  dataIndex?: number;
96
105
  testIdAttribute?: string;
106
+ softAssertions?: boolean;
107
+ softAssertionErrors?: SoftAssertionError[];
97
108
  }
98
109
  export interface Logger {
99
110
  info(message: string, data?: unknown): void;
@@ -117,6 +128,7 @@ export interface TestResult {
117
128
  };
118
129
  isLifecycle?: boolean;
119
130
  lifecycleType?: 'beforeAll' | 'afterAll' | 'beforeEach' | 'afterEach';
131
+ softAssertionErrors?: SoftAssertionError[];
120
132
  }
121
133
  export interface StepResult {
122
134
  stepId: string;
@@ -0,0 +1,18 @@
1
+ import { TestStep } from '../types';
2
+ /**
3
+ * Utility class for parsing Blockly state into TestSteps
4
+ */
5
+ export declare class BlocklyParser {
6
+ /**
7
+ * Extract steps from Blockly serialization state or direct step array
8
+ */
9
+ static extractSteps(state: unknown): TestStep[];
10
+ /**
11
+ * Convert Blockly blocks array to TestSteps
12
+ */
13
+ static blocksToSteps(blocks: unknown[]): TestStep[];
14
+ /**
15
+ * Convert a single Blockly block to a TestStep
16
+ */
17
+ static blockToStep(block: Record<string, unknown>): TestStep | null;
18
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlocklyParser = void 0;
4
+ /**
5
+ * Utility class for parsing Blockly state into TestSteps
6
+ */
7
+ class BlocklyParser {
8
+ /**
9
+ * Extract steps from Blockly serialization state or direct step array
10
+ */
11
+ static extractSteps(state) {
12
+ if (!state || typeof state !== 'object')
13
+ return [];
14
+ const stateObj = state;
15
+ // Handle Blockly serialization format
16
+ if ('blocks' in stateObj && typeof stateObj.blocks === 'object') {
17
+ const blocks = stateObj.blocks;
18
+ if ('blocks' in blocks && Array.isArray(blocks.blocks)) {
19
+ return this.blocksToSteps(blocks.blocks);
20
+ }
21
+ }
22
+ // Handle direct array of steps
23
+ if (Array.isArray(state)) {
24
+ return state;
25
+ }
26
+ return [];
27
+ }
28
+ /**
29
+ * Convert Blockly blocks array to TestSteps
30
+ */
31
+ static blocksToSteps(blocks) {
32
+ const steps = [];
33
+ for (const block of blocks) {
34
+ const step = this.blockToStep(block);
35
+ if (step) {
36
+ steps.push(step);
37
+ // Handle next block in chain
38
+ let currentBlock = block;
39
+ while (currentBlock.next) {
40
+ const nextBlock = currentBlock.next.block;
41
+ const nextStep = this.blockToStep(nextBlock);
42
+ if (nextStep) {
43
+ steps.push(nextStep);
44
+ }
45
+ currentBlock = nextBlock;
46
+ }
47
+ }
48
+ }
49
+ return steps;
50
+ }
51
+ /**
52
+ * Convert a single Blockly block to a TestStep
53
+ */
54
+ static blockToStep(block) {
55
+ if (!block || !block.type)
56
+ return null;
57
+ const step = {
58
+ id: block.id || `step-${Date.now()}`,
59
+ type: block.type,
60
+ params: {},
61
+ };
62
+ // Extract field values
63
+ if (block.fields && typeof block.fields === 'object') {
64
+ for (const [name, value] of Object.entries(block.fields)) {
65
+ step.params[name] = value;
66
+ }
67
+ }
68
+ // Extract inputs (connected blocks)
69
+ if (block.inputs && typeof block.inputs === 'object') {
70
+ for (const [name, input] of Object.entries(block.inputs)) {
71
+ const inputObj = input;
72
+ if (inputObj.block) {
73
+ // Recursively convert connected block
74
+ const connectedStep = this.blockToStep(inputObj.block);
75
+ if (connectedStep) {
76
+ step.params[name] = connectedStep;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ return step;
82
+ }
83
+ }
84
+ exports.BlocklyParser = BlocklyParser;
@@ -0,0 +1,9 @@
1
+ import { TestDataSet } from '../types';
2
+ /**
3
+ * Parse CSV content into TestDataSet array
4
+ */
5
+ export declare function parseCSV(content: string): TestDataSet[];
6
+ /**
7
+ * Load data from a file (CSV or JSON)
8
+ */
9
+ export declare function loadDataFromFile(filePath: string, baseDir?: string): TestDataSet[];
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseCSV = parseCSV;
37
+ exports.loadDataFromFile = loadDataFromFile;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ /**
41
+ * Parse CSV content into TestDataSet array
42
+ */
43
+ function parseCSV(content) {
44
+ const lines = content.trim().split('\n');
45
+ if (lines.length < 2)
46
+ return []; // Need header + at least one data row
47
+ const headers = lines[0].split(',').map(h => h.trim());
48
+ const dataSets = [];
49
+ for (let i = 1; i < lines.length; i++) {
50
+ const line = lines[i].trim();
51
+ if (!line)
52
+ continue;
53
+ // Simple CSV parsing (handles basic quoted values)
54
+ const values = [];
55
+ let current = '';
56
+ let inQuotes = false;
57
+ for (const char of line) {
58
+ if (char === '"') {
59
+ inQuotes = !inQuotes;
60
+ }
61
+ else if (char === ',' && !inQuotes) {
62
+ values.push(current.trim());
63
+ current = '';
64
+ }
65
+ else {
66
+ current += char;
67
+ }
68
+ }
69
+ values.push(current.trim());
70
+ const row = {};
71
+ headers.forEach((header, idx) => {
72
+ const val = values[idx] ?? '';
73
+ // Try to parse as number or boolean
74
+ if (val === 'true') {
75
+ row[header] = true;
76
+ }
77
+ else if (val === 'false') {
78
+ row[header] = false;
79
+ }
80
+ else if (val !== '' && !isNaN(Number(val))) {
81
+ row[header] = Number(val);
82
+ }
83
+ else {
84
+ row[header] = val;
85
+ }
86
+ });
87
+ dataSets.push({ name: `Row ${i}`, values: row });
88
+ }
89
+ return dataSets;
90
+ }
91
+ /**
92
+ * Load data from a file (CSV or JSON)
93
+ */
94
+ function loadDataFromFile(filePath, baseDir) {
95
+ const resolvedPath = baseDir && !path.isAbsolute(filePath)
96
+ ? path.join(baseDir, filePath)
97
+ : filePath;
98
+ if (!fs.existsSync(resolvedPath)) {
99
+ throw new Error(`Data file not found: ${resolvedPath}`);
100
+ }
101
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
102
+ const ext = path.extname(resolvedPath).toLowerCase();
103
+ if (ext === '.csv') {
104
+ return parseCSV(content);
105
+ }
106
+ else if (ext === '.json') {
107
+ const jsonData = JSON.parse(content);
108
+ if (Array.isArray(jsonData)) {
109
+ return jsonData.map((item, idx) => ({
110
+ name: item.name || `Item ${idx + 1}`,
111
+ values: item.values || item,
112
+ }));
113
+ }
114
+ throw new Error('JSON data file must contain an array');
115
+ }
116
+ throw new Error(`Unsupported data file format: ${ext}`);
117
+ }
@@ -0,0 +1,2 @@
1
+ export { VariableResolver, resolveVariableDefaults } from './variableResolver';
2
+ export { BlocklyParser } from './blocklyParser';
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlocklyParser = exports.resolveVariableDefaults = exports.VariableResolver = void 0;
4
+ // Variable resolution utilities (browser-safe)
5
+ var variableResolver_1 = require("./variableResolver");
6
+ Object.defineProperty(exports, "VariableResolver", { enumerable: true, get: function () { return variableResolver_1.VariableResolver; } });
7
+ Object.defineProperty(exports, "resolveVariableDefaults", { enumerable: true, get: function () { return variableResolver_1.resolveVariableDefaults; } });
8
+ // Blockly parsing utilities (browser-safe)
9
+ var blocklyParser_1 = require("./blocklyParser");
10
+ Object.defineProperty(exports, "BlocklyParser", { enumerable: true, get: function () { return blocklyParser_1.BlocklyParser; } });
11
+ // Note: dataLoader and logger use Node.js modules and should be imported directly
12
+ // by server/CLI code, not through this index (to avoid browser bundling issues)