diff --git a/packages/cli/src/controllers/e2e.controller.ts b/packages/cli/src/controllers/e2e.controller.ts index ea9364e854..3954bb1caf 100644 --- a/packages/cli/src/controllers/e2e.controller.ts +++ b/packages/cli/src/controllers/e2e.controller.ts @@ -114,6 +114,8 @@ export class E2EController { // eslint-disable-next-line @typescript-eslint/unbound-method license.getFeatureValue = (feature: NumericLicenseFeature) => this.numericFeatures[feature] ?? UNLIMITED_LICENSE_QUOTA; + + license.getPlanName = () => 'Enterprise'; } @Post('/reset', { skipAuth: true }) diff --git a/packages/editor-ui/src/components/CredentialCard.test.ts b/packages/editor-ui/src/components/CredentialCard.test.ts index d7309159ac..06760c75da 100644 --- a/packages/editor-ui/src/components/CredentialCard.test.ts +++ b/packages/editor-ui/src/components/CredentialCard.test.ts @@ -1,9 +1,12 @@ import { setActivePinia } from 'pinia'; +import { within } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; import { createTestingPinia } from '@pinia/testing'; import { createComponentRenderer } from '@/__tests__/render'; import CredentialCard from '@/components/CredentialCard.vue'; import type { ICredentialsResponse } from '@/Interface'; import type { ProjectSharingData } from '@/types/projects.types'; +import { useSettingsStore } from '@/stores/settings.store'; const renderComponent = createComponentRenderer(CredentialCard); @@ -19,9 +22,12 @@ const createCredential = (overrides = {}): ICredentialsResponse => ({ }); describe('CredentialCard', () => { + let settingsStore: ReturnType; + beforeEach(() => { const pinia = createTestingPinia(); setActivePinia(pinia); + settingsStore = useSettingsStore(); }); it('should render name and home project name', () => { @@ -55,4 +61,28 @@ describe('CredentialCard', () => { expect(heading).toHaveTextContent(data.name); expect(badge).toHaveTextContent('John Doe'); }); + + it('should show Move action only if there is resource permission and not on community plan', async () => { + vi.spyOn(settingsStore, 'isCommunityPlan', 'get').mockReturnValue(false); + + const data = createCredential({ + scopes: ['credential:move'], + }); + const { getByTestId } = renderComponent({ props: { data } }); + const cardActions = getByTestId('credential-card-actions'); + + expect(cardActions).toBeInTheDocument(); + + const cardActionsOpener = within(cardActions).getByRole('button'); + expect(cardActionsOpener).toBeInTheDocument(); + + const controllingId = cardActionsOpener.getAttribute('aria-controls'); + + await userEvent.click(cardActions); + const actions = document.querySelector(`#${controllingId}`); + if (!actions) { + throw new Error('Actions menu not found'); + } + expect(actions).toHaveTextContent('Move'); + }); }); diff --git a/packages/editor-ui/src/components/CredentialCard.vue b/packages/editor-ui/src/components/CredentialCard.vue index 31ad9814b3..f36926a19b 100644 --- a/packages/editor-ui/src/components/CredentialCard.vue +++ b/packages/editor-ui/src/components/CredentialCard.vue @@ -14,6 +14,7 @@ import { useProjectsStore } from '@/stores/projects.store'; import ProjectCardBadge from '@/components/Projects/ProjectCardBadge.vue'; import { useI18n } from '@/composables/useI18n'; import { ResourceType } from '@/utils/projects.utils'; +import { useSettingsStore } from '@/stores/settings.store'; const CREDENTIAL_LIST_ITEM_ACTIONS = { OPEN: 'open', @@ -45,6 +46,7 @@ const message = useMessage(); const uiStore = useUIStore(); const credentialsStore = useCredentialsStore(); const projectsStore = useProjectsStore(); +const settingsStore = useSettingsStore(); const resourceTypeLabel = computed(() => locale.baseText('generic.credential').toLowerCase()); const credentialType = computed(() => credentialsStore.getCredentialTypeByName(props.data.type)); @@ -64,7 +66,7 @@ const actions = computed(() => { }); } - if (credentialPermissions.value.move) { + if (credentialPermissions.value.move && !settingsStore.isCommunityPlan) { items.push({ label: locale.baseText('credentials.item.move'), value: CREDENTIAL_LIST_ITEM_ACTIONS.MOVE, diff --git a/packages/editor-ui/src/components/WorkflowCard.test.ts b/packages/editor-ui/src/components/WorkflowCard.test.ts index a58167c3b9..97be0e7fd4 100644 --- a/packages/editor-ui/src/components/WorkflowCard.test.ts +++ b/packages/editor-ui/src/components/WorkflowCard.test.ts @@ -7,6 +7,7 @@ import { VIEWS } from '@/constants'; import WorkflowCard from '@/components/WorkflowCard.vue'; import type { IWorkflowDb } from '@/Interface'; import { useRouter } from 'vue-router'; +import { useSettingsStore } from '@/stores/settings.store'; vi.mock('vue-router', () => { const push = vi.fn(); @@ -39,12 +40,14 @@ describe('WorkflowCard', () => { let pinia: ReturnType; let windowOpenSpy: MockInstance; let router: ReturnType; + let settingsStore: ReturnType; beforeEach(async () => { pinia = createPinia(); setActivePinia(pinia); router = useRouter(); - windowOpenSpy = vi.spyOn(window, 'open'); + settingsStore = useSettingsStore(); + windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null); }); afterEach(() => { @@ -95,10 +98,11 @@ describe('WorkflowCard', () => { }); const actions = document.querySelector(`#${controllingId}`); - await waitFor(() => { - expect(actions).toBeInTheDocument(); - }); - await userEvent.click(actions!.querySelectorAll('li')[0]); + if (!actions) { + throw new Error('Actions menu not found'); + } + await userEvent.click(actions.querySelectorAll('li')[0]); + expect(actions).not.toHaveTextContent('Move'); await waitFor(() => { expect(router.push).toHaveBeenCalledWith({ name: VIEWS.WORKFLOW, @@ -138,4 +142,28 @@ describe('WorkflowCard', () => { expect(heading).toHaveTextContent(data.name); expect(badge).toHaveTextContent('John Doe'); }); + + it('should show Move action only if there is resource permission and not on community plan', async () => { + vi.spyOn(settingsStore, 'isCommunityPlan', 'get').mockReturnValue(false); + + const data = createWorkflow({ + scopes: ['workflow:move'], + }); + const { getByTestId } = renderComponent({ props: { data } }); + const cardActions = getByTestId('workflow-card-actions'); + + expect(cardActions).toBeInTheDocument(); + + const cardActionsOpener = within(cardActions).getByRole('button'); + expect(cardActionsOpener).toBeInTheDocument(); + + const controllingId = cardActionsOpener.getAttribute('aria-controls'); + + await userEvent.click(cardActions); + const actions = document.querySelector(`#${controllingId}`); + if (!actions) { + throw new Error('Actions menu not found'); + } + expect(actions).toHaveTextContent('Move'); + }); }); diff --git a/packages/editor-ui/src/components/WorkflowCard.vue b/packages/editor-ui/src/components/WorkflowCard.vue index b6f7d3a6d7..21220e9492 100644 --- a/packages/editor-ui/src/components/WorkflowCard.vue +++ b/packages/editor-ui/src/components/WorkflowCard.vue @@ -95,7 +95,7 @@ const actions = computed(() => { }); } - if (workflowPermissions.value.move) { + if (workflowPermissions.value.move && !settingsStore.isCommunityPlan) { items.push({ label: locale.baseText('workflows.item.move'), value: WORKFLOW_LIST_ITEM_ACTIONS.MOVE, diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index 97ca75b1ee..89acc06c61 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -58,12 +58,11 @@ export function useDebugInfo() { : store.databaseType, executionMode: store.isQueueModeEnabled ? 'scaling' : 'regular', concurrency: store.settings.concurrency, - license: - store.planName === 'Community' - ? (store.planName.toLowerCase() as 'community') - : store.settings.license.environment === 'production' - ? 'enterprise (production)' - : 'enterprise (sandbox)', + license: store.isCommunityPlan + ? 'community' + : store.settings.license.environment === 'production' + ? 'enterprise (production)' + : 'enterprise (sandbox)', consumerId: store.consumerId, } as const; }; diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index c565c696fe..53bbab6a08 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -77,10 +77,13 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { return this.settings.databaseType; }, planName(): string { - return this.settings.license.planName ?? 'Community'; + return this.settings.license?.planName ?? 'Community'; + }, + isCommunityPlan(): boolean { + return this.planName.toLowerCase() === 'community'; }, consumerId(): string { - return this.settings.license.consumerId; + return this.settings.license?.consumerId ?? 'unknown'; }, binaryDataMode(): 'default' | 'filesystem' | 's3' { return this.settings.binaryDataMode;