mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
feat(app): introduce Enable App setting (#8521)
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
createRequest,
|
||||
openRequest,
|
||||
setAppCode,
|
||||
enableApp,
|
||||
previewApp,
|
||||
saveRequest,
|
||||
selectRequestBodyMode,
|
||||
getAppWebviewHtml
|
||||
@@ -87,7 +87,7 @@ test.describe('Apps - ctx API', () => {
|
||||
await openRequest(page, 'apps-ctx', 'ctx-req', { persist: true });
|
||||
|
||||
await setAppCode(page, CTX_APP);
|
||||
await enableApp(page);
|
||||
await previewApp(page);
|
||||
await waitForGuestReady(electronApp);
|
||||
|
||||
const raw = await guestEval(
|
||||
@@ -118,7 +118,7 @@ test.describe('Apps - ctx API', () => {
|
||||
await openRequest(page, 'apps-theme', 'theme-req', { persist: true });
|
||||
|
||||
await setAppCode(page, CTX_APP);
|
||||
await enableApp(page);
|
||||
await previewApp(page);
|
||||
await waitForGuestReady(electronApp);
|
||||
|
||||
const raw = await guestEval(
|
||||
@@ -137,7 +137,7 @@ test.describe('Apps - ctx API', () => {
|
||||
await openRequest(page, 'apps-log', 'log-req', { persist: true });
|
||||
|
||||
await setAppCode(page, CTX_APP);
|
||||
await enableApp(page);
|
||||
await previewApp(page);
|
||||
await waitForGuestReady(electronApp);
|
||||
|
||||
const result = await guestEval(electronApp, 'window.__log()');
|
||||
@@ -153,7 +153,7 @@ test.describe('Apps - ctx API', () => {
|
||||
await setJsonBodyWithVar(page);
|
||||
await setAppCode(page, CTX_APP);
|
||||
await saveRequest(page);
|
||||
await enableApp(page);
|
||||
await previewApp(page);
|
||||
await waitForGuestReady(electronApp);
|
||||
|
||||
await test.step('flat override keys become runtime variables', async () => {
|
||||
@@ -176,7 +176,7 @@ test.describe('Apps - ctx API', () => {
|
||||
await setJsonBodyWithVar(page);
|
||||
await setAppCode(page, CTX_APP);
|
||||
await saveRequest(page);
|
||||
await enableApp(page);
|
||||
await previewApp(page);
|
||||
await waitForGuestReady(electronApp);
|
||||
|
||||
await guestEval(electronApp, `window.__setVar('q', 'viaSet')`);
|
||||
@@ -197,7 +197,7 @@ test.describe('Apps - ctx API', () => {
|
||||
await openRequest(page, 'apps-boot', 'boot-req', { persist: true });
|
||||
|
||||
await setAppCode(page, CTX_APP);
|
||||
await enableApp(page);
|
||||
await previewApp(page);
|
||||
|
||||
const html = await getAppWebviewHtml(page);
|
||||
// ctx API surface is present in the injected bootstrap
|
||||
|
||||
@@ -5,7 +5,13 @@ import {
|
||||
openRequest,
|
||||
selectRequestPaneTab,
|
||||
setAppCode,
|
||||
enableApp,
|
||||
setAppEnabled,
|
||||
readAppEditor,
|
||||
requestPaneAppTab,
|
||||
openRequestPaneTabOverflow,
|
||||
requestPaneOverflowTabItem,
|
||||
activeAppView,
|
||||
previewApp,
|
||||
exitApp,
|
||||
selectViewMode,
|
||||
saveRequest,
|
||||
@@ -14,16 +20,50 @@ import {
|
||||
|
||||
const SIMPLE_APP = `<div id="hello">Hello from the app</div>`;
|
||||
|
||||
// Read the app code currently loaded in the App-tab editor (via the CodeMirror API).
|
||||
const readAppEditor = (page) =>
|
||||
page
|
||||
.getByTestId('app-code-editor')
|
||||
.locator('.CodeMirror')
|
||||
.first()
|
||||
.evaluate((el) => (el as any).CodeMirror?.getValue());
|
||||
// Assert the App tab is absent from the request pane — checks both the visible
|
||||
// tab row and the ResponsiveTabs overflow dropdown (tabs can overflow at narrow widths).
|
||||
const expectNoAppTab = async (page) => {
|
||||
await expect(requestPaneAppTab(page)).toHaveCount(0);
|
||||
await openRequestPaneTabOverflow(page);
|
||||
await expect(requestPaneOverflowTabItem(page, /^App$/)).toHaveCount(0);
|
||||
await page.keyboard.press('Escape');
|
||||
};
|
||||
|
||||
test.describe('Apps - request-level UI', () => {
|
||||
test('App tab: enable takes over the panes, exit returns to the editor', async ({ page, createTmpDir }) => {
|
||||
test('App tab and view-mode toggle are gated behind the Enable App setting', async ({ page, createTmpDir }) => {
|
||||
const collectionPath = await createTmpDir('apps-ui-gating');
|
||||
const collectionName = 'apps-gate';
|
||||
await createCollection(page, collectionName, collectionPath);
|
||||
await createRequest(page, 'gate-req', collectionName, {
|
||||
url: 'http://localhost:8081/api/echo/anything/x',
|
||||
method: 'GET'
|
||||
});
|
||||
await openRequest(page, collectionName, 'gate-req', { persist: true });
|
||||
|
||||
await test.step('App tab and view-mode toggle are hidden by default', async () => {
|
||||
await expect(page.getByTestId('responsive-tab-params')).toBeVisible();
|
||||
await expectNoAppTab(page);
|
||||
await expect(page.getByTestId('view-mode-toggle')).toHaveCount(0);
|
||||
});
|
||||
|
||||
await setAppEnabled(page, true);
|
||||
|
||||
await test.step('Enabling in settings reveals the App tab and view-mode toggle', async () => {
|
||||
await expect(page.getByTestId('view-mode-toggle')).toBeVisible();
|
||||
// selecting the tab proves it exists (handles the overflow dropdown too)
|
||||
await selectRequestPaneTab(page, 'App');
|
||||
await expect(page.getByTestId('app-code-editor')).toBeVisible();
|
||||
});
|
||||
|
||||
await setAppEnabled(page, false);
|
||||
|
||||
await test.step('Disabling hides them again', async () => {
|
||||
await expectNoAppTab(page);
|
||||
await expect(page.getByTestId('view-mode-toggle')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('App tab: Preview takes over the panes, exit returns to the editor', async ({ page, createTmpDir }) => {
|
||||
const collectionPath = await createTmpDir('apps-ui-toggle');
|
||||
await createCollection(page, 'apps-ui', collectionPath);
|
||||
await createRequest(page, 'app-req', 'apps-ui', {
|
||||
@@ -32,19 +72,18 @@ test.describe('Apps - request-level UI', () => {
|
||||
});
|
||||
await openRequest(page, 'apps-ui', 'app-req', { persist: true });
|
||||
|
||||
await test.step('App tab exposes the toggle and editor', async () => {
|
||||
await selectRequestPaneTab(page, 'App');
|
||||
await expect(page.getByTestId('app-enable-toggle')).toBeVisible();
|
||||
await setAppCode(page, SIMPLE_APP);
|
||||
|
||||
await test.step('App tab exposes the Preview button and editor', async () => {
|
||||
await expect(page.getByTestId('app-preview-btn')).toBeVisible();
|
||||
await expect(page.getByTestId('app-code-editor')).toBeVisible();
|
||||
// request pane is still the normal request view while disabled
|
||||
// request pane is still the normal request view before previewing
|
||||
await expect(page.getByTestId('request-pane')).toBeVisible();
|
||||
});
|
||||
|
||||
await setAppCode(page, SIMPLE_APP);
|
||||
|
||||
await test.step('Enabling app mode replaces the request/response area with the app view', async () => {
|
||||
await enableApp(page);
|
||||
await expect(page.getByTestId('app-view').locator('webview')).toBeVisible();
|
||||
await test.step('Preview replaces the request/response area with the app view', async () => {
|
||||
await previewApp(page);
|
||||
await expect(activeAppView(page).locator('webview')).toBeVisible();
|
||||
await expect(page.getByTestId('request-pane')).toBeHidden();
|
||||
});
|
||||
|
||||
@@ -61,12 +100,13 @@ test.describe('Apps - request-level UI', () => {
|
||||
await createRequest(page, 'ind-req', 'apps-ind', { url: 'http://localhost:8081/api/echo/anything/x' });
|
||||
await openRequest(page, 'apps-ind', 'ind-req', { persist: true });
|
||||
|
||||
await setAppEnabled(page, true);
|
||||
await selectRequestPaneTab(page, 'App');
|
||||
// No code yet → no indicator
|
||||
await expect(page.getByTestId('responsive-tab-app').getByTestId('status-dot-app')).toHaveCount(0);
|
||||
await expect(requestPaneAppTab(page).getByTestId('status-dot-app')).toHaveCount(0);
|
||||
|
||||
await setAppCode(page, SIMPLE_APP);
|
||||
await expect(page.getByTestId('responsive-tab-app').getByTestId('status-dot-app')).toBeVisible();
|
||||
await expect(requestPaneAppTab(page).getByTestId('status-dot-app')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Collection toolbar view-mode toggle switches Request / App / File', async ({ page, createTmpDir }) => {
|
||||
@@ -84,13 +124,13 @@ test.describe('Apps - request-level UI', () => {
|
||||
await test.step('Switch to App mode', async () => {
|
||||
await selectViewMode(page, 'app');
|
||||
await expect(page.getByTestId('view-mode-app')).toHaveClass(/active/);
|
||||
await expect(page.getByTestId('app-view').locator('webview')).toBeVisible();
|
||||
await expect(activeAppView(page).locator('webview')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Switch to File mode (app view goes away)', async () => {
|
||||
await selectViewMode(page, 'file');
|
||||
await expect(page.getByTestId('view-mode-file')).toHaveClass(/active/);
|
||||
await expect(page.getByTestId('app-view')).toBeHidden();
|
||||
await expect(activeAppView(page)).toHaveCount(0);
|
||||
});
|
||||
|
||||
await test.step('Switch back to Request mode', async () => {
|
||||
@@ -100,22 +140,31 @@ test.describe('Apps - request-level UI', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('App code persists across save + reopen', async ({ page, createTmpDir }) => {
|
||||
test('Enable App and code persist; an enabled app opens in preview mode by default', async ({ page, createTmpDir }) => {
|
||||
const collectionPath = await createTmpDir('apps-ui-persist');
|
||||
await createCollection(page, 'apps-persist', collectionPath);
|
||||
await createRequest(page, 'persist-req', 'apps-persist', { url: 'http://localhost:8081/api/echo/anything/x' });
|
||||
await openRequest(page, 'apps-persist', 'persist-req', { persist: true });
|
||||
|
||||
// Enabling from Settings keeps the tab in request mode so code can be written.
|
||||
await setAppCode(page, SIMPLE_APP);
|
||||
await expect(page.getByTestId('request-pane')).toBeVisible();
|
||||
await saveRequest(page);
|
||||
|
||||
await closeAllTabs(page);
|
||||
|
||||
await openRequest(page, 'apps-persist', 'persist-req', { persist: true });
|
||||
await selectRequestPaneTab(page, 'App');
|
||||
await expect(page.getByTestId('app-code-editor')).toBeVisible();
|
||||
await expect.poll(() => readAppEditor(page)).toBe(SIMPLE_APP);
|
||||
// App mode starts disabled on reopen (enabled is runtime-only, not persisted)
|
||||
await expect(page.getByTestId('app-enable-toggle')).toBeVisible();
|
||||
|
||||
await test.step('Freshly opened app-enabled request starts in preview mode', async () => {
|
||||
await expect(activeAppView(page).locator('webview')).toBeVisible();
|
||||
await expect(page.getByTestId('view-mode-app')).toHaveClass(/active/);
|
||||
});
|
||||
|
||||
await test.step('App code survives the round-trip', async () => {
|
||||
await exitApp(page);
|
||||
await selectRequestPaneTab(page, 'App');
|
||||
await expect(page.getByTestId('app-code-editor')).toBeVisible();
|
||||
await expect.poll(() => readAppEditor(page)).toBe(SIMPLE_APP);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
createRequest,
|
||||
createApp,
|
||||
selectAppView,
|
||||
activeAppPreviewSlot,
|
||||
selectRequestBodyMode,
|
||||
saveRequest
|
||||
} from '../utils/page';
|
||||
@@ -64,7 +65,7 @@ const waitForGuestReady = async (electronApp: ElectronApplication, collectionNam
|
||||
// directly to avoid auto-close-bracket corruption when typing HTML/JS.
|
||||
const setCollectionAppCode = async (page, code: string) => {
|
||||
await selectAppView(page, 'code');
|
||||
const editor = page.getByTestId('collection-app-code').locator('.CodeMirror').first();
|
||||
const editor = activeAppPreviewSlot(page).getByTestId('collection-app-code').locator('.CodeMirror').first();
|
||||
await editor.waitFor({ state: 'visible' });
|
||||
await editor.evaluate((el, val) => {
|
||||
const cm = (el as any).CodeMirror;
|
||||
@@ -112,13 +113,13 @@ test.describe('Collection apps', () => {
|
||||
});
|
||||
|
||||
await test.step('Tab opens, Code/Preview toggle works', async () => {
|
||||
await expect(page.getByTestId('collection-app')).toBeVisible({ timeout: 5000 });
|
||||
await expect(page.getByTestId('collection-app-view-preview')).toHaveClass(/active/);
|
||||
await expect(activeAppPreviewSlot(page).getByTestId('collection-app')).toBeVisible({ timeout: 5000 });
|
||||
await expect(activeAppPreviewSlot(page).getByTestId('collection-app-view-preview')).toHaveClass(/active/);
|
||||
await selectAppView(page, 'code');
|
||||
await expect(page.getByTestId('collection-app-code')).toBeVisible();
|
||||
await expect(page.getByTestId('collection-app-view-code')).toHaveClass(/active/);
|
||||
await expect(activeAppPreviewSlot(page).getByTestId('collection-app-code')).toBeVisible();
|
||||
await expect(activeAppPreviewSlot(page).getByTestId('collection-app-view-code')).toHaveClass(/active/);
|
||||
await selectAppView(page, 'preview');
|
||||
await expect(page.getByTestId('collection-app-preview').locator('webview')).toBeVisible();
|
||||
await expect(activeAppPreviewSlot(page).getByTestId('collection-app-preview').locator('webview')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user