Skip to content

Commit 91b4e94

Browse files
jamesdanielsalan-agius4wagnermacielValentinFunk
authored
Merged master and working on specs (#2298)
* merged master * registerVersion is actually on firebase object * version replacements and getting specs working Co-authored-by: Alan Agius <[email protected]> Co-authored-by: Wagner Maciel <[email protected]> Co-authored-by: Valentin Funk <[email protected]>
1 parent 3b79df8 commit 91b4e94

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+6377
-3610
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
"private": true,
66
"scripts": {
77
"test": "npm run test:node",
8-
"test:chrome": "npx firebase emulators:exec --project=angularfire2-test \"npx ng test --watch=false --browsers=Chrome\"",
8+
"test:watch": "npx firebase emulators:exec --project=angularfire2-test \"npx ng test --watch=true --browsers=Chrome\"",
9+
"test:chrome": "npx firebase emulators:exec --project=angularfire2-test \"npx ng test --watch=false --browsers=Chrome\"",
910
"test:chrome-headless": "npx firebase emulators:exec --project=angularfire2-test \"npx ng test --watch=false --browsers=ChromeHeadless\"",
1011
"lint": "npx ng lint",
1112
"test:node": "npx tsc -p tsconfig.jasmine.json; cp ./dist/packages-dist/schematics/versions.json ./dist/out-tsc/jasmine/schematics && npx firebase emulators:exec --project=angularfire2-test \"node -r tsconfig-paths/register ./tools/jasmine.js\"",
1213
"test:typings": "node ./tools/run-typings-test.js",
1314
"test:build": "bash ./test/ng-build/build.sh",
14-
"test:universal": "cp -R dist/packages-dist test/universal-test/node_modules/angularfire2 && cd test/universal-test && npm run prerender",
15+
"test:universal": "npm run build && cd test/universal-test && yarn && npm run prerender",
1516
"test:all": "npm run test:node && npm run test:chrome-headless && npm run test:typings && npm run test:build",
1617
"build": "tsc tools/build.ts; node ./tools/build.js",
1718
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1"
@@ -89,6 +90,7 @@
8990
"pretty-size": "^2.0.0",
9091
"protractor": "3.0.0",
9192
"reflect-metadata": "0.1.2",
93+
"replace-in-file": "^5.0.2",
9294
"rimraf": "^2.5.4",
9395
"schematics-utilities": "^2.0.1",
9496
"shelljs": "^0.8.0",

src/analytics/analytics.service.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Injectable, Optional, NgZone, OnDestroy, ComponentFactoryResolver, Inject, PLATFORM_ID, Injector, NgModuleFactory } from '@angular/core';
22
import { Subscription, from, Observable, of } from 'rxjs';
3-
import { filter, withLatestFrom, switchMap, map, tap, pairwise, startWith, groupBy, mergeMap } from 'rxjs/operators';
3+
import { filter, withLatestFrom, switchMap, map, tap, pairwise, startWith, groupBy, mergeMap, observeOn } from 'rxjs/operators';
44
import { Router, NavigationEnd, ActivationEnd, ROUTES } from '@angular/router';
5-
import { ɵrunOutsideAngular } from '@angular/fire';
5+
import { ɵAngularFireSchedulers } from '@angular/fire';
66
import { AngularFireAnalytics, DEBUG_MODE } from './analytics';
77
import { User } from 'firebase/app';
88
import { Title } from '@angular/platform-browser';
@@ -164,15 +164,17 @@ export class UserTrackingService implements OnDestroy {
164164
zone: NgZone,
165165
@Inject(PLATFORM_ID) platformId:Object
166166
) {
167+
const schedulers = new ɵAngularFireSchedulers(zone);
168+
167169
if (!isPlatformServer(platformId)) {
168170
zone.runOutsideAngular(() => {
169171
// @ts-ignore zap the import in the UMD
170172
this.disposable = from(import('firebase/auth')).pipe(
173+
observeOn(schedulers.outsideAngular),
171174
switchMap(() => analytics.app),
172175
map(app => app.auth()),
173176
switchMap(auth => new Observable<User|null>(auth.onAuthStateChanged.bind(auth))),
174-
switchMap(user => analytics.setUserId(user ? user.uid : null!)),
175-
ɵrunOutsideAngular(zone)
177+
switchMap(user => analytics.setUserId(user ? user.uid : null!))
176178
).subscribe();
177179
});
178180
}

src/analytics/analytics.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Injectable, Inject, Optional, NgZone, InjectionToken, PLATFORM_ID } from '@angular/core';
2-
import { of, empty, throwError } from 'rxjs';
2+
import { of, empty } from 'rxjs';
33
import { isPlatformBrowser } from '@angular/common';
4-
import { map, tap, shareReplay, switchMap, catchError } from 'rxjs/operators';
5-
import { FirebaseAppConfig, FirebaseOptions, ɵrunOutsideAngular, ɵlazySDKProxy, FIREBASE_OPTIONS, FIREBASE_APP_NAME, ɵfirebaseAppFactory, ɵPromiseProxy } from '@angular/fire';
4+
import { map, tap, shareReplay, switchMap, observeOn } from 'rxjs/operators';
5+
import { FirebaseAppConfig, FirebaseOptions, ɵAngularFireSchedulers, ɵlazySDKProxy, FIREBASE_OPTIONS, FIREBASE_APP_NAME, ɵfirebaseAppFactory, ɵPromiseProxy } from '@angular/fire';
66
import { analytics } from 'firebase';
77

88
export interface Config {[key:string]: any};
@@ -48,6 +48,8 @@ export class AngularFireAnalytics {
4848
zone: NgZone
4949
) {
5050

51+
const schedulers = new ɵAngularFireSchedulers(zone);
52+
5153
if (isPlatformBrowser(platformId)) {
5254

5355
window[DATA_LAYER_NAME] = window[DATA_LAYER_NAME] || [];
@@ -74,19 +76,18 @@ export class AngularFireAnalytics {
7476
if (debugModeEnabled) { this.updateConfig({ [DEBUG_MODE_KEY]: 1 }) }
7577

7678
const analytics = of(undefined).pipe(
77-
switchMap(() => zone.runOutsideAngular(() => import('firebase/analytics'))),
78-
catchError(err => err.message === 'Not supported' ? empty() : throwError(err) ),
79+
observeOn(schedulers.outsideAngular),
80+
switchMap(() => isPlatformBrowser(platformId) ? import('firebase/analytics') : empty()),
7981
map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)),
8082
map(app => app.analytics()),
8183
tap(analytics => {
8284
if (analyticsCollectionEnabled === false) { analytics.setAnalyticsCollectionEnabled(false) }
8385
}),
84-
ɵrunOutsideAngular(zone),
8586
shareReplay({ bufferSize: 1, refCount: false }),
8687
);
8788

8889
return ɵlazySDKProxy(this, analytics, zone);
89-
90+
9091
}
9192

9293
}

src/auth/auth.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Injectable, Inject, Optional, NgZone, PLATFORM_ID } from '@angular/core';
22
import { Observable, of, from } from 'rxjs';
3-
import { switchMap, shareReplay, map } from 'rxjs/operators';
4-
import { FIREBASE_OPTIONS, FIREBASE_APP_NAME, FirebaseOptions, FirebaseAppConfig, ɵfirebaseAppFactory, ɵFirebaseZoneScheduler, ɵrunOutsideAngular, ɵPromiseProxy, ɵlazySDKProxy } from '@angular/fire';
3+
import { switchMap, map, observeOn, shareReplay } from 'rxjs/operators';
4+
import { FIREBASE_OPTIONS, FIREBASE_APP_NAME, FirebaseOptions, FirebaseAppConfig, ɵPromiseProxy, ɵlazySDKProxy, ɵfirebaseAppFactory, ɵAngularFireSchedulers, ɵkeepUnstableUntilFirstFactory } from '@angular/fire';
55
import { User, auth } from 'firebase/app';
66

77
export interface AngularFireAuth extends ɵPromiseProxy<auth.Auth> {};
@@ -37,28 +37,29 @@ export class AngularFireAuth {
3737
@Inject(FIREBASE_OPTIONS) options:FirebaseOptions,
3838
@Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined,
3939
@Inject(PLATFORM_ID) platformId: Object,
40-
private zone: NgZone
40+
zone: NgZone
4141
) {
42-
const scheduler = new ɵFirebaseZoneScheduler(zone, platformId);
42+
const schedulers = new ɵAngularFireSchedulers(zone);
43+
const keepUnstableUntilFirst = ɵkeepUnstableUntilFirstFactory(schedulers, platformId);
4344

4445
const auth = of(undefined).pipe(
46+
observeOn(schedulers.outsideAngular),
4547
switchMap(() => zone.runOutsideAngular(() => import('firebase/auth'))),
4648
map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)),
4749
map(app => app.auth()),
48-
ɵrunOutsideAngular(zone),
4950
shareReplay({ bufferSize: 1, refCount: false }),
5051
);
5152

5253
this.authState = auth.pipe(
54+
observeOn(schedulers.outsideAngular),
5355
switchMap(auth => from(auth.onAuthStateChanged)),
54-
ɵrunOutsideAngular(zone),
55-
scheduler.keepUnstableUntilFirst.bind(scheduler),
56+
keepUnstableUntilFirst
5657
);
5758

5859
this.user = auth.pipe(
60+
observeOn(schedulers.outsideAngular),
5961
switchMap(auth => from(auth.onIdTokenChanged)),
60-
ɵrunOutsideAngular(zone),
61-
scheduler.keepUnstableUntilFirst.bind(scheduler),
62+
keepUnstableUntilFirst
6263
);
6364

6465
this.idToken = this.user.pipe(

src/core/angularfire2.spec.ts

Lines changed: 196 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { TestBed, inject } from '@angular/core/testing';
2-
import { PlatformRef, NgModule, CompilerFactory } from '@angular/core';
2+
import { PlatformRef, NgModule, CompilerFactory, NgZone } from '@angular/core';
33
import { FirebaseApp, AngularFireModule } from './public_api';
4-
import { Subscription } from 'rxjs';
4+
import { Subscription, Observable, Subject, of } from 'rxjs';
55
import { COMMON_CONFIG } from '../test-config';
66
import { BrowserModule } from '@angular/platform-browser';
77
import { database } from 'firebase/app';
8-
import 'firebase/database';
8+
import { ɵZoneScheduler, ɵkeepUnstableUntilFirstFactory, ɵAngularFireSchedulers } from './angularfire2';
9+
import { ɵPLATFORM_BROWSER_ID, ɵPLATFORM_SERVER_ID } from '@angular/common';
10+
import { tap } from 'rxjs/operators';
11+
import { TestScheduler } from 'rxjs/testing';
912

1013
describe('angularfire', () => {
1114
let subscription:Subscription;
@@ -41,6 +44,196 @@ describe('angularfire', () => {
4144
app.delete().then(done, done.fail);
4245
});
4346

47+
describe('ZoneScheduler', () => {
48+
it('should execute the scheduled work inside the specified zone', done => {
49+
let ngZone = Zone.current.fork({
50+
name: 'ngZone'
51+
});
52+
const rootZone = Zone.current;
53+
54+
// Mimic real behavior: Executing in Angular
55+
ngZone.run(() => {
56+
const outsideAngularScheduler = new ɵZoneScheduler(rootZone);
57+
outsideAngularScheduler.schedule(() => {
58+
expect(Zone.current.name).not.toEqual('ngZone');
59+
done();
60+
});
61+
});
62+
});
63+
64+
it('should execute nested scheduled work inside the specified zone', done => {
65+
const testScheduler = new TestScheduler(null!);
66+
testScheduler.run(helpers => {
67+
const outsideAngularScheduler = new ɵZoneScheduler(Zone.current, testScheduler);
68+
69+
let ngZone = Zone.current.fork({
70+
name: 'ngZone'
71+
});
72+
73+
let callbacksRan = 0;
74+
75+
// Mimic real behavior: Executing in Angular
76+
ngZone.run(() => {
77+
outsideAngularScheduler.schedule(() => {
78+
callbacksRan++;
79+
expect(Zone.current.name).not.toEqual('ngZone');
80+
81+
ngZone.run(() => {
82+
// Sync queueing
83+
outsideAngularScheduler.schedule(() => {
84+
callbacksRan++;
85+
expect(Zone.current.name).not.toEqual('ngZone');
86+
});
87+
88+
// Async (10ms delay) nested scheduling
89+
outsideAngularScheduler.schedule(() => {
90+
callbacksRan++;
91+
expect(Zone.current.name).not.toEqual('ngZone');
92+
}, 10);
93+
94+
// Simulate flush from inside angular-
95+
helpers.flush();
96+
done();
97+
expect(callbacksRan).toEqual(3);
98+
})
99+
});
100+
helpers.flush();
101+
});
102+
});
103+
})
104+
})
105+
106+
describe('keepUnstableUntilFirstFactory', () => {
107+
let schedulers: ɵAngularFireSchedulers;
108+
let outsideZone: Zone;
109+
let insideZone: Zone;
110+
beforeAll(() => {
111+
outsideZone = Zone.current;
112+
insideZone = Zone.current.fork({
113+
name: 'ngZone'
114+
});
115+
const ngZone = {
116+
run: insideZone.run.bind(insideZone),
117+
runGuarded: insideZone.runGuarded.bind(insideZone),
118+
runOutsideAngular: outsideZone.runGuarded.bind(outsideZone),
119+
runTask: insideZone.run.bind(insideZone)
120+
} as NgZone;
121+
schedulers = new ɵAngularFireSchedulers(ngZone);
122+
})
123+
124+
it('should re-schedule emissions asynchronously', done => {
125+
const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(schedulers, ɵPLATFORM_SERVER_ID);
126+
127+
let ran = false;
128+
of(null).pipe(
129+
keepUnstableOp,
130+
tap(() => ran = true)
131+
).subscribe(() => {
132+
expect(ran).toEqual(true);
133+
done();
134+
}, () => fail("Should not error"));
135+
136+
expect(ran).toEqual(false);
137+
});
138+
139+
[ɵPLATFORM_SERVER_ID, ɵPLATFORM_BROWSER_ID].map(platformId =>
140+
it(`should subscribe outside angular and observe inside angular (${platformId})`, done => {
141+
const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(schedulers, platformId);
142+
143+
insideZone.run(() => {
144+
new Observable(s => {
145+
expect(Zone.current).toEqual(outsideZone);
146+
s.next("test");
147+
}).pipe(
148+
keepUnstableOp,
149+
tap(() => {
150+
expect(Zone.current).toEqual(insideZone);
151+
})
152+
).subscribe(() => {
153+
expect(Zone.current).toEqual(insideZone);
154+
done();
155+
}, err => {
156+
fail(err);
157+
});
158+
});
159+
})
160+
);
161+
162+
it('should block until first emission on server platform', done => {
163+
const testScheduler = new TestScheduler(null!);
164+
testScheduler.run(helpers => {
165+
const outsideZone = Zone.current;
166+
const taskTrack = new Zone['TaskTrackingZoneSpec']();
167+
const insideZone = Zone.current.fork(taskTrack);
168+
const trackingSchedulers: ɵAngularFireSchedulers = {
169+
ngZone: {
170+
run: insideZone.run.bind(insideZone),
171+
runGuarded: insideZone.runGuarded.bind(insideZone),
172+
runOutsideAngular: outsideZone.runGuarded.bind(outsideZone),
173+
runTask: insideZone.run.bind(insideZone)
174+
} as NgZone,
175+
outsideAngular: new ɵZoneScheduler(outsideZone, testScheduler),
176+
insideAngular: new ɵZoneScheduler(insideZone, testScheduler),
177+
};
178+
const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(trackingSchedulers, ɵPLATFORM_SERVER_ID);
179+
180+
const s = new Subject();
181+
s.pipe(
182+
keepUnstableOp,
183+
).subscribe(() => { }, err => { fail(err); }, () => { });
184+
185+
// Flush to ensure all async scheduled functions are run
186+
helpers.flush();
187+
// Should now be blocked until first item arrives
188+
expect(taskTrack.macroTasks.length).toBe(1);
189+
expect(taskTrack.macroTasks[0].source).toBe('firebaseZoneBlock');
190+
191+
// Emit next item
192+
s.next(123);
193+
helpers.flush();
194+
195+
// Should not be blocked after first item
196+
expect(taskTrack.macroTasks.length).toBe(0);
197+
198+
done();
199+
});
200+
})
201+
202+
it('should not block on client platform', done => {
203+
const testScheduler = new TestScheduler(null!);
204+
testScheduler.run(helpers => {
205+
const outsideZone = Zone.current;
206+
const taskTrack = new Zone['TaskTrackingZoneSpec']();
207+
const insideZone = Zone.current.fork(taskTrack);
208+
const trackingSchedulers: ɵAngularFireSchedulers = {
209+
ngZone: {
210+
run: insideZone.run.bind(insideZone),
211+
runGuarded: insideZone.runGuarded.bind(insideZone),
212+
runOutsideAngular: outsideZone.runGuarded.bind(outsideZone),
213+
runTask: insideZone.run.bind(insideZone)
214+
} as NgZone,
215+
outsideAngular: new ɵZoneScheduler(outsideZone, testScheduler),
216+
insideAngular: new ɵZoneScheduler(insideZone, testScheduler),
217+
};
218+
const keepUnstableOp = ɵkeepUnstableUntilFirstFactory(trackingSchedulers, ɵPLATFORM_BROWSER_ID);
219+
220+
const s = new Subject();
221+
s.pipe(
222+
keepUnstableOp,
223+
).subscribe(() => { }, err => { fail(err); }, () => { });
224+
225+
// Flush to ensure all async scheduled functions are run
226+
helpers.flush();
227+
228+
// Zone should not be blocked
229+
expect(taskTrack.macroTasks.length).toBe(0);
230+
expect(taskTrack.microTasks.length).toBe(0);
231+
232+
done();
233+
});
234+
})
235+
})
236+
44237
describe('FirebaseApp', () => {
45238

46239
it('should provide a FirebaseApp for the FirebaseApp binding', () => {

0 commit comments

Comments
 (0)