mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-24 21:25:45 +00:00
add: tests
This commit is contained in:
@@ -78,11 +78,10 @@ class DefaultWorkspaceManager {
|
||||
};
|
||||
}
|
||||
|
||||
// Need to create/recreate default workspace
|
||||
this.initializationPromise = (async () => {
|
||||
try {
|
||||
const shouldMigrate = this.needsMigration();
|
||||
const newWorkspacePath = await this.initializeDefaultWorkspace(existingPath, { migrateFromPreferences: shouldMigrate });
|
||||
const newWorkspacePath = await this.initializeDefaultWorkspace({ migrateFromPreferences: shouldMigrate });
|
||||
|
||||
return {
|
||||
workspacePath: newWorkspacePath,
|
||||
@@ -99,21 +98,17 @@ class DefaultWorkspaceManager {
|
||||
return this.initializationPromise;
|
||||
}
|
||||
|
||||
async initializeDefaultWorkspace(existingPath = null, options = {}) {
|
||||
async initializeDefaultWorkspace(options = {}) {
|
||||
const { migrateFromPreferences = true } = options;
|
||||
|
||||
let workspacePath = existingPath;
|
||||
const configDir = app.getPath('userData');
|
||||
const baseWorkspacePath = path.join(configDir, 'default-workspace');
|
||||
|
||||
if (!workspacePath || !fs.existsSync(workspacePath)) {
|
||||
const configDir = app.getPath('userData');
|
||||
const baseWorkspacePath = path.join(configDir, 'default-workspace');
|
||||
|
||||
workspacePath = baseWorkspacePath;
|
||||
let counter = 1;
|
||||
while (fs.existsSync(workspacePath)) {
|
||||
workspacePath = `${baseWorkspacePath}-${counter}`;
|
||||
counter++;
|
||||
}
|
||||
let workspacePath = baseWorkspacePath;
|
||||
let counter = 1;
|
||||
while (fs.existsSync(workspacePath)) {
|
||||
workspacePath = `${baseWorkspacePath}-${counter}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
fs.mkdirSync(workspacePath, { recursive: true });
|
||||
|
||||
222
tests/workspace/default-workspace/default-workspace.spec.ts
Normal file
222
tests/workspace/default-workspace/default-workspace.spec.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { test, expect } from '../../../playwright';
|
||||
|
||||
test.describe('Default Workspace', () => {
|
||||
test.describe('First Launch', () => {
|
||||
test('should create default workspace with "My Workspace" name on first launch', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-first-launch');
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
// Verify the workspace name is "My Workspace" in the title bar
|
||||
const workspaceName = page.locator('.workspace-name');
|
||||
await expect(workspaceName).toContainText('My Workspace');
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Persistence', () => {
|
||||
test('should persist default workspace across app restarts', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-persistence');
|
||||
|
||||
// First launch
|
||||
const app1 = await launchElectronApp({ userDataPath });
|
||||
const page1 = await app1.firstWindow();
|
||||
await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await expect(page1.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
await app1.close();
|
||||
|
||||
// Second launch - same workspace should be loaded
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
const page2 = await app2.firstWindow();
|
||||
await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await expect(page2.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
await app2.close();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Recovery - Creates NEW workspace (never modifies existing)', () => {
|
||||
test('should create NEW workspace when existing workspace.yml is deleted', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-recovery-deleted');
|
||||
|
||||
// Create a corrupted default workspace BEFORE launching app
|
||||
const defaultWorkspacePath = path.join(userDataPath, 'default-workspace');
|
||||
fs.mkdirSync(defaultWorkspacePath, { recursive: true });
|
||||
fs.mkdirSync(path.join(defaultWorkspacePath, 'collections'), { recursive: true });
|
||||
// Note: NOT creating workspace.yml - simulating deleted file
|
||||
|
||||
// Create preferences pointing to the corrupted workspace
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
general: {
|
||||
defaultWorkspacePath: defaultWorkspacePath
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app - should create NEW workspace
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
// Should show "My Workspace"
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Old directory should still exist (never deleted)
|
||||
expect(fs.existsSync(defaultWorkspacePath)).toBe(true);
|
||||
|
||||
// New workspace directory should have been created (default-workspace-1 since default-workspace exists)
|
||||
const newWorkspacePath = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
expect(fs.existsSync(path.join(newWorkspacePath, 'workspace.yml'))).toBe(true);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should create NEW workspace when workspace.yml has invalid YAML', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-recovery-invalid');
|
||||
|
||||
// Create workspace with invalid YAML BEFORE launching app
|
||||
const defaultWorkspacePath = path.join(userDataPath, 'default-workspace');
|
||||
fs.mkdirSync(defaultWorkspacePath, { recursive: true });
|
||||
fs.writeFileSync(path.join(defaultWorkspacePath, 'workspace.yml'), 'invalid: yaml: [[[');
|
||||
|
||||
// Create preferences pointing to the corrupted workspace
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
general: {
|
||||
defaultWorkspacePath: defaultWorkspacePath
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app - should create NEW workspace
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Old corrupted file should still exist (never deleted)
|
||||
const oldContent = fs.readFileSync(path.join(defaultWorkspacePath, 'workspace.yml'), 'utf8');
|
||||
expect(oldContent).toContain('invalid: yaml: [[[');
|
||||
|
||||
// New workspace should have been created
|
||||
const newWorkspacePath = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should create NEW workspace when workspace.yml has wrong type', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-recovery-wrong-type');
|
||||
|
||||
// Create workspace with wrong type BEFORE launching app
|
||||
const defaultWorkspacePath = path.join(userDataPath, 'default-workspace');
|
||||
fs.mkdirSync(defaultWorkspacePath, { recursive: true });
|
||||
fs.writeFileSync(path.join(defaultWorkspacePath, 'workspace.yml'), `opencollection: 1.0.0
|
||||
info:
|
||||
name: My Workspace
|
||||
type: collection
|
||||
collections:
|
||||
specs:
|
||||
docs: ''
|
||||
`);
|
||||
|
||||
// Create preferences pointing to the invalid workspace
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
general: {
|
||||
defaultWorkspacePath: defaultWorkspacePath
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// New workspace should have been created
|
||||
const newWorkspacePath = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should create NEW workspace when directory does not exist', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-recovery-dir-missing');
|
||||
|
||||
// Create preferences pointing to non-existent directory
|
||||
const nonExistentPath = path.join(userDataPath, 'non-existent-workspace');
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
general: {
|
||||
defaultWorkspacePath: nonExistentPath
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// New workspace should have been created (default-workspace since non-existent doesn't block)
|
||||
const newWorkspacePath = path.join(userDataPath, 'default-workspace');
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
expect(fs.existsSync(path.join(newWorkspacePath, 'workspace.yml'))).toBe(true);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('UI Behavior', () => {
|
||||
test('should display default workspace in workspace dropdown', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-ui-dropdown');
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
// Click on workspace name to open dropdown
|
||||
await page.locator('.workspace-name-container').click();
|
||||
|
||||
// Verify default workspace is shown
|
||||
const workspaceItem = page.locator('.workspace-item, .dropdown-item').filter({ hasText: 'My Workspace' });
|
||||
await expect(workspaceItem.first()).toBeVisible();
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should not show pin button for default workspace', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-ui-no-pin');
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await page.locator('.workspace-name-container').click();
|
||||
|
||||
const workspaceItem = page.locator('.workspace-item').filter({ hasText: 'My Workspace' });
|
||||
// Default workspace should NOT have pin button
|
||||
await expect(workspaceItem.locator('.pin-btn')).not.toBeVisible();
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
204
tests/workspace/default-workspace/migration.spec.ts
Normal file
204
tests/workspace/default-workspace/migration.spec.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { test, expect } from '../../../playwright';
|
||||
|
||||
const env = {
|
||||
DISABLE_SAMPLE_COLLECTION_IMPORT: 'false'
|
||||
};
|
||||
|
||||
test.describe('Default Workspace Migration', () => {
|
||||
test.describe('Migration from lastOpenedCollections', () => {
|
||||
test('should migrate collections from lastOpenedCollections to new workspace', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-migration');
|
||||
|
||||
// Create a test collection that would have been opened before
|
||||
const testCollectionPath = path.join(userDataPath, 'my-old-collection');
|
||||
fs.mkdirSync(testCollectionPath, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(testCollectionPath, 'bruno.json'),
|
||||
JSON.stringify({
|
||||
version: '1',
|
||||
name: 'My Old Collection',
|
||||
type: 'collection'
|
||||
})
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
lastOpenedCollections: [testCollectionPath]
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app - should migrate
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
// Verify default workspace is created with correct name
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Verify workspace was created
|
||||
const workspacePath = path.join(userDataPath, 'default-workspace');
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
|
||||
// Verify workspace.yml exists and contains the migrated collection
|
||||
const workspaceYmlPath = path.join(workspacePath, 'workspace.yml');
|
||||
if (fs.existsSync(workspaceYmlPath)) {
|
||||
const workspaceYml = fs.readFileSync(workspaceYmlPath, 'utf8');
|
||||
expect(workspaceYml).toContain('collections:');
|
||||
// Collection should be referenced
|
||||
expect(workspaceYml).toContain('my-old-collection');
|
||||
}
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should migrate multiple collections from lastOpenedCollections', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-migration-multiple');
|
||||
|
||||
// Create multiple test collections
|
||||
const collection1Path = path.join(userDataPath, 'collection-1');
|
||||
const collection2Path = path.join(userDataPath, 'collection-2');
|
||||
|
||||
for (const collPath of [collection1Path, collection2Path]) {
|
||||
fs.mkdirSync(collPath, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(collPath, 'bruno.json'),
|
||||
JSON.stringify({
|
||||
version: '1',
|
||||
name: path.basename(collPath),
|
||||
type: 'collection'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Create old-style preferences
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
lastOpenedCollections: [collection1Path, collection2Path]
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Verify workspace.yml has both collections
|
||||
const workspacePath = path.join(userDataPath, 'default-workspace');
|
||||
const workspaceYmlPath = path.join(workspacePath, 'workspace.yml');
|
||||
if (fs.existsSync(workspaceYmlPath)) {
|
||||
const workspaceYml = fs.readFileSync(workspaceYmlPath, 'utf8');
|
||||
expect(workspaceYml).toContain('collection-1');
|
||||
expect(workspaceYml).toContain('collection-2');
|
||||
}
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Migration does not affect existing users', () => {
|
||||
test('should skip sample collection when user has existing collections', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-existing-user');
|
||||
|
||||
// Create a test collection (simulating existing user)
|
||||
const oldCollectionPath = path.join(userDataPath, 'old-user-collection');
|
||||
fs.mkdirSync(oldCollectionPath, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(oldCollectionPath, 'bruno.json'),
|
||||
JSON.stringify({
|
||||
version: '1',
|
||||
name: 'Old User Collection',
|
||||
type: 'collection'
|
||||
})
|
||||
);
|
||||
|
||||
// Create old-style preferences with lastOpenedCollections
|
||||
fs.writeFileSync(
|
||||
path.join(userDataPath, 'preferences.json'),
|
||||
JSON.stringify({
|
||||
lastOpenedCollections: [oldCollectionPath]
|
||||
})
|
||||
);
|
||||
|
||||
// Launch app - sample collection should NOT be created (existing user)
|
||||
const app = await launchElectronApp({ userDataPath, dotEnv: env });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
// Verify default workspace is created
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Sample collection should NOT be created (because user has existing collections)
|
||||
const sampleCollection = page.locator('#sidebar-collection-name').getByText('Sample API Collection');
|
||||
await expect(sampleCollection).not.toBeVisible();
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('No duplicate workspaces on restart', () => {
|
||||
test('should reuse existing workspace on subsequent launches', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-reuse');
|
||||
|
||||
// First launch - creates workspace
|
||||
const app1 = await launchElectronApp({ userDataPath });
|
||||
const page1 = await app1.firstWindow();
|
||||
await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await expect(page1.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Verify initial workspace was created
|
||||
const workspacePath = path.join(userDataPath, 'default-workspace');
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
const originalYmlContent = fs.readFileSync(path.join(workspacePath, 'workspace.yml'), 'utf8');
|
||||
|
||||
await app1.close();
|
||||
|
||||
// Second launch - should reuse existing workspace
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
const page2 = await app2.firstWindow();
|
||||
await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await expect(page2.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// workspace.yml should NOT have been modified
|
||||
const currentYmlContent = fs.readFileSync(path.join(workspacePath, 'workspace.yml'), 'utf8');
|
||||
expect(currentYmlContent).toBe(originalYmlContent);
|
||||
|
||||
// No new workspace should have been created
|
||||
expect(fs.existsSync(path.join(userDataPath, 'default-workspace-1'))).toBe(false);
|
||||
|
||||
await app2.close();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Clean installation', () => {
|
||||
test('should create empty workspace on fresh install without old preferences', async ({ launchElectronApp, createTmpDir }) => {
|
||||
const userDataPath = await createTmpDir('default-workspace-clean');
|
||||
|
||||
// Launch with completely empty user data (no preferences file)
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
|
||||
await expect(page.locator('.workspace-name')).toContainText('My Workspace');
|
||||
|
||||
// Verify workspace was created
|
||||
const workspacePath = path.join(userDataPath, 'default-workspace');
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
|
||||
// Verify workspace has empty collections section
|
||||
const workspaceYmlPath = path.join(workspacePath, 'workspace.yml');
|
||||
if (fs.existsSync(workspaceYmlPath)) {
|
||||
const workspaceYml = fs.readFileSync(workspaceYmlPath, 'utf8');
|
||||
// Collections should be empty (just the key)
|
||||
expect(workspaceYml).toMatch(/collections:\s*\n/);
|
||||
}
|
||||
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user