|
16 | 16 | */
|
17 | 17 | import '../test/setup';
|
18 | 18 | import { expect } from 'chai';
|
19 |
| -import { stub } from 'sinon'; |
20 |
| -import { activate, setTokenAutoRefreshEnabled } from './api'; |
| 19 | +import { stub, spy } from 'sinon'; |
| 20 | +import { |
| 21 | + activate, |
| 22 | + setTokenAutoRefreshEnabled, |
| 23 | + getToken, |
| 24 | + onTokenChanged |
| 25 | +} from './api'; |
21 | 26 | import {
|
22 | 27 | FAKE_SITE_KEY,
|
23 | 28 | getFakeApp,
|
24 |
| - getFakeCustomTokenProvider |
| 29 | + getFakeCustomTokenProvider, |
| 30 | + getFakePlatformLoggingProvider, |
| 31 | + removegreCAPTCHAScriptsOnPage |
25 | 32 | } from '../test/util';
|
26 |
| -import { getState } from './state'; |
| 33 | +import { clearState, getState } from './state'; |
27 | 34 | import * as reCAPTCHA from './recaptcha';
|
28 | 35 | import { FirebaseApp } from '@firebase/app-types';
|
| 36 | +import * as internalApi from './internal-api'; |
| 37 | +import * as client from './client'; |
| 38 | +import * as storage from './storage'; |
| 39 | +import * as logger from './logger'; |
29 | 40 |
|
30 | 41 | describe('api', () => {
|
31 | 42 | describe('activate()', () => {
|
@@ -86,4 +97,173 @@ describe('api', () => {
|
86 | 97 | expect(getState(app).isTokenAutoRefreshEnabled).to.equal(true);
|
87 | 98 | });
|
88 | 99 | });
|
| 100 | + describe('getToken()', () => { |
| 101 | + it('getToken() calls the internal getToken() function', async () => { |
| 102 | + const app = getFakeApp({ automaticDataCollectionEnabled: true }); |
| 103 | + const fakePlatformLoggingProvider = getFakePlatformLoggingProvider(); |
| 104 | + const internalGetToken = stub(internalApi, 'getToken').resolves({ |
| 105 | + token: 'a-token-string' |
| 106 | + }); |
| 107 | + await getToken(app, fakePlatformLoggingProvider, true); |
| 108 | + expect(internalGetToken).to.be.calledWith( |
| 109 | + app, |
| 110 | + fakePlatformLoggingProvider, |
| 111 | + true |
| 112 | + ); |
| 113 | + }); |
| 114 | + it('getToken() throws errors returned with token', async () => { |
| 115 | + const app = getFakeApp({ automaticDataCollectionEnabled: true }); |
| 116 | + const fakePlatformLoggingProvider = getFakePlatformLoggingProvider(); |
| 117 | + // If getToken() errors, it returns a dummy token with an error field |
| 118 | + // instead of throwing. |
| 119 | + stub(internalApi, 'getToken').resolves({ |
| 120 | + token: 'a-dummy-token', |
| 121 | + error: Error('there was an error') |
| 122 | + }); |
| 123 | + await expect( |
| 124 | + getToken(app, fakePlatformLoggingProvider, true) |
| 125 | + ).to.be.rejectedWith('there was an error'); |
| 126 | + }); |
| 127 | + }); |
| 128 | + describe('onTokenChanged()', () => { |
| 129 | + afterEach(() => { |
| 130 | + clearState(); |
| 131 | + removegreCAPTCHAScriptsOnPage(); |
| 132 | + }); |
| 133 | + it('Listeners work when using top-level parameters pattern', async () => { |
| 134 | + const app = getFakeApp({ automaticDataCollectionEnabled: true }); |
| 135 | + activate(app, FAKE_SITE_KEY, true); |
| 136 | + const fakePlatformLoggingProvider = getFakePlatformLoggingProvider(); |
| 137 | + const fakeRecaptchaToken = 'fake-recaptcha-token'; |
| 138 | + const fakeRecaptchaAppCheckToken = { |
| 139 | + token: 'fake-recaptcha-app-check-token', |
| 140 | + expireTimeMillis: 123, |
| 141 | + issuedAtTimeMillis: 0 |
| 142 | + }; |
| 143 | + stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken)); |
| 144 | + stub(client, 'exchangeToken').returns( |
| 145 | + Promise.resolve(fakeRecaptchaAppCheckToken) |
| 146 | + ); |
| 147 | + stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined)); |
| 148 | + |
| 149 | + const listener1 = (): void => { |
| 150 | + throw new Error(); |
| 151 | + }; |
| 152 | + const listener2 = spy(); |
| 153 | + |
| 154 | + const errorFn1 = spy(); |
| 155 | + const errorFn2 = spy(); |
| 156 | + |
| 157 | + const unsubscribe1 = onTokenChanged( |
| 158 | + app, |
| 159 | + fakePlatformLoggingProvider, |
| 160 | + listener1, |
| 161 | + errorFn1 |
| 162 | + ); |
| 163 | + const unsubscribe2 = onTokenChanged( |
| 164 | + app, |
| 165 | + fakePlatformLoggingProvider, |
| 166 | + listener2, |
| 167 | + errorFn2 |
| 168 | + ); |
| 169 | + |
| 170 | + expect(getState(app).tokenObservers.length).to.equal(2); |
| 171 | + |
| 172 | + await internalApi.getToken(app, fakePlatformLoggingProvider); |
| 173 | + |
| 174 | + expect(listener2).to.be.calledWith({ |
| 175 | + token: fakeRecaptchaAppCheckToken.token |
| 176 | + }); |
| 177 | + // onError should not be called on listener errors. |
| 178 | + expect(errorFn1).to.not.be.called; |
| 179 | + expect(errorFn2).to.not.be.called; |
| 180 | + unsubscribe1(); |
| 181 | + unsubscribe2(); |
| 182 | + expect(getState(app).tokenObservers.length).to.equal(0); |
| 183 | + }); |
| 184 | + |
| 185 | + it('Listeners work when using Observer pattern', async () => { |
| 186 | + const app = getFakeApp({ automaticDataCollectionEnabled: true }); |
| 187 | + activate(app, FAKE_SITE_KEY, true); |
| 188 | + const fakePlatformLoggingProvider = getFakePlatformLoggingProvider(); |
| 189 | + const fakeRecaptchaToken = 'fake-recaptcha-token'; |
| 190 | + const fakeRecaptchaAppCheckToken = { |
| 191 | + token: 'fake-recaptcha-app-check-token', |
| 192 | + expireTimeMillis: 123, |
| 193 | + issuedAtTimeMillis: 0 |
| 194 | + }; |
| 195 | + stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken)); |
| 196 | + stub(client, 'exchangeToken').returns( |
| 197 | + Promise.resolve(fakeRecaptchaAppCheckToken) |
| 198 | + ); |
| 199 | + stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined)); |
| 200 | + |
| 201 | + const listener1 = (): void => { |
| 202 | + throw new Error(); |
| 203 | + }; |
| 204 | + const listener2 = spy(); |
| 205 | + |
| 206 | + const errorFn1 = spy(); |
| 207 | + const errorFn2 = spy(); |
| 208 | + |
| 209 | + /** |
| 210 | + * Reverse the order of adding the failed and successful handler, for extra |
| 211 | + * testing. |
| 212 | + */ |
| 213 | + const unsubscribe2 = onTokenChanged(app, fakePlatformLoggingProvider, { |
| 214 | + next: listener2, |
| 215 | + error: errorFn2 |
| 216 | + }); |
| 217 | + const unsubscribe1 = onTokenChanged(app, fakePlatformLoggingProvider, { |
| 218 | + next: listener1, |
| 219 | + error: errorFn1 |
| 220 | + }); |
| 221 | + |
| 222 | + expect(getState(app).tokenObservers.length).to.equal(2); |
| 223 | + |
| 224 | + await internalApi.getToken(app, fakePlatformLoggingProvider); |
| 225 | + |
| 226 | + expect(listener2).to.be.calledWith({ |
| 227 | + token: fakeRecaptchaAppCheckToken.token |
| 228 | + }); |
| 229 | + // onError should not be called on listener errors. |
| 230 | + expect(errorFn1).to.not.be.called; |
| 231 | + expect(errorFn2).to.not.be.called; |
| 232 | + unsubscribe1(); |
| 233 | + unsubscribe2(); |
| 234 | + expect(getState(app).tokenObservers.length).to.equal(0); |
| 235 | + }); |
| 236 | + |
| 237 | + it('onError() catches token errors', async () => { |
| 238 | + stub(logger.logger, 'error'); |
| 239 | + const app = getFakeApp(); |
| 240 | + activate(app, FAKE_SITE_KEY, false); |
| 241 | + const fakePlatformLoggingProvider = getFakePlatformLoggingProvider(); |
| 242 | + const fakeRecaptchaToken = 'fake-recaptcha-token'; |
| 243 | + stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken)); |
| 244 | + stub(client, 'exchangeToken').rejects('exchange error'); |
| 245 | + stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined)); |
| 246 | + |
| 247 | + const listener1 = spy(); |
| 248 | + |
| 249 | + const errorFn1 = spy(); |
| 250 | + |
| 251 | + const unsubscribe1 = onTokenChanged( |
| 252 | + app, |
| 253 | + fakePlatformLoggingProvider, |
| 254 | + listener1, |
| 255 | + errorFn1 |
| 256 | + ); |
| 257 | + |
| 258 | + await internalApi.getToken(app, fakePlatformLoggingProvider); |
| 259 | + |
| 260 | + expect(getState(app).tokenObservers.length).to.equal(1); |
| 261 | + |
| 262 | + expect(errorFn1).to.be.calledOnce; |
| 263 | + expect(errorFn1.args[0][0].name).to.include('exchange error'); |
| 264 | + |
| 265 | + unsubscribe1(); |
| 266 | + expect(getState(app).tokenObservers.length).to.equal(0); |
| 267 | + }); |
| 268 | + }); |
89 | 269 | });
|
0 commit comments