Skip to content

Commit 2f2adec

Browse files
committed
chore UGN-355 - render error toast if error is thrown in async hook
1 parent cdb4f3c commit 2f2adec

6 files changed

Lines changed: 179 additions & 43 deletions

File tree

src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const fields: Fields<any> = [
4141

4242
const CONTINUE_BUTTON = "Next"
4343
const MUTATED_ENTRY = "mutated entry"
44+
const ERROR_MESSAGE = "Something happened"
4445

4546
describe("Match Columns automatic matching", () => {
4647
test("AutoMatch column and click next", async () => {
@@ -622,7 +623,7 @@ describe("Match Columns general tests", () => {
622623
...mockRsiValues,
623624
fields: mockRsiValues.fields.filter((field) => field.key === "name" || field.key === "age"),
624625
}
625-
const { getByText } = render(
626+
render(
626627
<ReactSpreadsheetImport
627628
{...mockValues}
628629
matchColumnsStepHook={matchColumnsStepHook}
@@ -638,10 +639,44 @@ describe("Match Columns general tests", () => {
638639
/>,
639640
)
640641

641-
const continueButton = getByText(CONTINUE_BUTTON)
642+
const continueButton = screen.getByText(CONTINUE_BUTTON)
642643
userEvent.click(continueButton)
643644

644645
const mutatedEntry = await screen.findByText(MUTATED_ENTRY)
645646
expect(mutatedEntry).toBeInTheDocument()
646647
})
648+
649+
test("Should show error toast if error is thrown in matchColumnsStepHook", async () => {
650+
const matchColumnsStepHook = jest.fn(async () => {
651+
throw new Error(ERROR_MESSAGE)
652+
return undefined as any
653+
})
654+
655+
const mockValues = {
656+
...mockRsiValues,
657+
fields: mockRsiValues.fields.filter((field) => field.key === "name" || field.key === "age"),
658+
}
659+
660+
render(
661+
<ReactSpreadsheetImport
662+
{...mockValues}
663+
matchColumnsStepHook={matchColumnsStepHook}
664+
initialStepState={{
665+
type: StepType.matchColumns,
666+
data: [
667+
["Josh", "2"],
668+
["Charlie", "3"],
669+
["Lena", "50"],
670+
],
671+
headerValues: ["name", "age"],
672+
}}
673+
/>,
674+
)
675+
676+
const continueButton = screen.getByText(CONTINUE_BUTTON)
677+
userEvent.click(continueButton)
678+
679+
const errorToast = await screen.findByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
680+
expect(errorToast).toBeInTheDocument()
681+
})
647682
})

src/steps/SelectHeaderStep/tests/SelectHeaderStep.test.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { StepType } from "../../UploadFlow"
1111

1212
const MUTATED_HEADER = "mutated header"
1313
const CONTINUE_BUTTON = "Next"
14+
const ERROR_MESSAGE = "Something happened"
1415

1516
test("Select header row and click next", async () => {
1617
const data = [
@@ -64,7 +65,7 @@ test("selectHeaderStepHook should be called after header is selected", async ()
6465
],
6566
},
6667
})
67-
const continueButton = await screen.findByText(CONTINUE_BUTTON, undefined, { timeout: 5000 })
68+
const continueButton = await screen.findByText(CONTINUE_BUTTON, undefined, { timeout: 10000 })
6869
fireEvent.click(continueButton)
6970
await waitFor(() => {
7071
expect(selectHeaderStepHook).toBeCalledWith(
@@ -104,3 +105,32 @@ test("selectHeaderStepHook should be able to modify raw data", async () => {
104105
expect(mutatedHeader).toBeInTheDocument()
105106
})
106107
})
108+
109+
test("Should show error toast if error is thrown in selectHeaderStepHook", async () => {
110+
const selectHeaderStepHook = jest.fn(async () => {
111+
throw new Error(ERROR_MESSAGE)
112+
return undefined as any
113+
})
114+
render(
115+
<ReactSpreadsheetImport
116+
{...mockRsiValues}
117+
selectHeaderStepHook={selectHeaderStepHook}
118+
initialStepState={{
119+
type: StepType.selectHeader,
120+
data: [
121+
["name", "age"],
122+
["Josh", "2"],
123+
["Charlie", "3"],
124+
["Lena", "50"],
125+
],
126+
}}
127+
/>,
128+
)
129+
const continueButton = screen.getByText(CONTINUE_BUTTON)
130+
fireEvent.click(continueButton)
131+
const errorToast = await screen.findByText(ERROR_MESSAGE)
132+
133+
await waitFor(() => {
134+
expect(errorToast).toBeInTheDocument()
135+
})
136+
})

src/steps/SelectSheetStep/tests/SelectSheetStep.test.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const SHEET_TITLE_2 = "Sheet2"
1212
const SELECT_HEADER_TABLE_ENTRY_1 = "Charlie"
1313
const SELECT_HEADER_TABLE_ENTRY_2 = "Josh"
1414
const SELECT_HEADER_TABLE_ENTRY_3 = "50"
15+
const ERROR_MESSAGE = "Something happened"
1516

1617
test("Should render select sheet screen if multi-sheet excel file was uploaded", async () => {
1718
render(<ReactSpreadsheetImport {...mockRsiValues} />)
@@ -83,3 +84,35 @@ test("Select sheet and click next", async () => {
8384
})
8485
expect(onContinue.mock.calls[0][0]).toEqual(sheetNames[selectSheetIndex])
8586
})
87+
88+
test("Should show error toast if error is thrown in uploadStepHook", async () => {
89+
const uploadStepHook = jest.fn(async () => {
90+
throw new Error(ERROR_MESSAGE)
91+
return undefined as any
92+
})
93+
render(<ReactSpreadsheetImport {...mockRsiValues} uploadStepHook={uploadStepHook} />)
94+
const uploader = screen.getByTestId("rsi-dropzone")
95+
const data = readFileSync(__dirname + "/../../../../static/Workbook1.xlsx")
96+
fireEvent.drop(uploader, {
97+
target: {
98+
files: [
99+
new File([data], "testFile.xlsx", {
100+
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
101+
}),
102+
],
103+
},
104+
})
105+
106+
const nextButton = await screen.findByRole(
107+
"button",
108+
{
109+
name: "Next",
110+
},
111+
{ timeout: 5000 },
112+
)
113+
114+
userEvent.click(nextButton)
115+
116+
const errorToast = await screen.findByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
117+
expect(errorToast).toBeInTheDocument()
118+
})

src/steps/UploadFlow.tsx

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react"
1+
import { useCallback, useState } from "react"
22
import { Progress, useToast } from "@chakra-ui/react"
33
import type XLSX from "xlsx"
44
import { UploadStep } from "./UploadStep/UploadStep"
@@ -49,6 +49,19 @@ export const UploadFlow = ({ nextStep }: Props) => {
4949
const [state, setState] = useState<StepState>(initialStepState || { type: StepType.upload })
5050
const { maxRecords, translations, uploadStepHook, selectHeaderStepHook, matchColumnsStepHook } = useRsi()
5151
const toast = useToast()
52+
const errorToast = useCallback(
53+
(description: string) => {
54+
toast({
55+
status: "error",
56+
variant: "left-accent",
57+
position: "bottom-left",
58+
title: `${translations.alerts.toast.error}`,
59+
description,
60+
isClosable: true,
61+
})
62+
},
63+
[toast, translations],
64+
)
5265

5366
switch (state.type) {
5467
case StepType.upload:
@@ -58,21 +71,19 @@ export const UploadFlow = ({ nextStep }: Props) => {
5871
const isSingleSheet = workbook.SheetNames.length === 1
5972
if (isSingleSheet) {
6073
if (maxRecords && exceedsMaxRecords(workbook.Sheets[workbook.SheetNames[0]], maxRecords)) {
61-
toast({
62-
status: "error",
63-
variant: "left-accent",
64-
position: "bottom-left",
65-
title: `${translations.uploadStep.maxRecordsExceeded(maxRecords.toString())}`,
66-
isClosable: true,
67-
})
74+
errorToast(translations.uploadStep.maxRecordsExceeded(maxRecords.toString()))
6875
return
6976
}
70-
const mappedWorkbook = await uploadStepHook(mapWorkbook(workbook))
71-
setState({
72-
type: StepType.selectHeader,
73-
data: mappedWorkbook,
74-
})
75-
nextStep()
77+
try {
78+
const mappedWorkbook = await uploadStepHook(mapWorkbook(workbook))
79+
setState({
80+
type: StepType.selectHeader,
81+
data: mappedWorkbook,
82+
})
83+
nextStep()
84+
} catch (e) {
85+
errorToast((e as Error).message)
86+
}
7687
} else {
7788
setState({ type: StepType.selectSheet, workbook })
7889
}
@@ -85,21 +96,19 @@ export const UploadFlow = ({ nextStep }: Props) => {
8596
sheetNames={state.workbook.SheetNames}
8697
onContinue={async (sheetName) => {
8798
if (maxRecords && exceedsMaxRecords(state.workbook.Sheets[sheetName], maxRecords)) {
88-
toast({
89-
status: "error",
90-
variant: "left-accent",
91-
position: "bottom-left",
92-
title: `${translations.uploadStep.maxRecordsExceeded(maxRecords.toString())}`,
93-
isClosable: true,
94-
})
99+
errorToast(translations.uploadStep.maxRecordsExceeded(maxRecords.toString()))
95100
return
96101
}
97-
const mappedWorkbook = await uploadStepHook(mapWorkbook(state.workbook, sheetName))
98-
setState({
99-
type: StepType.selectHeader,
100-
data: mappedWorkbook,
101-
})
102-
nextStep()
102+
try {
103+
const mappedWorkbook = await uploadStepHook(mapWorkbook(state.workbook, sheetName))
104+
setState({
105+
type: StepType.selectHeader,
106+
data: mappedWorkbook,
107+
})
108+
nextStep()
109+
} catch (e) {
110+
errorToast((e as Error).message)
111+
}
103112
}}
104113
/>
105114
)
@@ -108,13 +117,17 @@ export const UploadFlow = ({ nextStep }: Props) => {
108117
<SelectHeaderStep
109118
data={state.data}
110119
onContinue={async (...args) => {
111-
const { data, headerValues } = await selectHeaderStepHook(...args)
112-
setState({
113-
type: StepType.matchColumns,
114-
data,
115-
headerValues,
116-
})
117-
nextStep()
120+
try {
121+
const { data, headerValues } = await selectHeaderStepHook(...args)
122+
setState({
123+
type: StepType.matchColumns,
124+
data,
125+
headerValues,
126+
})
127+
nextStep()
128+
} catch (e) {
129+
errorToast((e as Error).message)
130+
}
118131
}}
119132
/>
120133
)
@@ -124,12 +137,16 @@ export const UploadFlow = ({ nextStep }: Props) => {
124137
data={state.data}
125138
headerValues={state.headerValues}
126139
onContinue={async (values, rawData, columns) => {
127-
const data = await matchColumnsStepHook(values, rawData, columns)
128-
setState({
129-
type: StepType.validateData,
130-
data,
131-
})
132-
nextStep()
140+
try {
141+
const data = await matchColumnsStepHook(values, rawData, columns)
142+
setState({
143+
type: StepType.validateData,
144+
data,
145+
})
146+
nextStep()
147+
} catch (e) {
148+
errorToast((e as Error).message)
149+
}
133150
}}
134151
/>
135152
)

src/steps/UploadStep/tests/UploadStep.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Providers } from "../../../components/Providers"
77
import { ModalWrapper } from "../../../components/ModalWrapper"
88

99
const MUTATED_RAW_DATA = "Bye"
10+
const ERROR_MESSAGE = 'Something happened'
1011

1112
test("Upload a file", async () => {
1213
const file = new File(["Hello, Hello, Hello, Hello"], "test.csv", { type: "text/csv" })
@@ -66,3 +67,20 @@ test("uploadStepHook should be able to mutate raw upload data", async () => {
6667
const el = await screen.findByText(MUTATED_RAW_DATA, undefined, { timeout: 5000 })
6768
expect(el).toBeInTheDocument()
6869
})
70+
71+
test("Should show error toast if error is thrown in uploadStepHook", async () => {
72+
const file = new File(["Hello, Hello, Hello, Hello"], "test.csv", { type: "text/csv" })
73+
const uploadStepHook = jest.fn(async () => {
74+
throw new Error(ERROR_MESSAGE)
75+
return undefined as any
76+
})
77+
render(<ReactSpreadsheetImport {...mockRsiValues} uploadStepHook={uploadStepHook} />)
78+
79+
const uploader = screen.getByTestId("rsi-dropzone")
80+
fireEvent.drop(uploader, {
81+
target: { files: [file] },
82+
})
83+
84+
const errorToast = await screen.findByText(ERROR_MESSAGE, undefined, { timeout: 5000 })
85+
expect(errorToast).toBeInTheDocument()
86+
})

src/translationsRSIProps.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ export const translations = {
6464
cancelButtonTitle: "Cancel",
6565
continueButtonTitle: "Continue",
6666
},
67+
toast: {
68+
error: "Error",
69+
},
6770
},
6871
}
6972

0 commit comments

Comments
 (0)