-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AG-26623 Improve Extended CSS tokenization
Merge in ADGUARD-FILTERS/tsurlfilter from feature/AG-26623-6 to feature/AG-26623-1 Squashed commit of the following: commit 7d9c9ed Author: scripthunter7 <d.tota@adguard.com> Date: Wed Oct 18 10:13:29 2023 +0200 Fix nits commit 9d5dd3e Merge: c18515d 02fb7f7 Author: scripthunter7 <d.tota@adguard.com> Date: Tue Oct 17 18:02:48 2023 +0200 Merge branch 'feature/AG-26623-1' into feature/AG-26623-6 commit c18515d Author: scripthunter7 <d.tota@adguard.com> Date: Tue Oct 17 17:50:29 2023 +0200 More robust Extended CSS tests commit 146b808 Author: scripthunter7 <d.tota@adguard.com> Date: Tue Oct 17 17:49:07 2023 +0200 Improve Extended CSS tokenization
- Loading branch information
1 parent
02fb7f7
commit ca47032
Showing
8 changed files
with
424 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 113 additions & 59 deletions
172
packages/css-tokenizer/test/extended-css-tokenizer/contains.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,127 @@ | ||
import type { TokenData } from '../helpers/test-interfaces'; | ||
import { tokenizeExtended } from '../../src/extended-css-tokenizer'; | ||
import { TokenType } from '../../src/common/enums/token-types'; | ||
import { ExtendedCssPseudo } from '../../src/common/enums/extended-css-pseudos'; | ||
import { testTokenization } from '../helpers/test-utils'; | ||
import { createTests, type PseudoValues } from './helpers/test-creator'; | ||
import { generateDelimStream } from './helpers/delim-generator'; | ||
|
||
const PSEUDO_NAMES = [ | ||
ExtendedCssPseudo.Contains, | ||
ExtendedCssPseudo.HasText, | ||
ExtendedCssPseudo.AbpContains, | ||
]; | ||
|
||
const PSEUDO_VALUES = [ | ||
String.raw``, // empty | ||
String.raw` `, // single space | ||
String.raw` `, // multiple spaces | ||
String.raw`a`, // single character | ||
String.raw`a b`, // multiple characters | ||
String.raw`a b`, // multiple characters with multiple spaces | ||
String.raw` a`, // single character with single space | ||
String.raw` a b`, // multiple characters with single space | ||
String.raw` a b`, // multiple characters with multiple spaces | ||
String.raw`a `, // single character with single space | ||
String.raw`a b `, // multiple characters with single space | ||
String.raw`a b `, // multiple characters with multiple spaces | ||
String.raw`(a)`, // single character with balanced parentheses | ||
String.raw`(a b)`, // multiple characters with balanced parentheses | ||
String.raw`a\(b`, // escaped left parenthesis | ||
String.raw`a\)b`, // escaped right parenthesis | ||
String.raw`a\(b\)c`, // escaped parentheses | ||
String.raw`/a/`, // regular expression | ||
String.raw`/a/i`, // regular expression with flags | ||
String.raw`/a\/b/`, // regular expression with escaped forward slash | ||
String.raw`/a(b|c)/`, // regular expression with balanced parentheses | ||
String.raw`/^(a|b){3,}$/`, // regular expression with balanced parentheses and quantifiers | ||
String.raw`/a\(\)/i`, // regular expression with escaped parentheses | ||
String.raw`'`, // orphaned single quote | ||
String.raw`a'`, // orphaned single quote | ||
String.raw`'b`, // orphaned single quote | ||
String.raw`a'b`, // orphaned single quote | ||
String.raw`"`, // orphaned double quote | ||
String.raw`a"`, // orphaned double quote | ||
String.raw`"b`, // orphaned double quote | ||
String.raw`a"b`, // orphaned double quote | ||
String.raw`'a'`, // single quoted string | ||
String.raw`"a"`, // double quoted string | ||
String.raw`a'b"c`, // mixed orphaned quotes | ||
String.raw`a'b"c'd`, // single quoted string with orphaned double quote in the middle | ||
]; | ||
const PSEUDO_VALUES: PseudoValues = { | ||
...generateDelimStream([ | ||
String.raw``, // empty | ||
String.raw` `, // single space | ||
String.raw` `, // multiple spaces | ||
String.raw`a`, // single character | ||
String.raw`ab`, // multiple characters | ||
String.raw`a b`, // multiple characters with single space | ||
String.raw`a b`, // multiple characters with multiple spaces | ||
String.raw` a`, // single character preceded by single space | ||
String.raw` a`, // single character preceded by multiple spaces | ||
String.raw`a `, // single character followed by single space | ||
String.raw`a `, // single character followed by multiple spaces | ||
String.raw` a `, // single character surrounded by single spaces | ||
String.raw` a `, // single character surrounded by multiple spaces | ||
String.raw` a b `, // multiple characters surrounded by single spaces | ||
String.raw` a b `, // multiple characters surrounded by multiple spaces | ||
String.raw`a b c`, // multiple characters with multiple spaces | ||
String.raw`\(`, // escaped left parenthesis | ||
String.raw`\)`, // escaped right parenthesis | ||
String.raw`\(\)`, // escaped parentheses | ||
String.raw`\)\(`, // escaped parentheses (reversed) | ||
String.raw`()`, // balanced parentheses | ||
String.raw`()(())`, // multiple balanced parentheses | ||
String.raw`(a)`, // single character with balanced parentheses | ||
String.raw`(a)(())`, // single character with multiple balanced parentheses | ||
String.raw`(ab)`, // multiple characters with balanced parentheses | ||
String.raw`a(\))(\()b`, // escaped parentheses with balanced parentheses | ||
String.raw`/a/`, // simple regular expression | ||
String.raw`/a/i`, // regular expression with flags | ||
String.raw`/a/ig`, // regular expression with multiple flags | ||
String.raw`/a\/b/`, // regular expression with escaped forward slash | ||
String.raw`/(a|b)/`, // regular expression with balanced parentheses | ||
String.raw`/^(a|b){3,}$/ig`, // regular expression with balanced parentheses and quantifiers and flags | ||
String.raw`/a\(\)/i`, // regular expression with escaped parentheses | ||
]), | ||
|
||
// 1-length string | ||
[String.raw`'a'`]: [ | ||
[TokenType.String, 0, 3], | ||
], | ||
[String.raw`"a"`]: [ | ||
[TokenType.String, 0, 3], | ||
], | ||
|
||
// 2-length string | ||
[String.raw`'ab'`]: [ | ||
[TokenType.String, 0, 4], | ||
], | ||
[String.raw`"ab"`]: [ | ||
[TokenType.String, 0, 4], | ||
], | ||
|
||
// ) in string | ||
[String.raw`'a)'`]: [ | ||
[TokenType.String, 0, 4], | ||
], | ||
[String.raw`"a)"`]: [ | ||
[TokenType.String, 0, 4], | ||
], | ||
|
||
// ( in string | ||
[String.raw`'a('`]: [ | ||
[TokenType.String, 0, 4], | ||
], | ||
[String.raw`"a("`]: [ | ||
[TokenType.String, 0, 4], | ||
], | ||
|
||
// ( and ) in string | ||
[String.raw`'a()b'`]: [ | ||
[TokenType.String, 0, 6], | ||
], | ||
[String.raw`"a()b"`]: [ | ||
[TokenType.String, 0, 6], | ||
], | ||
|
||
// string + something | ||
[String.raw`'a' 12px`]: [ | ||
[TokenType.String, 0, 3], | ||
[TokenType.Whitespace, 3, 4], | ||
[TokenType.Dimension, 4, 8], | ||
], | ||
|
||
// single space + string | ||
[String.raw` 'a'`]: [ | ||
[TokenType.Whitespace, 0, 1], | ||
[TokenType.String, 1, 4], | ||
], | ||
|
||
// multiple spaces + string | ||
[String.raw` 'a'`]: [ | ||
[TokenType.Whitespace, 0, 2], | ||
[TokenType.String, 2, 5], | ||
], | ||
|
||
// string + single space | ||
[String.raw`'a' `]: [ | ||
[TokenType.String, 0, 3], | ||
[TokenType.Whitespace, 3, 4], | ||
], | ||
|
||
const tests = PSEUDO_NAMES.map((name: string) => ( | ||
PSEUDO_VALUES.map((param) => ({ | ||
actual: `:${name}(${param})`, | ||
expected: [ | ||
// :name( | ||
[TokenType.Colon, 0, 1], | ||
[TokenType.Function, 1, name.length + 2], | ||
// parameter splitted into delim tokens | ||
...param.split('').map((_, index) => ( | ||
[TokenType.Delim, name.length + 2 + index, name.length + 3 + index] | ||
)), | ||
// ) | ||
[TokenType.CloseParenthesis, 1 + name.length + param.length + 1, 1 + name.length + param.length + 2], | ||
] as TokenData[], | ||
})) | ||
)).flat(); | ||
// string + multiple spaces | ||
[String.raw`'a' `]: [ | ||
[TokenType.String, 0, 3], | ||
[TokenType.Whitespace, 3, 5], | ||
], | ||
}; | ||
|
||
describe(`Extended CSS's :${PSEUDO_NAMES.join(', :')}`, () => { | ||
test.each(tests)("should tokenize '$actual'", ({ actual, expected }) => { | ||
const tokens: TokenData[] = []; | ||
tokenizeExtended(actual, (...args) => tokens.push(args)); | ||
expect(tokens).toEqual(expected); | ||
}); | ||
test.each( | ||
createTests(PSEUDO_NAMES, PSEUDO_VALUES), | ||
)("should tokenize '$actual' as $as", (testData) => testTokenization(testData, tokenizeExtended)); | ||
}); |
21 changes: 21 additions & 0 deletions
21
packages/css-tokenizer/test/extended-css-tokenizer/helpers/delim-generator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { TokenType } from '../../../src/common/enums/token-types'; | ||
import { type TokenData } from '../../helpers/test-interfaces'; | ||
import { type PseudoValues } from './test-creator'; | ||
|
||
/** | ||
* Helper function to generate token expectations for values that should be tokenized as delim tokens. | ||
* | ||
* @param inputs Inputs to generate delim pseudo values for. | ||
* @returns Expected token data for each input. | ||
*/ | ||
export const generateDelimStream = (inputs: string[]): PseudoValues => { | ||
const result: PseudoValues = {}; | ||
|
||
for (const input of inputs) { | ||
result[input] = input.split('').map((_, index) => ( | ||
[TokenType.Delim, index, index + 1] as TokenData | ||
)); | ||
} | ||
|
||
return result; | ||
}; |
Oops, something went wrong.