Skip to content

Commit 88a3b2e

Browse files
authored
Add custom auth integration tests (against emulator only) (#4572)
* Add custom auth test * Formatting * es lint * Update some stuff * Formatting
1 parent 529a13a commit 88a3b2e

File tree

5 files changed

+241
-5
lines changed

5 files changed

+241
-5
lines changed

packages-exp/auth-exp/karma.conf.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ function getTestFiles(argv) {
3636
if (argv.unit) {
3737
return ['src/**/*.test.ts', 'test/helpers/**/*.test.ts'];
3838
} else if (argv.integration) {
39-
return ['test/integration/**/*.test.ts'];
39+
return argv.local
40+
? ['test/integration/**/*.test.ts']
41+
: ['test/integration/**/*!(local).test.ts'];
4042
} else if (argv.cordova) {
4143
return ['src/platform_cordova/**/*.test.ts'];
4244
} else {

packages-exp/auth-exp/scripts/run-node-tests.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use strict';
12
/**
23
* @license
34
* Copyright 2020 Google LLC
@@ -13,10 +14,7 @@
1314
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1415
* See the License for the specific language governing permissions and
1516
* limitations under the License.
16-
*/
17-
18-
'use strict';
19-
var __spreadArrays =
17+
*/ var __spreadArrays =
2018
(this && this.__spreadArrays) ||
2119
function () {
2220
for (var s = 0, i = 0, il = arguments.length; i < il; i++)
@@ -44,6 +42,9 @@ var testConfig = [
4442
];
4543
if (argv.integration) {
4644
testConfig = ['test/integration/flows/{email,anonymous}.test.ts'];
45+
if (argv.local) {
46+
testConfig.push('test/integration/flows/*.local.test.ts');
47+
}
4748
}
4849
var args = __spreadArrays(['--reporter', 'lcovonly', mocha], testConfig, [
4950
'--config',

packages-exp/auth-exp/scripts/run-node-tests.ts

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ let testConfig = [
4242

4343
if (argv.integration) {
4444
testConfig = ['test/integration/flows/{email,anonymous}.test.ts'];
45+
if (argv.local) {
46+
testConfig.push('test/integration/flows/*.local.test.ts');
47+
}
4548
}
4649

4750
let args = [

packages-exp/auth-exp/test/helpers/integration/helpers.ts

+16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
import * as sinon from 'sinon';
1819
import { deleteApp, initializeApp } from '@firebase/app-exp';
1920
import { Auth, User } from '../../../src/model/public_types';
2021

@@ -39,7 +40,9 @@ export function getTestInstance(): Auth {
3940
const emulatorUrl = getEmulatorUrl();
4041

4142
if (emulatorUrl) {
43+
const stub = stubConsoleToSilenceEmulatorWarnings();
4244
useAuthEmulator(auth, emulatorUrl, { disableWarnings: true });
45+
stub.restore();
4346
}
4447

4548
auth.onAuthStateChanged(user => {
@@ -68,3 +71,16 @@ export async function cleanUpTestInstance(auth: Auth): Promise<void> {
6871
await auth.signOut();
6972
await (auth as IntegrationTestAuth).cleanUp();
7073
}
74+
75+
function stubConsoleToSilenceEmulatorWarnings(): sinon.SinonStub {
76+
const originalConsoleInfo = console.info.bind(console);
77+
return sinon.stub(console, 'info').callsFake((...args: unknown[]) => {
78+
if (
79+
!JSON.stringify(args[0]).includes(
80+
'WARNING: You are using the Auth Emulator'
81+
)
82+
) {
83+
originalConsoleInfo(...args);
84+
}
85+
});
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import {
19+
Auth,
20+
createUserWithEmailAndPassword,
21+
EmailAuthProvider,
22+
linkWithCredential,
23+
OperationType,
24+
reload,
25+
signInAnonymously,
26+
signInWithCustomToken,
27+
signInWithEmailAndPassword,
28+
updateEmail,
29+
updatePassword,
30+
updateProfile
31+
// eslint-disable-next-line import/no-extraneous-dependencies
32+
} from '@firebase/auth-exp';
33+
import { FirebaseError } from '@firebase/util';
34+
import { expect, use } from 'chai';
35+
import * as chaiAsPromised from 'chai-as-promised';
36+
import {
37+
cleanUpTestInstance,
38+
getTestInstance,
39+
randomEmail
40+
} from '../../helpers/integration/helpers';
41+
42+
use(chaiAsPromised);
43+
44+
describe('Integration test: custom auth', () => {
45+
let auth: Auth;
46+
let customToken: string;
47+
let uid: string;
48+
49+
beforeEach(() => {
50+
auth = getTestInstance();
51+
uid = randomEmail();
52+
customToken = JSON.stringify({
53+
uid,
54+
claims: {
55+
customClaim: 'some-claim'
56+
}
57+
});
58+
59+
if (!auth.emulatorConfig) {
60+
throw new Error('Test can only be run against the emulator!');
61+
}
62+
});
63+
64+
afterEach(async () => {
65+
await cleanUpTestInstance(auth);
66+
});
67+
68+
it('signs in with custom token', async () => {
69+
const cred = await signInWithCustomToken(auth, customToken);
70+
expect(auth.currentUser).to.eq(cred.user);
71+
expect(cred.operationType).to.eq(OperationType.SIGN_IN);
72+
73+
const { user } = cred;
74+
expect(user.isAnonymous).to.be.false;
75+
expect(user.uid).to.eq(uid);
76+
expect((await user.getIdTokenResult(false)).claims.customClaim).to.eq(
77+
'some-claim'
78+
);
79+
expect(user.providerId).to.eq('firebase');
80+
});
81+
82+
it('uid will overwrite existing user, joining accounts', async () => {
83+
const { user: anonUser } = await signInAnonymously(auth);
84+
const customCred = await signInWithCustomToken(
85+
auth,
86+
JSON.stringify({
87+
uid: anonUser.uid
88+
})
89+
);
90+
91+
expect(auth.currentUser).to.eq(customCred.user);
92+
expect(customCred.user.uid).to.eq(anonUser.uid);
93+
expect(customCred.user.isAnonymous).to.be.false;
94+
});
95+
96+
it('allows the user to delete the account', async () => {
97+
let { user } = await signInWithCustomToken(auth, customToken);
98+
await updateProfile(user, { displayName: 'Display Name' });
99+
expect(user.displayName).to.eq('Display Name');
100+
101+
await user.delete();
102+
await expect(reload(user)).to.be.rejectedWith(
103+
FirebaseError,
104+
'auth/user-token-expired'
105+
);
106+
expect(auth.currentUser).to.be.null;
107+
108+
({ user } = await signInWithCustomToken(auth, customToken));
109+
// New user in the system: the display name should be missing
110+
expect(user.displayName).to.be.null;
111+
});
112+
113+
it('sign in can be called twice successively', async () => {
114+
const { user: userA } = await signInWithCustomToken(auth, customToken);
115+
const { user: userB } = await signInWithCustomToken(auth, customToken);
116+
expect(userA.uid).to.eq(userB.uid);
117+
});
118+
119+
it('allows user to update profile', async () => {
120+
let { user } = await signInWithCustomToken(auth, customToken);
121+
await updateProfile(user, {
122+
displayName: 'Display Name',
123+
photoURL: 'photo-url'
124+
});
125+
expect(user.displayName).to.eq('Display Name');
126+
expect(user.photoURL).to.eq('photo-url');
127+
128+
await auth.signOut();
129+
130+
user = (await signInWithCustomToken(auth, customToken)).user;
131+
expect(user.displayName).to.eq('Display Name');
132+
expect(user.photoURL).to.eq('photo-url');
133+
});
134+
135+
context('email/password interaction', () => {
136+
let email: string;
137+
let customToken: string;
138+
139+
beforeEach(() => {
140+
email = randomEmail();
141+
customToken = JSON.stringify({
142+
uid: email
143+
});
144+
});
145+
146+
it('custom / email-password accounts remain independent', async () => {
147+
let customCred = await signInWithCustomToken(auth, customToken);
148+
const emailCred = await createUserWithEmailAndPassword(
149+
auth,
150+
email,
151+
'password'
152+
);
153+
expect(emailCred.user.uid).not.to.eql(customCred.user.uid);
154+
155+
await auth.signOut();
156+
customCred = await signInWithCustomToken(auth, customToken);
157+
const emailSignIn = await signInWithEmailAndPassword(
158+
auth,
159+
email,
160+
'password'
161+
);
162+
expect(emailCred.user.uid).to.eql(emailSignIn.user.uid);
163+
expect(emailSignIn.user.uid).not.to.eql(customCred.user.uid);
164+
});
165+
166+
it('account can have email / password attached', async () => {
167+
const { user: customUser } = await signInWithCustomToken(
168+
auth,
169+
customToken
170+
);
171+
await updateEmail(customUser, email);
172+
await updatePassword(customUser, 'password');
173+
174+
await auth.signOut();
175+
176+
const { user: emailPassUser } = await signInWithEmailAndPassword(
177+
auth,
178+
email,
179+
'password'
180+
);
181+
expect(emailPassUser.uid).to.eq(customUser.uid);
182+
});
183+
184+
it('account can be linked using email and password', async () => {
185+
const { user: customUser } = await signInWithCustomToken(
186+
auth,
187+
customToken
188+
);
189+
const cred = EmailAuthProvider.credential(email, 'password');
190+
await linkWithCredential(customUser, cred);
191+
await auth.signOut();
192+
193+
const { user: emailPassUser } = await signInWithEmailAndPassword(
194+
auth,
195+
email,
196+
'password'
197+
);
198+
expect(emailPassUser.uid).to.eq(customUser.uid);
199+
});
200+
201+
it('account cannot be linked with existing email/password', async () => {
202+
await createUserWithEmailAndPassword(auth, email, 'password');
203+
const { user: customUser } = await signInWithCustomToken(
204+
auth,
205+
customToken
206+
);
207+
const cred = EmailAuthProvider.credential(email, 'password');
208+
await expect(linkWithCredential(customUser, cred)).to.be.rejectedWith(
209+
FirebaseError,
210+
'auth/email-already-in-use'
211+
);
212+
});
213+
});
214+
});

0 commit comments

Comments
 (0)