Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Selector UI Rotation Work #2330

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/odyssey-react-mui/src/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { memo } from "react";
import { useTranslation } from "react-i18next";
import { Alert, AlertColor, AlertTitle, AlertProps } from "@mui/material";

import { type FeatureTestSelector } from "./test-selectors";
import type { HtmlProps } from "./HtmlProps";
import { Link } from "./Link";
import { ScreenReaderText } from "./ScreenReaderText";
Expand All @@ -26,6 +27,36 @@ export const bannerSeverityValues: AlertColor[] = [
"error",
];

export const BannerTestSelectors = {
feature: {
link: {
selector: {
method: "ByRole",
options: {
name: "${linkText}",
},
role: "link",
templateVariableNames: ["linkText"],
},
},
closeButton: {
selector: {
method: "ByRole",
options: {
name: "${labelText}",
},
role: "button",
templateVariableNames: ["labelText"],
},
},
},
selector: {
method: "ByRole",
role: "${role}",
templateVariableNames: ["role"],
},
} as const satisfies FeatureTestSelector;

export type BannerProps = {
/**
* If linkUrl is not undefined, this is the text of the link.
Expand Down
15 changes: 1 addition & 14 deletions packages/odyssey-react-mui/src/Callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,8 @@ export const CalloutTestSelectors = {
templateVariableNames: ["linkText"],
},
},
text: {
selector: {
method: "ByText",
templateVariableNames: ["text"],
text: "${text}",
},
},
title: {
selector: {
method: "ByText",
templateVariableNames: ["title"],
text: "${title}",
},
},
},
// label: ["description", "title"],
selector: {
method: "ByRole",
options: {
Expand Down
22 changes: 22 additions & 0 deletions packages/odyssey-react-mui/src/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,30 @@ import {
getControlState,
} from "./inputUtils";
import { FieldComponentProps } from "./FieldComponentProps";
import { type FeatureTestSelector } from "./test-selectors";
import { Typography } from "./Typography";

export const CheckboxTestSelectors = {
// feature: {
// hint: {
// selector: {
// method: "ByText",
// templateVariableNames: ["hint"],
// text: "${hint}",
// },
// }
// },
// label: ["hint"]
selector: {
method: "ByRole",
options: {
name: "${label}",
},
role: "checkbox",
templateVariableNames: ["label"],
},
} as const satisfies FeatureTestSelector;

export const checkboxValidityValues = ["valid", "invalid", "inherit"] as const;

export type CheckboxProps = {
Expand Down
30 changes: 13 additions & 17 deletions packages/odyssey-react-mui/src/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { type FeatureTestSelector } from "./test-selectors";

export const TextFieldTestSelectors = {
feature: {
// label: ["errorMessage", "hint", "label"],
description: {
selector: {
method: "ByText",
Expand All @@ -44,27 +45,14 @@ export const TextFieldTestSelectors = {
errorMessage: {
selector: {
method: "ByText",
templateVariableNames: ["errorMessage"],
text: "${errorMessage}",
},
},
input: {
selector: {
method: "ByRole",
options: {
name: "${label}",
},
role: "textbox",
templateVariableNames: ["label"],
templateVariableNames: ["text"],
text: "${text}",
},
},
label: {
selector: {
method: "ByRole",
options: {
name: "${label}",
},
role: "LabelText",
method: "ByLabelText",
text: "${label}",
templateVariableNames: ["label"],
},
},
Expand All @@ -79,6 +67,14 @@ export const TextFieldTestSelectors = {
},
},
},
selector: {
method: "ByRole",
options: {
name: "${label}",
},
role: "textbox",
templateVariableNames: ["label"],
},
} as const satisfies FeatureTestSelector;

export const textFieldTypeValues = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
*/

import { type FeatureTestSelector } from "./featureTestSelector";
import { BannerTestSelectors } from "../Banner";
import { CalloutTestSelectors } from "../Callout";
import { CheckboxTestSelectors } from "../Checkbox";
import { TabsTestSelectors } from "../Tabs";
import { TextFieldTestSelectors } from "../TextField";

export const odysseyTestSelectors = {
Banner: BannerTestSelectors,
Callout: CalloutTestSelectors,
Checkbox: CheckboxTestSelectors,
Tabs: TabsTestSelectors,
TextField: TextFieldTestSelectors,
} as const satisfies Record<string, FeatureTestSelector>;
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export const querySelector = <TestSelectors extends FeatureTestSelector>({
})
: null;

const select =
const selectChild =
"feature" in testSelectors
? <FeatureName extends keyof (typeof testSelectors)["feature"]>(
featureName: FeatureName,
Expand All @@ -166,7 +166,7 @@ export const querySelector = <TestSelectors extends FeatureTestSelector>({

return {
element,
select,
selectChild,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from "@okta/odyssey-react-mui";
import { Meta, StoryObj } from "@storybook/react";

import { queryOdysseySelector } from "@okta/odyssey-react-mui/test-selectors";
import { MuiThemeDecorator } from "../../../../.storybook/components";
import { userEvent, within } from "@storybook/testing-library";
import { expect, jest } from "@storybook/jest";
Expand Down Expand Up @@ -145,6 +146,20 @@ export const Linked: StoryObj<BannerProps> = {
await expect(link?.tagName).toBe("A");
await expect(link?.href).toBe(`${link?.baseURI}#anchor`);
});

await step("has visible link", async () => {
const element = queryOdysseySelector({
canvas: within(canvasElement),
componentName: "Banner",
templateArgs: {
role: "status",
},
}).select?.("link", {
linkText: "View report",
}).element;

expect(element).toBeVisible();
});
},
};

Expand All @@ -161,5 +176,19 @@ export const Dismissible: StoryObj<BannerProps> = {
await expect(args.onClose).toHaveBeenCalled();
await axeRun("Dismissible Banner");
});

await step("has visible close button", async () => {
const element = queryOdysseySelector({
canvas: within(canvasElement),
componentName: "Banner",
templateArgs: {
role: "alert",
},
}).select?.("closeButton", {
labelText: "Close",
}).element;

expect(element).toBeVisible();
});
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ export const TitleWithLink: StoryObj<CalloutProps> = {
role: "alert",
title: /Safety checks failed/,
},
}).select?.("link", {
}).selectChild?.("link", {
linkText: "Visit fueling console",
}).element;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
import { queryOdysseySelector } from "@okta/odyssey-react-mui/test-selectors";

import { fieldComponentPropsMetaData } from "../../../fieldComponentPropsMetaData";
import { MuiThemeDecorator } from "../../../../.storybook/components";
Expand Down Expand Up @@ -267,6 +268,17 @@ export const ReadOnly: StoryObj<typeof Checkbox> = {
},
play: async ({ canvasElement, step }) => {
await checkTheBox({ canvasElement, step })("ReadOnly Checkbox");
await step("has aria read only set", async () => {
const element = queryOdysseySelector({
canvas: within(canvasElement),
componentName: "Checkbox",
templateArgs: {
label: "Automatically assign Okta Admin Console",
},
}).element;

expect(element?.ariaReadOnly).toBeTruthy();
});
},
};

Expand All @@ -284,6 +296,18 @@ export const Hint: StoryObj<typeof Checkbox> = {
},
play: async ({ canvasElement, step }) => {
await checkTheBox({ canvasElement, step })("Checkbox Hint");
// This is commented because there's a separate ticket to cover this work: OKTA-793465
// await step("has visible hint", async () => {
// const element = queryOdysseySelector({
// canvas: within(canvasElement),
// componentName: "Checkbox",
// templateArgs: {
// label:"I agree to the terms and conditions",
// }
// }).element;

// expect(element).toHaveAccessibleDescription(/Really helpful hint/);
// });
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
textFieldTypeValues,
} from "@okta/odyssey-react-mui";

import { queryOdysseySelector } from "@okta/odyssey-react-mui/test-selectors";
import { userEvent, within } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
import { fieldComponentPropsMetaData } from "../../../fieldComponentPropsMetaData";
Expand Down Expand Up @@ -246,6 +247,19 @@ export const Error: StoryObj<typeof TextField> = {
errorMessage: "This field is required.",
defaultValue: "",
},
play: async ({ canvasElement, step }) => {
await step("has visible error message", async () => {
const element = queryOdysseySelector({
canvas: within(canvasElement),
componentName: "TextField",
templateArgs: {
label: "Destination",
},
}).element;

expect(element).toHaveErrorMessage(/This field is required/);
});
},
};

export const ErrorsList: StoryObj<typeof TextField> = {
Expand All @@ -254,6 +268,21 @@ export const ErrorsList: StoryObj<typeof TextField> = {
errorMessageList: ["At least 2 chars", "No more than 20 chars"],
defaultValue: "",
},
play: async ({ canvasElement, step }) => {
await step("has visible error messages", async () => {
const element = queryOdysseySelector({
canvas: within(canvasElement),
componentName: "TextField",
templateArgs: {
label: "Destination",
},
}).element;

expect(element).toHaveErrorMessage(/This field is required:/);
expect(element).toHaveErrorMessage(/At least 2 chars/);
expect(element).toHaveErrorMessage(/No more than 20 chars/);
});
},
};

export const FullWidth: StoryObj<typeof TextField> = {
Expand All @@ -267,6 +296,21 @@ export const Hint: StoryObj<typeof TextField> = {
hint: "Specify your destination within the Sol system.",
defaultValue: "",
},
play: async ({ canvasElement, step }) => {
await step("has visible label", async () => {
const element = queryOdysseySelector({
canvas: within(canvasElement),
componentName: "TextField",
templateArgs: {
label: "Destination",
},
}).element;

expect(element).toHaveAccessibleDescription(
"Specify your destination within the Sol system.",
);
});
},
};

export const HintLink: StoryObj<typeof TextField> = {
Expand Down