Skip to content

Commit ec60743

Browse files
author
João Dias
committed
feat(usePermission): added tests
1 parent 1dc889a commit ec60743

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import React from "react";
2+
import { usePermission } from "src/hooks";
3+
4+
// Test component that uses the hook
5+
interface TestComponentProps {
6+
permissionType: PermissionName;
7+
onStateChange?: (state: string) => void;
8+
}
9+
10+
const TestComponent: React.FC<TestComponentProps> = ({ permissionType, onStateChange }) => {
11+
const permissionState = usePermission({ name: permissionType });
12+
13+
React.useEffect(() => {
14+
onStateChange?.(permissionState);
15+
}, [permissionState, onStateChange]);
16+
17+
return (
18+
<div data-testid="permission-container">
19+
<span data-testid="permission-state">{permissionState}</span>
20+
{permissionState === "prompt" && (
21+
<button
22+
data-testid="request-permission"
23+
onClick={() => {
24+
// Simulate permission request
25+
if (permissionType === "notifications") {
26+
Notification.requestPermission();
27+
}
28+
}}
29+
>
30+
Request Permission
31+
</button>
32+
)}
33+
</div>
34+
);
35+
};
36+
37+
describe("usePermission Hook", () => {
38+
beforeEach(() => {
39+
// Mock the Permissions API
40+
cy.window().then((win) => {
41+
let currentState = "prompt";
42+
const listeners = new Set<() => void>();
43+
44+
const mockPermissionStatus = {
45+
state: currentState,
46+
addEventListener: (event: string, handler: () => void) => {
47+
if (event === "change") {
48+
listeners.add(handler);
49+
}
50+
},
51+
removeEventListener: (event: string, handler: () => void) => {
52+
if (event === "change") {
53+
listeners.delete(handler);
54+
}
55+
},
56+
};
57+
58+
// Add method to trigger state changes (for testing)
59+
win.mockPermissionChange = (newState: PermissionState) => {
60+
currentState = newState;
61+
mockPermissionStatus.state = newState;
62+
listeners.forEach((handler) => handler());
63+
};
64+
65+
cy.stub(navigator.permissions, "query").resolves(mockPermissionStatus);
66+
});
67+
});
68+
69+
it("should update state when permission status changes", () => {
70+
const onStateChange = cy.spy().as("stateChangeSpy");
71+
72+
cy.mount(<TestComponent permissionType="notifications" onStateChange={onStateChange} />);
73+
74+
// Wait for initial states
75+
cy.get("@stateChangeSpy").should("have.been.calledWith", "not-requested");
76+
cy.get("@stateChangeSpy").should("have.been.calledWith", "requested");
77+
cy.get("@stateChangeSpy").should("have.been.calledWith", "prompt");
78+
79+
// Simulate permission grant
80+
cy.window().then((win) => {
81+
win.mockPermissionChange("granted");
82+
});
83+
84+
cy.get('[data-testid="permission-state"]').should("have.text", "granted");
85+
cy.get("@stateChangeSpy").should("have.been.calledWith", "granted");
86+
});
87+
88+
it("should handle permission denial", () => {
89+
cy.mount(<TestComponent permissionType="notifications" />);
90+
91+
// Simulate permission denial
92+
cy.window().then((win) => {
93+
win.mockPermissionChange("denied");
94+
});
95+
96+
cy.get('[data-testid="permission-state"]').should("have.text", "denied");
97+
});
98+
99+
it("should show request button only in prompt state", () => {
100+
cy.mount(<TestComponent permissionType="notifications" />);
101+
102+
// Should show button in prompt state
103+
cy.get('[data-testid="request-permission"]').should("exist");
104+
105+
// Simulate permission grant
106+
cy.window().then((win) => {
107+
win.mockPermissionChange("granted");
108+
});
109+
110+
// Button should disappear
111+
cy.get('[data-testid="request-permission"]').should("not.exist");
112+
});
113+
114+
it("should cleanup event listeners on unmount", () => {
115+
const TestWrapper = () => {
116+
const [show, setShow] = React.useState(true);
117+
118+
return (
119+
<div>
120+
{show && <TestComponent permissionType="notifications" />}
121+
<button data-testid="toggle-component" onClick={() => setShow(false)}>
122+
Unmount
123+
</button>
124+
</div>
125+
);
126+
};
127+
128+
cy.mount(<TestWrapper />);
129+
130+
// Unmount component
131+
cy.get('[data-testid="toggle-component"]').click();
132+
133+
// Simulate permission change after unmount
134+
cy.window().then((win) => {
135+
win.mockPermissionChange("granted");
136+
});
137+
138+
// Verify component no longer exists
139+
cy.get('[data-testid="permission-state"]').should("not.exist");
140+
});
141+
142+
it("should handle different permission types", () => {
143+
cy.mount(<TestComponent permissionType="camera" />);
144+
145+
cy.get('[data-testid="permission-state"]').should("have.text", "prompt");
146+
147+
cy.window().then((win) => {
148+
win.mockPermissionChange("granted");
149+
});
150+
151+
cy.get('[data-testid="permission-state"]').should("have.text", "granted");
152+
});
153+
154+
it("should handle rapid permission state changes", () => {
155+
const onStateChange = cy.spy().as("stateChangeSpy");
156+
157+
cy.mount(<TestComponent permissionType="notifications" onStateChange={onStateChange} />);
158+
159+
// Simulate rapid state changes
160+
cy.window().then((win) => {
161+
win.mockPermissionChange("prompt");
162+
win.mockPermissionChange("granted");
163+
win.mockPermissionChange("denied");
164+
});
165+
166+
// Should end up in final state
167+
cy.get('[data-testid="permission-state"]').should("have.text", "denied");
168+
});
169+
170+
it("should handle permission descriptor changes", () => {
171+
const TestWrapper = () => {
172+
const [permissionType, setPermissionType] = React.useState<PermissionName>("notifications");
173+
174+
return (
175+
<div>
176+
<TestComponent permissionType={permissionType} />
177+
<button data-testid="change-permission" onClick={() => setPermissionType("camera")}>
178+
Change Permission
179+
</button>
180+
</div>
181+
);
182+
};
183+
184+
cy.mount(<TestWrapper />);
185+
186+
// Change permission type
187+
cy.get('[data-testid="change-permission"]').click();
188+
189+
// Should go through state cycle for new permission
190+
cy.get('[data-testid="permission-state"]').should("have.text", "prompt");
191+
});
192+
});

0 commit comments

Comments
 (0)