diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue index 8461107ac32f3..4a1ee8fe46735 100644 --- a/packages/editor-ui/src/components/WorkflowSettings.vue +++ b/packages/editor-ui/src/components/WorkflowSettings.vue @@ -67,7 +67,7 @@ -
+
{{ $locale.baseText('workflowSettings.callerPolicy') + ':' }} @@ -114,6 +114,7 @@ type="text" v-model="workflowSettings.callerIds" @update:modelValue="onCallerIdsInput" + data-test-id="workflow-caller-policy-workflow-ids" /> @@ -374,13 +375,11 @@ import { import type { WorkflowSettings } from 'n8n-workflow'; import { deepCopy } from 'n8n-workflow'; -import { - useWorkflowsStore, - useSettingsStore, - useRootStore, - useWorkflowsEEStore, - useUsersStore, -} from '@/stores'; +import { useSettingsStore } from '@/stores/settings.store'; +import { useUsersStore } from '@/stores/users.store'; +import { useRootStore } from '@/stores/n8nRoot.store'; +import { useWorkflowsEEStore } from '@/stores/workflows.ee.store'; +import { useWorkflowsStore } from '@/stores/workflows.store'; import { createEventBus } from 'n8n-design-system/utils'; export default defineComponent({ @@ -566,9 +565,9 @@ export default defineComponent({ }, methods: { onCallerIdsInput(str: string) { - this.workflowSettings.callerIds = /^[0-9,\s]+$/.test(str) + this.workflowSettings.callerIds = /^[a-zA-Z0-9,\s]+$/.test(str) ? str - : str.replace(/[^0-9,\s]/g, ''); + : str.replace(/[^a-zA-Z0-9,\s]/g, ''); }, closeDialog() { this.modalBus.emit('close'); diff --git a/packages/editor-ui/src/components/__tests__/WorkflowSettings.spec.ts b/packages/editor-ui/src/components/__tests__/WorkflowSettings.spec.ts new file mode 100644 index 0000000000000..ca12ecbd7ddd0 --- /dev/null +++ b/packages/editor-ui/src/components/__tests__/WorkflowSettings.spec.ts @@ -0,0 +1,152 @@ +import { createPinia, setActivePinia } from 'pinia'; +import WorkflowSettingsVue from '../WorkflowSettings.vue'; + +import { setupServer } from '@/__tests__/server'; +import { afterAll, beforeAll } from 'vitest'; +import { within, fireEvent } from '@testing-library/vue'; + +import { useWorkflowsStore } from '@/stores/workflows.store'; +import { useSettingsStore } from '@/stores/settings.store'; +import { useUIStore } from '@/stores/ui.store'; + +import { createComponentRenderer } from '@/__tests__/render'; +import { EnterpriseEditionFeature, WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants'; + +import { nextTick } from 'vue'; + +let pinia: ReturnType; +let workflowsStore: ReturnType; +let settingsStore: ReturnType; +let uiStore: ReturnType; + +const createComponent = createComponentRenderer(WorkflowSettingsVue, { + global: { + stubs: ['n8n-tooltip'], + }, +}); + +describe('WorkflowSettingsVue', () => { + let server: ReturnType; + beforeAll(() => { + server = setupServer(); + }); + + beforeEach(async () => { + pinia = createPinia(); + setActivePinia(pinia); + + workflowsStore = useWorkflowsStore(); + settingsStore = useSettingsStore(); + uiStore = useUIStore(); + + await settingsStore.getSettings(); + + vi.spyOn(workflowsStore, 'workflowName', 'get').mockReturnValue('Test Workflow'); + vi.spyOn(workflowsStore, 'workflowId', 'get').mockReturnValue('1'); + vi.spyOn(workflowsStore, 'workflow', 'get').mockReturnValue({ + id: '1', + name: 'Test Workflow', + active: true, + nodes: [], + connections: {}, + createdAt: 1, + updatedAt: 1, + versionId: '123', + } as IWorkflowDb); + + uiStore.modals[WORKFLOW_SETTINGS_MODAL_KEY] = { + open: true, + }; + }); + + afterAll(() => { + server.shutdown(); + }); + + it('should render correctly', async () => { + settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = false; + const wrapper = createComponent({ pinia }); + await nextTick(); + expect(wrapper.getByTestId('workflow-settings-dialog')).toBeVisible(); + }); + + it('should not render workflow caller policy when sharing is not enabled', async () => { + settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = false; + const wrapper = createComponent({ pinia }); + + await nextTick(); + + expect( + within(wrapper.getByTestId('workflow-settings-dialog')).queryByTestId( + 'workflow-caller-policy', + ), + ).not.toBeInTheDocument(); + }); + + it('should render workflow caller policy when sharing is enabled', async () => { + settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = true; + const wrapper = createComponent({ pinia }); + + await nextTick(); + + expect(wrapper.getByTestId('workflow-caller-policy')).toBeVisible(); + }); + + it('should render list of workflows field when policy is set to workflowsFromAList', async () => { + settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = true; + const wrapper = createComponent({ pinia }); + + await nextTick(); + + await fireEvent.click(wrapper.getByTestId('workflow-caller-policy')); + console.log(window.document.querySelectorAll('.el-select-dropdown__item')[4].innerHTML); + await fireEvent.click(window.document.querySelectorAll('.el-select-dropdown__item')[4]); + + expect(wrapper.getByTestId('workflow-caller-policy-workflow-ids')).toBeVisible(); + }); + + it('should not remove valid workflow ID characters', async () => { + const validWorkflowList = '1234567890, abcde, efgh, 1234'; + + settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = true; + const wrapper = createComponent({ pinia }); + + await nextTick(); + + await fireEvent.click(wrapper.getByTestId('workflow-caller-policy')); + console.log(window.document.querySelectorAll('.el-select-dropdown__item')[4].innerHTML); + await fireEvent.click(window.document.querySelectorAll('.el-select-dropdown__item')[4]); + + await fireEvent.update( + wrapper.getByTestId('workflow-caller-policy-workflow-ids'), + validWorkflowList, + ); + + expect(wrapper.getByTestId('workflow-caller-policy-workflow-ids')).toHaveValue( + validWorkflowList, + ); + }); + + it('should remove invalid workflow ID characters', async () => { + const invalidWorkflowList = '1234567890@, abc/de, ef*gh, 12%34'; + const cleanedUpWorkflowList = '1234567890, abcde, efgh, 1234'; + + settingsStore.settings.enterprise[EnterpriseEditionFeature.Sharing] = true; + const wrapper = createComponent({ pinia }); + + await nextTick(); + + await fireEvent.click(wrapper.getByTestId('workflow-caller-policy')); + console.log(window.document.querySelectorAll('.el-select-dropdown__item')[4].innerHTML); + await fireEvent.click(window.document.querySelectorAll('.el-select-dropdown__item')[4]); + + await fireEvent.update( + wrapper.getByTestId('workflow-caller-policy-workflow-ids'), + invalidWorkflowList, + ); + + expect(wrapper.getByTestId('workflow-caller-policy-workflow-ids')).toHaveValue( + cleanedUpWorkflowList, + ); + }); +}); diff --git a/packages/editor-ui/src/composables/useExternalHooks.ts b/packages/editor-ui/src/composables/useExternalHooks.ts index e492de00c6950..bc37d97456f62 100644 --- a/packages/editor-ui/src/composables/useExternalHooks.ts +++ b/packages/editor-ui/src/composables/useExternalHooks.ts @@ -1,6 +1,6 @@ import type { IExternalHooks } from '@/Interface'; import type { IDataObject } from 'n8n-workflow'; -import { useWebhooksStore } from '@/stores'; +import { useWebhooksStore } from '@/stores/webhooks.store'; import { runExternalHook } from '@/utils'; export function useExternalHooks(): IExternalHooks {