Skip to content

Commit f5d234b

Browse files
committed
tests
1 parent 789d93a commit f5d234b

File tree

5 files changed

+122
-12
lines changed

5 files changed

+122
-12
lines changed

packages/template-retail-react-app/app/components/otp-auth/index.test.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ describe('OtpAuth', () => {
8282
mockGetUsidWhenReady.mockResolvedValue('mock-usid-12345')
8383
mockGetEncUserIdWhenReady.mockResolvedValue('mock-enc-user-id')
8484
mockUseCurrentCustomer.mockReturnValue({
85-
data: null // Default to guest user
85+
data: {
86+
customerId: 'mock-customer-id',
87+
customerType: 'guest'
88+
}
8689
})
8790

8891
jest.clearAllMocks()
@@ -505,7 +508,12 @@ describe('OtpAuth', () => {
505508

506509
describe('Einstein Tracking - Privacy-Compliant User Identification', () => {
507510
test('uses USID for guest users when DNT is disabled', async () => {
508-
mockUseCurrentCustomer.mockReturnValue({data: null})
511+
mockUseCurrentCustomer.mockReturnValue({
512+
data: {
513+
customerId: 'guest-customer-id',
514+
customerType: 'guest'
515+
}
516+
})
509517

510518
renderWithProviders(
511519
<OtpAuth

packages/template-retail-react-app/app/components/passkey-registration-modal/index.test.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
*/
77
import React from 'react'
88
import {screen, waitFor} from '@testing-library/react'
9-
import PasskeyRegistrationModal from '@salesforce/retail-react-app/app/components/passkey-registration-modal/index'
109
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
1110
import mockConfig from '@salesforce/retail-react-app/config/mocks/default'
1211
import {rest} from 'msw'
1312

13+
// Unmock the component so we can test it
14+
jest.unmock('@salesforce/retail-react-app/app/components/passkey-registration-modal')
15+
import PasskeyRegistrationModal from '@salesforce/retail-react-app/app/components/passkey-registration-modal/index'
16+
1417
// Mock Commerce SDK hooks
1518
const mockMutateAsync = jest.fn()
1619
const mockUseAuthHelper = jest.fn()
@@ -78,6 +81,23 @@ describe('PasskeyRegistrationModal', () => {
7881
create: jest.fn()
7982
}
8083

84+
// Mock PublicKeyCredential API
85+
global.PublicKeyCredential = {
86+
parseCreationOptionsFromJSON: jest.fn((options) => ({
87+
challenge: new Uint8Array([1, 2, 3]),
88+
rp: {name: 'Test RP', id: 'example.com'},
89+
user: {
90+
id: new Uint8Array([4, 5, 6]),
91+
name: 'test@example.com',
92+
displayName: 'Test User'
93+
},
94+
pubKeyCredParams: [{type: 'public-key', alg: -7}],
95+
...options
96+
})),
97+
isUserVerifyingPlatformAuthenticatorAvailable: jest.fn().mockResolvedValue(true),
98+
isConditionalMediationAvailable: jest.fn().mockResolvedValue(true)
99+
}
100+
81101
// Mock product API calls that may be triggered by components in the provider tree
82102
global.server.use(
83103
rest.get('*/products*', (req, res, ctx) => {
@@ -86,6 +106,10 @@ describe('PasskeyRegistrationModal', () => {
86106
)
87107
})
88108

109+
afterEach(() => {
110+
delete global.PublicKeyCredential
111+
})
112+
89113
describe('Rendering', () => {
90114
test('renders modal when isOpen is true', () => {
91115
renderWithProviders(<PasskeyRegistrationModal isOpen={true} onClose={mockOnClose} />, {

packages/template-retail-react-app/app/hooks/use-auth-modal.test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ jest.mock('@salesforce/commerce-sdk-react', () => {
6565
const originalModule = jest.requireActual('@salesforce/commerce-sdk-react')
6666
return {
6767
...originalModule,
68-
useAuthHelper: jest
69-
.fn()
70-
.mockImplementation((helperType) => mockAuthHelperFunctions[helperType])
68+
useAuthHelper: jest.fn().mockImplementation((helperType) => {
69+
// Return the specific mock if defined, otherwise return a default mock
70+
return mockAuthHelperFunctions[helperType] || {mutateAsync: jest.fn()}
71+
})
7172
}
7273
})
7374

packages/template-retail-react-app/app/pages/login/index.test.js

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,33 @@ import Registration from '@salesforce/retail-react-app/app/pages/registration'
1919
import ResetPassword from '@salesforce/retail-react-app/app/pages/reset-password'
2020
import mockConfig from '@salesforce/retail-react-app/config/mocks/default'
2121
import {mockedRegisteredCustomer} from '@salesforce/retail-react-app/app/mocks/mock-data'
22+
import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
23+
24+
// Mock getConfig for passkey tests
25+
jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-config', () => ({
26+
getConfig: jest.fn()
27+
}))
28+
29+
// Mock Commerce SDK Auth Helpers
30+
const mockStartWebauthnAuthentication = jest.fn()
31+
const mockFinishWebauthnAuthentication = jest.fn()
32+
33+
jest.mock('@salesforce/commerce-sdk-react', () => {
34+
const actual = jest.requireActual('@salesforce/commerce-sdk-react')
35+
return {
36+
...actual,
37+
useAuthHelper: (helperType) => {
38+
if (helperType === actual.AuthHelpers.StartWebauthnAuthentication) {
39+
return {mutateAsync: mockStartWebauthnAuthentication}
40+
}
41+
if (helperType === actual.AuthHelpers.FinishWebauthnAuthentication) {
42+
return {mutateAsync: mockFinishWebauthnAuthentication}
43+
}
44+
// Return actual for other helper types
45+
return actual.useAuthHelper(helperType)
46+
}
47+
}
48+
})
2249

2350
const mockMergedBasket = {
2451
basketId: 'a10ff320829cb0eef93ca5310a',
@@ -53,6 +80,8 @@ const MockedComponent = () => {
5380
// Set up and clean up
5481
beforeEach(() => {
5582
jest.resetModules()
83+
// Setup getConfig mock with default config for all tests
84+
getConfig.mockReturnValue(mockConfig)
5685
global.server.use(
5786
rest.post('*/customers', (req, res, ctx) => {
5887
return res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
@@ -269,14 +298,17 @@ describe('Error while logging in', function () {
269298
})
270299
})
271300
describe('Passkey login', () => {
272-
let mockStartWebauthnAuthentication
273-
let mockFinishWebauthnAuthentication
274301
let mockCredentialsGet
275302
let mockPublicKeyCredential
276303

277304
beforeEach(() => {
278-
// Mock WebAuthn API
279-
mockCredentialsGet = jest.fn()
305+
// Clear all mocks
306+
jest.clearAllMocks()
307+
mockStartWebauthnAuthentication.mockClear()
308+
mockFinishWebauthnAuthentication.mockClear()
309+
310+
// Mock WebAuthn API - default to never resolving (simulating no user action)
311+
mockCredentialsGet = jest.fn().mockImplementation(() => new Promise(() => {}))
280312
mockPublicKeyCredential = {
281313
parseRequestOptionsFromJSON: jest.fn(),
282314
isConditionalMediationAvailable: jest.fn().mockResolvedValue(true),
@@ -291,6 +323,26 @@ describe('Passkey login', () => {
291323

292324
// Clear localStorage
293325
localStorage.clear()
326+
327+
// Setup mock responses for auth helpers
328+
mockStartWebauthnAuthentication.mockResolvedValue({
329+
publicKey: {
330+
challenge: 'mock-challenge-data',
331+
rpId: 'example.com',
332+
allowCredentials: [],
333+
timeout: 60000
334+
}
335+
})
336+
337+
mockFinishWebauthnAuthentication.mockResolvedValue({
338+
customer_id: 'customerid_passkey',
339+
access_token:
340+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXQiOiJHVUlEIiwic2NwIjoic2ZjYy5zaG9wcGVyLW15YWNjb3VudC5iYXNrZXRzIHNmY2Muc2hvcHBlci1teWFjY291bnQuYWRkcmVzc2VzIHNmY2Muc2hvcHBlci1wcm9kdWN0cyBzZmNjLnNob3BwZXItZGlzY292ZXJ5LXNlYXJjaCBzZmNjLnNob3BwZXItbXlhY2NvdW50LnJ3IHNmY2Muc2hvcHBlci1teWFjY291bnQucGF5bWVudGluc3RydW1lbnRzIHNmY2Muc2hvcHBlci1jdXN0b21lcnMubG9naW4gc2ZjYy5zaG9wcGVyLWV4cGVyaWVuY2Ugc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5vcmRlcnMgc2ZjYy5zaG9wcGVyLWN1c3RvbWVycy5yZWdpc3RlciBzZmNjLnNob3BwZXItYmFza2V0cy1vcmRlcnMgc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5hZGRyZXNzZXMucncgc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5wcm9kdWN0bGlzdHMucncgc2ZjYy5zaG9wcGVyLXByb2R1Y3RsaXN0cyBzZmNjLnNob3BwZXItcHJvbW90aW9ucyBzZmNjLnNob3BwZXItYmFza2V0cy1vcmRlcnMucncgc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5wYXltZW50aW5zdHJ1bWVudHMucncgc2ZjYy5zaG9wcGVyLWdpZnQtY2VydGlmaWNhdGVzIHNmY2Muc2hvcHBlci1wcm9kdWN0LXNlYXJjaCBzZmNjLnNob3BwZXItbXlhY2NvdW50LnByb2R1Y3RsaXN0cyBzZmNjLnNob3BwZXItY2F0ZWdvcmllcyBzZmNjLnNob3BwZXItbXlhY2NvdW50Iiwic3ViIjoiY2Mtc2xhczo6enpyZl8wMDE6OnNjaWQ6YzljNDViZmQtMGVkMy00YWEyLTk5NzEtNDBmODg5NjJiODM2Ojp1c2lkOjhlODgzOTczLTY4ZWItNDFmZS1hM2M1LTc1NjIzMjY1MmZmNSIsImN0eCI6InNsYXMiLCJpc3MiOiJzbGFzL3Byb2QvenpyZl8wMDEiLCJpc3QiOjEsImF1ZCI6ImNvbW1lcmNlY2xvdWQvcHJvZC96enJmXzAwMSIsIm5iZiI6MTY3ODgzNDI3MSwic3R5IjoiVXNlciIsImlzYiI6InVpZG86ZWNvbTo6dXBuOmtldjVAdGVzdC5jb206OnVpZG46a2V2aW4gaGU6OmdjaWQ6YWJtZXMybWJrM2xYa1JsSEZKd0dZWWt1eEo6OnJjaWQ6YWJVTXNhdnBEOVk2alcwMGRpMlNqeEdDTVU6OmNoaWQ6UmVmQXJjaEdsb2JhbCIsImV4cCI6MjY3ODgzNjEwMSwiaWF0IjoxNjc4ODM0MzAxLCJqdGkiOiJDMkM0ODU2MjAxODYwLTE4OTA2Nzg5MDM0ODA1ODMyNTcwNjY2NTQyIn0._tUrxeXdFYPj6ZoY-GILFRd3-aD1RGPkZX6TqHeS494',
341+
refresh_token: 'testrefeshtoken_passkey',
342+
usid: 'testusid_passkey',
343+
enc_user_id: 'testEncUserId_passkey',
344+
id_token: 'testIdToken_passkey'
345+
})
294346
})
295347

296348
afterEach(() => {
@@ -423,10 +475,18 @@ describe('Passkey login', () => {
423475
await user.type(screen.getByLabelText('Email'), 'test@salesforce.com')
424476
await user.click(screen.getByRole('button', {name: /sign in/i}))
425477

426-
// Should redirect to account page after successful passkey login
478+
// Should trigger passkey authentication with credentials.get
427479
await waitFor(
428480
() => {
429-
expect(mockFinishWebauthnAuthentication).toHaveBeenCalled()
481+
expect(mockCredentialsGet).toHaveBeenCalled()
482+
},
483+
{timeout: 5000}
484+
)
485+
486+
// After successful passkey login, should redirect to account page
487+
await waitFor(
488+
() => {
489+
expect(window.location.pathname).toBe('/uk/en-GB/account')
430490
},
431491
{timeout: 5000}
432492
)
@@ -441,6 +501,12 @@ describe('Passkey login', () => {
441501
}
442502
}
443503

504+
// Override getConfig to return config with passkey disabled
505+
getConfig.mockReturnValue({
506+
...mockConfig,
507+
app: mockAppConfig
508+
})
509+
444510
renderWithProviders(<MockedComponent />, {
445511
wrapperProps: {
446512
siteAlias: 'uk',
@@ -454,8 +520,13 @@ describe('Passkey login', () => {
454520
expect(screen.getByTestId('login-page')).toBeInTheDocument()
455521
})
456522

523+
// Give it a moment for any async effects to run
524+
await new Promise((resolve) => setTimeout(resolve, 100))
525+
457526
// Should not call credentials API when passkey is disabled
458527
expect(mockCredentialsGet).not.toHaveBeenCalled()
528+
// Should not call auth helpers when passkey is disabled
529+
expect(mockStartWebauthnAuthentication).not.toHaveBeenCalled()
459530
})
460531

461532
test('Handles passkey login cancellation gracefully', async () => {

packages/template-retail-react-app/jest-setup.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-config', () => {
122122
}
123123
})
124124

125+
// Mock PasskeyRegistrationModal to prevent auth helper issues in tests
126+
jest.mock('@salesforce/retail-react-app/app/components/passkey-registration-modal', () => ({
127+
__esModule: true,
128+
default: () => null
129+
}))
130+
125131
// TextEncoder is a web API, need to import it
126132
// from nodejs util in testing environment.
127133
global.TextEncoder = require('util').TextEncoder

0 commit comments

Comments
 (0)