diff --git a/.changeset/breezy-stingrays-give.md b/.changeset/breezy-stingrays-give.md new file mode 100644 index 00000000000..8e5c8b5f62d --- /dev/null +++ b/.changeset/breezy-stingrays-give.md @@ -0,0 +1,5 @@ +--- +"@clerk/vue": patch +--- + +Fixed an issue where `useOrganization` would show an incorrect warning and dialog due to checking environment settings before Clerk was fully loaded. diff --git a/packages/vue/src/composables/__tests__/useOrganization.test.ts b/packages/vue/src/composables/__tests__/useOrganization.test.ts new file mode 100644 index 00000000000..4f40c917e16 --- /dev/null +++ b/packages/vue/src/composables/__tests__/useOrganization.test.ts @@ -0,0 +1,70 @@ +import type { Clerk } from '@clerk/shared/types'; +import { render } from '@testing-library/vue'; +import { vi } from 'vitest'; +import { defineComponent, shallowRef } from 'vue'; + +import { useOrganization } from '../useOrganization'; + +const mockAttemptToEnableEnvironmentSetting = vi.fn(); + +const mockLoaded = shallowRef(false); +const mockClerk = shallowRef | null>(null); + +vi.mock('../useClerkContext', () => ({ + useClerkContext: () => ({ + loaded: mockLoaded, + clerk: mockClerk, + organizationCtx: shallowRef(undefined), + }), +})); + +vi.mock('../useSession', () => ({ + useSession: () => ({ + session: shallowRef(null), + }), +})); + +describe('useOrganization', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockLoaded.value = false; + mockClerk.value = null; + }); + + it('should not call __internal_attemptToEnableEnvironmentSetting before Clerk is loaded', () => { + mockClerk.value = { + __internal_attemptToEnableEnvironmentSetting: mockAttemptToEnableEnvironmentSetting, + }; + + const Component = defineComponent(() => { + useOrganization(); + return () => null; + }); + + render(Component); + + expect(mockAttemptToEnableEnvironmentSetting).not.toHaveBeenCalled(); + }); + + it('should call __internal_attemptToEnableEnvironmentSetting after Clerk is loaded', async () => { + mockClerk.value = { + __internal_attemptToEnableEnvironmentSetting: mockAttemptToEnableEnvironmentSetting, + }; + + const Component = defineComponent(() => { + useOrganization(); + return () => null; + }); + + render(Component); + + mockLoaded.value = true; + + await vi.waitFor(() => { + expect(mockAttemptToEnableEnvironmentSetting).toHaveBeenCalledWith({ + for: 'organizations', + caller: 'useOrganization', + }); + }); + }); +}); diff --git a/packages/vue/src/composables/useOrganization.ts b/packages/vue/src/composables/useOrganization.ts index 5545b284e9c..1d53da1b5b4 100644 --- a/packages/vue/src/composables/useOrganization.ts +++ b/packages/vue/src/composables/useOrganization.ts @@ -53,21 +53,23 @@ type UseOrganization = () => ToComputedRefs; * */ export const useOrganization: UseOrganization = () => { - const { clerk, organizationCtx } = useClerkContext('useOrganization'); + const { loaded, clerk, organizationCtx } = useClerkContext('useOrganization'); const { session } = useSession(); watch( - clerk, + loaded, value => { if (value) { // Optional chaining is important for `@clerk/vue` usage with older clerk-js versions that don't have the method - value.__internal_attemptToEnableEnvironmentSetting?.({ + clerk.value?.__internal_attemptToEnableEnvironmentSetting?.({ for: 'organizations', caller: 'useOrganization', }); } }, - { once: true }, + { + once: true, + }, ); const result = computed(() => { @@ -80,7 +82,7 @@ export const useOrganization: UseOrganization = () => { } /** In SSR context we include only the organization object when loadOrg is set to true. */ - if (!clerk.value?.loaded) { + if (!loaded.value) { return { isLoaded: true, organization: organizationCtx.value, @@ -89,7 +91,7 @@ export const useOrganization: UseOrganization = () => { } return { - isLoaded: clerk.value.loaded, + isLoaded: loaded.value, organization: organizationCtx.value, membership: getCurrentOrganizationMembership( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/vue/src/plugin.ts b/packages/vue/src/plugin.ts index 04573bbaa45..0aba60a8d0c 100644 --- a/packages/vue/src/plugin.ts +++ b/packages/vue/src/plugin.ts @@ -167,6 +167,7 @@ export const clerkPlugin: Plugin<[PluginOptions]> = { const organizationCtx = computed(() => derivedState.value.organization); app.provide(ClerkInjectionKey, { + loaded, clerk, authCtx, clientCtx, diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index e2670df88d9..f396f5e4afc 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -17,6 +17,7 @@ import type { import type { Component, ComputedRef, ShallowRef, Slot, VNode } from 'vue'; export interface VueClerkInjectionKeyType { + loaded: ShallowRef; clerk: ShallowRef; authCtx: ComputedRef<{ userId: string | null | undefined;