Skip to content

Commit

Permalink
🔄 synced local 'src/' with remote 'src/'
Browse files Browse the repository at this point in the history
  • Loading branch information
circle-github-action-bot committed Dec 5, 2024
1 parent d9e43f5 commit 949d37b
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 6 deletions.
131 changes: 130 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,24 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */

/**
* @jest-environment jsdom
*/
import * as firebaseAuth from 'firebase/auth'

import { SocialLoginProvider } from './types'

import { W3SSdk } from './'

import type { ChallengeResult, Error } from './types'
import type { ChallengeResult, Configs, Error } from './types'

jest.mock('firebase/auth', () => ({
...jest.requireActual('firebase/auth'),
signInWithPopup: jest.fn(),
getAuth: jest.fn(),
}))

describe('W3SSdk', () => {
let sdk: W3SSdk
Expand Down Expand Up @@ -101,3 +111,122 @@ describe('W3SSdk', () => {
)
})
})

describe('W3SSdk > Apple OAuth', () => {
const localStorageMock = (() => {
let store: { [key: string]: string } = {}

return {
getItem(key: string): string {
return store[key] || ''
},
setItem(key: string, value: string): void {
store[key] = value
},
removeItem(key: string): void {
delete store[key]
},
clear(): void {
store = {}
},
length: 0,
key(index: number): string | null {
return Object.keys(store)[index] || null
},
}
})()

Object.defineProperty(window, 'localStorage', { value: localStorageMock })

let sdk: W3SSdk
const configs: Configs = {
appSettings: {
appId: 'test-app-id',
},
loginConfigs: {
deviceToken: 'device-token',
deviceEncryptionKey: 'device-encryption-key',
apple: {
apiKey: 'test-api-key',
authDomain: 'test-auth-domain',
projectId: 'test-project-id',
storageBucket: 'test-storage-bucket',
messagingSenderId: 'test-messaging-sender-id',
appId: 'test-app-id',
},
},
}

beforeEach(() => {
jest.clearAllMocks()
window.localStorage.clear()
})

it('should perform Apple login successfully', async () => {
const onLoginComplete = jest.fn()
sdk = new W3SSdk(configs, onLoginComplete)

const mockFirebaseApp = {}
Object.defineProperty(sdk, 'firebaseApp', {
get: jest.fn(() => mockFirebaseApp),
configurable: true,
})

// Mock signInWithPopup to resolve to a UserCredential
const userCredentialMock = {
user: { uid: 'test-uid' },
credential: { idToken: 'test-id-token' },
}
;(firebaseAuth.signInWithPopup as jest.Mock).mockResolvedValueOnce(
userCredentialMock,
)

// Mock extractTokenFromResultAndSave to return true
const extractTokenSpy = jest
.spyOn(sdk as any, 'extractTokenFromResultAndSave')
.mockReturnValue(true)

// Mock verifyTokenViaService
const verifyTokenSpy = jest
.spyOn(sdk as any, 'verifyTokenViaService')
.mockImplementation(() => {})

await sdk.performLogin(SocialLoginProvider.APPLE)

expect(firebaseAuth.signInWithPopup).toHaveBeenCalled()
expect(extractTokenSpy).toHaveBeenCalledWith(userCredentialMock)
expect(verifyTokenSpy).toHaveBeenCalled()

expect(window.localStorage.getItem('socialLoginProvider')).toBe('')
})

it('should handle signInWithPopup error during Apple login', async () => {
const onLoginComplete = jest.fn()
sdk = new W3SSdk(configs, onLoginComplete)

// Simulate firebaseApp being initialized
const mockFirebaseApp = {}
Object.defineProperty(sdk, 'firebaseApp', {
get: jest.fn(() => mockFirebaseApp),
configurable: true,
})

// Mock getAuth
const mockAuth = { getProvider: jest.fn() }
;(firebaseAuth.getAuth as jest.Mock).mockReturnValue(mockAuth)

// Mock signInWithPopup to reject
const error = new Error('sign in error')
;(firebaseAuth.signInWithPopup as jest.Mock).mockRejectedValueOnce(error)

// Mock handleLoginFailure
const handleLoginFailureSpy = jest
.spyOn(sdk as any, 'handleLoginFailure')
.mockImplementation(() => {})

await sdk.performLogin(SocialLoginProvider.APPLE)

expect(firebaseAuth.signInWithPopup).toHaveBeenCalled()
expect(handleLoginFailureSpy).toHaveBeenCalled()
})
})
22 changes: 17 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
OAuthProvider,
getAuth,
getRedirectResult,
signInWithRedirect,
signInWithPopup,
} from 'firebase/auth'
import { decode } from 'jsonwebtoken'
import { v4 as uuidv4 } from 'uuid'
Expand Down Expand Up @@ -168,13 +168,13 @@ export class W3SSdk {
* Performs social login.
* @param provider - Social login provider.
*/
performLogin(provider: SocialLoginProvider): void {
async performLogin(provider: SocialLoginProvider): Promise<void> {
if (provider === SocialLoginProvider.GOOGLE) {
this.performGoogleLogin()
} else if (provider === SocialLoginProvider.FACEBOOK) {
this.performFacebookLogin()
} else if (provider === SocialLoginProvider.APPLE) {
this.performAppleLogin()
await this.performAppleLogin()
} else {
void this.onLoginComplete?.(
{
Expand Down Expand Up @@ -381,7 +381,7 @@ export class W3SSdk {
}, 1000 * 10)
}

private performAppleLogin() {
private async performAppleLogin() {
if (!this.firebaseApp) {
void this.onLoginComplete?.(
{
Expand All @@ -398,7 +398,19 @@ export class W3SSdk {
const provider = new OAuthProvider('apple.com')
const auth = getAuth(this.firebaseApp)

void signInWithRedirect(auth, provider)
try {
const cred = await signInWithPopup(auth, provider)

if (!this.extractTokenFromResultAndSave(cred)) {
return
}

// Send the token to the verification service and reset the social login provider
this.verifyTokenViaService()
this.window.localStorage.setItem('socialLoginProvider', '')
} catch (error) {
this.handleLoginFailure()
}
}

private performFacebookLogin() {
Expand Down

0 comments on commit 949d37b

Please sign in to comment.