diff --git a/integration/templates/next-app-router/src/app/user-button/page.tsx b/integration/templates/next-app-router/src/app/user-button/page.tsx
new file mode 100644
index 0000000000..9d776a7809
--- /dev/null
+++ b/integration/templates/next-app-router/src/app/user-button/page.tsx
@@ -0,0 +1,9 @@
+import { UserButton } from '@clerk/nextjs';
+
+export default function Page() {
+ return (
+
+ Loading user button>} />
+
+ );
+}
diff --git a/integration/templates/react-vite/src/main.tsx b/integration/templates/react-vite/src/main.tsx
index 86f1252b5e..015a037595 100644
--- a/integration/templates/react-vite/src/main.tsx
+++ b/integration/templates/react-vite/src/main.tsx
@@ -11,6 +11,7 @@ import UserProfile from './user';
import UserProfileCustom from './custom-user-profile';
import UserButtonCustom from './custom-user-button';
import UserButtonCustomTrigger from './custom-user-button-trigger';
+import UserButton from './user-button';
const Root = () => {
const navigate = useNavigate();
@@ -53,6 +54,10 @@ const router = createBrowserRouter([
path: '/user/*',
element: ,
},
+ {
+ path: '/user-button',
+ element: ,
+ },
{
path: '/protected',
element: ,
diff --git a/integration/templates/react-vite/src/user-button/index.tsx b/integration/templates/react-vite/src/user-button/index.tsx
new file mode 100644
index 0000000000..a8c6df3a10
--- /dev/null
+++ b/integration/templates/react-vite/src/user-button/index.tsx
@@ -0,0 +1,9 @@
+import { UserButton } from '@clerk/clerk-react';
+
+export default function Page() {
+ return (
+
+ Loading user button>} />
+
+ );
+}
diff --git a/integration/tests/components.test.ts b/integration/tests/components.test.ts
new file mode 100644
index 0000000000..fe819bbe53
--- /dev/null
+++ b/integration/tests/components.test.ts
@@ -0,0 +1,77 @@
+import { expect, test } from '@playwright/test';
+
+import { appConfigs } from '../presets';
+import type { FakeUser } from '../testUtils';
+import { createTestUtils, testAgainstRunningApps } from '../testUtils';
+
+testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('component smoke tests @generic', ({ app }) => {
+ let fakeUser: FakeUser;
+
+ test.beforeAll(async () => {
+ const u = createTestUtils({ app });
+ fakeUser = u.services.users.createFakeUser({
+ withPhoneNumber: true,
+ withUsername: true,
+ });
+ await u.services.users.createBapiUser(fakeUser);
+ });
+
+ test.afterAll(async () => {
+ await app.teardown();
+ await fakeUser.deleteIfExists();
+ });
+
+ const components = [
+ {
+ name: 'SignIn',
+ path: '/sign-in',
+ fallback: 'Loading sign in',
+ },
+ {
+ name: 'SignUp',
+ path: '/sign-up',
+ fallback: 'Loading sign up',
+ },
+ {
+ name: 'UserProfile',
+ path: '/user',
+ protected: true,
+ fallback: 'Loading user profile',
+ },
+ {
+ name: 'UserButton',
+ path: '/user-button',
+ protected: true,
+ fallback: 'Loading user button',
+ },
+ ];
+
+ const signIn = async ({ app, page, context }) => {
+ const u = createTestUtils({ app, page, context });
+ await u.po.signIn.goTo();
+ await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
+ await u.po.expect.toBeSignedIn();
+ };
+
+ const signOut = async ({ app, page, context }) => {
+ const u = createTestUtils({ app, page, context });
+ await u.page.evaluate(async () => {
+ await window.Clerk.signOut();
+ });
+ };
+
+ for (const component of components) {
+ test(`${component.name} supports fallback`, async ({ page, context }) => {
+ // eslint-disable-next-line playwright/no-conditional-in-test
+ if (component.protected) {
+ await signIn({ app, page, context });
+ }
+
+ const u = createTestUtils({ app, page, context });
+ await u.page.goToRelative(component.path);
+ await expect(u.page.getByText(component.fallback)).toBeVisible();
+
+ await signOut({ app, page, context });
+ });
+ }
+});
diff --git a/integration/tests/sign-in-flow.test.ts b/integration/tests/sign-in-flow.test.ts
index c8b1e8964e..29243df5c4 100644
--- a/integration/tests/sign-in-flow.test.ts
+++ b/integration/tests/sign-in-flow.test.ts
@@ -23,12 +23,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign in f
await app.teardown();
});
- test('sign in supports fallback', async ({ page, context }) => {
- const u = createTestUtils({ app, page, context });
- await u.page.goToRelative('/sign-in');
- await expect(u.page.getByText('Loading sign in')).toBeVisible();
- });
-
test('sign in with email and password', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
diff --git a/integration/tests/sign-up-flow.test.ts b/integration/tests/sign-up-flow.test.ts
index 8b074aadad..cedba70f21 100644
--- a/integration/tests/sign-up-flow.test.ts
+++ b/integration/tests/sign-up-flow.test.ts
@@ -10,12 +10,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign up f
await app.teardown();
});
- test('sign up supports fallback', async ({ page, context }) => {
- const u = createTestUtils({ app, page, context });
- await u.page.goToRelative('/sign-up');
- await expect(u.page.getByText('Loading sign up')).toBeVisible();
- });
-
test('sign up with email and password', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
const fakeUser = u.services.users.createFakeUser({