From c3e5eb634ea57e57850edd6892976a537d5f3098 Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 24 Jan 2018 15:03:21 +0000 Subject: [PATCH] [tests][auth] Tests for onAuthStateChange, onIdTokenChanged and onUserChanged --- lib/modules/auth/index.js | 86 +++------- tests/src/tests/auth/authTests.js | 260 ++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+), 61 deletions(-) diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index 427b9c72..b3679d95 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -37,6 +37,7 @@ type ActionCodeInfo = { const NATIVE_EVENTS = [ 'auth_state_changed', + 'auth_id_token_changed', 'phone_auth_state_changed', ]; @@ -63,37 +64,36 @@ export default class Auth extends ModuleBase { // sub to internal native event - this fans out to // public event name: onAuthStateChanged getAppEventName(this, 'auth_state_changed'), - this._onInternalAuthStateChanged.bind(this), + (state: AuthState) => { + this._setUser(state.user); + SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user); + }, ); SharedEventEmitter.addListener( // sub to internal native event - this fans out to // public events based on event.type getAppEventName(this, 'phone_auth_state_changed'), - this._onInternalPhoneAuthStateChanged.bind(this), + (event: Object) => { + const eventKey = `phone:auth:${event.requestKey}:${event.type}`; + SharedEventEmitter.emit(eventKey, event.state); + }, ); SharedEventEmitter.addListener( // sub to internal native event - this fans out to // public event name: onIdTokenChanged getAppEventName(this, 'auth_id_token_changed'), - this._onInternalIdTokenChanged.bind(this), + (auth: AuthState) => { + this._setUser(auth.user); + SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user); + }, ); getNativeModule(this).addAuthStateListener(); getNativeModule(this).addIdTokenListener(); } - /** - * Route a phone state change event to the correct listeners - * @param event - * @private - */ - _onInternalPhoneAuthStateChanged(event: Object) { - const eventKey = `phone:auth:${event.requestKey}:${event.type}`; - SharedEventEmitter.emit(eventKey, event.state); - } - _setUser(user: ?NativeUser): ?User { this._authResult = true; this._user = user ? new User(this, user) : null; @@ -112,27 +112,6 @@ export default class Auth extends ModuleBase { }; } - /** - * Internal auth changed listener - * @param auth - * @private - */ - _onInternalAuthStateChanged(auth: AuthState) { - this._setUser(auth.user); - SharedEventEmitter.emit(getAppEventName(this, 'onAuthStateChanged'), this._user); - } - - /** - * Internal auth changed listener - * @param auth - * @param emit - * @private - */ - _onInternalIdTokenChanged(auth: AuthState) { - this._setUser(auth.user); - SharedEventEmitter.emit(getAppEventName(this, 'onIdTokenChanged'), this._user); - } - /* * WEB API */ @@ -145,16 +124,11 @@ export default class Auth extends ModuleBase { getLogger(this).info('Creating onAuthStateChanged listener'); SharedEventEmitter.addListener(getAppEventName(this, 'onAuthStateChanged'), listener); if (this._authResult) listener(this._user || null); - return this._offAuthStateChanged.bind(this, listener); - } - /** - * Remove auth change listener - * @param listener - */ - _offAuthStateChanged(listener: Function) { - getLogger(this).info('Removing onAuthStateChanged listener'); - SharedEventEmitter.removeListener(getAppEventName(this, 'onAuthStateChanged'), listener); + return () => { + getLogger(this).info('Removing onAuthStateChanged listener'); + SharedEventEmitter.removeListener(getAppEventName(this, 'onAuthStateChanged'), listener); + }; } /** @@ -165,16 +139,11 @@ export default class Auth extends ModuleBase { getLogger(this).info('Creating onIdTokenChanged listener'); SharedEventEmitter.addListener(getAppEventName(this, 'onIdTokenChanged'), listener); if (this._authResult) listener(this._user || null); - return this._offIdTokenChanged.bind(this, listener); - } - /** - * Remove id token change listener - * @param listener - */ - _offIdTokenChanged(listener: Function) { - getLogger(this).info('Removing onIdTokenChanged listener'); - SharedEventEmitter.removeListener(getAppEventName(this, 'onIdTokenChanged'), listener); + return () => { + getLogger(this).info('Removing onIdTokenChanged listener'); + SharedEventEmitter.removeListener(getAppEventName(this, 'onIdTokenChanged'), listener); + }; } /** @@ -185,16 +154,11 @@ export default class Auth extends ModuleBase { getLogger(this).info('Creating onUserChanged listener'); SharedEventEmitter.addListener(getAppEventName(this, 'onUserChanged'), listener); if (this._authResult) listener(this._user || null); - return this._offUserChanged.bind(this, listener); - } - /** - * Remove user change listener - * @param listener - */ - _offUserChanged(listener: Function) { - getLogger(this).info('Removing onUserChanged listener'); - SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener); + return () => { + getLogger(this).info('Removing onUserChanged listener'); + SharedEventEmitter.removeListener(getAppEventName(this, 'onUserChanged'), listener); + }; } /** diff --git a/tests/src/tests/auth/authTests.js b/tests/src/tests/auth/authTests.js index 7d64b3b4..22cfd749 100644 --- a/tests/src/tests/auth/authTests.js +++ b/tests/src/tests/auth/authTests.js @@ -1,3 +1,5 @@ +import sinon from 'sinon'; +import 'should-sinon'; import should from 'should'; function randomString(length, chars) { @@ -12,6 +14,264 @@ function randomString(length, chars) { } function authTests({ tryCatch, describe, it, firebase }) { + describe('onAuthStateChanged', () => { + it('calls callback with the current user and when auth state changes', async () => { + await firebase.native.auth().signInAnonymously(); + + // Test + const callback = sinon.spy(); + + let unsubscribe; + await new Promise((resolve) => { + unsubscribe = firebase.native.auth().onAuthStateChanged((user) => { + callback(user); + resolve(); + }); + }); + + callback.should.be.calledWith(firebase.native.auth().currentUser); + callback.should.be.calledOnce(); + + // Sign out + + await firebase.native.auth().signOut(); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + // Assertions + + callback.should.be.calledWith(null); + callback.should.be.calledTwice(); + + // Tear down + + unsubscribe(); + }); + + it('stops listening when unsubscribed', async () => { + await firebase.native.auth().signInAnonymously(); + + // Test + const callback = sinon.spy(); + + let unsubscribe; + await new Promise((resolve) => { + unsubscribe = firebase.native.auth().onAuthStateChanged((user) => { + callback(user); + resolve(); + }); + }); + + callback.should.be.calledWith(firebase.native.auth().currentUser); + callback.should.be.calledOnce(); + + // Sign out + + await firebase.native.auth().signOut(); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + // Assertions + + callback.should.be.calledWith(null); + callback.should.be.calledTwice(); + + // Unsubscribe + + unsubscribe(); + + // Sign back in + + await firebase.native.auth().signInAnonymously(); + + // Assertions + + callback.should.be.calledTwice(); + + // Tear down + + await firebase.native.auth().signOut(); + }); + }); + + describe('onIdTokenChanged', () => { + it('calls callback with the current user and when auth state changes', async () => { + await firebase.native.auth().signInAnonymously(); + + // Test + const callback = sinon.spy(); + + let unsubscribe; + await new Promise((resolve) => { + unsubscribe = firebase.native.auth().onIdTokenChanged((user) => { + callback(user); + resolve(); + }); + }); + + callback.should.be.calledWith(firebase.native.auth().currentUser); + callback.should.be.calledOnce(); + + // Sign out + + await firebase.native.auth().signOut(); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + // Assertions + + callback.should.be.calledWith(null); + callback.should.be.calledTwice(); + + // Tear down + + unsubscribe(); + }); + + it('stops listening when unsubscribed', async () => { + await firebase.native.auth().signInAnonymously(); + + // Test + const callback = sinon.spy(); + + let unsubscribe; + await new Promise((resolve) => { + unsubscribe = firebase.native.auth().onIdTokenChanged((user) => { + callback(user); + resolve(); + }); + }); + + callback.should.be.calledWith(firebase.native.auth().currentUser); + callback.should.be.calledOnce(); + + // Sign out + + await firebase.native.auth().signOut(); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + // Assertions + + callback.should.be.calledWith(null); + callback.should.be.calledTwice(); + + // Unsubscribe + + unsubscribe(); + + // Sign back in + + await firebase.native.auth().signInAnonymously(); + + // Assertions + + callback.should.be.calledTwice(); + + // Tear down + + await firebase.native.auth().signOut(); + }); + }); + + describe('onUserChanged', () => { + it('calls callback with the current user and when auth state changes', async () => { + await firebase.native.auth().signInAnonymously(); + + // Test + const callback = sinon.spy(); + + let unsubscribe; + await new Promise((resolve) => { + unsubscribe = firebase.native.auth().onUserChanged((user) => { + callback(user); + resolve(); + }); + }); + + callback.should.be.calledWith(firebase.native.auth().currentUser); + callback.should.be.calledOnce(); + + // Sign out + + await firebase.native.auth().signOut(); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + // Assertions + + callback.should.be.calledWith(null); + // Because of the way onUserChanged works, it will be called double + // - once for onAuthStateChanged + // - once for onIdTokenChanged + callback.should.have.callCount(4); + + // Tear down + + unsubscribe(); + }); + + it('stops listening when unsubscribed', async () => { + await firebase.native.auth().signInAnonymously(); + + // Test + const callback = sinon.spy(); + + let unsubscribe; + await new Promise((resolve) => { + unsubscribe = firebase.native.auth().onUserChanged((user) => { + callback(user); + resolve(); + }); + }); + + callback.should.be.calledWith(firebase.native.auth().currentUser); + callback.should.be.calledOnce(); + + // Sign out + + await firebase.native.auth().signOut(); + + await new Promise((resolve) => { + setTimeout(() => resolve(), 5); + }); + + // Assertions + + callback.should.be.calledWith(null); + // Because of the way onUserChanged works, it will be called double + // - once for onAuthStateChanged + // - once for onIdTokenChanged + callback.should.have.callCount(4); + + // Unsubscribe + + unsubscribe(); + + // Sign back in + + await firebase.native.auth().signInAnonymously(); + + // Assertions + + callback.should.have.callCount(4); + + // Tear down + + await firebase.native.auth().signOut(); + }); + }); + describe('signInAnonymously', () => { it('it should sign in anonymously', () => { const successCb = (currentUser) => {