Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cd39109

Browse files
authored
chore: track errors in linking flow (#17425)
* track errors in linking flow * explanatory comment
1 parent 6ec3447 commit cd39109

3 files changed

Lines changed: 138 additions & 3 deletions

File tree

src/Components/AuthDialog/Hooks/useAuthDialogTracking.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import {
33
type AuthImpression,
44
AuthModalType,
55
type CreatedAccount,
6+
type ErrorMessageViewed,
67
Intent,
8+
OwnerType,
79
type ResetYourPassword,
810
type SuccessfullyLoggedIn,
911
} from "@artsy/cohesion"
@@ -106,6 +108,30 @@ export const useAuthDialogTracking = () => {
106108
return trackEvent(payload)
107109
},
108110

111+
errorMessageViewed: ({
112+
error_code,
113+
title,
114+
message,
115+
flow,
116+
}: {
117+
error_code?: string
118+
title: string
119+
message: string
120+
flow: string
121+
}) => {
122+
const payload: ErrorMessageViewed = {
123+
action: ActionType.errorMessageViewed,
124+
context_owner_type: OwnerType.authModal,
125+
context_owner_id: "", // required by schema but auth modal has no entity ID
126+
title,
127+
message,
128+
error_code,
129+
flow,
130+
}
131+
132+
trackEvent(payload)
133+
},
134+
109135
resetPassword: () => {
110136
if (!analytics.intent || !analytics.trigger) return
111137

src/Components/AuthDialog/Views/AuthDialogLinkAccounts.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from "@artsy/palette"
1414
import { useAuthDialogContext } from "Components/AuthDialog/AuthDialogContext"
1515
import { useAfterAuthentication } from "Components/AuthDialog/Hooks/useAfterAuthentication"
16+
import { useAuthDialogTracking } from "Components/AuthDialog/Hooks/useAuthDialogTracking"
1617
import { formatErrorMessage } from "Components/AuthDialog/Utils/formatErrorMessage"
1718
import { useRouter } from "System/Hooks/useRouter"
1819
import { login } from "Utils/auth"
@@ -160,6 +161,7 @@ const PasswordForm: FC<PasswordFormProps> = ({
160161
}) => {
161162
const { runAfterAuthentication } = useAfterAuthentication()
162163
const { dispatch } = useAuthDialogContext()
164+
const track = useAuthDialogTracking()
163165

164166
return (
165167
<Formik
@@ -179,6 +181,14 @@ const PasswordForm: FC<PasswordFormProps> = ({
179181

180182
if (res.linkingError) {
181183
setFieldValue("mode", "LinkError")
184+
185+
track.errorMessageViewed({
186+
error_code: "link_accounts_error",
187+
title: "Account linking failed",
188+
message: LINK_ERROR_MESSAGE(providerName),
189+
flow: "Link Accounts",
190+
})
191+
182192
return
183193
}
184194

@@ -196,11 +206,27 @@ const PasswordForm: FC<PasswordFormProps> = ({
196206
setStatus({
197207
error: AUTH_ERROR_CODES.TWO_FACTOR_AUTHENTICATION_ENABLED,
198208
})
209+
210+
track.errorMessageViewed({
211+
error_code: "two_factor_authentication_enabled",
212+
title: "Two-factor authentication enabled",
213+
message: AUTH_ERROR_CODES.TWO_FACTOR_AUTHENTICATION_ENABLED,
214+
flow: "Link Accounts",
215+
})
216+
199217
return
200218
}
201219

220+
const errorMessage = formatErrorMessage(err)
202221
setFieldValue("mode", "Error")
203-
setStatus({ error: formatErrorMessage(err) })
222+
setStatus({ error: errorMessage })
223+
224+
track.errorMessageViewed({
225+
error_code: err.message,
226+
title: "Account linking failed",
227+
message: errorMessage,
228+
flow: "Link Accounts",
229+
})
204230
}
205231
}}
206232
>
@@ -247,8 +273,7 @@ const PasswordForm: FC<PasswordFormProps> = ({
247273

248274
{values.mode === "LinkError" && (
249275
<Message variant="error">
250-
Your account was signed in, but we couldn't link your{" "}
251-
{providerName} account. Please try again from Settings.
276+
{LINK_ERROR_MESSAGE(providerName)}
252277
</Message>
253278
)}
254279

@@ -313,6 +338,9 @@ type FormMode =
313338
| "LinkError"
314339
| "TwoFactorBlocked"
315340

341+
const LINK_ERROR_MESSAGE = (providerName: string) =>
342+
`Your account was signed in, but we couldn't link your ${providerName} account. Please try again from Settings.`
343+
316344
const VALIDATION_SCHEMA = Yup.object().shape({
317345
password: Yup.string().required("Password required"),
318346
})

src/Components/AuthDialog/Views/__tests__/AuthDialogLinkAccounts.jest.tsx

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
22
import { useAuthDialogContext } from "Components/AuthDialog/AuthDialogContext"
3+
import { useAuthDialogTracking } from "Components/AuthDialog/Hooks/useAuthDialogTracking"
34
import { AuthDialogLinkAccounts } from "Components/AuthDialog/Views/AuthDialogLinkAccounts"
45
import { login } from "Utils/auth"
56

@@ -39,15 +40,24 @@ jest.mock("Components/AuthDialog/AuthDialogContext", () => ({
3940
useAuthDialogContext: jest.fn(),
4041
}))
4142

43+
jest.mock("Components/AuthDialog/Hooks/useAuthDialogTracking", () => ({
44+
useAuthDialogTracking: jest.fn(),
45+
}))
46+
4247
describe("AuthDialogLinkAccounts", () => {
4348
const mockDispatch = jest.fn()
49+
const mockErrorMessageViewed = jest.fn()
4450

4551
beforeEach(() => {
4652
mockDispatch.mockClear()
53+
mockErrorMessageViewed.mockClear()
4754
;(useAuthDialogContext as jest.Mock).mockReturnValue({
4855
dispatch: mockDispatch,
4956
state: { values: {}, options: {} },
5057
})
58+
;(useAuthDialogTracking as jest.Mock).mockReturnValue({
59+
errorMessageViewed: mockErrorMessageViewed,
60+
})
5161
})
5262

5363
it("renders the password form when only email provider is available", () => {
@@ -105,6 +115,13 @@ describe("AuthDialogLinkAccounts", () => {
105115
})
106116

107117
expect(screen.queryByText("Yes, Link Accounts")).not.toBeInTheDocument()
118+
expect(mockErrorMessageViewed).toHaveBeenCalledWith({
119+
error_code: "two_factor_authentication_enabled",
120+
title: "Two-factor authentication enabled",
121+
message:
122+
"Social account linking is not available while two-factor authentication is enabled on your Artsy account.",
123+
flow: "Link Accounts",
124+
})
108125

109126
fireEvent.click(screen.getByText("Go Back"))
110127

@@ -130,5 +147,69 @@ describe("AuthDialogLinkAccounts", () => {
130147
await waitFor(() => {
131148
expect(screen.getByText("Go Back")).toBeInTheDocument()
132149
})
150+
151+
expect(mockErrorMessageViewed).toHaveBeenCalledWith(
152+
expect.objectContaining({
153+
error_code: "two_factor_authentication_enabled",
154+
}),
155+
)
156+
})
157+
158+
it("shows a link error and tracks when account linking fails after sign-in", async () => {
159+
render(<AuthDialogLinkAccounts />)
160+
;(login as jest.Mock).mockResolvedValueOnce({ linkingError: true })
161+
162+
const passwordInput = screen.getByPlaceholderText("Enter your password")
163+
fireEvent.change(passwordInput, { target: { value: "secret" } })
164+
165+
// eslint-disable-next-line testing-library/no-node-access
166+
const button = screen.getByText("Yes, Link Accounts").parentNode!
167+
fireEvent.click(button)
168+
169+
await waitFor(
170+
() => {
171+
expect(
172+
screen.getByText(/couldn.t link your Google account/),
173+
).toBeInTheDocument()
174+
},
175+
{ timeout: 3000 },
176+
)
177+
178+
expect(mockErrorMessageViewed).toHaveBeenCalledWith({
179+
error_code: "link_accounts_error",
180+
title: "Account linking failed",
181+
message: expect.stringContaining("link your Google account"),
182+
flow: "Link Accounts",
183+
})
184+
})
185+
186+
it("shows a generic error and tracks when login fails with an API error", async () => {
187+
render(<AuthDialogLinkAccounts />)
188+
;(login as jest.Mock).mockRejectedValueOnce(
189+
new Error("invalid email or password"),
190+
)
191+
192+
const passwordInput = screen.getByPlaceholderText("Enter your password")
193+
fireEvent.change(passwordInput, { target: { value: "wrong" } })
194+
195+
// eslint-disable-next-line testing-library/no-node-access
196+
const button = screen.getByText("Yes, Link Accounts").parentNode!
197+
fireEvent.click(button)
198+
199+
await waitFor(() => {
200+
expect(
201+
screen.getByText(
202+
"The email or password you entered is invalid. Please try again.",
203+
),
204+
).toBeInTheDocument()
205+
})
206+
207+
expect(mockErrorMessageViewed).toHaveBeenCalledWith({
208+
error_code: "invalid email or password",
209+
title: "Account linking failed",
210+
message:
211+
"The email or password you entered is invalid. Please try again.",
212+
flow: "Link Accounts",
213+
})
133214
})
134215
})

0 commit comments

Comments
 (0)