mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-03 01:18:32 +00:00
Compare commits
2 Commits
dependabot
...
v2.13.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63aaaac517 | ||
|
|
a66e0a314b |
4
.github/workflows/npm-bru-cli.yml
vendored
4
.github/workflows/npm-bru-cli.yml
vendored
@@ -25,8 +25,8 @@ jobs:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
|
||||
6
.github/workflows/ssl-tests.yml
vendored
6
.github/workflows/ssl-tests.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node Dependencies
|
||||
uses: ./.github/actions/common/setup-node-deps
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node Dependencies
|
||||
uses: ./.github/actions/common/setup-node-deps
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node Dependencies
|
||||
uses: ./.github/actions/common/setup-node-deps
|
||||
|
||||
12
.github/workflows/tests.yml
vendored
12
.github/workflows/tests.yml
vendored
@@ -13,8 +13,8 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -66,8 +66,8 @@ jobs:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
@@ -108,8 +108,8 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: v22.11.x
|
||||
- name: Install dependencies
|
||||
|
||||
@@ -37,37 +37,13 @@ Bruno 直接在您的电脑文件夹中存储您的 API 信息。我们使用纯
|
||||
|
||||
Bruno 仅限离线使用。我们计划永不向 Bruno 添加云同步功能。我们重视您的数据隐私,并认为它应该留在您的设备上。阅读我们的长期愿景 [点击查看](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
[下载 Bruno](https://www.usebruno.com/downloads)
|
||||
|
||||
📢 观看我们在印度 FOSS 3.0 会议上的最新演讲 [点击查看](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
## 商业版本 ✨
|
||||
### 安装
|
||||
|
||||
我们的大多数功能都是免费且开源的。
|
||||
我们致力于在 [开源与可持续性发展](https://github.com/usebruno/bruno/discussions/269) 之间取得和谐的平衡
|
||||
|
||||
欢迎使用我们的 [付费版本](https://www.usebruno.com/pricing) ,看看附加的功能是否对您或团队有所帮助! <br/>
|
||||
|
||||
## 目录
|
||||
- [安装](#安装)
|
||||
- [特性](#特性)
|
||||
- [跨平台使用 🖥️](#跨平台使用-)
|
||||
- [通过Git协作 👩💻🧑💻](#通过git协作-)
|
||||
- [重要链接 📌](#重要链接-)
|
||||
- [展示 🎥](#展示-)
|
||||
- [分享评价 📣](#分享评价-)
|
||||
- [发布到新的包管理器](#发布到新的包管理器)
|
||||
- [联系方式 🌐](#联系方式-)
|
||||
- [商标](#商标)
|
||||
- [贡献 👩💻🧑💻](#贡献-)
|
||||
- [作者](#作者)
|
||||
- [许可证 📄](#许可证-)
|
||||
|
||||
## 安装
|
||||
|
||||
Bruno 可以在我们的 [网站上下载](https://www.usebruno.com/downloads) 适用于Mac、Windows 和 Linux 的可执行文件。
|
||||
Bruno 可以在我们的 [网站上下载](https://www.usebruno.com/downloads) Mac、Windows 和 Linux 的可执行文件。
|
||||
|
||||
您也可以通过包管理器如 Homebrew、Chocolatey、Scoop、Snap 和 Apt 安装 Bruno。
|
||||
|
||||
@@ -82,15 +58,9 @@ choco install bruno
|
||||
scoop bucket add extras
|
||||
scoop install bruno
|
||||
|
||||
# 在 Windows 上用 winget 安装
|
||||
winget install Bruno.Bruno
|
||||
|
||||
# 在 Linux 上用 Snap 安装
|
||||
snap install bruno
|
||||
|
||||
# 在 Linux 上用 Flatpak 安装
|
||||
flatpak install com.usebruno.Bruno
|
||||
|
||||
# 在 Linux 上用 Apt 安装
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo apt update && sudo apt install gpg curl
|
||||
@@ -103,50 +73,67 @@ echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebr
|
||||
sudo apt update && sudo apt install bruno
|
||||
```
|
||||
|
||||
## 特性
|
||||
|
||||
### 跨平台使用 🖥️
|
||||
### 在 Mac 上通过 Homebrew 安装 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### 通过Git协作 👩💻🧑💻
|
||||
### Collaborate 安装 👩💻🧑💻
|
||||
|
||||
或者任何您选择的版本控制系统
|
||||
|
||||
 <br /><br />
|
||||
|
||||
## 重要链接 📌
|
||||
### 重要链接 📌
|
||||
|
||||
- [我们的愿景](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [路线图](https://www.usebruno.com/roadmap)
|
||||
- [路线图](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [文档](https://docs.usebruno.com)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno)
|
||||
- [网站](https://www.usebruno.com)
|
||||
- [价格](https://www.usebruno.com/pricing)
|
||||
- [下载](https://www.usebruno.com/downloads)
|
||||
- [GitHub 赞助](https://github.com/sponsors/helloanoop).
|
||||
|
||||
## 展示 🎥
|
||||
### 展示 🎥
|
||||
|
||||
- [Testimonials](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Knowledge Hub](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
## 分享评价 📣
|
||||
### 支持 ❤️
|
||||
|
||||
如果您喜欢 Bruno 并想支持我们的开源工作,请考虑通过 [GitHub Sponsors](https://github.com/sponsors/helloanoop) 来赞助我们。
|
||||
|
||||
### 分享评价 📣
|
||||
|
||||
如果 Bruno 在您的工作和团队中帮助了您,请不要忘记在我们的 GitHub 讨论上分享您的 [评价](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
## 发布到新的包管理器
|
||||
### 发布到新的包管理器
|
||||
|
||||
如需了解更多信息,请参见 [此处](../publishing/publishing_cn.md) 。
|
||||
有关更多信息,请参见 [此处](../publishing/publishing_cn.md) 。
|
||||
|
||||
## 联系方式 🌐
|
||||
### 贡献 👩💻🧑💻
|
||||
|
||||
我很高兴您希望改进 bruno。请查看 [贡献指南](../contributing/contributing_cn.md)。
|
||||
|
||||
即使您无法通过代码做出贡献,我们仍然欢迎您提出 BUG 和新的功能需求。
|
||||
|
||||
### 作者
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### 联系方式 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Website](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
## 商标
|
||||
### 商标
|
||||
|
||||
**名称**
|
||||
|
||||
@@ -156,20 +143,6 @@ sudo apt update && sudo apt install bruno
|
||||
|
||||
Logo 源自 [OpenMoji](https://openmoji.org/library/emoji-1F436/). License: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
## 贡献 👩💻🧑💻
|
||||
|
||||
很高兴您希望改进 bruno。请查看 [贡献指南](../contributing/contributing_cn.md)。
|
||||
|
||||
即使您无法通过代码做出贡献,我们仍然欢迎您提出 BUG 和新的功能需求。
|
||||
|
||||
## 作者
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 许可证 📄
|
||||
### 许可证 📄
|
||||
|
||||
[MIT](../../license.md)
|
||||
|
||||
@@ -44,11 +44,7 @@ const CollectionSettings = ({ collection }) => {
|
||||
const activeVarsCount = requestVars.filter((v) => v.enabled).length + responseVars.filter((v) => v.enabled).length;
|
||||
const authMode = get(collection, 'root.request.auth', {}).mode || 'none';
|
||||
|
||||
const presets = get(collection, 'brunoConfig.presets', []);
|
||||
const hasPresets = presets && presets.requestUrl !== "";
|
||||
|
||||
const proxyConfig = get(collection, 'brunoConfig.proxy', {});
|
||||
const proxyEnabled = proxyConfig.hostname ? true : false;
|
||||
const clientCertConfig = get(collection, 'brunoConfig.clientCertificates.certs', []);
|
||||
const protobufConfig = get(collection, 'brunoConfig.protobuf', {});
|
||||
|
||||
@@ -167,11 +163,10 @@ const CollectionSettings = ({ collection }) => {
|
||||
</div>
|
||||
<div className={getTabClassname('presets')} role="tab" onClick={() => setTab('presets')}>
|
||||
Presets
|
||||
{hasPresets && <StatusDot />}
|
||||
</div>
|
||||
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
|
||||
Proxy
|
||||
{Object.keys(proxyConfig).length > 0 && proxyEnabled && <StatusDot />}
|
||||
{Object.keys(proxyConfig).length > 0 && <StatusDot />}
|
||||
</div>
|
||||
<div className={getTabClassname('clientCert')} role="tab" onClick={() => setTab('clientCert')}>
|
||||
Client Certificates
|
||||
|
||||
@@ -17,7 +17,7 @@ import NTLMAuth from 'components/RequestPane/Auth/NTLMAuth';
|
||||
import WsseAuth from 'components/RequestPane/Auth/WsseAuth';
|
||||
import ApiKeyAuth from 'components/RequestPane/Auth/ApiKeyAuth';
|
||||
import AwsV4Auth from 'components/RequestPane/Auth/AwsV4Auth';
|
||||
import { humanizeRequestAuthMode, getTreePathFromCollectionToItem } from 'utils/collections/index';
|
||||
import { findItemInCollection, findParentItemInCollection, humanizeRequestAuthMode } from 'utils/collections/index';
|
||||
|
||||
const GrantTypeComponentMap = ({ collection, folder }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -48,7 +48,15 @@ const Auth = ({ collection, folder }) => {
|
||||
let request = get(folder, 'root.request', {});
|
||||
const authMode = get(folder, 'root.request.auth.mode');
|
||||
|
||||
|
||||
const getTreePathFromCollectionToFolder = (collection, _folder) => {
|
||||
let path = [];
|
||||
let item = findItemInCollection(collection, _folder?.uid);
|
||||
while (item) {
|
||||
path.unshift(item);
|
||||
item = findParentItemInCollection(collection, item?.uid);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
const getEffectiveAuthSource = () => {
|
||||
if (authMode !== 'inherit') return null;
|
||||
@@ -61,7 +69,7 @@ const Auth = ({ collection, folder }) => {
|
||||
};
|
||||
|
||||
// Get path from collection to current folder
|
||||
const folderTreePath = getTreePathFromCollectionToItem(collection, folder);
|
||||
const folderTreePath = getTreePathFromCollectionToFolder(collection, folder);
|
||||
|
||||
// Check parent folders to find closest auth configuration
|
||||
// Skip the last item which is the current folder
|
||||
|
||||
@@ -136,7 +136,7 @@ const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentV
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex items-center" data-testid={`env-var-name-${index}`}>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
@@ -153,7 +153,7 @@ const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentV
|
||||
</div>
|
||||
</td>
|
||||
<td className="flex flex-row flex-nowrap items-center">
|
||||
<div className="overflow-hidden grow w-full relative" data-testid={`env-var-value-${index}`}>
|
||||
<div className="overflow-hidden grow w-full relative">
|
||||
<MultiLineEditor
|
||||
theme={storedTheme}
|
||||
collection={_collection}
|
||||
|
||||
@@ -9,8 +9,7 @@ const ModalHeader = ({ title, handleCancel, customHeader, hideClose }) => (
|
||||
<div className="bruno-modal-header">
|
||||
{customHeader ? customHeader : <>{title ? <div className="bruno-modal-header-title">{title}</div> : null}</>}
|
||||
{handleCancel && !hideClose ? (
|
||||
// TODO: Remove data-test-id and use data-testid instead across the codebase.
|
||||
<div className="close cursor-pointer" onClick={handleCancel ? () => handleCancel() : null} data-test-id="modal-close-button" data-testid="modal-close-button">
|
||||
<div className="close cursor-pointer" onClick={handleCancel ? () => handleCancel() : null} data-test-id="modal-close-button">
|
||||
×
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -9,7 +9,6 @@ import WsseAuth from './WsseAuth';
|
||||
import NTLMAuth from './NTLMAuth';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import ApiKeyAuth from './ApiKeyAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
@@ -28,7 +27,6 @@ const getTreePathFromCollectionToItem = (collection, _item) => {
|
||||
};
|
||||
|
||||
const Auth = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode');
|
||||
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
||||
|
||||
@@ -39,7 +37,7 @@ const Auth = ({ item, collection }) => {
|
||||
|
||||
// Save function for request level
|
||||
const save = () => {
|
||||
return dispatch(saveRequest(item.uid, collection.uid));
|
||||
return saveRequest(item.uid, collection.uid);
|
||||
};
|
||||
|
||||
const getEffectiveAuthSource = () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { humanizeRequestBodyMode } from 'utils/collections';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { updateRequestBody } from 'providers/ReduxStore/slices/collections/index';
|
||||
import { toastError } from 'utils/common/error';
|
||||
import { prettifyJSON } from 'utils/common';
|
||||
import { format, applyEdits } from 'jsonc-parser';
|
||||
import xmlFormat from 'xml-formatter';
|
||||
|
||||
const RequestBodyMode = ({ item, collection }) => {
|
||||
@@ -39,7 +39,8 @@ const RequestBodyMode = ({ item, collection }) => {
|
||||
const onPrettify = () => {
|
||||
if (body?.json && bodyMode === 'json') {
|
||||
try {
|
||||
const prettyBodyJson = prettifyJSON(body.json);
|
||||
const edits = format(body.json, undefined, { tabSize: 2, insertSpaces: true });
|
||||
const prettyBodyJson = applyEdits(body.json, edits);
|
||||
dispatch(
|
||||
updateRequestBody({
|
||||
content: prettyBodyJson,
|
||||
|
||||
@@ -96,17 +96,15 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi
|
||||
|
||||
const getMethodText = useCallback((item) => {
|
||||
if (!item) return;
|
||||
|
||||
switch (item.type) {
|
||||
case 'grpc-request':
|
||||
return 'gRPC';
|
||||
case 'ws-request':
|
||||
return 'WS';
|
||||
case 'graphql-request':
|
||||
return 'GQL';
|
||||
default:
|
||||
return item.draft ? get(item, 'draft.request.method') : get(item, 'request.method');
|
||||
const isGrpc = item.type === 'grpc-request';
|
||||
const isWS = item.type === 'ws-request';
|
||||
if (!isWS && !isGrpc) {
|
||||
return item.draft ? get(item, 'draft.request.method') : get(item, 'request.method');
|
||||
}
|
||||
if (isGrpc) {
|
||||
return 'gRPC';
|
||||
}
|
||||
return 'WS';
|
||||
}, [item]);
|
||||
|
||||
if (!item) {
|
||||
@@ -251,28 +249,6 @@ function RequestTabMenu({ onDropdownCreate, collectionRequestTabs, tabIndex, col
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
|
||||
function handleRevertChanges(event) {
|
||||
event.stopPropagation();
|
||||
dropdownTippyRef.current.hide();
|
||||
|
||||
if (!currentTabUid) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const item = findItemInCollection(collection, currentTabUid);
|
||||
if (item.draft) {
|
||||
dispatch(
|
||||
deleteRequestDraft({
|
||||
itemUid: item.uid,
|
||||
collectionUid: collection.uid
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
function handleCloseOtherTabs(event) {
|
||||
dropdownTippyRef.current.hide();
|
||||
|
||||
@@ -340,13 +316,6 @@ function RequestTabMenu({ onDropdownCreate, collectionRequestTabs, tabIndex, col
|
||||
>
|
||||
Clone Request
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item w-full"
|
||||
onClick={handleRevertChanges}
|
||||
disabled={!currentTabItem?.draft}
|
||||
>
|
||||
Revert Changes
|
||||
</button>
|
||||
<button className="dropdown-item w-full" onClick={(e) => handleCloseTab(e, currentTabUid)}>
|
||||
Close
|
||||
</button>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { get } from 'lodash';
|
||||
import find from 'lodash/find';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { updateResponsePaneScrollPosition } from 'providers/ReduxStore/slices/tabs';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { sendRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { Document, Page } from 'react-pdf';
|
||||
import 'pdfjs-dist/build/pdf.worker';
|
||||
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
|
||||
@@ -76,8 +76,6 @@ const QueryResultPreview = ({
|
||||
dispatch(sendRequest(item, collection.uid));
|
||||
};
|
||||
|
||||
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const onScroll = (event) => {
|
||||
dispatch(
|
||||
updateResponsePaneScrollPosition({
|
||||
@@ -129,7 +127,6 @@ const QueryResultPreview = ({
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
theme={displayedTheme}
|
||||
onRun={onRun}
|
||||
onSave={onSave}
|
||||
onScroll={onScroll}
|
||||
value={formattedData}
|
||||
mode={mode}
|
||||
|
||||
@@ -40,9 +40,6 @@ const Wrapper = styled.div`
|
||||
.method-ws {
|
||||
color: ${(props) => props.theme.request.ws};
|
||||
}
|
||||
.method-graphql {
|
||||
color: ${(props) => props.theme.request.gql};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -4,20 +4,18 @@ import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const getMethodFlags = (item) => ({
|
||||
isGrpc: item.type === 'grpc-request',
|
||||
isWS: item.type === 'ws-request',
|
||||
isGraphQL: item.type === 'graphql-request'
|
||||
isWS: item.type === 'ws-request'
|
||||
});
|
||||
|
||||
const getMethodText = (item, { isGrpc, isWS, isGraphQL }) => {
|
||||
const getMethodText = (item, { isGrpc, isWS }) => {
|
||||
if (isGrpc) return 'grpc';
|
||||
if (isWS) return 'ws';
|
||||
if (isGraphQL) return 'gql';
|
||||
return item.request.method.length > 5
|
||||
? item.request.method.substring(0, 3)
|
||||
: item.request.method;
|
||||
};
|
||||
|
||||
const getClassname = (method = '', { isGrpc, isWS, isGraphQL }) => {
|
||||
const getClassname = (method = '', { isGrpc, isWS }) => {
|
||||
method = method.toLocaleLowerCase();
|
||||
return classnames('mr-1', {
|
||||
'method-get': method === 'get',
|
||||
@@ -28,8 +26,7 @@ const getClassname = (method = '', { isGrpc, isWS, isGraphQL }) => {
|
||||
'method-head': method === 'head',
|
||||
'method-options': method === 'options',
|
||||
'method-grpc': isGrpc,
|
||||
'method-ws': isWS,
|
||||
'method-graphql': isGraphQL
|
||||
'method-ws': isWS
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { uuid } from 'utils/common/index';
|
||||
import { environmentSchema } from '@usebruno/schema';
|
||||
import { cloneDeep, has } from 'lodash';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
const initialState = {
|
||||
globalEnvironments: [],
|
||||
@@ -195,15 +195,11 @@ export const globalEnvironmentsUpdateEvent = ({ globalEnvironmentVariables }) =>
|
||||
}
|
||||
|
||||
let variables = cloneDeep(environment?.variables);
|
||||
console.log('globalEnvironmentVariables', globalEnvironmentVariables);
|
||||
|
||||
// "globalEnvironmentVariables" will include only the enabled variables and newly added variables created using the script.
|
||||
// Update the value of each variable if it's present in "globalEnvironmentVariables", otherwise keep the existing value.
|
||||
// update existing values
|
||||
variables = variables?.map?.(variable => ({
|
||||
...variable,
|
||||
value: has(globalEnvironmentVariables, variable?.name)
|
||||
? globalEnvironmentVariables[variable?.name]
|
||||
: variable?.value
|
||||
value: globalEnvironmentVariables?.[variable?.name]
|
||||
}));
|
||||
|
||||
// add new env values
|
||||
|
||||
@@ -103,8 +103,7 @@ const darkTheme = {
|
||||
head: '#d69956'
|
||||
},
|
||||
grpc: '#6366f1',
|
||||
ws: '#f59e0b',
|
||||
gql: '#e535ab'
|
||||
ws: '#f59e0b'
|
||||
},
|
||||
|
||||
requestTabPanel: {
|
||||
|
||||
@@ -103,8 +103,7 @@ const lightTheme = {
|
||||
head: '#ca7811'
|
||||
},
|
||||
grpc: '#6366f1',
|
||||
ws: '#f59e0b',
|
||||
gql: '#e535ab'
|
||||
ws: '#f59e0b'
|
||||
},
|
||||
|
||||
requestTabPanel: {
|
||||
|
||||
@@ -33,22 +33,6 @@ export const getAuthHeaders = (collectionRootAuth, requestAuth) => {
|
||||
value: `Bearer ${get(auth, 'bearer.token', '')}`
|
||||
}
|
||||
];
|
||||
case 'apikey':
|
||||
const apiKeyAuth = get(auth, 'apikey', {});
|
||||
const key = get(apiKeyAuth, 'key', '');
|
||||
const value = get(apiKeyAuth, 'value', '');
|
||||
const placement = get(apiKeyAuth, 'placement', 'header');
|
||||
|
||||
if (placement === 'header') {
|
||||
return [
|
||||
{
|
||||
enabled: true,
|
||||
name: key,
|
||||
value: value
|
||||
}
|
||||
];
|
||||
}
|
||||
return [];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -44,25 +44,13 @@ const createHeaders = (request, headers) => {
|
||||
return enabledHeaders;
|
||||
};
|
||||
|
||||
const createQuery = (queryParams = [], request) => {
|
||||
const params = queryParams
|
||||
const createQuery = (queryParams = []) => {
|
||||
return queryParams
|
||||
.filter((param) => param.enabled && param.type === 'query')
|
||||
.map((param) => ({
|
||||
name: param.name,
|
||||
value: param.value
|
||||
}));
|
||||
|
||||
if (request?.auth?.mode === 'apikey' &&
|
||||
request?.auth?.apikey?.placement === 'queryparams' &&
|
||||
request?.auth?.apikey?.key &&
|
||||
request?.auth?.apikey?.value) {
|
||||
params.push({
|
||||
name: request.auth.apikey.key,
|
||||
value: request.auth.apikey.value
|
||||
});
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
const createPostData = (body) => {
|
||||
@@ -137,7 +125,7 @@ export const buildHarRequest = ({ request, headers }) => {
|
||||
httpVersion: 'HTTP/1.1',
|
||||
cookies: [],
|
||||
headers: createHeaders(request, headers),
|
||||
queryString: createQuery(request.params, request),
|
||||
queryString: createQuery(request.params),
|
||||
postData: createPostData(request.body),
|
||||
headersSize: 0,
|
||||
bodySize: 0,
|
||||
|
||||
@@ -93,9 +93,6 @@ const STATIC_API_HINTS = {
|
||||
'bru.cookies.jar().clear(callback)',
|
||||
'bru.cookies.jar().deleteCookies(url, callback)',
|
||||
'bru.cookies.jar().deleteCookie(url, name, callback)',
|
||||
'bru.utils',
|
||||
'bru.utils.minifyJson(json)',
|
||||
'bru.utils.minifyXml(xml)'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -54,27 +54,10 @@ export const safeStringifyJSON = (obj, indent = false) => {
|
||||
|
||||
export const prettifyJSON = (obj, spaces = 2) => {
|
||||
try {
|
||||
const text = obj.replace(/\\"/g, '"').replace(/\\'/g, "'");
|
||||
const formatted = obj.replace(/\\"/g, '"').replace(/\\'/g, "'");
|
||||
const edits = format(formatted, undefined, { tabSize: spaces, insertSpaces: true });
|
||||
|
||||
const placeholders = [];
|
||||
const modifiedJson = text.replace(/"[^"]*?"|{{[^{}]+}}/g, (match) => {
|
||||
if (match.startsWith('{{')) {
|
||||
const placeholder = `__BRUNO_VAR_PLACEHOLDER_${placeholders.length}__`;
|
||||
placeholders.push(match);
|
||||
return `"${placeholder}"`; // Wrap bare variable in quotes to make it a valid JSON string
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
const edits = format(modifiedJson, undefined, { tabSize: spaces, insertSpaces: true });
|
||||
let result = applyEdits(modifiedJson, edits);
|
||||
|
||||
for (let i = 0; i < placeholders.length; i++) {
|
||||
const placeholder = `__BRUNO_VAR_PLACEHOLDER_${i}__`;
|
||||
result = result.replace(`"${placeholder}"`, placeholders[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
return applyEdits(formatted, edits);
|
||||
} catch (e) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
|
||||
import {
|
||||
normalizeFileName,
|
||||
startsWith,
|
||||
humanizeDate,
|
||||
relativeDate,
|
||||
getContentType,
|
||||
formatSize,
|
||||
prettifyJSON
|
||||
} from './index';
|
||||
import { normalizeFileName, startsWith, humanizeDate, relativeDate, getContentType, formatSize } from './index';
|
||||
|
||||
describe('common utils', () => {
|
||||
describe('normalizeFileName', () => {
|
||||
@@ -192,30 +184,4 @@ describe('common utils', () => {
|
||||
expect(formatSize(NaN)).toBe('0B');
|
||||
});
|
||||
});
|
||||
|
||||
describe('prettifyJSON', () => {
|
||||
it('should prettify a standard JSON string', () => {
|
||||
const input = '{"key":"value","number":123}';
|
||||
const expected = '{\n "key": "value",\n "number": 123\n}';
|
||||
expect(prettifyJSON(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should handle JSON with a Bruno variable as a value', () => {
|
||||
const input = '{"id":{{request_id}}}';
|
||||
const expected = '{\n "id": {{request_id}}\n}';
|
||||
expect(prettifyJSON(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should handle JSON with a Bruno variable inside a string value', () => {
|
||||
const input = '{"url":"https://example.com/{{path}}"}';
|
||||
const expected = '{\n "url": "https://example.com/{{path}}"\n}';
|
||||
expect(prettifyJSON(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return the original string for invalid JSON', () => {
|
||||
const input = '{"key":"value",';
|
||||
const expected = '{\n "key": "value",';
|
||||
expect(prettifyJSON(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,75 +167,6 @@ describe('Url Utils - parsePathParams', () => {
|
||||
expect(params).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Url Utils - URN parsing', () => {
|
||||
it('should handle basic URN segments correctly', () => {
|
||||
// Test case from issue #5817 - Don't treat URN segments as path parameters
|
||||
const params = parsePathParams('https://example.com/urn:ard:show:3479462da794e97');
|
||||
expect(params).toEqual([]);
|
||||
|
||||
// Test case for path parameter that starts with urn:
|
||||
const params2 = parsePathParams('https://example.com/:urn_type');
|
||||
expect(params2).toEqual([{ name: 'urn_type', value: '' }]);
|
||||
});
|
||||
|
||||
it('should handle URNs with special characters', () => {
|
||||
const params = parsePathParams('https://example.com/urn:isbn:0-330.12345-X');
|
||||
expect(params).toEqual([]);
|
||||
|
||||
// URN with percent-encoded characters
|
||||
const params2 = parsePathParams('https://example.com/urn:uuid:6e8bc430%2D9c3a-11d9-9669-0800200c9a66');
|
||||
expect(params).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle mixed URN and path parameter scenarios', () => {
|
||||
// URN followed by path parameter
|
||||
const params = parsePathParams('https://example.com/urn:nbn:de:bvb/123/:section');
|
||||
expect(params).toEqual([{ name: 'section', value: '' }]);
|
||||
|
||||
// Path parameter followed by URN
|
||||
const params2 = parsePathParams('https://example.com/:type/urn:isbn:123');
|
||||
expect(params2).toEqual([{ name: 'type', value: '' }]);
|
||||
|
||||
// URN-like path parameter (not a real URN)
|
||||
const params3 = parsePathParams('https://example.com/:urn:type');
|
||||
expect(params3).toEqual([{ name: 'urn:type', value: '' }]);
|
||||
});
|
||||
|
||||
it('should handle edge cases with URN-like patterns', () => {
|
||||
// URN with uppercase (should be case-insensitive)
|
||||
const params = parsePathParams('https://example.com/URN:isbn:123');
|
||||
expect(params).toEqual([]);
|
||||
|
||||
// Path that looks like URN but isn't
|
||||
const params2 = parsePathParams('https://example.com/noturn:something:here');
|
||||
expect(params2).toEqual([]);
|
||||
|
||||
// Multiple colons in path parameter
|
||||
const params3 = parsePathParams('https://example.com/:urn:isbn:type');
|
||||
expect(params3).toEqual([{ name: 'urn:isbn:type', value: '' }]);
|
||||
});
|
||||
|
||||
it('should handle URNs in complex URLs', () => {
|
||||
// URN with query parameters
|
||||
const params = parsePathParams('https://example.com/urn:isbn:123?format=:format');
|
||||
expect(params).toEqual([]);
|
||||
|
||||
// Multiple URNs and path parameters
|
||||
const params2 = parsePathParams('https://example.com/:category/urn:isbn:123/:subcategory/urn:issn:456');
|
||||
expect(params2).toEqual([
|
||||
{ name: 'category', value: '' },
|
||||
{ name: 'subcategory', value: '' }
|
||||
]);
|
||||
|
||||
// URN with fragment
|
||||
const params3 = parsePathParams('https://example.com/urn:nbn:de:bvb:123#:section');
|
||||
expect(params3).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Url Utils - OData parameters', () => {
|
||||
it('should handle OData parameters with escaped quotes', () => {
|
||||
const params = parsePathParams('https://example.com/odata/Products(\'ABC\'\'123\')');
|
||||
expect(params).toEqual([]);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import postmanToBruno from '../../../src/postman/postman-to-bruno';
|
||||
import { invalidVariableCharacterRegex } from '../../../src/constants';
|
||||
|
||||
describe('postman-collection', () => {
|
||||
it('should correctly import a valid Postman collection file', async () => {
|
||||
@@ -8,25 +7,6 @@ describe('postman-collection', () => {
|
||||
expect(brunoCollection).toMatchObject(expectedOutput);
|
||||
});
|
||||
|
||||
it('should replace invalid variable characters with underscores', () => {
|
||||
const variables = [
|
||||
{ key: 'validKey', value: 'value1' },
|
||||
{ key: 'invalid key', value: 'value2' },
|
||||
{ key: 'another@invalid#key$', value: 'value3' }
|
||||
];
|
||||
|
||||
const processedVariables = variables.map((v) => ({
|
||||
name: v.key.replace(invalidVariableCharacterRegex, '_'),
|
||||
value: v.value
|
||||
}));
|
||||
|
||||
expect(processedVariables).toEqual([
|
||||
{ name: 'validKey', value: 'value1' },
|
||||
{ name: 'invalid_key', value: 'value2' },
|
||||
{ name: 'another_invalid_key_', value: 'value3' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle falsy values in collection variables', async () => {
|
||||
const collectionWithFalsyVars = {
|
||||
"info": {
|
||||
|
||||
@@ -277,7 +277,6 @@ const fetchGqlSchemaHandler = async (event, endpoint, environment, _request, col
|
||||
const runtimeVars = collection.runtimeVariables;
|
||||
|
||||
// Precedence: runtimeVars > requestVariables > folderVars > envVars > collectionVariables > globalEnvironmentVars
|
||||
const processEnvVars = getProcessEnvVars(collection.uid);
|
||||
const resolvedVars = merge(
|
||||
{},
|
||||
globalEnvironmentVars,
|
||||
@@ -285,14 +284,7 @@ const fetchGqlSchemaHandler = async (event, endpoint, environment, _request, col
|
||||
envVars,
|
||||
folderVars,
|
||||
requestVariables,
|
||||
runtimeVars,
|
||||
{
|
||||
process: {
|
||||
env: {
|
||||
...processEnvVars
|
||||
}
|
||||
}
|
||||
}
|
||||
runtimeVars
|
||||
);
|
||||
|
||||
const collectionRoot = get(collection, 'root', {});
|
||||
@@ -309,6 +301,7 @@ const fetchGqlSchemaHandler = async (event, endpoint, environment, _request, col
|
||||
}
|
||||
|
||||
const collectionPath = collection.pathname;
|
||||
const processEnvVars = getProcessEnvVars(collection.uid);
|
||||
|
||||
const axiosInstance = await configureRequest(
|
||||
collection.uid,
|
||||
|
||||
@@ -63,32 +63,4 @@ describe('prepareGqlIntrospectionRequest', () => {
|
||||
expect(result.headers['Content-Type']).toBe('application/json');
|
||||
});
|
||||
|
||||
it('should handle process.env variables in endpoint URL', () => {
|
||||
const setup = createBasicSetup();
|
||||
setup.endpoint = 'https://{{process.env.API_HOST}}/graphql';
|
||||
const vars = {
|
||||
process: {
|
||||
env: {
|
||||
API_HOST: 'api.example.com'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = prepareGqlIntrospectionRequest(setup.endpoint, vars, setup.request, setup.collectionRoot);
|
||||
|
||||
expect(result.url).toBe('https://api.example.com/graphql');
|
||||
expect(result.method).toBe('POST');
|
||||
});
|
||||
|
||||
it('should handle missing process.env variables gracefully', () => {
|
||||
const setup = createBasicSetup();
|
||||
setup.request.headers = [
|
||||
{ name: 'X-API-Key', value: '{{process.env.MISSING_VAR}}', enabled: true }
|
||||
];
|
||||
|
||||
const result = prepareGqlIntrospectionRequest(setup.endpoint, {}, setup.request, setup.collectionRoot);
|
||||
|
||||
expect(result.headers['X-API-Key']).toBe('{{process.env.MISSING_VAR}}');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
const { cloneDeep } = require('lodash');
|
||||
const xmlFormat = require('xml-formatter');
|
||||
const { interpolate: _interpolate } = require('@usebruno/common');
|
||||
const { sendRequest } = require('@usebruno/requests').scripting;
|
||||
const { jar: createCookieJar } = require('@usebruno/requests').cookies;
|
||||
@@ -75,50 +74,6 @@ class Bru {
|
||||
this.nextRequest = nextRequest;
|
||||
}
|
||||
};
|
||||
|
||||
this.utils = {
|
||||
minifyJson: (json) => {
|
||||
if (json === null || json === undefined) {
|
||||
throw new Error('Failed to minify');
|
||||
}
|
||||
|
||||
if (typeof json === 'object') {
|
||||
try {
|
||||
return JSON.stringify(json);
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to minify: ${err?.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof json === 'string') {
|
||||
const trimmed = json.trim();
|
||||
if (trimmed === '') return trimmed;
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(trimmed));
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to minify: ${err?.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError('minifyJson expects a string or object');
|
||||
},
|
||||
|
||||
minifyXml: (xml) => {
|
||||
if (xml === null || xml === undefined) {
|
||||
throw new Error('Failed to minify');
|
||||
}
|
||||
|
||||
if (typeof xml === 'string') {
|
||||
try {
|
||||
return xmlFormat(xml, { collapseContent: false, indentation: '', lineSeparator: '' });
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to minify: ${err?.message || err}`);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError('minifyXml expects a string');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interpolate = (strOrObj) => {
|
||||
|
||||
@@ -87,10 +87,9 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
||||
vm.setProp(reqObject, 'setHeader', setHeader);
|
||||
setHeader.dispose();
|
||||
|
||||
let getBody = vm.newFunction('getBody', function (options) {
|
||||
return marshallToVm(req.getBody(vm.dump(options)), vm);
|
||||
let getBody = vm.newFunction('getBody', function () {
|
||||
return marshallToVm(req.getBody(), vm);
|
||||
});
|
||||
|
||||
vm.setProp(reqObject, 'getBody', getBody);
|
||||
getBody.dispose();
|
||||
|
||||
|
||||
@@ -92,9 +92,6 @@ snap install bruno
|
||||
# On Linux via Flatpak
|
||||
flatpak install com.usebruno.Bruno
|
||||
|
||||
# On Arch Linux via AUR
|
||||
yay -S bruno
|
||||
|
||||
# On Linux via Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo apt update && sudo apt install gpg curl
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "Global Environment Update",
|
||||
"type": "collection",
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
".git"
|
||||
]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
meta {
|
||||
name: Global Environment Update
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: none
|
||||
}
|
||||
|
||||
script:pre-request {
|
||||
//create a new global env variable.
|
||||
bru.setGlobalEnvVar('newEnv', "newEnvValue");
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
meta {
|
||||
name: Test Request
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{baseUrl}}/users
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
script:pre-request {
|
||||
//update already existing enabled env variable
|
||||
bru.setGlobalEnvVar("existingEnvEnabled", "newExistingEnvEnabledValue");
|
||||
|
||||
//update already existing disabled env variable
|
||||
bru.setGlobalEnvVar("existingEnvDisabled", "newExistingEnvDisabledValue");
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { test, expect } from '../../../playwright';
|
||||
import { closeAllCollections } from '../../utils/page';
|
||||
|
||||
test.describe('Global Environment Variable Update via Script', () => {
|
||||
test.afterEach(async ({ pageWithUserData: page }) => {
|
||||
// cleanup: close all collections
|
||||
await closeAllCollections(page);
|
||||
});
|
||||
|
||||
test('should update global environment values via script and verify the changes', async ({
|
||||
pageWithUserData: page
|
||||
}) => {
|
||||
await test.step('Open the collection from sidebar', async () => {
|
||||
await page.locator('#sidebar-collection-name').filter({ hasText: 'Global Environment Update' }).click();
|
||||
});
|
||||
|
||||
await test.step('Open the test request that has a pre-request script', async () => {
|
||||
await page.locator('.collection-name', { hasText: 'Global Environment Update' }).click();
|
||||
await page.locator('.collection-item-name', { hasText: 'Test Request' }).click();
|
||||
});
|
||||
|
||||
await test.step('Run the request', async () => {
|
||||
await page.getByTestId('send-arrow-icon').click();
|
||||
});
|
||||
|
||||
await test.step('Open the Global Environment Config modal', async () => {
|
||||
await page.getByTestId('environment-selector-trigger').click();
|
||||
await page.getByTestId('env-tab-global').click();
|
||||
await page.getByText('Configure', { exact: true }).click();
|
||||
});
|
||||
|
||||
const globalEnvModal = page.locator('.bruno-modal').filter({ hasText: 'Global Environments' });
|
||||
|
||||
await test.step('Verify that the value of "existingEnvEnabled" is updated by the pre-request script', async () => {
|
||||
const updatedExistingEnvEnabledInputDiv = await globalEnvModal.getByTestId('env-var-value-1');
|
||||
const updatedExistingEnvEnabledValue = await updatedExistingEnvEnabledInputDiv.locator('.CodeMirror-line').textContent();
|
||||
await expect(updatedExistingEnvEnabledValue).toContain('newExistingEnvEnabledValue');
|
||||
});
|
||||
|
||||
await test.step('Verify that the value of "existingEnvDisabled" is updated by the pre-request script', async () => {
|
||||
const updatedExistingEnvDisabledInputDiv = await globalEnvModal.getByTestId('env-var-value-2');
|
||||
const updatedExistingEnvDisabledValue = await updatedExistingEnvDisabledInputDiv.locator('.CodeMirror-line').textContent();
|
||||
await expect(updatedExistingEnvDisabledValue).toContain('newExistingEnvDisabledValue');
|
||||
});
|
||||
|
||||
await test.step('Verify that a new env variable "newEnv" is added by the pre-request script to the global environment', async () => {
|
||||
const newEnvInputDiv = await globalEnvModal.getByTestId('env-var-value-3');
|
||||
const newEnvValue = await newEnvInputDiv.locator('.CodeMirror-line').textContent();
|
||||
await expect(newEnvValue).toContain('newEnvValue');
|
||||
});
|
||||
|
||||
await test.step('Verify that the value of "baseUrl" is unchanged.', async () => {
|
||||
const currentBaseUrlInputDiv = await globalEnvModal.getByTestId('env-var-value-0');
|
||||
const currentBaseUrlValue = await currentBaseUrlInputDiv.locator('.CodeMirror-line').textContent();
|
||||
await expect(currentBaseUrlValue).toContain('https://echo.usebruno.com');
|
||||
});
|
||||
|
||||
await test.step('Close the global environment config modal.', async () => {
|
||||
await page.getByTestId('modal-close-button').click();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"collections": [
|
||||
{
|
||||
"path": "{{projectRoot}}/tests/environments/update-global-environment-via-script/fixtures/collection",
|
||||
"securityConfig": {
|
||||
"jsSandboxMode": "safe"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
"uid": "RrPsTcwRnHMv3yljQO3ex",
|
||||
"name": "global",
|
||||
"variables": [
|
||||
{
|
||||
"uid": "VXKOZdkYw0DyI4mlhn6Wr",
|
||||
"name": "baseUrl",
|
||||
"value": "https://echo.usebruno.com",
|
||||
"type": "text",
|
||||
"secret": false,
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"uid": "NTwrSscXsaeh4uee6ocJN",
|
||||
"name": "existingEnvEnabled",
|
||||
"value": "existingEnvEnabledValue",
|
||||
"type": "text",
|
||||
"secret": false,
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"uid": "PCsUccFm4pktVowXEKRvw",
|
||||
"name": "existingEnvDisabled",
|
||||
"value": "existingEnvDisabledValue",
|
||||
"type": "text",
|
||||
"secret": false,
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"activeGlobalEnvironmentUid": "RrPsTcwRnHMv3yljQO3ex"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": [
|
||||
"{{projectRoot}}/tests/environments/update-global-environment-via-script/fixtures/collection"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user