diff --git a/src/App.jsx b/src/App.jsx index 2fab11b..c789c76 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ import * as React from "react" // IMPORT ANY NEEDED COMPONENTS HERE -import { createDataSet } from "./data/dataset" +import { Dataset } from "./data/dataset" import "./App.css" // don't move this! @@ -18,9 +18,10 @@ export const appInfo = { }, } // or this! -const { data, categories, restaurants } = createDataSet() export function App() { + const { data, categories, restaurants } = Dataset.createDataSet() + return (
{/* CATEGORIES COLUMN */} diff --git a/src/data/dataset.js b/src/data/dataset.js index c7a3fc3..51849a0 100644 --- a/src/data/dataset.js +++ b/src/data/dataset.js @@ -1,12 +1,13 @@ import data from "./normalized-fast-food-feud.json" -export const getUniqueCategories = (d) => [...new Set(d.map((row) => row.food_category))] -export const getUniqueRestaurants = (d) => [...new Set(d.map((row) => row.restaurant))] - -export const createDataSet = () => { - return { - data, - restaurants: getUniqueRestaurants(data), - categories: getUniqueCategories(data), - } +export const Dataset = { + getUniqueCategories: (d) => [...new Set(d.map((row) => row.food_category))], + getUniqueRestaurants: (d) => [...new Set(d.map((row) => row.restaurant))], + createDataSet: () => { + return { + data, + restaurants: Dataset.getUniqueRestaurants(data), + categories: Dataset.getUniqueCategories(data), + } + }, } diff --git a/src/tests/feature-001-passing-props.test.jsx b/src/tests/feature-001-passing-props.test.jsx index 935fa3d..9858af8 100644 --- a/src/tests/feature-001-passing-props.test.jsx +++ b/src/tests/feature-001-passing-props.test.jsx @@ -3,8 +3,16 @@ import { configureSpecSuiteWithUtils } from "./utils" import { appInfo } from "../App" export function testPassingProps(App) { - const { assert, suite, render, fireEvent, customQueries, bootstrapTestSuiteContext } = - configureSpecSuiteWithUtils(App) + const { + assert, + suite, + render, + cleanup, + customQueries, + bootstrapTestSuiteContext, + within, + // + } = configureSpecSuiteWithUtils(App) const FeatureTestSuite = suite(`FEATURE 001: The \`Header\` component`) @@ -15,7 +23,6 @@ export function testPassingProps(App) { singleComponentNames: [ "Header", "Instructions", - // "NutritionLabel", ], multiComponentNames: ["Chip"], }) @@ -29,94 +36,124 @@ export function testPassingProps(App) { FeatureTestSuite.after.each((ctx) => { ctx.sandbox.restore() + cleanup() }) FeatureTestSuite.after((ctx) => { ctx.sandbox.restore() }) - FeatureTestSuite.test("Header component renders the appropriate site Title", async () => { - const { getByText, findByText } = render() + FeatureTestSuite.test("`Header` component receives the correct props", async (ctx) => { + ctx.propAssertions.assertComponentExistsAndHasProps("Header") + + ctx.propAssertions.assertComponentExistsAndHasValueInProps("Header", "title") + ctx.propAssertions.assertComponentExistsAndHasValueInProps("Header", "tagline") + ctx.propAssertions.assertComponentExistsAndHasValueInProps("Header", "description") + + ctx.propAssertions.assertComponentExistsAndHasPropOfType("Header", "title", "string") + ctx.propAssertions.assertComponentExistsAndHasPropOfType("Header", "tagline", "string") + ctx.propAssertions.assertComponentExistsAndHasPropOfType("Header", "description", "string") - assert.ok(getByText("Fast Food Feud", { exact: false, selector: "h1" }), "Couldn't find the site title") - assert.ok(await findByText("Fast Food Feud", { exact: false, selector: "h1" }), "Couldn't find the site title") + ctx.propAssertions.assertComponentExistsAndHasPropWithValue("Header", "title", appInfo.title) + ctx.propAssertions.assertComponentExistsAndHasPropWithValue("Header", "tagline", appInfo.tagline) + ctx.propAssertions.assertComponentExistsAndHasPropWithValue("Header", "description", appInfo.description) }) - FeatureTestSuite.test("Header component renders the appropriate tagline", async () => { - const { getByText, findByText } = render() + FeatureTestSuite.test("`Header` component renders the appropriate site title", async () => { + const { container, findByText } = render() - assert.ok(getByText("Fast Food Feud", { exact: false, selector: "h1" }), "Couldn't find the site title") + const Header = customQueries.getHeaderElement(container) - assert.equal( - Boolean(await findByText("Folks' Favorite Friendly Fuel Finder For Food Facts", { selector: "h4" })), - true, - "Couldn't find the proper tagline" + assert.ok( + Header, + `The \`Header\` component should be rendered to the screen and should return JSX inside a native HTML \`header\` element.` + ) + + assert.ok( + await findByText(appInfo.title, { exact: false, selector: "h1" }), + "Couldn't find the site title rendered by the `Header` component in an `h1` tag" ) assert.ok( - await findByText("Folks' Favorite Friendly Fuel Finder For Food Facts", { selector: "h4" }), - "Couldn't find the proper tagline" + within(Header).getByText(appInfo.title, { exact: false, selector: "h1" }), + "Couldn't find the site title rendered by the `Header` component in an `h1` tag" ) }) - FeatureTestSuite.test("Header component renders the appropriate site description prop", async () => { - const { getByText, findByText } = render() + FeatureTestSuite.test("`Header` component renders the appropriate tagline", async () => { + const { container, findByText } = render() + + const Header = customQueries.getHeaderElement(container) assert.ok( - getByText("is here to arm the public", { exact: false, selector: "p" }), - "Site description doesn't exist on the page." + Header, + `The \`Header\` component should be rendered to the screen and should return JSX inside a native HTML \`header\` element.` ) assert.ok( - await findByText("is here to arm the public", { exact: false, selector: "p" }), - "Site description doesn't exist on the page." + await findByText(appInfo.tagline, { exact: true, selector: "h4" }), + "Couldn't find the site tagline rendered by the `Header` component in an `h4` tag" + ) + assert.ok( + within(Header).getByText(appInfo.tagline, { exact: true, selector: "h4" }), + "Couldn't find the site tagline rendered by the `Header` component in an `h4` tag" ) }) - FeatureTestSuite.test("Header component receives the correct props", async (ctx) => { - ctx.propAssertions.assertComponentExistsAndHasProps("Header") + FeatureTestSuite.test("`Header` component renders the appropriate site description prop", async () => { + const { container, findByText } = render() - ctx.propAssertions.assertComponentExistsAndHasValueInProps("Header", "title") - ctx.propAssertions.assertComponentExistsAndHasValueInProps("Header", "tagline") - ctx.propAssertions.assertComponentExistsAndHasValueInProps("Header", "description") + const Header = customQueries.getHeaderElement(container) - ctx.propAssertions.assertComponentExistsAndHasPropOfType("Header", "title", "string") - ctx.propAssertions.assertComponentExistsAndHasPropOfType("Header", "tagline", "string") - ctx.propAssertions.assertComponentExistsAndHasPropOfType("Header", "description", "string") + assert.ok( + Header, + `The \`Header\` component should be rendered to the screen and should return JSX inside a native HTML \`header\` element.` + ) - ctx.propAssertions.assertComponentExistsAndHasPropWithValue("Header", "title", appInfo.title) - ctx.propAssertions.assertComponentExistsAndHasPropWithValue("Header", "tagline", appInfo.tagline) - ctx.propAssertions.assertComponentExistsAndHasPropWithValue("Header", "description", appInfo.description) + assert.ok( + await findByText("is here to arm the public", { exact: false, selector: "p" }), + "Couldn't find the site description rendered by the `Header` component inside a `p` tag" + ) + assert.ok( + within(Header).getByText(appInfo.description, { exact: true, selector: "p" }), + "Couldn't find the site description rendered by the `Header` component inside a `p` tag" + ) + }) + + FeatureTestSuite.test("`Instructions` component receives the correct props", async (ctx) => { + ctx.propAssertions.assertComponentExistsAndHasProps("Instructions") + ctx.propAssertions.assertComponentExistsAndHasValueInProps("Instructions", "instructions") + ctx.propAssertions.assertComponentExistsAndHasPropOfType("Instructions", "instructions", "string") + ctx.propAssertions.assertComponentExistsAndHasPropWithValue( + "Instructions", + "instructions", + appInfo.instructions.start + ) }) FeatureTestSuite.test( - "Instructions component receives props and renders the appropriate instruction", + "`Instructions` component receives props and renders the appropriate instruction", async (ctx) => { - const { getByText, findByText } = render() + const { container, findByText } = render() + + const Instructions = customQueries.getInstructionsAside(container) + + assert.ok( + Instructions, + `The \`Instructions\` component should be rendered to the screen and ` + + `should return JSX inside a native HTML \`aside\` element with a className of \`instructions\`.` + ) assert.ok( - await findByText( - "Start by clicking on a food category on the left and a fast food joint from the list above. Afterwards, you'll be able to choose from a list of menu items and see their nutritional content." - ), - "Couldn't find the proper instructions" + within(Instructions).getByText(appInfo.instructions.start, { exact: true, selector: "p" }), + "Couldn't find the start instructions rendered by the `Instructions` component inside a `p` tag" ) assert.ok( - getByText("clicking on a food category on the left", { exact: false }), - "Couldn't find the proper instructions" + await findByText(appInfo.instructions.start, { exact: true, selector: "p" }), + "Couldn't find the start instructions rendered by the `Instructions` component inside a `p` tag" ) } ) - FeatureTestSuite.test("Instructions component receives the correct props", async (ctx) => { - ctx.propAssertions.assertComponentExistsAndHasProps("Instructions") - ctx.propAssertions.assertComponentExistsAndHasValueInProps("Instructions", "instructions") - ctx.propAssertions.assertComponentExistsAndHasPropOfType("Instructions", "instructions", "string") - ctx.propAssertions.assertComponentExistsAndHasPropWithValue( - "Instructions", - "instructions", - appInfo.instructions.start - ) - }) - return FeatureTestSuite.run() } diff --git a/src/tests/feature-002-iteration-in-jsx.test.jsx b/src/tests/feature-002-iteration-in-jsx.test.jsx index 2d5a83d..aca224e 100644 --- a/src/tests/feature-002-iteration-in-jsx.test.jsx +++ b/src/tests/feature-002-iteration-in-jsx.test.jsx @@ -1,12 +1,18 @@ import * as React from "react" import { configureSpecSuiteWithUtils } from "./utils" -import { createDataSet } from "../data/dataset" - -const { categories, restaurants } = createDataSet() +import { Dataset } from "../data/dataset" export function testIteration(App) { - const { assert, suite, render, fireEvent, customQueries, bootstrapTestSuiteContext } = - configureSpecSuiteWithUtils(App) + const { + assert, + suite, + render, + cleanup, + customQueries, + bootstrapTestSuiteContext, + within, + // + } = configureSpecSuiteWithUtils(App) const FeatureTestSuite = suite(`FEATURE 002: Iteration in JSX`) @@ -20,25 +26,19 @@ export function testIteration(App) { ctx.results = results ctx.propAssertions = propAssertions - const categoryParagraphs = results.root.findAll( - (node) => node.type === "p" && node.parent.props.className.split(" ").includes("categories") - ) - const categoryChips = results.Chip.filter((chip) => chip.parent.props.className.split(" ").includes("categories")) - const restaurantParagraphs = results.root.findAll( - (node) => node.type === "p" && node.parent.props.className.split(" ").includes("restaurants") - ) - const restaurantChips = results.Chip.filter((chip) => - chip.parent.props.className.split(" ").includes("restaurants") - ) - - ctx.results.p = { categoryParagraphs, restaurantParagraphs } + const isPTag = (node) => node.type === "p" + const nodeHasCategoriesClass = (node) => node.parent.props.className.split(" ").includes("categories") + const nodeHasRestaurantsClass = (node) => node.parent.props.className.split(" ").includes("restaurants") + + const categoryParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasCategoriesClass(node)) ?? [] + const categoryChips = results.Chip?.filter((chip) => nodeHasCategoriesClass(chip)) ?? [] + const restaurantParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasRestaurantsClass(node)) ?? [] + const restaurantChips = results.Chip?.filter((chip) => nodeHasRestaurantsClass(chip)) ?? [] + + ctx.results.categoryParagraphs = categoryParagraphs + ctx.results.restaurantParagraphs = restaurantParagraphs ctx.results.categoryChips = categoryChips ctx.results.restaurantChips = restaurantChips - - // const inputFiber = results.root.find( - // (node) => - // node?.type === "input" && node?.parent?.type === "div" && node?.parent?.parent?.type?.name === "FilterInput" - // ) }) FeatureTestSuite.before.each((ctx) => { @@ -47,12 +47,18 @@ export function testIteration(App) { FeatureTestSuite.after.each((ctx) => { ctx.sandbox.restore() + cleanup() }) FeatureTestSuite.after((ctx) => { ctx.sandbox.restore() }) + /*================================ + = CONSTANTS = + =================================*/ + const { categories, restaurants } = Dataset.createDataSet() + FeatureTestSuite.test("Categories are iterated over and a paragraph tag is rendered for each one.", (ctx) => { const { categoryChips, categoryParagraphs } = ctx.results for (const category of categories) { @@ -75,7 +81,98 @@ export function testIteration(App) { } }) - // TODO: Write a test that uses sinon to stub the datasets function and ensure they're not hardcoding the values + FeatureTestSuite.test( + "The `categories` and `restaurants` arrays are being iterated over properly and not hardcoded in.", + (ctx) => { + let getUniqueCategoriesStub, getUniqueRestaurantsStub + const otherCategories = ["Other Category", "Stuff", "Things"] + const otherRestaurants = ["Other Restaurant", "Food Place", "Buffet"] + const customCategories = [...categories.slice(0, 2), ...otherCategories] + const customRestaurants = [...restaurants.slice(0, 2), ...otherRestaurants] + const excludedCategories = categories.slice(3, 5) + const excludedRestaurants = restaurants.slice(3, 5) + + try { + getUniqueCategoriesStub = ctx.sandbox + .stub(Dataset, "getUniqueCategories") + .callsFake(() => [...customCategories]) + getUniqueRestaurantsStub = ctx.sandbox + .stub(Dataset, "getUniqueRestaurants") + .callsFake(() => [...customRestaurants]) + + const { container } = render() + + const chipButtons = customQueries.getAllChipButtons(container) + const categoryParagraphs = container.querySelectorAll(".categories p") + const restaurantParagraphs = container.querySelectorAll(".restaurants p") + + // make sure all custom items are rendered + for (let i = 0; i < customCategories.length; i++) { + const customCategory = customCategories[i] + const customRestaurant = customRestaurants[i] + + let otherCategoryFound = Array.from(chipButtons).some((chip) => within(chip).queryByText(customCategory)) + let otherRestaurantFound = Array.from(chipButtons).some((chip) => within(chip).queryByText(customRestaurant)) + if (!otherCategoryFound) { + otherCategoryFound = Array.from(categoryParagraphs).some((p) => within(p).queryByText(customCategory)) + } + if (!otherRestaurantFound) { + otherRestaurantFound = Array.from(restaurantParagraphs).some((chip) => + within(chip).queryByText(customRestaurant) + ) + } + assert.ok( + otherCategoryFound, + "Make sure to iterate over the `categories` array returned by the `Dataset.createDataset()` function" + + " and render an element for each one." + + " Don't add them individually." + ) + assert.ok( + otherRestaurantFound, + "Make sure to iterate over the `restaurants` array returned by the `Dataset.createDataset()` function" + + " and render an element for each one." + + " Don't add them individually." + ) + } + + // make sure no excluded items are rendered + for (let i = 0; i < excludedCategories.length; i++) { + const excludedCategory = excludedCategories[i] + const excludedRestaurant = excludedRestaurants[i] + + let excludedCategoryFound = Array.from(chipButtons).some((chip) => within(chip).queryByText(excludedCategory)) + let excludedRestaurantFound = Array.from(chipButtons).some((chip) => + within(chip).queryByText(excludedRestaurant) + ) + if (!excludedCategoryFound) { + excludedCategoryFound = Array.from(categoryParagraphs).some((p) => within(p).queryByText(excludedCategory)) + } + if (!excludedRestaurantFound) { + excludedRestaurantFound = Array.from(restaurantParagraphs).some((chip) => + within(chip).queryByText(excludedRestaurant) + ) + } + assert.not.ok( + excludedCategoryFound, + "Make sure to iterate over the `categories` array returned by the `Dataset.createDataset()` function" + + " and render an element for each one." + + " Don't add them individually." + ) + assert.not.ok( + excludedRestaurantFound, + "Make sure to iterate over the `restaurants` array returned by the `Dataset.createDataset()` function" + + " and render an element for each one." + + " Don't add them individually." + ) + } + } catch (e) { + throw e + } finally { + getUniqueCategoriesStub?.restore?.() + getUniqueRestaurantsStub?.restore?.() + } + } + ) return FeatureTestSuite.run() } diff --git a/src/tests/feature-003-props-styles.test.jsx b/src/tests/feature-003-props-styles.test.jsx index 6f8ddf8..3e8d282 100644 --- a/src/tests/feature-003-props-styles.test.jsx +++ b/src/tests/feature-003-props-styles.test.jsx @@ -1,13 +1,17 @@ import * as React from "react" import Chip from "../components/Chip/Chip" import { configureSpecSuiteWithUtils } from "./utils" -import { createDataSet } from "../data/dataset" - -const { categories, restaurants } = createDataSet() +import { Dataset } from "../data/dataset" export function testPropsAndStyles(App) { - const { assert, suite, render, fireEvent, customQueries, bootstrapTestSuiteContext } = - configureSpecSuiteWithUtils(App) + const { + assert, + suite, + render, + cleanup, + bootstrapTestSuiteContext, + // + } = configureSpecSuiteWithUtils(App) const FeatureTestSuite = suite("FEATURE 003: The `Chip` component and using props to customize styles") @@ -21,11 +25,17 @@ export function testPropsAndStyles(App) { ctx.results = results ctx.propAssertions = propAssertions - const categoryChips = results.Chip.filter((chip) => chip.parent.props.className.split(" ").includes("categories")) - const restaurantChips = results.Chip.filter((chip) => - chip.parent.props.className.split(" ").includes("restaurants") - ) + const isPTag = (node) => node.type === "p" + const nodeHasCategoriesClass = (node) => node.parent.props.className.split(" ").includes("categories") + const nodeHasRestaurantsClass = (node) => node.parent.props.className.split(" ").includes("restaurants") + + const categoryParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasCategoriesClass(node)) ?? [] + const categoryChips = results.Chip?.filter((chip) => nodeHasCategoriesClass(chip)) ?? [] + const restaurantParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasRestaurantsClass(node)) ?? [] + const restaurantChips = results.Chip?.filter((chip) => nodeHasRestaurantsClass(chip)) ?? [] + ctx.results.categoryParagraphs = categoryParagraphs + ctx.results.restaurantParagraphs = restaurantParagraphs ctx.results.categoryChips = categoryChips ctx.results.restaurantChips = restaurantChips }) @@ -36,12 +46,18 @@ export function testPropsAndStyles(App) { FeatureTestSuite.after.each((ctx) => { ctx.sandbox.restore() + cleanup() }) FeatureTestSuite.after((ctx) => { ctx.sandbox.restore() }) + /*================================ + = CONSTANTS = + =================================*/ + const { categories, restaurants } = Dataset.createDataSet() + FeatureTestSuite.test("Categories are iterated over and a Chip is rendered for each one.", (ctx) => { const { categoryChips } = ctx.results for (const category of categories) { @@ -66,20 +82,38 @@ export function testPropsAndStyles(App) { const { categoryChips, restaurantChips } = ctx.results const chips = [...categoryChips, ...restaurantChips] + for (const category of categories) { + assert.ok( + chips.find((chip) => chip.props.label === category), + `Couldn't find a Chip component for the category ${category}` + ) + } + for (const restaurant of restaurants) { + assert.ok( + chips.find((chip) => chip.props.label === restaurant), + `Couldn't find a Chip component for the restaurant ${restaurant}` + ) + } // either all chips should be active by default const allChipsHaveIsActive = chips.every((chip) => (chip?.props?.className?.split?.(" ") ?? "").includes("active")) - const { findByText, rerender } = render() - const chipParagraph = await findByText("Chippy Chip") + const { queryByText, rerender } = render( +
+ +
+ ) + const chipParagraph = queryByText("Chippy Chip") const chip = chipParagraph?.parentElement - const classesBefore = Array.from(chip?.classList ?? []) - rerender() + rerender( +
+ +
+ ) const classesAfter = Array.from(chip?.classList ?? []) - const chipHasActiveClassWhenIsActiveProp = classesAfter.includes("active") && !classesBefore.includes("active") assert.ok( diff --git a/src/tests/feature-004-state-and-event-handlers.test.jsx b/src/tests/feature-004-state-and-event-handlers.test.jsx index 78a1fd8..8b028cb 100644 --- a/src/tests/feature-004-state-and-event-handlers.test.jsx +++ b/src/tests/feature-004-state-and-event-handlers.test.jsx @@ -1,13 +1,18 @@ import * as React from "react" import Chip from "../components/Chip/Chip" import { configureSpecSuiteWithUtils } from "./utils" -import { createDataSet } from "../data/dataset" - -const { categories, restaurants } = createDataSet() +import { Dataset } from "../data/dataset" export function testStateAndEventHandlers(App) { - const { assert, suite, render, fireEvent, customQueries, bootstrapTestSuiteContext } = - configureSpecSuiteWithUtils(App) + const { + assert, + suite, + render, + fireEvent, + cleanup, + bootstrapTestSuiteContext, + // + } = configureSpecSuiteWithUtils(App) const FeatureTestSuite = suite("FEATURE 004: Using the `onClick` handlers to modify React state") @@ -21,11 +26,17 @@ export function testStateAndEventHandlers(App) { ctx.results = results ctx.propAssertions = propAssertions - const categoryChips = results.Chip.filter((chip) => chip.parent.props.className.split(" ").includes("categories")) - const restaurantChips = results.Chip.filter((chip) => - chip.parent.props.className.split(" ").includes("restaurants") - ) + const isPTag = (node) => node.type === "p" + const nodeHasCategoriesClass = (node) => node.parent.props.className.split(" ").includes("categories") + const nodeHasRestaurantsClass = (node) => node.parent.props.className.split(" ").includes("restaurants") + + const categoryParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasCategoriesClass(node)) ?? [] + const categoryChips = results.Chip?.filter((chip) => nodeHasCategoriesClass(chip)) ?? [] + const restaurantParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasRestaurantsClass(node)) ?? [] + const restaurantChips = results.Chip?.filter((chip) => nodeHasRestaurantsClass(chip)) ?? [] + ctx.results.categoryParagraphs = categoryParagraphs + ctx.results.restaurantParagraphs = restaurantParagraphs ctx.results.categoryChips = categoryChips ctx.results.restaurantChips = restaurantChips }) @@ -36,54 +47,50 @@ export function testStateAndEventHandlers(App) { FeatureTestSuite.after.each((ctx) => { ctx.sandbox.restore() + cleanup() }) FeatureTestSuite.after((ctx) => { ctx.sandbox.restore() }) - // TODO: Test that the Chip passes the `button` element the correct props - - FeatureTestSuite.test("Chips have a working `isActive` prop that updates the chip styling.", async (ctx) => { - const { categoryChips, restaurantChips } = ctx.results - - const chips = [...categoryChips, ...restaurantChips] + /*================================ + = CONSTANTS = + =================================*/ + const { categories, restaurants } = Dataset.createDataSet() - // either all chips should be active by default - const allChipsHaveIsActive = chips.every((chip) => (chip?.props?.className?.split?.(" ") ?? "").includes("active")) - - const { findByText, rerender } = render() - const chipParagraph = await findByText("Chippy Chip") - const chip = chipParagraph?.parentElement - - const classesBefore = Array.from(chip?.classList ?? []) + FeatureTestSuite.test( + "Each `Chip` component passes its `onClick` prop to the `button` element inside of it.", + async (ctx) => { + const spy = ctx.sandbox.spy() - rerender() + const { container } = render( +
+ +
+ ) - const classesAfter = Array.from(chip?.classList ?? []) + const chipButton = container.querySelector("button.chip") - const chipHasActiveClassWhenIsActiveProp = classesAfter.includes("active") && !classesBefore.includes("active") + assert.ok(chipButton, `The Chip component doesn't have a \`button\` element with the className of \`chip\`.`) - assert.ok( - allChipsHaveIsActive || chipHasActiveClassWhenIsActiveProp, - `Either the Chip component doesn't have the \`isActive\` prop defaulted to \`true\`, or Chip component doesn't use the \`isActive\` prop to update its "className".` - ) - }) + fireEvent.click(chipButton) - FeatureTestSuite.test( - "Each `Chip` component passes its `onClick` prop to the `button` element inside of it.", - async (ctx) => { - // TODO: Test that the Chip passes the `button` element the correct props + assert.ok( + spy.calledOnce, + `The Chip component doesn't have pass its \`onClick\` prop to the \`button\` element as its \`onClick\` prop.` + ) } ) FeatureTestSuite.test( - "Clicking on a 'Categories' Chip triggers its `onClick` handler and updates state with the correct value. That value is then used to determine which Chip component is active.", + "Clicking on a 'categories' `Chip` triggers its `onClick` handler and updates state with the correct value. " + + "That value is then used to determine which categories `Chip` component is active.", async (ctx) => { - const { findByText } = render() + const { queryByText } = render() for (const category of categories) { - let chipParagraph = await findByText(category) + let chipParagraph = queryByText(category) let chip = chipParagraph?.parentElement const classesBefore = Array.from(chip?.classList ?? []) @@ -92,7 +99,7 @@ export function testStateAndEventHandlers(App) { fireEvent.click(chip ?? chipParagraph) - chipParagraph = await findByText(category) + chipParagraph = queryByText(category) chip = chipParagraph?.parentElement const classesAfter = Array.from(chip?.classList ?? []) @@ -103,12 +110,13 @@ export function testStateAndEventHandlers(App) { ) FeatureTestSuite.test( - "Clicking on a 'Restaurants' Chip triggers its `onClick` handler and updates state with the correct value. That value is then used to determine which Restaurant component is active.", + "Clicking on a 'restaurants' `Chip` triggers its `onClick` handler and updates state with the correct value. " + + "That value is then used to determine which restaurant `Chip` component is active.", async (ctx) => { - const { findByText } = render() + const { queryByText } = render() for (const restaurant of restaurants) { - let chipParagraph = await findByText(restaurant) + let chipParagraph = queryByText(restaurant) let chip = chipParagraph?.parentElement const classesBefore = Array.from(chip?.classList ?? []) @@ -120,7 +128,7 @@ export function testStateAndEventHandlers(App) { fireEvent.click(chip ?? chipParagraph) - chipParagraph = await findByText(restaurant) + chipParagraph = queryByText(restaurant) chip = chipParagraph?.parentElement const classesAfter = Array.from(chip?.classList ?? []) diff --git a/src/tests/feature-005-render-state-values.test.jsx b/src/tests/feature-005-render-state-values.test.jsx index 444cc4c..3234707 100644 --- a/src/tests/feature-005-render-state-values.test.jsx +++ b/src/tests/feature-005-render-state-values.test.jsx @@ -1,27 +1,16 @@ import * as React from "react" import NutritionalLabel from "../components/NutritionalLabel/NutritionalLabel" import { configureSpecSuiteWithUtils } from "./utils" -import { createDataSet } from "../data/dataset" +import { Dataset } from "../data/dataset" import { nutritionFacts } from "../constants" -const { data, categories, restaurants } = createDataSet() - -// pull out some constants -const doubleDoubleMenuItem = `Double Double Burger w/ Onion` -const burgersCategory = categories[8] -const inNOutRestaurant = restaurants[6] - -const currentMenuItems = data.filter((item) => { - return item.food_category === burgersCategory && item.restaurant === inNOutRestaurant -}) -const doubleDoubleMenuItemData = currentMenuItems.find((item) => item.item_name === doubleDoubleMenuItem) - export function testRenderStateValues(App) { const { assert, suite, render, fireEvent, + cleanup, customQueries, bootstrapTestSuiteContext, within, @@ -40,11 +29,17 @@ export function testRenderStateValues(App) { ctx.results = results ctx.propAssertions = propAssertions - const categoryChips = results.Chip.filter((chip) => chip.parent.props.className.split(" ").includes("categories")) - const restaurantChips = results.Chip.filter((chip) => - chip.parent.props.className.split(" ").includes("restaurants") - ) + const isPTag = (node) => node.type === "p" + const nodeHasCategoriesClass = (node) => node.parent.props.className.split(" ").includes("categories") + const nodeHasRestaurantsClass = (node) => node.parent.props.className.split(" ").includes("restaurants") + + const categoryParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasCategoriesClass(node)) ?? [] + const categoryChips = results.Chip?.filter((chip) => nodeHasCategoriesClass(chip)) ?? [] + const restaurantParagraphs = results.root?.findAll((node) => isPTag(node) && nodeHasRestaurantsClass(node)) ?? [] + const restaurantChips = results.Chip?.filter((chip) => nodeHasRestaurantsClass(chip)) ?? [] + ctx.results.categoryParagraphs = categoryParagraphs + ctx.results.restaurantParagraphs = restaurantParagraphs ctx.results.categoryChips = categoryChips ctx.results.restaurantChips = restaurantChips }) @@ -55,12 +50,29 @@ export function testRenderStateValues(App) { FeatureTestSuite.after.each((ctx) => { ctx.sandbox.restore() + cleanup() }) FeatureTestSuite.after((ctx) => { ctx.sandbox.restore() }) + /*================================ + = CONSTANTS = + =================================*/ + const { data, categories, restaurants } = Dataset.createDataSet() + const doubleDoubleMenuItem = `Double Double Burger w/ Onion` + const burgersCategory = categories[8] + const inNOutRestaurant = restaurants[6] + + /*================================ + = Helpers = + =================================*/ + const currentMenuItems = data.filter((item) => { + return item.food_category === burgersCategory && item.restaurant === inNOutRestaurant + }) + const doubleDoubleMenuItemData = currentMenuItems.find((item) => item.item_name === doubleDoubleMenuItem) + function setupMenuItems() { const renderQueries = render() @@ -69,10 +81,10 @@ export function testRenderStateValues(App) { assert.not.ok(menuItemChip, `Menu item Chips should not be rendered before a category and restaurant are selected`) - const categoryChipParagraph = renderQueries.getByText(burgersCategory) + const categoryChipParagraph = renderQueries.queryByText(burgersCategory) const categoryChip = categoryChipParagraph?.parentElement - const restaurantChipParagraph = renderQueries.getByText(inNOutRestaurant) + const restaurantChipParagraph = renderQueries.queryByText(inNOutRestaurant) const restaurantChip = restaurantChipParagraph?.parentElement assert.ok(categoryChip, `Category Chip for ${burgersCategory} could not be found`) @@ -96,24 +108,28 @@ export function testRenderStateValues(App) { `Clicking the ${burgersCategory} and ${inNOutRestaurant} Chips should be render 9 menu item Chips inside the div with a clasname of "menu-items"` ) - const p = await within(menuItemsDiv).findByText(doubleDoubleMenuItem) + const p = within(menuItemsDiv).queryByText(doubleDoubleMenuItem) const doubleDoubleChip = p.parentElement return doubleDoubleChip } + /*================================ + = TESTS = + =================================*/ + FeatureTestSuite.test("Clicking a `category` Chip and a `restaurant` Chip renders `menuItem` Chips", async (ctx) => { - const { container, getByText, queryByText } = render() + const { container, queryByText } = render() let menuItemChipParagraph = queryByText(doubleDoubleMenuItem) let menuItemChip = menuItemChipParagraph?.parentElement assert.not.ok(menuItemChip, `Menu item Chips should not be rendered before a category and restaurant are selected`) - const categoryChipParagraph = getByText(burgersCategory) + const categoryChipParagraph = queryByText(burgersCategory) const categoryChip = categoryChipParagraph?.parentElement - const restaurantChipParagraph = getByText(inNOutRestaurant) + const restaurantChipParagraph = queryByText(inNOutRestaurant) const restaurantChip = restaurantChipParagraph?.parentElement assert.ok(categoryChip, `Category Chip for ${burgersCategory} could not be found`) @@ -142,7 +158,8 @@ export function testRenderStateValues(App) { `Clicking the ${burgersCategory} and ${inNOutRestaurant} Chips should be render 9 menu item Chips inside the div with a clasname of "menu-items"` ) - const p = await within(menuItemsDiv).findByText(doubleDoubleMenuItem) + const p = within(menuItemsDiv).queryByText(doubleDoubleMenuItem) + const doubleDoubleChip = p.parentElement assert.ok(doubleDoubleChip, `Menu Item Chips should be rendered after a category and restaurant are selected.`)