mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
feat: error-boundary + crash cache addition (#8056)
* internal emit chain for clearance * fix: crash ui * fix(ErrorBoundary): ensure cache clearing is awaited before force quitting * test(e2e): environment persistence across collections * test: migration test * Update environment.spec.ts * fix: reduce padding for dark mode app errors * chore: re-add waitForReadyPage
This commit is contained in:
@@ -6,7 +6,7 @@ class ErrorBoundary extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = { hasError: false };
|
this.state = { hasError: false, clearCaches: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -21,6 +21,10 @@ class ErrorBoundary extends React.Component {
|
|||||||
this.setState({ hasError: true, error, errorInfo });
|
this.setState({ hasError: true, error, errorInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearCache() {
|
||||||
|
await window.ipcRenderer.invoke('main:cache-clear');
|
||||||
|
}
|
||||||
|
|
||||||
returnToApp() {
|
returnToApp() {
|
||||||
const { ipcRenderer } = window;
|
const { ipcRenderer } = window;
|
||||||
ipcRenderer.invoke('open-file');
|
ipcRenderer.invoke('open-file');
|
||||||
@@ -36,7 +40,7 @@ class ErrorBoundary extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
return (
|
return (
|
||||||
<div className="flex text-center justify-center p-20 h-full">
|
<div className="flex text-center justify-center p-10 h-full">
|
||||||
<div className="bg-white rounded-lg p-10 w-full">
|
<div className="bg-white rounded-lg p-10 w-full">
|
||||||
<div className="m-auto" style={{ width: '256px' }}>
|
<div className="m-auto" style={{ width: '256px' }}>
|
||||||
<Bruno width={256} />
|
<Bruno width={256} />
|
||||||
@@ -63,8 +67,30 @@ class ErrorBoundary extends React.Component {
|
|||||||
Return to App
|
Return to App
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="text-red-500 mt-3">
|
<div className="mt-5 pt-4 border-t border-gray-100 flex flex-col items-center gap-2">
|
||||||
<a href="" className="hover:underline cursor-pointer" onClick={this.forceQuit}>
|
<label className="flex items-center gap-2 text-sm text-gray-600 cursor-pointer select-none hover:text-gray-800 transition">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.clearCaches}
|
||||||
|
onChange={(e) => this.setState({ clearCaches: e.target.checked })}
|
||||||
|
className="cursor-pointer"
|
||||||
|
/>
|
||||||
|
Clear caches on quit
|
||||||
|
</label>
|
||||||
|
<a
|
||||||
|
href=""
|
||||||
|
className="text-sm text-red-400 border border-red-400 hover:text-red-600 px-4 py-2 rounded transition cursor-pointer"
|
||||||
|
onClick={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
if (this.state.clearCaches) {
|
||||||
|
await this.clearCache();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.forceQuit();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
Force Quit
|
Force Quit
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ const serializeSnapshot = async (state) => {
|
|||||||
|
|
||||||
const workspacePathname = activeWorkspace?.pathname || '';
|
const workspacePathname = activeWorkspace?.pathname || '';
|
||||||
const collectionSnapshotKey = getWorkspaceCollectionSnapshotKey(workspacePathname, collection.pathname);
|
const collectionSnapshotKey = getWorkspaceCollectionSnapshotKey(workspacePathname, collection.pathname);
|
||||||
|
const existingCollection = (collectionSnapshotKey && existingSnapshotLookups.collectionsByWorkspaceAndPath?.[collectionSnapshotKey])
|
||||||
|
|| existingSnapshotLookups.collectionsByPath?.[normalizedPath]
|
||||||
|
|| null;
|
||||||
if (collectionSnapshotKey) {
|
if (collectionSnapshotKey) {
|
||||||
serializedCollectionKeys.add(collectionSnapshotKey);
|
serializedCollectionKeys.add(collectionSnapshotKey);
|
||||||
}
|
}
|
||||||
@@ -174,7 +177,17 @@ const serializeSnapshot = async (state) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const selectedEnvironment = (collection.environments || []).find((env) => env.uid === collection.activeEnvironmentUid);
|
const selectedEnvironment = (collection.environments || []).find((env) => env.uid === collection.activeEnvironmentUid);
|
||||||
const environmentPath = getCollectionEnvironmentPath(collection, selectedEnvironment, '');
|
const environmentPathFromRedux = getCollectionEnvironmentPath(collection, selectedEnvironment, '');
|
||||||
|
const selectedEnvironmentFromRedux = selectedEnvironment?.name || '';
|
||||||
|
const existingEnvironmentPath = existingCollection?.environment?.collection || existingCollection?.environmentPath || '';
|
||||||
|
const existingSelectedEnvironment = existingCollection?.selectedEnvironment || '';
|
||||||
|
const shouldPreserveExistingEnvironment = collection.mountStatus !== 'mounted'
|
||||||
|
&& !environmentPathFromRedux
|
||||||
|
&& !selectedEnvironmentFromRedux;
|
||||||
|
const environmentPath = shouldPreserveExistingEnvironment ? existingEnvironmentPath : environmentPathFromRedux;
|
||||||
|
const selectedEnvironmentName = shouldPreserveExistingEnvironment
|
||||||
|
? existingSelectedEnvironment
|
||||||
|
: selectedEnvironmentFromRedux;
|
||||||
|
|
||||||
snapshot.collections.push({
|
snapshot.collections.push({
|
||||||
pathname: collection.pathname,
|
pathname: collection.pathname,
|
||||||
@@ -184,7 +197,7 @@ const serializeSnapshot = async (state) => {
|
|||||||
global: globalEnvironments.activeGlobalEnvironmentUid || ''
|
global: globalEnvironments.activeGlobalEnvironmentUid || ''
|
||||||
},
|
},
|
||||||
environmentPath,
|
environmentPath,
|
||||||
selectedEnvironment: selectedEnvironment?.name || '',
|
selectedEnvironment: selectedEnvironmentName,
|
||||||
isOpen: !collection.collapsed,
|
isOpen: !collection.collapsed,
|
||||||
isMounted: collection.mountStatus === 'mounted',
|
isMounted: collection.mountStatus === 'mounted',
|
||||||
activeTab: serializeActiveTab(activeTabInCollection, collection),
|
activeTab: serializeActiveTab(activeTabInCollection, collection),
|
||||||
|
|||||||
@@ -471,6 +471,11 @@ app.on('ready', async () => {
|
|||||||
registerSystemMonitorIpc(mainWindow, systemMonitor);
|
registerSystemMonitorIpc(mainWindow, systemMonitor);
|
||||||
registerGitIpc(mainWindow);
|
registerGitIpc(mainWindow);
|
||||||
registerOpenAPISyncIpc(mainWindow);
|
registerOpenAPISyncIpc(mainWindow);
|
||||||
|
|
||||||
|
// Internal delegator
|
||||||
|
ipcMain.handle('main:cache-clear', async () => {
|
||||||
|
ipcMain.emit('internal:snapshot:reset');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quit the app once all windows are closed
|
// Quit the app once all windows are closed
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ const registerSnapshotIpc = () => {
|
|||||||
return snapshotManager.getTabs(collectionPathname, workspacePathname);
|
return snapshotManager.getTabs(collectionPathname, workspacePathname);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('internal:snapshot:reset', () => {
|
||||||
|
try {
|
||||||
|
snapshotManager.resetSnapshot();
|
||||||
|
} catch (err) {
|
||||||
|
// digest error if reset fails
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('renderer:snapshot:save', async (event, data) => {
|
ipcMain.handle('renderer:snapshot:save', async (event, data) => {
|
||||||
return snapshotManager.saveSnapshot(data);
|
return snapshotManager.saveSnapshot(data);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -187,6 +187,23 @@ class SnapshotManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetSnapshot() {
|
||||||
|
this.store.delete('activeWorkspacePath');
|
||||||
|
this.store.set('workspaces', (this.store.store?.workspaces ?? []).map((d) => {
|
||||||
|
d.lastActiveCollectionPathname = undefined;
|
||||||
|
return d;
|
||||||
|
}));
|
||||||
|
this.store.set('collections', (this.store.store?.collections ?? []).map((d) => {
|
||||||
|
if ('tabs' in d) {
|
||||||
|
d.tabs = [];
|
||||||
|
}
|
||||||
|
if ('activeTab' in d) {
|
||||||
|
d.activeTab = undefined;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
setCollection(pathname, data) {
|
setCollection(pathname, data) {
|
||||||
const normalizedPath = normalizeLookupKey(pathname);
|
const normalizedPath = normalizeLookupKey(pathname);
|
||||||
if (!normalizedPath) {
|
if (!normalizedPath) {
|
||||||
|
|||||||
@@ -31,6 +31,28 @@ function isTracingEnabled(testInfo: TestInfo): boolean {
|
|||||||
return !!(testInfo as any)._tracing.traceOptions();
|
return !!(testInfo as any)._tracing.traceOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for the Electron app to have a ready, loaded window.
|
||||||
|
// Handles cases where the first window is slow to appear (e.g. on Windows).
|
||||||
|
export async function waitForReadyPage(app: ElectronApplication, options: { timeout?: number } = {}): Promise<Page> {
|
||||||
|
const { timeout = 45000 } = options;
|
||||||
|
|
||||||
|
let page: Page | null = null;
|
||||||
|
try {
|
||||||
|
page = await app.firstWindow();
|
||||||
|
} catch {
|
||||||
|
page = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!page) {
|
||||||
|
page = await app.waitForEvent('window', { timeout });
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.locator('[data-app-state="loaded"]').waitFor({ timeout });
|
||||||
|
await page.waitForTimeout(200);
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
async function usePageWithTracing(
|
async function usePageWithTracing(
|
||||||
context: BrowserContext,
|
context: BrowserContext,
|
||||||
page: Page,
|
page: Page,
|
||||||
|
|||||||
220
tests/snapshots/environment/environment.spec.ts
Normal file
220
tests/snapshots/environment/environment.spec.ts
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { test, expect, closeElectronApp } from '../../../playwright';
|
||||||
|
import {
|
||||||
|
createCollection,
|
||||||
|
createEnvironment,
|
||||||
|
openCollection,
|
||||||
|
selectEnvironment,
|
||||||
|
waitForReadyPage
|
||||||
|
} from '../../utils/page';
|
||||||
|
|
||||||
|
const readSnapshot = (userDataPath: string) => {
|
||||||
|
const snapshotPath = path.join(userDataPath, 'ui-state-snapshot.json');
|
||||||
|
if (!fs.existsSync(snapshotPath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(fs.readFileSync(snapshotPath, 'utf-8'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const legacyPromptVariablesInitUserDataPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'init-user-data'
|
||||||
|
);
|
||||||
|
|
||||||
|
const migrationCollectionPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'fixtures/collection'
|
||||||
|
);
|
||||||
|
|
||||||
|
test.describe('Snapshot: Collection Environment Persistence', () => {
|
||||||
|
test('migrates legacy snapshot format and preserves selected collection environment', async ({ launchElectronApp, createTmpDir }) => {
|
||||||
|
const userDataPath = await createTmpDir('snap-legacy-env-migration');
|
||||||
|
|
||||||
|
const app = await launchElectronApp({
|
||||||
|
initUserDataPath: legacyPromptVariablesInitUserDataPath,
|
||||||
|
userDataPath
|
||||||
|
});
|
||||||
|
const page = await waitForReadyPage(app);
|
||||||
|
|
||||||
|
await test.step('Verify legacy selected environment is hydrated in UI', async () => {
|
||||||
|
await openCollection(page, 'migration-collection');
|
||||||
|
await expect(page.locator('.current-environment')).toContainText('local');
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Close app and verify snapshot migrated to new shape', async () => {
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
await closeElectronApp(app);
|
||||||
|
|
||||||
|
const snapshot = readSnapshot(userDataPath);
|
||||||
|
expect(snapshot).not.toBeNull();
|
||||||
|
expect(snapshot).toHaveProperty('version');
|
||||||
|
expect(snapshot).toHaveProperty('activeWorkspacePath');
|
||||||
|
expect(snapshot).toHaveProperty('extras');
|
||||||
|
expect(snapshot).toHaveProperty('workspaces');
|
||||||
|
expect(snapshot).toHaveProperty('collections');
|
||||||
|
expect(Array.isArray(snapshot?.workspaces)).toBe(true);
|
||||||
|
expect(Array.isArray(snapshot?.collections)).toBe(true);
|
||||||
|
|
||||||
|
const migratedCollectionEntry = snapshot?.collections?.find(
|
||||||
|
(collection: any) => collection?.pathname === migrationCollectionPath
|
||||||
|
);
|
||||||
|
expect(migratedCollectionEntry).toBeTruthy();
|
||||||
|
console.log(JSON.stringify(migratedCollectionEntry));
|
||||||
|
|
||||||
|
expect(migratedCollectionEntry?.selectedEnvironment).toBe('local');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('keeps selected environments for non-active collections across snapshot saves', async ({ launchElectronApp, createTmpDir }) => {
|
||||||
|
const userDataPath = await createTmpDir('snap-env-persistence');
|
||||||
|
const firstCollectionPath = await createTmpDir('snap-col-a');
|
||||||
|
const secondCollectionPath = await createTmpDir('snap-col-b');
|
||||||
|
const firstCollectionRoot = path.join(firstCollectionPath, 'Collection A');
|
||||||
|
const secondCollectionRoot = path.join(secondCollectionPath, 'Collection B');
|
||||||
|
|
||||||
|
const app = await launchElectronApp({ userDataPath });
|
||||||
|
const page = await waitForReadyPage(app);
|
||||||
|
|
||||||
|
await test.step('Create two collections with distinct selected environments', async () => {
|
||||||
|
await createCollection(page, 'Collection A', firstCollectionPath);
|
||||||
|
await openCollection(page, 'Collection A');
|
||||||
|
await createEnvironment(page, 'local-a', 'collection');
|
||||||
|
await selectEnvironment(page, 'local-a', 'collection');
|
||||||
|
|
||||||
|
await createCollection(page, 'Collection B', secondCollectionPath);
|
||||||
|
await openCollection(page, 'Collection B');
|
||||||
|
await createEnvironment(page, 'local-b', 'collection');
|
||||||
|
await selectEnvironment(page, 'local-b', 'collection');
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Switch back to first collection and verify environment did not drift', async () => {
|
||||||
|
await openCollection(page, 'Collection A');
|
||||||
|
await expect(page.locator('.current-environment')).toContainText('local-a');
|
||||||
|
await openCollection(page, 'Collection B');
|
||||||
|
await expect(page.locator('.current-environment')).toContainText('local-b');
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Close app and assert snapshot stores both environments', async () => {
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
await closeElectronApp(app);
|
||||||
|
|
||||||
|
const snapshot = readSnapshot(userDataPath);
|
||||||
|
expect(snapshot).not.toBeNull();
|
||||||
|
|
||||||
|
const collections = Array.isArray(snapshot?.collections) ? snapshot.collections : [];
|
||||||
|
const firstEntry = collections.find((collection: any) => collection?.pathname === firstCollectionRoot);
|
||||||
|
const secondEntry = collections.find((collection: any) => collection?.pathname === secondCollectionRoot);
|
||||||
|
|
||||||
|
expect(firstEntry?.selectedEnvironment).toBe('local-a');
|
||||||
|
expect(secondEntry?.selectedEnvironment).toBe('local-b');
|
||||||
|
expect(firstEntry?.environmentPath).toContain(path.join('environments', 'local-a'));
|
||||||
|
expect(secondEntry?.environmentPath).toContain(path.join('environments', 'local-b'));
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Restart app and verify both selections are still restored', async () => {
|
||||||
|
const app2 = await launchElectronApp({ userDataPath });
|
||||||
|
const page2 = await waitForReadyPage(app2);
|
||||||
|
|
||||||
|
await openCollection(page2, 'Collection A');
|
||||||
|
await expect(page2.locator('.current-environment')).toContainText('local-a');
|
||||||
|
|
||||||
|
await openCollection(page2, 'Collection B');
|
||||||
|
await expect(page2.locator('.current-environment')).toContainText('local-b');
|
||||||
|
|
||||||
|
await closeElectronApp(app2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('keeps selected environments for three collections across delayed switches and snapshot updates', async ({ launchElectronApp, createTmpDir }) => {
|
||||||
|
const userDataPath = await createTmpDir('snap-env-persistence-three');
|
||||||
|
const firstCollectionPath = await createTmpDir('snap-col-a-three');
|
||||||
|
const secondCollectionPath = await createTmpDir('snap-col-b-three');
|
||||||
|
const thirdCollectionPath = await createTmpDir('snap-col-c-three');
|
||||||
|
const firstCollectionRoot = path.join(firstCollectionPath, 'Collection A');
|
||||||
|
const secondCollectionRoot = path.join(secondCollectionPath, 'Collection B');
|
||||||
|
const thirdCollectionRoot = path.join(thirdCollectionPath, 'Collection C');
|
||||||
|
|
||||||
|
const app = await launchElectronApp({ userDataPath });
|
||||||
|
const page = await waitForReadyPage(app);
|
||||||
|
|
||||||
|
await test.step('Create three collections with distinct selected environments', async () => {
|
||||||
|
await createCollection(page, 'Collection A', firstCollectionPath);
|
||||||
|
await openCollection(page, 'Collection A');
|
||||||
|
await createEnvironment(page, 'local-a', 'collection');
|
||||||
|
await selectEnvironment(page, 'local-a', 'collection');
|
||||||
|
|
||||||
|
await createCollection(page, 'Collection B', secondCollectionPath);
|
||||||
|
await openCollection(page, 'Collection B');
|
||||||
|
await createEnvironment(page, 'local-b', 'collection');
|
||||||
|
await selectEnvironment(page, 'local-b', 'collection');
|
||||||
|
|
||||||
|
await createCollection(page, 'Collection C', thirdCollectionPath);
|
||||||
|
await openCollection(page, 'Collection C');
|
||||||
|
await createEnvironment(page, 'local-c', 'collection');
|
||||||
|
await selectEnvironment(page, 'local-c', 'collection');
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Switch to each collection with delays and verify selected environment stays correct', async () => {
|
||||||
|
await openCollection(page, 'Collection A');
|
||||||
|
await expect(page.locator('.current-environment')).toContainText('local-a');
|
||||||
|
|
||||||
|
await openCollection(page, 'Collection B');
|
||||||
|
await expect(page.locator('.current-environment')).toContainText('local-b');
|
||||||
|
|
||||||
|
await openCollection(page, 'Collection C');
|
||||||
|
await expect(page.locator('.current-environment')).toContainText('local-c');
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Close app and assert snapshot stores all three environments', async () => {
|
||||||
|
await closeElectronApp(app);
|
||||||
|
|
||||||
|
const snapshot = readSnapshot(userDataPath);
|
||||||
|
expect(snapshot).not.toBeNull();
|
||||||
|
|
||||||
|
const collections = Array.isArray(snapshot?.collections) ? snapshot.collections : [];
|
||||||
|
const firstEntry = collections.find((collection: any) => collection?.pathname === firstCollectionRoot);
|
||||||
|
const secondEntry = collections.find((collection: any) => collection?.pathname === secondCollectionRoot);
|
||||||
|
const thirdEntry = collections.find((collection: any) => collection?.pathname === thirdCollectionRoot);
|
||||||
|
|
||||||
|
expect(firstEntry?.selectedEnvironment).toBe('local-a');
|
||||||
|
expect(secondEntry?.selectedEnvironment).toBe('local-b');
|
||||||
|
expect(thirdEntry?.selectedEnvironment).toBe('local-c');
|
||||||
|
expect(firstEntry?.environmentPath).toContain(path.join('environments', 'local-a'));
|
||||||
|
expect(secondEntry?.environmentPath).toContain(path.join('environments', 'local-b'));
|
||||||
|
expect(thirdEntry?.environmentPath).toContain(path.join('environments', 'local-c'));
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step('Restart app, switch through collections with delays, and verify all selections are restored', async () => {
|
||||||
|
const app2 = await launchElectronApp({ userDataPath });
|
||||||
|
const page2 = await waitForReadyPage(app2);
|
||||||
|
|
||||||
|
await openCollection(page2, 'Collection A');
|
||||||
|
await expect(page2.locator('.current-environment')).toContainText('local-a');
|
||||||
|
await page2.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await openCollection(page2, 'Collection B');
|
||||||
|
await expect(page2.locator('.current-environment')).toContainText('local-b');
|
||||||
|
await page2.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await openCollection(page2, 'Collection C');
|
||||||
|
await expect(page2.locator('.current-environment')).toContainText('local-c');
|
||||||
|
await page2.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await closeElectronApp(app2);
|
||||||
|
|
||||||
|
const updatedSnapshot = readSnapshot(userDataPath);
|
||||||
|
expect(updatedSnapshot).not.toBeNull();
|
||||||
|
|
||||||
|
const updatedCollections = Array.isArray(updatedSnapshot?.collections) ? updatedSnapshot.collections : [];
|
||||||
|
const firstUpdatedEntry = updatedCollections.find((collection: any) => collection?.pathname === firstCollectionRoot);
|
||||||
|
const secondUpdatedEntry = updatedCollections.find((collection: any) => collection?.pathname === secondCollectionRoot);
|
||||||
|
const thirdUpdatedEntry = updatedCollections.find((collection: any) => collection?.pathname === thirdCollectionRoot);
|
||||||
|
|
||||||
|
expect(firstUpdatedEntry?.selectedEnvironment).toBe('local-a');
|
||||||
|
expect(secondUpdatedEntry?.selectedEnvironment).toBe('local-b');
|
||||||
|
expect(thirdUpdatedEntry?.selectedEnvironment).toBe('local-c');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
10
tests/snapshots/environment/fixtures/collection/bruno.json
Normal file
10
tests/snapshots/environment/fixtures/collection/bruno.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "migration-collection",
|
||||||
|
"type": "collection",
|
||||||
|
"ignore": [
|
||||||
|
"node_modules",
|
||||||
|
".git"
|
||||||
|
],
|
||||||
|
"filesCount": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
vars {
|
||||||
|
collectionEnvVar: hello
|
||||||
|
~collectionEnvVarDisabled: there
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
meta {
|
||||||
|
name: http-request
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:8081/ping
|
||||||
|
}
|
||||||
12
tests/snapshots/environment/init-user-data/preferences.json
Normal file
12
tests/snapshots/environment/init-user-data/preferences.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"maximized": false,
|
||||||
|
"lastOpenedCollections": [
|
||||||
|
"{{projectRoot}}/tests/snapshots/environment/fixtures/collection"
|
||||||
|
],
|
||||||
|
"preferences": {
|
||||||
|
"onboarding": {
|
||||||
|
"hasLaunchedBefore": true,
|
||||||
|
"hasSeenWelcomeModal": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"collections": [
|
||||||
|
{
|
||||||
|
"pathname": "{{projectRoot}}/tests/snapshots/environment/fixtures/collection",
|
||||||
|
"selectedEnvironment": "local"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
import { test, expect, Page } from '../../../playwright';
|
import { test, expect, Page, ElectronApplication, waitForReadyPage as waitForReadyPageImpl } from '../../../playwright';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { buildCommonLocators, buildScriptErrorLocators } from './locators';
|
import { buildCommonLocators, buildScriptErrorLocators } from './locators';
|
||||||
|
|
||||||
type SandboxMode = 'safe' | 'developer';
|
type SandboxMode = 'safe' | 'developer';
|
||||||
|
|
||||||
|
type WaitForAppReadyOptions = {
|
||||||
|
timeout?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const waitForReadyPage = (
|
||||||
|
app: ElectronApplication,
|
||||||
|
options: WaitForAppReadyOptions = {}
|
||||||
|
) => waitForReadyPageImpl(app, options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close all collections
|
* Close all collections
|
||||||
* @param page - The page object
|
* @param page - The page object
|
||||||
@@ -1274,6 +1283,7 @@ const openExampleFromSidebar = async (page: Page, requestName: string, exampleNa
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
waitForReadyPage,
|
||||||
closeAllCollections,
|
closeAllCollections,
|
||||||
openCollection,
|
openCollection,
|
||||||
createCollection,
|
createCollection,
|
||||||
|
|||||||
Reference in New Issue
Block a user