@bsb/tests 0.0.1
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/README.md +7 -0
- package/bin/bsb-tests.cjs +376 -0
- package/docs/index.md +101 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +24 -0
- package/lib/index.js.map +1 -0
- package/lib/mocks.d.ts +39 -0
- package/lib/mocks.js +191 -0
- package/lib/mocks.js.map +1 -0
- package/lib/plugins/config-default/index.d.ts +27 -0
- package/lib/plugins/config-default/index.js +172 -0
- package/lib/plugins/config-default/index.js.map +1 -0
- package/lib/plugins/events-default/index.d.ts +27 -0
- package/lib/plugins/events-default/index.js +143 -0
- package/lib/plugins/events-default/index.js.map +1 -0
- package/lib/plugins/logging-default/index.d.ts +27 -0
- package/lib/plugins/logging-default/index.js +390 -0
- package/lib/plugins/logging-default/index.js.map +1 -0
- package/lib/plugins/observable-default/index.d.ts +27 -0
- package/lib/plugins/observable-default/index.js +166 -0
- package/lib/plugins/observable-default/index.js.map +1 -0
- package/lib/runner/plugin-custom.d.ts +1 -0
- package/lib/runner/plugin-custom.js +119 -0
- package/lib/runner/plugin-custom.js.map +1 -0
- package/lib/runner/plugin-events.d.ts +1 -0
- package/lib/runner/plugin-events.js +37 -0
- package/lib/runner/plugin-events.js.map +1 -0
- package/lib/runner/plugin-observable.d.ts +1 -0
- package/lib/runner/plugin-observable.js +22 -0
- package/lib/runner/plugin-observable.js.map +1 -0
- package/lib/runner/setup.d.ts +1 -0
- package/lib/runner/setup.js +19 -0
- package/lib/runner/setup.js.map +1 -0
- package/lib/sb/plugins/events/broadcast.d.ts +30 -0
- package/lib/sb/plugins/events/broadcast.js +390 -0
- package/lib/sb/plugins/events/broadcast.js.map +1 -0
- package/lib/sb/plugins/events/emit.d.ts +30 -0
- package/lib/sb/plugins/events/emit.js +386 -0
- package/lib/sb/plugins/events/emit.js.map +1 -0
- package/lib/sb/plugins/events/emitAndReturn.d.ts +30 -0
- package/lib/sb/plugins/events/emitAndReturn.js +415 -0
- package/lib/sb/plugins/events/emitAndReturn.js.map +1 -0
- package/lib/sb/plugins/events/emitStreamAndReceiveStream.d.ts +30 -0
- package/lib/sb/plugins/events/emitStreamAndReceiveStream.js +331 -0
- package/lib/sb/plugins/events/emitStreamAndReceiveStream.js.map +1 -0
- package/lib/sb/plugins/events/index.d.ts +28 -0
- package/lib/sb/plugins/events/index.js +69 -0
- package/lib/sb/plugins/events/index.js.map +1 -0
- package/lib/sb/plugins/events/plugin.d.ts +0 -0
- package/lib/sb/plugins/events/plugin.js +2 -0
- package/lib/sb/plugins/events/plugin.js.map +1 -0
- package/lib/sb/plugins/observable/index.d.ts +28 -0
- package/lib/sb/plugins/observable/index.js +141 -0
- package/lib/sb/plugins/observable/index.js.map +1 -0
- package/lib/trace.d.ts +40 -0
- package/lib/trace.js +85 -0
- package/lib/trace.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { spawnSync } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const argv = process.argv.slice(2);
|
|
8
|
+
|
|
9
|
+
const getArg = (name) => {
|
|
10
|
+
const idx = argv.indexOf(name);
|
|
11
|
+
if (idx === -1) return null;
|
|
12
|
+
return argv[idx + 1] || null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const hasFlag = (name) => argv.includes(name);
|
|
16
|
+
|
|
17
|
+
const cwd = path.resolve(getArg("--cwd") || process.cwd());
|
|
18
|
+
const pluginFilter = getArg("--plugin");
|
|
19
|
+
const forceTs = hasFlag("--ts");
|
|
20
|
+
const forceJs = hasFlag("--js");
|
|
21
|
+
const enableCoverage = !hasFlag("--no-coverage");
|
|
22
|
+
|
|
23
|
+
const ignoreDirs = new Set([
|
|
24
|
+
"node_modules",
|
|
25
|
+
".git",
|
|
26
|
+
"lib",
|
|
27
|
+
"dist",
|
|
28
|
+
".bsb",
|
|
29
|
+
".npm-cache",
|
|
30
|
+
".claude",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const findFiles = (dir, filename, results = []) => {
|
|
34
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
if (ignoreDirs.has(entry.name)) continue;
|
|
38
|
+
findFiles(path.join(dir, entry.name), filename, results);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (entry.isFile() && entry.name === filename) {
|
|
42
|
+
results.push(path.join(dir, entry.name));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return results;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
49
|
+
|
|
50
|
+
const resolvePluginModule = (pluginRoot, pluginPath, useTs) => {
|
|
51
|
+
const base = path.resolve(pluginRoot, pluginPath || "");
|
|
52
|
+
if (useTs) {
|
|
53
|
+
return path.join(base, "index.ts");
|
|
54
|
+
}
|
|
55
|
+
const directJs = path.join(base, "index.js");
|
|
56
|
+
if (fs.existsSync(directJs)) return directJs;
|
|
57
|
+
const swapped = base.includes(`${path.sep}src${path.sep}`)
|
|
58
|
+
? base.replace(`${path.sep}src${path.sep}`, `${path.sep}lib${path.sep}`)
|
|
59
|
+
: base.replace(`${path.sep}src`, `${path.sep}lib`);
|
|
60
|
+
return path.join(swapped, "index.js");
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const resolveLocalBaseEntry = (repoRoot, useTs) => {
|
|
64
|
+
const baseRoot = path.join(repoRoot, "nodejs");
|
|
65
|
+
const entryTs = path.join(baseRoot, "src", "index.ts");
|
|
66
|
+
const entryJs = path.join(baseRoot, "lib", "index.js");
|
|
67
|
+
if (useTs && fs.existsSync(entryTs)) return entryTs;
|
|
68
|
+
if (!useTs && fs.existsSync(entryJs)) return entryJs;
|
|
69
|
+
return fs.existsSync(entryTs) ? entryTs : entryJs;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const loadTestsManifest = (manifestDir) => {
|
|
73
|
+
const configPath = path.join(manifestDir, "bsb-tests.json");
|
|
74
|
+
if (!fs.existsSync(configPath)) return null;
|
|
75
|
+
return readJson(configPath);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const mergeConfig = (baseConfig, overrideConfig) => {
|
|
79
|
+
return {
|
|
80
|
+
...(baseConfig || {}),
|
|
81
|
+
...(overrideConfig || {}),
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const normalizeSetup = (setupValue) => {
|
|
86
|
+
if (!setupValue) return null;
|
|
87
|
+
if (typeof setupValue === "string") {
|
|
88
|
+
return { beforeAll: setupValue, afterAll: null };
|
|
89
|
+
}
|
|
90
|
+
if (typeof setupValue === "object") {
|
|
91
|
+
return {
|
|
92
|
+
beforeAll: setupValue.beforeAll || null,
|
|
93
|
+
afterAll: setupValue.afterAll || null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const normalizeDispose = (disposeValue) => {
|
|
100
|
+
if (!disposeValue) return null;
|
|
101
|
+
if (typeof disposeValue === "string") {
|
|
102
|
+
return { afterAll: disposeValue };
|
|
103
|
+
}
|
|
104
|
+
if (typeof disposeValue === "object") {
|
|
105
|
+
return {
|
|
106
|
+
afterAll: disposeValue.afterAll || null,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const manifests = findFiles(cwd, "bsb-plugin.json");
|
|
113
|
+
|
|
114
|
+
if (manifests.length === 0) {
|
|
115
|
+
console.error("No bsb-plugin.json files found in", cwd);
|
|
116
|
+
process.exit(2);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const plugins = [];
|
|
120
|
+
for (const manifestPath of manifests) {
|
|
121
|
+
const manifestDir = path.dirname(manifestPath);
|
|
122
|
+
const manifest = readJson(manifestPath);
|
|
123
|
+
const testsManifest = loadTestsManifest(manifestDir);
|
|
124
|
+
const testsById = new Map();
|
|
125
|
+
if (testsManifest && Array.isArray(testsManifest.nodejs)) {
|
|
126
|
+
for (const entry of testsManifest.nodejs) {
|
|
127
|
+
if (entry && entry.id) {
|
|
128
|
+
testsById.set(entry.id, entry);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const platform of Object.keys(manifest)) {
|
|
133
|
+
const items = Array.isArray(manifest[platform]) ? manifest[platform] : [];
|
|
134
|
+
for (const plugin of items) {
|
|
135
|
+
if (!plugin || !plugin.id) continue;
|
|
136
|
+
const pluginRoot = path.resolve(manifestDir, plugin.basePath || ".");
|
|
137
|
+
const testsEntry = testsById.get(plugin.id) || null;
|
|
138
|
+
plugins.push({
|
|
139
|
+
platform,
|
|
140
|
+
id: plugin.id,
|
|
141
|
+
name: plugin.name || plugin.id,
|
|
142
|
+
pluginPath: plugin.pluginPath,
|
|
143
|
+
pluginRoot,
|
|
144
|
+
testsEntry,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const filtered = pluginFilter
|
|
151
|
+
? plugins.filter((p) => p.id === pluginFilter || p.name === pluginFilter || p.pluginRoot.endsWith(pluginFilter))
|
|
152
|
+
: plugins;
|
|
153
|
+
|
|
154
|
+
if (filtered.length === 0) {
|
|
155
|
+
console.error("No plugins matched filter:", pluginFilter);
|
|
156
|
+
process.exit(2);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const repoRoot = cwd;
|
|
160
|
+
const mochaBin = require.resolve("mocha/bin/mocha.mjs");
|
|
161
|
+
const nycBin = require.resolve("nyc/bin/nyc.js");
|
|
162
|
+
const tsNodeRegister = require.resolve("ts-node/register");
|
|
163
|
+
const setupHook = path.join(__dirname, "..", "src", "runner", "setup.ts");
|
|
164
|
+
const pluginEventsRunner = path.join(__dirname, "..", "src", "runner", "plugin-events.ts");
|
|
165
|
+
const pluginObservableRunner = path.join(__dirname, "..", "src", "runner", "plugin-observable.ts");
|
|
166
|
+
const pluginCustomRunner = path.join(__dirname, "..", "src", "runner", "plugin-custom.ts");
|
|
167
|
+
const eventsDefaultSpec = path.join(__dirname, "..", "src", "plugins", "events-default", "index.ts");
|
|
168
|
+
const loggingDefaultSpec = path.join(__dirname, "..", "src", "plugins", "logging-default", "index.ts");
|
|
169
|
+
const configDefaultSpec = path.join(__dirname, "..", "src", "plugins", "config-default", "index.ts");
|
|
170
|
+
const observableDefaultSpec = path.join(__dirname, "..", "src", "plugins", "observable-default", "index.ts");
|
|
171
|
+
|
|
172
|
+
const runMocha = (env, spec, useTs, coverage, coverageInclude) => {
|
|
173
|
+
const mochaArgs = [];
|
|
174
|
+
if (useTs) {
|
|
175
|
+
mochaArgs.push("--require", tsNodeRegister);
|
|
176
|
+
}
|
|
177
|
+
mochaArgs.push("--require", setupHook, spec);
|
|
178
|
+
|
|
179
|
+
if (!coverage) {
|
|
180
|
+
const result = spawnSync(process.execPath, [mochaBin, ...mochaArgs], {
|
|
181
|
+
stdio: "inherit",
|
|
182
|
+
env,
|
|
183
|
+
});
|
|
184
|
+
return result.status || 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const nycArgs = [
|
|
188
|
+
"--all",
|
|
189
|
+
"--check-coverage",
|
|
190
|
+
"--lines",
|
|
191
|
+
"100",
|
|
192
|
+
"--functions",
|
|
193
|
+
"100",
|
|
194
|
+
"--branches",
|
|
195
|
+
"100",
|
|
196
|
+
"--statements",
|
|
197
|
+
"100",
|
|
198
|
+
"--extension",
|
|
199
|
+
useTs ? ".ts" : ".js",
|
|
200
|
+
"--reporter",
|
|
201
|
+
"text",
|
|
202
|
+
"--reporter",
|
|
203
|
+
"lcov",
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
if (coverageInclude && coverageInclude.length) {
|
|
207
|
+
for (const inc of coverageInclude) {
|
|
208
|
+
nycArgs.push("--include", inc);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
nycArgs.push(mochaBin, ...mochaArgs);
|
|
213
|
+
|
|
214
|
+
const result = spawnSync(process.execPath, [nycBin, ...nycArgs], {
|
|
215
|
+
stdio: "inherit",
|
|
216
|
+
env,
|
|
217
|
+
});
|
|
218
|
+
return result.status || 0;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
let exitCode = 0;
|
|
222
|
+
|
|
223
|
+
for (const plugin of filtered) {
|
|
224
|
+
if (plugin.testsEntry && plugin.testsEntry.skip === true) {
|
|
225
|
+
console.warn("Skipping tests for plugin (skipped):", plugin.name);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const useTs = forceTs || (!forceJs && fs.existsSync(path.join(plugin.pluginRoot, "src")));
|
|
230
|
+
const pluginModule = resolvePluginModule(plugin.pluginRoot, plugin.pluginPath, useTs);
|
|
231
|
+
if (!fs.existsSync(pluginModule)) {
|
|
232
|
+
console.error("Plugin module not found:", pluginModule);
|
|
233
|
+
exitCode = 2;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const localBaseEntry = resolveLocalBaseEntry(repoRoot, useTs);
|
|
238
|
+
const testsEntry = plugin.testsEntry || {};
|
|
239
|
+
const defaultConfig = testsEntry.default?.config || null;
|
|
240
|
+
const defaultSetup = normalizeSetup(testsEntry.default?.setup || null);
|
|
241
|
+
const defaultDispose = normalizeDispose(testsEntry.default?.dispose || null);
|
|
242
|
+
const testCases = Array.isArray(testsEntry.tests) && testsEntry.tests.length > 0
|
|
243
|
+
? testsEntry.tests
|
|
244
|
+
: [ { name: "default", config: null, setup: null, dispose: null } ];
|
|
245
|
+
|
|
246
|
+
const coverageInclude = useTs
|
|
247
|
+
? [path.join(plugin.pluginRoot, "src", "**", "*.ts")]
|
|
248
|
+
: [path.join(plugin.pluginRoot, "lib", "**", "*.js")];
|
|
249
|
+
|
|
250
|
+
const runSetupScript = (scriptPath, phase) => {
|
|
251
|
+
if (!scriptPath) return 0;
|
|
252
|
+
const resolved = path.resolve(plugin.pluginRoot, scriptPath);
|
|
253
|
+
if (!fs.existsSync(resolved)) {
|
|
254
|
+
console.error(`Setup script not found (${phase}):`, resolved);
|
|
255
|
+
return 2;
|
|
256
|
+
}
|
|
257
|
+
const ext = path.extname(resolved).toLowerCase();
|
|
258
|
+
let result;
|
|
259
|
+
if (ext === ".ts") {
|
|
260
|
+
result = spawnSync(process.execPath, ["-r", tsNodeRegister, resolved], {
|
|
261
|
+
stdio: "inherit",
|
|
262
|
+
env: {
|
|
263
|
+
...process.env,
|
|
264
|
+
BSB_TEST_PLUGIN_NAME: plugin.name,
|
|
265
|
+
BSB_TEST_PLUGIN_ID: plugin.id,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
} else if (ext === ".js" || ext === ".cjs" || ext === ".mjs") {
|
|
269
|
+
result = spawnSync(process.execPath, [resolved], {
|
|
270
|
+
stdio: "inherit",
|
|
271
|
+
env: {
|
|
272
|
+
...process.env,
|
|
273
|
+
BSB_TEST_PLUGIN_NAME: plugin.name,
|
|
274
|
+
BSB_TEST_PLUGIN_ID: plugin.id,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
} else {
|
|
278
|
+
result = spawnSync(resolved, {
|
|
279
|
+
stdio: "inherit",
|
|
280
|
+
shell: true,
|
|
281
|
+
env: {
|
|
282
|
+
...process.env,
|
|
283
|
+
BSB_TEST_PLUGIN_NAME: plugin.name,
|
|
284
|
+
BSB_TEST_PLUGIN_ID: plugin.id,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return result.status || 0;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
for (const testCase of testCases) {
|
|
292
|
+
if (testCase && testCase.skip === true) {
|
|
293
|
+
console.warn("Skipping test case (skipped):", testCase.name || "unnamed");
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const mergedConfig = mergeConfig(defaultConfig, testCase?.config);
|
|
298
|
+
const setup = normalizeSetup(testCase?.setup) || defaultSetup;
|
|
299
|
+
const dispose = normalizeDispose(testCase?.dispose) || defaultDispose;
|
|
300
|
+
const beforeAll = setup?.beforeAll || null;
|
|
301
|
+
const afterAll = setup?.afterAll || null;
|
|
302
|
+
|
|
303
|
+
const env = {
|
|
304
|
+
...process.env,
|
|
305
|
+
BSB_TEST_PLUGIN_MODULE: pluginModule,
|
|
306
|
+
BSB_TEST_PLUGIN_NAME: plugin.name,
|
|
307
|
+
BSB_TEST_PLUGIN_CONFIG: JSON.stringify(mergedConfig || null),
|
|
308
|
+
BSB_TEST_LOCAL_BASE_ENTRY: localBaseEntry || "",
|
|
309
|
+
TS_NODE_PROJECT: path.join(__dirname, "..", "tsconfig.json"),
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
if (beforeAll) {
|
|
313
|
+
const code = runSetupScript(beforeAll, "beforeAll");
|
|
314
|
+
if (code !== 0) {
|
|
315
|
+
exitCode = code;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const isEvents = plugin.id.startsWith("events-") || plugin.name.startsWith("events-");
|
|
321
|
+
if (isEvents) {
|
|
322
|
+
const code = runMocha(env, pluginEventsRunner, useTs, enableCoverage, coverageInclude);
|
|
323
|
+
if (code !== 0) exitCode = code;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const isObservable = plugin.id.startsWith("observable-") || plugin.name.startsWith("observable-");
|
|
327
|
+
if (isObservable) {
|
|
328
|
+
const code = runMocha(env, pluginObservableRunner, useTs, enableCoverage, coverageInclude);
|
|
329
|
+
if (code !== 0) exitCode = code;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (plugin.id === "events-default") {
|
|
333
|
+
const code = runMocha(env, eventsDefaultSpec, useTs, enableCoverage, coverageInclude);
|
|
334
|
+
if (code !== 0) exitCode = code;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (plugin.id === "logging-default") {
|
|
338
|
+
const code = runMocha(env, loggingDefaultSpec, useTs, enableCoverage, coverageInclude);
|
|
339
|
+
if (code !== 0) exitCode = code;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (plugin.id === "config-default") {
|
|
343
|
+
const code = runMocha(env, configDefaultSpec, useTs, enableCoverage, coverageInclude);
|
|
344
|
+
if (code !== 0) exitCode = code;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (plugin.id === "observable-default") {
|
|
348
|
+
const code = runMocha(env, observableDefaultSpec, useTs, enableCoverage, coverageInclude);
|
|
349
|
+
if (code !== 0) exitCode = code;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const customEnv = {
|
|
353
|
+
...env,
|
|
354
|
+
BSB_TEST_PLUGIN_ID: plugin.id,
|
|
355
|
+
BSB_TEST_PLUGIN_ROOT: plugin.pluginRoot,
|
|
356
|
+
};
|
|
357
|
+
const customCode = runMocha(customEnv, pluginCustomRunner, useTs, enableCoverage, coverageInclude);
|
|
358
|
+
if (customCode !== 0) exitCode = customCode;
|
|
359
|
+
|
|
360
|
+
if (afterAll) {
|
|
361
|
+
const code = runSetupScript(afterAll, "afterAll");
|
|
362
|
+
if (code !== 0) {
|
|
363
|
+
exitCode = code;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (dispose?.afterAll) {
|
|
368
|
+
const code = runSetupScript(dispose.afterAll, "dispose");
|
|
369
|
+
if (code !== 0) {
|
|
370
|
+
exitCode = code;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
process.exit(exitCode);
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @bsb/tests
|
|
2
|
+
|
|
3
|
+
Shared test runner for BSB plugins.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @bsb/tests
|
|
9
|
+
npx @bsb/tests --plugin events-rabbitmq
|
|
10
|
+
npx @bsb/tests --no-coverage
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## bsb-tests.json
|
|
14
|
+
|
|
15
|
+
Place `bsb-tests.json` next to `bsb-plugin.json`.
|
|
16
|
+
|
|
17
|
+
Format:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"nodejs": [
|
|
22
|
+
{
|
|
23
|
+
"id": "events-rabbitmq",
|
|
24
|
+
"skip": false,
|
|
25
|
+
"default": {
|
|
26
|
+
"config": {
|
|
27
|
+
"endpoints": ["amqp://127.0.0.1:5670"]
|
|
28
|
+
},
|
|
29
|
+
"setup": "scripts/test-setup.sh",
|
|
30
|
+
"dispose": "scripts/test-dispose.sh"
|
|
31
|
+
},
|
|
32
|
+
"tests": [
|
|
33
|
+
{
|
|
34
|
+
"name": "primary",
|
|
35
|
+
"skip": false,
|
|
36
|
+
"config": {
|
|
37
|
+
"prefetch": 10
|
|
38
|
+
},
|
|
39
|
+
"setup": {
|
|
40
|
+
"beforeAll": "scripts/test-setup.js",
|
|
41
|
+
"afterAll": "scripts/test-teardown.js"
|
|
42
|
+
},
|
|
43
|
+
"dispose": {
|
|
44
|
+
"afterAll": "scripts/test-dispose.js"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Notes:
|
|
54
|
+
|
|
55
|
+
- `skip: true` skips plugin tests or specific test cases.
|
|
56
|
+
- `setup` can be a single script path or `{ "beforeAll": "...", "afterAll": "..." }`.
|
|
57
|
+
- `dispose` is an optional cleanup hook (run after tests).
|
|
58
|
+
- If `tests` is empty, the `default` config is used once.
|
|
59
|
+
- Script paths are relative to the plugin root.
|
|
60
|
+
|
|
61
|
+
## Custom Tests
|
|
62
|
+
|
|
63
|
+
Add custom tests under `tests/{plugin-id}` in the repo root.
|
|
64
|
+
|
|
65
|
+
Layout:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
tests/
|
|
69
|
+
events-rabbitmq/
|
|
70
|
+
_before.ts
|
|
71
|
+
_after.ts
|
|
72
|
+
connection.ts
|
|
73
|
+
streaming/
|
|
74
|
+
_before.ts
|
|
75
|
+
_after.ts
|
|
76
|
+
roundtrip.ts
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Rules:
|
|
80
|
+
|
|
81
|
+
- Each `*.ts/js` file (not starting with `_`) exports a single test function.
|
|
82
|
+
- Files starting with `_` are ignored as tests and can be used for helpers.
|
|
83
|
+
- `_before` and `_after` run before/after all tests in the same folder.
|
|
84
|
+
- If `_before` returns a value, it is passed to each test in that folder.
|
|
85
|
+
|
|
86
|
+
Test function signature:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
export default async function test(ctx: any, data?: any) {
|
|
90
|
+
// ctx: { pluginId, pluginName, pluginRoot, config, group }
|
|
91
|
+
// data: value returned by _before (optional)
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Built-in Suites
|
|
96
|
+
|
|
97
|
+
- `events-*` plugins
|
|
98
|
+
- `observable-*` plugins
|
|
99
|
+
- `events-default` additional suite
|
|
100
|
+
- `observable-default` additional suite
|
|
101
|
+
- `config-default`
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./trace";
|
|
2
|
+
export * from "./mocks";
|
|
3
|
+
export * from "./sb/plugins/events/broadcast";
|
|
4
|
+
export * from "./sb/plugins/events/emit";
|
|
5
|
+
export * from "./sb/plugins/events/emitAndReturn";
|
|
6
|
+
export * from "./sb/plugins/events/emitStreamAndReceiveStream";
|
|
7
|
+
export * from "./sb/plugins/observable/index";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./trace"), exports);
|
|
18
|
+
__exportStar(require("./mocks"), exports);
|
|
19
|
+
__exportStar(require("./sb/plugins/events/broadcast"), exports);
|
|
20
|
+
__exportStar(require("./sb/plugins/events/emit"), exports);
|
|
21
|
+
__exportStar(require("./sb/plugins/events/emitAndReturn"), exports);
|
|
22
|
+
__exportStar(require("./sb/plugins/events/emitStreamAndReceiveStream"), exports);
|
|
23
|
+
__exportStar(require("./sb/plugins/observable/index"), exports);
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,0CAAwB;AACxB,gEAA8C;AAC9C,2DAAyC;AACzC,oEAAkD;AAClD,iFAA+D;AAC/D,gEAA8C"}
|
package/lib/mocks.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BSB (Better-Service-Base) is an event-bus based microservice framework.
|
|
3
|
+
* Copyright (C) 2016 - 2025 BetterCorp (PTY) Ltd
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* Alternatively, you may obtain a commercial license for this program.
|
|
11
|
+
* The commercial license allows you to use the Program in a closed-source manner,
|
|
12
|
+
* including the right to create derivative works that are not subject to the terms
|
|
13
|
+
* of the AGPL.
|
|
14
|
+
*
|
|
15
|
+
* To obtain a commercial license, please contact the copyright holders at
|
|
16
|
+
* https://www.bettercorp.dev. The terms and conditions of the commercial license
|
|
17
|
+
* will be provided upon request.
|
|
18
|
+
*
|
|
19
|
+
* This program is distributed in the hope that it will be useful,
|
|
20
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
21
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
22
|
+
* GNU Affero General Public License for more details.
|
|
23
|
+
*
|
|
24
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
25
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
26
|
+
*/
|
|
27
|
+
import { DEBUG_MODE, SBConfig, SBEvents, SBPlugins, BSBEventsConstructor, ObservableBackend, BSBObservableConstructor, BSBConfigConstructor } from "@bsb/base";
|
|
28
|
+
import { SBObservable } from "@bsb/base";
|
|
29
|
+
export declare const MockSBObservable: () => SBObservable;
|
|
30
|
+
export declare const MockSBConfig: () => SBConfig;
|
|
31
|
+
export declare const MockSBEvents: () => SBEvents;
|
|
32
|
+
export declare const MockSBPlugins: () => SBPlugins;
|
|
33
|
+
export declare const newSBObservable: () => Promise<SBObservable>;
|
|
34
|
+
export declare const newMetrics: () => Promise<ObservableBackend>;
|
|
35
|
+
export declare const getObservableConstructorConfig: (mode?: DEBUG_MODE) => BSBObservableConstructor;
|
|
36
|
+
export declare const getLoggingConstructorConfig: (mode?: DEBUG_MODE) => BSBObservableConstructor;
|
|
37
|
+
export declare const getConfigConstructorConfig: (config: any) => BSBConfigConstructor;
|
|
38
|
+
export declare const getEventsConstructorConfig: (config: any) => Promise<BSBEventsConstructor>;
|
|
39
|
+
export declare const generateNullLogging: () => ObservableBackend;
|