mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
refactor: remove HTML validation functions and simplify HtmlPreview component logic (#6730)
* refactor: remove HTML validation functions and simplify HtmlPreview component logic * chore: fix playwright - removed body value check since response is rendered in webview --------- Co-authored-by: Bijin A B <bijin@usebruno.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import { isValidHtml } from 'utils/common/index';
|
||||
import { escapeHtml, isValidHtmlSnippet } from 'utils/response/index';
|
||||
import { escapeHtml } from 'utils/response/index';
|
||||
|
||||
const HtmlPreview = React.memo(({ data, baseUrl }) => {
|
||||
const webviewContainerRef = useRef(null);
|
||||
@@ -31,7 +30,7 @@ const HtmlPreview = React.memo(({ data, baseUrl }) => {
|
||||
return () => mutationObserver.disconnect();
|
||||
}, []);
|
||||
|
||||
if (isValidHtml(data) || isValidHtmlSnippet(data)) {
|
||||
if (typeof data === 'string') {
|
||||
const htmlContent = data.includes('<head>')
|
||||
? data.replace('<head>', `<head><base href="${escapeHtml(baseUrl)}">`)
|
||||
: `<head><base href="${escapeHtml(baseUrl)}"></head>${data}`;
|
||||
@@ -60,8 +59,6 @@ const HtmlPreview = React.memo(({ data, baseUrl }) => {
|
||||
displayContent = String(data);
|
||||
} else if (typeof data === 'object') {
|
||||
displayContent = JSON.stringify(data, null);
|
||||
} else if (typeof data === 'string') {
|
||||
displayContent = data;
|
||||
} else {
|
||||
displayContent = String(data);
|
||||
}
|
||||
|
||||
@@ -506,12 +506,6 @@ export function prettifyJavaScriptString(jsString) {
|
||||
}
|
||||
};
|
||||
|
||||
// Check if string contains valid HTML structure
|
||||
export const isValidHtml = (str) => {
|
||||
if (typeof str !== 'string' || !str.trim()) return false;
|
||||
return /<\s*html[\s>]/i.test(str);
|
||||
};
|
||||
|
||||
export function formatHexView(buffer) {
|
||||
const width = 16;
|
||||
let output = '';
|
||||
|
||||
@@ -92,84 +92,6 @@ const isLikelyText = (buffer) => {
|
||||
return (textChars / sampleSize) > 0.85;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to detect if snippet is valid HTML
|
||||
*/
|
||||
export const isValidHtmlSnippet = (snippet) => {
|
||||
if (!snippet || typeof snippet !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const trimmed = snippet.trim();
|
||||
|
||||
// Check for XML declaration
|
||||
if (trimmed.startsWith('<?xml')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for XML namespaces
|
||||
if (/xmlns(:\w+)?=/.test(trimmed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract all tag names from the snippet
|
||||
const tagMatches = trimmed.matchAll(/<\s*\/?([a-zA-Z][a-zA-Z0-9]*)/g);
|
||||
const tags = [...tagMatches].map((match) => match[1].toLowerCase());
|
||||
|
||||
if (tags.length === 0) {
|
||||
return false; // No tags found
|
||||
}
|
||||
|
||||
// Define recognized HTML tags
|
||||
const validHtmlTags = new Set([
|
||||
'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio',
|
||||
'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button',
|
||||
'canvas', 'caption', 'cite', 'code', 'col', 'colgroup',
|
||||
'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt',
|
||||
'em', 'embed',
|
||||
'fieldset', 'figcaption', 'figure', 'footer', 'form',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html',
|
||||
'i', 'iframe', 'img', 'input', 'ins',
|
||||
'kbd',
|
||||
'label', 'legend', 'li', 'link',
|
||||
'main', 'map', 'mark', 'meta', 'meter',
|
||||
'nav', 'noscript',
|
||||
'object', 'ol', 'optgroup', 'option', 'output',
|
||||
'p', 'param', 'picture', 'pre', 'progress',
|
||||
'q',
|
||||
'rp', 'rt', 'ruby',
|
||||
's', 'samp', 'script', 'section', 'select', 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'svg',
|
||||
'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track',
|
||||
'u', 'ul',
|
||||
'var', 'video',
|
||||
'wbr'
|
||||
]);
|
||||
|
||||
// Check if all tags are valid HTML tags
|
||||
const allTagsValid = tags.every((tag) => validHtmlTags.has(tag));
|
||||
|
||||
if (!allTagsValid) {
|
||||
return false; // Contains non-HTML tags
|
||||
}
|
||||
|
||||
try {
|
||||
// Parse with DOMParser
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(trimmed, 'text/html');
|
||||
|
||||
// Check for parsing errors
|
||||
const parseError = doc.querySelector('parsererror');
|
||||
if (parseError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// HTML parser is lenient; if we reach here with valid tags, consider it valid
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode only the first N bytes from a Base64 string
|
||||
* Returns an empty buffer for invalid/missing input
|
||||
|
||||
@@ -79,9 +79,6 @@ test.describe('Assertions - BRU Collection', () => {
|
||||
|
||||
// Verify response status
|
||||
await expect(locators.response.statusCode()).toContainText('200');
|
||||
|
||||
// Verify response body contains "pong"
|
||||
await expect(locators.response.body()).toContainText('pong', { timeout: 5000 });
|
||||
});
|
||||
|
||||
await test.step('Delete assertion and save', async () => {
|
||||
|
||||
Reference in New Issue
Block a user