Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 }));
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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 || ''}
/>
Expand Down
46 changes: 46 additions & 0 deletions tests/collection/create/create-collection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
49 changes: 43 additions & 6 deletions tests/workspace/create-workspace/create-workspace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } });
Expand All @@ -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();
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();
Comment thread
prateek-bruno marked this conversation as resolved.
});

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 can\'t be empty')).toBeVisible({ timeout: 2000 });
});

await closeElectronApp(app);
Expand Down
Loading