mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
update: modal styles (#6487)
* update modals styles * chore: color and style improvements * fix: tests * fixes: tests --------- Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
This commit is contained in:
@@ -28,8 +28,8 @@ const Wrapper = styled.div`
|
||||
.bruno-modal-card {
|
||||
animation-duration: 0.85s;
|
||||
animation-delay: 0.1s;
|
||||
background: var(--color-background-top);
|
||||
border-radius: var(--border-radius);
|
||||
background: ${(props) => props.theme.modal.body.bg};
|
||||
border-radius: ${(props) => props.theme.border.radius.base};
|
||||
position: relative;
|
||||
z-index: 11;
|
||||
max-width: calc(100% - var(--spacing-base-unit));
|
||||
@@ -68,25 +68,37 @@ const Wrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
text-transform: uppercase;
|
||||
color: ${(props) => props.theme.modal.title.color};
|
||||
background-color: ${(props) => props.theme.modal.title.bg};
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
padding: 12px;
|
||||
font-size: ${(props) => props.theme.font.size.md};
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-left-radius: ${(props) => props.theme.border.radius.base};
|
||||
border-top-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
|
||||
.bruno-modal-header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.close {
|
||||
font-size: 1.3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: -0.5rem;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1;
|
||||
color: ${(props) => props.theme.modal.iconColor};
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
opacity: 0.5;
|
||||
margin-top: -2px;
|
||||
color: ${(props) => props.theme.modal.title.color};
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s ease, background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
opacity: 1;
|
||||
background-color: ${(props) => props.theme.modal.closeButton.hoverBg};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +116,7 @@ const Wrapper = styled.div`
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
transition: border-color ease-in-out 0.1s;
|
||||
border-radius: 3px;
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
background-color: ${(props) => props.theme.modal.input.bg};
|
||||
border: 1px solid ${(props) => props.theme.modal.input.border};
|
||||
|
||||
@@ -144,14 +156,14 @@ const Wrapper = styled.div`
|
||||
|
||||
.bruno-modal-footer {
|
||||
background-color: ${(props) => props.theme.modal.body.bg};
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: ${(props) => props.theme.border.radius.base};
|
||||
border-bottom-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
}
|
||||
|
||||
&.modal-footer-none {
|
||||
.bruno-modal-content {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: ${(props) => props.theme.border.radius.base};
|
||||
border-bottom-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -10,7 +10,7 @@ const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => (
|
||||
{customHeader ? customHeader : <>{title ? <div className="bruno-modal-header-title">{title}</div> : null}</>}
|
||||
{handleCancel && !hideClose ? (
|
||||
// TODO: Remove data-test-id and use data-testid instead across the codebase.
|
||||
<div className="close cursor-pointer" onClick={handleCancel ? () => handleCancel() : null} data-test-id="modal-close-button" data-testid="modal-close-button">
|
||||
<div className="close cursor-pointer" onClick={handleCancel ? () => handleCancel() : null} data-testid="modal-close-button">
|
||||
×
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { savePreferences } from 'providers/ReduxStore/slices/app';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import * as Yup from 'yup';
|
||||
import debounce from 'lodash/debounce';
|
||||
import toast from 'react-hot-toast';
|
||||
import { IconFlask } from '@tabler/icons';
|
||||
import get from 'lodash/get';
|
||||
@@ -56,19 +57,37 @@ const Beta = ({ close }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const handleSave = (newBetaPreferences) => {
|
||||
const handleSave = useCallback((newBetaPreferences) => {
|
||||
dispatch(
|
||||
savePreferences({
|
||||
...preferences,
|
||||
beta: newBetaPreferences
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
toast.success('Beta preferences saved successfully');
|
||||
close();
|
||||
})
|
||||
.catch((err) => console.log(err) && toast.error('Failed to update beta preferences'));
|
||||
}, [dispatch, preferences]);
|
||||
|
||||
const debouncedSave = useCallback(
|
||||
debounce((values) => {
|
||||
betaSchema.validate(values, { abortEarly: true })
|
||||
.then((validatedValues) => {
|
||||
handleSave(validatedValues);
|
||||
})
|
||||
.catch((error) => {
|
||||
});
|
||||
}, 500),
|
||||
[handleSave, betaSchema]
|
||||
);
|
||||
|
||||
// Auto-save when form values change
|
||||
useEffect(() => {
|
||||
if (formik.dirty && formik.isValid) {
|
||||
debouncedSave(formik.values);
|
||||
}
|
||||
return () => {
|
||||
debouncedSave.cancel();
|
||||
};
|
||||
}, [formik.values, formik.dirty, formik.isValid, debouncedSave]);
|
||||
|
||||
const hasAnyBetaFeatures = BETA_FEATURES.length > 0;
|
||||
|
||||
@@ -113,12 +132,6 @@ const Beta = ({ close }) => {
|
||||
<p>No beta features are currently available</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-10">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { savePreferences } from 'providers/ReduxStore/slices/app';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
@@ -8,6 +9,7 @@ import toast from 'react-hot-toast';
|
||||
const Font = ({ close }) => {
|
||||
const dispatch = useDispatch();
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const isInitialMount = useRef(true);
|
||||
|
||||
const [codeFont, setCodeFont] = useState(get(preferences, 'font.codeFont', 'default'));
|
||||
const [codeFontSize, setCodeFontSize] = useState(get(preferences, 'font.codeFontSize', '13'));
|
||||
@@ -22,22 +24,37 @@ const Font = ({ close }) => {
|
||||
setCodeFontSize(clampedSize);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const handleSave = useCallback((font, fontSize) => {
|
||||
dispatch(
|
||||
savePreferences({
|
||||
...preferences,
|
||||
font: {
|
||||
codeFont,
|
||||
codeFontSize
|
||||
codeFont: font,
|
||||
codeFontSize: fontSize
|
||||
}
|
||||
})
|
||||
).then(() => {
|
||||
toast.success('Preferences saved successfully');
|
||||
close();
|
||||
}).catch(() => {
|
||||
).catch(() => {
|
||||
toast.error('Failed to save preferences');
|
||||
});
|
||||
}, [dispatch, preferences]);
|
||||
|
||||
const debouncedSave = useCallback(
|
||||
debounce((font, fontSize) => {
|
||||
handleSave(font, fontSize);
|
||||
}, 500),
|
||||
[handleSave]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitialMount.current) {
|
||||
isInitialMount.current = false;
|
||||
return;
|
||||
}
|
||||
debouncedSave(codeFont, codeFontSize);
|
||||
return () => {
|
||||
debouncedSave.cancel();
|
||||
};
|
||||
}, [codeFont, codeFontSize, debouncedSave]);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
@@ -68,12 +85,6 @@ const Font = ({ close }) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useRef, useEffect, useCallback } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useFormik } from 'formik';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { savePreferences } from 'providers/ReduxStore/slices/app';
|
||||
@@ -95,7 +96,7 @@ const General = ({ close }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const handleSave = (newPreferences) => {
|
||||
const handleSave = useCallback((newPreferences) => {
|
||||
dispatch(
|
||||
savePreferences({
|
||||
...preferences,
|
||||
@@ -123,12 +124,29 @@ const General = ({ close }) => {
|
||||
defaultCollectionLocation: newPreferences.defaultCollectionLocation
|
||||
}
|
||||
}))
|
||||
.then(() => {
|
||||
toast.success('Preferences saved successfully');
|
||||
close();
|
||||
})
|
||||
.catch((err) => console.log(err) && toast.error('Failed to update preferences'));
|
||||
}, [dispatch, preferences]);
|
||||
|
||||
const debouncedSave = useCallback(
|
||||
debounce((values) => {
|
||||
preferencesSchema.validate(values, { abortEarly: true })
|
||||
.then((validatedValues) => {
|
||||
handleSave(validatedValues);
|
||||
})
|
||||
.catch((error) => {
|
||||
});
|
||||
}, 500),
|
||||
[handleSave]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (formik.dirty && formik.isValid) {
|
||||
debouncedSave(formik.values);
|
||||
}
|
||||
return () => {
|
||||
debouncedSave.cancel();
|
||||
};
|
||||
}, [formik.values, formik.dirty, formik.isValid, debouncedSave]);
|
||||
|
||||
const addCaCertificate = (e) => {
|
||||
const filePath = window?.ipcRenderer?.getFilePath(e?.target?.files?.[0]);
|
||||
@@ -366,11 +384,6 @@ const General = ({ close }) => {
|
||||
{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
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import debounce from 'lodash/debounce';
|
||||
import toast from 'react-hot-toast';
|
||||
import { savePreferences } from 'providers/ReduxStore/slices/app';
|
||||
|
||||
@@ -74,7 +75,7 @@ const ProxySettings = ({ close }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const onUpdate = (values) => {
|
||||
const onUpdate = useCallback((values) => {
|
||||
proxySchema
|
||||
.validate(values, { abortEarly: true })
|
||||
.then((validatedProxy) => {
|
||||
@@ -83,18 +84,20 @@ const ProxySettings = ({ close }) => {
|
||||
...preferences,
|
||||
proxy: validatedProxy
|
||||
})
|
||||
).then(() => {
|
||||
toast.success('Preferences saved successfully');
|
||||
close();
|
||||
}).catch(() => {
|
||||
).catch(() => {
|
||||
toast.error('Failed to save preferences');
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
let errMsg = error.message || 'Preferences validation error';
|
||||
toast.error(errMsg);
|
||||
});
|
||||
};
|
||||
}, [dispatch, preferences, proxySchema]);
|
||||
|
||||
const debouncedSave = useCallback(
|
||||
debounce((values) => {
|
||||
onUpdate(values);
|
||||
}, 500),
|
||||
[onUpdate]
|
||||
);
|
||||
|
||||
const [passwordVisible, setPasswordVisible] = useState(false);
|
||||
|
||||
@@ -113,6 +116,15 @@ const ProxySettings = ({ close }) => {
|
||||
});
|
||||
}, [preferences]);
|
||||
|
||||
useEffect(() => {
|
||||
if (formik.dirty) {
|
||||
debouncedSave(formik.values);
|
||||
}
|
||||
return () => {
|
||||
debouncedSave.cancel();
|
||||
};
|
||||
}, [formik.values, formik.dirty, debouncedSave]);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
@@ -365,11 +377,6 @@ const ProxySettings = ({ close }) => {
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div className="mt-6">
|
||||
<button type="submit" className="submit btn btn-md btn-secondary">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -2,14 +2,20 @@ import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
div.tabs {
|
||||
padding: 8px;
|
||||
min-width: 160px;
|
||||
|
||||
div.tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
min-width: 120px;
|
||||
padding: 7px 10px;
|
||||
padding: 6px 10px;
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
color: var(--color-tab-inactive);
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
&:focus,
|
||||
&:active,
|
||||
@@ -21,18 +27,36 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: ${(props) => props.theme.sidebar.color} !important;
|
||||
background: ${(props) => props.theme.sidebar.collection.item.bg};
|
||||
color: ${(props) => props.theme.text} !important;
|
||||
background: ${(props) => props.theme.modal.title.bg};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.sidebar.collection.item.bg} !important;
|
||||
background: ${(props) => props.theme.modal.title.bg} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.tab-panel {
|
||||
min-height: 300px;
|
||||
min-height: 70vh;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
max-width: 50vw;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
accent-color: ${(props) => props.theme.workspace.accent};
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.collection-info-card {
|
||||
background-color: ${(props) => props.theme.modal.title.bg};
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
}
|
||||
.collection-name {
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.text};
|
||||
margin-bottom: 4px;
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
.collection-path {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
word-break: break-all;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -2,11 +2,12 @@ import React, { useMemo } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import Modal from 'components/Modal';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { IconFiles } from '@tabler/icons';
|
||||
import { IconAlertCircle } from '@tabler/icons';
|
||||
import { removeCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { findCollectionByUid, flattenItems, isItemARequest, hasRequestChanges } from 'utils/collections/index';
|
||||
import filter from 'lodash/filter';
|
||||
import ConfirmCollectionCloseDrafts from './ConfirmCollectionCloseDrafts';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const RemoveCollection = ({ onClose, collectionUid }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -42,21 +43,35 @@ const RemoveCollection = ({ onClose, collectionUid }) => {
|
||||
return <ConfirmCollectionCloseDrafts onClose={onClose} collection={collection} collectionUid={collectionUid} />;
|
||||
}
|
||||
|
||||
const customHeader = (
|
||||
<div className="flex items-center gap-2" data-testid="close-collection-modal-title">
|
||||
<IconAlertCircle size={18} strokeWidth={1.5} className="text-red-500" />
|
||||
<span>Remove Collection</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Otherwise, show the standard remove confirmation modal
|
||||
return (
|
||||
<Modal size="sm" title="Remove Collection" confirmText="Remove" handleConfirm={onConfirm} handleCancel={onClose}>
|
||||
<div className="flex items-center">
|
||||
<IconFiles size={18} strokeWidth={1.5} />
|
||||
<span className="ml-2 mr-4 font-medium">{collection.name}</span>
|
||||
</div>
|
||||
<div className="break-words text-xs mt-1">{collection.pathname}</div>
|
||||
<div className="mt-4">
|
||||
Are you sure you want to remove collection <span className="font-medium">{collection.name}</span> from this workspace?
|
||||
</div>
|
||||
<div className="mt-4 text-muted">
|
||||
The collection files will remain on disk and can be re-added to this or another workspace later.
|
||||
<StyledWrapper>
|
||||
<Modal
|
||||
size="sm"
|
||||
title="Remove Collection"
|
||||
customHeader={customHeader}
|
||||
confirmText="Remove"
|
||||
confirmButtonClass="btn-danger"
|
||||
handleConfirm={onConfirm}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
<p className="mb-4">Are you sure you want to close following collection in Bruno?</p>
|
||||
<div className="collection-info-card">
|
||||
<div className="collection-name">{collection.name}</div>
|
||||
<div className="collection-path">{collection.pathname}</div>
|
||||
</div>
|
||||
<p className="mt-4 text-muted text-sm">
|
||||
It will still be available in the filesystem at the above location and can be re-opened later.
|
||||
</p>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ const RemoveCollectionsModal = ({ collectionUids, onClose }) => {
|
||||
Collections will be removed from the current workspace but will still be available in the file system and can be re-opened later.
|
||||
</div>
|
||||
<div className="flex justify-end mt-6">
|
||||
<button className="btn btn-close btn-sm mr-2" onClick={handleCancel}>
|
||||
<button className="btn btn-close btn-sm mr-2" data-testid="modal-close-button" onClick={handleCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
<button className="btn btn-secondary btn-sm" onClick={handleCloseAllCollections}>
|
||||
|
||||
@@ -313,6 +313,9 @@ const darkTheme = {
|
||||
},
|
||||
backdrop: {
|
||||
opacity: 0.2
|
||||
},
|
||||
closeButton: {
|
||||
hoverBg: 'rgba(255, 255, 255, 0.1)'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const colors = {
|
||||
BRAND: '#cf8730',
|
||||
BRAND: '#c7822e',
|
||||
TEXT: 'rgb(52, 52, 52)',
|
||||
TEXT_MUTED: '#838383',
|
||||
TEXT_LINK: '#1663bb',
|
||||
@@ -9,7 +9,7 @@ const colors = {
|
||||
BLACK: '#000',
|
||||
SLATE_BLACK: '#343434',
|
||||
GREEN: '#047857',
|
||||
YELLOW: '#cf8730',
|
||||
YELLOW: '#c7822e',
|
||||
|
||||
GRAY_1: '#f8f8f8',
|
||||
GRAY_2: '#f3f3f3',
|
||||
@@ -318,6 +318,9 @@ const lightTheme = {
|
||||
},
|
||||
backdrop: {
|
||||
opacity: 0.4
|
||||
},
|
||||
closeButton: {
|
||||
hoverBg: 'rgba(0, 0, 0, 0.08)'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -351,7 +354,7 @@ const lightTheme = {
|
||||
active: {
|
||||
fontWeight: 400,
|
||||
color: colors.SLATE_BLACK,
|
||||
border: '#cf8730'
|
||||
border: '#c7822e'
|
||||
},
|
||||
secondary: {
|
||||
active: {
|
||||
|
||||
@@ -16,6 +16,6 @@ test.describe('File Input Acceptance', () => {
|
||||
expect(acceptValue).toContain('.yml');
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,6 @@ test.describe('Invalid File Handling', () => {
|
||||
expect(hasError).toBe(true);
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,6 @@ test.describe('Invalid Insomnia Collection - Malformed Structure', () => {
|
||||
expect(hasError).toBe(true);
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,6 @@ test.describe('Invalid OpenAPI - Malformed YAML', () => {
|
||||
await expect(parseError.or(importError)).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,6 @@ test.describe('Invalid OpenAPI - Missing Info Section', () => {
|
||||
await expect(errorMessage).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,6 @@ test.describe('Invalid Postman Collection - Invalid JSON', () => {
|
||||
expect(hasError).toBe(true);
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,6 @@ test.describe('Invalid Postman Collection - Missing Info', () => {
|
||||
expect(hasError).toBe(true);
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,6 @@ test.describe('Invalid Postman Collection - Invalid Schema', () => {
|
||||
expect(hasError).toBe(true);
|
||||
|
||||
// Cleanup: close any open modals
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,8 +93,8 @@ test.describe('Onboarding', () => {
|
||||
await removeOption.click();
|
||||
|
||||
// Confirm removal in the modal
|
||||
const removeModal = page.getByRole('dialog').filter({ has: page.getByText('Remove Collection') });
|
||||
await removeModal.getByRole('button', { name: 'Remove' }).click();
|
||||
await page.locator('[data-testid="close-collection-modal-title"]', { hasText: 'Remove Collection' }).waitFor({ state: 'visible' });
|
||||
await page.locator('.bruno-modal-footer .submit').click();
|
||||
|
||||
// Verify collection is closed (no longer visible in sidebar)
|
||||
await expect(sampleCollection).not.toBeVisible();
|
||||
|
||||
@@ -43,10 +43,11 @@ test.describe('Autosave', () => {
|
||||
const autoSaveCheckbox = preferencesModal.locator('#autoSaveEnabled');
|
||||
await autoSaveCheckbox.check();
|
||||
|
||||
// Save preferences
|
||||
await preferencesModal.locator('button[type="submit"]').click();
|
||||
// Wait for auto-save to complete (debounce is 500ms)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Wait for preferences to close
|
||||
// Close preferences modal
|
||||
await preferencesModal.locator('[data-testid="modal-close-button"]').click();
|
||||
await expect(preferencesModal).not.toBeVisible();
|
||||
});
|
||||
|
||||
@@ -92,10 +93,11 @@ test.describe('Autosave', () => {
|
||||
const autoSaveCheckbox = preferencesModal.locator('#autoSaveEnabled');
|
||||
await autoSaveCheckbox.uncheck();
|
||||
|
||||
// Save preferences
|
||||
await preferencesModal.locator('button[type="submit"]').click();
|
||||
// Wait for auto-save to complete (debounce is 500ms)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Wait for preferences to close
|
||||
// Close preferences modal
|
||||
await preferencesModal.locator('[data-testid="modal-close-button"]').click();
|
||||
await expect(preferencesModal).not.toBeVisible();
|
||||
});
|
||||
|
||||
@@ -168,10 +170,11 @@ test.describe('Autosave', () => {
|
||||
const autoSaveCheckbox = preferencesModal.locator('#autoSaveEnabled');
|
||||
await autoSaveCheckbox.check();
|
||||
|
||||
// Save preferences
|
||||
await preferencesModal.locator('button[type="submit"]').click();
|
||||
// Wait for auto-save to complete (debounce is 500ms)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Wait for preferences to close
|
||||
// Close preferences modal
|
||||
await preferencesModal.locator('[data-testid="modal-close-button"]').click();
|
||||
await expect(preferencesModal).not.toBeVisible();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
@@ -10,7 +10,7 @@ test.describe('Default Collection Location Feature', () => {
|
||||
await expect(defaultLocationInput).toHaveValue('/tmp/bruno-collections');
|
||||
|
||||
// close the preferences
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
@@ -24,14 +24,14 @@ test.describe('Default Collection Location Feature', () => {
|
||||
const defaultLocationInput = page.locator('.default-collection-location-input');
|
||||
await defaultLocationInput.clear();
|
||||
|
||||
// save preferences
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
// wait for auto-save to complete (debounce is 500ms)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// verify success message
|
||||
await expect(page.locator('text=Preferences saved successfully').first()).toBeVisible();
|
||||
// close the preferences
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
// wait for modal to close
|
||||
await page.waitForTimeout(500);
|
||||
});
|
||||
|
||||
test('Should save a valid default location', async ({ pageWithUserData: page }) => {
|
||||
@@ -44,14 +44,14 @@ test.describe('Default Collection Location Feature', () => {
|
||||
// fill the default location input
|
||||
await defaultLocationInput.fill('/tmp/bruno-collections');
|
||||
|
||||
// save preferences
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
// wait for auto-save to complete (debounce is 500ms)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// verify success message
|
||||
await expect(page.locator('text=Preferences saved successfully').first()).toBeVisible();
|
||||
// close the preferences
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
// wait for 2 seconds
|
||||
await page.waitForTimeout(2000);
|
||||
// wait for modal to close
|
||||
await page.waitForTimeout(500);
|
||||
});
|
||||
|
||||
test('Should use default location in Create Collection modal', async ({ pageWithUserData: page }) => {
|
||||
|
||||
@@ -23,5 +23,5 @@ test('Should verify all support links with correct URL in preference > Support t
|
||||
const locator_documentation = page.getByRole('link', { name: 'Documentation', exact: true });
|
||||
expect(await locator_documentation.getAttribute('href')).toEqual('https://docs.usebruno.com');
|
||||
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { closeAllCollections, createRequest } from '../../utils/page';
|
||||
test.describe('Code Generation URL Encoding', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
try {
|
||||
const modalCloseButton = page.locator('[data-test-id="modal-close-button"]');
|
||||
const modalCloseButton = page.getByTestId('modal-close-button');
|
||||
if (await modalCloseButton.isVisible()) {
|
||||
await modalCloseButton.click();
|
||||
await modalCloseButton.waitFor({ state: 'hidden' });
|
||||
@@ -52,9 +52,9 @@ test.describe('Code Generation URL Encoding', () => {
|
||||
|
||||
expect(generatedCode).toContain('http://base.source/?name=John%20Doe');
|
||||
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
await page.locator('[data-test-id="modal-close-button"]').waitFor({ state: 'hidden' });
|
||||
await page.getByTestId('modal-close-button').waitFor({ state: 'hidden' });
|
||||
});
|
||||
|
||||
test('Should generate code with proper URL encoding for encoded input', async ({
|
||||
@@ -95,8 +95,8 @@ test.describe('Code Generation URL Encoding', () => {
|
||||
|
||||
expect(generatedCode).toContain('http://base.source/?name=John%20Doe');
|
||||
|
||||
await page.locator('[data-test-id="modal-close-button"]').click();
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
|
||||
await page.locator('[data-test-id="modal-close-button"]').waitFor({ state: 'hidden' });
|
||||
await page.getByTestId('modal-close-button').waitFor({ state: 'hidden' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,10 +18,10 @@ const closeAllCollections = async (page) => {
|
||||
await firstCollection.locator('.collection-actions .icon').click();
|
||||
await page.locator('.dropdown-item').getByText('Remove').click();
|
||||
// Wait for the remove collection modal to be visible
|
||||
await page.locator('.bruno-modal-header-title', { hasText: 'Remove Collection' }).waitFor({ state: 'visible' });
|
||||
await page.getByTestId('close-collection-modal-title').filter({ hasText: 'Remove Collection' }).waitFor({ state: 'visible' });
|
||||
await page.locator('.bruno-modal-footer .submit').click();
|
||||
// Wait for the remove collection modal to be hidden
|
||||
await page.locator('.bruno-modal-header-title', { hasText: 'Remove Collection' }).waitFor({ state: 'hidden' });
|
||||
await page.getByTestId('close-collection-modal-title').filter({ hasText: 'Remove Collection' }).waitFor({ state: 'hidden' });
|
||||
}
|
||||
|
||||
// Wait until no collections are left open (check sidebar only)
|
||||
@@ -288,9 +288,9 @@ const removeCollection = async (page: Page, collectionName: string) => {
|
||||
await locators.dropdown.item('Remove').click();
|
||||
|
||||
// Wait for and confirm modal
|
||||
await locators.modal.title('Remove Collection').waitFor({ state: 'visible' });
|
||||
await page.getByTestId('close-collection-modal-title').filter({ hasText: 'Remove Collection' }).waitFor({ state: 'visible' });
|
||||
await locators.modal.button('Remove').click();
|
||||
await locators.modal.title('Remove Collection').waitFor({ state: 'hidden' });
|
||||
await page.getByTestId('close-collection-modal-title').filter({ hasText: 'Remove Collection' }).waitFor({ state: 'hidden' });
|
||||
|
||||
// Verify collection is removed
|
||||
await expect(
|
||||
|
||||
Reference in New Issue
Block a user