@riebeckite/test 0.0.35 → 0.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/test",
3
- "version": "0.0.35",
3
+ "version": "0.0.37",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,12 +23,6 @@
23
23
  "import": "./dist/fixtures.js",
24
24
  "source": "./fixtures.ts",
25
25
  "default": "./dist/fixtures.js"
26
- },
27
- "./plugin-contract": {
28
- "types": "./dist/plugin-contract.d.ts",
29
- "import": "./dist/plugin-contract.js",
30
- "source": "./plugin-contract.ts",
31
- "default": "./dist/plugin-contract.js"
32
26
  }
33
27
  },
34
28
  "files": [
@@ -1,39 +0,0 @@
1
- export type PluginContractLevel = "basic" | "integration" | "full";
2
- export type PluginContractStatus = "PASS" | "FAIL" | "SKIP" | "UNSUPPORTED" | "UNVERIFIED";
3
- export type PluginContractCapability = "package" | "plugin" | "markdown" | "client" | "publication";
4
- export type PluginContractResult = {
5
- readonly contract: string;
6
- readonly status: PluginContractStatus;
7
- readonly expected: string;
8
- readonly actual: string;
9
- readonly guidance?: string;
10
- readonly executed: boolean;
11
- };
12
- export type PluginContractFixtureCommand = {
13
- readonly command: string;
14
- readonly args: readonly string[];
15
- readonly cwd?: string;
16
- readonly browser?: boolean;
17
- };
18
- export type PluginContractFixture = {
19
- readonly packageName: string;
20
- readonly packageDirectory: string;
21
- readonly integration: PluginContractFixtureCommand;
22
- readonly full?: PluginContractFixtureCommand;
23
- };
24
- export type PluginContractReport = {
25
- readonly packagePath: string;
26
- readonly packageName: string;
27
- readonly level: PluginContractLevel;
28
- readonly capabilities: readonly PluginContractCapability[];
29
- readonly results: readonly PluginContractResult[];
30
- readonly passed: boolean;
31
- readonly complete: boolean;
32
- };
33
- export type VerifyPluginContractOptions = {
34
- readonly level?: PluginContractLevel;
35
- readonly capabilities?: readonly PluginContractCapability[];
36
- readonly fixtures?: readonly PluginContractFixture[];
37
- };
38
- export declare function verifyPluginContract(packagePath: string, options?: VerifyPluginContractOptions): PluginContractReport;
39
- export declare function formatPluginContractReport(report: PluginContractReport): string;
@@ -1,413 +0,0 @@
1
- // plugin-contract.ts
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
-
6
- // e2e/process.ts
7
- import { spawn, spawnSync } from "node:child_process";
8
- var MAX_BUFFER = 128 * 1024 * 1024;
9
- function quote(value) {
10
- const text = String(value);
11
- return /[\s"]/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text;
12
- }
13
- function failureDetail(stdout, stderr) {
14
- return `--- stdout ---
15
- ${stdout}
16
- --- stderr ---
17
- ${stderr}`;
18
- }
19
- function run(command, args, options = {}) {
20
- const line = [command, ...args].map(quote).join(" ");
21
- const result = spawnSync(line, {
22
- shell: true,
23
- cwd: options.cwd ?? process.cwd(),
24
- env: { ...process.env, ...options.env },
25
- encoding: "utf8",
26
- maxBuffer: MAX_BUFFER
27
- });
28
- if (result.error) {
29
- throw new Error(`Failed to run: ${line}
30
- ${result.error.message}`);
31
- }
32
- const stdout = result.stdout ?? "";
33
- const stderr = result.stderr ?? "";
34
- if (result.status !== 0 && !options.allowFailure) {
35
- throw new Error(
36
- `Command failed (exit ${result.status}): ${line}
37
- ` + failureDetail(stdout, stderr)
38
- );
39
- }
40
- return { status: result.status, stdout, stderr };
41
- }
42
-
43
- // plugin-contract.ts
44
- function verifyPluginContract(packagePath, options = {}) {
45
- const level = options.level ?? "basic";
46
- const root = fs.realpathSync(packagePath);
47
- const manifestPath = path.join(root, "package.json");
48
- const manifest = JSON.parse(
49
- fs.readFileSync(manifestPath, "utf8")
50
- );
51
- const packageName = typeof manifest.name === "string" ? manifest.name : "(unnamed package)";
52
- const capabilities = options.capabilities ?? inferCapabilities(manifest);
53
- const fixture = findTrustedFixture(root, manifest, options.fixtures ?? []);
54
- const results = [
55
- ...verifyManifest(root, manifest),
56
- ...verifyDeclaredCapabilities(
57
- manifest,
58
- capabilities,
59
- level,
60
- fixture !== null
61
- ),
62
- ...verifyFixture(manifest, fixture, options.fixtures ?? [], level),
63
- ...level === "full" ? verifyPackedManifest(root, manifest) : []
64
- ];
65
- const passed = results.every((result) => result.status !== "FAIL");
66
- return {
67
- packagePath: root,
68
- packageName,
69
- level,
70
- capabilities,
71
- results,
72
- passed,
73
- complete: passed && results.every(
74
- (result) => result.status !== "UNVERIFIED" && result.status !== "UNSUPPORTED"
75
- )
76
- };
77
- }
78
- function formatPluginContractReport(report) {
79
- const executed = report.results.filter((result) => result.executed).length;
80
- const failed = report.results.filter(
81
- (result) => result.status === "FAIL"
82
- ).length;
83
- const lines = [
84
- `Plugin Contract Lab: ${report.packageName}`,
85
- `level: ${report.level}; capabilities: ${report.capabilities.join(", ")}`,
86
- `executed: ${executed}; not executed: ${report.results.length - executed}; failed: ${failed}`
87
- ];
88
- for (const result of report.results) {
89
- lines.push(
90
- `${result.status.padEnd(11)} ${result.contract}${result.executed ? "" : " (not executed)"}: expected ${result.expected}; actual ${result.actual}`
91
- );
92
- if (result.guidance) lines.push(` ${result.guidance}`);
93
- }
94
- lines.push(
95
- report.passed ? report.complete ? "Verification completed without contract failures." : "Verification incomplete: some requested contracts were not executed." : "Verification failed. Fix the failed contracts and rerun the same command."
96
- );
97
- return lines.join("\n");
98
- }
99
- function inferCapabilities(manifest) {
100
- const exports = exportEntries(manifest.exports);
101
- return [
102
- "package",
103
- ...exports.has("./client") || exports.has("./style.css") ? ["client"] : []
104
- ];
105
- }
106
- function verifyManifest(root, manifest) {
107
- const results = [];
108
- const exports = exportEntries(manifest.exports);
109
- if (typeof manifest.name !== "string" || manifest.name.length === 0) {
110
- results.push(
111
- fail(
112
- "package.name",
113
- "a non-empty npm package name",
114
- String(manifest.name)
115
- )
116
- );
117
- }
118
- if (!exports.has(".")) {
119
- results.push(fail("package.exports", 'a root "." export', "missing"));
120
- return results;
121
- }
122
- for (const [subpath, target] of exports) {
123
- const importPath = targetPath(target, "import");
124
- const typesPath = targetPath(target, "types");
125
- if (importPath === void 0 && !subpath.endsWith(".css")) {
126
- results.push(fail(`exports.${subpath}`, "an import target", "missing"));
127
- continue;
128
- }
129
- for (const [kind, relative] of [
130
- ["import", importPath],
131
- ["types", typesPath]
132
- ]) {
133
- if (relative === void 0) continue;
134
- const targetPathname = path.resolve(root, relative);
135
- if (!isInside(root, targetPathname) || !fs.existsSync(targetPathname)) {
136
- results.push(
137
- fail(
138
- `exports.${subpath}.${kind}`,
139
- `an existing file inside the package`,
140
- relative
141
- )
142
- );
143
- }
144
- }
145
- }
146
- if (typeof manifest.types === "string" && !fs.existsSync(path.join(root, manifest.types))) {
147
- results.push(
148
- fail("package.types", "an existing declaration file", manifest.types)
149
- );
150
- }
151
- if (results.length === 0) {
152
- results.push(
153
- pass(
154
- "package.exports",
155
- "published exports and types exist",
156
- "all declared targets exist"
157
- )
158
- );
159
- }
160
- return results;
161
- }
162
- function verifyDeclaredCapabilities(manifest, capabilities, level, hasTrustedFixture) {
163
- const exports = exportEntries(manifest.exports);
164
- const results = [];
165
- if (capabilities.includes("client")) {
166
- results.push(
167
- exports.has("./client") ? pass("client.entry", "a public ./client export", "declared") : fail("client.entry", "a public ./client export", "missing")
168
- );
169
- results.push(
170
- exports.has("./style.css") ? pass("client.css", "a public ./style.css export", "declared") : fail("client.css", "a public ./style.css export", "missing")
171
- );
172
- }
173
- for (const capability of capabilities) {
174
- if (capability === "package" || capability === "client") continue;
175
- results.push({
176
- contract: `${capability}.runtime`,
177
- status: level === "basic" ? "SKIP" : "UNVERIFIED",
178
- expected: "an explicit fixture supplied by the plugin author",
179
- actual: hasTrustedFixture ? "trusted fixture registered; capability-specific coverage is not declared" : "no fixture declaration",
180
- guidance: "Add a focused plugin test or run the site integration that exercises this capability.",
181
- executed: false
182
- });
183
- }
184
- if (level !== "basic" && !capabilities.includes("plugin") && !hasTrustedFixture) {
185
- results.push({
186
- contract: "plugin.runtime",
187
- status: "UNSUPPORTED",
188
- expected: "a plugin factory fixture",
189
- actual: "package metadata cannot safely execute arbitrary plugin code",
190
- guidance: "Register a repository-owned fixture; the verifier does not sandbox third-party code.",
191
- executed: false
192
- });
193
- }
194
- return results;
195
- }
196
- function findTrustedFixture(root, manifest, fixtures) {
197
- if (typeof manifest.name !== "string") return null;
198
- return fixtures.find(
199
- (fixture) => fixture.packageName === manifest.name && isSameDirectory(root, fixture.packageDirectory)
200
- ) ?? null;
201
- }
202
- function verifyFixture(manifest, fixture, fixtures, level) {
203
- if (level === "basic") return [];
204
- const packageName = typeof manifest.name === "string" ? manifest.name : "(unnamed package)";
205
- if (!fixture) {
206
- const registeredByName = fixtures.some(
207
- (candidate) => candidate.packageName === packageName
208
- );
209
- return [
210
- {
211
- contract: "fixture.registry",
212
- status: "UNVERIFIED",
213
- expected: `a trusted fixture registered for ${packageName} at this exact directory`,
214
- actual: registeredByName ? "a fixture is registered for a different package directory" : "no trusted fixture is registered",
215
- guidance: "The verifier runs only repository-owned fixtures registered for the exact package name and directory. No plugin code was executed.",
216
- executed: false
217
- }
218
- ];
219
- }
220
- const fullCommand = level === "full" ? fixture.full : void 0;
221
- const useFull = fullCommand !== void 0;
222
- const command = fullCommand ?? fixture.integration;
223
- const contract = useFull ? "fixture.full" : "fixture.integration";
224
- const executed = executeFixtureCommand(contract, command);
225
- const results = [executed];
226
- if (level !== "full") return results;
227
- if (!useFull) {
228
- results.push({
229
- contract: "fixture.browser",
230
- status: "UNVERIFIED",
231
- expected: "a browser fixture command",
232
- actual: "no full fixture is registered",
233
- guidance: "Register a full fixture that runs browser assertions; integration output does not prove browser behavior.",
234
- executed: false
235
- });
236
- return results;
237
- }
238
- if (!command.browser) {
239
- results.push({
240
- contract: "fixture.browser",
241
- status: "UNVERIFIED",
242
- expected: "a fixture command that runs a real browser",
243
- actual: "the full fixture is not marked as a browser fixture",
244
- guidance: "Mark the command as a browser fixture only when it launches a browser. A passing build is not browser verification.",
245
- executed: false
246
- });
247
- return results;
248
- }
249
- results.push(
250
- executed.status === "PASS" ? pass(
251
- "fixture.browser",
252
- "trusted fixture browser assertions pass",
253
- "fixture ran browser assertions and exited 0"
254
- ) : {
255
- contract: "fixture.browser",
256
- status: "FAIL",
257
- expected: "trusted fixture browser assertions pass",
258
- actual: "the full fixture command failed",
259
- guidance: "Inspect the fixture output. Browser behavior is not verified while the command fails.",
260
- executed: true
261
- }
262
- );
263
- return results;
264
- }
265
- function executeFixtureCommand(contract, command) {
266
- try {
267
- const result = run(command.command, command.args, {
268
- cwd: command.cwd,
269
- allowFailure: true
270
- });
271
- const output = [result.stdout, result.stderr].map((text) => text.trim()).filter(Boolean).join("\n");
272
- const commandLine = [command.command, ...command.args].join(" ");
273
- if (result.status === 0) {
274
- return pass(
275
- contract,
276
- `${commandLine} exits 0`,
277
- "trusted fixture command completed"
278
- );
279
- }
280
- return {
281
- contract,
282
- status: "FAIL",
283
- expected: `${commandLine} exits 0`,
284
- actual: `exit ${result.status}${output ? `
285
- ${tail(output, 40)}` : ""}`,
286
- guidance: "Inspect the fixture output and rerun the same command.",
287
- executed: true
288
- };
289
- } catch (error) {
290
- return {
291
- contract,
292
- status: "FAIL",
293
- expected: `${[command.command, ...command.args].join(" ")} exits 0`,
294
- actual: error instanceof Error ? error.message : String(error),
295
- guidance: "The trusted fixture could not start. Check the command and its working directory.",
296
- executed: false
297
- };
298
- }
299
- }
300
- function tail(text, lines) {
301
- return text.split(/\r?\n/).slice(-lines).join("\n");
302
- }
303
- function isSameDirectory(left, right) {
304
- try {
305
- return fs.realpathSync(left) === fs.realpathSync(right);
306
- } catch {
307
- return false;
308
- }
309
- }
310
- function verifyPackedManifest(root, manifest) {
311
- const temp = fs.mkdtempSync(
312
- path.join(os.tmpdir(), "riebeckite-plugin-contract-")
313
- );
314
- try {
315
- const packed = run(
316
- "pnpm",
317
- [
318
- "--config.ignore-scripts=true",
319
- "--config.ignore-pnpmfile=true",
320
- "pack",
321
- "--pack-destination",
322
- temp
323
- ],
324
- {
325
- cwd: root,
326
- allowFailure: true
327
- }
328
- );
329
- if (packed.status !== 0) {
330
- return [
331
- fail(
332
- "package.pack",
333
- "pnpm pack succeeds",
334
- packed.stderr.trim() || packed.stdout.trim() || "pnpm pack failed"
335
- )
336
- ];
337
- }
338
- const tarball = fs.readdirSync(temp).find((name) => name.endsWith(".tgz"));
339
- if (!tarball)
340
- return [fail("package.pack", "a .tgz tarball", "none produced")];
341
- const listed = run("tar", ["-tzf", path.join(temp, tarball)], {
342
- allowFailure: true
343
- });
344
- if (listed.status !== 0)
345
- return [
346
- fail("package.tarball", "a readable tarball", listed.stderr.trim())
347
- ];
348
- const files = new Set(listed.stdout.split(/\r?\n/));
349
- const missing = ["package/package.json", "package/dist/"].filter(
350
- (entry) => ![...files].some((file) => file === entry || file.startsWith(entry))
351
- );
352
- const lifecycleScripts = declaredPackScripts(manifest);
353
- return missing.length === 0 ? [
354
- pass(
355
- "package.tarball",
356
- "package.json and dist/ in npm pack output",
357
- `present; lifecycle scripts disabled; pnpmfile disabled${lifecycleScripts.length > 0 ? ` (${lifecycleScripts.join(", ")} declared)` : ""}`
358
- )
359
- ] : [
360
- fail(
361
- "package.tarball",
362
- "package.json and dist/ in npm pack output",
363
- `missing ${missing.join(", ")}`
364
- )
365
- ];
366
- } finally {
367
- fs.rmSync(temp, { recursive: true, force: true });
368
- }
369
- }
370
- function declaredPackScripts(manifest) {
371
- if (typeof manifest.scripts !== "object" || manifest.scripts === null || Array.isArray(manifest.scripts))
372
- return [];
373
- const scripts = manifest.scripts;
374
- return ["prepack", "prepare", "postpack"].filter(
375
- (name) => typeof scripts[name] === "string"
376
- );
377
- }
378
- function exportEntries(value) {
379
- if (typeof value !== "object" || value === null || Array.isArray(value))
380
- return /* @__PURE__ */ new Map();
381
- return new Map(
382
- Object.entries(value).filter(
383
- ([key]) => key === "." || key.startsWith("./")
384
- )
385
- );
386
- }
387
- function targetPath(target, condition) {
388
- if (typeof target === "string")
389
- return condition === "import" ? target : void 0;
390
- const value = target[condition];
391
- return typeof value === "string" ? value : void 0;
392
- }
393
- function isInside(root, target) {
394
- const relative = path.relative(root, target);
395
- return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
396
- }
397
- function pass(contract, expected, actual) {
398
- return { contract, status: "PASS", expected, actual, executed: true };
399
- }
400
- function fail(contract, expected, actual) {
401
- return {
402
- contract,
403
- status: "FAIL",
404
- expected,
405
- actual,
406
- guidance: "Correct the package contract and rerun the command.",
407
- executed: true
408
- };
409
- }
410
- export {
411
- formatPluginContractReport,
412
- verifyPluginContract
413
- };