Skip to content

Commit a26c982

Browse files
authored
Merge pull request #2126 from satanTime/issues/2097
2 parents 3e27cb0 + 9387209 commit a26c982

File tree

13 files changed

+317
-24
lines changed

13 files changed

+317
-24
lines changed

e2e/a5es2015/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"start": "ng serve",
1010
"build": "ng build --prod",
1111
"test": "npm run test:jasmine -- --progress=false && npm run test:jest",
12-
"test:debug": "npm run test:jasmine -- --browsers=Chrome --watch",
12+
"test:debug": "npm run test:jasmine -- --browsers=Chrome --watch --single-run=false",
1313
"test:jasmine": "ng test",
1414
"test:jasmine:debug": "ng test -- --watch --browsers Chrome",
1515
"test:jest": "jest -i",

e2e/a5es5/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"start": "ng serve",
1010
"build": "ng build --prod",
1111
"test": "npm run test:jasmine -- --progress=false && npm run test:jest",
12-
"test:debug": "npm run test:jasmine -- --browsers=Chrome --watch",
12+
"test:debug": "npm run test:jasmine -- --browsers=Chrome --watch --single-run=false",
1313
"test:jasmine": "ng test",
1414
"test:jasmine:debug": "ng test -- --watch --browsers Chrome",
1515
"test:jest": "jest -i",
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { isNgDef } from '../../common/func.is-ng-def';
2+
13
export default (value: any): boolean => {
2-
return typeof value === 'function';
4+
return typeof value === 'function' || isNgDef(value, 't');
35
};

libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instance.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getSourceOfMock } from '../../common/func.get-source-of-mock';
2+
import { isNgDef } from '../../common/func.is-ng-def';
23
import mockHelperCrawl from '../crawl/mock-helper.crawl';
34
import mockHelperFind from '../find/mock-helper.find';
45
import funcGetFromNode from '../func.get-from-node';
@@ -12,8 +13,8 @@ const defaultNotFoundValue = {}; // simulating Symbol
1213

1314
export default (...args: any[]) => {
1415
const [el, sel, notFoundValue] = funcParseFindArgs(args, funcIsValidFindInstanceSelector, defaultNotFoundValue);
15-
if (typeof sel !== 'function') {
16-
throw new Error('Only classes are accepted');
16+
if (typeof sel !== 'function' && !isNgDef(sel, 't')) {
17+
throw new Error('Only classes or tokens are accepted');
1718
}
1819

1920
const declaration = getSourceOfMock(sel);

libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instances.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getSourceOfMock } from '../../common/func.get-source-of-mock';
2+
import { isNgDef } from '../../common/func.is-ng-def';
23
import mockHelperCrawl from '../crawl/mock-helper.crawl';
34
import mockHelperFindAll from '../find/mock-helper.find-all';
45
import funcGetFromNode from '../func.get-from-node';
@@ -9,18 +10,22 @@ import funcIsValidFindInstanceSelector from './func.is-valid-find-instance-selec
910

1011
export default <T>(...args: any[]): T[] => {
1112
const [el, sel] = funcParseFindArgs(args, funcIsValidFindInstanceSelector);
12-
if (typeof sel !== 'function') {
13-
throw new Error('Only classes are accepted');
13+
if (typeof sel !== 'function' && !isNgDef(sel, 't')) {
14+
throw new Error('Only classes or tokens are accepted');
1415
}
1516

1617
const declaration = getSourceOfMock(sel);
1718
const result: T[] = [];
19+
const scanned: any[] = [];
1820
const elements = mockHelperFindAll(funcGetLastFixture(), el, undefined);
1921
for (const element of elements) {
2022
mockHelperCrawl(
2123
element,
2224
node => {
23-
funcGetFromNode(result, node, declaration);
25+
if (scanned.indexOf(node) === -1) {
26+
funcGetFromNode(result, node, declaration);
27+
scanned.push(node);
28+
}
2429
},
2530
true,
2631
);
Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,44 @@
1-
import { DebugNode } from '@angular/core';
1+
import { DebugNode, InjectionToken, Injector } from '@angular/core';
22

33
import coreInjector from '../common/core.injector';
44
import { Type } from '../common/core.types';
5+
import { isNgDef } from '../common/func.is-ng-def';
56

67
import { Node } from './func.get-from-node';
78

8-
export default <T>(result: T[], node: (DebugNode & Node) | null | undefined, proto: Type<T>): void => {
9-
if (!node) {
9+
const getParentWithInjector = (node: (DebugNode & Node) | null): Injector | undefined => {
10+
let parent: DebugNode | null = node;
11+
while (parent?.injector.constructor.name === 'NullInjector') {
12+
parent = parent.parent;
13+
}
14+
15+
if (parent) {
16+
return parent.injector;
17+
}
18+
19+
return undefined;
20+
};
21+
22+
export default <T>(
23+
result: T[],
24+
node: (DebugNode & Node) | null | undefined,
25+
proto: Type<T> | InjectionToken<T>,
26+
): void => {
27+
if (!node?.injector || node.injector.constructor.name === 'NullInjector') {
1028
return;
1129
}
1230

31+
const parentInjector = getParentWithInjector(node.parent);
32+
const parentInstance = parentInjector ? coreInjector(proto, parentInjector) : undefined;
1333
const instance = coreInjector(proto, node.injector);
14-
if (instance && result.indexOf(instance) === -1) {
34+
// a way to avoid inherited injections
35+
if (parentInstance === instance) {
36+
return;
37+
}
38+
39+
if (isNgDef(proto, 't') && instance !== undefined) {
40+
result.push(instance);
41+
} else if (instance !== undefined && result.indexOf(instance) === -1) {
1542
result.push(instance);
1643
}
1744
};

libs/ng-mocks/src/lib/mock-helper/func.get-from-node.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { DebugNode } from '@angular/core';
1+
import { DebugNode, InjectionToken } from '@angular/core';
22

33
import { Type } from '../common/core.types';
4+
import { isNgDef } from '../common/func.is-ng-def';
45

56
import funcGetFromNodeInjector from './func.get-from-node-injector';
67
import funcGetFromNodeIvy from './func.get-from-node-ivy';
@@ -27,10 +28,16 @@ export interface Node {
2728
parent?: (DebugNode & Node) | null;
2829
}
2930

30-
export default <T>(result: T[], node: (DebugNode & Node) | null | undefined, proto: Type<T>): T[] => {
31+
export default <T>(
32+
result: T[],
33+
node: (DebugNode & Node) | null | undefined,
34+
proto: Type<T> | InjectionToken<T>,
35+
): T[] => {
3136
funcGetFromNodeInjector(result, node, proto);
32-
funcGetFromNodeStandard(result, node, proto);
33-
funcGetFromNodeIvy(result, node, proto);
37+
if (!isNgDef(proto, 't')) {
38+
funcGetFromNodeStandard(result, node, proto);
39+
funcGetFromNodeIvy(result, node, proto);
40+
}
3441

3542
return result;
3643
};

libs/ng-mocks/src/lib/mock-helper/func.parse-find-args-name.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AnyType, DebugNodeSelector } from '../common/core.types';
2+
import { isNgDef } from '../common/func.is-ng-def';
23

34
export default (selector: AnyType<any> | DebugNodeSelector): string => {
45
if (typeof selector === 'string') {
@@ -7,6 +8,9 @@ export default (selector: AnyType<any> | DebugNodeSelector): string => {
78
if (typeof selector === 'function') {
89
return selector.name;
910
}
11+
if (isNgDef(selector, 't')) {
12+
return (selector as any)._desc;
13+
}
1014
if (Array.isArray(selector)) {
1115
return selector[0];
1216
}

libs/ng-mocks/src/lib/mock-helper/mock-helper.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,32 +195,36 @@ export const ngMocks: {
195195
/**
196196
* @see https://ng-mocks.sudo.eu/api/ngMocks/findInstance
197197
*/
198-
findInstance<T>(instanceClass: AnyType<T>): T;
198+
findInstance<T>(instanceClass: AnyType<T> | InjectionToken<T>): T;
199199

200200
/**
201201
* @see https://ng-mocks.sudo.eu/api/ngMocks/findInstance
202202
*/
203-
findInstance<T>(elSelector: DebugNodeSelector, instanceClass: AnyType<T>): T;
203+
findInstance<T>(elSelector: DebugNodeSelector, instanceClass: AnyType<T> | InjectionToken<T>): T;
204204

205205
/**
206206
* @see https://ng-mocks.sudo.eu/api/ngMocks/findInstance
207207
*/
208-
findInstance<T, D>(instanceClass: AnyType<T>, notFoundValue: D): D | T;
208+
findInstance<T, D>(instanceClass: AnyType<T> | InjectionToken<T>, notFoundValue: D): D | T;
209209

210210
/**
211211
* @see https://ng-mocks.sudo.eu/api/ngMocks/findInstance
212212
*/
213-
findInstance<T, D>(elSelector: DebugNodeSelector, instanceClass: AnyType<T>, notFoundValue: D): D | T;
213+
findInstance<T, D>(
214+
elSelector: DebugNodeSelector,
215+
instanceClass: AnyType<T> | InjectionToken<T>,
216+
notFoundValue: D,
217+
): D | T;
214218

215219
/**
216220
* @see https://ng-mocks.sudo.eu/api/ngMocks/findInstances
217221
*/
218-
findInstances<T>(instanceClass: AnyType<T>): T[];
222+
findInstances<T>(instanceClass: AnyType<T> | InjectionToken<T>): T[];
219223

220224
/**
221225
* @see https://ng-mocks.sudo.eu/api/ngMocks/findInstances
222226
*/
223-
findInstances<T>(elSelector: DebugNodeSelector, instanceClass: AnyType<T>): T[];
227+
findInstances<T>(elSelector: DebugNodeSelector, instanceClass: AnyType<T> | InjectionToken<T>): T[];
224228

225229
/**
226230
* @see https://ng-mocks.sudo.eu/api/ngMocks/findTemplateRef

tests/issue-2097/nested.spec.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
Component,
3+
Injectable,
4+
InjectionToken,
5+
NgModule,
6+
} from '@angular/core';
7+
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';
8+
9+
const TOKEN = new InjectionToken<string>('TOKEN');
10+
11+
@Component({
12+
providers: [
13+
{
14+
provide: TOKEN,
15+
useValue: 'parent',
16+
},
17+
],
18+
selector: 'parent',
19+
template: '<child></child><child></child><ng-content></ng-content>',
20+
})
21+
class ParentComponent {}
22+
23+
@Component({
24+
selector: 'child',
25+
template: 'child',
26+
})
27+
class ChildComponent {}
28+
29+
@NgModule({
30+
declarations: [ParentComponent, ChildComponent],
31+
providers: [
32+
{
33+
provide: TOKEN,
34+
useValue: 'token',
35+
},
36+
],
37+
})
38+
class TargetModule {}
39+
40+
describe('issue-2097:nested', () => {
41+
beforeEach(() =>
42+
MockBuilder(
43+
[ParentComponent, ChildComponent, TOKEN],
44+
TargetModule,
45+
),
46+
);
47+
48+
it('finds tokens correctly', () => {
49+
const fixture = MockRender(`
50+
<parent>
51+
<child></child>
52+
<child></child>
53+
</parent>
54+
`);
55+
56+
// it should be found on the parent node only.
57+
const instances = ngMocks.findInstances('parent', TOKEN);
58+
expect(instances).toEqual(['parent']);
59+
});
60+
});

0 commit comments

Comments
 (0)