From dfb0c4e77a169963cf2a0ffafc59ba7285b5eab3 Mon Sep 17 00:00:00 2001 From: Nikhil Kuriakose Date: Mon, 15 Sep 2025 17:04:58 +0200 Subject: [PATCH] fix: Disable update button when the user is not allowed to update (#19223) --- .../frontend/@n8n/i18n/src/locales/en.json | 1 + .../src/components/MainSidebar.test.ts | 59 +++ .../editor-ui/src/components/MainSidebar.vue | 7 +- .../src/components/VersionUpdateCTA.vue | 29 +- .../src/components/WhatsNewModal.test.ts | 63 ++- .../src/components/WhatsNewModal.vue | 33 +- .../__snapshots__/WhatsNewModal.test.ts.snap | 376 ++++++++++++++++++ .../editor-ui/src/stores/users.store.ts | 5 + 8 files changed, 554 insertions(+), 19 deletions(-) diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index d310fb3afe..a1caae7c79 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -3438,6 +3438,7 @@ "insights.upgradeModal.title": "Upgrade to Enterprise", "whatsNew.versionsBehind": "{count} version behind | {count} versions behind", "whatsNew.update": "Update", + "whatsNew.updateNudgeTooltip": "Only owners can perform updates", "whatsNew.updateAvailable": "You're currently on version {currentVersion}. Update to {latestVersion} to get {count} versions worth of new features, improvements, and fixes. See what changed", "whatsNew.updateAvailable.changelogLink": "in the full changelog", "workflowDiff.changes": "Changes", diff --git a/packages/frontend/editor-ui/src/components/MainSidebar.test.ts b/packages/frontend/editor-ui/src/components/MainSidebar.test.ts index a10ea3bd65..42c90e0a45 100644 --- a/packages/frontend/editor-ui/src/components/MainSidebar.test.ts +++ b/packages/frontend/editor-ui/src/components/MainSidebar.test.ts @@ -7,6 +7,9 @@ import MainSidebar from '@/components/MainSidebar.vue'; import { useSettingsStore } from '@/stores/settings.store'; import { useUIStore } from '@/stores/ui.store'; import { useSourceControlStore } from '@/stores/sourceControl.store'; +import { useVersionsStore } from '@/stores/versions.store'; +import { useUsersStore } from '@/stores/users.store'; +import type { Version } from '@n8n/rest-api-client/api/versions'; vi.mock('vue-router', () => ({ useRouter: () => ({}), @@ -18,6 +21,20 @@ let renderComponent: ReturnType; let settingsStore: MockedStore; let uiStore: MockedStore; let sourceControlStore: MockedStore; +let versionsStore: MockedStore; +let usersStore: MockedStore; + +const mockVersion: Version = { + name: '1.2.0', + nodes: [], + createdAt: '2025-01-01T00:00:00Z', + description: 'Test version', + documentationUrl: 'https://docs.n8n.io', + hasBreakingChange: false, + hasSecurityFix: false, + hasSecurityIssue: false, + securityIssueFixVersion: '', +}; describe('MainSidebar', () => { beforeEach(() => { @@ -27,8 +44,15 @@ describe('MainSidebar', () => { settingsStore = mockedStore(useSettingsStore); uiStore = mockedStore(useUIStore); sourceControlStore = mockedStore(useSourceControlStore); + versionsStore = mockedStore(useVersionsStore); + usersStore = mockedStore(useUsersStore); settingsStore.settings = defaultSettings; + + // Default store values + versionsStore.hasVersionUpdates = false; + versionsStore.nextVersions = []; + usersStore.canUserUpdateVersion = true; }); it('renders the sidebar without error', () => { @@ -51,4 +75,39 @@ describe('MainSidebar', () => { expect(queryByTestId('read-only-env-icon') !== null).toBe(shouldRender); }, ); + + describe('Version Update CTA', () => { + it('should not render version update CTA when hasVersionUpdates is false', () => { + versionsStore.hasVersionUpdates = false; + usersStore.canUserUpdateVersion = true; + + const { queryByTestId } = renderComponent(); + + expect(queryByTestId('version-update-cta-button')).not.toBeInTheDocument(); + }); + + it('should render version update CTA disabled when canUserUpdateVersion is false', () => { + versionsStore.hasVersionUpdates = true; + versionsStore.nextVersions = [mockVersion]; + usersStore.canUserUpdateVersion = false; + + const { getByTestId } = renderComponent(); + + const updateButton = getByTestId('version-update-cta-button'); + expect(updateButton).toBeInTheDocument(); + expect(updateButton).toBeDisabled(); + }); + + it('should render version update CTA enabled when canUserUpdateVersion is true and hasVersionUpdates is true', () => { + versionsStore.hasVersionUpdates = true; + versionsStore.nextVersions = [mockVersion]; + usersStore.canUserUpdateVersion = true; + + const { getByTestId } = renderComponent(); + + const updateButton = getByTestId('version-update-cta-button'); + expect(updateButton).toBeInTheDocument(); + expect(updateButton).toBeEnabled(); + }); + }); }); diff --git a/packages/frontend/editor-ui/src/components/MainSidebar.vue b/packages/frontend/editor-ui/src/components/MainSidebar.vue index 8e579a0787..1718a7d705 100644 --- a/packages/frontend/editor-ui/src/components/MainSidebar.vue +++ b/packages/frontend/editor-ui/src/components/MainSidebar.vue @@ -264,7 +264,12 @@ const mainMenuItems = computed(() => [ id: 'version-upgrade-cta', component: VersionUpdateCTA, available: versionsStore.hasVersionUpdates, - props: {}, + props: { + disabled: !usersStore.canUserUpdateVersion, + tooltipText: !usersStore.canUserUpdateVersion + ? i18n.baseText('whatsNew.updateNudgeTooltip') + : undefined, + }, }, ], }, diff --git a/packages/frontend/editor-ui/src/components/VersionUpdateCTA.vue b/packages/frontend/editor-ui/src/components/VersionUpdateCTA.vue index 1a7ccb301e..0717ad022e 100644 --- a/packages/frontend/editor-ui/src/components/VersionUpdateCTA.vue +++ b/packages/frontend/editor-ui/src/components/VersionUpdateCTA.vue @@ -1,12 +1,22 @@