mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(editor): Simplifying empty project deletion (#15834)
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import ProjectDeleteDialog from '@/components/Projects/ProjectDeleteDialog.vue';
|
||||
import { createTestProject } from '@/__tests__/data/projects';
|
||||
|
||||
const renderComponent = createComponentRenderer(ProjectDeleteDialog, {
|
||||
pinia: createTestingPinia(),
|
||||
});
|
||||
|
||||
const currentProject = createTestProject({
|
||||
id: 'xyz123',
|
||||
name: 'Test',
|
||||
});
|
||||
|
||||
describe('ProjectDeleteDialog', () => {
|
||||
let user: ReturnType<typeof userEvent.setup>;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
it('should render the dialog with correct title and content when project is empty', async () => {
|
||||
const { findByRole, getByRole, queryAllByRole } = renderComponent({
|
||||
props: {
|
||||
currentProject,
|
||||
projects: [],
|
||||
isCurrentProjectEmpty: true,
|
||||
modelValue: true,
|
||||
},
|
||||
});
|
||||
|
||||
const dialog = await findByRole('dialog');
|
||||
expect(dialog).toBeInTheDocument();
|
||||
expect(getByRole('heading', { name: 'Delete "Test" Project?' })).toBeInTheDocument();
|
||||
expect(queryAllByRole('radio')).toHaveLength(0);
|
||||
expect(getByRole('button', { name: 'Delete this project' })).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should render the dialog with correct title and content when project is not empty', async () => {
|
||||
const { findByRole, getByRole, getAllByRole, getByTestId, queryByTestId, emitted } =
|
||||
renderComponent({
|
||||
props: {
|
||||
currentProject,
|
||||
projects: [],
|
||||
isCurrentProjectEmpty: false,
|
||||
modelValue: true,
|
||||
},
|
||||
});
|
||||
|
||||
const dialog = await findByRole('dialog');
|
||||
expect(dialog).toBeInTheDocument();
|
||||
expect(getByRole('heading', { name: 'Delete "Test" Project?' })).toBeInTheDocument();
|
||||
expect(getByRole('button', { name: 'Delete this project' })).toBeDisabled();
|
||||
|
||||
expect(getAllByRole('radio')).toHaveLength(2);
|
||||
|
||||
await user.click(getAllByRole('radio')[0]);
|
||||
expect(getByTestId('project-sharing-select')).toBeVisible();
|
||||
expect(queryByTestId('project-delete-confirm-input')).not.toBeInTheDocument();
|
||||
expect(getByRole('button', { name: 'Delete this project' })).toBeDisabled();
|
||||
|
||||
await user.click(getAllByRole('radio')[1]);
|
||||
expect(queryByTestId('project-sharing-select')).not.toBeInTheDocument();
|
||||
expect(getByTestId('project-delete-confirm-input')).toBeVisible();
|
||||
expect(getByRole('button', { name: 'Delete this project' })).toBeDisabled();
|
||||
|
||||
await user.type(getByTestId('project-delete-confirm-input'), 'delete all data');
|
||||
expect(getByRole('button', { name: 'Delete this project' })).toBeEnabled();
|
||||
|
||||
await user.click(getByRole('button', { name: 'Delete this project' }));
|
||||
expect(emitted()).toHaveProperty('confirmDelete');
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { useI18n } from '@/composables/useI18n';
|
||||
type Props = {
|
||||
currentProject: Project | null;
|
||||
projects: ProjectListItem[];
|
||||
isCurrentProjectEmpty: boolean;
|
||||
};
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@@ -21,16 +22,15 @@ const selectedProject = ref<ProjectSharingData | null>(null);
|
||||
const operation = ref<'transfer' | 'wipe' | null>(null);
|
||||
const wipeConfirmText = ref('');
|
||||
const isValid = computed(() => {
|
||||
if (operation.value === 'transfer') {
|
||||
return !!selectedProject.value;
|
||||
}
|
||||
if (operation.value === 'wipe') {
|
||||
return (
|
||||
wipeConfirmText.value ===
|
||||
locale.baseText('projects.settings.delete.question.wipe.placeholder')
|
||||
);
|
||||
}
|
||||
return false;
|
||||
const expectedWipeConfirmation = locale.baseText(
|
||||
'projects.settings.delete.question.wipe.placeholder',
|
||||
);
|
||||
|
||||
return (
|
||||
props.isCurrentProjectEmpty ||
|
||||
(operation.value === 'transfer' && !!selectedProject.value) ||
|
||||
(operation.value === 'wipe' && wipeConfirmText.value === expectedWipeConfirmation)
|
||||
);
|
||||
});
|
||||
|
||||
const onDelete = () => {
|
||||
@@ -55,47 +55,55 @@ const onDelete = () => {
|
||||
"
|
||||
width="500"
|
||||
>
|
||||
<n8n-text color="text-base">{{ locale.baseText('projects.settings.delete.message') }}</n8n-text>
|
||||
<div class="pt-l">
|
||||
<el-radio
|
||||
:model-value="operation"
|
||||
label="transfer"
|
||||
class="mb-s"
|
||||
@update:model-value="operation = 'transfer'"
|
||||
>
|
||||
<n8n-text color="text-dark">{{
|
||||
locale.baseText('projects.settings.delete.question.transfer.label')
|
||||
}}</n8n-text>
|
||||
</el-radio>
|
||||
<div v-if="operation === 'transfer'" :class="$style.operation">
|
||||
<n8n-text color="text-dark">{{
|
||||
locale.baseText('projects.settings.delete.question.transfer.title')
|
||||
}}</n8n-text>
|
||||
<ProjectSharing
|
||||
v-model="selectedProject"
|
||||
class="pt-2xs"
|
||||
:projects="props.projects"
|
||||
:empty-options-text="locale.baseText('projects.sharing.noMatchingProjects')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-radio
|
||||
:model-value="operation"
|
||||
label="wipe"
|
||||
class="mb-s"
|
||||
@update:model-value="operation = 'wipe'"
|
||||
>
|
||||
<n8n-text color="text-dark">{{
|
||||
locale.baseText('projects.settings.delete.question.wipe.label')
|
||||
}}</n8n-text>
|
||||
</el-radio>
|
||||
<div v-if="operation === 'wipe'" :class="$style.operation">
|
||||
<n8n-input-label :label="locale.baseText('projects.settings.delete.question.wipe.title')">
|
||||
<n8n-input
|
||||
v-model="wipeConfirmText"
|
||||
:placeholder="locale.baseText('projects.settings.delete.question.wipe.placeholder')"
|
||||
<n8n-text v-if="isCurrentProjectEmpty" color="text-base">{{
|
||||
locale.baseText('projects.settings.delete.message.empty')
|
||||
}}</n8n-text>
|
||||
<div v-else>
|
||||
<n8n-text color="text-base">{{
|
||||
locale.baseText('projects.settings.delete.message')
|
||||
}}</n8n-text>
|
||||
<div class="pt-l">
|
||||
<el-radio
|
||||
:model-value="operation"
|
||||
label="transfer"
|
||||
class="mb-s"
|
||||
@update:model-value="operation = 'transfer'"
|
||||
>
|
||||
<n8n-text color="text-dark">{{
|
||||
locale.baseText('projects.settings.delete.question.transfer.label')
|
||||
}}</n8n-text>
|
||||
</el-radio>
|
||||
<div v-if="operation === 'transfer'" :class="$style.operation">
|
||||
<n8n-text color="text-dark">{{
|
||||
locale.baseText('projects.settings.delete.question.transfer.title')
|
||||
}}</n8n-text>
|
||||
<ProjectSharing
|
||||
v-model="selectedProject"
|
||||
class="pt-2xs"
|
||||
:projects="props.projects"
|
||||
:empty-options-text="locale.baseText('projects.sharing.noMatchingProjects')"
|
||||
/>
|
||||
</n8n-input-label>
|
||||
</div>
|
||||
|
||||
<el-radio
|
||||
:model-value="operation"
|
||||
label="wipe"
|
||||
class="mb-s"
|
||||
@update:model-value="operation = 'wipe'"
|
||||
>
|
||||
<n8n-text color="text-dark">{{
|
||||
locale.baseText('projects.settings.delete.question.wipe.label')
|
||||
}}</n8n-text>
|
||||
</el-radio>
|
||||
<div v-if="operation === 'wipe'" :class="$style.operation">
|
||||
<n8n-input-label :label="locale.baseText('projects.settings.delete.question.wipe.title')">
|
||||
<n8n-input
|
||||
v-model="wipeConfirmText"
|
||||
data-test-id="project-delete-confirm-input"
|
||||
:placeholder="locale.baseText('projects.settings.delete.question.wipe.placeholder')"
|
||||
/>
|
||||
</n8n-input-label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
|
||||
Reference in New Issue
Block a user