Skip to content

Commit 92e9561

Browse files
committed
feat(clerk-js): Implement TaskResetPassword component and update context and update tests
1 parent f4c1a05 commit 92e9561

File tree

3 files changed

+92
-5
lines changed

3 files changed

+92
-5
lines changed

packages/clerk-js/src/ui/components/SessionTasks/tasks/TaskResetPassword/__tests__/TaskResetPassword.test.tsx

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import userEvent from '@testing-library/user-event';
12
import { describe, expect, it } from 'vitest';
23

34
import { bindCreateFixtures } from '@/test/create-fixtures';
@@ -10,8 +11,6 @@ const { createFixtures } = bindCreateFixtures('TaskResetPassword');
1011
describe('TaskResetPassword', () => {
1112
it('does not render component without existing session task', async () => {
1213
const { wrapper } = await createFixtures(f => {
13-
f.withOrganizations();
14-
f.withForceOrganizationSelection();
1514
f.withUser({
1615
email_addresses: ['test@clerk.com'],
1716
});
@@ -21,6 +20,84 @@ describe('TaskResetPassword', () => {
2120

2221
expect(queryByText('New password')).not.toBeInTheDocument();
2322
expect(queryByText('Confirm password')).not.toBeInTheDocument();
24-
expect(queryByRole('button', { name: /sign out/i })).not.toBeInTheDocument();
23+
expect(queryByText('Sign out of all other devices')).not.toBeInTheDocument();
24+
expect(queryByRole('link', { name: /sign out/i })).not.toBeInTheDocument();
25+
});
26+
27+
it('renders component when session task exists', async () => {
28+
const { wrapper } = await createFixtures(f => {
29+
f.withUser({
30+
email_addresses: ['test@clerk.com'],
31+
tasks: [{ key: 'reset-password' }],
32+
});
33+
});
34+
35+
const { queryByText, queryByRole } = render(<TaskResetPassword />, { wrapper });
36+
37+
expect(queryByText('New password')).toBeInTheDocument();
38+
expect(queryByText('Confirm password')).toBeInTheDocument();
39+
expect(queryByText('Sign out of all other devices')).toBeInTheDocument();
40+
expect(queryByRole('link', { name: /sign out/i })).toBeInTheDocument();
41+
});
42+
43+
it('renders the task components in the order of the tasks', async () => {
44+
const { wrapper } = await createFixtures(f => {
45+
f.withOrganizations();
46+
f.withForceOrganizationSelection();
47+
f.withUser({
48+
email_addresses: ['test@clerk.com'],
49+
tasks: [{ key: 'reset-password' }, { key: 'choose-organization' }],
50+
});
51+
});
52+
53+
const { queryByText, queryByRole } = render(<TaskResetPassword />, { wrapper });
54+
55+
expect(queryByText('New password')).toBeInTheDocument();
56+
expect(queryByText('Confirm password')).toBeInTheDocument();
57+
expect(queryByText('Sign out of all other devices')).toBeInTheDocument();
58+
expect(queryByRole('link', { name: /sign out/i })).toBeInTheDocument();
59+
});
60+
61+
it('displays user identifier in sign out section', async () => {
62+
const { wrapper } = await createFixtures(f => {
63+
f.withUser({
64+
email_addresses: ['user@test.com'],
65+
tasks: [{ key: 'reset-password' }],
66+
});
67+
});
68+
69+
const { findByText } = render(<TaskResetPassword />, { wrapper });
70+
71+
expect(await findByText(/user@test\.com/)).toBeInTheDocument();
72+
expect(await findByText('Sign out')).toBeInTheDocument();
73+
});
74+
75+
it('handles sign out correctly', async () => {
76+
const { wrapper, fixtures } = await createFixtures(f => {
77+
f.withUser({
78+
email_addresses: ['test@clerk.com'],
79+
tasks: [{ key: 'reset-password' }],
80+
});
81+
});
82+
83+
const { findByRole } = render(<TaskResetPassword />, { wrapper });
84+
const signOutButton = await findByRole('link', { name: /sign out/i });
85+
86+
await userEvent.click(signOutButton);
87+
88+
expect(fixtures.clerk.signOut).toHaveBeenCalled();
89+
});
90+
91+
it('renders with username when email is not available', async () => {
92+
const { wrapper } = await createFixtures(f => {
93+
f.withUser({
94+
username: 'testuser',
95+
tasks: [{ key: 'reset-password' }],
96+
});
97+
});
98+
99+
const { findByText } = render(<TaskResetPassword />, { wrapper });
100+
101+
expect(await findByText(/testuser/)).toBeInTheDocument();
25102
});
26103
});

packages/clerk-js/src/ui/contexts/ClerkUIComponentsContext.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
APIKeysProps,
44
PricingTableProps,
55
TaskChooseOrganizationProps,
6+
TaskResetPasswordProps,
67
UserButtonProps,
78
WaitlistProps,
89
} from '@clerk/shared/types';
@@ -27,7 +28,7 @@ import {
2728
UserVerificationContext,
2829
WaitlistContext,
2930
} from './components';
30-
import { TaskChooseOrganizationContext } from './components/SessionTasks';
31+
import { TaskChooseOrganizationContext, TaskResetPasswordContext } from './components/SessionTasks';
3132

3233
export function ComponentContextProvider({
3334
componentName,
@@ -126,6 +127,14 @@ export function ComponentContextProvider({
126127
{children}
127128
</TaskChooseOrganizationContext.Provider>
128129
);
130+
case 'TaskResetPassword':
131+
return (
132+
<TaskResetPasswordContext.Provider
133+
value={{ componentName: 'TaskResetPassword', ...(props as TaskResetPasswordProps) }}
134+
>
135+
{children}
136+
</TaskResetPasswordContext.Provider>
137+
);
129138
default:
130139
throw new Error(`Unknown component context: ${componentName}`);
131140
}

packages/clerk-js/src/ui/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,6 @@ export type AvailableComponentCtx =
187187
| OAuthConsentCtx
188188
| SubscriptionDetailsCtx
189189
| PlanDetailsCtx
190-
| TaskChooseOrganizationCtx;
190+
| TaskChooseOrganizationCtx
191+
| TaskResetPasswordCtx;
191192
export type AvailableComponentName = AvailableComponentCtx['componentName'];

0 commit comments

Comments
 (0)