Skip to content

Commit 1611876

Browse files
authored
Merge pull request #1 from cloudnc/fea/tests
2 parents 0c33f98 + 42e9257 commit 1611876

File tree

10 files changed

+577
-53
lines changed

10 files changed

+577
-53
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ script:
2727
- yarn prettier:check
2828
- yarn readme:check
2929
# tests
30-
# - yarn lib:test:ci
30+
- yarn lib:test:ci
3131
# build
3232
- yarn lib:build:prod
3333
# prep deploy

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public timer$ = interval(500).pipe(automaticUnsubscribe(this));
4242
To build this lib, first we need to create a decorator that the implementing developers can add to their component.
4343

4444
```ts
45-
// ./src/app/lib-example/lib-example.ts#L1-L11
45+
// ./src/app/lib-example/lib-example.ts#L1-L14
4646
4747
import { decorateObservableLifecycle, getLifecycleHooks } from 'ngx-observable-lifecycle';
4848
import { Observable } from 'rxjs';
@@ -51,7 +51,10 @@ import { takeUntil } from 'rxjs/operators';
5151
export function AutomaticUnsubscribe(): ClassDecorator {
5252
return function (target) {
5353
decorateObservableLifecycle(target, {
54-
onDestroy: true,
54+
hooks: {
55+
onDestroy: true,
56+
},
57+
incompatibleComponentError: new Error(`You must use @AutomaticUnsubscribe with a directive or component.`),
5558
});
5659
};
5760
}
@@ -63,13 +66,19 @@ lifecycle hooks we want to observe (in this case, just `onDestroy`)
6366
Lastly to implement the rxjs operator itself, we do the following:
6467

6568
```ts
66-
// ./src/app/lib-example/lib-example.ts#L13-L20
69+
// ./src/app/lib-example/lib-example.ts#L13-L26
70+
71+
};
72+
}
6773
6874
export function automaticUnsubscribe<T>(component): (source: Observable<T>) => Observable<T> {
6975
const { onDestroy } = getLifecycleHooks(component, {
7076
missingDecoratorError: new Error(
7177
'You must decorate the component or interface with @AutomaticUnsubscribe for automaticUnsubscribe to be able to function!',
7278
),
79+
incompatibleComponentError: new Error(
80+
`You must use automaticUnsubscribe with a directive or component. This type (${component?.constructor.name}) is not compatible with automaticUnsubscribe!`,
81+
),
7382
});
7483
return (source: Observable<T>): Observable<T> => source.pipe(takeUntil(onDestroy));
7584
}

karma.conf.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ module.exports = function (config) {
2626
logLevel: config.LOG_INFO,
2727
autoWatch: true,
2828
browsers: ['Chrome'],
29+
customLaunchers: {
30+
ChromeHeadlessNoSandbox: {
31+
base: 'ChromeHeadless',
32+
flags: ['--no-sandbox'],
33+
},
34+
},
2935
singleRun: false,
3036
restartOnFileChange: true,
3137
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"lib:build:prod": "yarn run ng build --project ngx-observable-lifecycle --prod",
2222
"lib:build:watch": "yarn run lib:build:prod --watch",
2323
"lib:test:watch": "yarn run ng test --project ngx-observable-lifecycle",
24-
"lib:test:ci": "yarn run ng test --project ngx-observable-lifecycle --watch false",
24+
"lib:test:ci": "yarn run ng test --project ngx-observable-lifecycle --watch false --browsers ChromeHeadless",
2525
"------------------ RELEASE COMMANDS ------------------": "",
2626
"semantic-release": "semantic-release",
2727
"readme:build": "embedme README.md",

projects/ngx-observable-lifecycle/karma.conf.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ module.exports = function (config) {
2626
logLevel: config.LOG_INFO,
2727
autoWatch: true,
2828
browsers: ['Chrome'],
29+
customLaunchers: {
30+
ChromeHeadlessNoSandbox: {
31+
base: 'ChromeHeadless',
32+
flags: ['--no-sandbox'],
33+
},
34+
},
2935
singleRun: false,
3036
restartOnFileChange: true,
3137
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const NG_COMPONENT_DEF = 'ɵcmp';
2+
export const NG_DIRECTIVE_DEF = 'ɵdir';
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import { CommonModule } from '@angular/common';
2+
import {
3+
AfterContentChecked,
4+
AfterContentInit,
5+
AfterViewChecked,
6+
AfterViewInit,
7+
ChangeDetectionStrategy,
8+
Component,
9+
DoCheck,
10+
OnChanges,
11+
OnDestroy,
12+
OnInit,
13+
} from '@angular/core';
14+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
15+
import { getObservableLifecycle, ObservableLifecycle } from './ngx-observable-lifecycle';
16+
17+
describe('integration', () => {
18+
type ObserverSpy = {
19+
next: jasmine.Spy;
20+
complete: jasmine.Spy;
21+
error: jasmine.Spy;
22+
};
23+
24+
let ngAfterContentCheckedSpy: jasmine.Spy;
25+
let ngAfterContentInitSpy: jasmine.Spy;
26+
let ngAfterViewCheckedSpy: jasmine.Spy;
27+
let ngAfterViewInitSpy: jasmine.Spy;
28+
let ngDoCheckSpy: jasmine.Spy;
29+
let ngOnChangesSpy: jasmine.Spy;
30+
let ngOnDestroySpy: jasmine.Spy;
31+
let ngOnInitSpy: jasmine.Spy;
32+
33+
let onChanges$Spy: ObserverSpy;
34+
let onInit$Spy: ObserverSpy;
35+
let doCheck$Spy: ObserverSpy;
36+
let afterContentInit$Spy: ObserverSpy;
37+
let afterContentChecked$Spy: ObserverSpy;
38+
let afterViewInit$Spy: ObserverSpy;
39+
let afterViewChecked$Spy: ObserverSpy;
40+
let onDestroy$Spy: ObserverSpy;
41+
42+
// tslint:disable:no-conflicting-lifecycle
43+
@ObservableLifecycle()
44+
@Component({
45+
selector: 'lib-test-component',
46+
template: 'test-component',
47+
changeDetection: ChangeDetectionStrategy.OnPush,
48+
})
49+
class TestComponent
50+
implements
51+
OnDestroy,
52+
OnInit,
53+
DoCheck,
54+
OnChanges,
55+
AfterViewInit,
56+
AfterViewChecked,
57+
AfterContentChecked,
58+
AfterContentInit {
59+
public ngAfterContentChecked(): void {
60+
ngAfterContentCheckedSpy();
61+
}
62+
63+
public ngAfterContentInit(): void {
64+
ngAfterContentInitSpy();
65+
}
66+
67+
public ngAfterViewChecked(): void {
68+
ngAfterViewCheckedSpy();
69+
}
70+
71+
public ngAfterViewInit(): void {
72+
ngAfterViewInitSpy();
73+
}
74+
75+
public ngDoCheck(): void {
76+
ngDoCheckSpy();
77+
}
78+
79+
public ngOnChanges(): void {
80+
ngOnChangesSpy();
81+
}
82+
83+
public ngOnDestroy(): void {
84+
ngOnDestroySpy();
85+
}
86+
87+
public ngOnInit(): void {
88+
ngOnInitSpy();
89+
}
90+
91+
constructor() {
92+
const {
93+
onChanges,
94+
onInit,
95+
doCheck,
96+
afterContentInit,
97+
afterContentChecked,
98+
afterViewInit,
99+
afterViewChecked,
100+
onDestroy,
101+
} = getObservableLifecycle(this);
102+
103+
onChanges.subscribe(onChanges$Spy);
104+
onInit.subscribe(onInit$Spy);
105+
doCheck.subscribe(doCheck$Spy);
106+
afterContentInit.subscribe(afterContentInit$Spy);
107+
afterContentChecked.subscribe(afterContentChecked$Spy);
108+
afterViewInit.subscribe(afterViewInit$Spy);
109+
afterViewChecked.subscribe(afterViewChecked$Spy);
110+
onDestroy.subscribe(onDestroy$Spy);
111+
}
112+
}
113+
114+
@Component({
115+
selector: 'lib-host-component',
116+
template: `
117+
<h1>Host Component</h1>
118+
<lib-test-component *ngIf="testComponentVisible"></lib-test-component>
119+
`,
120+
})
121+
class HostComponent {
122+
public testComponentVisible = false;
123+
124+
public setTestComponentVisible(visible: boolean) {
125+
this.testComponentVisible = visible;
126+
}
127+
}
128+
129+
let component: HostComponent;
130+
let fixture: ComponentFixture<HostComponent>;
131+
132+
beforeEach(async(() => {
133+
TestBed.configureTestingModule({
134+
imports: [CommonModule],
135+
declarations: [HostComponent, TestComponent],
136+
}).compileComponents();
137+
}));
138+
139+
beforeEach(() => {
140+
ngAfterContentCheckedSpy = jasmine.createSpy('ngAfterContentChecked');
141+
ngAfterContentInitSpy = jasmine.createSpy('ngAfterContentInit');
142+
ngAfterViewCheckedSpy = jasmine.createSpy('ngAfterViewChecked');
143+
ngAfterViewInitSpy = jasmine.createSpy('ngAfterViewInit');
144+
ngDoCheckSpy = jasmine.createSpy('ngDoCheck');
145+
ngOnChangesSpy = jasmine.createSpy('ngOnChanges');
146+
ngOnDestroySpy = jasmine.createSpy('ngOnDestroy');
147+
ngOnInitSpy = jasmine.createSpy('ngOnInit');
148+
149+
const observerSpy = (name: string) => jasmine.createSpyObj(name, ['next', 'error', 'complete']);
150+
151+
onChanges$Spy = observerSpy('onChanges$Spy');
152+
onInit$Spy = observerSpy('onInit$Spy');
153+
doCheck$Spy = observerSpy('doCheck$Spy');
154+
afterContentInit$Spy = observerSpy('afterContentInit$Spy');
155+
afterContentChecked$Spy = observerSpy('afterContentChecked$Spy');
156+
afterViewInit$Spy = observerSpy('afterViewInit$Spy');
157+
afterViewChecked$Spy = observerSpy('afterViewChecked$Spy');
158+
onDestroy$Spy = observerSpy('onDestroy$Spy');
159+
160+
fixture = TestBed.createComponent(HostComponent);
161+
component = fixture.componentInstance;
162+
fixture.detectChanges();
163+
});
164+
165+
it('should be created', () => {
166+
expect(component).toBeTruthy();
167+
});
168+
169+
it('should not have called any of the spies', () => {
170+
expect(ngAfterContentCheckedSpy).not.toHaveBeenCalled();
171+
expect(ngAfterContentInitSpy).not.toHaveBeenCalled();
172+
expect(ngAfterViewCheckedSpy).not.toHaveBeenCalled();
173+
expect(ngAfterViewInitSpy).not.toHaveBeenCalled();
174+
expect(ngDoCheckSpy).not.toHaveBeenCalled();
175+
expect(ngOnChangesSpy).not.toHaveBeenCalled();
176+
expect(ngOnDestroySpy).not.toHaveBeenCalled();
177+
expect(ngOnInitSpy).not.toHaveBeenCalled();
178+
179+
expect(onChanges$Spy.next).not.toHaveBeenCalled();
180+
expect(onInit$Spy.next).not.toHaveBeenCalled();
181+
expect(doCheck$Spy.next).not.toHaveBeenCalled();
182+
expect(afterContentInit$Spy.next).not.toHaveBeenCalled();
183+
expect(afterContentChecked$Spy.next).not.toHaveBeenCalled();
184+
expect(afterViewInit$Spy.next).not.toHaveBeenCalled();
185+
expect(afterViewChecked$Spy.next).not.toHaveBeenCalled();
186+
expect(onDestroy$Spy.next).not.toHaveBeenCalled();
187+
});
188+
189+
it('should observe the init lifecycle', () => {
190+
expect(onInit$Spy.next).not.toHaveBeenCalled();
191+
expect(ngOnInitSpy).not.toHaveBeenCalled();
192+
component.setTestComponentVisible(true);
193+
194+
fixture.detectChanges();
195+
196+
expect(ngOnInitSpy).toHaveBeenCalledTimes(1);
197+
expect(onInit$Spy.next).toHaveBeenCalledTimes(1);
198+
component.setTestComponentVisible(false);
199+
fixture.detectChanges();
200+
expect(ngOnInitSpy).toHaveBeenCalledTimes(1);
201+
expect(onInit$Spy.next).toHaveBeenCalledTimes(1);
202+
component.setTestComponentVisible(true);
203+
fixture.detectChanges();
204+
expect(ngOnInitSpy).toHaveBeenCalledTimes(2);
205+
expect(onInit$Spy.next).toHaveBeenCalledTimes(2);
206+
});
207+
208+
it('should observe the destroy lifecycle', () => {
209+
expect(onDestroy$Spy.next).not.toHaveBeenCalled();
210+
expect(ngOnDestroySpy).not.toHaveBeenCalled();
211+
component.setTestComponentVisible(true);
212+
fixture.detectChanges();
213+
214+
expect(onDestroy$Spy.next).not.toHaveBeenCalled();
215+
expect(ngOnDestroySpy).not.toHaveBeenCalled();
216+
217+
component.setTestComponentVisible(false);
218+
fixture.detectChanges();
219+
220+
expect(onDestroy$Spy.next).toHaveBeenCalledTimes(1);
221+
expect(onDestroy$Spy.complete).toHaveBeenCalledTimes(1);
222+
expect(ngOnDestroySpy).toHaveBeenCalledTimes(1);
223+
});
224+
});

0 commit comments

Comments
 (0)