Skip to content

Commit fa900c9

Browse files
committed
Added initial logic structure
1 parent 8d7ffba commit fa900c9

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

packages/dom/src/lib/ElementAssertion.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,36 @@ export class ElementAssertion<T extends Element> extends Assertion<T> {
9090
invertedError,
9191
});
9292
}
93+
94+
public toHaveClass(classNames: string | string[], options: { exact?: boolean } = {}): this {
95+
const actualClassList = this.actual.className.split(/\s+/).filter(Boolean);
96+
const expectedClassList = Array.isArray(classNames) ? classNames : [classNames];
97+
const { exact = false } = options;
98+
99+
const error = new AssertionError({
100+
actual: actualClassList,
101+
expected: expectedClassList,
102+
message: exact
103+
? `Expected the element to have exactly these classes: "${expectedClassList.join(' ')}"`
104+
: `Expected the element to have class(es): "${expectedClassList.join(' ')}"`,
105+
});
106+
107+
const invertedError = new AssertionError({
108+
actual: actualClassList,
109+
expected: expectedClassList,
110+
message: exact
111+
? `Expected the element to NOT have exactly these classes: "${expectedClassList.join(' ')}"`
112+
: `Expected the element to NOT have class(es): "${expectedClassList.join(' ')}"`,
113+
});
114+
115+
const assertWhen = exact
116+
? actualClassList.length === expectedClassList.length && expectedClassList.every(cls => actualClassList.includes(cls))
117+
: expectedClassList.every(cls => actualClassList.includes(cls));
118+
119+
return this.execute({
120+
assertWhen,
121+
error,
122+
invertedError,
123+
});
124+
}
93125
}

packages/dom/test/unit/lib/ElementAssertion.test.tsx

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ElementAssertion } from "../../../src/lib/ElementAssertion";
66
import { NestedElementsTestComponent } from "./fixtures/nestedElementsTestComponent";
77
import { SimpleTestComponent } from "./fixtures/simpleTestComponent";
88
import { WithAttributesTestComponent } from "./fixtures/withAttributesTestComponent";
9+
import { HaveClassTestComponent } from "./fixtures/haveClassTestComponent";
910

1011
describe("[Unit] ElementAssertion.test.ts", () => {
1112
describe(".toBeInTheDocument", () => {
@@ -69,7 +70,7 @@ describe("[Unit] ElementAssertion.test.ts", () => {
6970

7071
context("and it is an indirect child", () => {
7172
it("returns the assertion instance", async () => {
72-
const { findByTestId } = render(<NestedElementsTestComponent/>);
73+
const { findByTestId } = render(<NestedElementsTestComponent />);
7374
const grandparent = await findByTestId("grandparent");
7475
const child = await findByTestId("child");
7576
const grandparentTest = new ElementAssertion(grandparent);
@@ -84,7 +85,7 @@ describe("[Unit] ElementAssertion.test.ts", () => {
8485

8586
context("and it is a deeply nested child", () => {
8687
it("returns the assertion instance", async () => {
87-
const { findByTestId } = render(<NestedElementsTestComponent/>);
88+
const { findByTestId } = render(<NestedElementsTestComponent />);
8889
const grandparent = await findByTestId("grandparent");
8990
const deepChild = await findByTestId("deep-child");
9091
const grandparentTest = new ElementAssertion(grandparent);
@@ -101,7 +102,7 @@ describe("[Unit] ElementAssertion.test.ts", () => {
101102
context("when element is NOT contained in ancestor element", () => {
102103
it("throws an assertion error", async () => {
103104
const notChildElement = document.createElement("span");
104-
const { findByTestId } = render(<NestedElementsTestComponent/>);
105+
const { findByTestId } = render(<NestedElementsTestComponent />);
105106
const grandparent = await findByTestId("grandparent");
106107
const grandparentTest = new ElementAssertion(grandparent);
107108

@@ -172,4 +173,51 @@ describe("[Unit] ElementAssertion.test.ts", () => {
172173
});
173174
});
174175
});
176+
177+
describe(".toHaveClass", () => {
178+
context("when the element has the the expected class", () => {
179+
it("returns the assertion instance", async () => {
180+
const { findByTestId } = render(<HaveClassTestComponent />);
181+
const divTest = await findByTestId("classTest");
182+
divTest.className = "foo bar";
183+
const test = new ElementAssertion(divTest);
184+
expect(test.toHaveClass("foo")).toBeEqual(test);
185+
});
186+
});
187+
188+
context("when the element does not have the expected class ", () => {
189+
it("throws an assertion error", async () => {
190+
const { findByTestId } = render(<HaveClassTestComponent />);
191+
const divTest = await findByTestId("classTest");
192+
divTest.className = "foo";
193+
const test = new ElementAssertion(divTest);
194+
expect(() => test.toHaveClass("bar"))
195+
.toThrowError(AssertionError)
196+
.toHaveMessage(`Expected the element to have class(es): "bar"`);
197+
});
198+
});
199+
200+
context("when the element element has the the exact matching expected class", () => {
201+
it("returns the assertion instance", async () => {
202+
const { findByTestId } = render(<HaveClassTestComponent />);
203+
const divTest = await findByTestId("classTest");
204+
divTest.className = "foo bar";
205+
const test = new ElementAssertion(divTest);
206+
expect(test.toHaveClass(["foo", "bar"], { exact: true })).toBeEqual(test);
207+
});
208+
});
209+
210+
context("when the element does not have the exact matching expected class ", async () => {
211+
it("throws an assertion error", async () => {
212+
const { findByTestId } = render(<HaveClassTestComponent />);
213+
const divTest = await findByTestId("classTest");
214+
divTest.className = "foo bar extra";
215+
const test = new ElementAssertion(divTest);
216+
expect(() => test.toHaveClass(["foo", "bar"], { exact: true }))
217+
.toThrowError(AssertionError)
218+
.toHaveMessage(`Expected the element to have exactly these classes: "foo bar"`);
219+
});
220+
});
221+
});
222+
175223
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ReactElement } from "react";
2+
3+
export function HaveClassTestComponent(): ReactElement {
4+
return (
5+
<div data-testid="classTest">
6+
Test text inside a div
7+
</div>
8+
);
9+
}

0 commit comments

Comments
 (0)