Skip to content

Commit a65524a

Browse files
authored
🪟 🔧 Add testing and storybook component for CatalogDiffModal (#15426)
* wip diff modal test setup * starting storybook add * storybook working now * cleanup * aria labels * test syncmode string
1 parent 2f17e99 commit a65524a

File tree

9 files changed

+363
-16
lines changed

9 files changed

+363
-16
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import { cleanup, render, screen } from "@testing-library/react";
2+
import userEvent from "@testing-library/user-event";
3+
import { IntlProvider } from "react-intl";
4+
5+
import {
6+
AirbyteCatalog,
7+
CatalogDiff,
8+
DestinationSyncMode,
9+
StreamTransform,
10+
SyncMode,
11+
} from "core/request/AirbyteClient";
12+
13+
import messages from "../../../locales/en.json";
14+
import { CatalogDiffModal } from "./CatalogDiffModal";
15+
16+
const mockCatalogDiff: CatalogDiff = {
17+
transforms: [],
18+
};
19+
20+
const removedItems: StreamTransform[] = [
21+
{
22+
transformType: "remove_stream",
23+
streamDescriptor: { namespace: "apple", name: "dragonfruit" },
24+
},
25+
{
26+
transformType: "remove_stream",
27+
streamDescriptor: { namespace: "apple", name: "eclair" },
28+
},
29+
{
30+
transformType: "remove_stream",
31+
streamDescriptor: { namespace: "apple", name: "fishcake" },
32+
},
33+
{
34+
transformType: "remove_stream",
35+
streamDescriptor: { namespace: "apple", name: "gelatin_mold" },
36+
},
37+
];
38+
39+
const addedItems: StreamTransform[] = [
40+
{
41+
transformType: "add_stream",
42+
streamDescriptor: { namespace: "apple", name: "banana" },
43+
},
44+
{
45+
transformType: "add_stream",
46+
streamDescriptor: { namespace: "apple", name: "carrot" },
47+
},
48+
];
49+
50+
const updatedItems: StreamTransform[] = [
51+
{
52+
transformType: "update_stream",
53+
streamDescriptor: { namespace: "apple", name: "harissa_paste" },
54+
updateStream: [
55+
{ transformType: "add_field", fieldName: ["users", "phone"] },
56+
{ transformType: "add_field", fieldName: ["users", "email"] },
57+
{ transformType: "remove_field", fieldName: ["users", "lastName"] },
58+
59+
{
60+
transformType: "update_field_schema",
61+
fieldName: ["users", "address"],
62+
updateFieldSchema: { oldSchema: { type: "number" }, newSchema: { type: "string" } },
63+
},
64+
],
65+
},
66+
];
67+
68+
const mockCatalog: AirbyteCatalog = {
69+
streams: [
70+
{
71+
stream: {
72+
namespace: "apple",
73+
name: "banana",
74+
},
75+
config: {
76+
syncMode: SyncMode.full_refresh,
77+
destinationSyncMode: DestinationSyncMode.overwrite,
78+
},
79+
},
80+
{
81+
stream: {
82+
namespace: "apple",
83+
name: "carrot",
84+
},
85+
config: {
86+
syncMode: SyncMode.full_refresh,
87+
destinationSyncMode: DestinationSyncMode.overwrite,
88+
},
89+
},
90+
{
91+
stream: {
92+
namespace: "apple",
93+
name: "dragonfruit",
94+
},
95+
config: {
96+
syncMode: SyncMode.full_refresh,
97+
destinationSyncMode: DestinationSyncMode.overwrite,
98+
},
99+
},
100+
{
101+
stream: {
102+
namespace: "apple",
103+
name: "eclair",
104+
},
105+
config: {
106+
syncMode: SyncMode.full_refresh,
107+
destinationSyncMode: DestinationSyncMode.overwrite,
108+
},
109+
},
110+
{
111+
stream: {
112+
namespace: "apple",
113+
name: "fishcake",
114+
},
115+
config: {
116+
syncMode: SyncMode.incremental,
117+
destinationSyncMode: DestinationSyncMode.append_dedup,
118+
},
119+
},
120+
{
121+
stream: {
122+
namespace: "apple",
123+
name: "gelatin_mold",
124+
},
125+
config: {
126+
syncMode: SyncMode.incremental,
127+
destinationSyncMode: DestinationSyncMode.append_dedup,
128+
},
129+
},
130+
{
131+
stream: {
132+
namespace: "apple",
133+
name: "harissa_paste",
134+
},
135+
config: {
136+
syncMode: SyncMode.full_refresh,
137+
destinationSyncMode: DestinationSyncMode.overwrite,
138+
},
139+
},
140+
],
141+
};
142+
143+
describe("catalog diff modal", () => {
144+
afterEach(cleanup);
145+
beforeEach(() => {
146+
mockCatalogDiff.transforms = [];
147+
});
148+
149+
test("it renders the correct section for each type of transform", () => {
150+
mockCatalogDiff.transforms.push(...addedItems, ...removedItems, ...updatedItems);
151+
152+
render(
153+
<IntlProvider messages={messages} locale="en">
154+
<CatalogDiffModal
155+
catalogDiff={mockCatalogDiff}
156+
catalog={mockCatalog}
157+
onClose={() => {
158+
return null;
159+
}}
160+
/>
161+
</IntlProvider>
162+
);
163+
164+
/**
165+
* tests for:
166+
* - proper sections being created
167+
* - syncmode string is only rendered for removed streams
168+
*/
169+
170+
const newStreamsTable = screen.getByRole("table", { name: /new streams/ });
171+
expect(newStreamsTable).toBeInTheDocument();
172+
173+
const newStreamRow = screen.getByRole("row", { name: "apple banana" });
174+
expect(newStreamRow).toBeInTheDocument();
175+
176+
const newStreamRowWithSyncMode = screen.queryByRole("row", { name: "apple carrot incremental | append_dedup" });
177+
expect(newStreamRowWithSyncMode).not.toBeInTheDocument();
178+
179+
const removedStreamsTable = screen.getByRole("table", { name: /removed streams/ });
180+
expect(removedStreamsTable).toBeInTheDocument();
181+
182+
const removedStreamRowWithSyncMode = screen.getByRole("row", {
183+
name: "apple dragonfruit full_refresh | overwrite",
184+
});
185+
expect(removedStreamRowWithSyncMode).toBeInTheDocument();
186+
187+
const updatedStreamsSection = screen.getByRole("list", { name: /table with changes/ });
188+
expect(updatedStreamsSection).toBeInTheDocument();
189+
190+
const updatedStreamRowWithSyncMode = screen.queryByRole("row", {
191+
name: "apple harissa_paste full_refresh | overwrite",
192+
});
193+
expect(updatedStreamRowWithSyncMode).not.toBeInTheDocument();
194+
});
195+
196+
test("added fields are not rendered when not in the diff", () => {
197+
mockCatalogDiff.transforms.push(...removedItems, ...updatedItems);
198+
199+
render(
200+
<IntlProvider messages={messages} locale="en">
201+
<CatalogDiffModal
202+
catalogDiff={mockCatalogDiff}
203+
catalog={mockCatalog}
204+
onClose={() => {
205+
return null;
206+
}}
207+
/>
208+
</IntlProvider>
209+
);
210+
211+
const newStreamsTable = screen.queryByRole("table", { name: /new streams/ });
212+
expect(newStreamsTable).not.toBeInTheDocument();
213+
});
214+
215+
test("removed fields are not rendered when not in the diff", () => {
216+
mockCatalogDiff.transforms.push(...addedItems, ...updatedItems);
217+
218+
render(
219+
<IntlProvider messages={messages} locale="en">
220+
<CatalogDiffModal
221+
catalogDiff={mockCatalogDiff}
222+
catalog={mockCatalog}
223+
onClose={() => {
224+
return null;
225+
}}
226+
/>
227+
</IntlProvider>
228+
);
229+
230+
const removedStreamsTable = screen.queryByRole("table", { name: /removed streams/ });
231+
expect(removedStreamsTable).not.toBeInTheDocument();
232+
});
233+
234+
test("changed streams accordion opens/closes on clicking the description row", () => {
235+
mockCatalogDiff.transforms.push(...addedItems, ...updatedItems);
236+
237+
render(
238+
<IntlProvider messages={messages} locale="en">
239+
<CatalogDiffModal
240+
catalogDiff={mockCatalogDiff}
241+
catalog={mockCatalog}
242+
onClose={() => {
243+
return null;
244+
}}
245+
/>
246+
</IntlProvider>
247+
);
248+
249+
const accordionHeader = screen.getByRole("button", { name: /toggle accordion/ });
250+
251+
expect(accordionHeader).toBeInTheDocument();
252+
253+
const nullAccordionBody = screen.queryByRole("table", { name: /removed fields/ });
254+
expect(nullAccordionBody).not.toBeInTheDocument();
255+
256+
userEvent.click(accordionHeader);
257+
const openAccordionBody = screen.getByRole("table", { name: /removed fields/ });
258+
expect(openAccordionBody).toBeInTheDocument();
259+
260+
userEvent.click(accordionHeader);
261+
const nullAccordionBodyAgain = screen.queryByRole("table", { name: /removed fields/ });
262+
expect(nullAccordionBodyAgain).not.toBeInTheDocument();
263+
mockCatalogDiff.transforms = [];
264+
});
265+
});

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffAccordion.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const DiffAccordion: React.FC<DiffAccordionProps> = ({ streamTransform })
2323
<Disclosure>
2424
{({ open }) => (
2525
<>
26-
<Disclosure.Button className={styles.accordionButton}>
26+
<Disclosure.Button className={styles.accordionButton} aria-label="toggle accordion">
2727
<DiffAccordionHeader
2828
streamDescriptor={streamTransform.streamDescriptor}
2929
removedCount={removedItems.length}

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffFieldTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface DiffFieldTableProps {
1414

1515
export const DiffFieldTable: React.FC<DiffFieldTableProps> = ({ fieldTransforms, diffVerb }) => {
1616
return (
17-
<table className={styles.table}>
17+
<table className={styles.table} aria-label={`${diffVerb} fields`}>
1818
<thead>
1919
<tr className={styles.accordionSubHeader}>
2020
<th>

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffIconBlock.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ export const DiffIconBlock: React.FC<DiffIconBlockProps> = ({ newCount, removedC
1919
num={removedCount}
2020
color="red"
2121
light
22-
ariaLabel={`${removedCount} ${formatMessage(
22+
ariaLabel={`${formatMessage(
2323
{
2424
id: "connection.updateSchema.removed",
2525
},
2626
{
2727
value: removedCount,
28-
item: formatMessage({ id: "field" }, { values: { count: removedCount } }),
28+
item: formatMessage({ id: "connection.updateSchema.field" }, { count: removedCount }),
2929
}
3030
)}`}
3131
/>
@@ -35,13 +35,13 @@ export const DiffIconBlock: React.FC<DiffIconBlockProps> = ({ newCount, removedC
3535
num={newCount}
3636
color="green"
3737
light
38-
ariaLabel={`${newCount} ${formatMessage(
38+
ariaLabel={`${formatMessage(
3939
{
4040
id: "connection.updateSchema.new",
4141
},
4242
{
4343
value: newCount,
44-
item: formatMessage({ id: "field" }, { values: { count: newCount } }),
44+
item: formatMessage({ id: "connection.updateSchema.field" }, { count: newCount }),
4545
}
4646
)}`}
4747
/>
@@ -51,13 +51,13 @@ export const DiffIconBlock: React.FC<DiffIconBlockProps> = ({ newCount, removedC
5151
num={changedCount}
5252
color="blue"
5353
light
54-
ariaLabel={`${changedCount} ${formatMessage(
54+
ariaLabel={`${formatMessage(
5555
{
5656
id: "connection.updateSchema.changed",
5757
},
5858
{
5959
value: changedCount,
60-
item: formatMessage({ id: "field" }, { values: { count: changedCount } }),
60+
item: formatMessage({ id: "connection.updateSchema.field" }, { count: changedCount }),
6161
}
6262
)}`}
6363
/>

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/DiffSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const DiffSection: React.FC<DiffSectionProps> = ({ streams, catalog, diff
3131
<div className={styles.sectionHeader}>
3232
<DiffHeader diffCount={streams.length} diffVerb={diffVerb} diffType="stream" />
3333
</div>
34-
<table>
34+
<table aria-label={`${diffVerb} streams table`}>
3535
<thead className={styles.sectionSubHeader}>
3636
<tr>
3737
<th>

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/FieldRow.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const FieldRow: React.FC<FieldRowProps> = ({ transform }) => {
2828
[styles.mod]: diffType === "update",
2929
});
3030

31-
const contentStyle = classnames(styles.content, {
31+
const contentStyle = classnames(styles.content, styles.cell, {
3232
[styles.add]: diffType === "add",
3333
[styles.remove]: diffType === "remove",
3434
[styles.update]: diffType === "update",
@@ -50,16 +50,16 @@ export const FieldRow: React.FC<FieldRowProps> = ({ transform }) => {
5050
)}
5151
</td>
5252
<td className={contentStyle}>
53-
<td className={styles.cell}>
53+
<div className={styles.cell}>
5454
<span>{fieldName}</span>
55-
</td>
56-
<td className={updateCellStyle}>
55+
</div>
56+
<div className={updateCellStyle}>
5757
{oldType && newType && (
5858
<span>
5959
{oldType} <FontAwesomeIcon icon={faArrowRight} /> {newType}
6060
</span>
6161
)}
62-
</td>
62+
</div>
6363
</td>
6464
</tr>
6565
);

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/FieldSection.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,15 @@ export const FieldSection: React.FC<FieldSectionProps> = ({ streams, diffVerb })
3232
</div>
3333
<div className={styles.fieldRowsContainer}>
3434
{streams.length > 0 && (
35-
<ul>
35+
<ul
36+
aria-label={formatMessage(
37+
{ id: "connection.updateSchema.changed" },
38+
{
39+
value: streams.length,
40+
item: formatMessage({ id: "connection.updateSchema.stream" }, { count: streams.length }),
41+
}
42+
)}
43+
>
3644
{streams.map((stream) => {
3745
return (
3846
<li key={`${stream.streamDescriptor.namespace}.${stream.streamDescriptor.name}`}>

airbyte-webapp/src/views/Connection/CatalogDiffModal/components/StreamRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const StreamRow: React.FC<StreamRowProps> = ({ streamTransform, syncMode,
4545
)}
4646
</td>
4747
<td className={styles.nameCell}>{namespace}</td>
48-
<td className={styles.nameCell}>{itemName}</td>{" "}
48+
<td className={styles.nameCell}>{itemName}</td>
4949
<td>{diffVerb === "removed" && syncMode && <SyncModeBox syncModeString={syncMode} />} </td>
5050
</tr>
5151
);

0 commit comments

Comments
 (0)