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:
naman-bruno
2025-12-23 23:29:03 +05:30
committed by GitHub
parent 8c7ed3fe51
commit 3081c06964
26 changed files with 269 additions and 141 deletions

View File

@@ -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};
}
}
`;

View File

@@ -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}

View File

@@ -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>
);

View File

@@ -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>
);
};

View File

@@ -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>
);

View File

@@ -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>
);

View File

@@ -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;
}
`;

View File

@@ -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;

View File

@@ -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.
</div>
</Modal>
<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>
);
};

View File

@@ -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}>

View File

@@ -313,6 +313,9 @@ const darkTheme = {
},
backdrop: {
opacity: 0.2
},
closeButton: {
hoverBg: 'rgba(255, 255, 255, 0.1)'
}
},

View File

@@ -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: {

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();
});
});

View File

@@ -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();

View File

@@ -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);

View File

@@ -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 }) => {

View File

@@ -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();
});

View File

@@ -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' });
});
});

View File

@@ -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(