diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.test.ts b/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.test.ts new file mode 100644 index 0000000000..73190fc7f8 --- /dev/null +++ b/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.test.ts @@ -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; + + 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'); + }); +}); diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.vue index 92a10c526a..a17c534672 100644 --- a/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.vue +++ b/packages/frontend/editor-ui/src/components/Projects/ProjectDeleteDialog.vue @@ -7,6 +7,7 @@ import { useI18n } from '@/composables/useI18n'; type Props = { currentProject: Project | null; projects: ProjectListItem[]; + isCurrentProjectEmpty: boolean; }; const props = defineProps(); @@ -21,16 +22,15 @@ const selectedProject = ref(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" > - {{ locale.baseText('projects.settings.delete.message') }} -
- - {{ - locale.baseText('projects.settings.delete.question.transfer.label') - }} - -
- {{ - locale.baseText('projects.settings.delete.question.transfer.title') - }} - -
- - - {{ - locale.baseText('projects.settings.delete.question.wipe.label') - }} - -
- - {{ + locale.baseText('projects.settings.delete.message.empty') + }} +
+ {{ + locale.baseText('projects.settings.delete.message') + }} +
+ + {{ + locale.baseText('projects.settings.delete.question.transfer.label') + }} + +
+ {{ + locale.baseText('projects.settings.delete.question.transfer.title') + }} + - +
+ + + {{ + locale.baseText('projects.settings.delete.question.wipe.label') + }} + +
+ + + +