mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-16 04:11:29 +00:00
fix: var into tooltip for faker vars (#6312)
* fix: var into tooltip for faker vars * fix: oauth variable * rm: test * rm: comment
This commit is contained in:
@@ -294,6 +294,11 @@ const GlobalStyle = createGlobalStyle`
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
color: ${(props) => props.theme.codemirror.variable.info.color};
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Scope Badge */
|
||||
@@ -305,6 +310,7 @@ const GlobalStyle = createGlobalStyle`
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: #D97706;
|
||||
letter-spacing: 0.03125rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Value Container */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file at https://github.com/graphql/codemirror-graphql/tree/v0.8.3
|
||||
*/
|
||||
|
||||
import { interpolate } from '@usebruno/common';
|
||||
import { interpolate, mockDataFunctions } from '@usebruno/common';
|
||||
import { getVariableScope, isVariableSecret, getAllVariables } from 'utils/collections';
|
||||
import { updateVariableInScope } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import store from 'providers/ReduxStore';
|
||||
@@ -73,6 +73,8 @@ const getScopeLabel = (scopeType) => {
|
||||
'request': 'Request',
|
||||
'runtime': 'Runtime',
|
||||
'process.env': 'Process Env',
|
||||
'dynamic': 'Dynamic',
|
||||
'oauth2': 'OAuth2',
|
||||
'undefined': 'Undefined'
|
||||
};
|
||||
return labels[scopeType] || scopeType;
|
||||
@@ -178,9 +180,28 @@ export const renderVarInfo = (token, options) => {
|
||||
const collection = options.collection;
|
||||
const item = options.item;
|
||||
|
||||
// Check if this is a process.env variable (starts with "process.env.")
|
||||
// Check if this is a dynamic/faker variable (starts with "$")
|
||||
let scopeInfo;
|
||||
if (variableName.startsWith('process.env.')) {
|
||||
if (variableName.startsWith('$oauth2.')) {
|
||||
// OAuth2 token variable - look up in variables object
|
||||
const oauth2Value = get(options.variables, variableName);
|
||||
scopeInfo = {
|
||||
type: 'oauth2',
|
||||
value: oauth2Value !== undefined ? oauth2Value : '',
|
||||
data: null,
|
||||
isValidOAuth2Variable: oauth2Value !== undefined
|
||||
};
|
||||
} else if (variableName.startsWith('$')) {
|
||||
const fakerKeyword = variableName.substring(1); // Remove the $ prefix
|
||||
const fakerFunction = mockDataFunctions[fakerKeyword];
|
||||
scopeInfo = {
|
||||
type: 'dynamic',
|
||||
value: '',
|
||||
data: null,
|
||||
isValidFakerVariable: !!fakerFunction
|
||||
};
|
||||
} else if (variableName.startsWith('process.env.')) {
|
||||
// Check if this is a process.env variable (starts with "process.env.")
|
||||
scopeInfo = {
|
||||
type: 'process.env',
|
||||
value: variableValue || '',
|
||||
@@ -229,8 +250,8 @@ export const renderVarInfo = (token, options) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if variable is read-only (process.env, runtime, and undefined variables cannot be edited)
|
||||
const isReadOnly = scopeInfo.type === 'process.env' || scopeInfo.type === 'runtime' || scopeInfo.type === 'undefined';
|
||||
// Check if variable is read-only (process.env, runtime, dynamic/faker, oauth2, and undefined variables cannot be edited)
|
||||
const isReadOnly = scopeInfo.type === 'process.env' || scopeInfo.type === 'runtime' || scopeInfo.type === 'dynamic' || scopeInfo.type === 'oauth2' || scopeInfo.type === 'undefined';
|
||||
|
||||
// Get raw value from scope
|
||||
const rawValue = scopeInfo.value || '';
|
||||
@@ -265,8 +286,8 @@ export const renderVarInfo = (token, options) => {
|
||||
header.appendChild(scopeBadge);
|
||||
into.appendChild(header);
|
||||
|
||||
// Check if variable name is valid (only for non-process.env variables)
|
||||
const isValidVariableName = scopeInfo.type === 'process.env' || variableNameRegex.test(variableName);
|
||||
// Check if variable name is valid
|
||||
const isValidVariableName = scopeInfo.type === 'process.env' || scopeInfo.type === 'dynamic' || scopeInfo.type === 'oauth2' || variableNameRegex.test(variableName);
|
||||
|
||||
// Show warning if variable name is invalid
|
||||
if (!isValidVariableName) {
|
||||
@@ -279,6 +300,33 @@ export const renderVarInfo = (token, options) => {
|
||||
return into;
|
||||
}
|
||||
|
||||
// Show warning for invalid faker variable (starts with $ but not a valid faker function)
|
||||
if (scopeInfo.type === 'dynamic' && !scopeInfo.isValidFakerVariable) {
|
||||
const warningNote = document.createElement('div');
|
||||
warningNote.className = 'var-warning-note';
|
||||
warningNote.textContent = `Unknown dynamic variable "${variableName}". Check the variable name.`;
|
||||
into.appendChild(warningNote);
|
||||
return into;
|
||||
}
|
||||
|
||||
// For valid dynamic variables, just show the read-only note (no value display since it's generated at runtime)
|
||||
if (scopeInfo.type === 'dynamic' && scopeInfo.isValidFakerVariable) {
|
||||
const readOnlyNote = document.createElement('div');
|
||||
readOnlyNote.className = 'var-readonly-note';
|
||||
readOnlyNote.textContent = 'Generates random value on each request';
|
||||
into.appendChild(readOnlyNote);
|
||||
return into;
|
||||
}
|
||||
|
||||
// Show warning for invalid OAuth2 variable (token not found)
|
||||
if (scopeInfo.type === 'oauth2' && !scopeInfo.isValidOAuth2Variable) {
|
||||
const warningNote = document.createElement('div');
|
||||
warningNote.className = 'var-warning-note';
|
||||
warningNote.textContent = `OAuth2 token not found. Make sure you have fetched the token with the correct Token ID.`;
|
||||
into.appendChild(warningNote);
|
||||
return into;
|
||||
}
|
||||
|
||||
// Value container with icons
|
||||
const valueContainer = document.createElement('div');
|
||||
valueContainer.className = 'var-value-container';
|
||||
@@ -531,6 +579,11 @@ export const renderVarInfo = (token, options) => {
|
||||
readOnlyNote.className = 'var-readonly-note';
|
||||
readOnlyNote.textContent = 'Set by scripts (read-only)';
|
||||
into.appendChild(readOnlyNote);
|
||||
} else if (scopeInfo.type === 'oauth2') {
|
||||
const readOnlyNote = document.createElement('div');
|
||||
readOnlyNote.className = 'var-readonly-note';
|
||||
readOnlyNote.textContent = 'read-only';
|
||||
into.appendChild(readOnlyNote);
|
||||
} else if (scopeInfo.type === 'undefined') {
|
||||
const readOnlyNote = document.createElement('div');
|
||||
readOnlyNote.className = 'var-readonly-note';
|
||||
|
||||
@@ -3,7 +3,13 @@ import { COPY_SUCCESS_TIMEOUT, extractVariableInfo, renderVarInfo } from './brun
|
||||
|
||||
// Mock the dependencies
|
||||
jest.mock('@usebruno/common', () => ({
|
||||
interpolate: jest.fn()
|
||||
interpolate: jest.fn(),
|
||||
mockDataFunctions: {
|
||||
randomFirstName: jest.fn(() => 'John'),
|
||||
randomLastName: jest.fn(() => 'Doe'),
|
||||
randomEmail: jest.fn(() => 'john.doe@example.com'),
|
||||
randomUUID: jest.fn(() => '123e4567-e89b-12d3-a456-426614174000')
|
||||
}
|
||||
}));
|
||||
|
||||
jest.mock('providers/ReduxStore', () => ({
|
||||
@@ -424,4 +430,93 @@ describe('renderVarInfo', () => {
|
||||
expect(console.error).toHaveBeenCalledWith('Failed to copy to clipboard:', 'Clipboard error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic/faker variable rendering', () => {
|
||||
function setupDynamicRender(variableName, variables = {}) {
|
||||
const result = renderVarInfo({ string: `{{${variableName}}}` }, { variables, collection: null, item: null });
|
||||
if (!result) return { result: null, containerDiv: null };
|
||||
|
||||
const containerDiv = result;
|
||||
const header = containerDiv.querySelector('.var-info-header');
|
||||
const scopeBadge = containerDiv.querySelector('.var-scope-badge');
|
||||
const readOnlyNote = containerDiv.querySelector('.var-readonly-note');
|
||||
const warningNote = containerDiv.querySelector('.var-warning-note');
|
||||
const valueContainer = containerDiv.querySelector('.var-value-container');
|
||||
|
||||
return { result, containerDiv, header, scopeBadge, readOnlyNote, warningNote, valueContainer };
|
||||
}
|
||||
|
||||
it('should show read-only note for dynamic variables', () => {
|
||||
const { readOnlyNote } = setupDynamicRender('$randomFirstName');
|
||||
|
||||
expect(readOnlyNote).not.toBeNull();
|
||||
expect(readOnlyNote.textContent).toBe('Generates random value on each request');
|
||||
});
|
||||
|
||||
it('should not show value container for valid dynamic variables', () => {
|
||||
const { valueContainer } = setupDynamicRender('$randomFirstName');
|
||||
|
||||
// Value is generated at runtime, so no value display
|
||||
expect(valueContainer).toBeNull();
|
||||
});
|
||||
|
||||
it('should show warning for unknown dynamic variable', () => {
|
||||
const { warningNote, scopeBadge } = setupDynamicRender('$unknownFaker');
|
||||
|
||||
expect(scopeBadge.textContent).toBe('Dynamic');
|
||||
expect(warningNote).not.toBeNull();
|
||||
expect(warningNote.textContent).toContain('Unknown dynamic variable');
|
||||
});
|
||||
});
|
||||
|
||||
describe('OAuth2 variable rendering', () => {
|
||||
function setupOAuth2Render(variableName, variables = {}) {
|
||||
const result = renderVarInfo({ string: `{{${variableName}}}` }, { variables, collection: null, item: null });
|
||||
if (!result) return { result: null, containerDiv: null };
|
||||
|
||||
const containerDiv = result;
|
||||
const header = containerDiv.querySelector('.var-info-header');
|
||||
const scopeBadge = containerDiv.querySelector('.var-scope-badge');
|
||||
const readOnlyNote = containerDiv.querySelector('.var-readonly-note');
|
||||
const warningNote = containerDiv.querySelector('.var-warning-note');
|
||||
const valueContainer = containerDiv.querySelector('.var-value-container');
|
||||
const valueDisplay = containerDiv.querySelector('.var-value-display');
|
||||
|
||||
return { result, containerDiv, header, scopeBadge, readOnlyNote, warningNote, valueContainer, valueDisplay };
|
||||
}
|
||||
|
||||
it('should show OAuth2 scope badge for $oauth2 variables', () => {
|
||||
const { scopeBadge } = setupOAuth2Render('$oauth2.credentials.access_token', {
|
||||
'$oauth2.credentials.access_token': 'test-token-123'
|
||||
});
|
||||
|
||||
expect(scopeBadge.textContent).toBe('OAuth2');
|
||||
});
|
||||
|
||||
it('should show read-only note for valid OAuth2 variables', () => {
|
||||
const { readOnlyNote } = setupOAuth2Render('$oauth2.credentials.access_token', {
|
||||
'$oauth2.credentials.access_token': 'test-token-123'
|
||||
});
|
||||
|
||||
expect(readOnlyNote).not.toBeNull();
|
||||
expect(readOnlyNote.textContent).toBe('read-only');
|
||||
});
|
||||
|
||||
it('should display the token value for valid OAuth2 variables', () => {
|
||||
const { valueDisplay } = setupOAuth2Render('$oauth2.credentials.access_token', {
|
||||
'$oauth2.credentials.access_token': 'test-token-123'
|
||||
});
|
||||
|
||||
expect(valueDisplay).not.toBeNull();
|
||||
expect(valueDisplay.textContent).toBe('test-token-123');
|
||||
});
|
||||
|
||||
it('should show warning for OAuth2 variable when token is not found', () => {
|
||||
const { warningNote, scopeBadge } = setupOAuth2Render('$oauth2.credentials.access_token', {});
|
||||
|
||||
expect(scopeBadge.textContent).toBe('OAuth2');
|
||||
expect(warningNote).not.toBeNull();
|
||||
expect(warningNote.textContent).toContain('OAuth2 token not found');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user