Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/hungry-chicken-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
42 changes: 42 additions & 0 deletions integration/templates/vue-vite/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,48 @@ const routes = [
path: '/billing/subscription-details-btn',
component: () => import('./views/billing/SubscriptionDetailsBtn.vue'),
},
// Composable state routes (public, for testing composable output)
{
name: 'AuthState',
path: '/auth-state',
component: () => import('./views/AuthState.vue'),
},
{
name: 'UserState',
path: '/user-state',
component: () => import('./views/UserState.vue'),
},
{
name: 'SessionState',
path: '/session-state',
component: () => import('./views/SessionState.vue'),
},
{
name: 'OrgState',
path: '/org-state',
component: () => import('./views/OrgState.vue'),
},
// Component test routes
{
name: 'SignOut',
path: '/sign-out',
component: () => import('./views/SignOutPage.vue'),
},
{
name: 'OrganizationList',
path: '/org-list',
component: () => import('./views/OrganizationListPage.vue'),
},
{
name: 'CreateOrganization',
path: '/create-org',
component: () => import('./views/CreateOrganizationPage.vue'),
},
{
name: 'ShowComponent',
path: '/show-component',
component: () => import('./views/ShowComponent.vue'),
},
];

const router = createRouter({
Expand Down
17 changes: 17 additions & 0 deletions integration/templates/vue-vite/src/views/AuthState.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import { useAuth } from '@clerk/vue';

const { isLoaded, isSignedIn, userId, sessionId, orgId, orgRole, orgSlug } = useAuth();
</script>

<template>
<div id="auth-state">
<p data-auth-is-loaded>{{ isLoaded }}</p>
<p data-auth-is-signed-in>{{ isSignedIn }}</p>
<p data-auth-user-id>{{ userId }}</p>
<p data-auth-session-id>{{ sessionId }}</p>
<p data-auth-org-id>{{ orgId }}</p>
<p data-auth-org-role>{{ orgRole }}</p>
<p data-auth-org-slug>{{ orgSlug }}</p>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
import { CreateOrganization } from '@clerk/vue';
</script>

<template>
<CreateOrganization />
</template>
15 changes: 15 additions & 0 deletions integration/templates/vue-vite/src/views/OrgState.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { useOrganization } from '@clerk/vue';

const { isLoaded, organization, membership } = useOrganization();
</script>

<template>
<div id="org-state">
<p data-org-is-loaded>{{ isLoaded }}</p>
<p data-org-id>{{ organization?.id }}</p>
<p data-org-name>{{ organization?.name }}</p>
<p data-org-slug>{{ organization?.slug }}</p>
<p data-org-role>{{ membership?.role }}</p>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
import { OrganizationList } from '@clerk/vue';
</script>

<template>
<OrganizationList />
</template>
15 changes: 15 additions & 0 deletions integration/templates/vue-vite/src/views/SessionState.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { useSession } from '@clerk/vue';

const { isLoaded, isSignedIn, session } = useSession();
</script>

<template>
<div id="session-state">
<p data-session-is-loaded>{{ isLoaded }}</p>
<p data-session-is-signed-in>{{ isSignedIn }}</p>
<p data-session-id>{{ session?.id }}</p>
<p data-session-status>{{ session?.status }}</p>
<p data-session-user-id>{{ session?.user?.id }}</p>
</div>
</template>
34 changes: 34 additions & 0 deletions integration/templates/vue-vite/src/views/ShowComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script setup lang="ts">
import { Show } from '@clerk/vue';
</script>

<template>
<div id="show-tests">
<div data-show-signed-in>
<Show when="signed-in">
<p>show-signed-in-content</p>
</Show>
</div>
<div data-show-signed-out>
<Show when="signed-out">
<p>show-signed-out-content</p>
</Show>
</div>
<div data-show-role>
<Show :when="{ role: 'org:admin' }">
<p>show-admin-content</p>
<template #fallback>
<p>show-admin-fallback</p>
</template>
</Show>
</div>
<div data-show-permission>
<Show :when="{ permission: 'org:sys_memberships:manage' }">
<p>show-permission-content</p>
<template #fallback>
<p>show-permission-fallback</p>
</template>
</Show>
</div>
</div>
</template>
15 changes: 15 additions & 0 deletions integration/templates/vue-vite/src/views/SignOutPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { SignOutButton, Show } from '@clerk/vue';
</script>

<template>
<div>
<Show when="signed-in">
<p data-signed-in>You are signed in</p>
<SignOutButton />
</Show>
<Show when="signed-out">
<p data-signed-out>You are signed out</p>
</Show>
</div>
</template>
16 changes: 16 additions & 0 deletions integration/templates/vue-vite/src/views/UserState.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { useUser } from '@clerk/vue';

const { isLoaded, isSignedIn, user } = useUser();
</script>

<template>
<div id="user-state">
<p data-user-is-loaded>{{ isLoaded }}</p>
<p data-user-is-signed-in>{{ isSignedIn }}</p>
<p data-user-id>{{ user?.id }}</p>
<p data-user-email>{{ user?.primaryEmailAddress?.emailAddress }}</p>
<p data-user-first-name>{{ user?.firstName }}</p>
<p data-user-last-name>{{ user?.lastName }}</p>
</div>
</template>
19 changes: 17 additions & 2 deletions integration/tests/vue/components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('basic te
});

test.afterAll(async () => {
await fakeOrganization.delete();
await fakeUser.deleteIfExists();
await fakeOrganization?.delete();
await fakeUser?.deleteIfExists();

await app.teardown();
});
Expand Down Expand Up @@ -252,6 +252,21 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('basic te
await u.page.waitForAppUrl('/');
});

test('<SignOutButton /> signs the user out when clicked', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();
await u.page.waitForAppUrl('/');

await u.page.goToRelative('/sign-out');
await expect(u.page.locator('[data-signed-in]')).toBeVisible();

await u.page.getByRole('button', { name: /Sign out/i }).click();
await u.po.expect.toBeSignedOut();
});

test('redirects to sign-in when unauthenticated', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/profile');
Expand Down
111 changes: 111 additions & 0 deletions integration/tests/vue/composables.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { expect, test } from '@playwright/test';

import { appConfigs } from '../../presets';
import type { FakeOrganization, FakeUser } from '../../testUtils';
import { createTestUtils, testAgainstRunningApps } from '../../testUtils';

testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('composable tests for @vue', ({ app }) => {
test.describe.configure({ mode: 'parallel' });

let fakeUser: FakeUser;
let fakeOrganization: FakeOrganization;

test.beforeAll(async () => {
const u = createTestUtils({ app });
fakeUser = u.services.users.createFakeUser();
const user = await u.services.users.createBapiUser(fakeUser);
fakeOrganization = await u.services.users.createFakeOrganization(user.id);
});

test.afterAll(async () => {
await fakeOrganization?.delete();
await fakeUser?.deleteIfExists();
await app.teardown();
});

test.afterEach(async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.signOut();
await u.page.context().clearCookies();
});

test('useAuth() returns correct values when signed in', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

await u.page.goToRelative('/auth-state');
await expect(u.page.locator('[data-auth-is-loaded]')).toContainText('true');
await expect(u.page.locator('[data-auth-is-signed-in]')).toContainText('true');
await expect(u.page.locator('[data-auth-user-id]')).not.toHaveText('');
await expect(u.page.locator('[data-auth-session-id]')).not.toHaveText('');
});

test('useAuth() returns organization data when org is active', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

// Wait for org to be selected (the org switcher auto-selects)
await u.page.waitForAppUrl('/');
await u.po.organizationSwitcher.waitForMounted();
await u.po.organizationSwitcher.waitForAnOrganizationToSelected();

await u.page.goToRelative('/auth-state');
await expect(u.page.locator('[data-auth-org-id]')).not.toHaveText('');
await expect(u.page.locator('[data-auth-org-role]')).toContainText('org:admin');
});

test('useUser() returns user data when signed in', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

await u.page.goToRelative('/user-state');
await expect(u.page.locator('[data-user-is-loaded]')).toContainText('true');
await expect(u.page.locator('[data-user-is-signed-in]')).toContainText('true');
await expect(u.page.locator('[data-user-id]')).not.toHaveText('');
await expect(u.page.locator('[data-user-email]')).toContainText(fakeUser.email);
await expect(u.page.locator('[data-user-first-name]')).toContainText(fakeUser.firstName);
await expect(u.page.locator('[data-user-last-name]')).toContainText(fakeUser.lastName);
});

test('useSession() returns session data when signed in', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

await u.page.goToRelative('/session-state');
await expect(u.page.locator('[data-session-is-loaded]')).toContainText('true');
await expect(u.page.locator('[data-session-is-signed-in]')).toContainText('true');
await expect(u.page.locator('[data-session-id]')).not.toHaveText('');
await expect(u.page.locator('[data-session-status]')).toContainText('active');
});

test('useOrganization() returns organization data when org is active', async ({ page, context }) => {
const u = createTestUtils({ app, page, context });
await u.page.goToRelative('/sign-in');
await u.po.signIn.waitForMounted();
await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
await u.po.expect.toBeSignedIn();

// Wait for org to be selected
await u.page.waitForAppUrl('/');
await u.po.organizationSwitcher.waitForMounted();
await u.po.organizationSwitcher.waitForAnOrganizationToSelected();

await u.page.goToRelative('/org-state');
await expect(u.page.locator('[data-org-is-loaded]')).toContainText('true');
await expect(u.page.locator('[data-org-id]')).not.toHaveText('');
await expect(u.page.locator('[data-org-name]')).toContainText(fakeOrganization.name);
await expect(u.page.locator('[data-org-role]')).toContainText('org:admin');
});
});
Loading
Loading