Skip to content

Commit 090ca74

Browse files
authored
feat(clerk-js,nextjs,clerk-react,testing,types,vue): UserAvatar component (#6808)
1 parent 2d4d8cb commit 090ca74

File tree

37 files changed

+392
-14
lines changed

37 files changed

+392
-14
lines changed

.changeset/purple-apples-read.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@clerk/tanstack-react-start': minor
3+
'@clerk/chrome-extension': minor
4+
'@clerk/react-router': minor
5+
'@clerk/clerk-js': minor
6+
'@clerk/nextjs': minor
7+
'@clerk/clerk-react': minor
8+
'@clerk/remix': minor
9+
'@clerk/types': minor
10+
'@clerk/vue': minor
11+
---
12+
13+
Add new <UserAvatar /> component

.changeset/silver-tools-clean.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/testing': minor
3+
---
4+
5+
Add Playwright testing helpers under unstable page-objects: `userAvatar.goTo()`, `userAvatar.waitForMounted()`, and `userAvatar.toBeVisible()` for <UserAvatar />.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { UserAvatar } from '@clerk/nextjs';
2+
3+
export default function Page() {
4+
return (
5+
<div>
6+
<UserAvatar fallback={<>Loading user avatar</>} />
7+
</div>
8+
);
9+
}

integration/templates/react-vite/src/main.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import UserButtonCustomDynamicLabelsAndCustomPages from './custom-user-button/wi
1515
import UserButtonCustomTrigger from './custom-user-button-trigger';
1616
import UserButtonCustomDynamicItems from './custom-user-button/with-dynamic-items.tsx';
1717
import UserButton from './user-button';
18+
import UserAvatar from './user-avatar';
1819
import Waitlist from './waitlist';
1920
import OrganizationProfile from './organization-profile';
2021
import OrganizationList from './organization-list';
@@ -68,6 +69,10 @@ const router = createBrowserRouter([
6869
path: '/user-button',
6970
element: <UserButton />,
7071
},
72+
{
73+
path: '/user-avatar',
74+
element: <UserAvatar />,
75+
},
7176
{
7277
path: '/protected',
7378
element: <Protected />,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { UserAvatar } from '@clerk/clerk-react';
2+
import React from 'react';
3+
4+
export default function UserAvatarPage() {
5+
return (
6+
<div>
7+
<h1>UserAvatar</h1>
8+
<UserAvatar fallback={<>Loading user avatar</>} />
9+
</div>
10+
);
11+
}

integration/templates/vue-vite/src/router.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ const routes = [
4141
path: '/pricing-table',
4242
component: () => import('./views/PricingTable.vue'),
4343
},
44+
{
45+
name: 'UserAvatar',
46+
path: '/user-avatar',
47+
component: () => import('./views/UserAvatar.vue'),
48+
},
4449
// This was added for billing tests
4550
{
4651
name: 'User',
@@ -72,7 +77,7 @@ const router = createRouter({
7277

7378
router.beforeEach(async (to, _, next) => {
7479
const { isSignedIn, isLoaded } = useAuth();
75-
const authenticatedPages = ['Profile', 'Admin', 'CustomUserProfile', 'CustomOrganizationProfile'];
80+
const authenticatedPages = ['Profile', 'Admin', 'CustomUserProfile', 'CustomOrganizationProfile', 'UserAvatar'];
7681

7782
if (!isLoaded.value) {
7883
await waitForClerkJsLoaded(isLoaded);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script setup lang="ts">
2+
import { UserAvatar } from '@clerk/vue';
3+
</script>
4+
5+
<template>
6+
<div>
7+
<h1>UserAvatar</h1>
8+
<UserAvatar />
9+
</div>
10+
</template>

integration/tests/components.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('component
4141
protected: true,
4242
fallback: 'Loading user profile',
4343
},
44+
{
45+
name: 'UserAvatar',
46+
path: '/user-avatar',
47+
protected: true,
48+
fallback: 'Loading user avatar',
49+
},
4450
{
4551
name: 'UserButton',
4652
path: '/user-button',
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { test } from '@playwright/test';
2+
3+
import { appConfigs } from '../presets';
4+
import type { FakeUser } from '../testUtils';
5+
import { createTestUtils, testAgainstRunningApps } from '../testUtils';
6+
7+
testAgainstRunningApps({
8+
withEnv: [appConfigs.envs.withEmailCodes],
9+
withPattern: ['react.vite.withEmailCodes', 'vue.vite'],
10+
})('UserAvatar component integration tests @generic', ({ app }) => {
11+
test.describe.configure({ mode: 'serial' });
12+
13+
let fakeUser: FakeUser;
14+
15+
test.beforeAll(async () => {
16+
const u = createTestUtils({ app });
17+
fakeUser = u.services.users.createFakeUser({
18+
withPhoneNumber: true,
19+
withUsername: true,
20+
});
21+
await u.services.users.createBapiUser(fakeUser);
22+
});
23+
24+
test.afterAll(async () => {
25+
await app.teardown();
26+
await fakeUser.deleteIfExists();
27+
});
28+
29+
test.afterEach(async ({ page, context }) => {
30+
const u = createTestUtils({ app, page, context });
31+
await u.page.signOut();
32+
await u.page.context().clearCookies();
33+
});
34+
35+
test('UserAvatar loads and renders correctly when user is signed in', async ({ page, context }) => {
36+
const u = createTestUtils({ app, page, context });
37+
38+
await u.po.signIn.goTo();
39+
await u.po.signIn.signInWithEmailAndInstantPassword({
40+
email: fakeUser.email,
41+
password: fakeUser.password,
42+
});
43+
await u.po.expect.toBeSignedIn();
44+
45+
await u.po.userAvatar.goTo();
46+
await u.po.userAvatar.toBeVisible();
47+
});
48+
});

integration/tests/vue/components.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('basic te
3838
await expect(u.page.getByRole('link', { name: /Sign in/i })).toBeVisible();
3939
});
4040

41+
test('render UserAvatar component when user completes sign in flow', async ({ page, context }) => {
42+
const u = createTestUtils({ app, page, context });
43+
await u.page.goToRelative('/sign-in');
44+
await u.po.signIn.waitForMounted();
45+
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
46+
await u.po.expect.toBeSignedIn();
47+
48+
await u.po.userAvatar.goTo();
49+
await u.po.userAvatar.toBeVisible();
50+
});
51+
4152
test('render user button component when user completes sign in flow', async ({ page, context }) => {
4253
const u = createTestUtils({ app, page, context });
4354
await u.page.goToRelative('/sign-in');

0 commit comments

Comments
 (0)