diff --git a/packages/dom/src/lib/ElementAssertion.ts b/packages/dom/src/lib/ElementAssertion.ts index 5e90457..ace918f 100644 --- a/packages/dom/src/lib/ElementAssertion.ts +++ b/packages/dom/src/lib/ElementAssertion.ts @@ -31,6 +31,29 @@ export class ElementAssertion extends Assertion { }); } + /** + * Check if a given container element contains a specified child element. + * + * @param element the child expected to be contained. + * @returns the assertion instance. + */ + public toContainElement(element: Element): this { + const error = new AssertionError({ + actual: this.actual, + message: "Expected the container to contain the element", + }); + const invertedError = new AssertionError({ + actual: this.actual, + message: "Expected the container to NOT contain the element", + }); + + return this.execute({ + assertWhen: this.actual.contains(element), + error, + invertedError, + }); + } + /** * Check if the element has a specific attribute. * @@ -67,5 +90,4 @@ export class ElementAssertion extends Assertion { invertedError, }); } - } diff --git a/packages/dom/test/unit/lib/ElementAssertion.test.tsx b/packages/dom/test/unit/lib/ElementAssertion.test.tsx index b110348..82a0eec 100644 --- a/packages/dom/test/unit/lib/ElementAssertion.test.tsx +++ b/packages/dom/test/unit/lib/ElementAssertion.test.tsx @@ -1,30 +1,17 @@ import { AssertionError, expect } from "@assertive-ts/core"; import { render } from "@testing-library/react"; -import { ReactElement } from "react"; import { ElementAssertion } from "../../../src/lib/ElementAssertion"; -function TestComponent(): ReactElement { - return ( -
- -
- ); -} - -function TestComponentElement(): ReactElement { - return ( - - ); -} +import { NestedElementsTestComponent } from "./fixtures/nestedElementsTestComponent"; +import { SimpleTestComponent } from "./fixtures/simpleTestComponent"; +import { WithAttributesTestComponent } from "./fixtures/withAttributesTestComponent"; describe("[Unit] ElementAssertion.test.ts", () => { describe(".toBeInTheDocument", () => { context("when the element is in the document", () => { it("returns the assertion instance", async () => { - const { findByRole } = render(); + const { findByRole } = render(); const button = await findByRole("button", { name: "click me" }); const test = new ElementAssertion(button); @@ -39,7 +26,6 @@ describe("[Unit] ElementAssertion.test.ts", () => { context("when the element is not in the document", () => { it("throws an assertion error", () => { const detachedElement = document.createElement("div"); - const test = new ElementAssertion(detachedElement); expect(() => test.toBeInTheDocument()) @@ -51,10 +37,87 @@ describe("[Unit] ElementAssertion.test.ts", () => { }); }); + describe(".toContainElement", () => { + context("when the descendant element is contained in the ancestor element", () => { + context("and it is a direct child", () => { + it("returns the assertion instance", async () => { + const { findByTestId } = render(); + const grandparent = await findByTestId("grandparent"); + const parent = await findByTestId("parent"); + const child = await findByTestId("child"); + const svgElement = await findByTestId("svg-element"); + const grandparentTest = new ElementAssertion(grandparent); + const parentTest = new ElementAssertion(parent); + + expect(grandparentTest.toContainElement(parent)); + expect(grandparentTest.toContainElement(svgElement)); + expect(parentTest.toContainElement(child)); + + expect(() => grandparentTest.not.toContainElement(parent)) + .toThrowError(AssertionError) + .toHaveMessage("Expected the container to NOT contain the element"); + + expect(() => grandparentTest.not.toContainElement(svgElement)) + .toThrowError(AssertionError) + .toHaveMessage("Expected the container to NOT contain the element"); + + expect(() => parentTest.not.toContainElement(child)) + .toThrowError(AssertionError) + .toHaveMessage("Expected the container to NOT contain the element"); + }); + }); + + context("and it is an indirect child", () => { + it("returns the assertion instance", async () => { + const { findByTestId } = render(); + const grandparent = await findByTestId("grandparent"); + const child = await findByTestId("child"); + const grandparentTest = new ElementAssertion(grandparent); + + expect(grandparentTest.toContainElement(child)); + + expect(() => grandparentTest.not.toContainElement(child)) + .toThrowError(AssertionError) + .toHaveMessage("Expected the container to NOT contain the element"); + }); + }); + + context("and it is a deeply nested child", () => { + it("returns the assertion instance", async () => { + const { findByTestId } = render(); + const grandparent = await findByTestId("grandparent"); + const deepChild = await findByTestId("deep-child"); + const grandparentTest = new ElementAssertion(grandparent); + + expect(grandparentTest.toContainElement(deepChild)); + + expect(() => grandparentTest.not.toContainElement(deepChild)) + .toThrowError(AssertionError) + .toHaveMessage("Expected the container to NOT contain the element"); + }); + }); + }); + + context("when element is NOT contained in ancestor element", () => { + it("throws an assertion error", async () => { + const notChildElement = document.createElement("span"); + const { findByTestId } = render(); + const grandparent = await findByTestId("grandparent"); + const grandparentTest = new ElementAssertion(grandparent); + + expect(() => grandparentTest.toContainElement(notChildElement)) + .toThrowError(AssertionError) + .toHaveMessage("Expected the container to contain the element"); + + expect(grandparentTest.not.toContainElement(notChildElement)).toBeEqual(grandparentTest); + }); + }); + }); + describe(".toHaveAttribute", () => { context("when the element has the attribute with the expected value", () => { it("returns the assertion instance", async () => { - const { findByRole } = render(); + const { findByRole } = render(); const button = await findByRole("button", { name: "click me" }); const test = new ElementAssertion(button); @@ -68,7 +131,7 @@ describe("[Unit] ElementAssertion.test.ts", () => { context("when the element has the attribute with a not expected value", () => { it("throws an assertion error", async () => { - const { findByRole } = render(); + const { findByRole } = render(); const button = await findByRole("button", { name: "click me" }); const test = new ElementAssertion(button); @@ -83,7 +146,7 @@ describe("[Unit] ElementAssertion.test.ts", () => { context("when the element has the attribute without checking value", () => { it("returns the assertion instance", async () => { - const { findByRole } = render(); + const { findByRole } = render(); const button = await findByRole("button", { name: "click me" }); const test = new ElementAssertion(button); @@ -97,7 +160,7 @@ describe("[Unit] ElementAssertion.test.ts", () => { context("when the element does not have the attribute", () => { it("throws an assertion error", async () => { - const { findByRole } = render(); + const { findByRole } = render(); const button = await findByRole("button", { name: "click me" }); const test = new ElementAssertion(button); diff --git a/packages/dom/test/unit/lib/fixtures/nestedElementsTestComponent.tsx b/packages/dom/test/unit/lib/fixtures/nestedElementsTestComponent.tsx new file mode 100644 index 0000000..a9a8410 --- /dev/null +++ b/packages/dom/test/unit/lib/fixtures/nestedElementsTestComponent.tsx @@ -0,0 +1,20 @@ +import { ReactElement } from "react"; + +export function NestedElementsTestComponent(): ReactElement { + return ( + + + + + + + + + + + + + + + ); +} diff --git a/packages/dom/test/unit/lib/fixtures/simpleTestComponent.tsx b/packages/dom/test/unit/lib/fixtures/simpleTestComponent.tsx new file mode 100644 index 0000000..9243f37 --- /dev/null +++ b/packages/dom/test/unit/lib/fixtures/simpleTestComponent.tsx @@ -0,0 +1,9 @@ +import { ReactElement } from "react"; + +export function SimpleTestComponent(): ReactElement { + return ( +
+ +
+ ); + } diff --git a/packages/dom/test/unit/lib/fixtures/withAttributesTestComponent.tsx b/packages/dom/test/unit/lib/fixtures/withAttributesTestComponent.tsx new file mode 100644 index 0000000..9b3aa1f --- /dev/null +++ b/packages/dom/test/unit/lib/fixtures/withAttributesTestComponent.tsx @@ -0,0 +1,9 @@ +import { ReactElement } from "react"; + +export function WithAttributesTestComponent(): ReactElement { + return ( + + ); +}