mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat(editor): Show error on Execute Workflow Node if connected workflow is archived (no-changelog) (#15056)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { renderComponent } from '@/__tests__/render';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import ParameterInput from '@/components/ParameterInput.vue';
|
||||
import type { useNDVStore } from '@/stores/ndv.store';
|
||||
import type { CompletionResult } from '@codemirror/autocomplete';
|
||||
@@ -7,8 +7,12 @@ import { faker } from '@faker-js/faker';
|
||||
import { waitFor } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import type { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { cleanupAppModals, createAppModals } from '@/__tests__/utils';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { cleanupAppModals, createAppModals, mockedStore } from '@/__tests__/utils';
|
||||
import { createEventBus } from '@n8n/utils/event-bus';
|
||||
import { createMockEnterpriseSettings } from '@/__tests__/mocks';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import type { INodeParameterResourceLocator } from 'n8n-workflow';
|
||||
|
||||
let mockNdvState: Partial<ReturnType<typeof useNDVStore>>;
|
||||
let mockNodeTypesState: Partial<ReturnType<typeof useNodeTypesStore>>;
|
||||
@@ -58,12 +62,22 @@ vi.mock('vue-router', () => {
|
||||
return {
|
||||
useRouter: () => ({
|
||||
push,
|
||||
resolve: vi.fn().mockReturnValue({
|
||||
href: '/projects/1/folders/1',
|
||||
}),
|
||||
}),
|
||||
useRoute: () => ({}),
|
||||
RouterLink: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const renderComponent = createComponentRenderer(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
});
|
||||
|
||||
const settingsStore = mockedStore(useSettingsStore);
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
|
||||
describe('ParameterInput.vue', () => {
|
||||
beforeEach(() => {
|
||||
mockNdvState = {
|
||||
@@ -84,15 +98,16 @@ describe('ParameterInput.vue', () => {
|
||||
getNodeType: vi.fn().mockReturnValue(null),
|
||||
};
|
||||
createAppModals();
|
||||
settingsStore.settings.enterprise = createMockEnterpriseSettings();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupAppModals();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should render an options parameter (select)', async () => {
|
||||
const { container, baseElement, emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { container, baseElement, emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'operation',
|
||||
parameter: {
|
||||
@@ -147,8 +162,7 @@ describe('ParameterInput.vue', () => {
|
||||
test('should render an options parameter even if it has invalid fields (like displayName)', async () => {
|
||||
// Test case based on the Schedule node
|
||||
// type=options parameters shouldn't have a displayName field, but some do
|
||||
const { container, baseElement, emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { container, baseElement, emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'operation',
|
||||
parameter: {
|
||||
@@ -191,8 +205,7 @@ describe('ParameterInput.vue', () => {
|
||||
});
|
||||
|
||||
test('should render a string parameter', async () => {
|
||||
const { container, emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { container, emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'tag',
|
||||
parameter: {
|
||||
@@ -212,8 +225,7 @@ describe('ParameterInput.vue', () => {
|
||||
});
|
||||
|
||||
test('should correctly handle paste events', async () => {
|
||||
const { container, emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { container, emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'tag',
|
||||
parameter: {
|
||||
@@ -254,8 +266,7 @@ describe('ParameterInput.vue', () => {
|
||||
{ name: 'Description', value: 'description' },
|
||||
]);
|
||||
|
||||
const { emitted, container } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { emitted, container } = renderComponent({
|
||||
props: {
|
||||
path: 'columns',
|
||||
parameter: {
|
||||
@@ -291,8 +302,7 @@ describe('ParameterInput.vue', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const { emitted, container, getByTestId } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { emitted, container, getByTestId } = renderComponent({
|
||||
props: {
|
||||
path: 'columns',
|
||||
parameter: {
|
||||
@@ -318,10 +328,114 @@ describe('ParameterInput.vue', () => {
|
||||
expect(emitted('update')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should render workflow selector without issues when selected workflow is not archived', async () => {
|
||||
const workflowId = faker.string.uuid();
|
||||
const modelValue = {
|
||||
mode: 'id',
|
||||
value: workflowId,
|
||||
};
|
||||
|
||||
workflowsStore.allWorkflows = [
|
||||
{
|
||||
id: workflowId,
|
||||
name: 'Test',
|
||||
active: false,
|
||||
isArchived: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
nodes: [],
|
||||
connections: {},
|
||||
versionId: faker.string.uuid(),
|
||||
},
|
||||
];
|
||||
|
||||
const { emitted, container, getByTestId, queryByTestId } = renderComponent({
|
||||
props: {
|
||||
path: 'columns',
|
||||
parameter: {
|
||||
displayName: 'Workflow',
|
||||
name: 'workflowId',
|
||||
type: 'workflowSelector',
|
||||
default: '',
|
||||
},
|
||||
modelValue,
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => expect(getByTestId('resource-locator-workflowId')).toBeInTheDocument());
|
||||
|
||||
expect(container.querySelector('.has-issues')).not.toBeInTheDocument();
|
||||
|
||||
const inputs = container.querySelectorAll('input');
|
||||
const mode = inputs[0];
|
||||
expect(mode).toBeInTheDocument();
|
||||
expect(mode).toHaveValue('By ID');
|
||||
|
||||
const value = inputs[1];
|
||||
expect(value).toBeInTheDocument();
|
||||
expect(value).toHaveValue(workflowId);
|
||||
|
||||
expect(queryByTestId('parameter-issues')).not.toBeInTheDocument();
|
||||
|
||||
expect(emitted('update')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should show error when workflow selector has archived workflow selected', async () => {
|
||||
const workflowId = faker.string.uuid();
|
||||
const modelValue: INodeParameterResourceLocator = {
|
||||
__rl: true,
|
||||
mode: 'id',
|
||||
value: workflowId,
|
||||
};
|
||||
|
||||
workflowsStore.allWorkflows = [
|
||||
{
|
||||
id: workflowId,
|
||||
name: 'Test',
|
||||
active: false,
|
||||
isArchived: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
nodes: [],
|
||||
connections: {},
|
||||
versionId: faker.string.uuid(),
|
||||
},
|
||||
];
|
||||
|
||||
const { emitted, container, getByTestId } = renderComponent({
|
||||
props: {
|
||||
path: 'columns',
|
||||
parameter: {
|
||||
displayName: 'Workflow',
|
||||
name: 'workflowId',
|
||||
type: 'workflowSelector',
|
||||
default: '',
|
||||
},
|
||||
modelValue,
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => expect(getByTestId('resource-locator-workflowId')).toBeInTheDocument());
|
||||
|
||||
expect(container.querySelector('.has-issues')).toBeInTheDocument();
|
||||
|
||||
const inputs = container.querySelectorAll('input');
|
||||
const mode = inputs[0];
|
||||
expect(mode).toBeInTheDocument();
|
||||
expect(mode).toHaveValue('By ID');
|
||||
|
||||
const value = inputs[1];
|
||||
expect(value).toBeInTheDocument();
|
||||
expect(value).toHaveValue(workflowId);
|
||||
|
||||
expect(getByTestId('parameter-issues')).toBeInTheDocument();
|
||||
|
||||
expect(emitted('update')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should reset bool on eventBus:removeExpression', async () => {
|
||||
const eventBus = createEventBus();
|
||||
const { emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'aSwitch',
|
||||
parameter: {
|
||||
@@ -341,8 +455,7 @@ describe('ParameterInput.vue', () => {
|
||||
|
||||
test('should reset bool with undefined evaluation on eventBus:removeExpression', async () => {
|
||||
const eventBus = createEventBus();
|
||||
const { emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'aSwitch',
|
||||
parameter: {
|
||||
@@ -362,8 +475,7 @@ describe('ParameterInput.vue', () => {
|
||||
|
||||
test('should reset number on eventBus:removeExpression', async () => {
|
||||
const eventBus = createEventBus();
|
||||
const { emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'aNum',
|
||||
parameter: {
|
||||
@@ -383,8 +495,7 @@ describe('ParameterInput.vue', () => {
|
||||
|
||||
test('should reset string on eventBus:removeExpression', async () => {
|
||||
const eventBus = createEventBus();
|
||||
const { emitted } = renderComponent(ParameterInput, {
|
||||
pinia: createTestingPinia(),
|
||||
const { emitted } = renderComponent({
|
||||
props: {
|
||||
path: 'aStr',
|
||||
parameter: {
|
||||
|
||||
Reference in New Issue
Block a user