mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-29 07:34:07 +00:00
Merge pull request #5557 from Pragadesh-45/feat/default-location
feat: default location for collections
This commit is contained in:
@@ -3,6 +3,7 @@ import get from 'lodash/get';
|
||||
import { useFormik } from 'formik';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { savePreferences } from 'providers/ReduxStore/slices/app';
|
||||
import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import * as Yup from 'yup';
|
||||
import toast from 'react-hot-toast';
|
||||
@@ -35,7 +36,8 @@ const General = ({ close }) => {
|
||||
})
|
||||
.test('isValidTimeout', 'Request Timeout must be equal or greater than 0', (value) => {
|
||||
return value === undefined || Number(value) >= 0;
|
||||
})
|
||||
}),
|
||||
defaultCollectionLocation: Yup.string().max(1024)
|
||||
});
|
||||
|
||||
const formik = useFormik({
|
||||
@@ -50,7 +52,8 @@ const General = ({ close }) => {
|
||||
},
|
||||
timeout: preferences.request.timeout,
|
||||
storeCookies: get(preferences, 'request.storeCookies', true),
|
||||
sendCookies: get(preferences, 'request.sendCookies', true)
|
||||
sendCookies: get(preferences, 'request.sendCookies', true),
|
||||
defaultCollectionLocation: get(preferences, 'general.defaultCollectionLocation', '')
|
||||
},
|
||||
validationSchema: preferencesSchema,
|
||||
onSubmit: async (values) => {
|
||||
@@ -79,6 +82,9 @@ const General = ({ close }) => {
|
||||
timeout: newPreferences.timeout,
|
||||
storeCookies: newPreferences.storeCookies,
|
||||
sendCookies: newPreferences.sendCookies
|
||||
},
|
||||
general: {
|
||||
defaultCollectionLocation: newPreferences.defaultCollectionLocation
|
||||
}
|
||||
}))
|
||||
.then(() => {
|
||||
@@ -99,6 +105,19 @@ const General = ({ close }) => {
|
||||
formik.setFieldValue('customCaCertificate.filePath', null);
|
||||
};
|
||||
|
||||
const browseDefaultLocation = () => {
|
||||
dispatch(browseDirectory())
|
||||
.then((dirPath) => {
|
||||
if (typeof dirPath === 'string') {
|
||||
formik.setFieldValue('defaultCollectionLocation', dirPath);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
formik.setFieldValue('defaultCollectionLocation', '');
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
@@ -231,6 +250,35 @@ const General = ({ close }) => {
|
||||
{formik.touched.timeout && formik.errors.timeout ? (
|
||||
<div className="text-red-500">{formik.errors.timeout}</div>
|
||||
) : null}
|
||||
<div className="flex flex-col mt-6">
|
||||
<label className="block select-none default-collection-location-label" htmlFor="defaultCollectionLocation">
|
||||
Default Collection Location
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="defaultCollectionLocation"
|
||||
className="block textbox mt-2 w-full cursor-pointer default-collection-location-input"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.defaultCollectionLocation || ''}
|
||||
onClick={browseDefaultLocation}
|
||||
placeholder="Click to browse for default location"
|
||||
/>
|
||||
<div className="mt-1">
|
||||
<span
|
||||
className="text-link cursor-pointer hover:underline default-collection-location-browse"
|
||||
onClick={browseDefaultLocation}
|
||||
>
|
||||
Browse
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{formik.touched.defaultCollectionLocation && formik.errors.defaultCollectionLocation ? (
|
||||
<div className="text-red-500">{formik.errors.defaultCollectionLocation}</div>
|
||||
) : null}
|
||||
<div className="mt-10">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary">
|
||||
Save
|
||||
|
||||
@@ -12,12 +12,15 @@ import PathDisplay from 'components/PathDisplay';
|
||||
import { useState } from 'react';
|
||||
import { IconArrowBackUp, IconEdit } from "@tabler/icons";
|
||||
import { findCollectionByUid } from 'utils/collections/index';
|
||||
import get from 'lodash/get';
|
||||
|
||||
const CloneCollection = ({ onClose, collectionUid }) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const collection = useSelector(state => findCollectionByUid(state.collections.collections, collectionUid));
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const defaultLocation = get(preferences, 'general.defaultCollectionLocation', '');
|
||||
const { name } = collection;
|
||||
|
||||
const formik = useFormik({
|
||||
@@ -25,7 +28,7 @@ const CloneCollection = ({ onClose, collectionUid }) => {
|
||||
initialValues: {
|
||||
collectionName: `${name} copy`,
|
||||
collectionFolderName: `${sanitizeName(name)} copy`,
|
||||
collectionLocation: ''
|
||||
collectionLocation: defaultLocation
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
collectionName: Yup.string()
|
||||
|
||||
@@ -252,7 +252,7 @@ const Collection = ({ collection, searchText }) => {
|
||||
</div>
|
||||
{isLoading ? <IconLoader2 className="animate-spin mx-1" size={18} strokeWidth={1.5} /> : null}
|
||||
</div>
|
||||
<div className="collection-actions">
|
||||
<div className="collection-actions" data-testid="collection-actions">
|
||||
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
@@ -274,6 +274,7 @@ const Collection = ({ collection, searchText }) => {
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
data-testid="clone-collection"
|
||||
onClick={(_e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowCloneCollectionModalOpen(true);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions';
|
||||
@@ -14,18 +14,21 @@ import Help from 'components/Help';
|
||||
import { multiLineMsg } from "utils/common";
|
||||
import { formatIpcError } from "utils/common/error";
|
||||
import { toggleSidebarCollapse } from 'providers/ReduxStore/slices/app';
|
||||
import get from 'lodash/get';
|
||||
|
||||
const CreateCollection = ({ onClose }) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const defaultLocation = get(preferences, 'general.defaultCollectionLocation', '');
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
collectionName: '',
|
||||
collectionFolderName: '',
|
||||
collectionLocation: ''
|
||||
collectionLocation: defaultLocation
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
collectionName: Yup.string()
|
||||
|
||||
@@ -82,7 +82,7 @@ const StatusBar = () => {
|
||||
|
||||
<ToolHint text="Preferences" toolhintId="Preferences" place="top-start" offset={10}>
|
||||
<button
|
||||
className="status-bar-button"
|
||||
className="status-bar-button preferences-button"
|
||||
data-trigger="preferences"
|
||||
onClick={() => dispatch(showPreferences(true))}
|
||||
tabIndex={0}
|
||||
|
||||
@@ -78,7 +78,7 @@ const Welcome = () => {
|
||||
aria-label={t('WELCOME.CREATE_COLLECTION')}
|
||||
>
|
||||
<IconPlus aria-hidden size={18} strokeWidth={2} />
|
||||
<span className="label ml-2" id="create-collection">
|
||||
<span className="label ml-2" id="create-collection" data-testid="create-collection">
|
||||
{t('WELCOME.CREATE_COLLECTION')}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -25,6 +25,9 @@ const initialState = {
|
||||
font: {
|
||||
codeFont: 'default'
|
||||
},
|
||||
general: {
|
||||
defaultCollectionLocation: ''
|
||||
},
|
||||
beta: {
|
||||
grpc: false
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ const defaultPreferences = {
|
||||
},
|
||||
onboarding: {
|
||||
hasLaunchedBefore: false
|
||||
},
|
||||
general: {
|
||||
defaultCollectionLocation: ''
|
||||
}
|
||||
};
|
||||
|
||||
@@ -89,6 +92,9 @@ const preferencesSchema = Yup.object().shape({
|
||||
}),
|
||||
onboarding: Yup.object({
|
||||
hasLaunchedBefore: Yup.boolean()
|
||||
}),
|
||||
general: Yup.object({
|
||||
defaultCollectionLocation: Yup.string().max(1024).nullable()
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "collection",
|
||||
"type": "collection"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
meta {
|
||||
name: collection
|
||||
type: collection
|
||||
version: 1.0.0
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
vars {
|
||||
host: https://www.httpfaker.org
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
meta {
|
||||
name: request
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{host}}/api/echo
|
||||
body: text
|
||||
auth: none
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
|
||||
test.describe('Default Collection Location Feature', () => {
|
||||
test('Should hydrate the default location from preferences', async ({ pageWithUserData: page }) => {
|
||||
// open preferences
|
||||
await page.locator('.preferences-button').click();
|
||||
|
||||
// verify the default location is pre-filled
|
||||
const defaultLocationInput = page.locator('.default-collection-location-input');
|
||||
await expect(defaultLocationInput).toHaveValue('/tmp/bruno-collections');
|
||||
|
||||
// close the preferences
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
|
||||
test('Should save empty default location', async ({ pageWithUserData: page }) => {
|
||||
// open preferences
|
||||
await page.locator('.preferences-button').click();
|
||||
|
||||
// clear the default location field
|
||||
const defaultLocationInput = page.locator('.default-collection-location-input');
|
||||
await defaultLocationInput.clear();
|
||||
|
||||
// save preferences
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// verify success message
|
||||
await expect(page.locator('text=Preferences saved successfully')).toBeVisible();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
|
||||
test('Should save a valid default location', async ({ pageWithUserData: page }) => {
|
||||
// open preferences
|
||||
await page.locator('.preferences-button').click();
|
||||
|
||||
// set a default location
|
||||
const defaultLocationInput = page.locator('.default-collection-location-input');
|
||||
|
||||
// fill the default location input
|
||||
await defaultLocationInput.fill('/tmp/bruno-collections');
|
||||
|
||||
// save preferences
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// verify success message
|
||||
await expect(page.locator('text=Preferences saved successfully')).toBeVisible();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
|
||||
test('Should use default location in Create Collection modal', async ({ pageWithUserData: page }) => {
|
||||
// test Create Collection modal
|
||||
await page.locator('[data-testid="create-collection"]').click();
|
||||
|
||||
// verify the default location is pre-filled
|
||||
const collectionLocationInput = page.getByLabel('Location');
|
||||
await expect(collectionLocationInput).toHaveValue('/tmp/bruno-collections');
|
||||
|
||||
// cancel the collection creation
|
||||
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
|
||||
test('Should use default location in Clone Collection modal', async ({ pageWithUserData: page }) => {
|
||||
// open the clone collection modal
|
||||
await page.locator('[data-testid="collection-actions"]').click();
|
||||
await page.getByTestId('clone-collection').click();
|
||||
|
||||
// verify the default location is pre-filled
|
||||
const cloneLocationInput = page.getByLabel('Location');
|
||||
await expect(cloneLocationInput).toHaveValue('/tmp/bruno-collections');
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"collections": [
|
||||
{
|
||||
"path": "{{projectRoot}}/tests/preferences/default-collection-location/collection",
|
||||
"securityConfig": {
|
||||
"jsSandboxMode": "developer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/preferences/default-collection-location/collection"],
|
||||
"preferences": {
|
||||
"general": {
|
||||
"defaultCollectionLocation": "/tmp/bruno-collections"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user