Skip to content

Commit

Permalink
add unit test for TemplateCard
Browse files Browse the repository at this point in the history
  • Loading branch information
Rocio De Santiago authored and Rocio De Santiago committed Aug 2, 2024
1 parent f43e6e3 commit 614cc2d
Show file tree
Hide file tree
Showing 13 changed files with 649 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
// utils
import { RouterWrappedComponent } from "utils/testing/setupJest";
// components
import { TemplateCardAccordion } from "components";
import verbiage from "verbiage/pages/home";
import { AnyObject } from "types";
import { testA11y } from "utils/testing/commonTests";

const accordionComponent = (mockProps?: AnyObject) => {
const props = {
verbiage: verbiage.cards.WP.accordion,
...mockProps,
};
return (
<RouterWrappedComponent>
<TemplateCardAccordion {...props} />
</RouterWrappedComponent>
);
};

const accordionContent = verbiage.cards.WP.accordion.text[0].content;
const accordionButtonLabel = verbiage.cards.WP.accordion.buttonLabel;

describe("<TemplateCardAccordion />", () => {
test("Accordion is visible", () => {
render(accordionComponent());
expect(screen.getByText(accordionButtonLabel)).toBeVisible();
});

test("Accordion default closed state only shows the question", () => {
render(accordionComponent());
expect(screen.getByText(accordionButtonLabel)).toBeVisible();
expect(screen.getByText(accordionContent)).not.toBeVisible();
});

test("Accordion should show answer on click", async () => {
render(accordionComponent());
const accordionQuestion = screen.getByText(accordionButtonLabel);
expect(accordionQuestion).toBeVisible();
expect(screen.getByText(accordionContent)).not.toBeVisible();
await userEvent.click(accordionQuestion);
expect(accordionQuestion).toBeVisible();
expect(screen.getByText(accordionContent)).toBeVisible();
});

test("Accordion should render a list when given one", async () => {
const mockProps = {
verbiage: {
buttonLabel: "expand",
list: ["item one", "item two", "item three"],
},
};

render(accordionComponent(mockProps));
const button = screen.getByText("expand");
await userEvent.click(button);

expect(screen.getByText("item one")).toBeVisible();
expect(screen.getByText("item two")).toBeVisible();
expect(screen.getByText("item three")).toBeVisible();
});

test("Accordion should render a table when given one", async () => {
const mockProps = {
verbiage: {
buttonLabel: "expand",
table: {
headRow: ["mock column header"],
},
},
};

render(accordionComponent(mockProps));
const button = screen.getByText("expand");
await userEvent.click(button);

expect(screen.getByText("mock column header")).toBeVisible();
});

testA11y(accordionComponent());
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// components
import { Accordion, ListItem, UnorderedList } from "@chakra-ui/react";
import { AccordionItem, Table } from "components";
// utils
import { AnyObject } from "types";
import { parseCustomHtml } from "utils";

export const TemplateCardAccordion = ({ verbiage, ...props }: Props) => (
<Accordion sx={sx.root} allowToggle={true} {...props}>
<AccordionItem sx={sx.text} label={verbiage.buttonLabel}>
{parseCustomHtml(verbiage.text)}
{verbiage.table && (
<Table
content={verbiage.table}
variant="striped"
sxOverride={sx.table}
/>
)}
{verbiage.list && (
<UnorderedList sx={sx.list}>
{verbiage.list.map((listItem: string, index: number) => (
<ListItem key={index}>{listItem}</ListItem>
))}
</UnorderedList>
)}
</AccordionItem>
</Accordion>
);

interface Props {
verbiage: AnyObject;
[key: string]: any;
}

const sx = {
root: {
marginTop: "2rem",
},
text: {
p: {
marginBottom: "1rem",
},
},
table: {
"tr td:last-of-type": {
fontWeight: "semibold",
},
},
list: {
paddingLeft: "1rem",
"li:last-of-type": {
fontWeight: "bold",
},
},
};
3 changes: 2 additions & 1 deletion services/ui-src/src/components/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Route, Routes } from "react-router-dom";
import { HelpPage } from "components";
import { HelpPage, HomePage } from "components";

export const AppRoutes = () => {
return (
<main id="main-content" tabIndex={-1}>
<Routes>
{/* General Routes */}
<Route path="/" element={<HomePage />} />
<Route path="/help" element={<HelpPage />} />
</Routes>
</main>
Expand Down
66 changes: 66 additions & 0 deletions services/ui-src/src/components/cards/TemplateCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
// components
import { TemplateCard } from "components";
// utils
import { mockUseStore, RouterWrappedComponent } from "utils/testing/setupJest";
import { useStore } from "utils";
// verbiage
import verbiage from "verbiage/pages/home";
import { testA11y } from "utils/testing/commonTests";

jest.mock("utils/other/useBreakpoint", () => ({
useBreakpoint: jest.fn(() => ({
isDesktop: true,
})),
}));

jest.mock("utils/state/useStore");

const mockedUseStore = useStore as jest.MockedFunction<typeof useStore>;

const mockUseNavigate = jest.fn();

jest.mock("react-router-dom", () => ({
useNavigate: () => mockUseNavigate,
}));

const wpTemplateVerbiage = verbiage.cards.QM;

const wpTemplateCardComponent = (
<RouterWrappedComponent>
<TemplateCard templateName="WP" verbiage={wpTemplateVerbiage} />
</RouterWrappedComponent>
);

describe("<TemplateCard />", () => {
describe("Renders", () => {
beforeEach(() => {
render(wpTemplateCardComponent);
});

test("QM TemplateCard is visible", () => {
expect(screen.getByText(wpTemplateVerbiage.title)).toBeVisible();
});

test("QM TemplateCard image is visible on desktop", () => {
const imageAltText = "Spreadsheet icon";
expect(screen.getByAltText(imageAltText)).toBeVisible();
});

test("QM TemplateCard link is visible on desktop", () => {
const templateCardLink = wpTemplateVerbiage.link.text;
expect(screen.getByText(templateCardLink)).toBeVisible();
});

test("QM TemplateCard navigates to next route on link click", async () => {
mockedUseStore.mockReturnValue(mockUseStore);
const templateCardLink = screen.getByText(wpTemplateVerbiage.link.text)!;
await userEvent.click(templateCardLink);
const expectedRoute = wpTemplateVerbiage.link.route;
await expect(mockUseNavigate).toHaveBeenCalledWith(expectedRoute);
});
});

testA11y(wpTemplateCardComponent);
});
149 changes: 149 additions & 0 deletions services/ui-src/src/components/cards/TemplateCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// components
import { Button, Flex, Heading, Image, Text, Link } from "@chakra-ui/react";
import { Card, TemplateCardAccordion } from "components";
// utils
import { useNavigate } from "react-router-dom";
import { useBreakpoint, getSignedTemplateUrl } from "utils";
import { AnyObject } from "types";
// assets
import downloadIcon from "assets/icons/icon_download.png";
import nextIcon from "assets/icons/icon_next_white.png";
import spreadsheetIcon from "assets/icons/icon_spreadsheet.png";

const downloadTemplate = async (templateName: string) => {
const signedUrl = await getSignedTemplateUrl(templateName);
const link = document.createElement("a");
link.setAttribute("target", "_blank");
link.setAttribute("href", signedUrl);
link.click();
link.remove();
};

export const TemplateCard = ({
templateName,
verbiage,
cardprops,
isHidden,
...props
}: Props) => {
const { isDesktop } = useBreakpoint();
const navigate = useNavigate();

return (
<Card boxShadow="0px 3px 9px rgba(0, 0, 0, 0.2)" {...cardprops}>
<Flex sx={sx.root} {...props}>
{isDesktop && (
<Image
src={spreadsheetIcon}
alt="Spreadsheet icon"
sx={sx.spreadsheetIcon}
/>
)}
<Flex sx={sx.cardContentFlex}>
<Heading sx={sx.cardTitleText}>{verbiage.title}</Heading>
<Text>
{verbiage.body.available}
{/* <Link href={verbiage.linkLocation} isExternal>
{verbiage.linkText}
</Link> */}
{verbiage.midText}
<Link href={verbiage.linkLocation2} isExternal>
{verbiage.linkText2}
</Link>
{/* {verbiage.postLinkText} */}
</Text>
<Flex sx={sx.actionsFlex}>
{verbiage.downloadText && (
<Button
variant="link"
sx={sx.templateDownloadButton}
leftIcon={
<Image
src={downloadIcon}
alt="Download Icon"
height="1.5rem"
/>
}
onClick={async () => {
await downloadTemplate(templateName);
}}
>
{verbiage.downloadText}
</Button>
)}
{!isHidden && (
<Button
sx={sx.formLink}
onClick={() => navigate(verbiage.link.route)}
rightIcon={
<Image src={nextIcon} alt="Link Icon" height="1rem" />
}
>
{verbiage.link.text}
</Button>
)}
</Flex>
<TemplateCardAccordion verbiage={verbiage.accordion} />
<Text sx={sx.textMargin}>Notes on when it's due</Text>
</Flex>
</Flex>
</Card>
);
};

interface Props {
verbiage: AnyObject;
isHidden?: boolean;
[key: string]: any;
}

const sx = {
root: {
flexDirection: "row",
},
spreadsheetIcon: {
marginRight: "2rem",
boxSize: "5.5rem",
},
cardContentFlex: {
width: "100%",
flexDirection: "column",
},
cardTitleText: {
marginBottom: "0.5rem",
fontSize: "lg",
fontWeight: "bold",
lineHeight: "1.5",
},
actionsFlex: {
flexFlow: "wrap",
gridGap: "1rem",
justifyContent: "space-between",
margin: "1rem 0 0 1rem",
".mobile &": {
flexDirection: "column",
},
},
templateDownloadButton: {
justifyContent: "start",
marginRight: "1rem",
padding: "0",
span: {
marginLeft: "0rem",
marginRight: "0.5rem",
},
".mobile &": {
marginRight: "0",
},
},
formLink: {
justifyContent: "start",
span: {
marginLeft: "0.5rem",
marginRight: "-0.25rem",
},
},
textMargin: {
paddingTop: "1rem",
},
};
Loading

0 comments on commit 614cc2d

Please sign in to comment.