Skip to content

Commit f91d2e8

Browse files
committed
Stub out the MutationObserver.
1 parent 6508f9a commit f91d2e8

File tree

2 files changed

+94
-18
lines changed

2 files changed

+94
-18
lines changed

src/lib/core/observe-content/observe-content.spec.ts

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import {Component} from '@angular/core';
2-
import {async, TestBed} from '@angular/core/testing';
3-
import {ObserveContentModule} from './observe-content';
2+
import {async, TestBed, ComponentFixture, fakeAsync, tick} from '@angular/core/testing';
3+
import {ObserveContentModule, MutationObserverFactory} from './observe-content';
44

55
/**
66
* TODO(elad): `ProxyZone` doesn't seem to capture the events raised by
77
* `MutationObserver` and needs to be investigated
88
*/
99

1010
describe('Observe content', () => {
11-
beforeEach(async(() => {
12-
TestBed.configureTestingModule({
13-
imports: [ObserveContentModule],
14-
declarations: [ComponentWithTextContent, ComponentWithChildTextContent],
15-
});
11+
describe('basic usage', () => {
12+
beforeEach(async(() => {
13+
TestBed.configureTestingModule({
14+
imports: [ObserveContentModule],
15+
declarations: [ComponentWithTextContent, ComponentWithChildTextContent]
16+
});
1617

17-
TestBed.compileComponents();
18-
}));
18+
TestBed.compileComponents();
19+
}));
1920

20-
describe('text content change', () => {
21-
it('should call the registered for changes function', done => {
21+
it('should trigger the callback when the content of the element changes', done => {
2222
let fixture = TestBed.createComponent(ComponentWithTextContent);
2323
fixture.detectChanges();
2424

@@ -33,10 +33,8 @@ describe('Observe content', () => {
3333
fixture.componentInstance.text = 'text';
3434
fixture.detectChanges();
3535
});
36-
});
3736

38-
describe('child text content change', () => {
39-
it('should call the registered for changes function', done => {
37+
it('should trigger the callback when the content of the children changes', done => {
4038
let fixture = TestBed.createComponent(ComponentWithChildTextContent);
4139
fixture.detectChanges();
4240

@@ -52,6 +50,62 @@ describe('Observe content', () => {
5250
fixture.detectChanges();
5351
});
5452
});
53+
54+
describe('debounced', () => {
55+
let fixture: ComponentFixture<ComponentWithDebouncedListener>;
56+
let instance: ComponentWithDebouncedListener;
57+
let callbacks: Function[];
58+
let fakeMutationObserver: {
59+
create(callback: MutationCallback): any;
60+
invokeCallbacks: (args: any[]) => void;
61+
};
62+
63+
beforeEach(async(() => {
64+
callbacks = [];
65+
66+
fakeMutationObserver = {
67+
invokeCallbacks: (args?: any[]) => callbacks.forEach(callback => callback(args)),
68+
create: (callback: MutationCallback) => {
69+
callbacks.push(callback);
70+
71+
return {
72+
observe: () => {},
73+
disconnect: () => {}
74+
};
75+
}
76+
};
77+
78+
TestBed.configureTestingModule({
79+
imports: [ObserveContentModule],
80+
declarations: [ComponentWithDebouncedListener],
81+
providers: [{ provide: MutationObserverFactory, useValue: fakeMutationObserver }]
82+
});
83+
84+
TestBed.compileComponents();
85+
86+
fixture = TestBed.createComponent(ComponentWithDebouncedListener);
87+
instance = fixture.componentInstance;
88+
fixture.detectChanges();
89+
}));
90+
91+
it('should debounce the content changes', fakeAsync(() => {
92+
fakeMutationObserver.invokeCallbacks([1]);
93+
fakeMutationObserver.invokeCallbacks([2]);
94+
fakeMutationObserver.invokeCallbacks([3]);
95+
96+
tick(500);
97+
expect(instance.spy).toHaveBeenCalledTimes(1);
98+
}));
99+
100+
it('should should keep track of all of the mutation records', fakeAsync(() => {
101+
fakeMutationObserver.invokeCallbacks([1]);
102+
fakeMutationObserver.invokeCallbacks([2]);
103+
fakeMutationObserver.invokeCallbacks([3]);
104+
105+
tick(500);
106+
expect(instance.spy).toHaveBeenCalledWith([1, 2, 3]);
107+
}));
108+
});
55109
});
56110

57111

@@ -66,3 +120,11 @@ class ComponentWithChildTextContent {
66120
text = '';
67121
doSomething() {}
68122
}
123+
124+
@Component({
125+
template: `<div (cdkObserveContent)="spy($event)" [debounce]="debounce">{{text}}</div>`
126+
})
127+
class ComponentWithDebouncedListener {
128+
debounce = 500;
129+
spy = jasmine.createSpy('MutationObserver callback');
130+
}

src/lib/core/observe-content/observe-content.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,22 @@ import {
77
Input,
88
EventEmitter,
99
OnDestroy,
10-
AfterContentInit
10+
AfterContentInit,
1111
} from '@angular/core';
1212

1313
import {debounce} from '../util/debounce';
1414

15+
/**
16+
* Factory class to create MutationObserver instances. Useful for being able to
17+
* stub out the MutationObserver for unit tests.
18+
* @docs-private
19+
*/
20+
export class MutationObserverFactory {
21+
create(callback: MutationCallback): MutationObserver {
22+
return new MutationObserver(callback);
23+
}
24+
}
25+
1526
/**
1627
* Directive that triggers a callback whenever the content of
1728
* its associated element has changed.
@@ -31,7 +42,9 @@ export class ObserveContent implements AfterContentInit, OnDestroy {
3142
/** Debounce interval for emitting the changes. */
3243
@Input() debounce: number;
3344

34-
constructor(private _elementRef: ElementRef) {}
45+
constructor(
46+
private _elementRef: ElementRef,
47+
private _observerFactory: MutationObserverFactory) { }
3548

3649
ngAfterContentInit() {
3750
let callback: MutationCallback;
@@ -51,7 +64,7 @@ export class ObserveContent implements AfterContentInit, OnDestroy {
5164
callback = (mutations: MutationRecord[]) => this.event.emit(mutations);
5265
}
5366

54-
this._observer = new MutationObserver(callback);
67+
this._observer = this._observerFactory.create(callback);
5568

5669
this._observer.observe(this._elementRef.nativeElement, {
5770
characterData: true,
@@ -70,7 +83,8 @@ export class ObserveContent implements AfterContentInit, OnDestroy {
7083

7184
@NgModule({
7285
exports: [ObserveContent],
73-
declarations: [ObserveContent]
86+
declarations: [ObserveContent],
87+
providers: [MutationObserverFactory]
7488
})
7589
export class ObserveContentModule {
7690
static forRoot(): ModuleWithProviders {

0 commit comments

Comments
 (0)