From 8e1e8811729c2b63b3bc21b146062f218ca7c060 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Fri, 13 Mar 2026 12:14:45 -0400 Subject: [PATCH 1/3] test(e2e): Expand Vue testing --- .changeset/hungry-chicken-flash.md | 2 + integration/templates/vue-vite/src/router.ts | 42 +++++++ .../vue-vite/src/views/AuthState.vue | 17 +++ .../src/views/CreateOrganizationPage.vue | 10 ++ .../templates/vue-vite/src/views/OrgState.vue | 15 +++ .../src/views/OrganizationListPage.vue | 10 ++ .../vue-vite/src/views/SessionState.vue | 15 +++ .../vue-vite/src/views/ShowComponent.vue | 34 ++++++ .../vue-vite/src/views/SignOutPage.vue | 15 +++ .../vue-vite/src/views/UserState.vue | 16 +++ integration/tests/vue/composables.test.ts | 111 ++++++++++++++++++ integration/tests/vue/organizations.test.ts | 74 ++++++++++++ integration/tests/vue/show-component.test.ts | 99 ++++++++++++++++ integration/tests/vue/sign-out.test.ts | 43 +++++++ 14 files changed, 503 insertions(+) create mode 100644 .changeset/hungry-chicken-flash.md create mode 100644 integration/templates/vue-vite/src/views/AuthState.vue create mode 100644 integration/templates/vue-vite/src/views/CreateOrganizationPage.vue create mode 100644 integration/templates/vue-vite/src/views/OrgState.vue create mode 100644 integration/templates/vue-vite/src/views/OrganizationListPage.vue create mode 100644 integration/templates/vue-vite/src/views/SessionState.vue create mode 100644 integration/templates/vue-vite/src/views/ShowComponent.vue create mode 100644 integration/templates/vue-vite/src/views/SignOutPage.vue create mode 100644 integration/templates/vue-vite/src/views/UserState.vue create mode 100644 integration/tests/vue/composables.test.ts create mode 100644 integration/tests/vue/organizations.test.ts create mode 100644 integration/tests/vue/show-component.test.ts create mode 100644 integration/tests/vue/sign-out.test.ts diff --git a/.changeset/hungry-chicken-flash.md b/.changeset/hungry-chicken-flash.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/hungry-chicken-flash.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/integration/templates/vue-vite/src/router.ts b/integration/templates/vue-vite/src/router.ts index 6fd11280ae6..59e951c3bd1 100644 --- a/integration/templates/vue-vite/src/router.ts +++ b/integration/templates/vue-vite/src/router.ts @@ -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({ diff --git a/integration/templates/vue-vite/src/views/AuthState.vue b/integration/templates/vue-vite/src/views/AuthState.vue new file mode 100644 index 00000000000..93af9193253 --- /dev/null +++ b/integration/templates/vue-vite/src/views/AuthState.vue @@ -0,0 +1,17 @@ + + + diff --git a/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue b/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue new file mode 100644 index 00000000000..cd855f7d040 --- /dev/null +++ b/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue @@ -0,0 +1,10 @@ + + + diff --git a/integration/templates/vue-vite/src/views/OrgState.vue b/integration/templates/vue-vite/src/views/OrgState.vue new file mode 100644 index 00000000000..c249ef0680d --- /dev/null +++ b/integration/templates/vue-vite/src/views/OrgState.vue @@ -0,0 +1,15 @@ + + + diff --git a/integration/templates/vue-vite/src/views/OrganizationListPage.vue b/integration/templates/vue-vite/src/views/OrganizationListPage.vue new file mode 100644 index 00000000000..73fa97fc2cd --- /dev/null +++ b/integration/templates/vue-vite/src/views/OrganizationListPage.vue @@ -0,0 +1,10 @@ + + + diff --git a/integration/templates/vue-vite/src/views/SessionState.vue b/integration/templates/vue-vite/src/views/SessionState.vue new file mode 100644 index 00000000000..77c682dd52f --- /dev/null +++ b/integration/templates/vue-vite/src/views/SessionState.vue @@ -0,0 +1,15 @@ + + + diff --git a/integration/templates/vue-vite/src/views/ShowComponent.vue b/integration/templates/vue-vite/src/views/ShowComponent.vue new file mode 100644 index 00000000000..a769f107a18 --- /dev/null +++ b/integration/templates/vue-vite/src/views/ShowComponent.vue @@ -0,0 +1,34 @@ + + + diff --git a/integration/templates/vue-vite/src/views/SignOutPage.vue b/integration/templates/vue-vite/src/views/SignOutPage.vue new file mode 100644 index 00000000000..bd9fc2c5c58 --- /dev/null +++ b/integration/templates/vue-vite/src/views/SignOutPage.vue @@ -0,0 +1,15 @@ + + + diff --git a/integration/templates/vue-vite/src/views/UserState.vue b/integration/templates/vue-vite/src/views/UserState.vue new file mode 100644 index 00000000000..cff3d62b506 --- /dev/null +++ b/integration/templates/vue-vite/src/views/UserState.vue @@ -0,0 +1,16 @@ + + + diff --git a/integration/tests/vue/composables.test.ts b/integration/tests/vue/composables.test.ts new file mode 100644 index 00000000000..3b1c46a8a64 --- /dev/null +++ b/integration/tests/vue/composables.test.ts @@ -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'); + }); +}); diff --git a/integration/tests/vue/organizations.test.ts b/integration/tests/vue/organizations.test.ts new file mode 100644 index 00000000000..838d803c4f7 --- /dev/null +++ b/integration/tests/vue/organizations.test.ts @@ -0,0 +1,74 @@ +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] })('organization 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(' renders and shows organizations', 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('/org-list'); + await u.page.waitForClerkComponentMounted(); + await expect(u.page.getByText(fakeOrganization.name)).toBeVisible(); + }); + + test(' renders and can create an org', 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('/create-org'); + await u.page.waitForClerkComponentMounted(); + await expect(u.page.getByText(/Create organization/i)).toBeVisible(); + }); + + test(' allows switching organizations', 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.po.organizationSwitcher.waitForMounted(); + await u.po.organizationSwitcher.waitForAnOrganizationToSelected(); + + // Open the switcher + await u.po.organizationSwitcher.toggleTrigger(); + await u.page.waitForSelector('.cl-organizationSwitcherPopoverCard', { state: 'visible' }); + + // Verify the org name is visible in the popover + await expect(u.page.locator('.cl-organizationSwitcherPopoverCard').getByText(fakeOrganization.name)).toBeVisible(); + }); +}); diff --git a/integration/tests/vue/show-component.test.ts b/integration/tests/vue/show-component.test.ts new file mode 100644 index 00000000000..db1a0511916 --- /dev/null +++ b/integration/tests/vue/show-component.test.ts @@ -0,0 +1,99 @@ +import type { OrganizationMembershipRole } from '@clerk/backend'; +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] })('Show component tests for @vue', ({ app }) => { + test.describe.configure({ mode: 'parallel' }); + + let fakeUser: FakeUser; + let fakeOrganization: FakeOrganization; + let memberUser: FakeUser; + + 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); + + // Create a member user (not admin) for fallback tests + memberUser = u.services.users.createFakeUser(); + const bapiMember = await u.services.users.createBapiUser(memberUser); + await u.services.clerk.organizations.createOrganizationMembership({ + organizationId: fakeOrganization.organization.id, + role: 'org:member' as OrganizationMembershipRole, + userId: bapiMember.id, + }); + }); + + test.afterAll(async () => { + await fakeOrganization?.delete(); + await memberUser?.deleteIfExists(); + 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(' renders when not authenticated', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + await u.page.goToRelative('/show-component'); + await u.page.waitForClerkJsLoaded(); + + await expect(u.page.getByText('show-signed-out-content')).toBeVisible(); + await expect(u.page.getByText('show-signed-in-content')).not.toBeVisible(); + }); + + test(' renders when authenticated', 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('/show-component'); + await expect(u.page.getByText('show-signed-in-content')).toBeVisible(); + await expect(u.page.getByText('show-signed-out-content')).not.toBeVisible(); + }); + + test(' with permission condition renders for admin with manage permission', 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('/show-component'); + await expect(u.page.getByText('show-permission-content')).toBeVisible(); + await expect(u.page.getByText('show-permission-fallback')).not.toBeVisible(); + }); + + test(' with role condition renders fallback for non-admin', 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: memberUser.email, password: memberUser.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('/show-component'); + await expect(u.page.getByText('show-admin-fallback')).toBeVisible(); + await expect(u.page.getByText('show-admin-content')).not.toBeVisible(); + }); +}); diff --git a/integration/tests/vue/sign-out.test.ts b/integration/tests/vue/sign-out.test.ts new file mode 100644 index 00000000000..73fe2f33e59 --- /dev/null +++ b/integration/tests/vue/sign-out.test.ts @@ -0,0 +1,43 @@ +import { expect, test } from '@playwright/test'; + +import { appConfigs } from '../../presets'; +import type { FakeUser } from '../../testUtils'; +import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; + +testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('sign-out tests for @vue', ({ app }) => { + test.describe.configure({ mode: 'parallel' }); + + let fakeUser: FakeUser; + + test.beforeAll(async () => { + const u = createTestUtils({ app }); + fakeUser = u.services.users.createFakeUser(); + await u.services.users.createBapiUser(fakeUser); + }); + + test.afterAll(async () => { + 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(' 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.goToRelative('/sign-out'); + await expect(u.page.locator('[data-signed-in]')).toBeVisible(); + + await u.page.getByRole('button', { name: /Sign out/i }).click(); + await expect(u.page.locator('[data-signed-out]')).toBeVisible(); + await u.po.expect.toBeSignedOut(); + }); +}); From 56cf09556ac8508066349513dff087f401fd1c11 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Fri, 13 Mar 2026 12:21:54 -0400 Subject: [PATCH 2/3] chore: Better consistency --- .../src/views/CreateOrganizationPage.vue | 5 +-- .../src/views/OrganizationListPage.vue | 5 +-- integration/tests/vue/components.test.ts | 15 +++++++ integration/tests/vue/organizations.test.ts | 2 +- integration/tests/vue/sign-out.test.ts | 43 ------------------- 5 files changed, 18 insertions(+), 52 deletions(-) delete mode 100644 integration/tests/vue/sign-out.test.ts diff --git a/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue b/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue index cd855f7d040..491feb7bbdb 100644 --- a/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue +++ b/integration/templates/vue-vite/src/views/CreateOrganizationPage.vue @@ -3,8 +3,5 @@ import { CreateOrganization } from '@clerk/vue'; diff --git a/integration/templates/vue-vite/src/views/OrganizationListPage.vue b/integration/templates/vue-vite/src/views/OrganizationListPage.vue index 73fa97fc2cd..58a4ede940b 100644 --- a/integration/templates/vue-vite/src/views/OrganizationListPage.vue +++ b/integration/templates/vue-vite/src/views/OrganizationListPage.vue @@ -3,8 +3,5 @@ import { OrganizationList } from '@clerk/vue'; diff --git a/integration/tests/vue/components.test.ts b/integration/tests/vue/components.test.ts index c5aa518a358..eadb194c3e0 100644 --- a/integration/tests/vue/components.test.ts +++ b/integration/tests/vue/components.test.ts @@ -252,6 +252,21 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('basic te await u.page.waitForAppUrl('/'); }); + test(' 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.goToRelative('/sign-out'); + await expect(u.page.locator('[data-signed-in]')).toBeVisible(); + + await u.page.getByRole('button', { name: /Sign out/i }).click(); + await expect(u.page.locator('[data-signed-out]')).toBeVisible(); + 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'); diff --git a/integration/tests/vue/organizations.test.ts b/integration/tests/vue/organizations.test.ts index 838d803c4f7..3544f74be85 100644 --- a/integration/tests/vue/organizations.test.ts +++ b/integration/tests/vue/organizations.test.ts @@ -41,7 +41,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('organiza await expect(u.page.getByText(fakeOrganization.name)).toBeVisible(); }); - test(' renders and can create an org', async ({ page, context }) => { + test(' renders', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); await u.page.goToRelative('/sign-in'); await u.po.signIn.waitForMounted(); diff --git a/integration/tests/vue/sign-out.test.ts b/integration/tests/vue/sign-out.test.ts deleted file mode 100644 index 73fe2f33e59..00000000000 --- a/integration/tests/vue/sign-out.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { expect, test } from '@playwright/test'; - -import { appConfigs } from '../../presets'; -import type { FakeUser } from '../../testUtils'; -import { createTestUtils, testAgainstRunningApps } from '../../testUtils'; - -testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('sign-out tests for @vue', ({ app }) => { - test.describe.configure({ mode: 'parallel' }); - - let fakeUser: FakeUser; - - test.beforeAll(async () => { - const u = createTestUtils({ app }); - fakeUser = u.services.users.createFakeUser(); - await u.services.users.createBapiUser(fakeUser); - }); - - test.afterAll(async () => { - 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(' 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.goToRelative('/sign-out'); - await expect(u.page.locator('[data-signed-in]')).toBeVisible(); - - await u.page.getByRole('button', { name: /Sign out/i }).click(); - await expect(u.page.locator('[data-signed-out]')).toBeVisible(); - await u.po.expect.toBeSignedOut(); - }); -}); From a974b3017769249ddb9a10285982ee07b3ebde77 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Fri, 13 Mar 2026 16:33:03 -0400 Subject: [PATCH 3/3] chore: Test tweaks --- integration/tests/vue/components.test.ts | 6 +++--- integration/tests/vue/organizations.test.ts | 2 +- integration/tests/vue/show-component.test.ts | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/integration/tests/vue/components.test.ts b/integration/tests/vue/components.test.ts index eadb194c3e0..c7966a53d34 100644 --- a/integration/tests/vue/components.test.ts +++ b/integration/tests/vue/components.test.ts @@ -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(); }); @@ -258,12 +258,12 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('basic te 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 expect(u.page.locator('[data-signed-out]')).toBeVisible(); await u.po.expect.toBeSignedOut(); }); diff --git a/integration/tests/vue/organizations.test.ts b/integration/tests/vue/organizations.test.ts index 3544f74be85..8405259f7ef 100644 --- a/integration/tests/vue/organizations.test.ts +++ b/integration/tests/vue/organizations.test.ts @@ -50,7 +50,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('organiza await u.page.goToRelative('/create-org'); await u.page.waitForClerkComponentMounted(); - await expect(u.page.getByText(/Create organization/i)).toBeVisible(); + await expect(u.page.getByRole('heading', { name: /Create organization/i })).toBeVisible(); }); test(' allows switching organizations', async ({ page, context }) => { diff --git a/integration/tests/vue/show-component.test.ts b/integration/tests/vue/show-component.test.ts index db1a0511916..7cacfd3c859 100644 --- a/integration/tests/vue/show-component.test.ts +++ b/integration/tests/vue/show-component.test.ts @@ -23,7 +23,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('Show com const bapiMember = await u.services.users.createBapiUser(memberUser); await u.services.clerk.organizations.createOrganizationMembership({ organizationId: fakeOrganization.organization.id, - role: 'org:member' as OrganizationMembershipRole, + role: 'org:viewer' as OrganizationMembershipRole, userId: bapiMember.id, }); }); @@ -47,7 +47,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('Show com await u.page.waitForClerkJsLoaded(); await expect(u.page.getByText('show-signed-out-content')).toBeVisible(); - await expect(u.page.getByText('show-signed-in-content')).not.toBeVisible(); + await expect(u.page.getByText('show-signed-in-content')).toBeHidden(); }); test(' renders when authenticated', async ({ page, context }) => { @@ -59,7 +59,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('Show com await u.page.goToRelative('/show-component'); await expect(u.page.getByText('show-signed-in-content')).toBeVisible(); - await expect(u.page.getByText('show-signed-out-content')).not.toBeVisible(); + await expect(u.page.getByText('show-signed-out-content')).toBeHidden(); }); test(' with permission condition renders for admin with manage permission', async ({ page, context }) => { @@ -76,7 +76,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('Show com await u.page.goToRelative('/show-component'); await expect(u.page.getByText('show-permission-content')).toBeVisible(); - await expect(u.page.getByText('show-permission-fallback')).not.toBeVisible(); + await expect(u.page.getByText('show-permission-fallback')).toBeHidden(); }); test(' with role condition renders fallback for non-admin', async ({ page, context }) => { @@ -94,6 +94,6 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withCustomRoles] })('Show com await u.page.goToRelative('/show-component'); await expect(u.page.getByText('show-admin-fallback')).toBeVisible(); - await expect(u.page.getByText('show-admin-content')).not.toBeVisible(); + await expect(u.page.getByText('show-admin-content')).toBeHidden(); }); });