Skip to content

Commit

Permalink
add testing
Browse files Browse the repository at this point in the history
Co-authored-by: Krishna (kc) Glick <krishnaglick@users.noreply.github.com>
  • Loading branch information
teallarson and krishnaglick committed Nov 21, 2022
1 parent 7696f85 commit 724c8c8
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 33 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { StreamHeaderProps } from "../StreamHeader";
import styles from "./CatalogTreeTableRow.module.scss";
import { StreamPathSelect } from "./StreamPathSelect";
import { SyncModeSelect } from "./SyncModeSelect";
import { useRowStatus } from "./useRowStatus";
import { useCatalogTreeRowProps } from "./useCatalogTreeRowProps";

export const CatalogTreeTableRow: React.FC<StreamHeaderProps> = ({
stream,
Expand Down Expand Up @@ -52,7 +52,7 @@ export const CatalogTreeTableRow: React.FC<StreamHeaderProps> = ({
const fieldCount = fields?.length ?? 0;
const onRowClick = fieldCount > 0 ? () => onExpand() : undefined;

const { streamHeaderContentStyle, statusIcon, pillButtonVariant } = useRowStatus(stream);
const { streamHeaderContentStyle, statusIcon, pillButtonVariant } = useCatalogTreeRowProps(stream);

const checkboxCellCustomStyle = classnames(styles.checkboxCell, styles.streamRowCheckboxCell);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { renderHook } from "@testing-library/react-hooks";
import classNames from "classnames";
import * as formik from "formik";

import { AirbyteStreamAndConfiguration } from "core/request/AirbyteClient";
import * as bulkEditService from "hooks/services/BulkEdit/BulkEditService";
import * as connectionFormService from "hooks/services/ConnectionForm/ConnectionFormService";
import { FormikConnectionFormValues } from "views/Connection/ConnectionForm/formConfig";

// eslint-disable-next-line css-modules/no-unused-class
import styles from "./CatalogTreeTableRow.module.scss";
import { useCatalogTreeRowProps } from "./useCatalogTreeRowProps";

const mockStream: Partial<AirbyteStreamAndConfiguration> = {
stream: {
name: "stream_name",
namespace: "stream_namespace",
},
config: { selected: true, syncMode: "full_refresh", destinationSyncMode: "overwrite" },
};

const mockInitialValues: Partial<FormikConnectionFormValues> = {
syncCatalog: {
streams: [
{
stream: {
name: "stream_name",
namespace: "stream_namespace",
},
config: { selected: true, syncMode: "full_refresh", destinationSyncMode: "overwrite" },
},
],
},
};

describe("<CatalogTreeTableRow />", () => {
it("should return default styles for a row that starts enabled", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
// eslint-disable-next-line
return { initialValues: mockInitialValues } as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});

const { result } = renderHook(() => useCatalogTreeRowProps(mockStream));

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent));
expect(result.current.statusIcon).toEqual(null);
expect(result.current.pillButtonVariant).toEqual("grey");
});
it("should return disabled styles for a row that starts disabled", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
return {
initialValues: {
syncCatalog: {
streams: [
{
...mockInitialValues.syncCatalog?.streams[0],
config: { ...mockInitialValues.syncCatalog?.streams[0].config, selected: false },
},
],
},
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});
const { result } = renderHook(() =>
useCatalogTreeRowProps({
...mockStream,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
config: { ...mockStream.config!, selected: false },
})
);

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.disabled));
expect(result.current.statusIcon).toEqual(null);
expect(result.current.pillButtonVariant).toEqual("grey");
});
it("should return added styles for a row that is added", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
return {
initialValues: {
syncCatalog: {
streams: [
{
...mockInitialValues.syncCatalog?.streams[0],
config: { ...mockInitialValues.syncCatalog?.streams[0].config, selected: false },
},
],
},
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});
const { result } = renderHook(() =>
useCatalogTreeRowProps({
...mockStream,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
config: { ...mockStream.config!, selected: true }, // selected true
})
);

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.added));
// expect(result.current.statusIcon).(); TODO: jest is weird at comparing react nodes
expect(result.current.pillButtonVariant).toEqual("green");
});
it("should return removed styles for a row that is removed", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
return {
initialValues: { ...mockInitialValues },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});
const { result } = renderHook(() =>
useCatalogTreeRowProps({
...mockStream,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
config: { ...mockStream.config!, selected: false }, // selected false
})
);

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.removed));
// expect(result.current.statusIcon).(); TODO: jest is weird at comparing react nodes
expect(result.current.pillButtonVariant).toEqual("red");
});
it("should return updated styles for a row that is updated", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
// todo: i'm only changing config.selected... this can be cleaned up with a spread operator and the mockInitialValues
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
return {
initialValues: { ...mockInitialValues },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});
const { result } = renderHook(() =>
useCatalogTreeRowProps({
...mockStream,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
config: { ...mockStream.config!, syncMode: "incremental", destinationSyncMode: "append_dedup" }, // new sync mode and destination sync mode
})
);

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.changed));
// expect(result.current.statusIcon).(); TODO: jest is weird at comparing react nodes
expect(result.current.pillButtonVariant).toEqual("blue");
});
it("should return added styles for a row that is both added and updated", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
// todo: i'm only changing config.selected... this can be cleaned up with a spread operator and the mockInitialValues
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
return {
initialValues: {
syncCatalog: {
streams: [
{
...mockInitialValues.syncCatalog?.streams[0],
config: { ...mockInitialValues.syncCatalog?.streams[0].config, selected: false },
},
],
},
}, // eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});
const { result } = renderHook(() =>
useCatalogTreeRowProps({
...mockStream,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
config: { selected: true, syncMode: "incremental", destinationSyncMode: "append_dedup" }, // selected true, new sync, mode and destination sync mode
})
);

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.added));
// expect(result.current.statusIcon).(); TODO: jest is weird at comparing react nodes
expect(result.current.pillButtonVariant).toEqual("green");
});
it("should return change background color with relevant icon if selected for bulk edit", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [true, () => null] as any); // not selected for bulk edit
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
// eslint-disable-next-line
return { initialValues: mockInitialValues } as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: undefined }] as any; // no error
});

const { result } = renderHook(() => useCatalogTreeRowProps(mockStream));

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.changed));
// expect(result.current.statusIcon).(); TODO: jest is weird at comparing react nodes
expect(result.current.pillButtonVariant).toEqual("blue");
});
it("should return error styles for a row that has an error", () => {
// eslint-disable-next-line
jest.spyOn(bulkEditService, "useBulkEditSelect").mockImplementation(() => [false, () => null] as any); // not selected for bulk edit
jest.spyOn(connectionFormService, "useConnectionFormService").mockImplementation(() => {
// eslint-disable-next-line
return { initialValues: mockInitialValues } as any;
});
jest.spyOn(formik, "useField").mockImplementationOnce(() => {
// eslint-disable-next-line
return [{}, { error: true }] as any; // no error
});

const { result } = renderHook(() => useCatalogTreeRowProps(mockStream));

expect(result.current.streamHeaderContentStyle).toEqual(classNames(styles.streamHeaderContent, styles.error));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useConnectionFormService } from "hooks/services/ConnectionForm/Connecti

import styles from "./CatalogTreeTableRow.module.scss";

export const useRowStatus = (stream: SyncSchemaStream) => {
export const useCatalogTreeRowProps = (stream: SyncSchemaStream) => {
const { initialValues } = useConnectionFormService();
const [isSelected] = useBulkEditSelect(stream.id);

Expand All @@ -25,23 +25,24 @@ export const useRowStatus = (stream: SyncSchemaStream) => {
const hasError = error !== undefined;

const isStreamEnabled = stream.config?.selected;
const rowStatusChanged =
initialValues.syncCatalog.streams.find(
(item) => item.stream?.name === stream.stream?.name && item.stream?.namespace === stream.stream?.namespace
)?.config?.selected !== stream.config?.selected;

const rowChanged = !isEqual(
initialValues.syncCatalog.streams.find(
(item) =>
item.stream &&
stream.stream &&
item.stream.name === stream.stream.name &&
item.stream.namespace === stream.stream.namespace
)?.config,
stream.config
);

const statusToDisplay = useMemo(() => {
const rowStatusChanged =
initialValues.syncCatalog.streams.find(
(item) => item.stream?.name === stream.stream?.name && item.stream?.namespace === stream.stream?.namespace
)?.config?.selected !== stream.config?.selected;

const rowChanged = !isEqual(
initialValues.syncCatalog.streams.find(
(item) =>
item.stream &&
stream.stream &&
item.stream.name === stream.stream.name &&
item.stream.namespace === stream.stream.namespace
)?.config,
stream.config
);

if (rowStatusChanged) {
return isStreamEnabled ? "added" : "removed";
} else if (rowChanged) {
Expand All @@ -50,18 +51,18 @@ export const useRowStatus = (stream: SyncSchemaStream) => {
return "disabled";
}
return "unchanged";
}, [isStreamEnabled, rowChanged, rowStatusChanged]);
}, [initialValues.syncCatalog.streams, isStreamEnabled, stream.config, stream.stream]);

const pillButtonVariant: PillButtonVariant = useMemo(() => {
if (statusToDisplay === "added") {
return "green";
} else if (statusToDisplay === "removed") {
return "red";
} else if (statusToDisplay === "changed") {
} else if (statusToDisplay === "changed" || isSelected) {
return "blue";
}
return "grey";
}, [statusToDisplay]);
}, [isSelected, statusToDisplay]);

const statusIcon = useMemo(() => {
if (statusToDisplay === "added") {
Expand All @@ -81,7 +82,7 @@ export const useRowStatus = (stream: SyncSchemaStream) => {
const streamHeaderContentStyle = classNames(styles.streamHeaderContent, {
[styles.added]: statusToDisplay === "added",
[styles.removed]: statusToDisplay === "removed",
[styles.changed]: isSelected || statusToDisplay === "changed",
[styles.changed]: statusToDisplay === "changed" || isSelected,
[styles.disabled]: statusToDisplay === "disabled",
[styles.error]: hasError,
});
Expand Down

0 comments on commit 724c8c8

Please sign in to comment.