mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 14:35:03 +00:00
* feat(workspaces): implement active workspace restoration from snapshot - Added a new action to restore the active workspace from a snapshot. - Integrated workspace restoration into IPC events for improved user experience. - Refactored workspace loading logic to prioritize valid workspaces and handle invalid paths. - Introduced utility functions for workspace path validation and normalization. - Added tests to ensure correct functionality of workspace restoration and validation logic. * feat(snapshot): refactor snapshot serialization and add environment preservation logic - Moved snapshot serialization logic to a new file for better organization. - Implemented environment preservation for collections during snapshot serialization. - Added utility functions to handle collection environment checks and serialization. - Introduced tests to validate the new serialization behavior and environment preservation. - Enhanced existing tests to cover edge cases for unmounted collections and environment persistence. * Add tests for missing and invalid environment files * feat(snapshot): refactor devTools normalization and add saveToken to IPC handler * refactor(snapshot): remove saveToken from IPC handler for renderer:snapshot:save * feat(snapshot): implement collection deduplication logic in SnapshotManager and add corresponding tests * feat(snapshot): enhance workspace-specific environment handling for shared collections * feat(snapshot): remove sourceWorkspacePathname from snapshot serialization and related tests * feat(snapshot): remove sourceWorkspacePathname from SnapshotManager and add tests for shared collection environment restoration * feat(snapshot): refactor collection deduplication tests and enhance snapshot seeding utility * feat(snapshot): enhance workspace path normalization and improve snapshot environment tests --------- Co-authored-by: engineering-bruno <engineering@usebruno.com>
143 lines
5.7 KiB
TypeScript
143 lines
5.7 KiB
TypeScript
import path from 'path';
|
|
import fs from 'fs';
|
|
import { test, expect, closeElectronApp } from '../../playwright';
|
|
import { waitForReadyPage } from '../utils/page';
|
|
import { readSnapshot, findSnapshotCollectionEntry, seedSnapshotWithCollections } from '../utils/snapshot';
|
|
|
|
test.describe('Snapshot: Collection Deduplication', () => {
|
|
test('duplicate collection entries for the same pathname are deduplicated, with the latest entry taking precedence', async ({ launchElectronApp, createTmpDir }) => {
|
|
const userDataPath = await createTmpDir('snap-collection-dedup');
|
|
const colPath = await createTmpDir('col');
|
|
|
|
fs.writeFileSync(path.join(colPath, 'bruno.json'), JSON.stringify({ version: '1', name: 'TestCol', type: 'collection' }, null, 2));
|
|
|
|
seedSnapshotWithCollections(userDataPath, [
|
|
// Older entry: has a selected environment and an open tab, but is not marked open/mounted.
|
|
{
|
|
pathname: colPath,
|
|
workspacePathname: '',
|
|
environment: {
|
|
collection: 'local.yml',
|
|
global: ''
|
|
},
|
|
environmentPath: 'local.yml',
|
|
selectedEnvironment: 'local',
|
|
isOpen: false,
|
|
isMounted: false,
|
|
activeTab: {
|
|
accessor: 'pathname',
|
|
value: path.join(colPath, 'ReqAlpha.bru')
|
|
},
|
|
tabs: [
|
|
{
|
|
type: 'http-request',
|
|
accessor: 'pathname',
|
|
pathname: path.join(colPath, 'ReqAlpha.bru'),
|
|
permanent: true
|
|
}
|
|
]
|
|
},
|
|
// Newer entry: same pathname, marks the collection open/mounted but omits the environment/tabs
|
|
// that the older entry had set. The newer entry should win on the fields it specifies,
|
|
// while fields it leaves unspecified should fall back to the older entry.
|
|
{
|
|
pathname: colPath,
|
|
workspacePathname: '',
|
|
isOpen: true,
|
|
isMounted: true
|
|
}
|
|
]);
|
|
|
|
await test.step('Assert seeded snapshot has two entries for the same collection before launch', () => {
|
|
const snapshot = readSnapshot(userDataPath);
|
|
const entries = snapshot.collections.filter((c: any) => path.normalize(c.pathname) === path.normalize(colPath));
|
|
expect(entries).toHaveLength(2);
|
|
});
|
|
|
|
const app = await launchElectronApp({ userDataPath });
|
|
const page = await waitForReadyPage(app);
|
|
|
|
await expect(page.getByTestId('workspace-name')).toBeVisible({ timeout: 10000 });
|
|
|
|
await test.step('Close app and verify the snapshot is deduplicated to a single, merged entry', async () => {
|
|
await page.waitForTimeout(2000);
|
|
await closeElectronApp(app);
|
|
|
|
const snapshot = readSnapshot(userDataPath);
|
|
const entries = snapshot.collections.filter((c: any) => path.normalize(c.pathname) === path.normalize(colPath));
|
|
expect(entries).toHaveLength(1);
|
|
|
|
const entry = findSnapshotCollectionEntry(snapshot, colPath);
|
|
|
|
// Fields set by the newer entry win.
|
|
expect(entry.isOpen).toBe(true);
|
|
expect(entry.isMounted).toBe(true);
|
|
|
|
// Fields not touched by the newer entry are preserved from the older entry.
|
|
expect(entry.selectedEnvironment).toBe('local');
|
|
expect(entry.environment.collection).toBe('local.yml');
|
|
expect(entry.tabs).toHaveLength(1);
|
|
expect(entry.tabs[0].pathname).toBe(path.join(colPath, 'ReqAlpha.bru'));
|
|
});
|
|
});
|
|
|
|
test('collection entries with the same pathname but different workspacePathname are not deduplicated', async ({ launchElectronApp, createTmpDir }) => {
|
|
const userDataPath = await createTmpDir('snap-collection-dedup-diff-workspace');
|
|
const colPath = await createTmpDir('col');
|
|
const workspaceAPath = await createTmpDir('workspace-a');
|
|
const workspaceBPath = await createTmpDir('workspace-b');
|
|
|
|
fs.writeFileSync(path.join(colPath, 'bruno.json'), JSON.stringify({ version: '1', name: 'TestCol', type: 'collection' }, null, 2));
|
|
|
|
seedSnapshotWithCollections(
|
|
userDataPath,
|
|
[
|
|
// Same collection pathname, but scoped to workspace A.
|
|
{
|
|
pathname: colPath,
|
|
workspacePathname: workspaceAPath,
|
|
isOpen: true,
|
|
isMounted: true
|
|
},
|
|
// Same collection pathname, but scoped to workspace B. Distinct entry since the
|
|
// workspacePathname differs - it should NOT be merged/removed by dedup.
|
|
{
|
|
pathname: colPath,
|
|
workspacePathname: workspaceBPath,
|
|
isOpen: false,
|
|
isMounted: false
|
|
}
|
|
],
|
|
{ workspaces: [{ pathname: workspaceAPath }, { pathname: workspaceBPath }] }
|
|
);
|
|
|
|
await test.step('Assert seeded snapshot has two entries for the same collection pathname before launch', () => {
|
|
const snapshot = readSnapshot(userDataPath);
|
|
const entries = snapshot.collections.filter((c: any) => path.normalize(c.pathname) === path.normalize(colPath));
|
|
expect(entries).toHaveLength(2);
|
|
});
|
|
|
|
const app = await launchElectronApp({ userDataPath });
|
|
const page = await waitForReadyPage(app);
|
|
|
|
await expect(page.getByTestId('workspace-name')).toBeVisible({ timeout: 10000 });
|
|
|
|
await test.step('Close app and verify both workspace-scoped entries are preserved', async () => {
|
|
await page.waitForTimeout(2000);
|
|
await closeElectronApp(app);
|
|
|
|
const snapshot = readSnapshot(userDataPath);
|
|
const entries = snapshot.collections.filter((c: any) => path.normalize(c.pathname) === path.normalize(colPath));
|
|
expect(entries).toHaveLength(2);
|
|
|
|
const entryA = findSnapshotCollectionEntry(snapshot, colPath, workspaceAPath);
|
|
const entryB = findSnapshotCollectionEntry(snapshot, colPath, workspaceBPath);
|
|
|
|
expect(entryA).not.toBeNull();
|
|
expect(entryB).not.toBeNull();
|
|
expect(entryA.isOpen).toBe(true);
|
|
expect(entryB.isOpen).toBe(false);
|
|
});
|
|
});
|
|
});
|