Files
bruno/tests/snapshots/workspace.spec.ts
Sid cea883eda2 fix(snapshot): normalize renderer:get-last-opened-workspaces output to avoid reactivating a deleted workspace (#8033)
* fix: normalize workspace paths during workspace switch to prevent stale state

* chore: test text

* tests(snapshot): more workspace coverage
2026-05-18 21:24:58 +05:30

268 lines
13 KiB
TypeScript

import path from 'path';
import { test, expect, closeElectronApp } from '../../playwright';
import {
createCollection,
createRequest,
openRequest,
openWorkspaceFromDialog,
waitForReadyPage
} from '../utils/page';
import fs from 'fs';
const buildWorkspaceYml = (workspaceName: string) => [
'opencollection: 1.0.0',
'info:',
` name: ${workspaceName}`,
' type: workspace',
'collections:',
'specs: []',
'docs: \'\'',
''
].join('\n');
test.describe('Snapshot: Deleted Workspace Restoration', () => {
test('falls back to default workspace when saved workspace is deleted', async ({ launchElectronApp, createTmpDir }) => {
const userDataPath = await createTmpDir('snap-workspace-state');
const workspacePath = await createTmpDir('demo-workspace');
const defaultCollectionPath = await createTmpDir('default-workspace-col');
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), buildWorkspaceYml('Demo Workspace'));
const app = await launchElectronApp({ userDataPath });
const page = await waitForReadyPage(app);
await test.step('Create a collection in default workspace and mount it', async () => {
await createCollection(page, 'Default Workspace Col', defaultCollectionPath);
await createRequest(page, 'Default Workspace Req', 'Default Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Default Workspace Col', 'Default Workspace Req', { persist: true });
await expect(page.getByRole('tab', { name: 'Default Workspace Req' })).toBeVisible({ timeout: 10000 });
});
await test.step('Open Demo Workspace and switch to it', async () => {
await openWorkspaceFromDialog(app, page, workspacePath);
await expect(page.getByTestId('workspace-name')).toHaveText('Demo Workspace', { timeout: 10000 });
});
await test.step('Close and restart app', async () => {
await page.waitForTimeout(2000);
await closeElectronApp(app);
});
await test.step('Open after deleting workspace', async () => {
await fs.promises.rm(workspacePath, { force: true, recursive: true });
const app2 = await launchElectronApp({ userDataPath });
const page2 = await waitForReadyPage(app2);
await expect(page2.getByTestId('workspace-name')).toHaveText('My Workspace', { timeout: 10000 });
await expect(page2.getByTestId('sidebar-collection-row').filter({ hasText: 'Default Workspace Col' })).toBeVisible({ timeout: 10000 });
await openRequest(page2, 'Default Workspace Col', 'Default Workspace Req');
await expect(page2.getByRole('tab', { name: 'Default Workspace Req' })).toBeVisible({ timeout: 10000 });
await page2.getByTestId('workspace-menu').click();
await expect(page2.locator('.workspace-item.active')).toContainText('My Workspace');
await expect(page2.locator('.workspace-item').filter({ hasText: 'Demo Workspace' })).toHaveCount(0);
await closeElectronApp(app2);
});
});
test('falls back to default workspace when saved workspace exists but workspace.yml is missing', async ({ launchElectronApp, createTmpDir }) => {
const userDataPath = await createTmpDir('snap-workspace-missing-yml');
const workspacePath = await createTmpDir('demo-workspace-missing-yml');
const defaultCollectionPath = await createTmpDir('default-workspace-col-missing-yml');
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), buildWorkspaceYml('Demo Workspace'));
const app = await launchElectronApp({ userDataPath });
const page = await waitForReadyPage(app);
await test.step('Create collection and request in default workspace', async () => {
await createCollection(page, 'Default Workspace Col', defaultCollectionPath);
await createRequest(page, 'Default Workspace Req', 'Default Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Default Workspace Col', 'Default Workspace Req', { persist: true });
await expect(page.getByRole('tab', { name: 'Default Workspace Req' })).toBeVisible({ timeout: 10000 });
});
await test.step('Switch to demo workspace and restart', async () => {
await openWorkspaceFromDialog(app, page, workspacePath);
await expect(page.getByTestId('workspace-name')).toHaveText('Demo Workspace', { timeout: 10000 });
await page.waitForTimeout(2000);
await closeElectronApp(app);
});
await test.step('Delete only workspace.yml and verify fallback', async () => {
await fs.promises.unlink(path.join(workspacePath, 'workspace.yml'));
const app2 = await launchElectronApp({ userDataPath });
const page2 = await waitForReadyPage(app2);
await expect(page2.getByTestId('workspace-name')).toHaveText('My Workspace', { timeout: 10000 });
await expect(page2.getByTestId('sidebar-collection-row').filter({ hasText: 'Default Workspace Col' })).toBeVisible({ timeout: 10000 });
await page2.getByTestId('workspace-menu').click();
await expect(page2.locator('.workspace-item.active')).toContainText('My Workspace');
await expect(page2.locator('.workspace-item').filter({ hasText: 'Demo Workspace' })).toHaveCount(0);
await closeElectronApp(app2);
});
});
test('falls back to default workspace when saved workspace.yml is malformed', async ({ launchElectronApp, createTmpDir }) => {
const userDataPath = await createTmpDir('snap-workspace-malformed-yml');
const workspacePath = await createTmpDir('demo-workspace-malformed-yml');
const defaultCollectionPath = await createTmpDir('default-workspace-col-malformed-yml');
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), buildWorkspaceYml('Demo Workspace'));
const app = await launchElectronApp({ userDataPath });
const page = await waitForReadyPage(app);
await test.step('Create collection and request in default workspace', async () => {
await createCollection(page, 'Default Workspace Col', defaultCollectionPath);
await createRequest(page, 'Default Workspace Req', 'Default Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Default Workspace Col', 'Default Workspace Req', { persist: true });
});
await test.step('Switch to demo workspace and restart', async () => {
await openWorkspaceFromDialog(app, page, workspacePath);
await expect(page.getByTestId('workspace-name')).toHaveText('Demo Workspace', { timeout: 10000 });
await page.waitForTimeout(2000);
await closeElectronApp(app);
});
await test.step('Corrupt workspace.yml and verify fallback', async () => {
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), 'invalid: yaml: [[[');
const app2 = await launchElectronApp({ userDataPath });
const page2 = await waitForReadyPage(app2);
await expect(page2.getByTestId('workspace-name')).toHaveText('My Workspace', { timeout: 10000 });
await openRequest(page2, 'Default Workspace Col', 'Default Workspace Req');
await expect(page2.getByRole('tab', { name: 'Default Workspace Req' })).toBeVisible({ timeout: 10000 });
await page2.getByTestId('workspace-menu').click();
await expect(page2.locator('.workspace-item').filter({ hasText: 'Demo Workspace' })).toHaveCount(0);
await closeElectronApp(app2);
});
});
test('does not restore stale tabs from deleted workspace and remains interactive', async ({ launchElectronApp, createTmpDir }) => {
const userDataPath = await createTmpDir('snap-workspace-stale-tabs-deleted');
const workspacePath = await createTmpDir('demo-workspace-stale-tabs');
const defaultCollectionPath = await createTmpDir('default-workspace-col-stale-tabs');
const deletedWorkspaceCollectionPath = await createTmpDir('deleted-workspace-col');
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), buildWorkspaceYml('Demo Workspace'));
const app = await launchElectronApp({ userDataPath });
const page = await waitForReadyPage(app);
await test.step('Create request in default workspace', async () => {
await createCollection(page, 'Default Workspace Col', defaultCollectionPath);
await createRequest(page, 'Default Workspace Req', 'Default Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Default Workspace Col', 'Default Workspace Req', { persist: true });
});
await test.step('Switch to demo workspace and open a request there', async () => {
await openWorkspaceFromDialog(app, page, workspacePath);
await expect(page.getByTestId('workspace-name')).toHaveText('Demo Workspace', { timeout: 10000 });
await createCollection(page, 'Deleted Workspace Col', deletedWorkspaceCollectionPath);
await createRequest(page, 'Deleted Workspace Req', 'Deleted Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Deleted Workspace Col', 'Deleted Workspace Req', { persist: true });
await expect(page.getByRole('tab', { name: 'Deleted Workspace Req' })).toBeVisible({ timeout: 10000 });
});
await test.step('Close app, delete active workspace, and verify stale tab is not restored', async () => {
await page.waitForTimeout(2000);
await closeElectronApp(app);
await fs.promises.rm(workspacePath, { recursive: true, force: true });
const app2 = await launchElectronApp({ userDataPath });
const page2 = await waitForReadyPage(app2);
await expect(page2.getByTestId('workspace-name')).toHaveText('My Workspace', { timeout: 10000 });
await expect(page2.getByRole('tab', { name: 'Deleted Workspace Req' })).toHaveCount(0);
await openRequest(page2, 'Default Workspace Col', 'Default Workspace Req');
await expect(page2.getByRole('tab', { name: 'Default Workspace Req' })).toBeVisible({ timeout: 10000 });
await closeElectronApp(app2);
});
});
test('falls back when active workspace and active tab belong to malformed workspace snapshot', async ({ launchElectronApp, createTmpDir }) => {
const userDataPath = await createTmpDir('snap-workspace-malformed-with-active-tab');
const workspacePath = await createTmpDir('demo-workspace-malformed-active-tab');
const defaultCollectionPath = await createTmpDir('default-workspace-col-malformed-active-tab');
const malformedWorkspaceCollectionPath = await createTmpDir('malformed-workspace-col');
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), buildWorkspaceYml('Demo Workspace'));
const app = await launchElectronApp({ userDataPath });
const page = await waitForReadyPage(app);
await test.step('Create default workspace request', async () => {
await createCollection(page, 'Default Workspace Col', defaultCollectionPath);
await createRequest(page, 'Default Workspace Req', 'Default Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Default Workspace Col', 'Default Workspace Req', { persist: true });
});
await test.step('Switch to demo workspace, create active request, and close app', async () => {
await openWorkspaceFromDialog(app, page, workspacePath);
await expect(page.getByTestId('workspace-name')).toHaveText('Demo Workspace', { timeout: 10000 });
await createCollection(page, 'Malformed Workspace Col', malformedWorkspaceCollectionPath);
await createRequest(page, 'Malformed Workspace Req', 'Malformed Workspace Col', {
url: 'https://echo.usebruno.com',
method: 'GET'
});
await openRequest(page, 'Malformed Workspace Col', 'Malformed Workspace Req', { persist: true });
await expect(page.getByRole('tab', { name: 'Malformed Workspace Req' })).toBeVisible({ timeout: 10000 });
await page.waitForTimeout(2000);
await closeElectronApp(app);
});
await test.step('Corrupt workspace config and verify app recovers to default workspace', async () => {
fs.writeFileSync(path.join(workspacePath, 'workspace.yml'), 'broken: [[[');
const app2 = await launchElectronApp({ userDataPath });
const page2 = await waitForReadyPage(app2);
await expect(page2.getByTestId('workspace-name')).toHaveText('My Workspace', { timeout: 10000 });
await expect(page2.getByRole('tab', { name: 'Malformed Workspace Req' })).toHaveCount(0);
await page2.getByTestId('workspace-menu').click();
await expect(page2.locator('.workspace-item.active')).toContainText('My Workspace');
await expect(page2.locator('.workspace-item').filter({ hasText: 'Demo Workspace' })).toHaveCount(0);
await page2.keyboard.press('Escape');
await openRequest(page2, 'Default Workspace Col', 'Default Workspace Req');
await expect(page2.getByRole('tab', { name: 'Default Workspace Req' })).toBeVisible({ timeout: 10000 });
await closeElectronApp(app2);
});
});
});