@ttt-productions/repo-guards 0.1.30 → 0.1.31

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.
@@ -14,11 +14,18 @@ export interface ForbiddenOverride {
14
14
  why: string;
15
15
  fakeCalls?: readonly FakeCall[];
16
16
  }
17
+ export interface BuiltOverride {
18
+ module: string;
19
+ names: readonly string[];
20
+ builders: TypedMockHelper;
21
+ why: string;
22
+ }
17
23
  export interface TypedMockFacts {
18
24
  importAliases: Readonly<Record<string, string>>;
19
25
  fakes: readonly SharedFake[];
20
26
  typedHelpers: readonly TypedMockHelper[];
21
27
  forbiddenOverrides: readonly ForbiddenOverride[];
28
+ builtOverrides: readonly BuiltOverride[];
22
29
  }
23
30
  export declare const typedMockGuard: GenericGuardDefinition<TypedMockFacts>;
24
31
  //# sourceMappingURL=typed-mocks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"typed-mocks.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAwBzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAClD;AAuTD,eAAO,MAAM,cAAc,EAAE,sBAAsB,CAAC,cAAc,CAqCjE,CAAC"}
1
+ {"version":3,"file":"typed-mocks.d.ts","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAwBzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;CAC1C;AAmcD,eAAO,MAAM,cAAc,EAAE,sBAAsB,CAAC,cAAc,CAoDjE,CAAC"}
@@ -241,6 +241,118 @@ function checkForbidden(reader, facts, files) {
241
241
  }
242
242
  return findings;
243
243
  }
244
+ function fileBindings(file, resolve) {
245
+ const bindings = new Map();
246
+ for (const site of file.imports) {
247
+ if (site.kind !== 'import' || !site.topLevel || site.typeOnly)
248
+ continue;
249
+ const path = resolve(site.spec);
250
+ if (site.namespace)
251
+ bindings.set(site.namespace, { path, name: null });
252
+ for (const named of site.named)
253
+ if (!named.typeOnly)
254
+ bindings.set(named.local, { path, name: named.imported });
255
+ }
256
+ return bindings;
257
+ }
258
+ const overrideFunction = (node) => ts.isPropertyAssignment(node) ? factoryFunction(node.initializer) : null;
259
+ function holderValues(file, holder) {
260
+ const values = [];
261
+ const visit = (node) => {
262
+ if (ts.isBinaryExpression(node) &&
263
+ node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
264
+ unwrap(node.left).getText(file.sourceFile) === holder)
265
+ values.push(node.right);
266
+ else if (ts.isVariableDeclaration(node) &&
267
+ ts.isIdentifier(node.name) &&
268
+ node.name.text === holder &&
269
+ node.initializer)
270
+ values.push(node.initializer);
271
+ ts.forEachChild(node, visit);
272
+ };
273
+ visit(file.sourceFile);
274
+ return values;
275
+ }
276
+ function checkBuilt(reader, facts, files) {
277
+ const findings = [];
278
+ const aliases = aliasList(facts.importAliases);
279
+ const byModule = new Map(facts.builtOverrides.map((entry) => [entry.module, entry]));
280
+ for (const entry of facts.builtOverrides) {
281
+ const missing = missingOwner(reader, entry.builders.owner);
282
+ if (missing)
283
+ findings.push({ file: entry.builders.owner, detail: missing });
284
+ }
285
+ for (const file of files) {
286
+ const resolve = (spec) => resolveSpecifier(reader, file.path, spec, aliases);
287
+ const atFile = fileBindings(file, resolve);
288
+ for (const site of sitesOf(reader, file, aliases)) {
289
+ const entry = byModule.get(site.module);
290
+ if (!entry || !site.factory || isRunnerOptions(site.factory))
291
+ continue;
292
+ const fn = factoryFunction(site.factory);
293
+ if (!fn)
294
+ continue;
295
+ const bindings = new Map([...atFile, ...bindingsOf(fn, resolve)]);
296
+ const listed = new Set(entry.names);
297
+ const builders = new Set(entry.builders.names);
298
+ const builderList = entry.builders.names.join(' / ');
299
+ const builds = (value) => {
300
+ const call = awaited(value);
301
+ if (!ts.isCallExpression(call))
302
+ return false;
303
+ const target = calledExport(call, bindings, resolve);
304
+ return target?.path === entry.builders.owner && builders.has(target.name);
305
+ };
306
+ const report = (node, why) => findings.push({
307
+ file: file.path,
308
+ line: lineOfNode(file, node),
309
+ detail: `overrides ${site.module}'s ${why} — ${entry.why}`,
310
+ });
311
+ for (const returned of returnedExpressions(fn)) {
312
+ if (!returned)
313
+ continue;
314
+ for (const read of overridesOf(returned, parameterName(fn), new Map())) {
315
+ if ('unread' in read) {
316
+ findings.push({
317
+ file: file.path,
318
+ line: lineOfNode(file, read.unread),
319
+ detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must return a listed builder: ${entry.why}`,
320
+ });
321
+ continue;
322
+ }
323
+ if (!listed.has(read.name))
324
+ continue;
325
+ const override = overrideFunction(read.node);
326
+ if (!override) {
327
+ report(read.node, `${read.name} with something other than a function written there`);
328
+ continue;
329
+ }
330
+ for (const value of returnedExpressions(override)) {
331
+ if (!value) {
332
+ report(override, `${read.name} with a function that returns nothing`);
333
+ continue;
334
+ }
335
+ if (builds(value))
336
+ continue;
337
+ const holder = unwrap(value);
338
+ if (!ts.isIdentifier(holder) && !ts.isPropertyAccessExpression(holder)) {
339
+ report(value, `${read.name} with a value that is no ${builderList} call`);
340
+ continue;
341
+ }
342
+ const name = holder.getText(file.sourceFile);
343
+ const assigned = holderValues(file, name);
344
+ if (!assigned.length)
345
+ report(value, `${read.name} with ${name}, which nothing in the file sets`);
346
+ for (const set of assigned)
347
+ if (!builds(set))
348
+ report(set, `${read.name} through ${name}, set to a value that is no ${builderList} call`);
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ return findings;
355
+ }
244
356
  function factProblems(facts) {
245
357
  const problems = [];
246
358
  if (!objectProblem(facts, 'the facts', problems))
@@ -294,6 +406,26 @@ function factProblems(facts) {
294
406
  }
295
407
  }
296
408
  }
409
+ if (!Array.isArray(facts.builtOverrides))
410
+ problems.push('builtOverrides is missing (declare [] when there is none)');
411
+ else {
412
+ const seen = new Set();
413
+ for (const raw of facts.builtOverrides) {
414
+ const entry = (raw && typeof raw === 'object' ? raw : {});
415
+ if (!isText(entry.module)) {
416
+ problems.push('an entry of builtOverrides names no module');
417
+ continue;
418
+ }
419
+ if (seen.has(entry.module))
420
+ problems.push(`builtOverrides lists ${entry.module} twice`);
421
+ seen.add(entry.module);
422
+ namesProblem(entry.names, `builtOverrides entry ${entry.module}'s names`, problems);
423
+ textProblem(entry.why, `builtOverrides entry ${entry.module}'s why`, problems);
424
+ const builders = (entry.builders && typeof entry.builders === 'object' ? entry.builders : {});
425
+ pathProblem(builders.owner, `builtOverrides entry ${entry.module}'s builders owner`, problems);
426
+ namesProblem(builders.names, `builtOverrides entry ${entry.module}'s builders`, problems);
427
+ }
428
+ }
297
429
  return problems;
298
430
  }
299
431
  export const typedMockGuard = {
@@ -329,6 +461,20 @@ export const typedMockGuard = {
329
461
  return checkForbidden(context.reader, facts, context.files(SCOPE));
330
462
  },
331
463
  },
464
+ {
465
+ title: 'an override of a listed export returns one of its listed builders',
466
+ description: 'A listed export is replaced in a test only by a function written at the override whose every return ' +
467
+ 'is a call to one of its listed builders, or a holder — a name or a property — that every assignment in ' +
468
+ 'the file sets from one of them. A hand-written stand-in can claim a combination the real export never ' +
469
+ 'returns, so a case passes on a state the product never meets. The guard reads the names each override ' +
470
+ 'object sets at its top level; a spread of anything but the real module, a computed name, or an override ' +
471
+ 'object it cannot see fails, since it could hide a listed one. Each entry names its module, the exports, ' +
472
+ 'the builders’ owner and names, and why only the builders may stand in.',
473
+ timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,
474
+ run(context) {
475
+ return checkBuilt(context.reader, facts, context.files(SCOPE));
476
+ },
477
+ },
332
478
  ];
333
479
  },
334
480
  };
@@ -1 +1 @@
1
- {"version":3,"file":"typed-mocks.js","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport { join } from 'node:path';\nimport type { Finding } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode, unwrap } from '../../support/ast.js';\nimport {\n awaited,\n bindingsOf,\n calledExport,\n factoryFunction,\n isRunnerOptions,\n parameterName,\n returnedExpressions,\n type Binding,\n type Factory,\n} from '../../support/mock-factories.js';\nimport {\n importAliasesProblem,\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n textProblem,\n} from '../../support/facts.js';\nimport { aliasList, isLocalModule, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\nimport type { SharedFake } from './shared-fakes.js';\n\nconst MOCK_CALLS: ReadonlySet<string> = new Set(['vi.mock', 'vi.doMock']);\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nexport interface TypedMockHelper {\n owner: string;\n names: readonly string[];\n}\n\nexport interface FakeCall {\n name: string;\n overridesArgument: number;\n}\n\nexport interface ForbiddenOverride {\n module: string;\n names: readonly string[];\n why: string;\n fakeCalls?: readonly FakeCall[];\n}\n\nexport interface TypedMockFacts {\n importAliases: Readonly<Record<string, string>>;\n fakes: readonly SharedFake[];\n typedHelpers: readonly TypedMockHelper[];\n forbiddenOverrides: readonly ForbiddenOverride[];\n}\n\ninterface Site {\n node: ts.CallExpression;\n module: string;\n byImport: boolean;\n factory: ts.Expression | undefined;\n}\n\nfunction sitesOf(reader: RepoReader, file: FileView, aliases: Array<[string, string]>): Site[] {\n const out: Site[] = [];\n for (const site of file.imports) {\n if (site.kind !== 'mock' || !MOCK_CALLS.has(site.callee ?? '')) continue;\n if (!ts.isCallExpression(site.node)) continue;\n out.push({\n node: site.node,\n module: resolveSpecifier(reader, file.path, site.spec, aliases),\n byImport: site.byImport,\n factory: site.node.arguments[1],\n });\n }\n return out;\n}\n\ntype OwnerKind = 'fake' | 'helper';\ntype Owners = ReadonlyMap<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n>;\n\nfunction ownerKind(owners: Owners, path: string, name: string): OwnerKind | null {\n const owner = owners.get(path);\n if (!owner) return null;\n if (owner.kind === 'fake') return 'fake';\n return owner.names.has(name) ? 'helper' : null;\n}\n\nfunction calleeKind(\n call: ts.CallExpression,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): OwnerKind | null {\n const target = calledExport(call, bindings, resolve);\n return target ? ownerKind(owners, target.path, target.name) : null;\n}\n\nfunction untypedReason(\n expression: ts.Expression | null,\n fn: Factory,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): string | null {\n const call = expression ? awaited(expression) : null;\n if (!call || !ts.isCallExpression(call))\n return 'returns something other than a shared fake or a typed mock helper';\n const kind = calleeKind(call, owners, bindings, resolve);\n if (!kind) return 'returns through a function that is no listed shared fake or typed mock helper';\n if (kind === 'fake') return null;\n if (call.typeArguments?.length)\n return 'writes the type of its typed mock helper instead of taking it from importOriginal';\n const original = parameterName(fn);\n const [first] = call.arguments;\n const passed = first ? unwrap(first) : null;\n if (!original || !passed || !ts.isIdentifier(passed) || passed.text !== original)\n return \"hands its typed mock helper something other than the factory's own importOriginal\";\n return null;\n}\n\nfunction ownersOf(facts: TypedMockFacts): Owners {\n const owners = new Map<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n >();\n for (const fake of facts.fakes) owners.set(fake.owner, { kind: 'fake' });\n for (const helper of facts.typedHelpers) {\n const already = owners.get(helper.owner);\n if (already?.kind === 'fake') continue;\n owners.set(helper.owner, {\n kind: 'helper',\n names: new Set([...(already?.names ?? []), ...helper.names]),\n });\n }\n return owners;\n}\n\nfunction ownerFiles(facts: TypedMockFacts): Set<string> {\n return new Set([\n ...facts.fakes.map((fake) => fake.owner),\n ...facts.typedHelpers.map((h) => h.owner),\n ]);\n}\n\n// A package-owned helper is named by its package entry (`@scope/pkg/testing`), not a repo file.\nfunction missingOwner(reader: RepoReader, owner: string): string | null {\n if (isLocalModule(owner) || owner.startsWith('.'))\n return reader.exists(owner) ? null : 'this typed fake or helper file no longer exists';\n const segments = owner.split('/');\n const name = segments.slice(0, owner.startsWith('@') ? 2 : 1).join('/');\n const subpath = segments.slice(name.split('/').length).join('/');\n const manifest = reader.readOutside(\n join(reader.layout.repoRoot, 'node_modules', ...name.split('/'), 'package.json'),\n );\n if (manifest === null) return `no installed package ${name} holds this typed helper entry`;\n const exports: unknown = (JSON.parse(manifest) as { exports?: unknown }).exports;\n const key = subpath ? `./${subpath}` : '.';\n return exports && typeof exports === 'object' && key in exports\n ? null\n : `${name} exports no ${key} entry for this typed helper`;\n}\n\nfunction checkTyped(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const owners = ownersOf(facts);\n const skip = ownerFiles(facts);\n for (const owner of skip) {\n const missing = missingOwner(reader, owner);\n if (missing) findings.push({ file: owner, detail: missing });\n }\n for (const file of files) {\n if (skip.has(file.path)) continue;\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n for (const site of sitesOf(reader, file, aliases)) {\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `mocks ${site.module} with a factory that ${why}`,\n });\n if (!site.byImport)\n findings.push({\n file: file.path,\n line: lineOfNode(file, site.node),\n detail:\n `mocks ${site.module} by a string path — name it through import('…'), so the factory's ` +\n \"importOriginal carries the module's own type\",\n });\n if (!site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) {\n report(site.node, 'the guard cannot read, since it is not written at the mock');\n continue;\n }\n const returns = returnedExpressions(fn);\n if (!returns.length) report(site.node, 'returns nothing');\n const bindings = bindingsOf(fn, resolve);\n for (const returned of returns) {\n const why = untypedReason(returned, fn, owners, bindings, resolve);\n if (why) report(returned ?? site.node, why);\n }\n }\n }\n return findings;\n}\n\nconst isOriginalSpread = (expression: ts.Expression, original: string | null): boolean => {\n const value = awaited(expression);\n if (!ts.isCallExpression(value)) return false;\n const callee = unwrap(value.expression);\n return (\n (ts.isIdentifier(callee) && callee.text === original) ||\n (ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n callee.expression.text === 'vi' &&\n callee.name.text === 'importActual')\n );\n};\n\ntype OverrideRead = { name: string; node: ts.Node } | { unread: ts.Node };\n\nfunction calleeName(call: ts.CallExpression): string | null {\n const callee = unwrap(call.expression);\n if (ts.isIdentifier(callee)) return callee.text;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n return null;\n}\n\nfunction overridesOf(\n returned: ts.Expression,\n original: string | null,\n fakeCalls: ReadonlyMap<string, number>,\n): OverrideRead[] {\n const value = awaited(returned);\n const objects: ts.ObjectLiteralExpression[] = [];\n const out: OverrideRead[] = [];\n const name = ts.isCallExpression(value) ? calleeName(value) : null;\n const overridesAt = name === null ? undefined : fakeCalls.get(name);\n if (ts.isObjectLiteralExpression(value)) objects.push(value);\n else if (ts.isCallExpression(value) && overridesAt !== undefined) {\n const argument = value.arguments[overridesAt];\n if (argument) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else out.push({ unread: arg });\n }\n } else if (ts.isCallExpression(value))\n for (const argument of value.arguments) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else if (!(ts.isIdentifier(arg) && arg.text === original)) out.push({ unread: arg });\n }\n for (const object of objects)\n for (const property of object.properties) {\n if (ts.isSpreadAssignment(property)) {\n if (!isOriginalSpread(property.expression, original)) out.push({ unread: property });\n continue;\n }\n const name = property.name;\n if (name && (ts.isIdentifier(name) || ts.isStringLiteralLike(name)))\n out.push({ name: name.text, node: property });\n else out.push({ unread: property });\n }\n return out;\n}\n\nfunction checkForbidden(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.forbiddenOverrides.map((entry) => [entry.module, entry]));\n for (const file of files) {\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const forbidden = new Set(entry.names);\n const fakeCalls = new Map(\n (entry.fakeCalls ?? []).map((call) => [call.name, call.overridesArgument]),\n );\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn), fakeCalls)) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must stay real: ${entry.why}`,\n });\n continue;\n }\n if (!forbidden.has(read.name)) continue;\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.node),\n detail: `overrides ${site.module}'s ${read.name} — the real one runs in every test: ${entry.why}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\nfunction factProblems(facts: TypedMockFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n importAliasesProblem(facts.importAliases, 'importAliases', problems);\n if (!Array.isArray(facts.fakes))\n problems.push('fakes is missing (declare [] when there is none)');\n else\n for (const fake of facts.fakes as unknown[]) {\n const entry = (fake && typeof fake === 'object' ? fake : {}) as Record<string, unknown>;\n textProblem(entry.module, 'an entry of fakes’ module', problems);\n pathProblem(entry.owner, `fakes entry ${String(entry.module)}'s owner`, problems);\n }\n if (!Array.isArray(facts.typedHelpers))\n problems.push('typedHelpers is missing (declare [] when there is none)');\n else\n for (const helper of facts.typedHelpers as unknown[]) {\n const entry = (helper && typeof helper === 'object' ? helper : {}) as Record<string, unknown>;\n pathProblem(entry.owner, \"a typedHelpers entry's owner\", problems);\n namesProblem(entry.names, `typedHelpers entry ${String(entry.owner)}'s names`, problems);\n }\n if (!Array.isArray(facts.forbiddenOverrides))\n problems.push('forbiddenOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.forbiddenOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of forbiddenOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`forbiddenOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);\n if (entry.fakeCalls === undefined) continue;\n if (!Array.isArray(entry.fakeCalls)) {\n problems.push(`forbiddenOverrides entry ${entry.module}'s fakeCalls is not a list`);\n continue;\n }\n for (const call of entry.fakeCalls as unknown[]) {\n const fake = (call && typeof call === 'object' ? call : {}) as Record<string, unknown>;\n if (!isText(fake.name)) problems.push(`a fakeCalls entry of ${entry.module} names no fake`);\n if (\n typeof fake.overridesArgument !== 'number' ||\n !Number.isInteger(fake.overridesArgument) ||\n fake.overridesArgument < 1\n )\n problems.push(\n `fakeCalls entry ${String(fake.name)} of ${entry.module} needs overridesArgument, the position of its override object after importOriginal`,\n );\n }\n }\n }\n return problems;\n}\n\nexport const typedMockGuard: GenericGuardDefinition<TypedMockFacts> = {\n id: 'typedMocks',\n absentWhen: false,\n factProblems,\n build(facts) {\n return [\n {\n title: 'every module mock is typed against the module it replaces',\n description:\n \"A test's module mock names its module through `import('…')` and returns, on every path, through the \" +\n \"module's shared fake or a typed mock helper. A helper takes its module type from the factory's own \" +\n '`importOriginal`, never from a type argument the test writes, so a wrong module, a misspelled export, ' +\n 'and a wrong signature fail to compile. A factory that returns a hand-written object is never compared to ' +\n 'the module, so a double that no longer matches it still passes. A mock with no factory, or with the ' +\n 'runner’s options object (`{ spy: true }`) in its place, is the runner’s own and passes.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkTyped(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'no test overrides an export whose real behavior every test needs',\n description:\n 'A listed export is never replaced in a test, by its shared fake’s overrides or by a factory of its own: ' +\n 'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +\n 'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +\n 'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +\n 'listed one. Each entry names its module, the exports, and why the real one runs. An entry may name ' +\n 'its shared fake’s calls with the position of their override object: for a call to that fake only that ' +\n 'argument is read, and the arguments before it are the fake’s state.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkForbidden(context.reader, facts, context.files(SCOPE));\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"typed-mocks.js","sourceRoot":"","sources":["../../../src/modules/repo/typed-mocks.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport { join } from 'node:path';\nimport type { Finding } from '../../guard.js';\nimport type { RepoReader, Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode, unwrap } from '../../support/ast.js';\nimport {\n awaited,\n bindingsOf,\n calledExport,\n factoryFunction,\n isRunnerOptions,\n parameterName,\n returnedExpressions,\n type Binding,\n type Factory,\n} from '../../support/mock-factories.js';\nimport {\n importAliasesProblem,\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n textProblem,\n} from '../../support/facts.js';\nimport { aliasList, isLocalModule, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { REPOSITORY_GUARD_TIMEOUT_MS } from '../../support/timeouts.js';\nimport type { SharedFake } from './shared-fakes.js';\n\nconst MOCK_CALLS: ReadonlySet<string> = new Set(['vi.mock', 'vi.doMock']);\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'scripts', 'workers'],\n kinds: ['code'],\n tests: 'only',\n optionalRoots: ['functions', 'scripts', 'workers'],\n};\n\nexport interface TypedMockHelper {\n owner: string;\n names: readonly string[];\n}\n\nexport interface FakeCall {\n name: string;\n overridesArgument: number;\n}\n\nexport interface ForbiddenOverride {\n module: string;\n names: readonly string[];\n why: string;\n fakeCalls?: readonly FakeCall[];\n}\n\nexport interface BuiltOverride {\n module: string;\n names: readonly string[];\n builders: TypedMockHelper;\n why: string;\n}\n\nexport interface TypedMockFacts {\n importAliases: Readonly<Record<string, string>>;\n fakes: readonly SharedFake[];\n typedHelpers: readonly TypedMockHelper[];\n forbiddenOverrides: readonly ForbiddenOverride[];\n builtOverrides: readonly BuiltOverride[];\n}\n\ninterface Site {\n node: ts.CallExpression;\n module: string;\n byImport: boolean;\n factory: ts.Expression | undefined;\n}\n\nfunction sitesOf(reader: RepoReader, file: FileView, aliases: Array<[string, string]>): Site[] {\n const out: Site[] = [];\n for (const site of file.imports) {\n if (site.kind !== 'mock' || !MOCK_CALLS.has(site.callee ?? '')) continue;\n if (!ts.isCallExpression(site.node)) continue;\n out.push({\n node: site.node,\n module: resolveSpecifier(reader, file.path, site.spec, aliases),\n byImport: site.byImport,\n factory: site.node.arguments[1],\n });\n }\n return out;\n}\n\ntype OwnerKind = 'fake' | 'helper';\ntype Owners = ReadonlyMap<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n>;\n\nfunction ownerKind(owners: Owners, path: string, name: string): OwnerKind | null {\n const owner = owners.get(path);\n if (!owner) return null;\n if (owner.kind === 'fake') return 'fake';\n return owner.names.has(name) ? 'helper' : null;\n}\n\nfunction calleeKind(\n call: ts.CallExpression,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): OwnerKind | null {\n const target = calledExport(call, bindings, resolve);\n return target ? ownerKind(owners, target.path, target.name) : null;\n}\n\nfunction untypedReason(\n expression: ts.Expression | null,\n fn: Factory,\n owners: Owners,\n bindings: Map<string, Binding>,\n resolve: (spec: string) => string,\n): string | null {\n const call = expression ? awaited(expression) : null;\n if (!call || !ts.isCallExpression(call))\n return 'returns something other than a shared fake or a typed mock helper';\n const kind = calleeKind(call, owners, bindings, resolve);\n if (!kind) return 'returns through a function that is no listed shared fake or typed mock helper';\n if (kind === 'fake') return null;\n if (call.typeArguments?.length)\n return 'writes the type of its typed mock helper instead of taking it from importOriginal';\n const original = parameterName(fn);\n const [first] = call.arguments;\n const passed = first ? unwrap(first) : null;\n if (!original || !passed || !ts.isIdentifier(passed) || passed.text !== original)\n return \"hands its typed mock helper something other than the factory's own importOriginal\";\n return null;\n}\n\nfunction ownersOf(facts: TypedMockFacts): Owners {\n const owners = new Map<\n string,\n { kind: 'fake' } | { kind: 'helper'; names: ReadonlySet<string> }\n >();\n for (const fake of facts.fakes) owners.set(fake.owner, { kind: 'fake' });\n for (const helper of facts.typedHelpers) {\n const already = owners.get(helper.owner);\n if (already?.kind === 'fake') continue;\n owners.set(helper.owner, {\n kind: 'helper',\n names: new Set([...(already?.names ?? []), ...helper.names]),\n });\n }\n return owners;\n}\n\nfunction ownerFiles(facts: TypedMockFacts): Set<string> {\n return new Set([\n ...facts.fakes.map((fake) => fake.owner),\n ...facts.typedHelpers.map((h) => h.owner),\n ]);\n}\n\n// A package-owned helper is named by its package entry (`@scope/pkg/testing`), not a repo file.\nfunction missingOwner(reader: RepoReader, owner: string): string | null {\n if (isLocalModule(owner) || owner.startsWith('.'))\n return reader.exists(owner) ? null : 'this typed fake or helper file no longer exists';\n const segments = owner.split('/');\n const name = segments.slice(0, owner.startsWith('@') ? 2 : 1).join('/');\n const subpath = segments.slice(name.split('/').length).join('/');\n const manifest = reader.readOutside(\n join(reader.layout.repoRoot, 'node_modules', ...name.split('/'), 'package.json'),\n );\n if (manifest === null) return `no installed package ${name} holds this typed helper entry`;\n const exports: unknown = (JSON.parse(manifest) as { exports?: unknown }).exports;\n const key = subpath ? `./${subpath}` : '.';\n return exports && typeof exports === 'object' && key in exports\n ? null\n : `${name} exports no ${key} entry for this typed helper`;\n}\n\nfunction checkTyped(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const owners = ownersOf(facts);\n const skip = ownerFiles(facts);\n for (const owner of skip) {\n const missing = missingOwner(reader, owner);\n if (missing) findings.push({ file: owner, detail: missing });\n }\n for (const file of files) {\n if (skip.has(file.path)) continue;\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n for (const site of sitesOf(reader, file, aliases)) {\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `mocks ${site.module} with a factory that ${why}`,\n });\n if (!site.byImport)\n findings.push({\n file: file.path,\n line: lineOfNode(file, site.node),\n detail:\n `mocks ${site.module} by a string path — name it through import('…'), so the factory's ` +\n \"importOriginal carries the module's own type\",\n });\n if (!site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) {\n report(site.node, 'the guard cannot read, since it is not written at the mock');\n continue;\n }\n const returns = returnedExpressions(fn);\n if (!returns.length) report(site.node, 'returns nothing');\n const bindings = bindingsOf(fn, resolve);\n for (const returned of returns) {\n const why = untypedReason(returned, fn, owners, bindings, resolve);\n if (why) report(returned ?? site.node, why);\n }\n }\n }\n return findings;\n}\n\nconst isOriginalSpread = (expression: ts.Expression, original: string | null): boolean => {\n const value = awaited(expression);\n if (!ts.isCallExpression(value)) return false;\n const callee = unwrap(value.expression);\n return (\n (ts.isIdentifier(callee) && callee.text === original) ||\n (ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n callee.expression.text === 'vi' &&\n callee.name.text === 'importActual')\n );\n};\n\ntype OverrideRead = { name: string; node: ts.Node } | { unread: ts.Node };\n\nfunction calleeName(call: ts.CallExpression): string | null {\n const callee = unwrap(call.expression);\n if (ts.isIdentifier(callee)) return callee.text;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n return null;\n}\n\nfunction overridesOf(\n returned: ts.Expression,\n original: string | null,\n fakeCalls: ReadonlyMap<string, number>,\n): OverrideRead[] {\n const value = awaited(returned);\n const objects: ts.ObjectLiteralExpression[] = [];\n const out: OverrideRead[] = [];\n const name = ts.isCallExpression(value) ? calleeName(value) : null;\n const overridesAt = name === null ? undefined : fakeCalls.get(name);\n if (ts.isObjectLiteralExpression(value)) objects.push(value);\n else if (ts.isCallExpression(value) && overridesAt !== undefined) {\n const argument = value.arguments[overridesAt];\n if (argument) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else out.push({ unread: arg });\n }\n } else if (ts.isCallExpression(value))\n for (const argument of value.arguments) {\n const arg = unwrap(argument);\n if (ts.isObjectLiteralExpression(arg)) objects.push(arg);\n else if (!(ts.isIdentifier(arg) && arg.text === original)) out.push({ unread: arg });\n }\n for (const object of objects)\n for (const property of object.properties) {\n if (ts.isSpreadAssignment(property)) {\n if (!isOriginalSpread(property.expression, original)) out.push({ unread: property });\n continue;\n }\n const name = property.name;\n if (name && (ts.isIdentifier(name) || ts.isStringLiteralLike(name)))\n out.push({ name: name.text, node: property });\n else out.push({ unread: property });\n }\n return out;\n}\n\nfunction checkForbidden(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.forbiddenOverrides.map((entry) => [entry.module, entry]));\n for (const file of files) {\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const forbidden = new Set(entry.names);\n const fakeCalls = new Map(\n (entry.fakeCalls ?? []).map((call) => [call.name, call.overridesArgument]),\n );\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn), fakeCalls)) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must stay real: ${entry.why}`,\n });\n continue;\n }\n if (!forbidden.has(read.name)) continue;\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.node),\n detail: `overrides ${site.module}'s ${read.name} — the real one runs in every test: ${entry.why}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\nfunction fileBindings(file: FileView, resolve: (spec: string) => string): Map<string, Binding> {\n const bindings = new Map<string, Binding>();\n for (const site of file.imports) {\n if (site.kind !== 'import' || !site.topLevel || site.typeOnly) continue;\n const path = resolve(site.spec);\n if (site.namespace) bindings.set(site.namespace, { path, name: null });\n for (const named of site.named)\n if (!named.typeOnly) bindings.set(named.local, { path, name: named.imported });\n }\n return bindings;\n}\n\nconst overrideFunction = (node: ts.Node): Factory | null =>\n ts.isPropertyAssignment(node) ? factoryFunction(node.initializer) : null;\n\nfunction holderValues(file: FileView, holder: string): ts.Expression[] {\n const values: ts.Expression[] = [];\n const visit = (node: ts.Node): void => {\n if (\n ts.isBinaryExpression(node) &&\n node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&\n unwrap(node.left).getText(file.sourceFile) === holder\n )\n values.push(node.right);\n else if (\n ts.isVariableDeclaration(node) &&\n ts.isIdentifier(node.name) &&\n node.name.text === holder &&\n node.initializer\n )\n values.push(node.initializer);\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return values;\n}\n\nfunction checkBuilt(reader: RepoReader, facts: TypedMockFacts, files: FileView[]): Finding[] {\n const findings: Finding[] = [];\n const aliases = aliasList(facts.importAliases);\n const byModule = new Map(facts.builtOverrides.map((entry) => [entry.module, entry]));\n for (const entry of facts.builtOverrides) {\n const missing = missingOwner(reader, entry.builders.owner);\n if (missing) findings.push({ file: entry.builders.owner, detail: missing });\n }\n for (const file of files) {\n const resolve = (spec: string) => resolveSpecifier(reader, file.path, spec, aliases);\n const atFile = fileBindings(file, resolve);\n for (const site of sitesOf(reader, file, aliases)) {\n const entry = byModule.get(site.module);\n if (!entry || !site.factory || isRunnerOptions(site.factory)) continue;\n const fn = factoryFunction(site.factory);\n if (!fn) continue;\n const bindings = new Map([...atFile, ...bindingsOf(fn, resolve)]);\n const listed = new Set(entry.names);\n const builders = new Set(entry.builders.names);\n const builderList = entry.builders.names.join(' / ');\n const builds = (value: ts.Expression): boolean => {\n const call = awaited(value);\n if (!ts.isCallExpression(call)) return false;\n const target = calledExport(call, bindings, resolve);\n return target?.path === entry.builders.owner && builders.has(target.name);\n };\n const report = (node: ts.Node, why: string) =>\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `overrides ${site.module}'s ${why} — ${entry.why}`,\n });\n for (const returned of returnedExpressions(fn)) {\n if (!returned) continue;\n for (const read of overridesOf(returned, parameterName(fn), new Map())) {\n if ('unread' in read) {\n findings.push({\n file: file.path,\n line: lineOfNode(file, read.unread),\n detail: `overrides ${site.module} through names the guard cannot read — write each override by name, since ${entry.names.join(', ')} must return a listed builder: ${entry.why}`,\n });\n continue;\n }\n if (!listed.has(read.name)) continue;\n const override = overrideFunction(read.node);\n if (!override) {\n report(read.node, `${read.name} with something other than a function written there`);\n continue;\n }\n for (const value of returnedExpressions(override)) {\n if (!value) {\n report(override, `${read.name} with a function that returns nothing`);\n continue;\n }\n if (builds(value)) continue;\n const holder = unwrap(value);\n if (!ts.isIdentifier(holder) && !ts.isPropertyAccessExpression(holder)) {\n report(value, `${read.name} with a value that is no ${builderList} call`);\n continue;\n }\n const name = holder.getText(file.sourceFile);\n const assigned = holderValues(file, name);\n if (!assigned.length)\n report(value, `${read.name} with ${name}, which nothing in the file sets`);\n for (const set of assigned)\n if (!builds(set))\n report(\n set,\n `${read.name} through ${name}, set to a value that is no ${builderList} call`,\n );\n }\n }\n }\n }\n }\n return findings;\n}\n\nfunction factProblems(facts: TypedMockFacts): string[] {\n const problems: string[] = [];\n if (!objectProblem(facts, 'the facts', problems)) return problems;\n importAliasesProblem(facts.importAliases, 'importAliases', problems);\n if (!Array.isArray(facts.fakes))\n problems.push('fakes is missing (declare [] when there is none)');\n else\n for (const fake of facts.fakes as unknown[]) {\n const entry = (fake && typeof fake === 'object' ? fake : {}) as Record<string, unknown>;\n textProblem(entry.module, 'an entry of fakes’ module', problems);\n pathProblem(entry.owner, `fakes entry ${String(entry.module)}'s owner`, problems);\n }\n if (!Array.isArray(facts.typedHelpers))\n problems.push('typedHelpers is missing (declare [] when there is none)');\n else\n for (const helper of facts.typedHelpers as unknown[]) {\n const entry = (helper && typeof helper === 'object' ? helper : {}) as Record<string, unknown>;\n pathProblem(entry.owner, \"a typedHelpers entry's owner\", problems);\n namesProblem(entry.names, `typedHelpers entry ${String(entry.owner)}'s names`, problems);\n }\n if (!Array.isArray(facts.forbiddenOverrides))\n problems.push('forbiddenOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.forbiddenOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of forbiddenOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`forbiddenOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `forbiddenOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `forbiddenOverrides entry ${entry.module}'s why`, problems);\n if (entry.fakeCalls === undefined) continue;\n if (!Array.isArray(entry.fakeCalls)) {\n problems.push(`forbiddenOverrides entry ${entry.module}'s fakeCalls is not a list`);\n continue;\n }\n for (const call of entry.fakeCalls as unknown[]) {\n const fake = (call && typeof call === 'object' ? call : {}) as Record<string, unknown>;\n if (!isText(fake.name)) problems.push(`a fakeCalls entry of ${entry.module} names no fake`);\n if (\n typeof fake.overridesArgument !== 'number' ||\n !Number.isInteger(fake.overridesArgument) ||\n fake.overridesArgument < 1\n )\n problems.push(\n `fakeCalls entry ${String(fake.name)} of ${entry.module} needs overridesArgument, the position of its override object after importOriginal`,\n );\n }\n }\n }\n if (!Array.isArray(facts.builtOverrides))\n problems.push('builtOverrides is missing (declare [] when there is none)');\n else {\n const seen = new Set<string>();\n for (const raw of facts.builtOverrides as unknown[]) {\n const entry = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;\n if (!isText(entry.module)) {\n problems.push('an entry of builtOverrides names no module');\n continue;\n }\n if (seen.has(entry.module)) problems.push(`builtOverrides lists ${entry.module} twice`);\n seen.add(entry.module);\n namesProblem(entry.names, `builtOverrides entry ${entry.module}'s names`, problems);\n textProblem(entry.why, `builtOverrides entry ${entry.module}'s why`, problems);\n const builders = (\n entry.builders && typeof entry.builders === 'object' ? entry.builders : {}\n ) as Record<string, unknown>;\n pathProblem(\n builders.owner,\n `builtOverrides entry ${entry.module}'s builders owner`,\n problems,\n );\n namesProblem(builders.names, `builtOverrides entry ${entry.module}'s builders`, problems);\n }\n }\n return problems;\n}\n\nexport const typedMockGuard: GenericGuardDefinition<TypedMockFacts> = {\n id: 'typedMocks',\n absentWhen: false,\n factProblems,\n build(facts) {\n return [\n {\n title: 'every module mock is typed against the module it replaces',\n description:\n \"A test's module mock names its module through `import('…')` and returns, on every path, through the \" +\n \"module's shared fake or a typed mock helper. A helper takes its module type from the factory's own \" +\n '`importOriginal`, never from a type argument the test writes, so a wrong module, a misspelled export, ' +\n 'and a wrong signature fail to compile. A factory that returns a hand-written object is never compared to ' +\n 'the module, so a double that no longer matches it still passes. A mock with no factory, or with the ' +\n 'runner’s options object (`{ spy: true }`) in its place, is the runner’s own and passes.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkTyped(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'no test overrides an export whose real behavior every test needs',\n description:\n 'A listed export is never replaced in a test, by its shared fake’s overrides or by a factory of its own: ' +\n 'a stand-in that behaves differently on the path a case proves lets the case pass on behavior the app ' +\n 'never has. The guard reads the names each override object sets at its top level; a spread of anything ' +\n 'but the real module, a computed name, or an override object it cannot see fails, since it could hide a ' +\n 'listed one. Each entry names its module, the exports, and why the real one runs. An entry may name ' +\n 'its shared fake’s calls with the position of their override object: for a call to that fake only that ' +\n 'argument is read, and the arguments before it are the fake’s state.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkForbidden(context.reader, facts, context.files(SCOPE));\n },\n },\n {\n title: 'an override of a listed export returns one of its listed builders',\n description:\n 'A listed export is replaced in a test only by a function written at the override whose every return ' +\n 'is a call to one of its listed builders, or a holder — a name or a property — that every assignment in ' +\n 'the file sets from one of them. A hand-written stand-in can claim a combination the real export never ' +\n 'returns, so a case passes on a state the product never meets. The guard reads the names each override ' +\n 'object sets at its top level; a spread of anything but the real module, a computed name, or an override ' +\n 'object it cannot see fails, since it could hide a listed one. Each entry names its module, the exports, ' +\n 'the builders’ owner and names, and why only the builders may stand in.',\n timeoutMs: REPOSITORY_GUARD_TIMEOUT_MS,\n run(context) {\n return checkBuilt(context.reader, facts, context.files(SCOPE));\n },\n },\n ];\n },\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/repo-guards",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",