@faststore/core 4.3.0-dev.6 → 4.3.0-dev.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-generate.log +3 -3
- package/.turbo/turbo-test.log +271 -30
- package/CHANGELOG.md +12 -0
- package/package.json +9 -6
- package/src/pages/api/fs/password-protection/unlock.ts +123 -0
- package/src/pages/password-protection/index.tsx +93 -0
- package/src/pages/password-protection/password-protection.module.scss +43 -0
- package/src/proxy.ts +47 -2
- package/src/sdk/account/useSetPassword.ts +82 -72
- package/src/server/password-protection/webops-api.ts +38 -0
- package/src/server/password-protection-service.ts +283 -0
- package/src/utils/unlockResponse.ts +25 -0
- package/test/pages/api/unlock.test.ts +277 -0
- package/test/pages/password-protection.browser.test.tsx +201 -0
- package/test/proxy.test.ts +99 -0
- package/test/sdk/account/useSetPassword.test.ts +192 -0
- package/test/server/password-protection-service.test.ts +624 -0
- package/test/server/webops-api.test.ts +92 -0
- package/test/utils/unlockResponse.test.ts +57 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { act, renderHook } from '@testing-library/react'
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
|
|
8
|
+
vi.mock('discovery.config', () => ({
|
|
9
|
+
__esModule: true,
|
|
10
|
+
default: {
|
|
11
|
+
api: { storeId: 'storeframework' },
|
|
12
|
+
},
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
const mockFetch = vi.hoisted(() => vi.fn())
|
|
16
|
+
vi.mock('isomorphic-unfetch', () => ({ __esModule: true, default: mockFetch }))
|
|
17
|
+
|
|
18
|
+
import { useSetPassword } from '../../../src/sdk/account/useSetPassword'
|
|
19
|
+
|
|
20
|
+
const okResponse = (body: unknown) => ({
|
|
21
|
+
ok: true,
|
|
22
|
+
statusText: 'OK',
|
|
23
|
+
json: async () => body,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const errorResponse = (body: unknown) => ({
|
|
27
|
+
ok: false,
|
|
28
|
+
statusText: 'Bad Request',
|
|
29
|
+
json: async () => body,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const validInput = {
|
|
33
|
+
userEmail: 'shopper@example.com',
|
|
34
|
+
currentPassword: 'OldPass123',
|
|
35
|
+
newPassword: 'NewPass123',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getSetPasswordCall() {
|
|
39
|
+
return mockFetch.mock.calls.find((call) =>
|
|
40
|
+
String(call[0]).includes('setpassword')
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getStartLoginCall() {
|
|
45
|
+
return mockFetch.mock.calls.find((call) =>
|
|
46
|
+
String(call[0]).includes('/pub/authentication/start')
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('useSetPassword', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
mockFetch.mockReset()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
vi.clearAllMocks()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('posts to the new authenticator setpassword route with expireSessions and an', async () => {
|
|
60
|
+
mockFetch
|
|
61
|
+
.mockResolvedValueOnce(okResponse({})) // startLogin
|
|
62
|
+
.mockResolvedValueOnce(okResponse({ authStatus: 'success' }))
|
|
63
|
+
|
|
64
|
+
const { result } = renderHook(() => useSetPassword('myaccount'))
|
|
65
|
+
|
|
66
|
+
await act(async () => {
|
|
67
|
+
await result.current.setPassword(validInput)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const call = getSetPasswordCall()
|
|
71
|
+
expect(call).toBeDefined()
|
|
72
|
+
|
|
73
|
+
const url = String(call?.[0])
|
|
74
|
+
expect(url).toContain(
|
|
75
|
+
'/api/authenticator/pub/authentication/classic/setpassword'
|
|
76
|
+
)
|
|
77
|
+
expect(url).not.toContain('/api/vtexid/')
|
|
78
|
+
expect(url).toContain('expireSessions=true')
|
|
79
|
+
expect(url).toContain('an=myaccount')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('posts to the new authenticator start route with an and credentials', async () => {
|
|
83
|
+
mockFetch
|
|
84
|
+
.mockResolvedValueOnce(okResponse({})) // startLogin
|
|
85
|
+
.mockResolvedValueOnce(okResponse({ authStatus: 'success' }))
|
|
86
|
+
|
|
87
|
+
const { result } = renderHook(() => useSetPassword('myaccount'))
|
|
88
|
+
|
|
89
|
+
await act(async () => {
|
|
90
|
+
await result.current.setPassword(validInput)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const call = getStartLoginCall()
|
|
94
|
+
expect(call).toBeDefined()
|
|
95
|
+
|
|
96
|
+
const url = String(call?.[0])
|
|
97
|
+
expect(url).toContain('/api/authenticator/pub/authentication/start')
|
|
98
|
+
expect(url).not.toContain('/api/vtexid/')
|
|
99
|
+
expect(url).toContain('an=myaccount')
|
|
100
|
+
|
|
101
|
+
const init = call?.[1] as RequestInit
|
|
102
|
+
expect(init?.method).toBe('POST')
|
|
103
|
+
expect(init?.credentials).toBe('include')
|
|
104
|
+
|
|
105
|
+
const body = init?.body as FormData
|
|
106
|
+
expect(body).toBeInstanceOf(FormData)
|
|
107
|
+
expect(body.get('user')).toBe(validInput.userEmail)
|
|
108
|
+
expect(body.get('scope')).toBe('myaccount')
|
|
109
|
+
expect(body.get('accountName')).toBe('myaccount')
|
|
110
|
+
expect(body.get('returnUrl')).toBe('/')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('sends a POST with credentials and the expected form-data body', async () => {
|
|
114
|
+
mockFetch
|
|
115
|
+
.mockResolvedValueOnce(okResponse({}))
|
|
116
|
+
.mockResolvedValueOnce(okResponse({ authStatus: 'success' }))
|
|
117
|
+
|
|
118
|
+
const { result } = renderHook(() => useSetPassword('myaccount'))
|
|
119
|
+
|
|
120
|
+
await act(async () => {
|
|
121
|
+
await result.current.setPassword(validInput)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const call = getSetPasswordCall()
|
|
125
|
+
const init = call?.[1] as RequestInit
|
|
126
|
+
|
|
127
|
+
expect(init?.method).toBe('POST')
|
|
128
|
+
expect(init?.credentials).toBe('include')
|
|
129
|
+
|
|
130
|
+
const body = init?.body as FormData
|
|
131
|
+
expect(body).toBeInstanceOf(FormData)
|
|
132
|
+
expect(body.get('login')).toBe(validInput.userEmail)
|
|
133
|
+
expect(body.get('currentPassword')).toBe(validInput.currentPassword)
|
|
134
|
+
expect(body.get('newPassword')).toBe(validInput.newPassword)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('returns success when the endpoint reports authStatus success', async () => {
|
|
138
|
+
mockFetch
|
|
139
|
+
.mockResolvedValueOnce(okResponse({}))
|
|
140
|
+
.mockResolvedValueOnce(okResponse({ authStatus: 'success' }))
|
|
141
|
+
|
|
142
|
+
const { result } = renderHook(() => useSetPassword('myaccount'))
|
|
143
|
+
|
|
144
|
+
let outcome: { success: boolean; message: string } | undefined
|
|
145
|
+
await act(async () => {
|
|
146
|
+
outcome = await result.current.setPassword(validInput)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
expect(outcome).toEqual({
|
|
150
|
+
success: true,
|
|
151
|
+
message: 'Password set successfully',
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('maps wrongcredentials error responses to the existing message', async () => {
|
|
156
|
+
mockFetch
|
|
157
|
+
.mockResolvedValueOnce(okResponse({}))
|
|
158
|
+
.mockResolvedValueOnce(errorResponse({ authStatus: 'wrongcredentials' }))
|
|
159
|
+
|
|
160
|
+
const { result } = renderHook(() => useSetPassword('myaccount'))
|
|
161
|
+
|
|
162
|
+
let outcome: { success: boolean; message: string } | undefined
|
|
163
|
+
await act(async () => {
|
|
164
|
+
outcome = await result.current.setPassword(validInput)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
expect(outcome).toEqual({ success: false, message: 'Wrong credentials' })
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('falls back to config.api.storeId when accountName is missing', async () => {
|
|
171
|
+
mockFetch
|
|
172
|
+
.mockResolvedValueOnce(okResponse({}))
|
|
173
|
+
.mockResolvedValueOnce(okResponse({ authStatus: 'success' }))
|
|
174
|
+
|
|
175
|
+
const { result } = renderHook(() => useSetPassword())
|
|
176
|
+
|
|
177
|
+
await act(async () => {
|
|
178
|
+
await result.current.setPassword(validInput)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const setPasswordUrl = String(getSetPasswordCall()?.[0])
|
|
182
|
+
expect(setPasswordUrl).toContain('an=storeframework')
|
|
183
|
+
|
|
184
|
+
const startLoginCall = getStartLoginCall()
|
|
185
|
+
const startLoginUrl = String(startLoginCall?.[0])
|
|
186
|
+
expect(startLoginUrl).toContain('an=storeframework')
|
|
187
|
+
|
|
188
|
+
const startBody = (startLoginCall?.[1] as RequestInit)?.body as FormData
|
|
189
|
+
expect(startBody.get('scope')).toBe('storeframework')
|
|
190
|
+
expect(startBody.get('accountName')).toBe('storeframework')
|
|
191
|
+
})
|
|
192
|
+
})
|