fix: show whitespace-only error on Create workspace and collection modal (#7883)

This commit is contained in:
prateek-bruno
2026-05-13 09:59:42 +05:30
committed by GitHub
parent d8fb7c7e7e
commit dd922c7163
4 changed files with 121 additions and 20 deletions

View File

@@ -45,23 +45,24 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation, initi
},
validationSchema: Yup.object({
collectionName: Yup.string()
.min(1, 'must be at least 1 character')
.max(255, 'must be 255 characters or less')
.required('collection name is required'),
.trim()
.min(1, 'Collection name can\'t be empty')
.max(255, 'Must be 255 characters or less')
.required('Collection name is required'),
collectionFolderName: Yup.string()
.min(1, 'must be at least 1 character')
.max(255, 'must be 255 characters or less')
.min(1, 'Must be at least 1 character')
.max(255, 'Must be 255 characters or less')
.test('is-valid-collection-name', function (value) {
const isValid = validateName(value);
return isValid ? true : this.createError({ message: validateNameError(value) });
})
.required('folder name is required'),
collectionLocation: Yup.string().min(1, 'location is required').required('location is required'),
format: Yup.string().oneOf(['bru', 'yml'], 'invalid format').required('format is required')
.required('Folder name is required'),
collectionLocation: Yup.string().min(1, 'Location is required').required('Location is required'),
format: Yup.string().oneOf(['bru', 'yml'], 'invalid format').required('Format is required')
}),
onSubmit: async (values) => {
try {
await dispatch(createCollection(values.collectionName,
await dispatch(createCollection(values.collectionName.trim(),
values.collectionFolderName,
values.collectionLocation,
{ format: values.format }));
@@ -126,8 +127,17 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation, initi
ref={inputRef}
className="block textbox mt-2 w-full"
onChange={(e) => {
const collectionName = e.target.value;
if (!isEditing) {
formik.setValues((values) => ({
...values,
collectionName,
collectionFolderName: sanitizeName(collectionName)
}));
return;
}
formik.handleChange(e);
!isEditing && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value));
}}
autoComplete="off"
autoCorrect="off"

View File

@@ -33,7 +33,8 @@ const CreateWorkspace = ({ onClose }) => {
},
validationSchema: Yup.object({
workspaceName: Yup.string()
.min(1, 'Must be at least 1 character')
.trim()
.min(1, 'Workspace name can\'t be empty')
.max(255, 'Must be 255 characters or less')
.required('Workspace name is required')
.test('unique-name', 'A workspace with this name already exists', function (value) {
@@ -58,7 +59,7 @@ const CreateWorkspace = ({ onClose }) => {
try {
setIsSubmitting(true);
await dispatch(createWorkspaceAction(values.workspaceName, values.workspaceFolderName, values.workspaceLocation));
await dispatch(createWorkspaceAction(values.workspaceName.trim(), values.workspaceFolderName, values.workspaceLocation));
toast.success('Workspace created!');
onClose();
} catch (error) {
@@ -116,10 +117,17 @@ const CreateWorkspace = ({ onClose }) => {
autoCapitalize="off"
spellCheck="false"
onChange={(e) => {
formik.handleChange(e);
const workspaceName = e.target.value;
if (!isEditing) {
formik.setFieldValue('workspaceFolderName', sanitizeName(e.target.value));
formik.setValues((values) => ({
...values,
workspaceName,
workspaceFolderName: sanitizeName(workspaceName)
}));
return;
}
formik.handleChange(e);
}}
value={formik.values.workspaceName || ''}
/>

View File

@@ -7,6 +7,52 @@ test.describe('Create collection', () => {
await closeAllCollections(page);
});
test('should show validation error for empty name in modal and keep modal open', async ({ page }) => {
await page.getByTestId('collections-header-add-menu').click();
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }).click();
const inlineCreator = page.locator('.inline-collection-creator');
await inlineCreator.waitFor({ state: 'visible', timeout: 5000 });
await inlineCreator.locator('.cog-btn').click();
const createCollectionModal = page.locator('.bruno-modal-card').filter({ hasText: 'Create Collection' });
await createCollectionModal.waitFor({ state: 'visible', timeout: 5000 });
const submitButton = createCollectionModal.getByRole('button', { name: 'Create', exact: true });
await createCollectionModal.getByLabel('Name').fill('');
await expect(submitButton).toBeEnabled();
await submitButton.click();
await expect(createCollectionModal).toBeVisible();
await expect(submitButton).toBeEnabled();
await expect(createCollectionModal.getByText('Collection name is required')).toBeVisible({ timeout: 2000 });
await createCollectionModal.getByRole('button', { name: 'Cancel' }).click();
});
test('should show validation error for whitespace-only name in modal and keep modal open', async ({ page }) => {
await page.getByTestId('collections-header-add-menu').click();
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Create collection' }).click();
const inlineCreator = page.locator('.inline-collection-creator');
await inlineCreator.waitFor({ state: 'visible', timeout: 5000 });
await inlineCreator.locator('.cog-btn').click();
const createCollectionModal = page.locator('.bruno-modal-card').filter({ hasText: 'Create Collection' });
await createCollectionModal.waitFor({ state: 'visible', timeout: 5000 });
const submitButton = createCollectionModal.getByRole('button', { name: 'Create', exact: true });
await createCollectionModal.getByLabel('Name').fill(' ');
await expect(submitButton).toBeEnabled();
await submitButton.click();
await expect(createCollectionModal).toBeVisible();
await expect(submitButton).toBeEnabled();
await expect(createCollectionModal.getByText('Collection name can\'t be empty')).toBeVisible({ timeout: 2000 });
await createCollectionModal.getByRole('button', { name: 'Cancel' }).click();
});
test('Create collection and add a simple HTTP request', async ({ page, createTmpDir }) => {
const collectionName = 'test-collection';
const requestName = 'ping';

View File

@@ -362,7 +362,7 @@ test.describe('Create Workspace', () => {
await closeElectronApp(app);
});
test('should show validation error for empty name in modal', async ({ launchElectronApp, createTmpDir }) => {
test('should show validation error for empty name in modal and keep modal open', async ({ launchElectronApp, createTmpDir }) => {
const wsLocation = await createTmpDir('ws-location-modal-empty');
const app = await launchElectronApp({ initUserDataPath, templateVars: { wsLocation } });
@@ -376,20 +376,57 @@ test.describe('Create Workspace', () => {
await page.locator('.cog-btn').click();
});
await test.step('Clear name and try to submit', async () => {
await test.step('Clear name and submit', async () => {
const modal = page.locator('.bruno-modal-card').filter({ hasText: 'Create Workspace' });
await modal.waitFor({ state: 'visible', timeout: 5000 });
const submitButton = modal.getByRole('button', { name: 'Create Workspace' });
// Ensure name field is empty
await modal.locator('#workspace-name').fill('');
await modal.getByRole('button', { name: 'Create Workspace' }).click();
await expect(submitButton).toBeEnabled();
await submitButton.click();
});
await test.step('Verify validation error appears and modal stays open', async () => {
const modal = page.locator('.bruno-modal-card').filter({ hasText: 'Create Workspace' });
const submitButton = modal.getByRole('button', { name: 'Create Workspace' });
await expect(modal).toBeVisible();
const error = modal.locator('.text-red-500');
await expect(error.first()).toBeVisible({ timeout: 2000 });
await expect(submitButton).toBeEnabled();
await expect(modal.getByText('Workspace name is required')).toBeVisible({ timeout: 2000 });
});
await closeElectronApp(app);
});
test('should show validation error for whitespace-only name in modal and keep modal open', async ({ launchElectronApp, createTmpDir }) => {
const wsLocation = await createTmpDir('ws-location-modal-whitespace');
const app = await launchElectronApp({ initUserDataPath, templateVars: { wsLocation } });
const page = await app.firstWindow();
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
await test.step('Start inline creation and open advanced modal', async () => {
await page.locator('.workspace-name-container').click();
await page.locator('.dropdown-item').filter({ hasText: 'Create workspace' }).click();
await expect(page.locator('.workspace-name-input')).toBeVisible({ timeout: 5000 });
await page.locator('.cog-btn').click();
});
await test.step('Enter whitespace-only name and submit', async () => {
const modal = page.locator('.bruno-modal-card').filter({ hasText: 'Create Workspace' });
await modal.waitFor({ state: 'visible', timeout: 5000 });
const submitButton = modal.getByRole('button', { name: 'Create Workspace' });
await modal.locator('#workspace-name').fill(' ');
await expect(submitButton).toBeEnabled();
await submitButton.click();
});
await test.step('Verify validation error appears and modal stays open', async () => {
const modal = page.locator('.bruno-modal-card').filter({ hasText: 'Create Workspace' });
const submitButton = modal.getByRole('button', { name: 'Create Workspace' });
await expect(modal).toBeVisible();
await expect(submitButton).toBeEnabled();
await expect(modal.getByText('Workspace name can\'t be empty')).toBeVisible({ timeout: 2000 });
});
await closeElectronApp(app);