From 29c26e47249f68ec4163ee396d176af9b2e54725 Mon Sep 17 00:00:00 2001 From: devsi-bruno Date: Mon, 7 Sep 2026 19:30:18 +0530 Subject: [PATCH 1/2] test(playground): cover bundled libraries in the quickjs sandbox Covers chai, Buffer/btoa/atob, path and ajv in a tests script; axios.get through the sandbox shim; the playground-specific rejection for jsonwebtoken; moment and uuid in a pre-request script, asserted on the outgoing header; and crypto-js in a post-response script. --- .../tests/playground/script-execution.spec.ts | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts b/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts index a05d209d..cdd4d31b 100644 --- a/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts +++ b/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts @@ -24,6 +24,97 @@ test('tv4 validates against a schema', function () { }); `; +const LIBRARY_FUNCTIONS_SCRIPT = ` +const chai = require('chai'); +const path = require('path'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); + +test('chai.expect and chai.assert', function () { + chai.expect(2 + 3).to.equal(5); + if (typeof chai.assert === 'function') { + chai.assert.equal(4, 4); + } +}); + +test('Buffer btoa atob globals', function () { + expect(Buffer.from('hello').toString('base64')).to.equal('aGVsbG8='); + expect(Buffer.from('6272756e6f', 'hex').toString('utf8')).to.equal('bruno'); + expect(btoa('hello')).to.equal('aGVsbG8='); + expect(atob('aGVsbG8=')).to.equal('hello'); +}); + +test('path.resolve join and basename', function () { + expect(path.resolve('/a/b', '../c')).to.equal('/a/c'); + expect(path.join('a', 'b')).to.include('b'); + expect(path.basename('foo.txt')).to.equal('foo.txt'); +}); + +test('ajv with ajv-formats', function () { + const ajv = new Ajv(); + addFormats(ajv); + const validate = ajv.compile({ type: 'string', format: 'email' }); + expect(validate('qa@usebruno.com')).to.equal(true); + expect(validate('not-an-email')).to.equal(false); +}); +`; + +const AXIOS_GET_SCRIPT = ` +const axios = require('axios'); +const url = bru.interpolate('{{host}}/api/users'); +const echo = await axios.get(url); + +test('axios.get via the sandbox shim', function () { + expect(echo.status).to.equal(200); + expect(echo.data.users[0].name).to.equal('Ada'); +}); +`; + +const JWT_UNSUPPORTED_SCRIPT = ` +test('jsonwebtoken is not supported in the playground', function () { + var message = ''; + try { + require('jsonwebtoken'); + } catch (e) { + message = String(e && e.message ? e.message : e); + } + expect(message).to.contain('not currently supported in the docs playground'); +}); +`; + +const PRE_REQUEST_MOMENT_SCRIPT = ` +const moment = require('moment'); +const { v4 } = require('uuid'); +const stamp = moment.utc('2026-09-03T12:00:00Z').format('YYYY-MM-DD'); +req.setHeader('X-Moment-Date', stamp); +bru.setVar('preRequestMoment', stamp); +bru.setVar('preRequestUuid', v4()); +`; + +const PRE_REQUEST_MOMENT_TESTS = ` +test('pre-request moment and uuid ran before send', function () { + expect(bru.getVar('preRequestMoment')).to.equal('2026-09-03'); + expect(bru.getVar('preRequestUuid')).to.match( + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i + ); +}); +`; + +const POST_RESPONSE_CRYPTO_SCRIPT = ` +const CryptoJS = require('crypto-js'); +const body = typeof res.body === 'string' ? res.body : JSON.stringify(res.body); +bru.setVar('responseSha256', CryptoJS.SHA256(body).toString()); +`; + +const POST_RESPONSE_CRYPTO_TESTS = ` +test('post-response crypto-js hashed the body', function () { + const digest = bru.getVar('responseSha256'); + expect(digest).to.be.a('string'); + expect(digest).to.have.lengthOf(64); + expect(digest).to.match(/^[0-9a-f]{64}$/); +}); +`; + const REQUIRE_FS_TESTS_SCRIPT = ` test('ran before the throw', function () { expect(1).to.equal(1); }); require('fs'); @@ -70,6 +161,119 @@ test.describe('playground script execution', () => { await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); }); + test('runs chai Buffer path and ajv library functions on Send', async ({ page, playground, responsePane }) => { + await page.route('**/api/users**', (route) => + route.fulfill({ + status: 200, + headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, + body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) + }) + ); + + await page.goto('/#/?pg=1&dock=bottom'); + await playground.openSidebarItem('get users'); + await playground.selectTab('tests'); + await setEditorScript(page, playground.testsEditor, LIBRARY_FUNCTIONS_SCRIPT); + + await responsePane.send(); + await responsePane.switchToTab('tests'); + + await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); + await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + }); + + test('runs axios.get through the sandbox shim on Send', async ({ page, playground, responsePane }) => { + await page.route('**/api/users**', (route) => + route.fulfill({ + status: 200, + headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, + body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) + }) + ); + + await page.goto('/#/?pg=1&dock=bottom'); + await playground.openSidebarItem('get users'); + await playground.selectTab('tests'); + await setEditorScript(page, playground.testsEditor, AXIOS_GET_SCRIPT); + + await responsePane.send(); + await responsePane.switchToTab('tests'); + + await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); + await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + }); + + test('jsonwebtoken require is rejected with a playground-specific error', async ({ page, playground, responsePane }) => { + await page.route('**/api/users**', (route) => + route.fulfill({ + status: 200, + headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, + body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) + }) + ); + + await page.goto('/#/?pg=1&dock=bottom'); + await playground.openSidebarItem('get users'); + await playground.selectTab('tests'); + await setEditorScript(page, playground.testsEditor, JWT_UNSUPPORTED_SCRIPT); + + await responsePane.send(); + await responsePane.switchToTab('tests'); + + await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); + await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + }); + + test('uses moment and uuid in a pre-request script on Send', async ({ page, playground, responsePane }) => { + let momentHeader = ''; + await page.route('**/api/users**', (route) => { + momentHeader = route.request().headers()['x-moment-date'] || ''; + return route.fulfill({ + status: 200, + headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, + body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) + }); + }); + + await page.goto('/#/?pg=1&dock=bottom'); + await playground.openSidebarItem('get users'); + await playground.selectTab('scripts'); + await setEditorScript(page, playground.preRequestScriptEditor, PRE_REQUEST_MOMENT_SCRIPT); + await playground.selectTab('tests'); + await setEditorScript(page, playground.testsEditor, PRE_REQUEST_MOMENT_TESTS); + + await responsePane.send(); + await responsePane.switchToTab('tests'); + + await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); + await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + expect(momentHeader).toBe('2026-09-03'); + }); + + test('runs crypto-js in a post-response script on Send', async ({ page, playground, responsePane }) => { + await page.route('**/api/users**', (route) => + route.fulfill({ + status: 200, + headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, + body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) + }) + ); + + await page.goto('/#/?pg=1&dock=bottom'); + await playground.openSidebarItem('get users'); + await playground.selectTab('scripts'); + await page.getByTestId('scripts-tabs-tab-post-response').click(); + await setEditorScript(page, playground.postResponseScriptEditor, POST_RESPONSE_CRYPTO_SCRIPT); + await playground.selectTab('tests'); + await setEditorScript(page, playground.testsEditor, POST_RESPONSE_CRYPTO_TESTS); + + await responsePane.send(); + await responsePane.switchToTab('tests'); + + await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); + await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + }); + test('a tests script that throws shows a dismissable Test Script Error card and keeps the tests that ran', async ({ page, playground, responsePane }) => { await responsePane.mockUsersResponse(JSON.stringify({ users: [] })); From 75eabc6bc2f476400091a134b0e0d25a0dd6d4fc Mon Sep 17 00:00:00 2001 From: devsi-bruno Date: Tue, 8 Sep 2026 15:35:19 +0530 Subject: [PATCH 2/2] test(playground): simplify script execution tests --- .../tests/playground/script-execution.spec.ts | 272 +++++++----------- 1 file changed, 103 insertions(+), 169 deletions(-) diff --git a/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts b/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts index cdd4d31b..dd1963b6 100644 --- a/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts +++ b/packages/bruno-api-docs/e2e/tests/playground/script-execution.spec.ts @@ -1,6 +1,14 @@ import { test, expect } from '../../playwright'; import type { Page } from '@playwright/test'; import type { CodeEditorComponent } from '../../components/code-editor/code-editor.component'; +import type { PlaygroundComponent } from '../../components/playground.component'; +import type { ResponsePaneComponent } from '../../components/playground/response-pane.component'; + +const USERS_BODY = JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }); +const EMPTY_USERS_BODY = JSON.stringify({ users: [] }); + +// SHA256 of USERS_BODY, i.e. what the sandbox must produce from CryptoJS.SHA256(JSON.stringify(res.body)). +const USERS_BODY_SHA256 = '714f77ec08cc80082fc8d5a05e4cef08dda92cad53b7ecfebc5032c81d816c5e'; const LIBRARY_TESTS_SCRIPT = ` const moment = require('moment'); @@ -8,6 +16,10 @@ const CryptoJS = require('crypto-js'); const { v4, validate } = require('uuid'); const { nanoid } = require('nanoid'); const tv4 = require('tv4'); +const chai = require('chai'); +const path = require('path'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); test('moment formats a date', function () { expect(moment('2026-01-02').format('YYYY-MM-DD')).to.equal('2026-01-02'); @@ -22,19 +34,10 @@ test('crypto-js hashes and uuid validates', function () { test('tv4 validates against a schema', function () { expect(tv4.validate({ a: 1 }, { type: 'object' })).to.equal(true); }); -`; - -const LIBRARY_FUNCTIONS_SCRIPT = ` -const chai = require('chai'); -const path = require('path'); -const Ajv = require('ajv'); -const addFormats = require('ajv-formats'); test('chai.expect and chai.assert', function () { chai.expect(2 + 3).to.equal(5); - if (typeof chai.assert === 'function') { - chai.assert.equal(4, 4); - } + chai.assert.equal(4, 4); }); test('Buffer btoa atob globals', function () { @@ -46,39 +49,27 @@ test('Buffer btoa atob globals', function () { test('path.resolve join and basename', function () { expect(path.resolve('/a/b', '../c')).to.equal('/a/c'); - expect(path.join('a', 'b')).to.include('b'); + expect(path.join('a', 'b')).to.equal('a/b'); expect(path.basename('foo.txt')).to.equal('foo.txt'); }); test('ajv with ajv-formats', function () { const ajv = new Ajv(); addFormats(ajv); - const validate = ajv.compile({ type: 'string', format: 'email' }); - expect(validate('qa@usebruno.com')).to.equal(true); - expect(validate('not-an-email')).to.equal(false); + const validateEmail = ajv.compile({ type: 'string', format: 'email' }); + expect(validateEmail('qa@usebruno.com')).to.equal(true); + expect(validateEmail('not-an-email')).to.equal(false); }); `; const AXIOS_GET_SCRIPT = ` const axios = require('axios'); const url = bru.interpolate('{{host}}/api/users'); -const echo = await axios.get(url); +const response = await axios.get(url); test('axios.get via the sandbox shim', function () { - expect(echo.status).to.equal(200); - expect(echo.data.users[0].name).to.equal('Ada'); -}); -`; - -const JWT_UNSUPPORTED_SCRIPT = ` -test('jsonwebtoken is not supported in the playground', function () { - var message = ''; - try { - require('jsonwebtoken'); - } catch (e) { - message = String(e && e.message ? e.message : e); - } - expect(message).to.contain('not currently supported in the docs playground'); + expect(response.status).to.equal(200); + expect(response.data.users[0].name).to.equal('Ada'); }); `; @@ -102,16 +93,12 @@ test('pre-request moment and uuid ran before send', function () { const POST_RESPONSE_CRYPTO_SCRIPT = ` const CryptoJS = require('crypto-js'); -const body = typeof res.body === 'string' ? res.body : JSON.stringify(res.body); -bru.setVar('responseSha256', CryptoJS.SHA256(body).toString()); +bru.setVar('responseSha256', CryptoJS.SHA256(JSON.stringify(res.body)).toString()); `; const POST_RESPONSE_CRYPTO_TESTS = ` test('post-response crypto-js hashed the body', function () { - const digest = bru.getVar('responseSha256'); - expect(digest).to.be.a('string'); - expect(digest).to.have.lengthOf(64); - expect(digest).to.match(/^[0-9a-f]{64}$/); + expect(bru.getVar('responseSha256')).to.equal('${USERS_BODY_SHA256}'); }); `; @@ -121,6 +108,10 @@ require('fs'); test('never reached', function () { expect(1).to.equal(1); }); `; +const REQUIRE_JWT_TESTS_SCRIPT = ` +require('jsonwebtoken'); +`; + const UNREACHABLE_HOST_POST_RESPONSE_SCRIPT = ` const axios = require('axios'); await axios.get('https://unreachable.invalid/get'); @@ -136,153 +127,98 @@ const setEditorScript = async (page: Page, editor: CodeEditorComponent, script: await page.keyboard.insertText(script); }; -test.describe('playground script execution', () => { - test.use({ viewport: { width: 1280, height: 900 } }); +type PlaygroundFixtures = { + page: Page; + playground: PlaygroundComponent; + responsePane: ResponsePaneComponent; +}; - test('runs a tests script using the safe-mode libraries on Send', async ({ page, playground, responsePane }) => { - await page.route('**/api/users**', (route) => - route.fulfill({ - status: 200, - headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, - body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) - }) - ); +type Scripts = { tests?: string; preRequest?: string; postResponse?: string }; - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); +const sendWithScripts = async ( + { page, playground, responsePane }: PlaygroundFixtures, + { tests, preRequest, postResponse }: Scripts +): Promise => { + await page.goto('/#/?pg=1&dock=bottom'); + await playground.openSidebarItem('get users'); - await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, LIBRARY_TESTS_SCRIPT); - - await responsePane.send(); - await responsePane.switchToTab('tests'); + if (preRequest) { + await playground.selectTab('scripts'); + await setEditorScript(page, playground.preRequestScriptEditor, preRequest); + } - await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); - await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); - }); + if (postResponse) { + await playground.selectTab('scripts'); + await page.getByTestId('scripts-tabs-tab-post-response').click(); + await setEditorScript(page, playground.postResponseScriptEditor, postResponse); + } - test('runs chai Buffer path and ajv library functions on Send', async ({ page, playground, responsePane }) => { - await page.route('**/api/users**', (route) => - route.fulfill({ - status: 200, - headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, - body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) - }) - ); - - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); + if (tests) { await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, LIBRARY_FUNCTIONS_SCRIPT); + await setEditorScript(page, playground.testsEditor, tests); + } - await responsePane.send(); - await responsePane.switchToTab('tests'); + await responsePane.send(); +}; - await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); - await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); - }); +const runTestsScript = async ( + fixtures: PlaygroundFixtures, + scripts: Scripts & { passed: number } +): Promise => { + await sendWithScripts(fixtures, scripts); - test('runs axios.get through the sandbox shim on Send', async ({ page, playground, responsePane }) => { - await page.route('**/api/users**', (route) => - route.fulfill({ - status: 200, - headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, - body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) - }) - ); - - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, AXIOS_GET_SCRIPT); + await fixtures.responsePane.switchToTab('tests'); + const summary = `Tests (${scripts.passed}), Passed: ${scripts.passed}, Failed: 0`; + await expect(fixtures.responsePane.testsPanel.getByText(summary)).toBeVisible(); +}; - await responsePane.send(); - await responsePane.switchToTab('tests'); +test.describe('playground script execution', () => { + test.use({ viewport: { width: 1280, height: 900 } }); - await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); - await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); - }); + test('runs a tests script using the safe-mode libraries on Send', async ({ page, playground, responsePane }) => { + await responsePane.mockUsersResponse(USERS_BODY); - test('jsonwebtoken require is rejected with a playground-specific error', async ({ page, playground, responsePane }) => { - await page.route('**/api/users**', (route) => - route.fulfill({ - status: 200, - headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, - body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) - }) - ); - - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, JWT_UNSUPPORTED_SCRIPT); + await runTestsScript({ page, playground, responsePane }, { tests: LIBRARY_TESTS_SCRIPT, passed: 7 }); + }); - await responsePane.send(); - await responsePane.switchToTab('tests'); + test('runs axios.get through the sandbox shim on Send', async ({ page, playground, responsePane }) => { + await responsePane.mockUsersResponse(USERS_BODY); - await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); - await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + await runTestsScript({ page, playground, responsePane }, { tests: AXIOS_GET_SCRIPT, passed: 1 }); }); test('uses moment and uuid in a pre-request script on Send', async ({ page, playground, responsePane }) => { + await responsePane.mockUsersResponse(USERS_BODY); + let momentHeader = ''; - await page.route('**/api/users**', (route) => { - momentHeader = route.request().headers()['x-moment-date'] || ''; - return route.fulfill({ - status: 200, - headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, - body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) - }); + page.on('request', (request) => { + if (request.url().includes('/api/users')) { + momentHeader = request.headers()['x-moment-date']; + } }); - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('scripts'); - await setEditorScript(page, playground.preRequestScriptEditor, PRE_REQUEST_MOMENT_SCRIPT); - await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, PRE_REQUEST_MOMENT_TESTS); - - await responsePane.send(); - await responsePane.switchToTab('tests'); + await runTestsScript({ page, playground, responsePane }, { + tests: PRE_REQUEST_MOMENT_TESTS, + preRequest: PRE_REQUEST_MOMENT_SCRIPT, + passed: 1 + }); - await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); - await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); expect(momentHeader).toBe('2026-09-03'); }); test('runs crypto-js in a post-response script on Send', async ({ page, playground, responsePane }) => { - await page.route('**/api/users**', (route) => - route.fulfill({ - status: 200, - headers: { 'content-type': 'application/json', 'access-control-allow-origin': '*' }, - body: JSON.stringify({ users: [{ id: 1, name: 'Ada' }] }) - }) - ); - - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('scripts'); - await page.getByTestId('scripts-tabs-tab-post-response').click(); - await setEditorScript(page, playground.postResponseScriptEditor, POST_RESPONSE_CRYPTO_SCRIPT); - await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, POST_RESPONSE_CRYPTO_TESTS); + await responsePane.mockUsersResponse(USERS_BODY); - await responsePane.send(); - await responsePane.switchToTab('tests'); - - await expect(page.getByText(/Passed: [1-9]\d*, Failed: 0/).first()).toBeVisible(); - await expect(page.getByText(/Failed: [1-9]/)).toHaveCount(0); + await runTestsScript({ page, playground, responsePane }, { + tests: POST_RESPONSE_CRYPTO_TESTS, + postResponse: POST_RESPONSE_CRYPTO_SCRIPT, + passed: 1 + }); }); test('a tests script that throws shows a dismissable Test Script Error card and keeps the tests that ran', async ({ page, playground, responsePane }) => { - await responsePane.mockUsersResponse(JSON.stringify({ users: [] })); - - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('tests'); - await setEditorScript(page, playground.testsEditor, REQUIRE_FS_TESTS_SCRIPT); - - await responsePane.send(); + await responsePane.mockUsersResponse(EMPTY_USERS_BODY); + await sendWithScripts({ page, playground, responsePane }, { tests: REQUIRE_FS_TESTS_SCRIPT }); await expect(responsePane.status).toContainText('200'); await expect(responsePane.scriptErrors.getByTestId('error-title')).toHaveText('Test Script Error'); @@ -298,16 +234,20 @@ test.describe('playground script execution', () => { await expect(responsePane.testsScriptErrors).toHaveCount(0); await responsePane.switchToTab('response'); await expect(responsePane.scriptErrors).toHaveCount(0); - await expect(responsePane.bodyEditor.surface).toBeVisible(); + await expect(responsePane.bodyEditorCanvas).toBeVisible(); }); - test('a pre-request script that throws shows a Pre-Request Script Error card instead of a response', async ({ page, playground, responsePane }) => { - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('scripts'); - await setEditorScript(page, playground.preRequestScriptEditor, REQUIRE_LODASH_PRE_REQUEST_SCRIPT); + test('a tests script requiring jsonwebtoken shows a Test Script Error card', async ({ page, playground, responsePane }) => { + await responsePane.mockUsersResponse(EMPTY_USERS_BODY); + await sendWithScripts({ page, playground, responsePane }, { tests: REQUIRE_JWT_TESTS_SCRIPT }); - await responsePane.send(); + await expect(responsePane.status).toContainText('200'); + await expect(responsePane.scriptErrors.getByTestId('error-title')).toHaveText('Test Script Error'); + await expect(responsePane.scriptErrors.getByTestId('error-message')).toContainText('not currently supported in the docs playground'); + }); + + test('a pre-request script that throws shows a Pre-Request Script Error card instead of a response', async ({ page, playground, responsePane }) => { + await sendWithScripts({ page, playground, responsePane }, { preRequest: REQUIRE_LODASH_PRE_REQUEST_SCRIPT }); await expect(responsePane.errorTitle).toHaveText('Pre-Request Script Error'); await expect(responsePane.errorMessage).toContainText('\'lodash\' is only available in the Bruno desktop app\'s developer mode'); @@ -315,21 +255,15 @@ test.describe('playground script execution', () => { }); test('a post-response script that throws shows a Post-Response Script Error card while the body still renders', async ({ page, playground, responsePane }) => { - await responsePane.mockUsersResponse(JSON.stringify({ users: [] })); + await responsePane.mockUsersResponse(EMPTY_USERS_BODY); await page.route('https://unreachable.invalid/**', (route) => route.abort('namenotresolved')); - await page.goto('/#/?pg=1&dock=bottom'); - await playground.openSidebarItem('get users'); - await playground.selectTab('scripts'); - await page.getByTestId('scripts-tabs-tab-post-response').click(); - await setEditorScript(page, playground.postResponseScriptEditor, UNREACHABLE_HOST_POST_RESPONSE_SCRIPT); - - await responsePane.send(); + await sendWithScripts({ page, playground, responsePane }, { postResponse: UNREACHABLE_HOST_POST_RESPONSE_SCRIPT }); await expect(responsePane.status).toContainText('200'); await expect(responsePane.scriptErrors.getByTestId('error-title')).toHaveText('Post-Response Script Error'); await expect(responsePane.scriptErrors.getByTestId('error-message')).toHaveText('Network Error'); - await expect(responsePane.bodyEditor.surface).toBeVisible(); + await expect(responsePane.bodyEditorCanvas).toBeVisible(); await responsePane.switchToTab('tests'); await expect(responsePane.testsPanel.getByText('Tests (1), Passed: 0, Failed: 1')).toBeVisible();