diff --git a/package-lock.json b/package-lock.json
index 6a2e83e5..16d7ec37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,6 +42,7 @@
"jest": "27.5.1",
"jest-watch-typeahead": "1.0.0",
"prettier": "2.5.1",
+ "react-select-event": "5.3.0",
"rollup": "2.67.3",
"rollup-plugin-typescript2": "0.31.2",
"ts-essentials": "9.1.2",
@@ -17952,6 +17953,15 @@
"react-dom": "^16.8.0 || ^17.0.0"
}
},
+ "node_modules/react-select-event": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-select-event/-/react-select-event-5.3.0.tgz",
+ "integrity": "sha512-Novkl7X9JJKmDV5LyYaKwl0vffWtqPrBa1vuI0v43P/f87mSA7JfdYxU93SFb99RssphVzBSIAbcnbX1w21QIQ==",
+ "dev": true,
+ "dependencies": {
+ "@testing-library/dom": ">=7"
+ }
+ },
"node_modules/react-sizeme": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz",
@@ -36515,6 +36525,15 @@
"react-transition-group": "^4.3.0"
}
},
+ "react-select-event": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-select-event/-/react-select-event-5.3.0.tgz",
+ "integrity": "sha512-Novkl7X9JJKmDV5LyYaKwl0vffWtqPrBa1vuI0v43P/f87mSA7JfdYxU93SFb99RssphVzBSIAbcnbX1w21QIQ==",
+ "dev": true,
+ "requires": {
+ "@testing-library/dom": ">=7"
+ }
+ },
"react-sizeme": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz",
diff --git a/package.json b/package.json
index 374767ad..4d1606a3 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
"jest": "27.5.1",
"jest-watch-typeahead": "1.0.0",
"prettier": "2.5.1",
+ "react-select-event": "5.3.0",
"rollup": "2.67.3",
"rollup-plugin-typescript2": "0.31.2",
"ts-essentials": "9.1.2",
diff --git a/src/components/Selects/MatchColumnSelect.tsx b/src/components/Selects/MatchColumnSelect.tsx
index 4f9dae68..13866738 100644
--- a/src/components/Selects/MatchColumnSelect.tsx
+++ b/src/components/Selects/MatchColumnSelect.tsx
@@ -9,9 +9,10 @@ interface Props {
value?: SelectOption
options: readonly SelectOption[]
placeholder?: string
+ name?: string
}
-export const MatchColumnSelect = ({ onChange, value, options, placeholder }: Props) => {
+export const MatchColumnSelect = ({ onChange, value, options, placeholder, name }: Props) => {
const styles = useStyleConfig("MatchColumnsStep") as Styles
return (
)
}
diff --git a/src/steps/MatchColumnsStep/components/MatchIcon.tsx b/src/steps/MatchColumnsStep/components/MatchIcon.tsx
index eff62b3e..135409f8 100644
--- a/src/steps/MatchColumnsStep/components/MatchIcon.tsx
+++ b/src/steps/MatchColumnsStep/components/MatchIcon.tsx
@@ -26,13 +26,14 @@ export const MatchIcon = (props: MatchIconProps) => {
minH={6}
w={6}
h={6}
- data-highlighted={dataAttr(props.isChecked)}
ml="0.875rem"
mr={3}
+ data-highlighted={dataAttr(props.isChecked)}
+ data-testid="column-checkmark"
>
{props.isChecked && (
-
+
)}
diff --git a/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx b/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx
index 02758299..fbc3bc31 100644
--- a/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx
+++ b/src/steps/MatchColumnsStep/components/SubMatchingSelect.tsx
@@ -25,6 +25,7 @@ export const SubMatchingSelect = ({ option, column, onSubChang
placeholder={translations.matchColumnsStep.subSelectPlaceholder}
onChange={(value) => onSubChange(value?.value as T, column.index, option.entry!)}
options={options}
+ name={option.entry}
/>
)
diff --git a/src/steps/MatchColumnsStep/components/TemplateColumn.tsx b/src/steps/MatchColumnsStep/components/TemplateColumn.tsx
index f31130ff..6fbebd30 100644
--- a/src/steps/MatchColumnsStep/components/TemplateColumn.tsx
+++ b/src/steps/MatchColumnsStep/components/TemplateColumn.tsx
@@ -57,6 +57,7 @@ export const TemplateColumn = ({ column, onChange, onSubChange
value={selectValue}
onChange={(value) => onChange(value?.value as T, column.index)}
options={selectOptions}
+ name={column.header}
/>
@@ -65,7 +66,13 @@ export const TemplateColumn = ({ column, onChange, onSubChange
-
+
@@ -75,7 +82,7 @@ export const TemplateColumn = ({ column, onChange, onSubChange
{column.matchedOptions.map((option) => (
-
+
))}
diff --git a/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx b/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx
index 1dbcfdeb..f3dd62c1 100644
--- a/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx
+++ b/src/steps/MatchColumnsStep/tests/MatchColumnsStep.test.tsx
@@ -7,46 +7,557 @@ import { Providers } from "../../../components/Providers"
import { ModalWrapper } from "../../../components/ModalWrapper"
import userEvent from "@testing-library/user-event"
import type { Fields } from "../../../types"
+import selectEvent from "react-select-event"
+import { translations } from "../../../translationsRSIProps"
const fields: Fields = [
{
label: "Name",
key: "name",
- alternateMatches: ["first name", "first"],
fieldType: {
type: "input",
},
example: "Stephanie",
},
+ {
+ label: "Mobile Phone",
+ key: "mobile",
+ fieldType: {
+ type: "input",
+ },
+ example: "+12323423",
+ },
+ {
+ label: "Is cool",
+ key: "is_cool",
+ fieldType: {
+ type: "checkbox",
+ },
+ example: "No",
+ },
]
-test("Match columns and click next", async () => {
- const header = ["Name", "Phone", "Email"]
- const data = [
- ["John", "123", "j@j.com"],
- ["Dane", "333", "dane@bane.com"],
- ["Kane", "534", "kane@linch.com"],
- ]
- // finds only names with automatic matching
- const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
-
- const onContinue = jest.fn()
- render(
-
- {}}>
-
-
- ,
- )
-
- const nextButton = screen.getByRole("button", {
- name: "Next",
- })
-
- userEvent.click(nextButton)
-
- await waitFor(() => {
- expect(onContinue).toBeCalled()
- expect(onContinue.mock.calls[0][0]).toEqual(result)
+describe("Match Columns automatic matching", () => {
+ test("AutoMatch column and click next", async () => {
+ const header = ["namezz", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+ // finds only names with automatic matching
+ const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("AutoMatching disabled does not match any columns", async () => {
+ const header = ["Name", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+ // finds only names with automatic matching
+ const result = [{}, {}, {}]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("AutoMatching exact values", async () => {
+ const header = ["Name", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+ // finds only names with automatic matching
+ const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("AutoMatches only one value", async () => {
+ const header = ["first name", "name", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+ // finds only names with automatic matching
+ const result = [{ name: data[0][1] }, { name: data[1][1] }, { name: data[2][1] }]
+
+ const alternativeFields = [
+ {
+ label: "Name",
+ key: "name",
+ alternateMatches: ["first name"],
+ fieldType: {
+ type: "input",
+ },
+ example: "Stephanie",
+ },
+ ] as const
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("Boolean-like values are returned as Booleans", async () => {
+ const header = ["namezz", "is_cool", "Email"]
+ const data = [
+ ["John", "yes", "j@j.com"],
+ ["Dane", "TRUE", "dane@bane.com"],
+ ["Kane", "false", "kane@linch.com"],
+ ["Kaney", "no", "kane@linch.com"],
+ ["Kanye", "maybe", "kane@linch.com"],
+ ]
+
+ const result = [
+ { name: data[0][0], is_cool: true },
+ { name: data[1][0], is_cool: true },
+ { name: data[2][0], is_cool: false },
+ { name: data[3][0], is_cool: false },
+ { name: data[4][0], is_cool: false },
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("Boolean-like values are returned as Booleans for 'booleanMatches' props", async () => {
+ const BOOLEAN_MATCHES_VALUE = "definitely"
+ const header = ["is_cool"]
+ const data = [["true"], ["false"], [BOOLEAN_MATCHES_VALUE]]
+
+ const fields = [
+ {
+ label: "Is cool",
+ key: "is_cool",
+ fieldType: {
+ type: "checkbox",
+ booleanMatches: { [BOOLEAN_MATCHES_VALUE]: true },
+ },
+ example: "No",
+ },
+ ] as const
+
+ const result = [{ is_cool: true }, { is_cool: false }, { is_cool: true }]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+})
+
+describe("Match Columns general tests", () => {
+ test("Displays all user header columns", async () => {
+ const header = ["namezz", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ expect(screen.getByText(header[0])).toBeInTheDocument()
+ expect(screen.getByText(header[1])).toBeInTheDocument()
+ expect(screen.getByText(header[2])).toBeInTheDocument()
+ })
+
+ test("Displays two rows of example data", async () => {
+ const header = ["namezz", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ // only displays two rows
+ expect(screen.queryByText(data[0][0])).toBeInTheDocument()
+ expect(screen.queryByText(data[0][1])).toBeInTheDocument()
+ expect(screen.queryByText(data[0][2])).toBeInTheDocument()
+ expect(screen.queryByText(data[1][0])).toBeInTheDocument()
+ expect(screen.queryByText(data[1][1])).toBeInTheDocument()
+ expect(screen.queryByText(data[1][2])).toBeInTheDocument()
+ expect(screen.queryByText(data[2][0])).not.toBeInTheDocument()
+ expect(screen.queryByText(data[2][1])).not.toBeInTheDocument()
+ expect(screen.queryByText(data[2][2])).not.toBeInTheDocument()
+ })
+
+ test("Displays all fields in selects dropdown", async () => {
+ const header = ["Something random", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const firstSelect = screen.getByLabelText(header[0])
+
+ userEvent.click(firstSelect)
+
+ fields.forEach((field) => {
+ expect(screen.queryByText(field.label)).toBeInTheDocument()
+ })
+ })
+
+ test("Manually matches first column", async () => {
+ const header = ["Something random", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+ const result = [{ name: data[0][0] }, { name: data[1][0] }, { name: data[2][0] }]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ await selectEvent.select(screen.getByLabelText(header[0]), fields[0].label)
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("Checkmark changes when field is matched", async () => {
+ const header = ["Something random", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const checkmark = screen.getAllByTestId("column-checkmark")[0]
+ // kinda dumb way to check if it has checkmark or not
+ expect(checkmark).toBeEmptyDOMElement()
+
+ await selectEvent.select(screen.getByLabelText(header[0]), fields[0].label)
+
+ expect(checkmark).not.toBeEmptyDOMElement()
+ })
+
+ test("Selecting select field adds more selects", async () => {
+ const OPTION_ONE = "one"
+ const OPTION_TWO = "two"
+ const OPTION_RESULT_ONE = "uno"
+ const OPTION_RESULT_TWO = "dos"
+ const options = [
+ { label: "One", value: OPTION_RESULT_ONE },
+ { label: "Two", value: OPTION_RESULT_TWO },
+ ]
+ const header = ["Something random"]
+ const data = [[OPTION_ONE], [OPTION_TWO], [OPTION_ONE]]
+
+ const result = [
+ {
+ team: OPTION_RESULT_ONE,
+ },
+ {
+ team: OPTION_RESULT_TWO,
+ },
+ {
+ team: OPTION_RESULT_ONE,
+ },
+ ]
+
+ const enumFields = [
+ {
+ label: "Team",
+ key: "team",
+ fieldType: {
+ type: "select",
+ options: options,
+ },
+ },
+ ] as const
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ expect(screen.queryByTestId("accordion-button")).not.toBeInTheDocument()
+
+ await selectEvent.select(screen.getByLabelText(header[0]), enumFields[0].label)
+
+ expect(screen.queryByTestId("accordion-button")).toBeInTheDocument()
+
+ userEvent.click(screen.getByTestId("accordion-button"))
+
+ await selectEvent.select(screen.getByLabelText(data[0][0]), options[0].label)
+
+ await selectEvent.select(screen.getByLabelText(data[1][0]), options[1].label)
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ expect(onContinue.mock.calls[0][0]).toEqual(result)
+ })
+ })
+
+ test("Can ignore columns", async () => {
+ const header = ["Something random", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const ignoreButton = screen.getAllByLabelText("Ignore column")[0]
+
+ expect(screen.queryByText(translations.matchColumnsStep.ignoredColumnText)).not.toBeInTheDocument()
+
+ userEvent.click(ignoreButton)
+
+ expect(screen.queryByText(translations.matchColumnsStep.ignoredColumnText)).toBeInTheDocument()
+ })
+
+ test("Required unselected fields show warning alert on submit", async () => {
+ const header = ["Something random", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const requiredFields = [
+ {
+ label: "Name",
+ key: "name",
+ fieldType: {
+ type: "input",
+ },
+ example: "Stephanie",
+ validations: [
+ {
+ rule: "required",
+ errorMessage: "Hello",
+ },
+ ],
+ },
+ ] as const
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ const nextButton = screen.getByRole("button", {
+ name: "Next",
+ })
+
+ userEvent.click(nextButton)
+
+ expect(onContinue).not.toBeCalled()
+ expect(screen.queryByText(translations.alerts.unmatchedRequiredFields.bodyText)).toBeInTheDocument()
+
+ const continueButton = screen.getByRole("button", {
+ name: "Continue",
+ })
+
+ userEvent.click(continueButton)
+
+ await waitFor(() => {
+ expect(onContinue).toBeCalled()
+ })
+ })
+
+ test("Required unselected fields show warning alert on submit", async () => {
+ const header = ["Something random", "Phone", "Email"]
+ const data = [
+ ["John", "123", "j@j.com"],
+ ["Dane", "333", "dane@bane.com"],
+ ["Kane", "534", "kane@linch.com"],
+ ]
+
+ const onContinue = jest.fn()
+ render(
+
+ {}}>
+
+
+ ,
+ )
+
+ await selectEvent.select(screen.getByLabelText(header[0]), fields[0].label)
+ await selectEvent.select(screen.getByLabelText(header[1]), fields[0].label)
+
+ expect(screen.queryByText(translations.matchColumnsStep.duplicateColumnWarningDescription)).toBeInTheDocument()
})
})