@elench/testkit 0.1.35 → 0.1.37

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.
@@ -72,6 +72,7 @@ describe("runner results", () => {
72
72
  status: "failed",
73
73
  durationMs: 250,
74
74
  error: "boom",
75
+ reason: null,
75
76
  },
76
77
  ]);
77
78
  });
@@ -193,6 +194,119 @@ describe("runner results", () => {
193
194
  expect(result.totalTaskDurationMs).toBe(3_000);
194
195
  });
195
196
 
197
+ it("counts config-skipped files and runtime-skipped files separately from passed work", () => {
198
+ const trackers = buildServiceTrackers(
199
+ [
200
+ {
201
+ skipped: false,
202
+ config: {
203
+ name: "api",
204
+ testkit: {
205
+ database: {
206
+ selectedBackend: null,
207
+ },
208
+ },
209
+ },
210
+ suites: [
211
+ {
212
+ name: "billing",
213
+ type: "integration",
214
+ framework: "k6",
215
+ files: ["tests/billing-live.int.testkit.ts"],
216
+ skippedFiles: [
217
+ {
218
+ path: "tests/billing-stubbed.int.testkit.ts",
219
+ reason: "Billing is stubbed",
220
+ },
221
+ ],
222
+ totalFileCount: 2,
223
+ orderIndex: 0,
224
+ },
225
+ {
226
+ name: "frontend-smoke",
227
+ type: "e2e",
228
+ framework: "playwright",
229
+ files: ["tests/frontend-smoke.pw.testkit.ts"],
230
+ orderIndex: 1,
231
+ },
232
+ ],
233
+ },
234
+ ],
235
+ 1000
236
+ );
237
+
238
+ recordTaskOutcome(
239
+ trackers,
240
+ {
241
+ serviceName: "api",
242
+ suiteKey: "integration:billing",
243
+ file: "tests/billing-live.int.testkit.ts",
244
+ },
245
+ {
246
+ failed: false,
247
+ durationMs: 100,
248
+ error: null,
249
+ },
250
+ 1100
251
+ );
252
+ recordTaskOutcome(
253
+ trackers,
254
+ {
255
+ serviceName: "api",
256
+ suiteKey: "e2e:frontend-smoke",
257
+ file: "tests/frontend-smoke.pw.testkit.ts",
258
+ },
259
+ {
260
+ failed: false,
261
+ status: "skipped",
262
+ reason: "Playwright test.skip()",
263
+ durationMs: 50,
264
+ error: null,
265
+ },
266
+ 1150
267
+ );
268
+
269
+ const result = finalizeServiceResult(trackers.get("api"), 1000, 1200);
270
+
271
+ expect(result.failed).toBe(false);
272
+ expect(result.completedSuiteCount).toBe(2);
273
+ expect(result.skippedSuiteCount).toBe(1);
274
+ expect(result.totalFileCount).toBe(3);
275
+ expect(result.completedFileCount).toBe(1);
276
+ expect(result.passedFileCount).toBe(1);
277
+ expect(result.failedFileCount).toBe(0);
278
+ expect(result.skippedFileCount).toBe(2);
279
+ expect(result.notRunFileCount).toBe(0);
280
+ expect(result.suites[0].files).toEqual([
281
+ {
282
+ path: "tests/billing-live.int.testkit.ts",
283
+ failed: false,
284
+ status: "passed",
285
+ durationMs: 100,
286
+ error: null,
287
+ reason: null,
288
+ },
289
+ {
290
+ path: "tests/billing-stubbed.int.testkit.ts",
291
+ failed: false,
292
+ status: "skipped",
293
+ durationMs: 0,
294
+ error: null,
295
+ reason: "Billing is stubbed",
296
+ },
297
+ ]);
298
+ expect(result.suites[1].files).toEqual([
299
+ {
300
+ path: "tests/frontend-smoke.pw.testkit.ts",
301
+ failed: false,
302
+ status: "skipped",
303
+ durationMs: 50,
304
+ error: null,
305
+ reason: "Playwright test.skip()",
306
+ },
307
+ ]);
308
+ });
309
+
196
310
  it("summarizes mixed db backends", () => {
197
311
  expect(
198
312
  summarizeDbBackend([{ dbBackend: "local" }, { dbBackend: "neon" }])
@@ -8,7 +8,9 @@ export function findUnmatchedRequestedFiles(
8
8
  ) {
9
9
  const matchedFiles = new Set();
10
10
  for (const config of configs) {
11
- const suites = collectSuites(config, typeValues, suiteSelectors, []);
11
+ const suites = collectSuites(config, typeValues, suiteSelectors, [], {
12
+ ignoreSkipRules: true,
13
+ });
12
14
  for (const suite of suites) {
13
15
  for (const file of suite.files) {
14
16
  matchedFiles.add(normalizePathSeparators(file));
@@ -23,6 +23,12 @@ export interface RuntimeOptions {
23
23
  thresholds?: Record<string, unknown>;
24
24
  }
25
25
 
26
+ export interface RuntimeArtifactOptions {
27
+ contentType?: string;
28
+ kind?: string;
29
+ summary?: string;
30
+ }
31
+
26
32
  export interface RuntimeEnv {
27
33
  BASE: string;
28
34
  MACHINE_ID?: string;
@@ -133,6 +139,11 @@ export declare const http: RuntimeHttpClient;
133
139
 
134
140
  export declare function file(data: unknown, filename?: string, contentType?: string): unknown;
135
141
  export declare function json<T = unknown>(response: Pick<RuntimeResponse, "body">): T;
142
+ export declare function emitArtifact(
143
+ name: string,
144
+ data: unknown,
145
+ options?: RuntimeArtifactOptions
146
+ ): void;
136
147
  export declare function contains<T extends Record<string, unknown>>(
137
148
  rows: T[],
138
149
  field: keyof T | string,
@@ -10,6 +10,9 @@ export function file(data, filename, contentType) {
10
10
  return rawHttp.file(data, filename, contentType);
11
11
  }
12
12
 
13
+ export {
14
+ emitArtifact,
15
+ } from "../runtime-src/k6/artifacts.js";
13
16
  export {
14
17
  allMatch,
15
18
  contains,
@@ -0,0 +1,36 @@
1
+ export const RUNTIME_ARTIFACT_MARKER = "TESTKIT_ARTIFACT:";
2
+
3
+ export function emitArtifact(name, data, options = {}) {
4
+ const normalizedName = normalizeArtifactName(name);
5
+ const payload = encodeURIComponent(
6
+ JSON.stringify({
7
+ name: normalizedName,
8
+ kind: normalizeOptionalString(options.kind),
9
+ summary: normalizeOptionalString(options.summary),
10
+ contentType: normalizeContentType(options.contentType),
11
+ data,
12
+ emittedAt: new Date().toISOString(),
13
+ })
14
+ );
15
+ console.log(
16
+ `${RUNTIME_ARTIFACT_MARKER}${payload}`
17
+ );
18
+ }
19
+
20
+ function normalizeArtifactName(name) {
21
+ if (typeof name !== "string" || name.trim().length === 0) {
22
+ throw new Error("emitArtifact(name, data) requires a non-empty artifact name");
23
+ }
24
+ return name.trim();
25
+ }
26
+
27
+ function normalizeOptionalString(value) {
28
+ if (value === undefined || value === null) return null;
29
+ const normalized = String(value).trim();
30
+ return normalized.length > 0 ? normalized : null;
31
+ }
32
+
33
+ function normalizeContentType(value) {
34
+ const normalized = normalizeOptionalString(value);
35
+ return normalized || "application/json";
36
+ }
@@ -18,6 +18,21 @@ export interface LifecycleConfig {
18
18
  testkitCwd?: string;
19
19
  }
20
20
 
21
+ export interface SkipFileRule {
22
+ path: string;
23
+ reason: string;
24
+ }
25
+
26
+ export interface SkipSuiteRule {
27
+ selector: string;
28
+ reason: string;
29
+ }
30
+
31
+ export interface SkipConfig {
32
+ files?: SkipFileRule[];
33
+ suites?: SkipSuiteRule[];
34
+ }
35
+
21
36
  export interface ServiceConfig {
22
37
  database?: LocalDatabaseConfig;
23
38
  databaseFrom?: string;
@@ -38,6 +53,7 @@ export interface ServiceConfig {
38
53
  };
39
54
  migrate?: LifecycleConfig;
40
55
  seed?: LifecycleConfig;
56
+ skip?: SkipConfig;
41
57
  }
42
58
 
43
59
  export interface TestkitSetup {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
5
5
  "type": "module",
6
6
  "types": "./lib/index.d.ts",