diff --git a/frontend/e2e/files-plugin.spec.js b/frontend/e2e/files-plugin.spec.js index adcdac4..629f4f9 100644 --- a/frontend/e2e/files-plugin.spec.js +++ b/frontend/e2e/files-plugin.spec.js @@ -114,6 +114,24 @@ test.describe('G: Files Plugin', () => { await expect(page.locator('[data-file-name="bad"]')).toHaveCount(0); }); + test('files explorer shows inline rename conflict errors', async ({ page }) => { + await page.locator('.wt-label').filter({ hasText: 'Project' }).click(); + await openFilesTool(page); + await expect(page.locator('.files-breadcrumb')).toContainText('Project', { timeout: 10000 }); + + await page.locator('[data-file-name="project-only.txt"]').click(); + await page.locator('[data-files-action="rename"]').click(); + await page.locator('[data-files-rename-input]').fill('Notes'); + await page.locator('[data-files-rename-confirm]').click(); + + await expect(page.locator('[data-files-rename-error]')).toBeVisible(); + await expect(page.locator('[data-files-rename-error]')).toContainText('already exists'); + await expect(page.locator('[data-files-rename-input]')).toBeVisible(); + await expect(page.locator('[data-files-rename-input]')).toHaveValue('Notes'); + await expect(page.locator('[data-file-name="project-only.txt"]')).toBeVisible(); + await expect(page.locator('[data-file-name="Notes"]')).toBeVisible(); + }); + test('files explorer shows inline create validation errors', async ({ page }) => { await page.locator('.wt-label').filter({ hasText: 'Project' }).click(); await openFilesTool(page); diff --git a/frontend/src/lib/test/wails-mock.js b/frontend/src/lib/test/wails-mock.js index 50d4cbd..75e03d9 100644 --- a/frontend/src/lib/test/wails-mock.js +++ b/frontend/src/lib/test/wails-mock.js @@ -749,6 +749,10 @@ function parent(path) { path = clean(path); var i = path.lastIndexOf('/'); return i < 0 ? '' : path.slice(0, i); } function ext(name) { var i = String(name || '').lastIndexOf('.'); return i > 0 ? name.slice(i + 1).toLowerCase() : ''; } function base(path) { path = clean(path); var i = path.lastIndexOf('/'); return i < 0 ? path : path.slice(i + 1); } + function isConflictError(err) { + var message = (err && err.message) ? err.message : String(err || ''); + return /conflict|already exists|exists/i.test(message); + } function formatDate(value) { if (!value) return ''; var date = new Date(value); @@ -1064,7 +1068,10 @@ if (newName === '.' || newName === '..' || newName[0] === ' ' || newName[newName.length - 1] === ' ' || newName[newName.length - 1] === '.') { setRenameError('Invalid name'); return; } var to = parent(renaming.relativePath); to = to ? to + '/' + newName : newName; - api.files.move(renaming.relativePath, to, { overwrite: false }).then(function () { cancelRename(); load(); }).catch(function (err) { setRenameError('Error: ' + ((err && err.message) ? err.message : String(err))); }); + api.files.move(renaming.relativePath, to, { overwrite: false }).then(function () { cancelRename(); load(); }).catch(function (err) { + if (isConflictError(err)) { setRenameError('A file with that name already exists'); return; } + setRenameError('Error: ' + ((err && err.message) ? err.message : String(err))); + }); } function confirmModal(message, options) { options = options || {};