{
This file is part of the Free Component Library (FCL)
Copyright (c) 2025 by Michael Van Canneyt
HTML renderer unit test
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
unit unittest.htmlrender;
interface
{$mode objfpc}{$H+}
uses
Classes, SysUtils, fpcunit, testregistry,
syntax.highlighter, syntax.javascript, syntax.css, syntax.html,
syntax.htmlrender;
type
TTestHtmlRenderer = class(TTestCase)
protected
procedure SetUp; override;
procedure TearDown; override;
private
renderer: THtmlSyntaxRenderer;
function RenderToString(const tokens: TSyntaxTokenArray): string;
function RenderToStringList(const tokens: TSyntaxTokenArray): TStringList;
published
procedure TestJavaScriptRendering;
procedure TestCssRendering;
procedure TestHtmlRendering;
procedure TestEmbeddedHtmlRendering;
procedure TestStringOutput;
procedure TestSpecialCharacterEscaping;
procedure TestEmptyTokenArray;
procedure TestClassNameGeneration;
procedure TestCategoryMapping;
procedure TestHtmlEscaping;
procedure TestTokensWithoutCategory;
procedure TestComplexJavaScript;
procedure TestNoDefaultSpanOption;
procedure TestMultilineRendering;
procedure TestPreserveLineStructureOption;
procedure TestExtraClassesProperty;
end;
implementation
procedure TTestHtmlRenderer.SetUp;
begin
renderer := THtmlSyntaxRenderer.Create;
end;
procedure TTestHtmlRenderer.TearDown;
begin
renderer.Free;
end;
function TTestHtmlRenderer.RenderToString(const tokens: TSyntaxTokenArray): string;
begin
Result:='';
renderer.RenderTokensToString(tokens, Result);
end;
function TTestHtmlRenderer.RenderToStringList(const tokens: TSyntaxTokenArray): TStringList;
begin
Result := TStringList.Create;
renderer.RenderTokens(tokens, Result);
end;
procedure TTestHtmlRenderer.TestJavaScriptRendering;
var
jsHighlighter: TJavaScriptSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
jsHighlighter := TJavaScriptSyntaxHighlighter.Create;
try
tokens := jsHighlighter.Execute( 'function test() { return "hello"; }');
output := RenderToString(tokens);
// Check for essential elements
AssertTrue('Should contain function keyword', Pos('function', output) > 0);
AssertTrue('Should contain string literal', Pos('"hello"', output) > 0);
AssertTrue('Should contain return keyword', Pos('return', output) > 0);
AssertTrue('Should contain symbols', Pos('', output) > 0);
finally
jsHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestCssRendering;
var
cssHighlighter: TCssSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
cssHighlighter := TCssSyntaxHighlighter.Create;
try
tokens := cssHighlighter.Execute( 'body { color: #FF0000; font-size: 16px; }');
output := RenderToString(tokens);
// Check for essential CSS elements
AssertTrue('Should contain color property', Pos('color', output) > 0);
AssertTrue('Should contain hex color', Pos('#FF0000', output) > 0);
AssertTrue('Should contain pixel value', Pos('16px', output) > 0);
AssertTrue('Should contain CSS symbols', Pos('{', output) > 0);
finally
cssHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestHtmlRendering;
var
htmlHighlighter: THtmlSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
htmlHighlighter := THtmlSyntaxHighlighter.Create;
try
tokens := htmlHighlighter.Execute( '<Hello>
');
output := RenderToString(tokens);
// Check for essential HTML elements
AssertTrue('Should contain div keyword', Pos('div', output) > 0);
AssertTrue('Should contain class attribute', Pos('class', output) > 0);
AssertTrue('Should contain string value', Pos('"container"', output) > 0);
AssertTrue('Should contain HTML symbols', Pos('<', output) > 0);
AssertTrue('Should contain escaped entities', Pos('<', output) > 0);
finally
htmlHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestEmbeddedHtmlRendering;
var
htmlHighlighter: THtmlSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
htmlHighlighter := THtmlSyntaxHighlighter.Create;
try
tokens := htmlHighlighter.Execute( '');
output := RenderToString(tokens);
// Check for embedded CSS
// Writeln('output ',output);
AssertTrue('Should contain CSS color property', Pos('color', output) > 0);
AssertTrue('Should contain CSS category', Pos('embeddedcss', output) > 0);
// Check for embedded JavaScript
AssertTrue('Should contain JS alert function', Pos('alert', output) > 0);
AssertTrue('Should contain JS category', Pos('embeddedjs', output) > 0);
// Check for HTML tags
AssertTrue('Should contain style tag', Pos('style', output) > 0);
AssertTrue('Should contain script tag', Pos('script', output) > 0);
finally
htmlHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestStringOutput;
var
jsHighlighter: TJavaScriptSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
jsHighlighter := TJavaScriptSyntaxHighlighter.Create;
try
tokens := jsHighlighter.Execute( 'var x = 42;');
output := RenderToString(tokens);
// Should be single continuous string without line breaks
AssertTrue('Should not contain line breaks', Pos(#10, output) = 0);
AssertTrue('Should not contain line breaks', Pos(#13, output) = 0);
// Should contain all expected elements in sequence
AssertTrue('Should contain var keyword', Pos('var', output) > 0);
AssertTrue('Should contain number', Pos('42', output) > 0);
AssertTrue('Should contain operator', Pos('=', output) > 0);
finally
jsHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestSpecialCharacterEscaping;
var
jsHighlighter: TJavaScriptSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
jsHighlighter := TJavaScriptSyntaxHighlighter.Create;
try
tokens := jsHighlighter.Execute( 'var html = "\"Hello & Welcome\"
";');
output := RenderToString(tokens);
// Check that special characters are properly escaped
AssertTrue('Should escape < character', Pos('<', output) > 0);
AssertTrue('Should escape > character', Pos('>', output) > 0);
AssertTrue('Should escape & character', Pos('&', output) > 0);
AssertTrue('Should escape " character', Pos('"', output) > 0);
// Should not contain unescaped special characters in content
AssertFalse('Should not contain raw < in content', Pos('>"<', output) > 0);
AssertFalse('Should not contain raw > in content', Pos('>>', output) > 0);
finally
jsHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestEmptyTokenArray;
var
tokens: TSyntaxTokenArray;
output: string;
stringList: TStringList;
begin
tokens:=[];
SetLength(tokens, 0);
// Test string output
output := RenderToString(tokens);
AssertEquals('Empty token array should produce empty string', '', output);
// Test string list output
stringList := RenderToStringList(tokens);
try
AssertEquals('Empty token array should produce empty string list', 0, stringList.Count);
finally
stringList.Free;
end;
end;
procedure TTestHtmlRenderer.TestClassNameGeneration;
var
jsHighlighter: TJavaScriptSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
jsHighlighter := TJavaScriptSyntaxHighlighter.Create;
try
tokens := jsHighlighter.Execute( 'var');
output := RenderToString(tokens);
// Should have both base class and category-specific class
AssertTrue('Should contain base keyword class', Pos('class="keyword keyword-javascript"', output) > 0);
finally
jsHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestCategoryMapping;
var
cssHighlighter: TCssSyntaxHighlighter;
tokens: TSyntaxTokenArray;
output: string;
begin
cssHighlighter := TCssSyntaxHighlighter.Create;
try
tokens := cssHighlighter.Execute( 'color');
output := RenderToString(tokens);
// Should map CSS category correctly
AssertTrue('Should contain CSS category', Pos('keyword-css', output) > 0);
finally
cssHighlighter.Free;
end;
end;
procedure TTestHtmlRenderer.TestHtmlEscaping;
var
tokens: TSyntaxTokenArray;
output: string;
begin
// Create a simple token with special characters
tokens:=[];
SetLength(tokens, 1);
tokens[0] := TSyntaxToken.Create('',shDefault);
output := RenderToString(tokens);
// All HTML special characters should be escaped
AssertTrue('Should escape