mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
chore: fix flaky tests
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { test as baseTest, BrowserContext, ElectronApplication, Page } from '@playwright/test';
|
||||
import { test as baseTest, BrowserContext, ElectronApplication, Page, TestInfo } from '@playwright/test';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
@@ -25,6 +25,71 @@ async function recursiveCopy(src: string, dest: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const TRACING_OPTIONS = { screenshots: true, snapshots: true, sources: true };
|
||||
|
||||
function isTracingEnabled(testInfo: TestInfo): boolean {
|
||||
return !!(testInfo as any)._tracing.traceOptions();
|
||||
}
|
||||
|
||||
async function usePageWithTracing(
|
||||
context: BrowserContext,
|
||||
page: Page,
|
||||
testInfo: TestInfo,
|
||||
use: (page: Page) => Promise<void>,
|
||||
options: { initTracing?: boolean; useChunks?: boolean } = {}
|
||||
) {
|
||||
const { initTracing = false, useChunks = true } = options;
|
||||
|
||||
if (!isTracingEnabled(testInfo)) {
|
||||
await use(page);
|
||||
return;
|
||||
}
|
||||
|
||||
const tracePath = testInfo.outputPath(`trace-${testInfo.testId}.zip`);
|
||||
|
||||
if (initTracing) {
|
||||
try {
|
||||
await context.tracing.start(TRACING_OPTIONS);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
if (useChunks) {
|
||||
await context.tracing.startChunk();
|
||||
await use(page);
|
||||
await context.tracing.stopChunk({ path: tracePath });
|
||||
} else {
|
||||
await use(page);
|
||||
await context.tracing.stop({ path: tracePath });
|
||||
}
|
||||
|
||||
await testInfo.attach('trace', { path: tracePath });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully close an Electron app by telling it to exit with code 0.
|
||||
* This avoids the macOS "quit unexpectedly" crash dialog that appears when
|
||||
* app.context().close() kills subprocesses (renderer/GPU) abruptly before
|
||||
* the main process can shut down cleanly.
|
||||
*
|
||||
* Emits 'before-quit' first so cleanup handlers run (e.g., saving cookies to disk),
|
||||
* since app.exit() bypasses all lifecycle events.
|
||||
*/
|
||||
export async function closeElectronApp(app: ElectronApplication) {
|
||||
try {
|
||||
await app.evaluate(({ app }) => {
|
||||
app.emit('before-quit');
|
||||
app.exit(0);
|
||||
});
|
||||
} catch {
|
||||
// Expected: process exited before the CDP response was sent
|
||||
}
|
||||
try {
|
||||
await app.close();
|
||||
} catch {
|
||||
// Process already exited
|
||||
}
|
||||
}
|
||||
|
||||
export const test = baseTest.extend<
|
||||
{
|
||||
context: BrowserContext;
|
||||
@@ -113,8 +178,7 @@ export const test = baseTest.extend<
|
||||
return app;
|
||||
});
|
||||
for (const app of apps) {
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
},
|
||||
{ scope: 'worker' }
|
||||
@@ -130,10 +194,9 @@ export const test = baseTest.extend<
|
||||
|
||||
context: async ({ electronApp }, use, testInfo) => {
|
||||
const context = await electronApp.context();
|
||||
const tracingOptions = (testInfo as any)._tracing.traceOptions();
|
||||
if (tracingOptions) {
|
||||
if (isTracingEnabled(testInfo)) {
|
||||
try {
|
||||
await context.tracing.start({ screenshots: true, snapshots: true, sources: true });
|
||||
await context.tracing.start(TRACING_OPTIONS);
|
||||
} catch (e) { }
|
||||
}
|
||||
await use(context);
|
||||
@@ -141,32 +204,14 @@ export const test = baseTest.extend<
|
||||
|
||||
page: async ({ electronApp, context }, use, testInfo) => {
|
||||
const page = await electronApp.firstWindow();
|
||||
const tracingOptions = (testInfo as any)._tracing.traceOptions();
|
||||
if (tracingOptions) {
|
||||
const tracePath = testInfo.outputPath(`trace-${testInfo.testId}.zip`);
|
||||
await context.tracing.startChunk();
|
||||
await use(page);
|
||||
await context.tracing.stopChunk({ path: tracePath });
|
||||
await testInfo.attach('trace', { path: tracePath });
|
||||
} else {
|
||||
await use(page);
|
||||
}
|
||||
await usePageWithTracing(context, page, testInfo, use);
|
||||
},
|
||||
|
||||
newPage: async ({ launchElectronApp }, use, testInfo) => {
|
||||
const app = await launchElectronApp();
|
||||
const context = await app.context();
|
||||
const page = await app.firstWindow();
|
||||
const tracingOptions = (testInfo as any)._tracing.traceOptions();
|
||||
if (tracingOptions) {
|
||||
const tracePath = testInfo.outputPath(`trace-${testInfo.testId}.zip`);
|
||||
await context.tracing.start({ screenshots: true, snapshots: true, sources: true });
|
||||
await use(page);
|
||||
await context.tracing.stop({ path: tracePath });
|
||||
await testInfo.attach('trace', { path: tracePath });
|
||||
} else {
|
||||
await use(page);
|
||||
}
|
||||
await usePageWithTracing(context, page, testInfo, use, { initTracing: true, useChunks: false });
|
||||
},
|
||||
|
||||
reuseOrLaunchElectronApp: [
|
||||
@@ -208,8 +253,7 @@ export const test = baseTest.extend<
|
||||
|
||||
// Clean up all app instances
|
||||
for (const { app } of appInstances) {
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -231,19 +275,7 @@ export const test = baseTest.extend<
|
||||
|
||||
const context = await app.context();
|
||||
const page = await app.firstWindow();
|
||||
const tracingOptions = (testInfo as any)._tracing.traceOptions();
|
||||
if (tracingOptions) {
|
||||
const tracePath = testInfo.outputPath(`trace-${testInfo.testId}.zip`);
|
||||
try {
|
||||
await context.tracing.start({ screenshots: true, snapshots: true, sources: true });
|
||||
} catch (e) { }
|
||||
await context.tracing.startChunk();
|
||||
await use(page);
|
||||
await context.tracing.stopChunk({ path: tracePath });
|
||||
await testInfo.attach('trace', { path: tracePath });
|
||||
} else {
|
||||
await use(page);
|
||||
}
|
||||
await usePageWithTracing(context, page, testInfo, use, { initTracing: true });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ test.describe('Draft values are used in requests', () => {
|
||||
|
||||
const nameEditor = headerRow.locator('.CodeMirror').first();
|
||||
await nameEditor.click();
|
||||
await page.keyboard.type('X-Draft-Header');
|
||||
await headerRow.locator('textarea').first().fill('X-Draft-Header');
|
||||
|
||||
const valueEditor = headerRow.locator('.CodeMirror').nth(1);
|
||||
await valueEditor.click();
|
||||
await page.keyboard.type('draft-value-123');
|
||||
await headerRow.locator('textarea').nth(1).fill('draft-value-123');
|
||||
|
||||
// Verify draft indicator appears (header is not saved yet)
|
||||
const collectionTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Collection' }) });
|
||||
@@ -51,11 +51,11 @@ test.describe('Draft values are used in requests', () => {
|
||||
|
||||
const folderNameEditor = folderHeaderRow.locator('.CodeMirror').first();
|
||||
await folderNameEditor.click();
|
||||
await page.keyboard.type('X-Folder-Draft-Header');
|
||||
await folderHeaderRow.locator('textarea').first().fill('X-Folder-Draft-Header');
|
||||
|
||||
const folderValueEditor = folderHeaderRow.locator('.CodeMirror').nth(1);
|
||||
await folderValueEditor.click();
|
||||
await page.keyboard.type('folder-draft-value-123');
|
||||
await folderHeaderRow.locator('textarea').nth(1).fill('folder-draft-value-123');
|
||||
|
||||
// Create a request in the collection
|
||||
// Create a new request via collection menu
|
||||
@@ -122,7 +122,7 @@ test.describe('Draft values are used in requests', () => {
|
||||
// Create a new request from collection menu
|
||||
const collection = page.locator('.collection-name').filter({ hasText: collectionName });
|
||||
await collection.hover();
|
||||
await collection.locator('.collection-actions .icon').click();
|
||||
await collection.locator('.collection-actions .icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
|
||||
await page.getByTestId('request-name').fill('Test Request');
|
||||
await page.getByTestId('new-request-url').locator('.CodeMirror').click();
|
||||
|
||||
@@ -29,7 +29,7 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
|
||||
|
||||
// Add a request to the folder to make it more realistic
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).hover();
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).locator('.menu-icon').click();
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'test-folder' }).locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
|
||||
await page.getByPlaceholder('Request Name').fill('test-request-in-folder');
|
||||
await page.locator('#new-request-url .CodeMirror').click();
|
||||
@@ -126,7 +126,7 @@ test.describe('Cross-Collection Drag and Drop for folder', () => {
|
||||
|
||||
// Add a request to the folder to make it more realistic
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).hover();
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).locator('.menu-icon').click();
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'folder-1' }).locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
|
||||
await page.getByPlaceholder('Request Name').fill('http-request');
|
||||
await page.locator('#new-request-url .CodeMirror').click();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { test, expect, closeElectronApp } from '../../playwright';
|
||||
|
||||
test('should persist cookies across app restarts', async ({ createTmpDir, launchElectronApp }) => {
|
||||
// Create a temporary user-data directory so we control where the cookies store file is written.
|
||||
@@ -26,7 +26,7 @@ test('should persist cookies across app restarts', async ({ createTmpDir, launch
|
||||
|
||||
await expect(page1.getByText('example.com')).toBeVisible();
|
||||
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Second launch – verify the cookie was persisted and re-loaded
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
@@ -39,5 +39,5 @@ test('should persist cookies across app restarts', async ({ createTmpDir, launch
|
||||
// The domain we added earlier should still be present.
|
||||
await expect(page2.getByText('example.com')).toBeVisible();
|
||||
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { test, expect, closeElectronApp } from '../../playwright';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs/promises';
|
||||
|
||||
@@ -24,7 +24,7 @@ test('should handle corrupted passkey and still display saved cookie list', asyn
|
||||
|
||||
await expect(page1.getByText('example.com')).toBeVisible();
|
||||
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// 2. Corrupt the encryptedPasskey in cookies.json
|
||||
const cookiesFilePath = path.join(userDataPath, 'cookies.json');
|
||||
@@ -43,5 +43,5 @@ test('should handle corrupted passkey and still display saved cookie list', asyn
|
||||
// The domain row should still be visible (even if cookie values are blank).
|
||||
await expect(page2.getByText('example.com')).toBeVisible();
|
||||
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => {
|
||||
await expect(page.getByRole('row', { name: 'token' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
await expect(page.getByRole('row', { name: 'secret' }).getByRole('cell').nth(2)).toBeVisible();
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
// we restart the app to confirm that the environment variable is persisted
|
||||
const newApp = await restartApp();
|
||||
@@ -59,7 +59,7 @@ test.describe.serial('bru.setEnvVar(name, value, { persist: true })', () => {
|
||||
await expect(newPage.getByRole('row', { name: 'secret' }).getByRole('cell').nth(2)).toBeVisible();
|
||||
|
||||
await newEnvTab.hover();
|
||||
await newEnvTab.getByTestId('request-tab-close-icon').click();
|
||||
await newEnvTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
// Restore the original Stage.bru file
|
||||
fs.writeFileSync(originalStageBruPath, originalStageBruContent);
|
||||
|
||||
@@ -28,7 +28,7 @@ test.describe.serial('bru.setEnvVar(name, value)', () => {
|
||||
await expect(page.getByRole('row', { name: 'token' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
await expect(page.getByRole('row', { name: 'secret' }).getByRole('cell').nth(2)).toBeVisible();
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
// we restart the app to confirm that the environment variable is not persisted
|
||||
const newApp = await restartApp();
|
||||
@@ -48,7 +48,7 @@ test.describe.serial('bru.setEnvVar(name, value)', () => {
|
||||
await expect(newPage.locator('.table-container tbody')).not.toContainText('token');
|
||||
|
||||
await newEnvTab.hover();
|
||||
await newEnvTab.getByTestId('request-tab-close-icon').click();
|
||||
await newEnvTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await newPage.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ test.describe.serial('bru.setEnvVar multiple persistent variables', () => {
|
||||
}
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore cleanup errors to avoid masking test failures
|
||||
@@ -85,7 +85,7 @@ test.describe.serial('bru.setEnvVar multiple persistent variables', () => {
|
||||
await expect(page.getByRole('row', { name: 'multiple-persist-vars-key2' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
await expect(page.getByRole('row', { name: 'value2' }).getByRole('cell').nth(2)).toBeVisible();
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
|
||||
await test.step('Verify variables are persisted to file', async () => {
|
||||
|
||||
@@ -35,6 +35,6 @@ test.describe('Collection Environment Configuration Selection Tests', () => {
|
||||
await expect(activeEnvItem).toContainText('prod');
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,6 @@ test.describe('Global Environment Configuration Selection Tests', () => {
|
||||
await expect(activeEnvItem).toContainText(currentEnvName);
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +47,7 @@ test.describe.serial('Collection Environment Import Tests', () => {
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
|
||||
await test.step('Clean up after test', async () => {
|
||||
@@ -128,7 +128,7 @@ test.describe.serial('Collection Environment Import Tests', () => {
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
|
||||
await test.step('Clean up after test', async () => {
|
||||
|
||||
@@ -62,7 +62,7 @@ test.describe.serial('Global Environment Import Tests', () => {
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -145,7 +145,7 @@ test.describe.serial('Global Environment Import Tests', () => {
|
||||
await expect(page.getByRole('row', { name: 'secretToken' }).getByRole('cell').nth(1)).toBeVisible();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,7 +62,7 @@ test.describe('Collection Environment Import Tests', () => {
|
||||
await expect(page.locator('input[name="5.name"]')).toHaveValue('secretApiToken');
|
||||
await expect(page.locator('input[name="5.secret"]')).toBeChecked();
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
await page.locator('.collection-item-name').first().click();
|
||||
await expect(page.locator('#request-url .CodeMirror-line')).toContainText('{{host}}/posts/{{userId}}');
|
||||
|
||||
@@ -57,7 +57,7 @@ test.describe('Global Environment Import Tests', () => {
|
||||
await expect(variablesTable.locator('input[name="5.name"]')).toHaveValue('secretApiToken');
|
||||
await expect(variablesTable.locator('input[name="5.secret"]')).toBeChecked();
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
await page.locator('#collection-environment-test-collection .collection-item-name').first().click();
|
||||
await expect(page.locator('#request-url .CodeMirror-line')).toContainText('{{host}}/posts/{{userId}}');
|
||||
|
||||
@@ -33,6 +33,12 @@ test.describe('Multiline Variables - Write Test', () => {
|
||||
await expect(emptyRowNameInput).toBeVisible();
|
||||
await emptyRowNameInput.fill('multiline_data_json');
|
||||
|
||||
// After filling the name, the table appends a new empty row causing persistent layout shifts.
|
||||
// Use force:true to bypass Playwright's stability check on the CodeMirror click.
|
||||
const variableRow = page.locator('tbody tr').filter({ has: page.locator('input[value="multiline_data_json"]') });
|
||||
await expect(variableRow).toBeVisible();
|
||||
const codeMirror = variableRow.locator('.CodeMirror');
|
||||
|
||||
const jsonValue = `{
|
||||
"user": {
|
||||
"name": "John Doe",
|
||||
@@ -48,15 +54,13 @@ test.describe('Multiline Variables - Write Test', () => {
|
||||
}
|
||||
}`;
|
||||
|
||||
const variableRow = page.locator('tbody tr').filter({ has: page.locator('input[value="multiline_data_json"]') });
|
||||
const codeMirror = variableRow.locator('.CodeMirror');
|
||||
await codeMirror.click();
|
||||
await codeMirror.click({ force: true });
|
||||
await page.keyboard.insertText(jsonValue);
|
||||
|
||||
await page.getByTestId('save-env').click();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ test.describe('Global Environment Variable Update via Script', () => {
|
||||
await test.step('Close the global environment config tab.', async () => {
|
||||
const envTab = page.locator('.request-tab').filter({ hasText: 'Global Environments' });
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ test.describe('Global Environment Variables - Non-string Values', () => {
|
||||
await page.getByTestId('save-env').click();
|
||||
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
|
||||
// Request contains a script that sets the non-string global variables.
|
||||
@@ -124,7 +124,7 @@ test.describe('Global Environment Variables - Non-string Values', () => {
|
||||
|
||||
const envTab = page.locator('.request-tab').filter({ hasText: 'Global Environments' });
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Grpc Collection - Method Search Functionality', () => {
|
||||
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
await test.step('Close the gRPC sayHello tab without saving changes', async () => {
|
||||
await page.getByRole('tab', { name: 'gRPC sayHello' }).getByTestId('request-tab-close-icon').click();
|
||||
await page.getByRole('tab', { name: 'gRPC sayHello' }).getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await page.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -200,7 +200,7 @@ test.describe('Import Insomnia v4 Collection - Environment Import', () => {
|
||||
await test.step('Close environment tab', async () => {
|
||||
const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' });
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -225,7 +225,7 @@ test.describe('Import Insomnia v5 Collection - Environment Import', () => {
|
||||
await test.step('Close environment tab', async () => {
|
||||
const envTab = page.locator('.request-tab').filter({ hasText: 'Environments' });
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { test, expect, errors } from '../../playwright';
|
||||
import { test, expect, errors, closeElectronApp } from '../../playwright';
|
||||
|
||||
const env = {
|
||||
DISABLE_SAMPLE_COLLECTION_IMPORT: 'false'
|
||||
@@ -28,7 +28,7 @@ test.describe('Onboarding', () => {
|
||||
await expect(page.locator('#request-url')).toContainText('https://jsonplaceholder.typicode.com/users');
|
||||
|
||||
// Clean up
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should not create duplicate collections on subsequent launches', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -51,7 +51,7 @@ test.describe('Onboarding', () => {
|
||||
await expect(page.locator('#request-url')).toContainText('https://jsonplaceholder.typicode.com/users');
|
||||
|
||||
// Close the first app instance
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
|
||||
// Restart app - should not create sample collection again
|
||||
const newApp = await launchElectronApp({ userDataPath, dotEnv: env });
|
||||
@@ -71,7 +71,7 @@ test.describe('Onboarding', () => {
|
||||
await expect(newPage.locator('#request-url')).toContainText('https://jsonplaceholder.typicode.com/users');
|
||||
|
||||
// Clean up
|
||||
await newApp.close();
|
||||
await closeElectronApp(newApp);
|
||||
});
|
||||
|
||||
test('should not recreate sample collection after user deletes it', async ({ launchElectronApp, reuseOrLaunchElectronApp, createTmpDir }) => {
|
||||
|
||||
@@ -52,7 +52,7 @@ test.describe('Autosave', () => {
|
||||
// Close preferences tab using the close icon
|
||||
const preferencesTab = page.locator('.request-tab').filter({ hasText: 'Preferences' });
|
||||
await preferencesTab.hover();
|
||||
await preferencesTab.locator('.close-icon').click();
|
||||
await preferencesTab.locator('.close-icon').click({ force: true });
|
||||
|
||||
// Click on the request to make it active again
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'Test Request' }).click();
|
||||
@@ -77,7 +77,7 @@ test.describe('Autosave', () => {
|
||||
// Close and reopen the request tab to verify persistence
|
||||
const requestTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Test Request' }) });
|
||||
await requestTab.hover();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
// Reopen request
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'Test Request' }).click();
|
||||
@@ -108,7 +108,7 @@ test.describe('Autosave', () => {
|
||||
// Close preferences tab using the close icon
|
||||
const preferencesTab = page.locator('.request-tab').filter({ hasText: 'Preferences' });
|
||||
await preferencesTab.hover();
|
||||
await preferencesTab.locator('.close-icon').click();
|
||||
await preferencesTab.locator('.close-icon').click({ force: true });
|
||||
|
||||
// Click on the request to make it active again
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'Test Request' }).click();
|
||||
@@ -192,7 +192,7 @@ test.describe('Autosave', () => {
|
||||
// Close preferences tab using the close icon
|
||||
const preferencesTab = page.locator('.request-tab').filter({ hasText: 'Preferences' });
|
||||
await preferencesTab.hover();
|
||||
await preferencesTab.locator('.close-icon').click();
|
||||
await preferencesTab.locator('.close-icon').click({ force: true });
|
||||
|
||||
// Click on the request to make it active again
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'Draft Request' }).click();
|
||||
@@ -207,7 +207,7 @@ test.describe('Autosave', () => {
|
||||
// Close and reopen the request tab to verify persistence
|
||||
const requestTab = page.locator('.request-tab').filter({ has: page.locator('.tab-label', { hasText: 'Draft Request' }) });
|
||||
await requestTab.hover();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
|
||||
// Reopen request
|
||||
await page.locator('.collection-item-name').filter({ hasText: 'Draft Request' }).click();
|
||||
|
||||
@@ -108,7 +108,7 @@ test.describe('manage protofile', () => {
|
||||
await method.click();
|
||||
const requestTab = page.getByRole('tab', { name: 'gRPC sayHello' });
|
||||
await requestTab.hover();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await page.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
});
|
||||
|
||||
@@ -136,7 +136,7 @@ test.describe('manage protofile', () => {
|
||||
|
||||
const requestTab = page.getByRole('tab', { name: 'gRPC sayHello' });
|
||||
await requestTab.hover();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await page.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
});
|
||||
|
||||
@@ -180,7 +180,7 @@ test.describe('manage protofile', () => {
|
||||
// Clean up
|
||||
const requestTab = page.getByRole('tab', { name: 'gRPC sayHello' });
|
||||
await requestTab.hover();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click();
|
||||
await requestTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
await page.getByRole('button', { name: 'Don\'t Save' }).click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ test.describe('Copy and Paste Folders', () => {
|
||||
|
||||
// Add a request to the folder
|
||||
await folder.hover();
|
||||
await folder.locator('.menu-icon').click();
|
||||
await folder.locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'New Request' }).click();
|
||||
await page.getByPlaceholder('Request Name').fill('request-in-folder');
|
||||
await page.locator('#new-request-url .CodeMirror').click();
|
||||
@@ -34,7 +34,7 @@ test.describe('Copy and Paste Folders', () => {
|
||||
|
||||
// Copy the folder
|
||||
await folder.hover();
|
||||
await folder.locator('.menu-icon').click();
|
||||
await folder.locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Copy' }).click();
|
||||
|
||||
// Paste into the collection root
|
||||
@@ -77,13 +77,13 @@ test.describe('Copy and Paste Folders', () => {
|
||||
|
||||
// Copy folder-to-copy
|
||||
await folderToCopy.hover();
|
||||
await folderToCopy.locator('.menu-icon').click();
|
||||
await folderToCopy.locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Copy' }).click();
|
||||
await folderToCopy.click();
|
||||
|
||||
// Paste into target folder
|
||||
await targetFolder.hover();
|
||||
await targetFolder.locator('.menu-icon').click();
|
||||
await targetFolder.locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Paste' }).click();
|
||||
|
||||
// Verify folder was pasted inside target folder
|
||||
|
||||
@@ -24,7 +24,7 @@ test.describe('Copy and Paste Requests', () => {
|
||||
// Copy the request
|
||||
const requestItem = page.locator('.collection-item-name').filter({ hasText: 'original-request' });
|
||||
await requestItem.hover();
|
||||
await requestItem.locator('.menu-icon').click();
|
||||
await requestItem.locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Copy' }).click();
|
||||
|
||||
// Paste into the collection root
|
||||
@@ -48,7 +48,7 @@ test.describe('Copy and Paste Requests', () => {
|
||||
const folder = page.locator('.collection-item-name').filter({ hasText: 'test-folder' });
|
||||
await folder.click();
|
||||
await folder.hover();
|
||||
await folder.locator('.menu-icon').click();
|
||||
await folder.locator('.menu-icon').click({ force: true });
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Paste' }).click();
|
||||
|
||||
await expect(page.locator('.collection-item-name').filter({ hasText: 'original-request' })).toHaveCount(3);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test, expect, closeElectronApp } from '../../../playwright';
|
||||
import { createCollection, openCollection } from '../../utils/page';
|
||||
import { getTableCell } from '../../utils/page/locators';
|
||||
|
||||
@@ -39,17 +39,22 @@ test('should persist request with newlines across app restarts', async ({ create
|
||||
await page.getByRole('tab', { name: 'Vars' }).click();
|
||||
const preReqRow = page.locator('table').first().locator('tbody tr').first();
|
||||
await getTableCell(preReqRow, 0).getByRole('textbox').fill('preRequestVar');
|
||||
// Wait for table to stabilize after fill (new empty row may be appended)
|
||||
await expect(getTableCell(preReqRow, 0).getByRole('textbox')).toHaveValue('preRequestVar');
|
||||
await getTableCell(preReqRow, 1).locator('.CodeMirror').click();
|
||||
await getTableCell(preReqRow, 1).locator('textarea').fill('pre\nRequest\nValue');
|
||||
|
||||
const postResRow = page.locator('table').nth(1).locator('tbody tr').first();
|
||||
await getTableCell(postResRow, 0).getByRole('textbox').fill('postResponseVar');
|
||||
// Wait for table to stabilize after fill (new empty row may be appended)
|
||||
await expect(getTableCell(postResRow, 0).getByRole('textbox')).toHaveValue('postResponseVar');
|
||||
await getTableCell(postResRow, 1).locator('.CodeMirror').click();
|
||||
await getTableCell(postResRow, 1).locator('textarea').fill('post\nResponse\nValue');
|
||||
|
||||
const saveShortcut = process.platform === 'darwin' ? 'Meta+s' : 'Control+s';
|
||||
await page.keyboard.press(saveShortcut);
|
||||
await app1.close();
|
||||
await expect(page.getByText('Request saved successfully')).toBeVisible();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Verify persistence after restart
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
@@ -71,5 +76,5 @@ test('should persist request with newlines across app restarts', async ({ create
|
||||
await expect(page2.locator('table').first().locator('tbody tr')).toHaveCount(2);
|
||||
await expect(page2.locator('table').nth(1).locator('tbody tr')).toHaveCount(2);
|
||||
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ test.describe('Max Redirects Settings Tests', () => {
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
|
||||
// Close without saving to avoid modifying the .bru file
|
||||
await page.locator('.close-icon-container').click();
|
||||
await page.locator('.close-icon-container').click({ force: true });
|
||||
await page.locator('button:has-text("Don\'t Save")').first().click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ test.describe('No Redirects Settings Tests', () => {
|
||||
await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 });
|
||||
|
||||
// Close without saving to avoid modifying the .bru file
|
||||
await page.locator('.close-icon-container').click();
|
||||
await page.locator('.close-icon-container').click({ force: true });
|
||||
await page.locator('button:has-text("Don\'t Save")').first().click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ test.describe('Timeout Settings Tests', () => {
|
||||
await expect(responsePane).toContainText('302');
|
||||
|
||||
// Close without saving to avoid modifying the .bru file
|
||||
await page.locator('.close-icon-container').click();
|
||||
await page.locator('.close-icon-container').click({ force: true });
|
||||
await page.locator('button:has-text("Don\'t Save")').first().click();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { test, expect } from '../../playwright';
|
||||
import { execSync } from 'child_process';
|
||||
import path from 'path';
|
||||
import { clickResponseAction } from '../utils/page/actions';
|
||||
import { clickResponseAction, sendRequest } from '../utils/page/actions';
|
||||
|
||||
test.describe.serial('Response Example Menu Operations', () => {
|
||||
test.setTimeout(1 * 60 * 1000); // 1 minute for all tests in this describe block, default is 30 seconds.
|
||||
@@ -17,7 +17,7 @@ test.describe.serial('Response Example Menu Operations', () => {
|
||||
});
|
||||
|
||||
await test.step('Create example', async () => {
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
await sendRequest(page, 200);
|
||||
await clickResponseAction(page, 'response-bookmark-btn');
|
||||
await page.getByTestId('create-example-name-input').clear();
|
||||
await page.getByTestId('create-example-name-input').fill('Example to Clone');
|
||||
@@ -33,7 +33,7 @@ test.describe.serial('Response Example Menu Operations', () => {
|
||||
await test.step('Clone example', async () => {
|
||||
const exampleRow = page.locator('.collection-item-name').filter({ hasText: 'Example to Clone' });
|
||||
await exampleRow.hover();
|
||||
await exampleRow.locator('.menu-icon').click();
|
||||
await exampleRow.locator('.menu-icon').click({ force: true });
|
||||
|
||||
await page.getByTestId('response-example-menu-clone').click();
|
||||
const clonedExampleItem = page.locator('.collection-item-name').filter({ hasText: 'Example to Clone (Copy)' });
|
||||
@@ -48,7 +48,7 @@ test.describe.serial('Response Example Menu Operations', () => {
|
||||
});
|
||||
|
||||
await test.step('Create example to delete', async () => {
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
await sendRequest(page, 200);
|
||||
await clickResponseAction(page, 'response-bookmark-btn');
|
||||
await page.getByTestId('create-example-name-input').clear();
|
||||
await page.getByTestId('create-example-name-input').fill('Example to Delete');
|
||||
@@ -65,7 +65,7 @@ test.describe.serial('Response Example Menu Operations', () => {
|
||||
const exampleRow = page.locator('.collection-item-name').filter({ hasText: 'Example to Delete' });
|
||||
await expect(exampleRow).toBeVisible();
|
||||
await exampleRow.hover();
|
||||
await exampleRow.locator('.menu-icon').click();
|
||||
await exampleRow.locator('.menu-icon').click({ force: true });
|
||||
|
||||
await page.getByTestId('response-example-menu-delete').click();
|
||||
await expect(page.getByText('Delete Example')).toBeVisible();
|
||||
@@ -81,7 +81,7 @@ test.describe.serial('Response Example Menu Operations', () => {
|
||||
});
|
||||
|
||||
await test.step('Create example to rename', async () => {
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
await sendRequest(page, 200);
|
||||
await clickResponseAction(page, 'response-bookmark-btn');
|
||||
await page.getByTestId('create-example-name-input').clear();
|
||||
await page.getByTestId('create-example-name-input').fill('Example to Rename');
|
||||
@@ -98,7 +98,7 @@ test.describe.serial('Response Example Menu Operations', () => {
|
||||
const exampleRow = page.locator('.collection-item-name').filter({ hasText: 'Example to Rename' });
|
||||
await expect(exampleRow).toBeVisible();
|
||||
await exampleRow.hover();
|
||||
await exampleRow.locator('.menu-icon').click();
|
||||
await exampleRow.locator('.menu-icon').click({ force: true });
|
||||
await page.getByTestId('response-example-menu-rename').click();
|
||||
await expect(page.getByText('Rename Example')).toBeVisible();
|
||||
const renameExampleNameInput = page.getByTestId('rename-example-name-input');
|
||||
|
||||
@@ -569,7 +569,7 @@ const closeEnvironmentPanel = async (page: Page, type: EnvironmentType = 'collec
|
||||
const tabLabel = type === 'collection' ? 'Environments' : 'Global Environments';
|
||||
const envTab = page.locator('.request-tab').filter({ hasText: tabLabel });
|
||||
await envTab.hover();
|
||||
await envTab.getByTestId('request-tab-close-icon').click();
|
||||
await envTab.getByTestId('request-tab-close-icon').click({ force: true });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@ test.describe.serial('WebSocket Variable Interpolation', () => {
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Test' }).click();
|
||||
await expect(page.locator('.current-environment').filter({ hasText: /Test/ })).toBeVisible();
|
||||
|
||||
// Wait a bit for environment to be applied
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
// Connect WebSocket
|
||||
await locators.connectionControls.connect().click();
|
||||
|
||||
@@ -52,7 +49,6 @@ test.describe.serial('WebSocket Variable Interpolation', () => {
|
||||
|
||||
// Click to expand the collection
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'variable-interpolation' }).click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Open the request
|
||||
await expect(page.getByTitle(BRU_REQ_NAME)).toBeVisible();
|
||||
@@ -60,7 +56,9 @@ test.describe.serial('WebSocket Variable Interpolation', () => {
|
||||
|
||||
// Select the test environment (which has data: test-data)
|
||||
await page.locator('div.current-environment').click();
|
||||
await expect(page.locator('.dropdown-item').filter({ hasText: 'Test' })).toBeVisible();
|
||||
await page.locator('.dropdown-item').filter({ hasText: 'Test' }).click();
|
||||
await expect(page.locator('.current-environment').filter({ hasText: /Test/ })).toBeVisible();
|
||||
|
||||
// Clear any previous messages
|
||||
await locators.toolbar.clearResponse().click();
|
||||
@@ -73,9 +71,6 @@ test.describe.serial('WebSocket Variable Interpolation', () => {
|
||||
timeout: MAX_CONNECTION_TIME
|
||||
});
|
||||
|
||||
// Wait a bit for messages to be sent and received (echo server echoes back)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify the sent message contains interpolated value
|
||||
// Should send {"test": "test-data"} (not {"test": "{{data}}"})
|
||||
const messages = locators.messages();
|
||||
@@ -83,14 +78,14 @@ test.describe.serial('WebSocket Variable Interpolation', () => {
|
||||
// Find the outgoing message with interpolated content
|
||||
// The echo server will echo back the same message, so we should see it twice
|
||||
const sentMessage = messages.filter({ hasText: 'test-data' }).first();
|
||||
await expect(sentMessage).toBeAttached({ timeout: 2000 });
|
||||
await expect(sentMessage).toBeAttached({ timeout: MAX_CONNECTION_TIME });
|
||||
|
||||
// Verify the message content shows interpolated value, not literal variable
|
||||
const messageText = await sentMessage.locator('.text-ellipsis').textContent();
|
||||
expect(messageText).toContain('test-data');
|
||||
expect(messageText).not.toContain('{{data}}');
|
||||
const messageContent = sentMessage.locator('.text-ellipsis');
|
||||
await expect(messageContent).toContainText('test-data');
|
||||
await expect(messageContent).not.toContainText('{{data}}');
|
||||
|
||||
// Verify JSON structure is correct
|
||||
expect(messageText).toMatch(/\{[\s\S]*"test"[\s\S]*"test-data"[\s\S]*\}/);
|
||||
await expect(messageContent).toContainText('"test"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test, expect, closeElectronApp } from '../../../playwright';
|
||||
|
||||
test.describe('Default Workspace', () => {
|
||||
test.describe('First Launch', () => {
|
||||
@@ -15,8 +15,7 @@ test.describe('Default Workspace', () => {
|
||||
const workspaceName = page.getByTestId('workspace-name');
|
||||
await expect(workspaceName).toHaveText('My Workspace');
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,7 +29,7 @@ test.describe('Default Workspace', () => {
|
||||
await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await expect(page1.getByTestId('workspace-name')).toHaveText('My Workspace');
|
||||
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Second launch - same workspace should be loaded
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
@@ -38,8 +37,7 @@ test.describe('Default Workspace', () => {
|
||||
await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await expect(page2.getByTestId('workspace-name')).toHaveText('My Workspace');
|
||||
|
||||
await app2.context().close();
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,8 +77,7 @@ test.describe('Default Workspace', () => {
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
expect(fs.existsSync(path.join(newWorkspacePath, 'workspace.yml'))).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should create NEW workspace when workspace.yml has invalid YAML', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -116,8 +113,7 @@ test.describe('Default Workspace', () => {
|
||||
const newWorkspacePath = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should create NEW workspace when workspace.yml has wrong type', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -156,8 +152,7 @@ docs: ''
|
||||
const newWorkspacePath = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should create NEW workspace when directory does not exist', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -186,8 +181,7 @@ docs: ''
|
||||
expect(fs.existsSync(newWorkspacePath)).toBe(true);
|
||||
expect(fs.existsSync(path.join(newWorkspacePath, 'workspace.yml'))).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -206,8 +200,7 @@ docs: ''
|
||||
const workspaceItem = page.locator('.workspace-item, .dropdown-item').filter({ hasText: 'My Workspace' });
|
||||
await expect(workspaceItem.first()).toBeVisible();
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should not show pin button for default workspace', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -223,8 +216,7 @@ docs: ''
|
||||
// Default workspace should NOT have pin button
|
||||
await expect(workspaceItem.locator('.pin-btn')).not.toBeVisible();
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test, expect, closeElectronApp } from '../../../playwright';
|
||||
|
||||
const env = {
|
||||
DISABLE_SAMPLE_COLLECTION_IMPORT: 'false'
|
||||
@@ -50,8 +50,7 @@ test.describe('Default Workspace Migration', () => {
|
||||
});
|
||||
|
||||
await test.step('Cleanup', async () => {
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,8 +96,7 @@ test.describe('Default Workspace Migration', () => {
|
||||
expect(workspaceYml).toContain('collection-1');
|
||||
expect(workspaceYml).toContain('collection-2');
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -138,8 +136,7 @@ test.describe('Default Workspace Migration', () => {
|
||||
const sampleCollection = page.locator('#sidebar-collection-name').getByText('Sample API Collection');
|
||||
await expect(sampleCollection).not.toBeVisible();
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -158,8 +155,7 @@ test.describe('Default Workspace Migration', () => {
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
const originalYmlContent = fs.readFileSync(path.join(workspacePath, 'workspace.yml'), 'utf8');
|
||||
|
||||
await app1.context().close();
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Second launch - should reuse existing workspace
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
@@ -174,8 +170,7 @@ test.describe('Default Workspace Migration', () => {
|
||||
// No new workspace should have been created
|
||||
expect(fs.existsSync(path.join(userDataPath, 'default-workspace-1'))).toBe(false);
|
||||
|
||||
await app2.context().close();
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -201,8 +196,7 @@ test.describe('Default Workspace Migration', () => {
|
||||
// Collections should be empty (just the key)
|
||||
expect(workspaceYml).toMatch(/collections:\s*\n/);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { test, expect, closeElectronApp } from '../../../playwright';
|
||||
|
||||
test.describe('Default Workspace Recovery and Backup', () => {
|
||||
test.describe('Global Environments Backup', () => {
|
||||
@@ -61,8 +61,7 @@ test.describe('Default Workspace Recovery and Backup', () => {
|
||||
expect(backup.activeGlobalEnvironmentUid).toBe('env1abcdefghijk123456');
|
||||
expect(backup.backupDate).toBeDefined();
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should preserve global environments backup across multiple app restarts', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -96,7 +95,7 @@ test.describe('Default Workspace Recovery and Backup', () => {
|
||||
const app1 = await launchElectronApp({ userDataPath });
|
||||
const page1 = await app1.firstWindow();
|
||||
await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Verify backup exists
|
||||
const backupPath = path.join(userDataPath, 'global-environments-backup.json');
|
||||
@@ -113,8 +112,7 @@ test.describe('Default Workspace Recovery and Backup', () => {
|
||||
const backupContentAfterSecond = fs.readFileSync(backupPath, 'utf8');
|
||||
expect(backupContentAfterSecond).toBe(backupContentAfterFirst);
|
||||
|
||||
await app2.context().close();
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,7 +138,7 @@ test.describe('Default Workspace Recovery and Backup', () => {
|
||||
const app = await launchElectronApp({ userDataPath });
|
||||
const page = await app.firstWindow();
|
||||
await page.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
|
||||
// Verify lastOpenedCollections is still in preferences
|
||||
const prefsPath = path.join(userDataPath, 'preferences.json');
|
||||
@@ -192,8 +190,7 @@ docs: ''
|
||||
const prefs = JSON.parse(fs.readFileSync(path.join(userDataPath, 'preferences.json'), 'utf8'));
|
||||
expect(prefs.preferences?.general?.defaultWorkspacePath).toBe(workspacePath);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should find latest numbered workspace when multiple exist and path not in preferences', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -240,8 +237,7 @@ docs: ''
|
||||
// No new workspace should be created
|
||||
expect(fs.existsSync(path.join(userDataPath, 'default-workspace-3'))).toBe(false);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should skip invalid workspaces and use latest valid one', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -301,8 +297,7 @@ docs: ''
|
||||
const prefs = JSON.parse(fs.readFileSync(path.join(userDataPath, 'preferences.json'), 'utf8'));
|
||||
expect(prefs.preferences?.general?.defaultWorkspacePath).toBe(workspace1);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -357,8 +352,7 @@ docs: ''
|
||||
const newWorkspace = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspace)).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should recover environments from broken workspace to new workspace', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -432,8 +426,7 @@ docs: ''
|
||||
expect(fs.existsSync(path.join(newEnvDir, 'production.yml'))).toBe(true);
|
||||
expect(fs.existsSync(path.join(newEnvDir, 'staging.yml'))).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should use lastOpenedCollections as fallback when workspace config parsing fails', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -473,8 +466,7 @@ docs: ''
|
||||
const workspaceYml = fs.readFileSync(path.join(newWorkspace, 'workspace.yml'), 'utf8');
|
||||
expect(workspaceYml).toContain('fallback-collection');
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -531,8 +523,7 @@ docs: ''
|
||||
const createdNew = fs.existsSync(path.join(userDataPath, 'default-workspace-1'));
|
||||
expect(usedExisting || createdNew).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should recover from latest workspace when path does not exist and multiple workspaces exist', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -611,8 +602,7 @@ docs: ''
|
||||
const createdWorkspace2 = fs.existsSync(path.join(userDataPath, 'default-workspace-2'));
|
||||
expect(usedWorkspace1 || createdWorkspace2).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -637,7 +627,7 @@ docs: ''
|
||||
const workspacePath = path.join(userDataPath, 'default-workspace');
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Now add collection to the workspace
|
||||
const workspaceYmlPath = path.join(workspacePath, 'workspace.yml');
|
||||
@@ -686,8 +676,7 @@ variables:
|
||||
// Environment should be recovered
|
||||
expect(fs.existsSync(path.join(newWorkspace, 'environments', 'myenv.yml'))).toBe(true);
|
||||
|
||||
await app2.context().close();
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
|
||||
test('should handle workspace deleted between app restarts', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -701,7 +690,7 @@ variables:
|
||||
const workspacePath = path.join(userDataPath, 'default-workspace');
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// DELETE the workspace directory
|
||||
fs.rmSync(workspacePath, { recursive: true, force: true });
|
||||
@@ -716,8 +705,7 @@ variables:
|
||||
expect(fs.existsSync(workspacePath)).toBe(true);
|
||||
expect(fs.existsSync(path.join(workspacePath, 'workspace.yml'))).toBe(true);
|
||||
|
||||
await app2.context().close();
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
});
|
||||
|
||||
test('should preserve all data through multiple corruption and recovery cycles', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -741,7 +729,7 @@ variables:
|
||||
const app1 = await launchElectronApp({ userDataPath });
|
||||
const page1 = await app1.firstWindow();
|
||||
await page1.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await app1.close();
|
||||
await closeElectronApp(app1);
|
||||
|
||||
// Verify workspace-0 created
|
||||
const ws0 = path.join(userDataPath, 'default-workspace');
|
||||
@@ -764,7 +752,7 @@ variables: []
|
||||
const app2 = await launchElectronApp({ userDataPath });
|
||||
const page2 = await app2.firstWindow();
|
||||
await page2.locator('[data-app-state="loaded"]').waitFor({ timeout: 30000 });
|
||||
await app2.close();
|
||||
await closeElectronApp(app2);
|
||||
|
||||
// Verify workspace-1 created with recovered data
|
||||
const ws1 = path.join(userDataPath, 'default-workspace-1');
|
||||
@@ -790,8 +778,7 @@ variables: []
|
||||
const ws2Yml = fs.readFileSync(path.join(ws2, 'workspace.yml'), 'utf8');
|
||||
expect(ws2Yml).toContain('persistent-collection');
|
||||
|
||||
await app3.context().close();
|
||||
await app3.close();
|
||||
await closeElectronApp(app3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -818,8 +805,7 @@ variables: []
|
||||
const newWorkspace = path.join(userDataPath, 'default-workspace-1');
|
||||
expect(fs.existsSync(newWorkspace)).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should handle missing environments directory during recovery', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -842,8 +828,7 @@ variables: []
|
||||
// Should not crash
|
||||
expect(fs.existsSync(path.join(userDataPath, 'default-workspace-1'))).toBe(true);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should deduplicate collections between recovered and preference sources', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -885,8 +870,7 @@ variables: []
|
||||
const collectionEntries = yml.match(/- name:/g);
|
||||
expect(collectionEntries).toHaveLength(1);
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
|
||||
test('should not overwrite recovered environments with global environments of same name', async ({ launchElectronApp, createTmpDir }) => {
|
||||
@@ -943,8 +927,7 @@ variables:
|
||||
expect(envContent).toContain('workspace-value');
|
||||
expect(envContent).not.toContain('global-value');
|
||||
|
||||
await app.context().close();
|
||||
await app.close();
|
||||
await closeElectronApp(app);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user