123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- {
- This file is part of the Free Component Library (FCL)
- Copyright (c) 2025 by Michael Van Canneyt
- Javascript highlighter 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.javascript;
- interface
- {$mode objfpc}{$H+}
- uses
- Classes, SysUtils, fpcunit, testregistry,
- syntax.highlighter, syntax.javascript;
- type
- TTestJavaScriptHighlighter = class(TTestCase)
- protected
- procedure SetUp; override;
- procedure TearDown; override;
- private
- function DoJavaScriptHighlighting(const source: string): TSyntaxTokenArray;
- published
- procedure TestJavaScriptKeywords;
- procedure TestJavaScriptStrings;
- procedure TestJavaScriptNumbers;
- procedure TestJavaScriptComments;
- procedure TestJavaScriptOperators;
- procedure TestJavaScriptSymbols;
- procedure TestJavaScriptIdentifiers;
- procedure TestJavaScriptRegexLiterals;
- procedure TestJavaScriptTemplateLiterals;
- procedure TestJavaScriptNumericFormats;
- procedure TestComplexJavaScriptFunction;
- procedure TestJavaScriptContextSensitive;
- end;
- implementation
- procedure TTestJavaScriptHighlighter.SetUp;
- begin
- end;
- procedure TTestJavaScriptHighlighter.TearDown;
- begin
- // Nothing to do
- end;
- function TTestJavaScriptHighlighter.DoJavaScriptHighlighting(const source: string): TSyntaxTokenArray;
- var
- highlighter: TJavaScriptSyntaxHighlighter;
- begin
- highlighter := TJavaScriptSyntaxHighlighter.Create;
- try
- Result := highlighter.Execute(source);
- finally
- highlighter.Free;
- end;
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptKeywords;
- const
- Keywords: array[0..19] of string = (
- 'var', 'let', 'const', 'function', 'if', 'else', 'for', 'while', 'do', 'switch',
- 'case', 'default', 'break', 'continue', 'return', 'try', 'catch', 'finally', 'throw', 'new'
- );
- var
- tokens: TSyntaxTokenArray;
- i: Integer;
- begin
- for i := 0 to High(Keywords) do
- begin
- tokens := DoJavaScriptHighlighting(Keywords[i]);
- AssertEquals('Should have 1 token for ' + Keywords[i], 1, Length(tokens));
- AssertEquals('Token should be ' + Keywords[i], Keywords[i], tokens[0].Text);
- AssertEquals(Keywords[i] + ' should be keyword', Ord(shKeyword), Ord(tokens[0].Kind));
- end;
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptStrings;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test single-quoted string
- tokens := DoJavaScriptHighlighting('''hello world''');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be single-quoted string', '''hello world''', tokens[0].Text);
- AssertEquals('Token should be string', Ord(shStrings), Ord(tokens[0].Kind));
- // Test double-quoted string
- tokens := DoJavaScriptHighlighting('"hello world"');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be double-quoted string', '"hello world"', tokens[0].Text);
- AssertEquals('Token should be string', Ord(shStrings), Ord(tokens[0].Kind));
- // Test string with escapes
- tokens := DoJavaScriptHighlighting('"hello\nworld"');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be escaped string', '"hello\nworld"', tokens[0].Text);
- AssertEquals('Token should be string', Ord(shStrings), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptNumbers;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test integer
- tokens := DoJavaScriptHighlighting('123');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be integer', '123', tokens[0].Text);
- AssertEquals('Token should be number', Ord(shNumbers), Ord(tokens[0].Kind));
- // Test decimal
- tokens := DoJavaScriptHighlighting('123.45');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be decimal', '123.45', tokens[0].Text);
- AssertEquals('Token should be number', Ord(shNumbers), Ord(tokens[0].Kind));
- // Test hex number
- tokens := DoJavaScriptHighlighting('0xFF');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be hex', '0xFF', tokens[0].Text);
- AssertEquals('Token should be number', Ord(shNumbers), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptComments;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test single-line comment
- tokens := DoJavaScriptHighlighting('// This is a comment');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be single-line comment', '// This is a comment', tokens[0].Text);
- AssertEquals('Token should be comment', Ord(shComment), Ord(tokens[0].Kind));
- // Test multi-line comment
- tokens := DoJavaScriptHighlighting('/* This is a comment */');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be multi-line comment', '/* This is a comment */', tokens[0].Text);
- AssertEquals('Token should be comment', Ord(shComment), Ord(tokens[0].Kind));
- // Test multi-line comment with newlines
- tokens := DoJavaScriptHighlighting('/* Line 1' + #10 + 'Line 2 */');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be multi-line comment with newlines', '/* Line 1' + #10 + 'Line 2 */', tokens[0].Text);
- AssertEquals('Token should be comment', Ord(shComment), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptOperators;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test strict equality
- tokens := DoJavaScriptHighlighting('===');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be strict equality', '===', tokens[0].Text);
- AssertEquals('Token should be operator', Ord(shOperator), Ord(tokens[0].Kind));
- // Test inequality
- tokens := DoJavaScriptHighlighting('!==');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be strict inequality', '!==', tokens[0].Text);
- AssertEquals('Token should be operator', Ord(shOperator), Ord(tokens[0].Kind));
- // Test arrow function
- tokens := DoJavaScriptHighlighting('=>');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be arrow operator', '=>', tokens[0].Text);
- AssertEquals('Token should be operator', Ord(shOperator), Ord(tokens[0].Kind));
- // Test logical AND
- tokens := DoJavaScriptHighlighting('&&');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be logical AND', '&&', tokens[0].Text);
- AssertEquals('Token should be operator', Ord(shOperator), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptSymbols;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test parentheses
- tokens := DoJavaScriptHighlighting('(');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be opening paren', '(', tokens[0].Text);
- AssertEquals('Token should be symbol', Ord(shSymbol), Ord(tokens[0].Kind));
- // Test braces
- tokens := DoJavaScriptHighlighting('{');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be opening brace', '{', tokens[0].Text);
- AssertEquals('Token should be symbol', Ord(shSymbol), Ord(tokens[0].Kind));
- // Test semicolon
- tokens := DoJavaScriptHighlighting(';');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be semicolon', ';', tokens[0].Text);
- AssertEquals('Token should be symbol', Ord(shSymbol), Ord(tokens[0].Kind));
- // Test comma
- tokens := DoJavaScriptHighlighting(',');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be comma', ',', tokens[0].Text);
- AssertEquals('Token should be symbol', Ord(shSymbol), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptIdentifiers;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test simple identifier
- tokens := DoJavaScriptHighlighting('myVariable');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be identifier', 'myVariable', tokens[0].Text);
- AssertEquals('Token should be default', Ord(shDefault), Ord(tokens[0].Kind));
- // Test identifier with underscore
- tokens := DoJavaScriptHighlighting('_private');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be underscore identifier', '_private', tokens[0].Text);
- AssertEquals('Token should be default', Ord(shDefault), Ord(tokens[0].Kind));
- // Test identifier with dollar sign
- tokens := DoJavaScriptHighlighting('$element');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be dollar identifier', '$element', tokens[0].Text);
- AssertEquals('Token should be default', Ord(shDefault), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptRegexLiterals;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test simple regex
- tokens := DoJavaScriptHighlighting('/[a-z]+/gi');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be regex', '/[a-z]+/gi', tokens[0].Text);
- AssertEquals('Token should be regex type', Ord(shRegex), Ord(tokens[0].Kind));
- // Test regex with escape sequences
- tokens := DoJavaScriptHighlighting('/\\d+\\.\\d*/');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be escaped regex', '/\\d+\\.\\d*/', tokens[0].Text);
- AssertEquals('Token should be regex type', Ord(shRegex), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptTemplateLiterals;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test simple template literal
- tokens := DoJavaScriptHighlighting('`hello world`');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be template literal', '`hello world`', tokens[0].Text);
- AssertEquals('Token should be raw string type', Ord(shRawString), Ord(tokens[0].Kind));
- // Test template literal with interpolation
- tokens := DoJavaScriptHighlighting('`hello ${name}`');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be template with interpolation', '`hello ${name}`', tokens[0].Text);
- AssertEquals('Token should be raw string type', Ord(shRawString), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptNumericFormats;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test scientific notation
- tokens := DoJavaScriptHighlighting('1.23e-4');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be scientific notation', '1.23e-4', tokens[0].Text);
- AssertEquals('Token should be number', Ord(shNumbers), Ord(tokens[0].Kind));
- // Test binary number
- tokens := DoJavaScriptHighlighting('0b1010');
- AssertEquals('Should have 1 token', 1, Length(tokens));
- AssertEquals('Token should be binary number', '0b1010', tokens[0].Text);
- AssertEquals('Token should be number', Ord(shNumbers), Ord(tokens[0].Kind));
- // Test octal number
- tokens := DoJavaScriptHighlighting('0o755');
- AssertTrue('Should have at least 1 token', Length(tokens) >= 1);
- AssertEquals('First token should be number type', Ord(shNumbers), Ord(tokens[0].Kind));
- end;
- procedure TTestJavaScriptHighlighter.TestComplexJavaScriptFunction;
- var
- tokens: TSyntaxTokenArray;
- jsCode: string;
- i: Integer;
- hasKeywords, hasStrings, hasSymbols, hasIdentifiers: Boolean;
- begin
- jsCode := 'function greet(name) { return `Hello ${name}!`; }';
- tokens := DoJavaScriptHighlighting(jsCode);
- AssertTrue('Should have multiple tokens', Length(tokens) > 5);
- // Check that we have different token types
- hasKeywords := False;
- hasStrings := False;
- hasSymbols := False;
- hasIdentifiers := False;
- for i := 0 to High(tokens) do
- case tokens[i].Kind of
- shKeyword: hasKeywords := True;
- shRawString: hasStrings := True;
- shSymbol: hasSymbols := True;
- shDefault: hasIdentifiers := True;
- end;
- AssertTrue('Should contain keyword tokens', hasKeywords);
- AssertTrue('Should contain string tokens', hasStrings);
- AssertTrue('Should contain symbol tokens', hasSymbols);
- AssertTrue('Should contain identifier tokens', hasIdentifiers);
- // First token should be 'function' keyword
- AssertEquals('First token should be function', 'function', tokens[0].Text);
- AssertEquals('First token should be keyword', Ord(shKeyword), Ord(tokens[0].Kind));
- // Should contain template literal
- for i := 0 to High(tokens) do
- if tokens[i].Kind = shRawString then
- begin
- AssertEquals('Should have template literal', '`Hello ${name}!`', tokens[i].Text);
- Break;
- end;
- end;
- procedure TTestJavaScriptHighlighter.TestJavaScriptContextSensitive;
- var
- tokens: TSyntaxTokenArray;
- begin
- // Test that context-sensitive features work at least partially
- // Note: Full context sensitivity for regex vs division is complex
- // and may not be fully implemented in all cases
- // Test assignment context - this should work well
- tokens := DoJavaScriptHighlighting('var x = 42;');
- AssertTrue('Should have multiple tokens', Length(tokens) >= 5);
- // Should have var keyword
- AssertEquals('First token should be var', 'var', tokens[0].Text);
- AssertEquals('First token should be keyword', Ord(shKeyword), Ord(tokens[0].Kind));
- // Should have identifier
- AssertEquals('Second token should be space', ' ', tokens[1].Text);
- AssertEquals('Third token should be identifier', 'x', tokens[2].Text);
- AssertEquals('Third token should be default', Ord(shDefault), Ord(tokens[2].Kind));
- // Should have assignment and number
- AssertEquals('Fourth token should be space', ' ', tokens[3].Text);
- AssertEquals('Fifth token should be assignment', '=', tokens[4].Text);
- AssertEquals('Fifth token should be operator', Ord(shOperator), Ord(tokens[4].Kind));
- end;
- initialization
- RegisterTest(TTestJavaScriptHighlighter);
- end.
|