mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-28 15:14:06 +00:00
* fix: normalize workspace paths during workspace switch to prevent stale state * chore: test text * tests(snapshot): more workspace coverage
268 lines
13 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|