Testing with Cypress
Testing with Cypress
About Cypress
Advantages include snapshots at the time of test execution, implicit wait commands, multiple browsers in which to test and real-time command execution with visual feedback.
Cypress allows for end-to-end testing, replicating how "users interact with your app by using a real browser", and component testing, also known as unit testing, which tests a component's functionality, styling and appearance in isolation.
Cypress and the Shadow DOM
Cypress gives two ways in which to test@ukic
components in the
Configuring at test level
AddincludeShadowDom: true
to thecy.get
query.
// cypress/e2e/test.cy.js
it("has an app bar with title link", () => {
cy.get("ic-top-navigation", { includeShadowDom: true })
.shadow()
.find(".title-link")
.should("have.attr", "href", "/")
.should("have.text", "My App Title");
});
This allows traversing the shadow DOM by chaining the.shadow()
method.
Configuring at global level
AddincludeShadowDom: true
to thecypress.config.js
.
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
includeShadowDom: true,
component: {
devServer: {
framework: "create-react-app",
bundler: "webpack",
},
},
});
This allows traversing the shadow DOM using assertions without having to explicitly chain the.shadow()
method.
Example: Component testing
Below are examples of component tests on the@ukic/react
componentsIcTextfield
andIcButton
. Component tests are normally located adjacent to the component they are testing and are rendered on a development server.
// Textfield.tsx
import { IcTextField, IcButton } from "@ukic/react";
import React, { useState } from "react";
function Textfield() {
const [favouriteCoffee, setFavouriteCoffee] = useState<String>("");
const handleChange = (event) => {
event.preventDefault();
setFavouriteCoffee(event.detail.value);
};
const handleClick = () => {
console.log("This is the best coffee going:", favouriteCoffee);
};
return (
<form><IcTextField
label="What is your favourite coffee?"
placeholder="Placeholder"
helperText="Such as Arabica, Robusta or Liberica"
onIcChange={(ev) => handleChange(ev)}
/><IcButton variant="primary" onClick={() => handleClick()}>
Send
</IcButton></form>
);
}
export default Textfield;
// Textfield.cy.tsx
import React from "react";
import Textfield from "./Textfield.tsx";
describe("<Textfield />", () => {
it("renders", () => {
cy.mount(<Textfield />).should("have.property", "component");
});
it("takes a value in the textfield and prints to console when the button is clicked", () => {
// Render the component
cy.mount(<Textfield />);
// Capture the log to check later
cy.stub(window.console, "log").as("consoleLog");
// Check the IcComponents have finished rendering
cy.get("ic-text-field").should("have.class", "hydrated");
cy.get("ic-button").should("have.class", "hydrated");
// Interact with the components and provide assertions
cy.get("#ic-text-field-input-1").type("Black");
cy.get("ic-button").click();
cy.get("@consoleLog").should(
"be.calledWith",
"This is the best coffee going:",
"Black"
);
});
});
Example: End-to-end testing
Below is an example of end-to-end tests written against a ‘test app’ that has been created using ICDS components.
// StackBlitz cannot currently run Cypress testsit("should fill the form with no errors/validation",()=>{mount(<Subscription/>);// checkHydrated will wait until the component is hydrated and ensures it is ready to testcheckHydrated(IC_PAGE_HEADER);// Check the first step is visible cy.get(IC_STEPPER).should(BE_VISIBLE);checkCurrentStep(0);// Select a radio option cy.get(IC_RADIO_OPTION).find(RADIO+'[value="house"]').check({force:true}); cy.get(IC_RADIO_OPTION+'[label="House Blend"]').should(HAVE_ATTR,SELECTED);// Select a select option using mouseclickOnShadowEl(IC_SELECT,IC_INPUT_CONTAINER);clickOnShadowEl(IC_SELECT,IC_MENU_OPTION+'[data-value="aeropress"]');// Select a select option using keyboardclickOnShadowEl(IC_SELECT,IC_INPUT_CONTAINER,1); cy.realPress([ARROW_DOWN_KEY,ESCAPE_KEY]);// Go to next step, check the stepper and the logged formValues so far cy.get(IC_BUTTON).contains("Add to order").click();checkCurrentStep(1); cy.get(CONSOLE_LOG).should(HAVE_BEEN_CALLED_WITH,filledForm());// Fill out the text fieldsclickOnShadowEl(IC_TEXT_FIELD,IC_INPUT_CONTAINER); cy.realType("Java the Hutt").realPress(TAB_KEY).realType("javadahutt@tattooine.com").realPress(TAB_KEY).realType("1234567890");// Check both options in the checkbox groupfindShadowEl(IC_CHECKBOX,CHECKBOX).eq(0).check();findShadowEl(IC_CHECKBOX,CHECKBOX).eq(1).check(); cy.get(IC_CHECKBOX).eq(0).should(HAVE_ATTR,CHECKED); cy.get(IC_CHECKBOX).eq(1).should(HAVE_ATTR,CHECKED);// Go to next step, check the stepper and the logged formValues so far cy.get(IC_BUTTON).contains("Add to order").click(); cy.get(CONSOLE_LOG).should(HAVE_BEEN_CALLED_WITH,filledForm("details"));checkCurrentStep(2);// Select a date using the date pickerfindShadowEl(IC_DATE_PICKER,IC_DATE_INPUT).shadow().find(CALENDAR_BUTTON_ID).click();findShadowEl(IC_DATE_PICKER,FOCUSSED_DAY_BTN_CLASS).click();checkDateInputValue(newDate());// Agree to terms cy.get(IC_RADIO_OPTION).find(RADIO+'[name="agree"]').check({force:true}); cy.get(IC_RADIO_OPTION+'[name="agree"]').should(HAVE_ATTR,SELECTED);// Submit and check the logged formValues cy.get(IC_BUTTON).contains("Submit order").click();let date =newDate(); cy.get(CONSOLE_LOG).should(HAVE_BEEN_CALLED_WITH,filledForm("checkout"));});