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 {