From 98efb91c1a5d1413e06f299e9d6aff3f3b037e6f Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 27 May 2025 17:11:51 -0500 Subject: [PATCH 1/3] Create to haveFocus assertion and tests --- packages/dom/src/lib/ElementAssertion.ts | 28 ++++++++++++++++ .../test/unit/lib/ElementAssertion.test.tsx | 33 +++++++++++++++++++ .../unit/lib/fixtures/focusTestComponent.tsx | 10 ++++++ 3 files changed, 71 insertions(+) create mode 100644 packages/dom/test/unit/lib/fixtures/focusTestComponent.tsx diff --git a/packages/dom/src/lib/ElementAssertion.ts b/packages/dom/src/lib/ElementAssertion.ts index 537d8084..32e464d9 100644 --- a/packages/dom/src/lib/ElementAssertion.ts +++ b/packages/dom/src/lib/ElementAssertion.ts @@ -142,6 +142,34 @@ export class ElementAssertion extends Assertion { ); } + /** + * Check if the provided element is currently focused in the document. + * + * @returns The assertion instance. + */ + public toHaveFocus(): this { + + const hasFocus = this.actual === document.activeElement; + + const error = new AssertionError({ + actual: this.actual, + expected: document.activeElement, + message: "Expected the element to have focus.", + }); + + const invertedError = new AssertionError({ + actual: this.actual, + expected: document.activeElement, + message: "Expected the element not to have focus.", + }); + + return this.execute({ + assertWhen: hasFocus, + error, + invertedError, + }); + } + private getClassList(): string[] { return this.actual.className.split(/\s+/).filter(Boolean); } diff --git a/packages/dom/test/unit/lib/ElementAssertion.test.tsx b/packages/dom/test/unit/lib/ElementAssertion.test.tsx index 47bb1674..105419cc 100644 --- a/packages/dom/test/unit/lib/ElementAssertion.test.tsx +++ b/packages/dom/test/unit/lib/ElementAssertion.test.tsx @@ -3,6 +3,7 @@ import { render } from "@testing-library/react"; import { ElementAssertion } from "../../../src/lib/ElementAssertion"; +import { FocusTestComponent } from "./fixtures/focusTestComponent"; import { HaveClassTestComponent } from "./fixtures/haveClassTestComponent"; import { NestedElementsTestComponent } from "./fixtures/nestedElementsTestComponent"; import { SimpleTestComponent } from "./fixtures/simpleTestComponent"; @@ -265,4 +266,36 @@ describe("[Unit] ElementAssertion.test.ts", () => { }); }); + describe(".toHaveFocus", () => { + context("when the element has focus", () => { + it("returns the assertion instance", () => { + const { getByTestId } = render(); + const input1 = getByTestId("input1"); + input1.focus(); + const test = new ElementAssertion(input1); + + expect(test.toHaveFocus()).toBeEqual(test); + + expect(() => test.not.toHaveFocus()) + .toThrowError(AssertionError) + .toHaveMessage("Expected the element not to have focus."); + }); + }); + + context("when the element does not have focus", () => { + it("throws an assertion error", () => { + const { getByTestId } = render(); + const input1 = getByTestId("input1"); + const input2 = getByTestId("input2"); + input1.focus(); + const test = new ElementAssertion(input2); + + expect(() => test.toHaveFocus()) + .toThrowError(AssertionError) + .toHaveMessage("Expected the element to have focus."); + + expect(test.not.toHaveFocus()).toBeEqual(test); + }); + }); + }); }); diff --git a/packages/dom/test/unit/lib/fixtures/focusTestComponent.tsx b/packages/dom/test/unit/lib/fixtures/focusTestComponent.tsx new file mode 100644 index 00000000..91dd421a --- /dev/null +++ b/packages/dom/test/unit/lib/fixtures/focusTestComponent.tsx @@ -0,0 +1,10 @@ +import { ReactElement } from "react"; + +export function FocusTestComponent(): ReactElement { + return ( +
+ + +
+ ); +} From 7a10e46528f4ca3020f52873eaca272571a7e83a Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 22 Aug 2025 16:02:02 -0500 Subject: [PATCH 2/3] Updated output messages --- packages/dom/src/lib/ElementAssertion.ts | 4 ++-- packages/dom/test/unit/lib/ElementAssertion.test.tsx | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/dom/src/lib/ElementAssertion.ts b/packages/dom/src/lib/ElementAssertion.ts index 32e464d9..a0e51e9b 100644 --- a/packages/dom/src/lib/ElementAssertion.ts +++ b/packages/dom/src/lib/ElementAssertion.ts @@ -154,13 +154,13 @@ export class ElementAssertion extends Assertion { const error = new AssertionError({ actual: this.actual, expected: document.activeElement, - message: "Expected the element to have focus.", + message: "Expected the element to be focused", }); const invertedError = new AssertionError({ actual: this.actual, expected: document.activeElement, - message: "Expected the element not to have focus.", + message: "Expected the element NOT to be focused", }); return this.execute({ diff --git a/packages/dom/test/unit/lib/ElementAssertion.test.tsx b/packages/dom/test/unit/lib/ElementAssertion.test.tsx index 105419cc..7b0f46ca 100644 --- a/packages/dom/test/unit/lib/ElementAssertion.test.tsx +++ b/packages/dom/test/unit/lib/ElementAssertion.test.tsx @@ -278,7 +278,7 @@ describe("[Unit] ElementAssertion.test.ts", () => { expect(() => test.not.toHaveFocus()) .toThrowError(AssertionError) - .toHaveMessage("Expected the element not to have focus."); + .toHaveMessage("Expected the element NOT to be focused"); }); }); @@ -286,13 +286,11 @@ describe("[Unit] ElementAssertion.test.ts", () => { it("throws an assertion error", () => { const { getByTestId } = render(); const input1 = getByTestId("input1"); - const input2 = getByTestId("input2"); - input1.focus(); - const test = new ElementAssertion(input2); + const test = new ElementAssertion(input1); expect(() => test.toHaveFocus()) .toThrowError(AssertionError) - .toHaveMessage("Expected the element to have focus."); + .toHaveMessage("Expected the element to be focused"); expect(test.not.toHaveFocus()).toBeEqual(test); }); From ce56cb571b0819294a6ae647234b683738b6a868 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 19 Dec 2025 13:35:44 -0500 Subject: [PATCH 3/3] Add example for documentation --- packages/dom/src/lib/ElementAssertion.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/dom/src/lib/ElementAssertion.ts b/packages/dom/src/lib/ElementAssertion.ts index a0e51e9b..bdbeb36b 100644 --- a/packages/dom/src/lib/ElementAssertion.ts +++ b/packages/dom/src/lib/ElementAssertion.ts @@ -145,6 +145,12 @@ export class ElementAssertion extends Assertion { /** * Check if the provided element is currently focused in the document. * + * @example + * const userNameInput = document.querySelector('#username'); + * userNameInput.focus(); + * expect(userNameInput).toHaveFocus(); // passes + * expect(userNameInput).not.toHaveFocus(); // fails + * * @returns The assertion instance. */ public toHaveFocus(): this {