#Testing the directive with timer RxJS, forceAsync and timer not working.

2 messages · Page 1 of 1 (latest)

unkempt crystal
#
@Directive({
    selector: '[clickOutside]',
})
export class ClickOutsideDirective implements OnInit {
    private readonly ngZone = inject(NgZone);
    private readonly elementRef = inject(ElementRef);
    private document = inject(DOCUMENT);
    private destroyRef = inject(DestroyRef);
    private readonly documentClick$ = this.documentClick();

    @Output() clickOutside = new EventEmitter<EventTarget>();

    ngOnInit(): void {
        this.documentClick$
            .pipe(
                takeUntilDestroyed(this.destroyRef),
                filter((event: Event) => {
                    return !this.elementRef.nativeElement.contains(event.target);
                }),
            )
            .subscribe((event: Event) => {
                this.ngZone.run(() => {
                    if (!event?.target) {
                        return;
                    }
                    this.clickOutside.emit(event.target);
                });
            });
    }

    private documentClick(): Subject<Event> {
        const click$ = new Subject<Event>();
        this.ngZone.runOutsideAngular(() => {
            timer(50)
                .pipe(switchMap(() => fromEvent(this.document, 'click')))
                .subscribe(click$);
        });
        return click$;
    }
}
#
@Component({
    standalone: true,
    template: `
        <div clickOutside>
            <div id="inner-div"></div>
        </div>
        <div id="sibling-div"></div>
    `,
    imports: [ClickOutsideModule],
})
class TestComponent {}

describe('ClickOutsideDirective', () => {
    let fixture: ComponentFixture<TestComponent>;
    let directive: ClickOutsideDirective;
    let debugElement: DebugElement;

    beforeEach(waitForAsync(() => {
        TestBed.configureTestingModule({
            imports: [TestComponent, ClickOutsideModule],
        });

        fixture = TestBed.createComponent(TestComponent);
        debugElement = fixture.debugElement.query(By.directive(ClickOutsideDirective));
        directive = debugElement.injector.get(ClickOutsideDirective);
        fixture.detectChanges();
    }));

    it('should create the directive', () => {
        expect(directive).toBeDefined();
    });

    it('should emit clickOutside even when click occurs outside of the element', fakeAsync(() => {
        spyOn(directive.clickOutside, 'emit');
        fixture.detectChanges();
        tick(500)
        const siblingDiv = fixture.debugElement.query(By.css('#sibling-div'));
        siblingDiv.nativeElement.click();
        expect(directive.clickOutside.emit).toHaveBeenCalledWith(siblingDiv.nativeElement);
    }));
});