@testsmith/testblocks 0.3.0 → 0.4.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/dist/cli/index.js CHANGED
@@ -248,7 +248,7 @@ program
248
248
  'test:junit': 'testblocks run tests/**/*.testblocks.json -r junit -o reports',
249
249
  },
250
250
  devDependencies: {
251
- '@testsmith/testblocks': '^0.3.0',
251
+ '@testsmith/testblocks': '^0.4.0',
252
252
  },
253
253
  };
254
254
  fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
@@ -9,9 +9,39 @@ const cors_1 = __importDefault(require("cors"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  // Read version from package.json
12
- const packageJsonPath = path_1.default.join(__dirname, '../../package.json');
13
- const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'));
14
- const VERSION = packageJson.version || '0.0.0';
12
+ function getVersion() {
13
+ try {
14
+ // Method 1: Try require.resolve (works for global/local installs)
15
+ try {
16
+ const pkgPath = require.resolve('@testsmith/testblocks/package.json');
17
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
18
+ if (pkg.version)
19
+ return pkg.version;
20
+ }
21
+ catch {
22
+ // Package might not be installed under this name
23
+ }
24
+ // Method 2: Try relative paths
25
+ const possiblePaths = [
26
+ path_1.default.join(__dirname, '../../package.json'),
27
+ path_1.default.join(__dirname, '../../../package.json'),
28
+ path_1.default.join(__dirname, '../../../../package.json'),
29
+ ];
30
+ for (const pkgPath of possiblePaths) {
31
+ if (fs_1.default.existsSync(pkgPath)) {
32
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
33
+ if (pkg.name === '@testsmith/testblocks' && pkg.version) {
34
+ return pkg.version;
35
+ }
36
+ }
37
+ }
38
+ }
39
+ catch {
40
+ // Ignore errors
41
+ }
42
+ return '0.0.0';
43
+ }
44
+ const VERSION = getVersion();
15
45
  const executor_1 = require("./executor");
16
46
  Object.defineProperty(exports, "TestExecutor", { enumerable: true, get: function () { return executor_1.TestExecutor; } });
17
47
  const reporters_1 = require("../cli/reporters");
@@ -43,9 +43,97 @@ const path_1 = __importDefault(require("path"));
43
43
  const fs_1 = __importDefault(require("fs"));
44
44
  const executor_1 = require("./executor");
45
45
  const reporters_1 = require("../cli/reporters");
46
+ // Read version from package.json
47
+ function getVersion() {
48
+ try {
49
+ // Method 1: Try require.resolve to find package.json (works for global/local installs)
50
+ try {
51
+ const pkgPath = require.resolve('@testsmith/testblocks/package.json');
52
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
53
+ if (pkg.version)
54
+ return pkg.version;
55
+ }
56
+ catch {
57
+ // Package might not be installed under this name, try relative paths
58
+ }
59
+ // Method 2: Try relative paths from this file
60
+ const possiblePaths = [
61
+ path_1.default.join(__dirname, '../../package.json'), // dist/server -> package.json
62
+ path_1.default.join(__dirname, '../../../package.json'), // nested node_modules
63
+ path_1.default.join(__dirname, '../../../../package.json'), // scoped package in node_modules
64
+ ];
65
+ for (const pkgPath of possiblePaths) {
66
+ if (fs_1.default.existsSync(pkgPath)) {
67
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
68
+ // Verify it's our package
69
+ if (pkg.name === '@testsmith/testblocks' && pkg.version) {
70
+ return pkg.version;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ catch {
76
+ // Ignore errors
77
+ }
78
+ return '0.0.0';
79
+ }
80
+ const VERSION = getVersion();
46
81
  const plugins_1 = require("./plugins");
47
82
  const globals_1 = require("./globals");
48
83
  const codegenManager_1 = require("./codegenManager");
84
+ /**
85
+ * Merge folder hooks into a test file.
86
+ * Folder hooks are ordered from outermost to innermost folder.
87
+ * - beforeAll: run parent hooks first, then child hooks, then test file hooks
88
+ * - afterAll: run test file hooks first, then child hooks, then parent hooks
89
+ * - beforeEach/afterEach: same pattern
90
+ */
91
+ function mergeFolderHooksIntoTestFile(testFile, folderHooks) {
92
+ if (!folderHooks || folderHooks.length === 0) {
93
+ return testFile;
94
+ }
95
+ // Collect all steps from folder hooks (parent to child order is already provided)
96
+ const beforeAllSteps = [];
97
+ const afterAllSteps = [];
98
+ const beforeEachSteps = [];
99
+ const afterEachSteps = [];
100
+ // Parent to child order for beforeAll/beforeEach
101
+ for (const hooks of folderHooks) {
102
+ if (hooks.beforeAll)
103
+ beforeAllSteps.push(...hooks.beforeAll);
104
+ if (hooks.beforeEach)
105
+ beforeEachSteps.push(...hooks.beforeEach);
106
+ }
107
+ // Child to parent order for afterAll/afterEach
108
+ for (let i = folderHooks.length - 1; i >= 0; i--) {
109
+ const hooks = folderHooks[i];
110
+ if (hooks.afterAll)
111
+ afterAllSteps.unshift(...hooks.afterAll);
112
+ if (hooks.afterEach)
113
+ afterEachSteps.unshift(...hooks.afterEach);
114
+ }
115
+ // Merge with test file hooks
116
+ const merged = {
117
+ ...testFile,
118
+ beforeAll: [
119
+ ...beforeAllSteps,
120
+ ...(testFile.beforeAll || []),
121
+ ],
122
+ afterAll: [
123
+ ...(testFile.afterAll || []),
124
+ ...afterAllSteps,
125
+ ],
126
+ beforeEach: [
127
+ ...beforeEachSteps,
128
+ ...(testFile.beforeEach || []),
129
+ ],
130
+ afterEach: [
131
+ ...(testFile.afterEach || []),
132
+ ...afterEachSteps,
133
+ ],
134
+ };
135
+ return merged;
136
+ }
49
137
  async function startServer(options = {}) {
50
138
  const port = options.port || 3000;
51
139
  const workingDir = process.cwd();
@@ -73,7 +161,11 @@ async function startServer(options = {}) {
73
161
  }
74
162
  // Health check
75
163
  app.get('/api/health', (_req, res) => {
76
- res.json({ status: 'ok', version: '1.0.0' });
164
+ res.json({ status: 'ok', version: VERSION });
165
+ });
166
+ // Version endpoint
167
+ app.get('/api/version', (_req, res) => {
168
+ res.json({ version: VERSION });
77
169
  });
78
170
  // List available plugins (with full block definitions for client registration)
79
171
  app.get('/api/plugins', (_req, res) => {
@@ -130,11 +222,13 @@ async function startServer(options = {}) {
130
222
  // Run tests
131
223
  app.post('/api/run', async (req, res) => {
132
224
  try {
133
- const testFile = req.body;
225
+ const { testFile, folderHooks } = req.body;
134
226
  if (!testFile || !testFile.tests) {
135
227
  return res.status(400).json({ error: 'Invalid test file format' });
136
228
  }
137
229
  console.log(`Running ${testFile.tests.length} tests from "${testFile.name}"...`);
230
+ // Merge folder hooks into test file
231
+ const mergedTestFile = mergeFolderHooksIntoTestFile(testFile, folderHooks || []);
138
232
  const globalVars = (0, globals_1.getGlobalVariables)();
139
233
  const testIdAttr = (0, globals_1.getTestIdAttribute)();
140
234
  const executor = new executor_1.TestExecutor({
@@ -144,7 +238,7 @@ async function startServer(options = {}) {
144
238
  testIdAttribute: testIdAttr,
145
239
  baseDir: globalsDir,
146
240
  });
147
- const results = await executor.runTestFile(testFile);
241
+ const results = await executor.runTestFile(mergedTestFile);
148
242
  const passed = results.filter(r => r.status === 'passed').length;
149
243
  const failed = results.filter(r => r.status === 'failed').length;
150
244
  console.log(`Results: ${passed} passed, ${failed} failed`);
@@ -162,12 +256,14 @@ async function startServer(options = {}) {
162
256
  // Run a single test
163
257
  app.post('/api/run/:testId', async (req, res) => {
164
258
  try {
165
- const testFile = req.body;
259
+ const { testFile, folderHooks } = req.body;
166
260
  const { testId } = req.params;
167
261
  const test = testFile.tests.find(t => t.id === testId);
168
262
  if (!test) {
169
263
  return res.status(404).json({ error: `Test not found: ${testId}` });
170
264
  }
265
+ // Merge folder hooks for beforeEach/afterEach
266
+ const mergedTestFile = mergeFolderHooksIntoTestFile(testFile, folderHooks || []);
171
267
  const globalVars = (0, globals_1.getGlobalVariables)();
172
268
  const testIdAttr = (0, globals_1.getTestIdAttribute)();
173
269
  const executor = new executor_1.TestExecutor({
@@ -177,11 +273,11 @@ async function startServer(options = {}) {
177
273
  testIdAttribute: testIdAttr,
178
274
  baseDir: globalsDir,
179
275
  });
180
- if (testFile.procedures) {
181
- executor.registerProcedures(testFile.procedures);
276
+ if (mergedTestFile.procedures) {
277
+ executor.registerProcedures(mergedTestFile.procedures);
182
278
  }
183
279
  await executor.initialize();
184
- const result = await executor.runTest(test, testFile.variables);
280
+ const result = await executor.runTest(test, mergedTestFile.variables);
185
281
  await executor.cleanup();
186
282
  res.json(result);
187
283
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testsmith/testblocks",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Visual test automation tool with Blockly - API and Playwright testing",
5
5
  "author": "Roy de Kleijn",
6
6
  "license": "MIT",