packages/web-components/fast-foundation/src/accordion/accordion.pw.spec.ts
import type { Locator, Page } from "@playwright/test";
import { expect, test } from "@playwright/test";
import { fixtureURL } from "../__test__/helpers.js";
import { AccordionExpandMode } from "./accordion.options.js";
test.describe("Accordion", () => {
let page: Page;
let element: Locator;
let root: Locator;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
element = page.locator("fast-accordion");
root = page.locator("#storybook-root");
await page.goto(fixtureURL("accordion--accordion"));
await element.waitFor({ state: "visible" });
});
test.afterAll(async () => {
await page.close();
});
test("should set an expand mode of `multi` when passed to the `expand-mode` attribute", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="multi">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.multi);
});
test("should set an expand mode of `single` when passed to the `expand-mode` attribute", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.single);
});
test("should set a default expand mode of `multi` when `expand-mode` attribute is not passed", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion>
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
await expect(element).toHaveJSProperty("expandmode", AccordionExpandMode.multi);
await expect(element).toHaveAttribute("expand-mode", AccordionExpandMode.multi);
});
test("should expand/collapse items when clicked in multi mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="multi">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
await items.nth(0).click();
await items.nth(1).click();
await expect(items.nth(0)).toHaveAttribute("expanded", "");
await expect(items.nth(1)).toHaveAttribute("expanded", "");
});
test("should only have one expanded item in single mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
const firstItem = items.nth(0);
const secondItem = items.nth(1);
await firstItem.click();
await expect(firstItem).toHaveAttribute("expanded");
await expect(secondItem).not.toHaveAttribute("expanded");
const secondItemButton = secondItem.locator(`[part="button"]`);
await secondItemButton.click();
await secondItemButton.evaluate(node => {
node.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
await expect(firstItem).not.toHaveAttribute("expanded");
await expect(secondItem).toHaveAttribute("expanded");
});
test("should set the expanded items' button to aria-disabled when in single expand mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
const firstItem = items.nth(0);
const secondItem = items.nth(1);
await firstItem.click();
await expect(firstItem).toHaveAttribute("expanded");
await expect(firstItem.locator("button")).toHaveAttribute(
"aria-disabled",
"true"
);
await secondItem.click();
await expect(firstItem).not.toHaveAttribute("expanded");
await expect(firstItem.locator("button")).not.toHaveAttribute(
"aria-disabled",
"true"
);
await expect(firstItem.locator("button")).not.toHaveAttribute(
"aria-disabled",
"false"
);
await expect(secondItem).toHaveAttribute("expanded");
await expect(secondItem.locator("button")).toHaveAttribute(
"aria-disabled",
"true"
);
});
/* eslint-disable-next-line max-len */
test("should remove an expanded items' expandbutton aria-disabled attribute when expand mode changes from single to multi", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
const firstItem = items.nth(0);
await firstItem.click();
await expect(firstItem).toHaveAttribute("expanded");
await expect(firstItem.locator("button")).toHaveAttribute(
"aria-disabled",
"true"
);
await element.evaluate(node => {
node.setAttribute("expand-mode", "multi");
});
await expect(firstItem.locator("button")).not.toHaveAttribute("aria-disabled");
});
test("should set the first item as expanded if no child is expanded by default in single mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
const firstItem = items.nth(0);
const secondItem = items.nth(1);
await expect(firstItem).toHaveAttribute("expanded");
await expect(secondItem).not.toHaveAttribute("expanded");
await secondItem.evaluate<void>(node => node.setAttribute("expanded", ""));
await expect(firstItem).not.toHaveAttribute("expanded");
await expect(secondItem).toHaveAttribute("expanded");
});
test("should set the first item with an expanded attribute to expanded in single mode", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item expanded>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
<fast-accordion-item expanded>
<span slot="heading">Heading 3</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
const firstItem = items.nth(0);
const secondItem = items.nth(1);
const thirdItem = items.nth(2);
await expect(firstItem).not.toHaveAttribute("expanded");
await expect(secondItem).toHaveAttribute("expanded");
await expect(thirdItem).not.toHaveAttribute("expanded");
});
test("should allow disabled items to be expanded when in single mode", async () => {
test.slow();
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<span slot="heading">Heading 1</span>
<div>Content 1</div>
</fast-accordion-item>
<fast-accordion-item expanded disabled>
<span slot="heading">Heading 2</span>
<div>Content 2</div>
</fast-accordion-item>
<fast-accordion-item expanded>
<span slot="heading">Heading 3</span>
<div>Content 2</div>
</fast-accordion-item>
</fast-accordion>
`;
});
const items = element.locator("fast-accordion-item");
const firstItem = items.nth(0);
const secondItem = items.nth(1);
const thirdItem = items.nth(2);
await expect(firstItem).not.toHaveAttribute("expanded");
await expect(secondItem).toHaveAttribute("expanded");
await expect(thirdItem).toHaveAttribute("expanded");
await secondItem.evaluate(node => {
node.removeAttribute("disabled");
});
await expect(firstItem).not.toHaveAttribute("expanded");
await expect(secondItem).toHaveAttribute("expanded");
await expect(thirdItem).not.toHaveAttribute("expanded");
});
test("should ignore `change` events from components other than accordion items", async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fast-accordion expand-mode="single">
<fast-accordion-item>
<div slot="heading">Accordion Item 1 Heading</div>
Accordion Item 1 Content
</fast-accordion-item>
<fast-accordion-item>
<div slot="heading">Accordion Item 2 Heading</div>
<fast-checkbox>A checkbox as content</fast-checkbox>
</fast-accordion-item>
</fast-accordion>
`;
});
const item = element.locator("fast-accordion-item").nth(1);
const button = item.locator(`button[part="button"]`);
await button.click();
await expect(item).toHaveAttribute("expanded", "");
const checkbox = item.locator("fast-checkbox");
await checkbox.click();
await expect(item).toHaveAttribute("expanded", "");
});
});