Skip to content

Commit e672c04

Browse files
kdquistanchalalopenchiJoseLion
authored
feat(native): Add toBeVisible (#145)
Co-authored-by: Carolina López <lopenchii@gmail.com> Co-authored-by: Jose Luis Leon <joseluis5000l@gmail.com> Co-authored-by: Carolina Lopez <calopez@twilio.com>
1 parent bd8b2a7 commit e672c04

File tree

2 files changed

+200
-2
lines changed

2 files changed

+200
-2
lines changed

packages/native/src/lib/ElementAssertion.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class ElementAssertion extends Assertion<ReactTestInstance> {
4242
}
4343

4444
/**
45-
* Check if the component is enabled.
45+
* Check if the component is enabled and has not been disabled by an ancestor.
4646
*
4747
* @example
4848
* ```
@@ -94,6 +94,33 @@ export class ElementAssertion extends Assertion<ReactTestInstance> {
9494
});
9595
}
9696

97+
/**
98+
* Check if the element is visible and has not been hidden by an ancestor.
99+
*
100+
* @example
101+
* ```
102+
* expect(element).toBeVisible();
103+
* ```
104+
*
105+
* @returns the assertion instance
106+
*/
107+
public toBeVisible(): this {
108+
const error = new AssertionError({
109+
actual: this.actual,
110+
message: `Expected element ${this.toString()} to be visible.`,
111+
});
112+
const invertedError = new AssertionError({
113+
actual: this.actual,
114+
message: `Expected element ${this.toString()} NOT to be visible.`,
115+
});
116+
117+
return this.execute({
118+
assertWhen: this.isElementVisible(this.actual) && this.isAncestorVisible(this.actual),
119+
error,
120+
invertedError,
121+
});
122+
}
123+
97124
private isElementDisabled(element: ReactTestInstance): boolean {
98125
const { type } = element;
99126
const elementType = type.toString();
@@ -113,4 +140,24 @@ export class ElementAssertion extends Assertion<ReactTestInstance> {
113140
const { parent } = element;
114141
return parent !== null && (this.isElementDisabled(element) || this.isAncestorDisabled(parent));
115142
}
143+
144+
private isElementVisible(element: ReactTestInstance): boolean {
145+
const { type } = element;
146+
147+
if (type.toString() === "Modal") {
148+
return Boolean(element.props?.visible);
149+
}
150+
151+
return (
152+
get(element, "props.style.display") !== "none"
153+
&& get(element, "props.style.opacity") !== 0
154+
&& get(element, "props.accessibilityElementsHidden") !== true
155+
&& get(element, "props.importantForAccessibility") !== "no-hide-descendants"
156+
);
157+
}
158+
159+
private isAncestorVisible(element: ReactTestInstance): boolean {
160+
const { parent } = element;
161+
return parent === null || (this.isElementVisible(parent) && this.isAncestorVisible(parent));
162+
}
116163
}

packages/native/test/lib/ElementAssertion.test.tsx

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
11
import { AssertionError, expect } from "@assertive-ts/core";
2-
import { render } from "@testing-library/react-native";
2+
import { fireEvent, render } from "@testing-library/react-native";
3+
import { useState, useCallback, ReactElement } from "react";
34
import {
45
View,
56
TextInput,
67
Text,
8+
Modal,
9+
Button,
710
} from "react-native";
811

912
import { ElementAssertion } from "../../src/lib/ElementAssertion";
1013

14+
function SimpleToggleText(): ReactElement {
15+
const [isVisible, setIsVisible] = useState(true);
16+
17+
const handleToggle = useCallback((): void => {
18+
setIsVisible(prev => !prev);
19+
}, []);
20+
21+
return (
22+
<View>
23+
<Text style={{ display: isVisible ? "flex" : "none" }}>
24+
{"Toggle me!"}
25+
</Text>
26+
<Button
27+
title="Toggle Text"
28+
onPress={handleToggle}
29+
/>
30+
</View>
31+
);
32+
}
33+
1134
describe("[Unit] ElementAssertion.test.ts", () => {
1235
describe(".toBeDisabled", () => {
1336
context("when the element is TextInput", () => {
@@ -160,4 +183,132 @@ describe("[Unit] ElementAssertion.test.ts", () => {
160183
});
161184
});
162185
});
186+
187+
describe (".toBeVisible", () => {
188+
context("when the modal is visible", () => {
189+
it("returns the assertion instance", () => {
190+
const { getByTestId } = render(
191+
<Modal testID="id" visible={true} />,
192+
);
193+
const test = new ElementAssertion(getByTestId("id"));
194+
195+
expect(test.toBeVisible()).toBe(test);
196+
expect(() => test.not.toBeVisible())
197+
.toThrowError(AssertionError)
198+
.toHaveMessage("Expected element <Modal ... /> NOT to be visible.");
199+
});
200+
});
201+
202+
context("when the element contains 'display' property", () => {
203+
context("and display = none", () => {
204+
it("throws an error", () => {
205+
const { getByText, getByRole } = render(
206+
<SimpleToggleText />,
207+
);
208+
const textElement = new ElementAssertion(getByText("Toggle me!"));
209+
210+
expect(textElement.toBeVisible()).toBeEqual(textElement);
211+
212+
const toggleButton = getByRole("button", { name: "Toggle Text" });
213+
fireEvent.press(toggleButton);
214+
215+
expect(textElement.not.toBeVisible()).toBeEqual(textElement);
216+
});
217+
});
218+
219+
context("and display = flex", () => {
220+
it("returns the assertion instance", () => {
221+
const { getByTestId } = render(
222+
<View testID="id" style={{ display: "flex" }} />,
223+
);
224+
const test = new ElementAssertion(getByTestId("id"));
225+
226+
expect(test.toBeVisible()).toBe(test);
227+
expect(() => test.not.toBeVisible())
228+
.toThrowError(AssertionError)
229+
.toHaveMessage("Expected element <View ... /> NOT to be visible.");
230+
});
231+
});
232+
});
233+
234+
context("when the element contains 'accessibilityElementsHidden' property", () => {
235+
it("returns the assertion instance", () => {
236+
const { getByTestId } = render(
237+
<View testID="id" accessibilityElementsHidden={false} />,
238+
);
239+
const test = new ElementAssertion(getByTestId("id"));
240+
241+
expect(test.toBeVisible()).toBe(test);
242+
expect(() => test.not.toBeVisible())
243+
.toThrowError(AssertionError)
244+
.toHaveMessage("Expected element <View ... /> NOT to be visible.");
245+
});
246+
});
247+
248+
context("when the element contains 'importantForAccessibility' property", () => {
249+
it("returns the assertion instance", () => {
250+
const { getByTestId } = render(
251+
<View testID="id" importantForAccessibility={"yes"} />,
252+
);
253+
const test = new ElementAssertion(getByTestId("id"));
254+
255+
expect(test.toBeVisible()).toBe(test);
256+
expect(() => test.not.toBeVisible())
257+
.toThrowError(AssertionError)
258+
.toHaveMessage("Expected element <View ... /> NOT to be visible.");
259+
});
260+
});
261+
262+
context("when the parent element contains 'opacity' property", () => {
263+
context("and parent opacity = 0", () => {
264+
const { getByTestId } = render(
265+
<View testID="parentId" style={{ opacity: 0 }} >
266+
<View testID="childId" style={{ opacity: 1 }} />
267+
</View>,
268+
);
269+
270+
const parent = new ElementAssertion(getByTestId("parentId"));
271+
const child = new ElementAssertion(getByTestId("childId"));
272+
273+
it("returns assertion instance for NOT visible elements", () => {
274+
expect(parent.not.toBeVisible()).toBeEqual(parent);
275+
expect(child.not.toBeVisible()).toBeEqual(child);
276+
});
277+
278+
it("throws an error for visible elements", () => {
279+
expect(() => parent.toBeVisible())
280+
.toThrowError(AssertionError)
281+
.toHaveMessage("Expected element <View ... /> to be visible.");
282+
expect(() => child.toBeVisible())
283+
.toThrowError(AssertionError)
284+
.toHaveMessage("Expected element <View ... /> to be visible.");
285+
});
286+
});
287+
288+
context("and child opacity = 0", () => {
289+
const { getByTestId } = render(
290+
<View testID="parentId" style={{ opacity: 1 }} >
291+
<View testID="childId" style={{ opacity: 0 }} />
292+
</View>,
293+
);
294+
295+
const parent = new ElementAssertion(getByTestId("parentId"));
296+
const child = new ElementAssertion(getByTestId("childId"));
297+
298+
it("returns assertion instance for visible parent and NOT visible child", () => {
299+
expect(parent.toBeVisible()).toBeEqual(parent);
300+
expect(child.not.toBeVisible()).toBeEqual(child);
301+
});
302+
303+
it("throws an error for NOT visible parent and visible child", () => {
304+
expect(() => parent.not.toBeVisible())
305+
.toThrowError(AssertionError)
306+
.toHaveMessage("Expected element <View ... /> NOT to be visible.");
307+
expect(() => child.toBeVisible())
308+
.toThrowError(AssertionError)
309+
.toHaveMessage("Expected element <View ... /> to be visible.");
310+
});
311+
});
312+
});
313+
});
163314
});

0 commit comments

Comments
 (0)