mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
test(editor): Test workflow moving with used credentials
This commit is contained in:
parent
2e3503fa10
commit
d0f3a10f43
|
@ -1,12 +1,18 @@
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
import { createComponentRenderer } from '@/__tests__/render';
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import { createTestWorkflow } from '@/__tests__/mocks';
|
||||||
|
import { createProjectListItem } from '@/__tests__/data/projects';
|
||||||
|
import { getDropdownItems, mockedStore } from '@/__tests__/utils';
|
||||||
|
import type { MockedStore } from '@/__tests__/utils';
|
||||||
import { PROJECT_MOVE_RESOURCE_MODAL } from '@/constants';
|
import { PROJECT_MOVE_RESOURCE_MODAL } from '@/constants';
|
||||||
import ProjectMoveResourceModal from '@/components/Projects/ProjectMoveResourceModal.vue';
|
import ProjectMoveResourceModal from '@/components/Projects/ProjectMoveResourceModal.vue';
|
||||||
import { useTelemetry } from '@/composables/useTelemetry';
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
import { mockedStore } from '@/__tests__/utils';
|
|
||||||
import { useProjectsStore } from '@/stores/projects.store';
|
import { useProjectsStore } from '@/stores/projects.store';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
|
||||||
const renderComponent = createComponentRenderer(ProjectMoveResourceModal, {
|
const renderComponent = createComponentRenderer(ProjectMoveResourceModal, {
|
||||||
|
pinia: createTestingPinia(),
|
||||||
global: {
|
global: {
|
||||||
stubs: {
|
stubs: {
|
||||||
Modal: {
|
Modal: {
|
||||||
|
@ -18,28 +24,22 @@ const renderComponent = createComponentRenderer(ProjectMoveResourceModal, {
|
||||||
});
|
});
|
||||||
|
|
||||||
let telemetry: ReturnType<typeof useTelemetry>;
|
let telemetry: ReturnType<typeof useTelemetry>;
|
||||||
|
let projectsStore: MockedStore<typeof useProjectsStore>;
|
||||||
|
let workflowsStore: MockedStore<typeof useWorkflowsStore>;
|
||||||
|
|
||||||
describe('ProjectMoveResourceModal', () => {
|
describe('ProjectMoveResourceModal', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
telemetry = useTelemetry();
|
telemetry = useTelemetry();
|
||||||
|
projectsStore = mockedStore(useProjectsStore);
|
||||||
|
workflowsStore = mockedStore(useWorkflowsStore);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send telemetry when mounted', async () => {
|
it('should send telemetry when mounted', async () => {
|
||||||
const pinia = createTestingPinia();
|
|
||||||
const telemetryTrackSpy = vi.spyOn(telemetry, 'track');
|
const telemetryTrackSpy = vi.spyOn(telemetry, 'track');
|
||||||
|
|
||||||
const projectsStore = mockedStore(useProjectsStore);
|
projectsStore.availableProjects = [createProjectListItem()];
|
||||||
projectsStore.availableProjects = [
|
workflowsStore.fetchWorkflow.mockResolvedValueOnce(createTestWorkflow());
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'My Project',
|
|
||||||
icon: { type: 'icon', value: 'folder' },
|
|
||||||
type: 'personal',
|
|
||||||
role: 'project:personalOwner',
|
|
||||||
createdAt: '2021-01-01T00:00:00.000Z',
|
|
||||||
updatedAt: '2021-01-01T00:00:00.000Z',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
modalName: PROJECT_MOVE_RESOURCE_MODAL,
|
modalName: PROJECT_MOVE_RESOURCE_MODAL,
|
||||||
|
@ -55,7 +55,7 @@ describe('ProjectMoveResourceModal', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
renderComponent({ props, pinia });
|
renderComponent({ props });
|
||||||
expect(telemetryTrackSpy).toHaveBeenCalledWith(
|
expect(telemetryTrackSpy).toHaveBeenCalledWith(
|
||||||
'User clicked to move a workflow',
|
'User clicked to move a workflow',
|
||||||
expect.objectContaining({ workflow_id: '1' }),
|
expect.objectContaining({ workflow_id: '1' }),
|
||||||
|
@ -63,10 +63,8 @@ describe('ProjectMoveResourceModal', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show no available projects message', async () => {
|
it('should show no available projects message', async () => {
|
||||||
const pinia = createTestingPinia();
|
|
||||||
|
|
||||||
const projectsStore = mockedStore(useProjectsStore);
|
|
||||||
projectsStore.availableProjects = [];
|
projectsStore.availableProjects = [];
|
||||||
|
workflowsStore.fetchWorkflow.mockResolvedValueOnce(createTestWorkflow());
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
modalName: PROJECT_MOVE_RESOURCE_MODAL,
|
modalName: PROJECT_MOVE_RESOURCE_MODAL,
|
||||||
|
@ -82,7 +80,89 @@ describe('ProjectMoveResourceModal', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const { getByText } = renderComponent({ props, pinia });
|
const { getByText } = renderComponent({ props });
|
||||||
expect(getByText(/Currently there are not any projects or users available/)).toBeVisible();
|
expect(getByText(/Currently there are not any projects or users available/)).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not load workflow if the resource is a credential', async () => {
|
||||||
|
const telemetryTrackSpy = vi.spyOn(telemetry, 'track');
|
||||||
|
projectsStore.availableProjects = [createProjectListItem()];
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
modalName: PROJECT_MOVE_RESOURCE_MODAL,
|
||||||
|
data: {
|
||||||
|
resourceType: 'credential',
|
||||||
|
resourceTypeLabel: 'Credential',
|
||||||
|
resource: {
|
||||||
|
id: '1',
|
||||||
|
homeProject: {
|
||||||
|
id: '2',
|
||||||
|
name: 'My Project',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
renderComponent({ props });
|
||||||
|
expect(telemetryTrackSpy).toHaveBeenCalledWith(
|
||||||
|
'User clicked to move a credential',
|
||||||
|
expect.objectContaining({ credential_id: '1' }),
|
||||||
|
);
|
||||||
|
expect(workflowsStore.fetchWorkflow).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send credential IDs when workflow moved with used credentials and checkbox checked', async () => {
|
||||||
|
const destinationProject = createProjectListItem();
|
||||||
|
const currentProjectId = '123';
|
||||||
|
const movedWorkflow = {
|
||||||
|
...createTestWorkflow(),
|
||||||
|
usedCredentials: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'PG Credential',
|
||||||
|
credentialType: 'postgres',
|
||||||
|
currentUserHasAccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: 'Notion Credential',
|
||||||
|
credentialType: 'notion',
|
||||||
|
currentUserHasAccess: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
projectsStore.currentProjectId = currentProjectId;
|
||||||
|
projectsStore.availableProjects = [destinationProject];
|
||||||
|
workflowsStore.fetchWorkflow.mockResolvedValueOnce(movedWorkflow);
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
modalName: PROJECT_MOVE_RESOURCE_MODAL,
|
||||||
|
data: {
|
||||||
|
resourceType: 'workflow',
|
||||||
|
resourceTypeLabel: 'Workflow',
|
||||||
|
resource: movedWorkflow,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { getByTestId } = renderComponent({ props });
|
||||||
|
expect(getByTestId('project-move-resource-modal-button')).toBeDisabled();
|
||||||
|
|
||||||
|
const projectSelect = getByTestId('project-move-resource-modal-select');
|
||||||
|
expect(projectSelect).toBeVisible();
|
||||||
|
|
||||||
|
const projectSelectDropdownItems = await getDropdownItems(projectSelect);
|
||||||
|
await userEvent.click(projectSelectDropdownItems[0]);
|
||||||
|
|
||||||
|
expect(getByTestId('project-move-resource-modal-button')).toBeEnabled();
|
||||||
|
|
||||||
|
await userEvent.click(getByTestId('project-move-resource-modal-checkbox-all'));
|
||||||
|
await userEvent.click(getByTestId('project-move-resource-modal-button'));
|
||||||
|
|
||||||
|
expect(projectsStore.moveResourceToProject).toHaveBeenCalledWith(
|
||||||
|
'workflow',
|
||||||
|
movedWorkflow.id,
|
||||||
|
destinationProject.id,
|
||||||
|
['1', '2'],
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,6 +54,7 @@ const selectedProject = computed(() =>
|
||||||
);
|
);
|
||||||
const isResourceInTeamProject = computed(() => isHomeProjectTeam(props.data.resource));
|
const isResourceInTeamProject = computed(() => isHomeProjectTeam(props.data.resource));
|
||||||
const isResourceWorkflow = computed(() => props.data.resourceType === ResourceType.Workflow);
|
const isResourceWorkflow = computed(() => props.data.resourceType === ResourceType.Workflow);
|
||||||
|
const isResourceCredential = computed(() => props.data.resourceType === ResourceType.Credential);
|
||||||
|
|
||||||
const isHomeProjectTeam = (resource: IWorkflowDb | ICredentialsResponse) =>
|
const isHomeProjectTeam = (resource: IWorkflowDb | ICredentialsResponse) =>
|
||||||
resource.homeProject?.type === ProjectTypes.Team;
|
resource.homeProject?.type === ProjectTypes.Team;
|
||||||
|
@ -184,7 +185,10 @@ onMounted(async () => {
|
||||||
></N8nOption>
|
></N8nOption>
|
||||||
</N8nSelect>
|
</N8nSelect>
|
||||||
<N8nText>
|
<N8nText>
|
||||||
<i18n-t keypath="projects.move.resource.modal.message.sharingNote">
|
<i18n-t
|
||||||
|
v-if="isResourceCredential"
|
||||||
|
keypath="projects.move.resource.modal.message.sharingNote"
|
||||||
|
>
|
||||||
<template #note
|
<template #note
|
||||||
><strong>{{
|
><strong>{{
|
||||||
i18n.baseText('projects.move.resource.modal.message.note')
|
i18n.baseText('projects.move.resource.modal.message.note')
|
||||||
|
@ -209,6 +213,7 @@ onMounted(async () => {
|
||||||
v-if="usedCredentials.length"
|
v-if="usedCredentials.length"
|
||||||
v-model="shareUsedCredentials"
|
v-model="shareUsedCredentials"
|
||||||
:class="$style.textBlock"
|
:class="$style.textBlock"
|
||||||
|
data-test-id="project-move-resource-modal-checkbox-all"
|
||||||
>
|
>
|
||||||
<i18n-t keypath="projects.move.resource.modal.message.usedCredentials">
|
<i18n-t keypath="projects.move.resource.modal.message.usedCredentials">
|
||||||
<template #usedCredentials>
|
<template #usedCredentials>
|
||||||
|
@ -255,7 +260,12 @@ onMounted(async () => {
|
||||||
<N8nButton type="secondary" text class="mr-2xs" @click="closeModal">
|
<N8nButton type="secondary" text class="mr-2xs" @click="closeModal">
|
||||||
{{ i18n.baseText('generic.cancel') }}
|
{{ i18n.baseText('generic.cancel') }}
|
||||||
</N8nButton>
|
</N8nButton>
|
||||||
<N8nButton :disabled="!projectId" type="primary" @click="moveResource">
|
<N8nButton
|
||||||
|
:disabled="!projectId"
|
||||||
|
type="primary"
|
||||||
|
data-test-id="project-move-resource-modal-button"
|
||||||
|
@click="moveResource"
|
||||||
|
>
|
||||||
{{
|
{{
|
||||||
i18n.baseText('projects.move.resource.modal.button', {
|
i18n.baseText('projects.move.resource.modal.button', {
|
||||||
interpolate: { resourceTypeLabel: props.data.resourceTypeLabel },
|
interpolate: { resourceTypeLabel: props.data.resourceTypeLabel },
|
||||||
|
|
Loading…
Reference in a new issue