{ 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('&lt;', 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