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 @@ -36,6 +36,7 @@ const CloneGitRepository = ({ onClose, onFinish, collectionRepositoryUrl = null
? get(preferences, 'general.defaultLocation', '')
: (activeWorkspace?.pathname ? path.join(activeWorkspace.pathname, 'collections') : '');
const inputRef = useRef();
const selectAllRef = useRef();
const dispatch = useDispatch();

useEffect(() => {
Expand All @@ -56,6 +57,15 @@ const CloneGitRepository = ({ onClose, onFinish, collectionRepositoryUrl = null
}
}, []);

const areAllCollectionsSelected = collectionPaths.length > 0 && selectedCollectionPaths.length === collectionPaths.length;
const areSomeCollectionsSelected = selectedCollectionPaths.length > 0 && selectedCollectionPaths.length < collectionPaths.length;

useEffect(() => {
if (selectAllRef?.current) {
selectAllRef.current.indeterminate = areSomeCollectionsSelected;
}
}, [areSomeCollectionsSelected]);

const cloneInProgress = () => {
setSteps((prev) => [
...prev,
Expand Down Expand Up @@ -164,6 +174,12 @@ const CloneGitRepository = ({ onClose, onFinish, collectionRepositoryUrl = null
);
};

const handleToggleSelectAllCollections = () => {
setSelectedCollectionPaths((prevSelected) =>
prevSelected.length === collectionPaths.length ? [] : [...collectionPaths]
);
};

const getRelativePath = (fullPath, pathname) => {
let relativePath = path.relative(fullPath, pathname);
const { dir, name } = path.parse(relativePath);
Expand Down Expand Up @@ -342,6 +358,16 @@ const CloneGitRepository = ({ onClose, onFinish, collectionRepositoryUrl = null
<h3 className="text-sm mb-2">
{collectionPaths.length} bruno collections found. Please select the collections to open:
</h3>
<label className="flex items-center space-x-2 mb-2">
<input
type="checkbox"
ref={selectAllRef}
checked={areAllCollectionsSelected}
onChange={handleToggleSelectAllCollections}
className="form-checkbox"
/>
<span>Select all</span>
</label>
<ul>
{collectionPaths.map((collection) => (
<li key={collection} className="mb-2">
Expand Down
69 changes: 69 additions & 0 deletions tests/import/url-import/github-repository-import.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,73 @@ test.describe('GitHub Repository URL Import', () => {
// Cleanup: close any open modals using Cancel button (avoids form validation)
await page.getByRole('button', { name: 'Cancel' }).click();
});

test('Clone Git Repository collection selection supports Select all', async ({ page, createTmpDir, electronApp }) => {
const githubUrl = 'https://github.com/usebruno/github-rest-api-collection';

await page.getByTestId('collections-header-add-menu').click();
await page.locator('.tippy-box .dropdown-item').filter({ hasText: 'Import collection' }).click();

const importModal = page.getByRole('dialog');
await importModal.waitFor({ state: 'visible' });
await expect(importModal.locator('.bruno-modal-header-title')).toContainText('Import Collection');

await page.getByTestId('github-tab').click();
await page.getByTestId('git-url-input').fill(githubUrl);
await page.locator('#clone-git-button').click();
await page.locator('#import-collection-loader').waitFor({ state: 'hidden' });

const cloneModal = page.getByRole('dialog');
await expect(cloneModal.locator('.bruno-modal-header-title')).toContainText('Clone Git Repository');

const cloneTargetPath = await createTmpDir('clone-git-select-all');
const mockedCollectionPaths = [
`${cloneTargetPath}/mock-repo/collection-one`,
`${cloneTargetPath}/mock-repo/folder/collection-two`,
`${cloneTargetPath}/mock-repo/collection-three`
];

await electronApp.evaluate(({ ipcMain }, paths) => {
ipcMain.removeHandler('renderer:clone-git-repository');
ipcMain.handle('renderer:clone-git-repository', async () => 'Repository cloned successfully');

ipcMain.removeHandler('renderer:scan-for-bruno-files');
ipcMain.handle('renderer:scan-for-bruno-files', async () => paths);
}, mockedCollectionPaths);

const locationInput = cloneModal.locator('#collection-location');
await locationInput.evaluate((el) => {
const input = el as HTMLInputElement;
input.removeAttribute('readonly');
input.readOnly = false;
});
await locationInput.fill(cloneTargetPath);

await cloneModal.getByRole('button', { name: 'Clone', exact: true }).click();

await expect(cloneModal.getByText(/bruno collections found/i)).toBeVisible({ timeout: 120000 });

const selectAll = cloneModal.getByLabel('Select all');
const collectionCheckboxes = cloneModal.locator('ul > li label input[type="checkbox"]');

await expect(selectAll).toBeVisible();
const checkboxCount = await collectionCheckboxes.count();
expect(checkboxCount).toBe(3);

await selectAll.check();
for (let i = 0; i < checkboxCount; i++) {
await expect(collectionCheckboxes.nth(i)).toBeChecked();
}

await selectAll.uncheck();
for (let i = 0; i < checkboxCount; i++) {
await expect(collectionCheckboxes.nth(i)).not.toBeChecked();
}

await collectionCheckboxes.first().check();
const isIndeterminate = await selectAll.evaluate((el) => (el as HTMLInputElement).indeterminate);
expect(isIndeterminate).toBe(true);

await page.getByRole('button', { name: 'Cancel' }).click();
});
});