Skip to content

Commit

Permalink
fix(ngMocks.findInstance): finds pipes in attributes help-me-mom#2314
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Apr 24, 2022
1 parent f458f69 commit 0e5a4de
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import nestedCheck from './nested-check';

export default (
sel: DebugNode | DebugNodeSelector,
callback: (node: DebugNode) => void | boolean,
callback: (node: DebugNode, parent?: DebugNode) => void | boolean,
includeTextNode = false,
): void => {
const el = mockHelperFind(funcGetLastFixture(), sel, undefined);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ export default (...args: any[]) => {
const result: any[] = [];
mockHelperCrawl(
mockHelperFind(funcGetLastFixture(), el, undefined),
node => {
(node, parent) => {
funcGetFromNode(result, node, declaration);
if (result.length === 0 && parent && parent.nativeNode.nodeName === '#comment') {
funcGetFromNode(result, parent, declaration);
}

return result.length > 0;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ export default <T>(...args: any[]): T[] => {
for (const element of elements) {
mockHelperCrawl(
element,
node => {
(node, parent) => {
if (scanned.indexOf(node) === -1) {
funcGetFromNode(result, node, declaration);
scanned.push(node);
}
if (parent && parent.nativeNode.nodeName === '#comment' && scanned.indexOf(parent) === -1) {
funcGetFromNode(result, parent, declaration);
scanned.push(parent);
}
},
true,
);
Expand Down
20 changes: 18 additions & 2 deletions libs/ng-mocks/src/lib/mock-helper/func.get-from-node-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ import { DebugNode } from '@angular/core';
import { Type } from '../common/core.types';

const detectGatherFlag = (gather: boolean, el: DebugNode | null, node: any): boolean => {
// LContainer for structural directives can be a trigger for pipes.
if (
el &&
el.nativeNode &&
el.nativeNode.nodeName === '#comment' &&
Array.isArray(node) &&
node[0] === el.nativeNode
) {
return true;
}

// LContainer should stop the scan.
if (Array.isArray(node)) {
return false;
Expand Down Expand Up @@ -44,8 +55,13 @@ const scan = <T>(
scanned.push(nodes);
let gather = gatherDefault;

for (const raw of nodes) {
const node = normalize(raw);
let nodesLength = nodes.length;
if (nodes.length > 1 && nodes[1] && typeof nodes[1] === 'object' && nodes[1].bindingStartIndex) {
nodesLength = nodes[1].bindingStartIndex;
}

for (let index = 0; index < nodesLength; index += 1) {
const node = normalize(nodes[index]);
if (isNotObject(node)) {
continue;
}
Expand Down
136 changes: 136 additions & 0 deletions tests/issue-2314/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { AsyncPipe, NgIf } from '@angular/common';
import { Component, Pipe, PipeTransform } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BehaviorSubject } from 'rxjs';

import { ngMocks } from 'ng-mocks';

@Pipe({
name: 'nothing',
pure: false,
})
class NothingPipe implements PipeTransform {
transform<T>(value: T): T {
return value;
}
}

@Component({
selector: 'target',
template: `
<div
class="array"
*ngFor="
let item of array$ | nothing | async | nothing | nothing
"
>
item: {{ item }}
</div>
<div
class="false"
*ngIf="false$ | nothing | async | nothing | nothing"
>
false
</div>
<div class="text">
{{ text$ | nothing | async | nothing | nothing }}
</div>
<div
class="true"
*ngIf="true$ | nothing | async | nothing | nothing"
>
true
</div>
`,
})
export class TargetComponent {
public array$ = new BehaviorSubject([1]);
public false$ = new BehaviorSubject(false);
public text$ = new BehaviorSubject('text');
public true$ = new BehaviorSubject(true);
}

// @see https://github.com/ike18t/ng-mocks/issues/2314
describe('issue-2314', () => {
let fixture: ComponentFixture<TargetComponent>;

beforeEach(async () => {
return TestBed.configureTestingModule({
declarations: [TargetComponent, NothingPipe],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(TargetComponent);
fixture.detectChanges();
});

it(`finds all pipes`, () => {
const allPipes = ngMocks.findInstances(AsyncPipe);
expect(allPipes.length).toBe(4);
});

describe('ngMocks.findInstance', () => {
it(`finds pipes on '.array'`, () => {
const arrayPipe = ngMocks.findInstance(
'.array',
AsyncPipe,
undefined,
);
expect(arrayPipe).toBeDefined();
});

it(`finds pipes on '.false'`, () => {
// Because it isn't rendered, we cannot find the element with `.false`, therefore, we need to rely on NgIf itself.
const ngIf = ngMocks.reveal(NgIf);
const falsePipe = ngMocks.findInstance(
ngIf,
AsyncPipe,
undefined,
);
expect(falsePipe).toBeDefined();
});

it(`find pipes on '.text'`, () => {
const textPipe = ngMocks.findInstance(
'.text',
AsyncPipe,
undefined,
);
expect(textPipe).toBeDefined();
});

it(`should find pipe on '.true'`, () => {
const truePipe = ngMocks.findInstance(
'.true',
AsyncPipe,
undefined,
);
expect(truePipe).toBeDefined();
});
});

describe('ngMocks.findInstances', () => {
it(`finds pipes on '.array'`, () => {
const arrayPipe = ngMocks.findInstances('.array', AsyncPipe);
expect(arrayPipe.length).toEqual(1);
});

it(`finds pipes on '.false'`, () => {
// Because it isn't rendered, we cannot find the element with `.false`, therefore, we need to rely on NgIf itself.
const ngIf = ngMocks.reveal(NgIf);
const falsePipe = ngMocks.findInstances(ngIf, AsyncPipe);
expect(falsePipe.length).toEqual(1);
});

it(`find pipes on '.text'`, () => {
const textPipe = ngMocks.findInstances('.text', AsyncPipe);
expect(textPipe.length).toEqual(1);
});

it(`should find pipe on '.true'`, () => {
const truePipe = ngMocks.findInstances('.true', AsyncPipe);
expect(truePipe.length).toEqual(1);
});
});
});

0 comments on commit 0e5a4de

Please sign in to comment.