diff --git a/packages/editor-ui/src/__tests__/server/endpoints/versionControl.ts b/packages/editor-ui/src/__tests__/server/endpoints/versionControl.ts index 09a079d869..b3c6c60ac8 100644 --- a/packages/editor-ui/src/__tests__/server/endpoints/versionControl.ts +++ b/packages/editor-ui/src/__tests__/server/endpoints/versionControl.ts @@ -19,7 +19,7 @@ export function routesForVersionControl(server: Server) { }; server.post(`${versionControlApiRoot}/preferences`, (schema: AppSchema, request: Request) => { - const requestBody = jsonParse(request.requestBody) as Partial; + const requestBody: Partial = jsonParse(request.requestBody); return new Response( 200, @@ -34,7 +34,7 @@ export function routesForVersionControl(server: Server) { }); server.patch(`${versionControlApiRoot}/preferences`, (schema: AppSchema, request: Request) => { - const requestBody = jsonParse(request.requestBody) as Partial; + const requestBody: Partial = jsonParse(request.requestBody); return new Response( 200, diff --git a/packages/editor-ui/src/components/MainSidebarVersionControl.vue b/packages/editor-ui/src/components/MainSidebarVersionControl.vue index 86e47b9607..937e47a28c 100644 --- a/packages/editor-ui/src/components/MainSidebarVersionControl.vue +++ b/packages/editor-ui/src/components/MainSidebarVersionControl.vue @@ -51,20 +51,26 @@ async function pullWorkfolder() { try { await versionControlStore.pullWorkfolder(false); } catch (error) { - const confirm = await message.confirm( - i18n.baseText('settings.versionControl.modals.pull.description'), - i18n.baseText('settings.versionControl.modals.pull.title'), - { - confirmButtonText: i18n.baseText('settings.versionControl.modals.pull.buttons.save'), - cancelButtonText: i18n.baseText('settings.versionControl.modals.pull.buttons.cancel'), - }, - ); + const errorResponse = error.response; - try { - if (confirm === 'confirm') { - await versionControlStore.pullWorkfolder(true); + if (errorResponse?.status === 409) { + const confirm = await message.confirm( + i18n.baseText('settings.versionControl.modals.pull.description'), + i18n.baseText('settings.versionControl.modals.pull.title'), + { + confirmButtonText: i18n.baseText('settings.versionControl.modals.pull.buttons.save'), + cancelButtonText: i18n.baseText('settings.versionControl.modals.pull.buttons.cancel'), + }, + ); + + try { + if (confirm === 'confirm') { + await versionControlStore.pullWorkfolder(true); + } + } catch (error) { + toast.showError(error, 'Error'); } - } catch (error) { + } else { toast.showError(error, 'Error'); } } finally { diff --git a/packages/editor-ui/src/components/__tests__/MainSidebarVersionControl.test.ts b/packages/editor-ui/src/components/__tests__/MainSidebarVersionControl.test.ts index 95f06ed1dd..af2ec02768 100644 --- a/packages/editor-ui/src/components/__tests__/MainSidebarVersionControl.test.ts +++ b/packages/editor-ui/src/components/__tests__/MainSidebarVersionControl.test.ts @@ -1,13 +1,14 @@ import { describe, it, expect, vi } from 'vitest'; -import { render } from '@testing-library/vue'; +import { render, waitFor } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; import { PiniaVuePlugin } from 'pinia'; import { createTestingPinia } from '@pinia/testing'; +import { merge } from 'lodash-es'; import { STORES } from '@/constants'; import { i18nInstance } from '@/plugins/i18n'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; import MainSidebarVersionControl from '@/components/MainSidebarVersionControl.vue'; import { useUsersStore, useVersionControlStore } from '@/stores'; -import { merge } from 'lodash-es'; let pinia: ReturnType; let versionControlStore: ReturnType; @@ -65,21 +66,45 @@ describe('MainSidebarVersionControl', () => { expect(queryByTestId('main-sidebar-version-control-connected')).not.toBeInTheDocument(); }); - it('should render connected content', async () => { - vi.spyOn(versionControlStore, 'preferences', 'get').mockReturnValue({ - branchName: 'main', - branches: [], - authorName: '', - authorEmail: '', - repositoryUrl: '', - branchReadOnly: false, - branchColor: '#F4A6DC', - connected: true, - publicKey: '', + describe('when connected', () => { + beforeEach(() => { + vi.spyOn(versionControlStore, 'preferences', 'get').mockReturnValue({ + branchName: 'main', + branches: [], + authorName: '', + authorEmail: '', + repositoryUrl: '', + branchReadOnly: false, + branchColor: '#F4A6DC', + connected: true, + publicKey: '', + }); }); - const { getByTestId, queryByTestId } = renderComponent({ props: { isCollapsed: false } }); - expect(getByTestId('main-sidebar-version-control-connected')).toBeInTheDocument(); - expect(queryByTestId('main-sidebar-version-control-setup')).not.toBeInTheDocument(); + it('should render the appropriate content', async () => { + const { getByTestId, queryByTestId } = renderComponent({ props: { isCollapsed: false } }); + expect(getByTestId('main-sidebar-version-control-connected')).toBeInTheDocument(); + expect(queryByTestId('main-sidebar-version-control-setup')).not.toBeInTheDocument(); + }); + + it('should show toast error if pull response http status code is not 409', async () => { + vi.spyOn(versionControlStore, 'pullWorkfolder').mockRejectedValueOnce({ + response: { status: 400 }, + }); + const { getAllByRole, getByRole } = renderComponent({ props: { isCollapsed: false } }); + + await userEvent.click(getAllByRole('button')[0]); + await waitFor(() => expect(getByRole('alert')).toBeInTheDocument()); + }); + + it('should show confirm if pull response http status code is 409', async () => { + vi.spyOn(versionControlStore, 'pullWorkfolder').mockRejectedValueOnce({ + response: { status: 409 }, + }); + const { getAllByRole, getByRole } = renderComponent({ props: { isCollapsed: false } }); + + await userEvent.click(getAllByRole('button')[0]); + await waitFor(() => expect(getByRole('dialog')).toBeInTheDocument()); + }); }); }); diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index cb0bd33d60..fffc363e83 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1374,7 +1374,7 @@ "settings.versionControl.modals.push.description.workflow": "Since you are currently editing a Workflow, the modified workflow file has been pre-selected for you.", "settings.versionControl.modals.push.description.credentials": "Since you are on the Credentials page, the modified credential files have been pre-selected for you.", "settings.versionControl.modals.push.description.learnMore": "Learn more", - "settings.versionControl.modals.push.description.learnMore.url": "https://n8n.io/docs", + "settings.versionControl.modals.push.description.learnMore.url": "https://docs.n8n.io/environments/version-control/using/", "settings.versionControl.modals.push.filesToCommit": "Files to commit", "settings.versionControl.modals.push.everythingIsUpToDate": "Everything is up to date", "settings.versionControl.modals.push.commitMessage": "Commit message", @@ -1407,6 +1407,9 @@ "settings.versionControl.refreshBranches.tooltip": "Reload branches list", "settings.versionControl.refreshBranches.success": "Branches successfully refreshed", "settings.versionControl.refreshBranches.error": "Error refreshing branches", + "settings.versionControl.docs.url": "https://docs.n8n.io/environments/version-control/", + "settings.versionControl.docs.setup.url": "https://docs.n8n.io/environments/version-control/setup/", + "settings.versionControl.docs.using.url": "https://docs.n8n.io/environments/version-control/using/", "showMessage.cancel": "@:_reusableBaseText.cancel", "settings.auditLogs.title": "Audit Logs", "settings.auditLogs.actionBox.title": "Available on Enterprise plan", diff --git a/packages/editor-ui/src/views/SettingsVersionControl.vue b/packages/editor-ui/src/views/SettingsVersionControl.vue index e996277725..877dfd24b1 100644 --- a/packages/editor-ui/src/views/SettingsVersionControl.vue +++ b/packages/editor-ui/src/views/SettingsVersionControl.vue @@ -13,8 +13,9 @@ const toast = useToast(); const message = useMessage(); const loadingService = useLoadingService(); -const versionControlDocsUrl = ref('https://docs.n8n.io/environments/version-control/'); -const versionControlDocsSetupUrl = computed(() => versionControlDocsUrl.value + 'setup/'); +const versionControlDocsSetupUrl = computed(() => + locale.baseText('settings.versionControl.docs.setup.url'), +); const isConnected = ref(false); const onConnect = async () => {