mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
fix(timeline): scope scripted requests to their own request (#8210)
* fix(timeline): scope scripted requests to their own request * fix: oauth playwright test
This commit is contained in:
@@ -55,32 +55,31 @@ test.describe('Timeline — nested bru.runRequest bubbles inner scripted entries
|
||||
await test.step('Outer Timeline shows three rows: main + runRequest + bubbled inner sendRequest', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
// Without the fix: 2 (main + runRequest); inner sendRequest is dropped.
|
||||
await expect(rows).toHaveCount(3);
|
||||
|
||||
// Badge mix guards against an accidental wrong-3-rows pass.
|
||||
await expect(rows.locator('.tl-badge--main')).toHaveCount(1);
|
||||
await expect(rows.locator('.tl-badge--run-request')).toHaveCount(1);
|
||||
await expect(rows.locator('.tl-badge--scripted')).toHaveCount(1);
|
||||
await expect(rows.getByTestId('timeline-badge-main')).toHaveCount(1);
|
||||
await expect(rows.getByTestId('timeline-badge-post')).toHaveCount(1);
|
||||
await expect(rows.getByTestId('timeline-badge-pre')).toHaveCount(1);
|
||||
});
|
||||
|
||||
await test.step('Bubbled sendRequest row targets the inner-script URL (proving it came from inner)', async () => {
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const scriptedRow = rows.filter({ has: page.locator('.tl-badge--scripted') });
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
const scriptedRow = rows.filter({ has: page.getByTestId('timeline-badge-pre') });
|
||||
await expect(scriptedRow).toHaveCount(1);
|
||||
await expect(scriptedRow.locator('.tl-col-url')).toContainText('/headers');
|
||||
await expect(scriptedRow.getByTestId('timeline-url')).toContainText('/headers');
|
||||
});
|
||||
|
||||
await test.step('Filter chips count the bubbled entry under Pre-Request', async () => {
|
||||
const chips = page.locator('.timeline-filter-bar .timeline-chip');
|
||||
const countFor = (label: string) =>
|
||||
chips.filter({ hasText: label }).locator('.timeline-chip-count').first();
|
||||
const countFor = (id: string) =>
|
||||
page.getByTestId(`timeline-chip-${id}`).getByTestId('timeline-chip-count');
|
||||
|
||||
await expect(countFor('All')).toHaveText('3');
|
||||
await expect(countFor('Main')).toHaveText('1');
|
||||
await expect(countFor('all')).toHaveText('3');
|
||||
await expect(countFor('main')).toHaveText('1');
|
||||
// runRequest + bubbled sendRequest both ran during outer's pre-request.
|
||||
await expect(countFor('Pre-Request')).toHaveText('2');
|
||||
await expect(countFor('pre')).toHaveText('2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,13 +118,13 @@ test.describe('Timeline — nested bru.runRequest bubbles inner scripted entries
|
||||
await test.step('Outer Timeline shows the bubbled post-response sendRequest row', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
await expect(rows).toHaveCount(3);
|
||||
|
||||
// URL match confirms the scripted row is the post-response one.
|
||||
const scriptedRow = rows.filter({ has: page.locator('.tl-badge--scripted') });
|
||||
const scriptedRow = rows.filter({ has: page.getByTestId('timeline-badge-pre') });
|
||||
await expect(scriptedRow).toHaveCount(1);
|
||||
await expect(scriptedRow.locator('.tl-col-url')).toContainText('/query');
|
||||
await expect(scriptedRow.getByTestId('timeline-url')).toContainText('/query');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,13 +47,13 @@ test.describe('Timeline — runRequest network-error row shows URL and error cod
|
||||
await test.step('Outer Timeline has the runRequest row with inner URL (URL fallback)', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
await expect(rows).toHaveCount(2); // main + runRequest
|
||||
|
||||
// Without the URL fallback this column would be empty.
|
||||
const runRequestRow = rows.filter({ has: page.locator('.tl-badge--run-request') });
|
||||
const runRequestRow = rows.filter({ has: page.getByTestId('timeline-badge-post') });
|
||||
await expect(runRequestRow).toHaveCount(1);
|
||||
await expect(runRequestRow.locator('.tl-col-url')).toContainText('localhost:9999');
|
||||
await expect(runRequestRow.getByTestId('timeline-url')).toContainText('localhost:9999');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,13 +42,13 @@ test.describe('Timeline — bru.runRequest skips unsupported item types', () =>
|
||||
await test.step('Timeline has main + two Skipped runRequest rows', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
await expect(rows).toHaveCount(3);
|
||||
|
||||
const skippedRows = rows.filter({ has: page.locator('.tl-badge--run-request') });
|
||||
const skippedRows = rows.filter({ has: page.getByTestId('timeline-badge-post') });
|
||||
await expect(skippedRows).toHaveCount(2);
|
||||
await expect(skippedRows.nth(0).locator('.timeline-status')).toContainText('Skipped');
|
||||
await expect(skippedRows.nth(1).locator('.timeline-status')).toContainText('Skipped');
|
||||
await expect(skippedRows.nth(0).getByTestId('timeline-status')).toContainText('Skipped');
|
||||
await expect(skippedRows.nth(1).getByTestId('timeline-status')).toContainText('Skipped');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import {
|
||||
closeAllCollections,
|
||||
createCollection,
|
||||
createFolder,
|
||||
createRequest,
|
||||
expandFolder,
|
||||
openRequest,
|
||||
addCollectionScript,
|
||||
addFolderScript,
|
||||
addPreRequestScript,
|
||||
saveRequest,
|
||||
sendRequest,
|
||||
selectResponsePaneTab
|
||||
} from '../../utils/page/actions';
|
||||
|
||||
test.describe('Timeline — scoped request attribution', () => {
|
||||
test.afterEach(async ({ page }) => {
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('request-level sendRequest is attributed to the request, not the collection script', async ({
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const collectionName = 'timeline-scope-collection';
|
||||
const requestName = 'scoped-driver';
|
||||
const url = 'http://localhost:8081/ping';
|
||||
|
||||
await test.step('Create collection with a (non-empty) collection-level pre-request script', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir(collectionName), 'yml');
|
||||
// Non-empty collection script => stamps the collection scope before the request runs.
|
||||
await addCollectionScript(page, collectionName, 'pre-request', `bru.setVar('collectionRan', true);`);
|
||||
});
|
||||
|
||||
await test.step('Create a request whose pre-request script issues a sendRequest', async () => {
|
||||
await createRequest(page, requestName, collectionName, { url });
|
||||
await openRequest(page, collectionName, requestName);
|
||||
await addPreRequestScript(page, `await bru.sendRequest({ url: "${url}", method: "GET" });`);
|
||||
await saveRequest(page);
|
||||
});
|
||||
|
||||
await test.step('Send the request', async () => {
|
||||
await sendRequest(page, 200);
|
||||
});
|
||||
|
||||
const scriptedRow = page
|
||||
.getByTestId('timeline-entry')
|
||||
.filter({ has: page.getByTestId('timeline-badge-pre') });
|
||||
|
||||
await test.step('Open Timeline and expand the scripted (sendRequest) row', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
await expect(scriptedRow).toHaveCount(1);
|
||||
await scriptedRow.getByTestId('timeline-item-header').click();
|
||||
});
|
||||
|
||||
await test.step('Source file points to the request, not the collection', async () => {
|
||||
const sourceFile = scriptedRow.getByTestId('timeline-source-file');
|
||||
await expect(sourceFile).toBeVisible();
|
||||
await expect(sourceFile).toHaveText('scoped-driver.yml');
|
||||
await expect(sourceFile).not.toContainText('opencollection.yml');
|
||||
});
|
||||
|
||||
await test.step('Clicking the source link opens the request Script tab', async () => {
|
||||
await scriptedRow.getByTestId('timeline-source-link').click();
|
||||
await expect(page.locator('.request-tab.active')).toContainText(requestName);
|
||||
await expect(page.getByTestId('responsive-tab-script')).toHaveClass(/active/);
|
||||
});
|
||||
});
|
||||
|
||||
test('request-level sendRequest is attributed to the request, not the parent folder script', async ({
|
||||
page,
|
||||
createTmpDir
|
||||
}) => {
|
||||
const collectionName = 'timeline-scope-folder';
|
||||
const folderName = 'auth';
|
||||
const requestName = 'folder-driver';
|
||||
const url = 'http://localhost:8081/ping';
|
||||
|
||||
await test.step('Create a folder with a (non-empty) folder-level pre-request script', async () => {
|
||||
await createCollection(page, collectionName, await createTmpDir(collectionName), 'yml');
|
||||
await createFolder(page, folderName, collectionName);
|
||||
await expandFolder(page, folderName);
|
||||
// Folder script runs after the collection and overwrites the scope — used to
|
||||
// be what a nested request's sendRequest inherited.
|
||||
await addFolderScript(page, folderName, 'pre-request', `bru.setVar('folderRan', true);`);
|
||||
});
|
||||
|
||||
await test.step('Create a request inside the folder whose pre-request script issues a sendRequest', async () => {
|
||||
await createRequest(page, requestName, folderName, { url, inFolder: true });
|
||||
await page.locator('.collection-item-name').filter({ hasText: requestName }).first().click();
|
||||
await addPreRequestScript(page, `await bru.sendRequest({ url: "${url}", method: "GET" });`);
|
||||
await saveRequest(page);
|
||||
});
|
||||
|
||||
await test.step('Send the request', async () => {
|
||||
await sendRequest(page, 200);
|
||||
});
|
||||
|
||||
const scriptedRow = page
|
||||
.getByTestId('timeline-entry')
|
||||
.filter({ has: page.getByTestId('timeline-badge-pre') });
|
||||
|
||||
await test.step('Open Timeline and expand the scripted (sendRequest) row', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
await expect(scriptedRow).toHaveCount(1);
|
||||
await scriptedRow.getByTestId('timeline-item-header').click();
|
||||
});
|
||||
|
||||
await test.step('Source file points to the request file, not the folder script', async () => {
|
||||
const sourceFile = scriptedRow.getByTestId('timeline-source-file');
|
||||
await expect(sourceFile).toBeVisible();
|
||||
await expect(sourceFile).toHaveText('auth/folder-driver.yml');
|
||||
await expect(sourceFile).not.toContainText('auth/folder.yml');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -54,55 +54,53 @@ test.describe('Timeline — scripted requests (sendRequest / runRequest)', () =>
|
||||
|
||||
await test.step('Open Timeline and assert four rows', async () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
await expect(rows).toHaveCount(4);
|
||||
});
|
||||
|
||||
await test.step('Filter chips appear with correct counts (only Main + Pre-Request show)', async () => {
|
||||
const chips = page.locator('.timeline-filter-bar .timeline-chip');
|
||||
await expect(chips).toHaveCount(3); // All, Main, Pre-Request
|
||||
await test.step('Filter chips appear with correct counts (only Request + Pre-Request show)', async () => {
|
||||
const filterBar = page.getByTestId('timeline-filter-bar');
|
||||
await expect(filterBar.getByRole('button')).toHaveCount(3); // All, Request, Pre-Request
|
||||
|
||||
const countFor = (label: string) =>
|
||||
chips.filter({ hasText: label }).locator('.timeline-chip-count').first();
|
||||
const countFor = (id: string) =>
|
||||
page.getByTestId(`timeline-chip-${id}`).getByTestId('timeline-chip-count');
|
||||
|
||||
await expect(countFor('All')).toHaveText('4');
|
||||
await expect(countFor('Main')).toHaveText('1');
|
||||
await expect(countFor('Pre-Request')).toHaveText('3');
|
||||
await expect(countFor('all')).toHaveText('4');
|
||||
await expect(countFor('main')).toHaveText('1');
|
||||
await expect(countFor('pre')).toHaveText('3');
|
||||
});
|
||||
|
||||
await test.step('Rows are sorted newest-first; the collection-script row sits last', async () => {
|
||||
const rows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
|
||||
// Execution order: collection → folder → request → main.
|
||||
// Newest-first: main → request-script → folder-script → collection-script.
|
||||
await expect(rows.nth(0).locator('.tl-badge--main')).toHaveCount(1);
|
||||
await expect(rows.nth(0).getByTestId('timeline-badge-main')).toHaveCount(1);
|
||||
|
||||
const requestScriptRow = rows.nth(1);
|
||||
await expect(requestScriptRow.locator('.tl-badge--scripted')).toHaveCount(1);
|
||||
await expect(requestScriptRow.locator('.tl-col-url')).toContainText('/query');
|
||||
await expect(requestScriptRow.getByTestId('timeline-badge-pre')).toHaveCount(1);
|
||||
await expect(requestScriptRow.getByTestId('timeline-url')).toContainText('/query');
|
||||
|
||||
const folderScriptRow = rows.nth(2);
|
||||
await expect(folderScriptRow.locator('.tl-badge--scripted')).toHaveCount(1);
|
||||
await expect(folderScriptRow.locator('.tl-col-url')).toContainText('/headers');
|
||||
await expect(folderScriptRow.getByTestId('timeline-badge-pre')).toHaveCount(1);
|
||||
await expect(folderScriptRow.getByTestId('timeline-url')).toContainText('/headers');
|
||||
|
||||
const collectionScriptRow = rows.nth(3);
|
||||
await expect(collectionScriptRow.locator('.tl-badge--scripted')).toHaveCount(1);
|
||||
await expect(collectionScriptRow.locator('.tl-col-url')).toContainText('/echo/path');
|
||||
await expect(collectionScriptRow.getByTestId('timeline-badge-pre')).toHaveCount(1);
|
||||
await expect(collectionScriptRow.getByTestId('timeline-url')).toContainText('/echo/path');
|
||||
});
|
||||
|
||||
await test.step('Clicking the Pre-Request chip narrows to the three sendRequest rows', async () => {
|
||||
const chips = page.locator('.timeline-filter-bar .timeline-chip');
|
||||
await chips.filter({ hasText: 'Pre-Request' }).click();
|
||||
await page.getByTestId('timeline-chip-pre').click();
|
||||
|
||||
const visibleRows = page.locator('.timeline-container .tl-row-wrap');
|
||||
const visibleRows = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
await expect(visibleRows).toHaveCount(3);
|
||||
await expect(visibleRows.locator('.tl-badge--scripted')).toHaveCount(3);
|
||||
await expect(visibleRows.getByTestId('timeline-badge-pre')).toHaveCount(3);
|
||||
});
|
||||
|
||||
await test.step('Clicking All restores every row', async () => {
|
||||
const chips = page.locator('.timeline-filter-bar .timeline-chip');
|
||||
await chips.filter({ hasText: 'All' }).click();
|
||||
await expect(page.locator('.timeline-container .tl-row-wrap')).toHaveCount(4);
|
||||
await page.getByTestId('timeline-chip-all').click();
|
||||
await expect(page.getByTestId('timeline-container').getByTestId('timeline-entry')).toHaveCount(4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -139,15 +137,15 @@ test.describe('Timeline — scripted requests (sendRequest / runRequest)', () =>
|
||||
});
|
||||
|
||||
await test.step('Runner timeline shows main + sendRequest + runRequest rows', async () => {
|
||||
const rows = page.locator('.tl-row-wrap');
|
||||
const rows = page.getByTestId('timeline-entry');
|
||||
await expect(rows).toHaveCount(3, { timeout: 10000 });
|
||||
|
||||
await expect(rows.locator('.tl-badge--main')).toHaveCount(1);
|
||||
await expect(rows.locator('.tl-badge--scripted')).toHaveCount(1);
|
||||
await expect(rows.locator('.tl-badge--run-request')).toHaveCount(1);
|
||||
await expect(rows.getByTestId('timeline-badge-main')).toHaveCount(1);
|
||||
await expect(rows.getByTestId('timeline-badge-pre')).toHaveCount(1);
|
||||
await expect(rows.getByTestId('timeline-badge-post')).toHaveCount(1);
|
||||
|
||||
// The runner view never shows the filter chip bar (no chip-bar UI here).
|
||||
await expect(page.locator('.timeline-filter-bar')).toHaveCount(0);
|
||||
await expect(page.getByTestId('timeline-filter-bar')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ test.describe('Timeline URL Update', () => {
|
||||
await selectResponsePaneTab(page, 'Timeline');
|
||||
|
||||
// Get all timeline entries
|
||||
const timelineItems = page.locator('.tl-row-wrap');
|
||||
const timelineItems = page.getByTestId('timeline-container').getByTestId('timeline-entry');
|
||||
await expect(timelineItems).toHaveCount(2, { timeout: 5000 });
|
||||
|
||||
// Most recent entry (first in list) should show the second URL
|
||||
|
||||
Reference in New Issue
Block a user