diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.spec.ts b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.spec.ts index 4eb0a829d9..24d10c9a0e 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.spec.ts +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.spec.ts @@ -1,30 +1,22 @@ import WorkflowDetails from '@/components/MainHeader/WorkflowDetails.vue'; import { createComponentRenderer } from '@/__tests__/render'; -import { STORES, WORKFLOW_SHARE_MODAL_KEY } from '@/constants'; +import { EnterpriseEditionFeature, STORES, WORKFLOW_SHARE_MODAL_KEY } from '@/constants'; import { createTestingPinia } from '@pinia/testing'; import userEvent from '@testing-library/user-event'; import { useUIStore } from '@/stores/ui.store'; -vi.mock('vue-router', async () => { - const actual = await import('vue-router'); - - return { - ...actual, - useRoute: () => ({ - value: { - params: { - id: '1', - }, - }, - }), - }; -}); +vi.mock('vue-router', () => ({ + useRoute: () => vi.fn(), + useRouter: () => vi.fn(), + RouterLink: vi.fn(), +})); const initialState = { [STORES.SETTINGS]: { settings: { enterprise: { - sharing: true, + [EnterpriseEditionFeature.Sharing]: true, + [EnterpriseEditionFeature.WorkflowHistory]: true, }, }, areTagsEnabled: true, @@ -45,21 +37,26 @@ const initialState = { const renderComponent = createComponentRenderer(WorkflowDetails, { pinia: createTestingPinia({ initialState }), + global: { + stubs: { + RouterLink: true, + }, + }, }); let uiStore: ReturnType; +const workflow = { + id: '1', + name: 'Test Workflow', + tags: ['1', '2'], + active: false, +}; describe('WorkflowDetails', () => { beforeEach(() => { uiStore = useUIStore(); }); it('renders workflow name and tags', async () => { - const workflow = { - id: '1', - name: 'Test Workflow', - tags: ['1', '2'], - }; - const { getByTestId, getByText } = renderComponent({ props: { workflow, @@ -79,11 +76,7 @@ describe('WorkflowDetails', () => { const onSaveButtonClick = vi.fn(); const { getByTestId } = renderComponent({ props: { - workflow: { - id: '1', - name: 'Test Workflow', - tags: [], - }, + workflow, readOnly: false, }, global: { @@ -102,11 +95,7 @@ describe('WorkflowDetails', () => { const { getByTestId } = renderComponent({ props: { - workflow: { - id: '1', - name: 'Test Workflow', - tags: [], - }, + workflow, readOnly: false, }, }); diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index 12c9cc33a1..dd9a795d4a 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -21,6 +21,7 @@ import SaveButton from '@/components/SaveButton.vue'; import TagsDropdown from '@/components/TagsDropdown.vue'; import InlineTextEdit from '@/components/InlineTextEdit.vue'; import BreakpointsObserver from '@/components/BreakpointsObserver.vue'; +import WorkflowHistoryButton from '@/components/MainHeader/WorkflowHistoryButton.vue'; import { useRootStore } from '@/stores/root.store'; import { useSettingsStore } from '@/stores/settings.store'; @@ -216,19 +217,6 @@ const isWorkflowHistoryFeatureEnabled = computed(() => { return settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.WorkflowHistory]; }); -const workflowHistoryRoute = computed<{ name: string; params: { workflowId: string } }>(() => { - return { - name: VIEWS.WORKFLOW_HISTORY, - params: { - workflowId: props.workflow.id, - }, - }; -}); - -const isWorkflowHistoryButtonDisabled = computed(() => { - return isNewWorkflow.value; -}); - const workflowTagIds = computed(() => { return (props.workflow.tags ?? []).map((tag) => (typeof tag === 'string' ? tag : tag.id)); }); @@ -588,6 +576,10 @@ function goToUpgrade() { void uiStore.goToUpgrade('workflow_sharing', 'upgrade-workflow-sharing'); } +function goToWorkflowHistoryUpgrade() { + void uiStore.goToUpgrade('workflow-history', 'upgrade-workflow-history'); +} + function showCreateWorkflowSuccessToast(id?: string) { if (!id || ['new', PLACEHOLDER_EMPTY_WORKFLOW_ID].includes(id)) { let toastTitle = locale.baseText('workflows.create.personal.toast.title'); @@ -732,20 +724,12 @@ function showCreateWorkflowSuccessToast(id?: string) { data-test-id="workflow-save-button" @click="onSaveButtonClick" /> - - - +
diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowHistoryButton.test.ts b/packages/editor-ui/src/components/MainHeader/WorkflowHistoryButton.test.ts new file mode 100644 index 0000000000..ed26a5afaf --- /dev/null +++ b/packages/editor-ui/src/components/MainHeader/WorkflowHistoryButton.test.ts @@ -0,0 +1,63 @@ +import { createComponentRenderer } from '@/__tests__/render'; +import { within } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; +import WorkflowHistoryButton from '@/components/MainHeader/WorkflowHistoryButton.vue'; + +vi.mock('vue-router', () => ({ + useRoute: () => vi.fn(), + useRouter: () => vi.fn(), + RouterLink: vi.fn(), +})); + +const renderComponent = createComponentRenderer(WorkflowHistoryButton, { + global: { + stubs: { + RouterLink: true, + 'router-link': { + template: '
', + }, + }, + }, +}); + +describe('WorkflowHistoryButton', () => { + it('should be disabled if the feature is disabled', async () => { + const { getByRole, emitted } = renderComponent({ + props: { + workflowId: '1', + isNewWorkflow: false, + isFeatureEnabled: false, + }, + }); + expect(getByRole('button')).toBeDisabled(); + + await userEvent.hover(getByRole('button')); + expect(getByRole('tooltip')).toBeVisible(); + + within(getByRole('tooltip')).getByText('View plans').click(); + + expect(emitted()).toHaveProperty('upgrade'); + }); + + it('should be disabled if the feature is enabled but the workflow is new', async () => { + const { getByRole } = renderComponent({ + props: { + workflowId: '1', + isNewWorkflow: true, + isFeatureEnabled: true, + }, + }); + expect(getByRole('button')).toBeDisabled(); + }); + + it('should be enabled if the feature is enabled and the workflow is not new', async () => { + const { getByRole } = renderComponent({ + props: { + workflowId: '1', + isNewWorkflow: false, + isFeatureEnabled: true, + }, + }); + expect(getByRole('button')).toBeEnabled(); + }); +}); diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowHistoryButton.vue b/packages/editor-ui/src/components/MainHeader/WorkflowHistoryButton.vue new file mode 100644 index 0000000000..afa554e241 --- /dev/null +++ b/packages/editor-ui/src/components/MainHeader/WorkflowHistoryButton.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 7f94aec41c..551f99beae 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -2166,6 +2166,10 @@ "workflowHistory.action.restore.success.title": "Successfully restored workflow version", "workflowHistory.action.clone.success.title": "Successfully cloned workflow version", "workflowHistory.action.clone.success.message": "Open cloned workflow in a new tab", + "workflowHistory.button.tooltip.empty": "This workflow currently has no history to view. Once you've made your first save, you'll be able to view previous versions", + "workflowHistory.button.tooltip.enabled": "Workflow history to view and restore previous versions of your workflows", + "workflowHistory.button.tooltip.disabled": "Upgrade to unlock workflow history to view and restore previous versions of your workflows. {link}", + "workflowHistory.button.tooltip.disabled.link": "View plans", "workflows.heading": "Workflows", "workflows.add": "Add workflow", "workflows.project.add": "Add workflow to project",