Skip to content

Commit

Permalink
refactor(auth): remove credential and mock out of Auth class
Browse files Browse the repository at this point in the history
  • Loading branch information
gustavohenke committed Jun 30, 2020
1 parent 3ccdfa7 commit e6f6df4
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 38 deletions.
1 change: 1 addition & 0 deletions auth/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe("#createUserWithEmailAndPassword()", () => {
expect(auth.store.add).toHaveBeenCalledWith({
email: "foo@bar.com",
password: "password",
providerId: firebase.auth.EmailAuthProvider.PROVIDER_ID,
});
});

Expand Down
51 changes: 13 additions & 38 deletions auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SocialSignInMock } from "./social-signin-mock";
import { User } from "./user";
import { UserStore } from "./user-store";
import { AuthSettings } from "./auth-settings";
import { UserCredential } from "./user-credential";

export type AuthStateChangeListener = (user: firebase.User | null) => void;

Expand All @@ -13,7 +14,6 @@ export class MockAuth implements firebase.auth.Auth {
public settings: firebase.auth.AuthSettings = new AuthSettings();
public tenantId: string | null;
public readonly store = new UserStore();
private readonly socialSignIns = new Set<SocialSignInMock>();
private readonly authStateEvents = new Set<AuthStateChangeListener>();

constructor(public readonly app: MockApp) {}
Expand All @@ -38,8 +38,9 @@ export class MockAuth implements firebase.auth.Auth {
throw new Error("auth/email-already-in-use");
}

const user = this.store.add({ email, password });
return this.signIn(user);
const { providerId } = new firebase.auth.EmailAuthProvider();
const user = this.store.add({ email, password, providerId });
return this.signIn(user, { isNewUser: true });
}

fetchProvidersForEmail(email: string): Promise<any> {
Expand All @@ -60,7 +61,7 @@ export class MockAuth implements firebase.auth.Auth {

mockSocialSignIn(provider: firebase.auth.AuthProvider) {
const mock = new SocialSignInMock(provider.providerId);
this.socialSignIns.add(mock);
this.store.addSocialMock(mock);
return mock;
}

Expand Down Expand Up @@ -105,51 +106,25 @@ export class MockAuth implements firebase.auth.Auth {

private signIn(
user: User,
additionalUserInfo: firebase.auth.AdditionalUserInfo | null = null
{ isNewUser }: { isNewUser: boolean }
): Promise<firebase.auth.UserCredential> {
this.currentUser = user;
this.authStateEvents.forEach((listener) => {
listener(user);
});

return Promise.resolve<firebase.auth.UserCredential>({
user,
additionalUserInfo,
credential: null,
operationType: "signIn",
});
return Promise.resolve(new UserCredential(user, "signIn", isNewUser));
}

private async signInWithSocial(provider: firebase.auth.AuthProvider) {
const mock = Array.from(this.socialSignIns.values()).find(
(mock) => mock.type === provider.providerId
);

if (!mock) {
throw new Error("No mock response set.");
}

// Mock is used, then it must go
this.socialSignIns.delete(mock);

const data = await mock.response;
const data = await this.store.consumeSocialMock(provider);
let user = this.store.findByProviderAndEmail(data.email, provider.providerId);
if (user) {
return this.signIn(user, {
isNewUser: false,
providerId: provider.providerId,
profile: null,
username: data.email,
});
return this.signIn(user, { isNewUser: false });
}

user = this.store.add({ ...data, providerId: provider.providerId });
return this.signIn(user, {
isNewUser: true,
providerId: provider.providerId,
profile: null,
username: data.email,
});
return this.signIn(user, { isNewUser: true });
}

signInAndRetrieveDataWithCredential(credential: firebase.auth.AuthCredential): Promise<any> {
Expand All @@ -158,11 +133,11 @@ export class MockAuth implements firebase.auth.Auth {

signInAnonymously(): Promise<firebase.auth.UserCredential> {
if (this.currentUser && this.currentUser.isAnonymous) {
return this.signIn(this.currentUser);
return this.signIn(this.currentUser, { isNewUser: false });
}

const user = this.store.add({ isAnonymous: true });
return this.signIn(user);
return this.signIn(user, { isNewUser: true });
}

signInWithCredential(credential: firebase.auth.AuthCredential): Promise<any> {
Expand All @@ -184,7 +159,7 @@ export class MockAuth implements firebase.auth.Auth {
return Promise.reject(new Error("auth/wrong-password"));
}

return this.signIn(user);
return this.signIn(user, { isNewUser: false });
}

signInWithEmailLink(
Expand Down
22 changes: 22 additions & 0 deletions auth/user-credential.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { User } from "./user";

export class UserCredential implements firebase.auth.UserCredential {
readonly additionalUserInfo: firebase.auth.AdditionalUserInfo | null = null;
readonly credential = null;

constructor(
readonly user: User,
readonly operationType: "signIn" | "link" | "reauthenticate",
isNewUser: boolean
) {
if (!user.isAnonymous) {
this.additionalUserInfo = {
isNewUser,
profile: null,
// providerId should be at the right value in the user object already.
providerId: user.providerId,
username: user.email,
};
}
}
}
19 changes: 19 additions & 0 deletions auth/user-store.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { User, UserSchema, UserInfo } from "./user";
import { SocialSignInMock } from "./social-signin-mock";

export class UserStore {
private nextId = 0;
private store = new Map<string, UserSchema>();
private readonly socialMocks: SocialSignInMock[] = [];

add(data: Partial<UserSchema>): User {
const uid = ++this.nextId + "";
Expand All @@ -20,6 +22,23 @@ export class UserStore {
return user;
}

addSocialMock(mock: SocialSignInMock) {
this.socialMocks.push(mock);
}

consumeSocialMock(provider: firebase.auth.AuthProvider) {
const index = this.socialMocks.findIndex((mock) => mock.type === provider.providerId);
if (index === -1) {
throw new Error("No mock response set.");
}

// Mock is used, then it must go
const mock = this.socialMocks[index];
this.socialMocks.splice(index, 1);

return mock.response;
}

findByEmail(email: string): User | undefined {
for (const user of this.store.values()) {
if (user.email === email) {
Expand Down

0 comments on commit e6f6df4

Please sign in to comment.