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:
Pooja
2025-12-22 13:02:51 +05:30
committed by GitHub
parent 41efa8505b
commit 6f2804ea0f
3 changed files with 162 additions and 8 deletions

View File

@@ -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 */

View File

@@ -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';

View File

@@ -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');
});
});
});