vitest-auto-spy 5.40.0 → 5.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -1
- package/agent-docs/angular.md +5 -1
- package/agent-docs/configuration.md +2 -1
- package/dist/angular-diagnostics.js +12 -12
- package/dist/angular-doubles.js +10 -10
- package/dist/angular-http.js +4 -4
- package/dist/angular-matchers.js +84 -10
- package/dist/angular-router.js +3 -3
- package/dist/angular.js +32 -25
- package/dist/bun-angular.js +23 -22
- package/dist/bun.js +6 -6
- package/dist/{chunk-YNXQ2WFH.js → chunk-6ZGH56G4.js} +1 -1
- package/dist/{chunk-5D5GTU4A.js → chunk-DG5EP3PO.js} +5 -5
- package/dist/{chunk-M3NTXLZG.js → chunk-DYAS4OK4.js} +4 -1
- package/dist/{chunk-RYFF55PV.js → chunk-HUS2MHK5.js} +1 -4
- package/dist/{chunk-W5EXSI22.js → chunk-HVNKTPKG.js} +1 -1
- package/dist/{chunk-B6C3K27C.js → chunk-IE4NI2OQ.js} +1 -1
- package/dist/{chunk-GKSVYPZQ.js → chunk-JFOJFCVQ.js} +1 -1
- package/dist/{chunk-RCUQDITG.js → chunk-MNY4ZHEQ.js} +1 -1
- package/dist/{chunk-UH3YJSRP.js → chunk-O55MVK7N.js} +7 -3
- package/dist/{chunk-3UUM6YM5.js → chunk-OWDUWUAO.js} +5 -4
- package/dist/{chunk-Z7RCF47R.js → chunk-T7EGA4G4.js} +12 -9
- package/dist/{chunk-DBC6526L.js → chunk-UH3KWLVL.js} +2 -2
- package/dist/{chunk-BS432KB2.js → chunk-XEIOGUGP.js} +2 -1
- package/dist/cli.js +1 -1
- package/dist/console.js +4 -4
- package/dist/index.js +13 -11
- package/dist/jasmine.js +5 -5
- package/dist/nestjs.js +7 -7
- package/dist/node.cjs +13 -11
- package/dist/node.js +13 -11
- package/dist/rstest.js +7 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,55 @@ The latest released version here must always match the one published on
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [5.41.0] - 2026-09-27
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **Every Angular-adapter message prints class names as the source spells them.** 5.40.1 took the
|
|
18
|
+
esbuild `_` prefix and the Rollup `$1` suffix off in `toHaveDirectiveApplied` only; the rest still
|
|
19
|
+
told the reader to edit `_ReportCardComponent`. The same cleanup now applies to `injectSpy`,
|
|
20
|
+
`overrideComponentProvider`, `assertComponentDefIntact`, `enableAngularDiagnostics`' shadowed-provider
|
|
21
|
+
report, `setInputs` / `renderShallow` input errors, `renderShallow({ keepTemplate: true })`,
|
|
22
|
+
`createComponentStub` (and the stub's own class name), `provideMatDialogRef` / `injectMatDialogRef`,
|
|
23
|
+
`hostElement` / `queryElement`, `trackInjections(...).get()` and the `createSpyFromClass(X)` label.
|
|
24
|
+
`trackInjections(...).names()` still returns `Function.name` unchanged.
|
|
25
|
+
- **`toHaveDirectiveApplied` blames the selector when the host already has the directive in scope.**
|
|
26
|
+
A `createDirectiveHost({ template, scope: [FlagDirective] })` whose template no longer matched the
|
|
27
|
+
directive's selector failed with "Build the host with createDirectiveHost({ template, scope:
|
|
28
|
+
[FlagDirective] })", the call the spec already made. When the host's compiled scope carries the
|
|
29
|
+
directive, the failure now says its selector matches nothing the template renders and prints it
|
|
30
|
+
(`'[appFlagX]'`; a `:not()` selector is left out). With a selector argument, a directive found only
|
|
31
|
+
on other elements is reported as `it is on 1 element that selector does not match`.
|
|
32
|
+
`/angular/matchers` is +421 B min+gzip (2241 → 2662 B, +18.8 %) and +2.4 kB to import
|
|
33
|
+
(19972 → 22349 B): the scope lookup, the selector printer and the two messages.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **The `signalStore()` `fillMissing` default recognises the ngrx base under a bundler's rename.** The
|
|
38
|
+
base was matched by `name === 'SignalStore'`, which a `_SignalStore` or `SignalStore$1` missed; the
|
|
39
|
+
name is now compared with the rename taken off. The `HttpTestingController` lookup behind
|
|
40
|
+
`enableAngularDiagnostics({ pendingRequests })` compares the same way.
|
|
41
|
+
|
|
42
|
+
## [5.40.1] - 2026-09-27
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **`toHaveDirectiveApplied` prints class names as the source spells them.** Under
|
|
47
|
+
`@angular/build:unit-test` esbuild emits a decorated class as `_TestIdDirective`, and the failure told
|
|
48
|
+
the reader to edit `_ReportRenameComponent`, a name no file contains. The leading `_` (and a Rollup
|
|
49
|
+
`$1` suffix) now comes off, the way strict-mode messages already took it off a spied class.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **`toHaveDirectiveApplied` finds a structural directive.** `*dir` and `<ng-template dir>` put the
|
|
54
|
+
directive on the comment Angular leaves in place of the template, and the search read elements only,
|
|
55
|
+
so a directive that rendered its view failed the matcher, whose hint then advised the
|
|
56
|
+
`createDirectiveHost({ template, scope })` the spec already used. The search now takes template
|
|
57
|
+
anchors in; `.not` reports them as `1 template anchor`, and a selector naming the rendered element
|
|
58
|
+
fails with a message that says to drop it. `/angular/matchers` is +226 B min+gzip (2015 → 2241 B,
|
|
59
|
+
+11.2 %) and +1.0 kB to import (18929 → 19972 B): the anchor search, its two messages and the
|
|
60
|
+
class-name cleanup.
|
|
61
|
+
|
|
13
62
|
## [5.40.0] - 2026-09-27
|
|
14
63
|
|
|
15
64
|
### Added
|
|
@@ -5541,7 +5590,9 @@ moved under 1 %: `/angular` +192 B (+1.0 %) for the override queue and the HTTP
|
|
|
5541
5590
|
This copy ships with the package and carries 5.x only. Releases before 5.0.0 are in the
|
|
5542
5591
|
[full changelog](https://github.com/ASDAlexey/vitest-auto-spy/blob/master/CHANGELOG.md).
|
|
5543
5592
|
|
|
5544
|
-
[Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.
|
|
5593
|
+
[Unreleased]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.41.0...HEAD
|
|
5594
|
+
[5.41.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.40.1...v5.41.0
|
|
5595
|
+
[5.40.1]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.40.0...v5.40.1
|
|
5545
5596
|
[5.40.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.39.0...v5.40.0
|
|
5546
5597
|
[5.39.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.38.0...v5.39.0
|
|
5547
5598
|
[5.38.0]: https://github.com/ASDAlexey/vitest-auto-spy/compare/v5.37.0...v5.38.0
|
package/agent-docs/angular.md
CHANGED
|
@@ -1207,7 +1207,11 @@ assertion on the attribute alone stays green when the entry is dropped and the a
|
|
|
1207
1207
|
written statically, or set by something else; the fixture's root element is searched, so the entry
|
|
1208
1208
|
itself can be asserted with no selector. On a fixture of the component under test the failure names
|
|
1209
1209
|
that component's `hostDirectives` / `imports` as the fix; only a spec-built host (`createDirectiveHost`,
|
|
1210
|
-
`TestBed.createDirective`) is pointed at `createDirectiveHost
|
|
1210
|
+
`TestBed.createDirective`) is pointed at `createDirectiveHost`. A directive already in the host's
|
|
1211
|
+
compiled scope gets neither hint: the failure prints its own selector, which matches nothing in the
|
|
1212
|
+
template (a mutated or mistyped selector). A structural directive (`*dir`,
|
|
1213
|
+
`<ng-template dir>`) counts too: it sits on the template's comment anchor, which the search includes;
|
|
1214
|
+
assert it without a selector, since the element it renders does not carry it:
|
|
1211
1215
|
|
|
1212
1216
|
```ts
|
|
1213
1217
|
import { registerDirectiveMatchers } from 'vitest-auto-spy/angular/matchers';
|
|
@@ -39,7 +39,8 @@ the reference suite). Method discovery walks the _prototype chain_; a callable a
|
|
|
39
39
|
|
|
40
40
|
- an Angular `signal()` / `computed()` field — the dominant case in a signals codebase
|
|
41
41
|
- an arrow-function property — `readonly reload = (): void => {}`
|
|
42
|
-
- anything on an ngrx `signalStore()`, which puts **everything** on the instance
|
|
42
|
+
- anything on an ngrx `signalStore()`, which puts **everything** on the instance — covered by the
|
|
43
|
+
`fillMissing` default below, not by a list
|
|
43
44
|
- **members Angular's own classes moved onto the instance** — `Router.currentNavigation` in
|
|
44
45
|
Angular 20 is `currentNavigation = this.navigationTransitions.currentNavigation.asReadonly()`
|
|
45
46
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { pendingRequestsReport, assertAngularInternals, instrumentTestBed, onTestingModuleConfigured, onComponentCreated, beforeTestBedReset, verifyOnTeardown } from './chunk-
|
|
2
|
-
export { disableTestBedDiagnostics, enableTestBedDiagnostics, formatSpecTiming, getTestBedTiming, instrumentTestBed, reportSpecTiming } from './chunk-
|
|
3
|
-
import { failOnUnspiedProvider } from './chunk-
|
|
1
|
+
import { pendingRequestsReport, assertAngularInternals, instrumentTestBed, onTestingModuleConfigured, onComponentCreated, beforeTestBedReset, verifyOnTeardown } from './chunk-JFOJFCVQ.js';
|
|
2
|
+
export { disableTestBedDiagnostics, enableTestBedDiagnostics, formatSpecTiming, getTestBedTiming, instrumentTestBed, reportSpecTiming } from './chunk-JFOJFCVQ.js';
|
|
3
|
+
import { failOnUnspiedProvider } from './chunk-OWDUWUAO.js';
|
|
4
4
|
import './chunk-GG7BQKEK.js';
|
|
5
|
-
import './chunk-
|
|
6
|
-
import './chunk-
|
|
5
|
+
import './chunk-MNY4ZHEQ.js';
|
|
6
|
+
import './chunk-T7EGA4G4.js';
|
|
7
7
|
import './chunk-FOL5RH22.js';
|
|
8
8
|
import './chunk-PC3FAZT3.js';
|
|
9
|
-
import './chunk-
|
|
9
|
+
import './chunk-HUS2MHK5.js';
|
|
10
10
|
import { isAutoSpyLike } from './chunk-Q7L4AWI7.js';
|
|
11
|
-
import { count, taskName } from './chunk-
|
|
11
|
+
import { count, sourceClassName, taskName } from './chunk-DYAS4OK4.js';
|
|
12
12
|
import { angularPendingRequests, angularShadowedProviders, angularDeadSchemas, angularNgModuleScopesAuto } from './chunk-5FQZGJLJ.js';
|
|
13
13
|
import { withDocs } from './chunk-ENCEEH23.js';
|
|
14
14
|
import { getTestBed } from '@angular/core/testing';
|
|
@@ -31,7 +31,7 @@ function componentInjector(fixture, component) {
|
|
|
31
31
|
}
|
|
32
32
|
function describeResolved(resolved) {
|
|
33
33
|
const constructorName = readProperty(readProperty(resolved, "constructor"), "name");
|
|
34
|
-
return typeof constructorName === "string" && constructorName.length > 0 ? `a ${constructorName} instance` : String(resolved);
|
|
34
|
+
return typeof constructorName === "string" && constructorName.length > 0 ? `a ${sourceClassName(constructorName)} instance` : String(resolved);
|
|
35
35
|
}
|
|
36
36
|
function readProperty(value, key) {
|
|
37
37
|
if (value === null || typeof value !== "object" && typeof value !== "function") {
|
|
@@ -59,7 +59,7 @@ function isDeadNgModuleImport(module) {
|
|
|
59
59
|
}
|
|
60
60
|
function moduleName(module) {
|
|
61
61
|
const name = readProperty(module, "name");
|
|
62
|
-
return typeof name === "string" ? name : String(module);
|
|
62
|
+
return typeof name === "string" ? sourceClassName(name) : String(module);
|
|
63
63
|
}
|
|
64
64
|
function listed(names) {
|
|
65
65
|
return names.length === 1 ? String(names[0]) : `${names.slice(0, -1).join(", ")} and ${String(names.at(-1))}`;
|
|
@@ -89,7 +89,7 @@ function isComponentClass(value) {
|
|
|
89
89
|
}
|
|
90
90
|
function className(value) {
|
|
91
91
|
const name = readProperty(value, "name");
|
|
92
|
-
return typeof name === "string" ? name : String(value);
|
|
92
|
+
return typeof name === "string" ? sourceClassName(name) : String(value);
|
|
93
93
|
}
|
|
94
94
|
function checkNgModuleScopes(config) {
|
|
95
95
|
failDeadNgModuleImports(readList(config, "imports").filter(isDeadNgModuleImport));
|
|
@@ -135,7 +135,7 @@ function flattenProviders(value, into) {
|
|
|
135
135
|
function findControllerToken(providers) {
|
|
136
136
|
const flat = [];
|
|
137
137
|
flattenProviders(providers, flat);
|
|
138
|
-
return flat.map((provider) => readProperty(provider, "provide")).find((token) => typeof token === "function" && token.name === HTTP_TESTING_CONTROLLER);
|
|
138
|
+
return flat.map((provider) => readProperty(provider, "provide")).find((token) => typeof token === "function" && sourceClassName(token.name) === HTTP_TESTING_CONTROLLER);
|
|
139
139
|
}
|
|
140
140
|
var controllerTokens = /* @__PURE__ */ new WeakMap();
|
|
141
141
|
function findControllerTokenInModule(imported, seen) {
|
|
@@ -221,7 +221,7 @@ function collectModuleDoubles(config) {
|
|
|
221
221
|
}
|
|
222
222
|
function tokenName(token) {
|
|
223
223
|
const name = readProperty(token, "name");
|
|
224
|
-
return typeof name === "string" && name.length > 0 ? name : String(token);
|
|
224
|
+
return typeof name === "string" && name.length > 0 ? sourceClassName(name) : String(token);
|
|
225
225
|
}
|
|
226
226
|
function codeName(token) {
|
|
227
227
|
const description = readProperty(token, "_desc");
|
package/dist/angular-doubles.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { describeInstance } from './chunk-
|
|
2
|
-
import { useVitestAdapter } from './chunk-
|
|
3
|
-
import './chunk-
|
|
1
|
+
import { describeInstance } from './chunk-XEIOGUGP.js';
|
|
2
|
+
import { useVitestAdapter } from './chunk-6ZGH56G4.js';
|
|
3
|
+
import './chunk-IE4NI2OQ.js';
|
|
4
4
|
import './chunk-YHDBRBGU.js';
|
|
5
5
|
import './chunk-TXDBDMIL.js';
|
|
6
6
|
import './chunk-FOL5RH22.js';
|
|
7
7
|
import './chunk-PC3FAZT3.js';
|
|
8
|
-
import { createFunctionSpy } from './chunk-
|
|
8
|
+
import { createFunctionSpy } from './chunk-HUS2MHK5.js';
|
|
9
9
|
import './chunk-Q7L4AWI7.js';
|
|
10
|
-
import './chunk-
|
|
10
|
+
import { sourceClassName } from './chunk-DYAS4OK4.js';
|
|
11
11
|
import { angularDialog } from './chunk-5FQZGJLJ.js';
|
|
12
12
|
import { withDocs } from './chunk-ENCEEH23.js';
|
|
13
13
|
import { TestBed } from '@angular/core/testing';
|
|
@@ -22,10 +22,10 @@ function guardMissingMembers(RefClass, double) {
|
|
|
22
22
|
const guarded = new Proxy(double, {
|
|
23
23
|
get(target, key, receiver) {
|
|
24
24
|
if (typeof key === "string" && !(key in target) && declared.has(key)) {
|
|
25
|
-
const repair = key === "componentInstance" ? `Hand the double the component's stand-in: provideMatDialogRef(${RefClass.name}, { componentInstance: { \u2026 } }).` : `${key} is the dialog doing its own work; open the real dialog through MatDialog for that.`;
|
|
25
|
+
const repair = key === "componentInstance" ? `Hand the double the component's stand-in: provideMatDialogRef(${sourceClassName(RefClass.name)}, { componentInstance: { \u2026 } }).` : `${key} is the dialog doing its own work; open the real dialog through MatDialog for that.`;
|
|
26
26
|
throw new Error(
|
|
27
27
|
withDocs(
|
|
28
|
-
`[vitest-auto-spy] provideMatDialogRef: the code under test read ${RefClass.name}.${key}, which the double does not have.
|
|
28
|
+
`[vitest-auto-spy] provideMatDialogRef: the code under test read ${sourceClassName(RefClass.name)}.${key}, which the double does not have.
|
|
29
29
|
${repair}`,
|
|
30
30
|
angularDialog
|
|
31
31
|
)
|
|
@@ -47,7 +47,7 @@ function createMatDialogRef(RefClass, init = {}) {
|
|
|
47
47
|
closed.next(result);
|
|
48
48
|
closed.complete();
|
|
49
49
|
};
|
|
50
|
-
const close = createFunctionSpy(`${RefClass.name}.close`);
|
|
50
|
+
const close = createFunctionSpy(`${sourceClassName(RefClass.name)}.close`);
|
|
51
51
|
getMockAdapter().restoreImplementation(close, emitClose);
|
|
52
52
|
if (init.closedWith !== void 0) {
|
|
53
53
|
emitClose(init.closedWith);
|
|
@@ -79,7 +79,7 @@ function injectMatDialogRef(RefClass, injector) {
|
|
|
79
79
|
if (ref === null) {
|
|
80
80
|
throw new Error(
|
|
81
81
|
withDocs(
|
|
82
|
-
`${caller}: nothing provides ${RefClass.name} in the injector given. Add provideMatDialogRef(${RefClass.name}) to its providers.`,
|
|
82
|
+
`${caller}: nothing provides ${sourceClassName(RefClass.name)} in the injector given. Add provideMatDialogRef(${sourceClassName(RefClass.name)}) to its providers.`,
|
|
83
83
|
angularDialog
|
|
84
84
|
)
|
|
85
85
|
);
|
|
@@ -88,7 +88,7 @@ function injectMatDialogRef(RefClass, injector) {
|
|
|
88
88
|
if (double === void 0) {
|
|
89
89
|
throw new Error(
|
|
90
90
|
withDocs(
|
|
91
|
-
`${caller}: the ${RefClass.name} here is ${describeInstance(ref)}, not one provideMatDialogRef() built. A later provider of ${RefClass.name} won over the double \u2014 list provideMatDialogRef() last, or drop the other one.`,
|
|
91
|
+
`${caller}: the ${sourceClassName(RefClass.name)} here is ${describeInstance(ref)}, not one provideMatDialogRef() built. A later provider of ${sourceClassName(RefClass.name)} won over the double \u2014 list provideMatDialogRef() last, or drop the other one.`,
|
|
92
92
|
angularDialog
|
|
93
93
|
)
|
|
94
94
|
);
|
package/dist/angular-http.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { assertAngularInternals, pendingRequestsReport, verifyOnTeardown, beforeTestBedReset } from './chunk-
|
|
2
|
-
import { flushEffects } from './chunk-
|
|
1
|
+
import { assertAngularInternals, pendingRequestsReport, verifyOnTeardown, beforeTestBedReset } from './chunk-JFOJFCVQ.js';
|
|
2
|
+
import { flushEffects } from './chunk-UH3KWLVL.js';
|
|
3
3
|
import './chunk-GG7BQKEK.js';
|
|
4
|
-
import './chunk-
|
|
5
|
-
import { count, taskName } from './chunk-
|
|
4
|
+
import './chunk-HVNKTPKG.js';
|
|
5
|
+
import { count, taskName } from './chunk-DYAS4OK4.js';
|
|
6
6
|
import { angularHttpNoRequest, angularHttpPending, angularHttpController, angularHttpExpectRequest } from './chunk-5FQZGJLJ.js';
|
|
7
7
|
import { withDocs } from './chunk-ENCEEH23.js';
|
|
8
8
|
import { provideHttpClient } from '@angular/common/http';
|
package/dist/angular-matchers.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { count } from './chunk-
|
|
1
|
+
import { sourceClassName, count } from './chunk-DYAS4OK4.js';
|
|
2
2
|
import { angularDirectiveApplied } from './chunk-5FQZGJLJ.js';
|
|
3
3
|
import { withDocs } from './chunk-ENCEEH23.js';
|
|
4
4
|
import '@angular/compiler';
|
|
5
|
-
import { ɵSIGNAL as _SIGNAL, isStandalone, reflectComponentType } from '@angular/core';
|
|
5
|
+
import { ɵSIGNAL as _SIGNAL, isStandalone, DebugElement, reflectComponentType } from '@angular/core';
|
|
6
6
|
import { By } from '@angular/platform-browser';
|
|
7
7
|
import { expect } from 'vitest';
|
|
8
8
|
|
|
@@ -23,6 +23,17 @@ function rootOf(received) {
|
|
|
23
23
|
function matching(root, predicate) {
|
|
24
24
|
return predicate(root) ? [root, ...root.queryAll(predicate)] : root.queryAll(predicate);
|
|
25
25
|
}
|
|
26
|
+
function matchingNodes(root, predicate) {
|
|
27
|
+
return predicate(root) ? [root, ...root.queryAllNodes(predicate)] : root.queryAllNodes(predicate);
|
|
28
|
+
}
|
|
29
|
+
function isAnchor(node) {
|
|
30
|
+
return !(node instanceof DebugElement);
|
|
31
|
+
}
|
|
32
|
+
function located(nodes) {
|
|
33
|
+
const anchors = nodes.filter(isAnchor).length;
|
|
34
|
+
const elements = nodes.length - anchors;
|
|
35
|
+
return [elements > 0 ? count(elements, "element") : "", anchors > 0 ? count(anchors, "template anchor") : ""].filter((part) => part !== "").join(" and ");
|
|
36
|
+
}
|
|
26
37
|
function isClass(value) {
|
|
27
38
|
return typeof value === "function";
|
|
28
39
|
}
|
|
@@ -33,12 +44,64 @@ function componentUnderTest(root) {
|
|
|
33
44
|
return void 0;
|
|
34
45
|
}
|
|
35
46
|
const mirror = reflectComponentType(type);
|
|
36
|
-
return mirror === null || mirror.selector.includes(TESTBED_DIRECTIVE_HOST) ? void 0 : type.name;
|
|
47
|
+
return mirror === null || mirror.selector.includes(TESTBED_DIRECTIVE_HOST) ? void 0 : sourceClassName(type.name);
|
|
48
|
+
}
|
|
49
|
+
function inHostScope(root, directive) {
|
|
50
|
+
const host = Reflect.get(Object(root.componentInstance), "constructor");
|
|
51
|
+
const defs = Reflect.get(Object(Reflect.get(Object(host), "\u0275cmp")), "directiveDefs");
|
|
52
|
+
const list = typeof defs === "function" ? Reflect.apply(defs, void 0, []) : defs;
|
|
53
|
+
return Array.isArray(list) && list.some((def) => Reflect.get(Object(def), "type") === directive);
|
|
54
|
+
}
|
|
55
|
+
var SELECTOR_NOT = 1;
|
|
56
|
+
var SELECTOR_CLASS = 8;
|
|
57
|
+
function selectorText(directive) {
|
|
58
|
+
const selectors = Reflect.get(Object(Reflect.get(directive, "\u0275dir") ?? Reflect.get(directive, "\u0275cmp")), "selectors");
|
|
59
|
+
if (!Array.isArray(selectors) || selectors.length === 0) {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
const parts = [];
|
|
63
|
+
for (const selector of selectors) {
|
|
64
|
+
if (!Array.isArray(selector)) {
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
let text = String(selector[0]);
|
|
68
|
+
let classes = false;
|
|
69
|
+
for (let index = 1; index < selector.length; index++) {
|
|
70
|
+
const entry = selector[index];
|
|
71
|
+
if (typeof entry === "number") {
|
|
72
|
+
if ((entry & SELECTOR_NOT) !== 0) {
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
classes = (entry & SELECTOR_CLASS) !== 0;
|
|
76
|
+
} else if (classes) {
|
|
77
|
+
text += `.${String(entry)}`;
|
|
78
|
+
} else {
|
|
79
|
+
const value = String(selector[++index]);
|
|
80
|
+
text += value === "" ? `[${String(entry)}]` : `[${String(entry)}="${value}"]`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
parts.push(text);
|
|
84
|
+
}
|
|
85
|
+
return parts.join(", ");
|
|
86
|
+
}
|
|
87
|
+
function selectorMismatch(directive, selector, root, withDirective) {
|
|
88
|
+
const name = sourceClassName(directive.name);
|
|
89
|
+
if (selector !== void 0 && withDirective.length > 0) {
|
|
90
|
+
return `it is on ${located(withDirective)} that selector does not match.
|
|
91
|
+
Check the selector against the template, or drop it: expect(fixture).toHaveDirectiveApplied(${name}).`;
|
|
92
|
+
}
|
|
93
|
+
if (!inHostScope(root, directive)) {
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
const own = selectorText(directive);
|
|
97
|
+
return `it is not on any element of this fixture, though it is in the host's scope \u2014 so its selector${own === void 0 ? "" : ` '${own}'`} matches nothing the template renders.
|
|
98
|
+
Check that selector against the template, and run fixture.detectChanges() before asserting if the element renders later.`;
|
|
37
99
|
}
|
|
38
|
-
function diagnose(directive, selector, root) {
|
|
100
|
+
function diagnose(directive, selector, root, withDirective) {
|
|
101
|
+
const name = sourceClassName(directive.name);
|
|
39
102
|
const elements = selector === void 0 ? [] : matching(root, By.css(selector));
|
|
40
103
|
const where = selector === void 0 ? "" : ` on '${selector}'`;
|
|
41
|
-
const prefix = `[vitest-auto-spy] expected ${
|
|
104
|
+
const prefix = `[vitest-auto-spy] expected ${name} to be applied${where}`;
|
|
42
105
|
if (selector !== void 0 && elements.length === 0) {
|
|
43
106
|
return withDocs(
|
|
44
107
|
`${prefix}, but no element matches that selector.
|
|
@@ -46,19 +109,30 @@ Run fixture.detectChanges() before asserting, and check the selector against the
|
|
|
46
109
|
angularDirectiveApplied
|
|
47
110
|
);
|
|
48
111
|
}
|
|
112
|
+
if (selector !== void 0 && withDirective.some(isAnchor)) {
|
|
113
|
+
return withDocs(
|
|
114
|
+
`${prefix}, but it is on a template anchor, not on an element \u2014 a structural directive sits on the comment Angular leaves in place of its template.
|
|
115
|
+
Assert it without a selector: expect(fixture).toHaveDirectiveApplied(${name}).`,
|
|
116
|
+
angularDirectiveApplied
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const mismatch = selectorMismatch(directive, selector, root, withDirective);
|
|
120
|
+
if (mismatch !== void 0) {
|
|
121
|
+
return withDocs(`${prefix}, but ${mismatch}`, angularDirectiveApplied);
|
|
122
|
+
}
|
|
49
123
|
const component = componentUnderTest(root);
|
|
50
124
|
if (component !== void 0) {
|
|
51
|
-
const scope = isStandalone(directive) ? `list ${
|
|
125
|
+
const scope = isStandalone(directive) ? `list ${name} in ${component}'s hostDirectives if the component should carry it, or in its imports if its template uses it` : `add the NgModule that declares ${name} to ${component}'s imports if its template uses it`;
|
|
52
126
|
return withDocs(
|
|
53
127
|
`${prefix}, but it is not on ${component}'s host element or in its template.
|
|
54
128
|
To apply it, ${scope}.`,
|
|
55
129
|
angularDirectiveApplied
|
|
56
130
|
);
|
|
57
131
|
}
|
|
58
|
-
const cause = isStandalone(directive) ? `${
|
|
132
|
+
const cause = isStandalone(directive) ? `${name} is standalone, so only the host component's own imports put it in scope` : `${name} is declared by an NgModule, and a test bundle drops that module's scope, so importing it contributes nothing`;
|
|
59
133
|
return withDocs(
|
|
60
134
|
`${prefix}, but it is not on any element of this fixture \u2014 ${cause}.
|
|
61
|
-
Build the host with createDirectiveHost({ template, scope: [${isStandalone(directive) ?
|
|
135
|
+
Build the host with createDirectiveHost({ template, scope: [${isStandalone(directive) ? name : "ItsModule"}] }).`,
|
|
62
136
|
angularDirectiveApplied
|
|
63
137
|
);
|
|
64
138
|
}
|
|
@@ -80,11 +154,11 @@ Pass the fixture itself, or fixture.debugElement \u2014 not fixture.nativeElemen
|
|
|
80
154
|
)
|
|
81
155
|
);
|
|
82
156
|
}
|
|
83
|
-
const withDirective =
|
|
157
|
+
const withDirective = matchingNodes(root, By.directive(directive));
|
|
84
158
|
const applied = selector === void 0 ? withDirective.length > 0 : matching(root, By.css(selector)).some((element) => element.providerTokens.includes(directive));
|
|
85
159
|
return {
|
|
86
160
|
pass: applied,
|
|
87
|
-
message: () => applied ? `[vitest-auto-spy] expected ${directive.name} not to be applied, but it is on ${
|
|
161
|
+
message: () => applied ? `[vitest-auto-spy] expected ${sourceClassName(directive.name)} not to be applied, but it is on ${located(withDirective)}.` : diagnose(directive, selector, root, withDirective)
|
|
88
162
|
};
|
|
89
163
|
}
|
|
90
164
|
function registerDirectiveMatchers() {
|
package/dist/angular-router.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { describeInstance } from './chunk-
|
|
1
|
+
import { describeInstance } from './chunk-XEIOGUGP.js';
|
|
2
2
|
import { angularInternalsError } from './chunk-GG7BQKEK.js';
|
|
3
|
-
import { createFunctionSpy } from './chunk-
|
|
3
|
+
import { createFunctionSpy } from './chunk-HUS2MHK5.js';
|
|
4
4
|
import './chunk-Q7L4AWI7.js';
|
|
5
|
-
import { count } from './chunk-
|
|
5
|
+
import { count } from './chunk-DYAS4OK4.js';
|
|
6
6
|
import { angularRouteDouble, angularRouterInject, angularRouterEvents, angularLocationDouble, angularRouteWiring, angularRouterDouble } from './chunk-5FQZGJLJ.js';
|
|
7
7
|
import { withDocs } from './chunk-ENCEEH23.js';
|
|
8
8
|
import { TestBed } from '@angular/core/testing';
|
package/dist/angular.js
CHANGED
|
@@ -40,6 +40,14 @@ var angularTypedElements = "https://asdalexey.github.io/vitest-auto-spy/adapters
|
|
|
40
40
|
var angularTrackRuns = "https://asdalexey.github.io/vitest-auto-spy/adapters/angular#counting-recomputations-and-effect-runs";
|
|
41
41
|
var trackInjectionsUntracked = "https://asdalexey.github.io/vitest-auto-spy/utilities/track-injections#get-on-a-token-that-is-not-tracked";
|
|
42
42
|
|
|
43
|
+
// src/lib/message-text.ts
|
|
44
|
+
function count(value, noun, plural = `${noun}s`) {
|
|
45
|
+
return `${value} ${value === 1 ? noun : plural}`;
|
|
46
|
+
}
|
|
47
|
+
function sourceClassName(name) {
|
|
48
|
+
return name.replace(/^_(?=[A-Z])/, "").replace(/\$\d+$/, "");
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
// src/lib/angular.ts
|
|
44
52
|
function provideAutoSpy(ObjectClass, methodsToSpyOnOrConfig) {
|
|
45
53
|
return {
|
|
@@ -75,11 +83,11 @@ function describeUnspied(token, injected) {
|
|
|
75
83
|
return `[vitest-auto-spy] injectSpy(${name2}): got a plain value, not an auto-spy \u2014 the token is provided for real.
|
|
76
84
|
Provide the double instead: { provide: ${name2}, useValue: createAutoMock<T>() }.`;
|
|
77
85
|
}
|
|
78
|
-
const name = token.name;
|
|
86
|
+
const name = sourceClassName(token.name);
|
|
79
87
|
const provider = Reflect.get(token, "\u0275prov");
|
|
80
88
|
const fromRoot = Reflect.get(Object(provider), "providedIn") === "root";
|
|
81
89
|
const built = Reflect.get(Object(Reflect.get(Object(injected), "constructor")), "name");
|
|
82
|
-
const what = typeof built === "string" && built.length > 0 ? `a real ${built}` : "a real instance";
|
|
90
|
+
const what = typeof built === "string" && built.length > 0 ? `a real ${sourceClassName(built)}` : "a real instance";
|
|
83
91
|
const why = fromRoot ? "nothing in the testing module provides a double, so Angular built it (providedIn: 'root')" : "the testing module provides the real class, not a double";
|
|
84
92
|
return `[vitest-auto-spy] injectSpy(${name}): got ${what} \u2014 ${why}.
|
|
85
93
|
Add provideAutoSpy(${name}) to providers.`;
|
|
@@ -240,7 +248,7 @@ function explainLateOverride(error, component, token) {
|
|
|
240
248
|
}
|
|
241
249
|
return new Error(
|
|
242
250
|
_message_link$withDocs(
|
|
243
|
-
`[vitest-auto-spy] overrideComponentProvider(${component.name}, ${token.name}) ran after the testing module was instantiated, and Angular accepts no override past that point. Something read the injector first \u2014 a \`TestBed.inject\`, an \`injectSpy\`, a \`createComponent\` \u2014 earlier in this test or in the same \`beforeCreate\`. Override first, then inject.`,
|
|
251
|
+
`[vitest-auto-spy] overrideComponentProvider(${sourceClassName(component.name)}, ${sourceClassName(token.name)}) ran after the testing module was instantiated, and Angular accepts no override past that point. Something read the injector first \u2014 a \`TestBed.inject\`, an \`injectSpy\`, a \`createComponent\` \u2014 earlier in this test or in the same \`beforeCreate\`. Override first, then inject.`,
|
|
244
252
|
angularOverrideLate
|
|
245
253
|
),
|
|
246
254
|
{ cause: error }
|
|
@@ -288,7 +296,7 @@ function componentInjector(fixture, component) {
|
|
|
288
296
|
}
|
|
289
297
|
function describeResolved(resolved) {
|
|
290
298
|
const constructorName = readProperty(readProperty(resolved, "constructor"), "name");
|
|
291
|
-
return typeof constructorName === "string" && constructorName.length > 0 ? `a ${constructorName} instance` : String(resolved);
|
|
299
|
+
return typeof constructorName === "string" && constructorName.length > 0 ? `a ${sourceClassName(constructorName)} instance` : String(resolved);
|
|
292
300
|
}
|
|
293
301
|
function verify(fixture, { component, token, spy }) {
|
|
294
302
|
const injector = componentInjector(fixture, component);
|
|
@@ -301,7 +309,7 @@ function verify(fixture, { component, token, spy }) {
|
|
|
301
309
|
}
|
|
302
310
|
throw new Error(
|
|
303
311
|
_message_link$withDocs(
|
|
304
|
-
`[vitest-auto-spy] overrideComponentProvider(${component.name}, ${token.name}): the override did not apply \u2014 ${component.name} resolved ${token.name} to ${describeResolved(resolved)}, not the spy this call returned.
|
|
312
|
+
`[vitest-auto-spy] overrideComponentProvider(${sourceClassName(component.name)}, ${sourceClassName(token.name)}): the override did not apply \u2014 ${sourceClassName(component.name)} resolved ${sourceClassName(token.name)} to ${describeResolved(resolved)}, not the spy this call returned.
|
|
305
313
|
` + explainMissedOverride(token, resolved),
|
|
306
314
|
angularOverrideApplied
|
|
307
315
|
)
|
|
@@ -309,7 +317,7 @@ function verify(fixture, { component, token, spy }) {
|
|
|
309
317
|
}
|
|
310
318
|
function explainMissedOverride(token, resolved) {
|
|
311
319
|
const later = _spy_mark$isAutoSpyLike(resolved) ? "a different double" : "the real service";
|
|
312
|
-
return `It got ${later} because something configured ${token.name} again after this call \u2014 a later TestBed.overrideProvider or configureTestingModule. Keep overrideComponentProvider as the last word on it.`;
|
|
320
|
+
return `It got ${later} because something configured ${sourceClassName(token.name)} again after this call \u2014 a later TestBed.overrideProvider or configureTestingModule. Keep overrideComponentProvider as the last word on it.`;
|
|
313
321
|
}
|
|
314
322
|
function verifyOnNextCreate(entry) {
|
|
315
323
|
if (readTestBedMethod("createComponent") === void 0) {
|
|
@@ -342,7 +350,7 @@ function hasEmptyRuntimeScope(module) {
|
|
|
342
350
|
}
|
|
343
351
|
function moduleName(module) {
|
|
344
352
|
const name = readProperty(module, "name");
|
|
345
|
-
return typeof name === "string" ? name : String(module);
|
|
353
|
+
return typeof name === "string" ? sourceClassName(name) : String(module);
|
|
346
354
|
}
|
|
347
355
|
var DEFINITION_LISTS = ["providers", "viewProviders", "dependencies"];
|
|
348
356
|
function resolveList(list) {
|
|
@@ -437,6 +445,9 @@ function tokenName(token) {
|
|
|
437
445
|
const name = typeof token === "function" ? token.name : void 0;
|
|
438
446
|
return typeof name === "string" && name.length > 0 ? name : String(token);
|
|
439
447
|
}
|
|
448
|
+
function messageName(token) {
|
|
449
|
+
return sourceClassName(tokenName(token));
|
|
450
|
+
}
|
|
440
451
|
function trackInjections(tokens, options = {}) {
|
|
441
452
|
const build = options.double ?? createSpyForToken;
|
|
442
453
|
const doubles = new Map(tokens.map((token) => [token, build(token)]));
|
|
@@ -457,8 +468,8 @@ function trackInjections(tokens, options = {}) {
|
|
|
457
468
|
if (!doubles.has(token)) {
|
|
458
469
|
throw new Error(
|
|
459
470
|
_message_link$withDocs(
|
|
460
|
-
`[vitest-auto-spy] trackInjections(...).get(${
|
|
461
|
-
Tracked here: ${tokens.map(
|
|
471
|
+
`[vitest-auto-spy] trackInjections(...).get(${messageName(token)}): that token is not tracked by this log.
|
|
472
|
+
Tracked here: ${tokens.map(messageName).join(", ") || "(none)"}. Add it to the trackInjections([...]) list, or read it from the injector directly \u2014 \`get\` only answers for the tokens whose providers this log created.`,
|
|
462
473
|
trackInjectionsUntracked
|
|
463
474
|
)
|
|
464
475
|
);
|
|
@@ -536,7 +547,7 @@ var CLASS = 8;
|
|
|
536
547
|
var SIGNAL_BASED = 1;
|
|
537
548
|
var stubCount = 0;
|
|
538
549
|
function createComponentStub(real, overrides = {}, options = {}) {
|
|
539
|
-
const realName = typeof real === "function" ? real.name : String(real);
|
|
550
|
+
const realName = typeof real === "function" ? sourceClassName(real.name) : String(real);
|
|
540
551
|
const name = `${realName}Stub`;
|
|
541
552
|
const pipe = definition(real, "\u0275pipe");
|
|
542
553
|
if (pipe) {
|
|
@@ -712,7 +723,7 @@ ${askFor(autoSpies.tokens().map(describeToken))}`,
|
|
|
712
723
|
// src/lib/host-element.ts
|
|
713
724
|
function describeElement(element) {
|
|
714
725
|
const classes = [...element.classList].map((name) => `.${name}`).join("");
|
|
715
|
-
return `<${element.tagName.toLowerCase()}${classes}> (${element.constructor.name})`;
|
|
726
|
+
return `<${element.tagName.toLowerCase()}${classes}> (${sourceClassName(element.constructor.name)})`;
|
|
716
727
|
}
|
|
717
728
|
function fail(message) {
|
|
718
729
|
return _message_link$withDocs(`[vitest-auto-spy] ${message}`, angularTypedElements);
|
|
@@ -738,7 +749,7 @@ Check the predicate against the rendered template, or use queryElement(fixture,
|
|
|
738
749
|
}
|
|
739
750
|
function checkType(element, type, what, helper) {
|
|
740
751
|
if (!(element instanceof type)) {
|
|
741
|
-
throw new TypeError(fail(`${helper}: ${what} ${describeElement(element)}, not ${type.name}.`));
|
|
752
|
+
throw new TypeError(fail(`${helper}: ${what} ${describeElement(element)}, not ${sourceClassName(type.name)}.`));
|
|
742
753
|
}
|
|
743
754
|
return element;
|
|
744
755
|
}
|
|
@@ -779,10 +790,10 @@ function whyNoInputs(component) {
|
|
|
779
790
|
Import the component from its own file rather than through the barrel.`;
|
|
780
791
|
}
|
|
781
792
|
if (Reflect.get(component, "\u0275pipe") !== void 0) {
|
|
782
|
-
return `${component.name} is a @Pipe, and a pipe has no inputs.
|
|
793
|
+
return `${sourceClassName(component.name)} is a @Pipe, and a pipe has no inputs.
|
|
783
794
|
Call its transform() directly, or render it inside a host component.`;
|
|
784
795
|
}
|
|
785
|
-
return `${component.name} carries no \u0275cmp, so Angular never compiled it as a component and there are no inputs to set.
|
|
796
|
+
return `${sourceClassName(component.name)} carries no \u0275cmp, so Angular never compiled it as a component and there are no inputs to set.
|
|
786
797
|
Pass the @Component class the fixture was created from.`;
|
|
787
798
|
}
|
|
788
799
|
function unknownInputsError(caller, component, unknown, declared) {
|
|
@@ -795,7 +806,7 @@ function unknownInputsError(caller, component, unknown, declared) {
|
|
|
795
806
|
const known = declared.length > 0 ? `Its inputs are ${quote(declared)}.` : "It declares no inputs at all.";
|
|
796
807
|
return new Error(
|
|
797
808
|
_message_link$withDocs(
|
|
798
|
-
`[vitest-auto-spy] ${caller}: ${component.name} declares no input named ${quote(unknown)}.${hint}
|
|
809
|
+
`[vitest-auto-spy] ${caller}: ${sourceClassName(component.name)} declares no input named ${quote(unknown)}.${hint}
|
|
799
810
|
${known} Angular would answer the name with an NG0303 and change nothing; a plain field is assigned on the instance instead, and a signal the component owns is set through the signal.`,
|
|
800
811
|
angularInputs
|
|
801
812
|
)
|
|
@@ -888,31 +899,32 @@ function refusedInImports(dependency) {
|
|
|
888
899
|
return void 0;
|
|
889
900
|
}
|
|
890
901
|
const definition2 = DECLARATION_KEYS.map((key) => Reflect.get(dependency, key)).find((value) => value !== void 0);
|
|
891
|
-
return definition2 !== void 0 && Reflect.get(definition2, "standalone") !== true ? dependency.name : void 0;
|
|
902
|
+
return definition2 !== void 0 && Reflect.get(definition2, "standalone") !== true ? sourceClassName(dependency.name) : void 0;
|
|
892
903
|
}
|
|
893
904
|
function assertScopeIsImportable(component, kept, candidates) {
|
|
894
905
|
const refused = kept.filter((dependency) => refusedInImports(dependency) !== void 0);
|
|
895
906
|
if (refused.length === 0) {
|
|
896
907
|
return;
|
|
897
908
|
}
|
|
898
|
-
const names = refused.map((dependency) => dependency.name);
|
|
909
|
+
const names = refused.map((dependency) => sourceClassName(dependency.name));
|
|
899
910
|
const owners = [...new Set(refused.map((dependency) => declaringModule(dependency, candidates)))];
|
|
900
911
|
const [owner] = owners;
|
|
901
912
|
const fix = owners.length === 1 && owner !== void 0 ? `keepModules: [${owner}]` : `keepModules: [<the NgModule that declares ${String(names[0])}>]`;
|
|
902
913
|
throw new Error(
|
|
903
914
|
_message_link$withDocs(
|
|
904
|
-
`[vitest-auto-spy] renderShallow(${component.name}, { keepTemplate: true }): ${names.join(", ")} ${names.length === 1 ? "is" : "are"} declared by ${owner === void 0 || owners.length > 1 ? "an NgModule" : owner}, not standalone, and Angular takes only standalone declarations and NgModules in \`imports\`.
|
|
915
|
+
`[vitest-auto-spy] renderShallow(${sourceClassName(component.name)}, { keepTemplate: true }): ${names.join(", ")} ${names.length === 1 ? "is" : "are"} declared by ${owner === void 0 || owners.length > 1 ? "an NgModule" : owner}, not standalone, and Angular takes only standalone declarations and NgModules in \`imports\`.
|
|
905
916
|
An AOT build flattened that module away, so name it and it is put back whole: ${fix}.`,
|
|
906
917
|
angularKeepTemplate
|
|
907
918
|
)
|
|
908
919
|
);
|
|
909
920
|
}
|
|
910
921
|
function declaringModule(dependency, candidates) {
|
|
911
|
-
|
|
922
|
+
const owner = candidates.filter((candidate) => typeof candidate === "function").find((candidate) => {
|
|
912
923
|
const scope = /* @__PURE__ */ new Set();
|
|
913
924
|
exportedScope(candidate, scope, /* @__PURE__ */ new Set());
|
|
914
925
|
return scope.has(dependency);
|
|
915
|
-
})
|
|
926
|
+
});
|
|
927
|
+
return owner === void 0 ? void 0 : sourceClassName(owner.name);
|
|
916
928
|
}
|
|
917
929
|
function unwrapList(value) {
|
|
918
930
|
const resolved = typeof value === "function" ? Reflect.apply(value, void 0, []) : value;
|
|
@@ -1047,11 +1059,6 @@ function runEffect(effectRef) {
|
|
|
1047
1059
|
});
|
|
1048
1060
|
}
|
|
1049
1061
|
|
|
1050
|
-
// src/lib/message-text.ts
|
|
1051
|
-
function count(value, noun, plural = `${noun}s`) {
|
|
1052
|
-
return `${value} ${value === 1 ? noun : plural}`;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
1062
|
// src/lib/zoneless.ts
|
|
1056
1063
|
function flushEffects() {
|
|
1057
1064
|
TestBed.inject(NgZone).run(() => {
|