unittest.html.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2025 by Michael Van Canneyt
  4. HTML highlighter unit test
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit unittest.html;
  12. interface
  13. {$mode objfpc}{$H+}
  14. uses
  15. Classes, SysUtils, fpcunit, testregistry,
  16. syntax.highlighter, syntax.html;
  17. type
  18. TTestHtmlHighlighter = class(TTestCase)
  19. protected
  20. procedure SetUp; override;
  21. procedure TearDown; override;
  22. private
  23. function DoHtmlHighlighting(const source: string): TSyntaxTokenArray;
  24. published
  25. procedure TestHtmlBasicTags;
  26. procedure TestHtmlAttributes;
  27. procedure TestHtmlComments;
  28. procedure TestHtmlEntities;
  29. procedure TestHtmlEmbeddedCSS;
  30. procedure TestHtmlEmbeddedJavaScript;
  31. procedure TestHtmlDoctype;
  32. procedure TestHtmlSelfClosingTags;
  33. procedure TestHtmlNestedTags;
  34. procedure TestComplexHtmlDocument;
  35. procedure TestHtmlCDATA;
  36. procedure TestCategorySystem;
  37. end;
  38. implementation
  39. procedure TTestHtmlHighlighter.SetUp;
  40. begin
  41. end;
  42. procedure TTestHtmlHighlighter.TearDown;
  43. begin
  44. // Nothing to do
  45. end;
  46. function TTestHtmlHighlighter.DoHtmlHighlighting(const source: string): TSyntaxTokenArray;
  47. var
  48. highlighter: THtmlSyntaxHighlighter;
  49. begin
  50. highlighter := THtmlSyntaxHighlighter.Create;
  51. try
  52. Result := highlighter.Execute(source);
  53. finally
  54. highlighter.Free;
  55. end;
  56. end;
  57. procedure TTestHtmlHighlighter.TestHtmlBasicTags;
  58. var
  59. tokens: TSyntaxTokenArray;
  60. begin
  61. // Test simple div tag
  62. tokens := DoHtmlHighlighting('<div>');
  63. AssertTrue('Should have at least 3 tokens', Length(tokens) >= 3);
  64. AssertEquals('First token should be opening bracket', '<', tokens[0].Text);
  65. AssertEquals('Opening bracket should be symbol', Ord(shSymbol), Ord(tokens[0].Kind));
  66. AssertEquals('Tag name should be div', 'div', tokens[1].Text);
  67. AssertEquals('Tag name should be keyword', Ord(shKeyword), Ord(tokens[1].Kind));
  68. AssertEquals('Closing bracket should be >', '>', tokens[2].Text);
  69. AssertEquals('Closing bracket should be symbol', Ord(shSymbol), Ord(tokens[2].Kind));
  70. // Test closing tag
  71. tokens := DoHtmlHighlighting('</div>');
  72. AssertTrue('Should have at least 3 tokens', Length(tokens) >= 3);
  73. AssertEquals('First token should be opening bracket with slash', '</', tokens[0].Text);
  74. AssertEquals('Tag name should be div', 'div', tokens[1].Text);
  75. AssertEquals('Closing bracket should be >', '>', tokens[2].Text);
  76. // Test self-closing tag
  77. tokens := DoHtmlHighlighting('<br/>');
  78. AssertTrue('Should have at least 4 tokens', Length(tokens) >= 4);
  79. AssertEquals('First token should be <', '<', tokens[0].Text);
  80. AssertEquals('Tag name should be br', 'br', tokens[1].Text);
  81. AssertEquals('Slash should be symbol', Ord(shSymbol), Ord(tokens[2].Kind));
  82. AssertEquals('Closing bracket should be >', '>', tokens[3].Text);
  83. end;
  84. procedure TTestHtmlHighlighter.TestHtmlAttributes;
  85. var
  86. tokens: TSyntaxTokenArray;
  87. i: Integer;
  88. hasAttribute, hasValue: Boolean;
  89. begin
  90. tokens := DoHtmlHighlighting('<div class="container">');
  91. AssertTrue('Should have multiple tokens', Length(tokens) > 5);
  92. hasAttribute := False;
  93. hasValue := False;
  94. for i := 0 to High(tokens) do
  95. begin
  96. if tokens[i].Text = 'class' then
  97. hasAttribute := True;
  98. if tokens[i].Text = '"container"' then
  99. hasValue := True;
  100. end;
  101. AssertTrue('Should contain class attribute', hasAttribute);
  102. AssertTrue('Should contain attribute value', hasValue);
  103. end;
  104. procedure TTestHtmlHighlighter.TestHtmlComments;
  105. var
  106. tokens: TSyntaxTokenArray;
  107. foundComment: Boolean;
  108. i: Integer;
  109. begin
  110. tokens := DoHtmlHighlighting('<!-- This is a comment -->');
  111. foundComment := False;
  112. for i := 0 to High(tokens) do
  113. if (tokens[i].Kind = shComment) or (tokens[i].Kind = shSymbol) then
  114. foundComment := True;
  115. AssertTrue('Should contain comment tokens', foundComment);
  116. AssertTrue('Should have multiple tokens', Length(tokens) >= 1);
  117. end;
  118. procedure TTestHtmlHighlighter.TestHtmlEntities;
  119. var
  120. tokens: TSyntaxTokenArray;
  121. foundEntity: Boolean;
  122. i: Integer;
  123. begin
  124. tokens := DoHtmlHighlighting('&amp;');
  125. foundEntity := False;
  126. for i := 0 to High(tokens) do
  127. begin
  128. if (tokens[i].Text = '&amp;') and (tokens[i].Kind = shEscape) then
  129. foundEntity := True;
  130. end;
  131. AssertTrue('Should recognize HTML entity', foundEntity);
  132. // Test numeric entity
  133. tokens := DoHtmlHighlighting('&#123;');
  134. foundEntity := False;
  135. for i := 0 to High(tokens) do
  136. begin
  137. if (tokens[i].Text = '&#123;') and (tokens[i].Kind = shEscape) then
  138. foundEntity := True;
  139. end;
  140. AssertTrue('Should recognize numeric HTML entity', foundEntity);
  141. end;
  142. procedure TTestHtmlHighlighter.TestHtmlEmbeddedCSS;
  143. var
  144. tokens: TSyntaxTokenArray;
  145. lToken : TSyntaxToken;
  146. hasStyleTag, hasCSS: Boolean;
  147. i: Integer;
  148. begin
  149. tokens := DoHtmlHighlighting('<style>body { color: red; }</style>');
  150. hasStyleTag := False;
  151. hasCSS := False;
  152. for i := 0 to High(tokens) do
  153. begin
  154. lToken:=tokens[i];
  155. if (lToken.Text = 'style') and (lToken.Kind = shKeyword) then
  156. hasStyleTag := True;
  157. if (lToken.CategoryCount> 0) and (lToken.Text = 'body') then
  158. hasCSS := True;
  159. end;
  160. AssertTrue('Should contain style tag', hasStyleTag);
  161. AssertTrue('Should have multiple tokens', Length(tokens) > 5);
  162. AssertTrue('Should have CSS', hasCSS);
  163. // Note: CSS parsing depends on embedded highlighter
  164. end;
  165. procedure TTestHtmlHighlighter.TestHtmlEmbeddedJavaScript;
  166. var
  167. tokens: TSyntaxTokenArray;
  168. hasScriptTag: Boolean;
  169. i: Integer;
  170. begin
  171. tokens := DoHtmlHighlighting('<script>var x = 5;</script>');
  172. hasScriptTag := False;
  173. for i := 0 to High(tokens) do
  174. if (tokens[i].Text = 'script') and (tokens[i].Kind = shKeyword) then
  175. hasScriptTag := True;
  176. AssertTrue('Should contain script tag', hasScriptTag);
  177. AssertTrue('Should have multiple tokens', Length(tokens) > 5);
  178. // Note: JavaScript parsing depends on embedded highlighter
  179. end;
  180. procedure TTestHtmlHighlighter.TestHtmlDoctype;
  181. var
  182. tokens: TSyntaxTokenArray;
  183. foundDoctype: Boolean;
  184. i: Integer;
  185. begin
  186. tokens := DoHtmlHighlighting('<!DOCTYPE html>');
  187. foundDoctype := False;
  188. for i := 0 to High(tokens) do
  189. if (tokens[i].Kind = shDirective) and (Pos('DOCTYPE', tokens[i].Text) > 0) then
  190. foundDoctype := True;
  191. AssertTrue('Should recognize DOCTYPE as directive', foundDoctype);
  192. end;
  193. procedure TTestHtmlHighlighter.TestHtmlSelfClosingTags;
  194. var
  195. tokens: TSyntaxTokenArray;
  196. hasImg, hasSlash: Boolean;
  197. i: Integer;
  198. begin
  199. tokens := DoHtmlHighlighting('<img src="test.jpg" />');
  200. hasImg := False;
  201. hasSlash := False;
  202. for i := 0 to High(tokens) do
  203. begin
  204. if (tokens[i].Text = 'img') and (tokens[i].Kind = shKeyword) then
  205. hasImg := True;
  206. if (tokens[i].Text = '/') and (tokens[i].Kind = shSymbol) then
  207. hasSlash := True;
  208. end;
  209. AssertTrue('Should contain img tag', hasImg);
  210. AssertTrue('Should contain closing slash', hasSlash);
  211. end;
  212. procedure TTestHtmlHighlighter.TestHtmlNestedTags;
  213. var
  214. tokens: TSyntaxTokenArray;
  215. tagCount: Integer;
  216. i: Integer;
  217. begin
  218. tokens := DoHtmlHighlighting('<div><p>Hello</p></div>');
  219. tagCount := 0;
  220. for i := 0 to High(tokens) do
  221. begin
  222. if tokens[i].Kind = shKeyword then
  223. Inc(tagCount);
  224. end;
  225. AssertTrue('Should contain multiple tags', tagCount >= 4); // div, p, p, div
  226. AssertTrue('Should have many tokens', Length(tokens) > 10);
  227. end;
  228. procedure TTestHtmlHighlighter.TestComplexHtmlDocument;
  229. var
  230. tokens: TSyntaxTokenArray;
  231. document: string;
  232. hasHtml, hasHead, hasBody, hasTitle: Boolean;
  233. i: Integer;
  234. begin
  235. document := '<html><head><title>Test</title></head><body><h1>Hello</h1></body></html>';
  236. tokens := DoHtmlHighlighting(document);
  237. hasHtml := False;
  238. hasHead := False;
  239. hasBody := False;
  240. hasTitle := False;
  241. for i := 0 to High(tokens) do
  242. begin
  243. if (tokens[i].Text = 'html') and (tokens[i].Kind = shKeyword) then
  244. hasHtml := True;
  245. if (tokens[i].Text = 'head') and (tokens[i].Kind = shKeyword) then
  246. hasHead := True;
  247. if (tokens[i].Text = 'body') and (tokens[i].Kind = shKeyword) then
  248. hasBody := True;
  249. if (tokens[i].Text = 'title') and (tokens[i].Kind = shKeyword) then
  250. hasTitle := True;
  251. end;
  252. AssertTrue('Should contain html tag', hasHtml);
  253. AssertTrue('Should contain head tag', hasHead);
  254. AssertTrue('Should contain body tag', hasBody);
  255. AssertTrue('Should contain title tag', hasTitle);
  256. AssertTrue('Should have many tokens for complex document', Length(tokens) > 20);
  257. end;
  258. procedure TTestHtmlHighlighter.TestHtmlCDATA;
  259. var
  260. tokens: TSyntaxTokenArray;
  261. foundCDATA: Boolean;
  262. i: Integer;
  263. begin
  264. tokens := DoHtmlHighlighting('<![CDATA[Some data here]]>');
  265. foundCDATA := False;
  266. for i := 0 to High(tokens) do
  267. if (tokens[i].Kind = shRawString) or
  268. ((tokens[i].Kind = shSymbol) and (tokens[i].Text = '<![CDATA[')) then
  269. foundCDATA := True;
  270. AssertTrue('Should recognize CDATA section', foundCDATA);
  271. end;
  272. procedure TTestHtmlHighlighter.TestCategorySystem;
  273. var
  274. tokens: TSyntaxTokenArray;
  275. htmlCategoryFound, cssCategoryFound, jsCategoryFound: Boolean;
  276. lCat,i: Integer;
  277. begin
  278. // Test basic HTML category
  279. tokens := DoHtmlHighlighting('<div>text</div>');
  280. htmlCategoryFound := False;
  281. for i := 0 to High(tokens) do
  282. if tokens[i].HasCategory(THtmlSyntaxHighlighter.CategoryHTML) then
  283. htmlCategoryFound := True;
  284. AssertTrue('Should have HTML category tokens', htmlCategoryFound);
  285. // Test embedded CSS category
  286. tokens := DoHtmlHighlighting('<style>body { color: red; }</style>');
  287. cssCategoryFound := False;
  288. lCat:=TSyntaxHighLighter.GetRegisteredCategoryID('EmbeddedCSS');
  289. for i := 0 to High(tokens) do
  290. if tokens[i].HasCategory(lCat) then
  291. cssCategoryFound := True;
  292. AssertTrue('Should have category tokens for CSS', cssCategoryFound);
  293. // Test embedded JavaScript category
  294. tokens := DoHtmlHighlighting('<script>var x = 5;</script>');
  295. jsCategoryFound := False;
  296. lCat:=TSyntaxHighlighter.GetRegisteredCategoryID('EmbeddedJS');
  297. for i := 0 to High(tokens) do
  298. if tokens[i].HasCategory(lCat) then
  299. jsCategoryFound := True;
  300. AssertTrue('Should have category tokens for JavaScript', jsCategoryFound);
  301. end;
  302. initialization
  303. RegisterTest(TTestHtmlHighlighter);
  304. end.