From 46b13e45d637482dc905fe8bb513c81ce1d9272e Mon Sep 17 00:00:00 2001 From: koal44 Date: Sat, 4 Apr 2026 17:21:40 -0700 Subject: [PATCH 01/19] switch build and packaging over to Node tooling - add clean/min/lint - remove shell/bin tooling --- .eslintrc.yml | 123 --- .github/workflows/node.js.yml | 28 - .gitignore | 1 - Makefile | 58 -- bin/jsl.exe | Bin 557056 -> 0 bytes bin/jsl_linux | Bin 498217 -> 0 bytes bin/jsl_macos | Bin 500140 -> 0 bytes bin/jsmin.exe | Bin 11776 -> 0 bytes bin/jsmin_linux | Bin 7306 -> 0 bytes bin/jsmin_macos | Bin 12652 -> 0 bytes bin/os_list | 6 - bower.json | 34 - build/HEADER | 5 - build/VERSION | 1 - build/conf/jsl.conf | 123 --- build/conf/jsl.default.conf | 123 --- build/packer/base2.js | 1678 --------------------------------- build/packer/packer.js | 559 ----------- build/scripts/nwjslint.sh | 47 - build/scripts/nwpacker.js | 34 - build/scripts/nwpackjs.sh | 72 -- dist/lint.log | 0 eslint.config.mjs | 33 + package.json | 16 +- scripts/clean.mjs | 4 + scripts/min.mjs | 31 + src/nwsapi.js | 2 +- 27 files changed, 77 insertions(+), 2901 deletions(-) delete mode 100644 .eslintrc.yml delete mode 100644 .github/workflows/node.js.yml delete mode 100755 Makefile delete mode 100755 bin/jsl.exe delete mode 100755 bin/jsl_linux delete mode 100755 bin/jsl_macos delete mode 100755 bin/jsmin.exe delete mode 100755 bin/jsmin_linux delete mode 100755 bin/jsmin_macos delete mode 100644 bower.json delete mode 100644 build/HEADER delete mode 100644 build/VERSION delete mode 100755 build/conf/jsl.conf delete mode 100755 build/conf/jsl.default.conf delete mode 100644 build/packer/base2.js delete mode 100644 build/packer/packer.js delete mode 100755 build/scripts/nwjslint.sh delete mode 100755 build/scripts/nwpacker.js delete mode 100755 build/scripts/nwpackjs.sh delete mode 100644 dist/lint.log create mode 100644 eslint.config.mjs create mode 100644 scripts/clean.mjs create mode 100644 scripts/min.mjs diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 570fefd..0000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,123 +0,0 @@ -env: - amd: true - browser: true - commonjs: true - -rules: - accessor-pairs: 2 - array-callback-return: 2 - block-scoped-var: 0 - class-methods-use-this: 2 - complexity: 0 - consistent-return: 2 - curly: 0 - default-case: 2 - dot-location: 2 - dot-notation: 0 - eqeqeq: 2 - for-direction: 2 - getter-return: 2 - guard-for-in: 0 - init-declarations: 0 - no-alert: 2 - no-await-in-loop: 2 - no-caller: 2 - no-case-declarations: 2 - no-catch-shadow: 2 - no-compare-neg-zero: 2 - no-cond-assign: 2 - no-console: 0 - no-constant-condition: 2 - no-control-regex: 0 - no-debugger: 2 - no-delete-var: 2 - no-div-regex: 2 - no-dupe-args: 2 - no-dupe-keys: 2 - no-duplicate-case: 2 - no-else-return: 2 - no-empty-character-class: 2 - no-empty-function: 2 - no-empty-pattern: 2 - no-empty: 2 - no-eq-null: 2 - no-eval: 2 - no-ex-assign: 2 - no-extend-native: 2 - no-extra-bind: 2 - no-extra-boolean-cast: 2 - no-extra-label: 2 - no-extra-parens: 2 - no-extra-semi: 2 - no-fallthrough: 2 - no-floating-decimal: 2 - no-func-assign: 2 - no-global-assign: 2 - no-implicit-coercion: 2 - no-implicit-globals: 2 - no-implied-eval: 2 - no-inner-declarations: 2 - no-invalid-regexp: 2 - no-invalid-this: 2 - no-irregular-whitespace: 2 - no-iterator: 2 - no-label-var: 2 - no-labels: 2 - no-lone-blocks: 2 - no-loop-func: 2 - no-magic-numbers: 0 - no-multi-spaces: 2 - no-multi-str: 2 - no-new-func: 0 - no-new-wrappers: 2 - no-new: 2 - no-obj-calls: 2 - no-octal-escape: 2 - no-octal: 2 - no-param-reassign: 0 - no-proto: 0 - no-prototype-builtins: 2 - no-redeclare: 2 - no-regex-spaces: 2 - no-restricted-globals: 2 - no-restricted-properties: 2 - no-return-assign: 2 - no-return-await: 2 - no-script-url: 2 - no-self-assign: 2 - no-self-compare: 2 - no-sequences: 2 - no-shadow-restricted-names: 2 - no-shadow: 0 - no-sparse-arrays: 2 - no-template-curly-in-string: 2 - no-throw-literal: 2 - no-undef-init: 2 - no-undef: 2 - no-undefined: 0 - no-unexpected-multiline: 2 - no-unmodified-loop-condition: 2 - no-unreachable: 2 - no-unsafe-finally: 2 - no-unsafe-negation: 2 - no-unused-expressions: 0 - no-unused-labels: 2 - no-unused-vars: 2 - no-use-before-define: 0 - no-useless-call: 2 - no-useless-concat: 2 - no-useless-escape: 2 - no-useless-return: 2 - no-void: 2 - no-warning-comments: 2 - no-with: 2 - prefer-promise-reject-errors: 2 - radix: 2 - require-await: 2 - strict: 0 - use-isnan: 2 - valid-jsdoc: 2 - valid-typeof: 2 - vars-on-top: 0 - wrap-iife: 0 - yoda: 2 diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 0873017..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: nwsapi - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: '14' - - - name: Install dependencies - run: npm install - - - name: Run tests - run: npm testIs the NodeJS yaml configuration needed in my "nwsapi" repository ? diff --git a/.gitignore b/.gitignore index 5be2eea..504afef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules/ package-lock.json -yarn.lock diff --git a/Makefile b/Makefile deleted file mode 100755 index 84068ab..0000000 --- a/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# -# Makefile -# -# NWSAPI distribution builder -# -# currently just a wrapper for: -# -# - nwjslint.sh (writes to lint.log) -# - nwpackjs.sh (writes to dist.log) -# -# minifier by Dean Edwards -# - -# getting path -SOURCES=./ - -# resets default rules -.SUFFIXES: - -# just in case they exists -.PHONY: all clean dist lint - -# add a SILENT flag to stop -# showing directories access -ifeq ($(filter -s,$(MAKEFLAGS)),) -MAKEFLAGS += -s -endif - -# shell scripts directory -SCRIPTS=${SOURCES}/build/scripts - -# default all stages -all: - @make clean ${SOURCES} - @make lint ${SOURCES} - @make dist ${SOURCES} - @make test ${SOURCES} - -# clean stage -clean: - @rm -f dist/nwsapi.js - @rm -f dist/nwsapi-min.js - @rm -f dist/nwsapi-pac.js - @rm -f dist/nwsapi-src.js - @rm -f dist/nwsapi-zip.js - @rm -f dist/lint.log - -# dist stage -dist: - @${SCRIPTS}/nwpackjs.sh ${SOURCES} - -# lint stage -lint: - @${SCRIPTS}/nwjslint.sh ${SOURCES} - -# test stage -#test: -# @${SCRIPTS}/nwtestjs.sh ${SOURCES} diff --git a/bin/jsl.exe b/bin/jsl.exe deleted file mode 100755 index 30dbd14ac5462f490717340d8989553cefa36479..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 557056 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 -AjGx?V-co*+o!W*#SA6(5bine${eJORIlZ*F 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

VTbAaL}0u&=2P*Tbs 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

^wNTx?#q#F`JoOlRv0N%n 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 Ie`|sN4T(of*Z=?k diff --git a/bin/jsl_linux b/bin/jsl_linux deleted file mode 100755 index b2882eb2ef4b8752a2eada89683b4942d073183b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 498217 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 diff --git a/bin/jsmin_linux b/bin/jsmin_linux deleted file mode 100755 index 8117c988d66cdb2bed852ef49f8fe35f1a3df9f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7306 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 vWu|1K68zg{N?t%-Ao#b=lx!_jwY?ZLaFiO-= diff --git a/bin/jsmin_macos b/bin/jsmin_macos deleted file mode 100755 index a96e1d6bf4649c863f33b91645c38961e0bb01a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12652 zcmeHOU2IcT96#F%l&;XBfCE7a7J*II35)U}vf*ybfDPOSS=jK>ZI@BfbxV7SL+D0w zSCXbSB)+IIKInrXMhzMtd}Dkh(-3*lC=bL`oynaY3794hRO;`4KYH7JppPcplbv(^ z=l?(duirWM+%`SCe(%=(1&py0)MC_9)M^`JG4w^KTTw;vK;6s2A)$Fc21Y3}h?za< zjiQQTtI*z>Cnp_44?X#vhf02_h$5E;xkhW_LjA$V7`p?dK~zIWoo!=h3@uUY4)c&d z*sC+9#}HV<*qe|AT!l9B?NR(TG@@8v*IH*-M(;*7a?s-^+^A9!#U3B`X|y(;sb5Y4 z=;r$~HB=iX13e~ZdJN@m##TXG{y-aD;jn39Mjkb=Vb|sPaUkVfA$k;56dM{3>~A(# zu^LSHHn5;Iq-xj^#esf*kV_#wfZ6n&m{0nK4h`p1qZd&;9_bqpxvwiA5o@}QTEwsd z{91RR9&)nLTF{=N__e*euc2wLP~Tu=P>Z!EA#6}jvD45f6qC9}kYkIm@6f-Eo}P;X zsQJan-M}(mX3$?ie>VAIOrUinEWD9$$Q$r?d3&^_r})Vi#Z`v-Ukg1Svla4DgGza6 zn3Lrq(=UAYc6W>6s%NVZg1-yW={hAv{WEl0PKLSESG7B*gc(+`5U>#VcLa{J_^3J-*LlldlwA`lYz&8d&y3iG0Nt8;$&dfm1n*<0fA3VM-j&aYi8* z?b!=0I9w8EI?!fQ8ZU8XuR3ajgUNC^YCkeGAMNWy)o8scy=}+?x>ltR4bdEtKZ4f7 zooG8b%^wJcjYXr}7d>xz8r_GV4HgGJMF<9q{Eri*0ary!F zmcgEK>u+r#NHb>+W>q(A+hy0tsG6%`*)d$ByTPP3EmAuov?t}C@ZQ4)x5b?v<)dXL zB=3MWL+f#oaaRXWuObMBXVl*&B!E6Lt@-5#Yy zX=>?V&0VpJ0Llz+qif`RLBdbFM&2r5>~xKsEnsZtCr$kh9xw-Mxo8fa;?3sZG#AXl zF221MraqizYObi>$K>?cyflWt)dw@S(-K-LeLHnP&3MB0kiQ$cKW#nbtDfAs zp+9Fyc{LuUF-C_GH0fid68gmmmFa#V5fgJ|7+Wl~o%VuMve2EdyYO?CbOfcxvnc^fyySg}ry-7JbS;=3Zc6c3HH?Kpxkyqqg z!f>f(xFP)YuKHK0?(pfr$` z+YG#i*cZUou+onCvv&)#+eMQSnlTl`DlpQ~oQirfSMuFB!77J0qTRp|er6NL+bXk; z64aqaf)$$W&1pc8IZO#=qLg5!Ai-|{Lwc2w?nN3c#z=Mk8I5xB%2QcatQsfwEbDK) z`>JsmUrH612u?aQC-89`N2`8drI41f!|p7v3y9N=?=Yw~R7TY* z76KLm76KLm76KLm76KLm76KLm76KLm76KLm4;2ELxynO@f|aa=fQ5jCfQ5jCfQ5jC zfQ5jCfQ5jCfQ5jC!2cToT=i!z+>M~$`036s(eztC?)RuP{oap1+;PW60Q}YHe7Xy; zlW1HSDaD=omvox$AUA*po9>RV);#*vJle0)Md}>wX0b*O=JDfs^tn8GA!ywDQ}0#N z9R|9aO|dURrF+#>#HRMTP;a;1pTHe*aUj&+8}#)_p!<%A(xB8G;gWbv^7Tj|CQ88* z{!o9AiG4nQkcGJ~#7Qa4Aq@@aq(5j-{SgjQ8uVk<(E" - ], - "description": "Fast CSS Selectors API Engine", - "main": "src/nwsapi.js", - "moduleType": [ - "globals" - ], - "keywords": [ - "javascript", - "selector", - "matcher", - "library", - "nwsapi" - ], - "license": "MIT", - "ignore": [ - "*/.*", - "*.md", - "bin", - "test", - "dist", - "build", - "Makefile", - ".gitignore", - ".npmignore", - "package.json", - ".gitattributes" - ] -} diff --git a/build/HEADER b/build/HEADER deleted file mode 100644 index 43cf79a..0000000 --- a/build/HEADER +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * NWSAPI 2.2.23 - Fast CSS Selectors API Engine - * Copyright (c) 2007-2025 Diego Perini - * See http://nwbox.com/license - */ diff --git a/build/VERSION b/build/VERSION deleted file mode 100644 index 2f09892..0000000 --- a/build/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.2.23 diff --git a/build/conf/jsl.conf b/build/conf/jsl.conf deleted file mode 100755 index 5385825..0000000 --- a/build/conf/jsl.conf +++ /dev/null @@ -1,123 +0,0 @@ -# -# Configuration File for JavaScript Lint 0.3.0 -# Developed by Matthias Miller (http://www.JavaScriptLint.com) -# -# This configuration file can be used to lint a collection of scripts, or to enable -# or disable warnings for scripts that are linted via the command line. -# - -### Warnings -# Enable or disable warnings based on requirements. -# Use "+WarningName" to display or "-WarningName" to suppress. -# --no_return_value # function {0} does not always return a value -+duplicate_formal # duplicate formal argument {0} --equal_as_assign # test for equality (==) mistyped as assignment (=)?{0} -+var_hides_arg # variable {0} hides argument -+redeclared_var # redeclaration of {0} {1} --anon_no_return_value # anonymous function does not always return a value --missing_semicolon # missing semicolon -+meaningless_block # meaningless block; curly braces have no impact -+comma_separated_stmts # multiple statements separated by commas (use semicolons?) -+unreachable_code # unreachable code -+missing_break # missing break statement -+missing_break_for_last_case # missing break statement for last case in switch --comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) --inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement -+useless_void # use of the void type may be unnecessary (void is always undefined) -+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs -+use_of_label # use of label --block_without_braces # block statement without curly braces -+leading_decimal_point # leading decimal point may indicate a number or an object member -+trailing_decimal_point # trailing decimal point may indicate a number or an object member -+octal_number # leading zeros make an octal number -+nested_comment # nested comment -+misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma --ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement -+empty_statement # empty statement or extra semicolon --missing_option_explicit # the "option explicit" control comment is missing -+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag -+dup_option_explicit # duplicate "option explicit" control comment -+useless_assign # useless assignment --ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity -+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent) -+missing_default_case # missing default case in switch statement -+duplicate_case_in_switch # duplicate case in switch statements -+default_not_at_end # the default case is not at the end of the switch statement -+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax -+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax -+useless_comparison # useless comparison; comparing identical expressions -+with_statement # with statement hides undeclared variables; use temporary variable instead -+trailing_comma_in_array # extra comma is not recommended in array initializers -+assign_to_function_call # assignment to a function call -+parseint_missing_radix # parseInt missing radix parameter - - -### Output format -# Customize the format of the error message. -# __FILE__ indicates current file path -# __FILENAME__ indicates current file name -# __LINE__ indicates current line -# __ERROR__ indicates error message -# -# Visual Studio syntax (default): -+output-format __FILE__(__LINE__): __ERROR__ -# Alternative syntax: -#+output-format __FILE__:__LINE__: __ERROR__ - - -### Context -# Show the in-line position of the error. -# Use "+context" to display or "-context" to suppress. -# -+context - - -### Semicolons -# By default, assignments of an anonymous function to a variable or -# property (such as a function prototype) must be followed by a semicolon. -# -+lambda_assign_requires_semicolon - - -### Control Comments -# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for -# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is -# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, -# although legacy control comments are enabled by default for backward compatibility. -# -+legacy_control_comments - - -### JScript Function Extensions -# JScript allows member functions to be defined like this: -# function MyObj() { /*constructor*/ } -# function MyObj.prototype.go() { /*member function*/ } -# -# It also allows events to be attached like this: -# function window::onload() { /*init page*/ } -# -# This is a Microsoft-only JavaScript extension. Enable this setting to allow them. -# --jscript_function_extensions - - -### Defining identifiers -# By default, "option explicit" is enabled on a per-file basis. -# To enable this for all files, use "+always_use_option_explicit" --always_use_option_explicit - -# Define certain identifiers of which the lint is not aware. -# (Use this in conjunction with the "undeclared identifier" warning.) -# -# Common uses for webpages might be: -#+define window -#+define document - -### Files -# Specify which files to lint -# Use "+recurse" to enable recursion (disabled by default). -# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", -# or "+process Folder\Path\*.htm". -# -#+process jsl-test.js diff --git a/build/conf/jsl.default.conf b/build/conf/jsl.default.conf deleted file mode 100755 index 418c0c8..0000000 --- a/build/conf/jsl.default.conf +++ /dev/null @@ -1,123 +0,0 @@ -# -# Configuration File for JavaScript Lint 0.3.0 -# Developed by Matthias Miller (http://www.JavaScriptLint.com) -# -# This configuration file can be used to lint a collection of scripts, or to enable -# or disable warnings for scripts that are linted via the command line. -# - -### Warnings -# Enable or disable warnings based on requirements. -# Use "+WarningName" to display or "-WarningName" to suppress. -# -+no_return_value # function {0} does not always return a value -+duplicate_formal # duplicate formal argument {0} -+equal_as_assign # test for equality (==) mistyped as assignment (=)?{0} -+var_hides_arg # variable {0} hides argument -+redeclared_var # redeclaration of {0} {1} -+anon_no_return_value # anonymous function does not always return a value -+missing_semicolon # missing semicolon -+meaningless_block # meaningless block; curly braces have no impact -+comma_separated_stmts # multiple statements separated by commas (use semicolons?) -+unreachable_code # unreachable code -+missing_break # missing break statement -+missing_break_for_last_case # missing break statement for last case in switch -+comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) -+inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement -+useless_void # use of the void type may be unnecessary (void is always undefined) -+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs -+use_of_label # use of label --block_without_braces # block statement without curly braces -+leading_decimal_point # leading decimal point may indicate a number or an object member -+trailing_decimal_point # trailing decimal point may indicate a number or an object member -+octal_number # leading zeros make an octal number -+nested_comment # nested comment -+misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma -+ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement -+empty_statement # empty statement or extra semicolon --missing_option_explicit # the "option explicit" control comment is missing -+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag -+dup_option_explicit # duplicate "option explicit" control comment -+useless_assign # useless assignment -+ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity -+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent) -+missing_default_case # missing default case in switch statement -+duplicate_case_in_switch # duplicate case in switch statements -+default_not_at_end # the default case is not at the end of the switch statement -+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax -+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax -+useless_comparison # useless comparison; comparing identical expressions -+with_statement # with statement hides undeclared variables; use temporary variable instead -+trailing_comma_in_array # extra comma is not recommended in array initializers -+assign_to_function_call # assignment to a function call -+parseint_missing_radix # parseInt missing radix parameter - - -### Output format -# Customize the format of the error message. -# __FILE__ indicates current file path -# __FILENAME__ indicates current file name -# __LINE__ indicates current line -# __ERROR__ indicates error message -# -# Visual Studio syntax (default): -+output-format __FILE__(__LINE__): __ERROR__ -# Alternative syntax: -#+output-format __FILE__:__LINE__: __ERROR__ - - -### Context -# Show the in-line position of the error. -# Use "+context" to display or "-context" to suppress. -# -+context - - -### Semicolons -# By default, assignments of an anonymous function to a variable or -# property (such as a function prototype) must be followed by a semicolon. -# -+lambda_assign_requires_semicolon - - -### Control Comments -# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for -# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is -# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, -# although legacy control comments are enabled by default for backward compatibility. -# -+legacy_control_comments - - -### JScript Function Extensions -# JScript allows member functions to be defined like this: -# function MyObj() { /*constructor*/ } -# function MyObj.prototype.go() { /*member function*/ } -# -# It also allows events to be attached like this: -# function window::onload() { /*init page*/ } -# -# This is a Microsoft-only JavaScript extension. Enable this setting to allow them. -# --jscript_function_extensions - - -### Defining identifiers -# By default, "option explicit" is enabled on a per-file basis. -# To enable this for all files, use "+always_use_option_explicit" --always_use_option_explicit - -# Define certain identifiers of which the lint is not aware. -# (Use this in conjunction with the "undeclared identifier" warning.) -# -# Common uses for webpages might be: -#+define window -#+define document - -### Files -# Specify which files to lint -# Use "+recurse" to enable recursion (disabled by default). -# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", -# or "+process Folder\Path\*.htm". -# -#+process jsl-test.js diff --git a/build/packer/base2.js b/build/packer/base2.js deleted file mode 100644 index 226453c..0000000 --- a/build/packer/base2.js +++ /dev/null @@ -1,1678 +0,0 @@ -/* - base2 - copyright 2007-2008, Dean Edwards - http://code.google.com/p/base2/ - http://www.opensource.org/licenses/mit-license.php - - Contributors: - Doeke Zanstra -*/ - -// timestamp: Sat, 06 Sep 2008 16:52:32 - -var base2 = { - name: "base2", - version: "1.0", - exports: - "Base,Package,Abstract,Module,Enumerable,Map,Collection,RegGrp," + - "Undefined,Null,This,True,False,assignID,detect,global", - namespace: "" -}; - -new function(_no_shrink_) { /////////////// BEGIN: CLOSURE /////////////// - -// ========================================================================= -// base2/header.js -// ========================================================================= - -var Undefined = K(), Null = K(null), True = K(true), False = K(false), This = function(){return this}; - -var global = This(); -var base2 = global.base2; - -// private -var _FORMAT = /%([1-9])/g; -var _LTRIM = /^\s\s*/; -var _RTRIM = /\s\s*$/; -var _RESCAPE = /([\/()[\]{}|*+-.,^$?\\])/g; // safe regular expressions -var _BASE = /try/.test(detect) ? /\bbase\b/ : /.*/; // some platforms don't allow decompilation -var _HIDDEN = ["constructor", "toString", "valueOf"]; // only override these when prototyping -var _MSIE_NATIVE_FUNCTION = detect("(jscript)") ? - new RegExp("^" + rescape(isNaN).replace(/isNaN/, "\\w+") + "$") : {test: False}; - -var _counter = 1; -var _slice = Array.prototype.slice; - -_Function_forEach(); // make sure this is initialised - -function assignID(object) { - // Assign a unique ID to an object. - if (!object.base2ID) object.base2ID = "b2_" + _counter++; - return object.base2ID; -}; - -// ========================================================================= -// base2/Base.js -// ========================================================================= - -// http://dean.edwards.name/weblog/2006/03/base/ - -var _subclass = function(_instance, _static) { - // Build the prototype. - base2.__prototyping = this.prototype; - var _prototype = new this; - if (_instance) extend(_prototype, _instance); - delete base2.__prototyping; - - // Create the wrapper for the constructor function. - var _constructor = _prototype.constructor; - function _class() { - // Don't call the constructor function when prototyping. - if (!base2.__prototyping) { - if (this.constructor == arguments.callee || this.__constructing) { - // Instantiation. - this.__constructing = true; - _constructor.apply(this, arguments); - delete this.__constructing; - } else { - // Casting. - return extend(arguments[0], _prototype); - } - } - return this; - }; - _prototype.constructor = _class; - - // Build the static interface. - for (var i in Base) _class[i] = this[i]; - _class.ancestor = this; - _class.base = Undefined; - //_class.init = Undefined; - if (_static) extend(_class, _static); - _class.prototype = _prototype; - if (_class.init) _class.init(); - - // introspection (removed when packed) - ;;; _class["#implements"] = []; - ;;; _class["#implemented_by"] = []; - - return _class; -}; - -var Base = _subclass.call(Object, { - constructor: function() { - if (arguments.length > 0) { - this.extend(arguments[0]); - } - }, - - base: function() { - // Call this method from any other method to invoke the current method's ancestor (super). - }, - - extend: delegate(extend) -}, Base = { - ancestorOf: function(klass) { - return _ancestorOf(this, klass); - }, - - extend: _subclass, - - forEach: function(object, block, context) { - _Function_forEach(this, object, block, context); - }, - - implement: function(source) { - if (typeof source == "function") { - ;;; if (_ancestorOf(Base, source)) { - // introspection (removed when packed) - ;;; this["#implements"].push(source); - ;;; source["#implemented_by"].push(this); - ;;; } - source = source.prototype; - } - // Add the interface using the extend() function. - extend(this.prototype, source); - return this; - } -}); - -// ========================================================================= -// base2/Package.js -// ========================================================================= - -var Package = Base.extend({ - constructor: function(_private, _public) { - this.extend(_public); - if (this.init) this.init(); - - if (this.name && this.name != "base2") { - if (!this.parent) this.parent = base2; - this.parent.addName(this.name, this); - this.namespace = format("var %1=%2;", this.name, String2.slice(this, 1, -1)); - } - - if (_private) { - // This next line gets round a bug in old Mozilla browsers - var JSNamespace = base2.JavaScript ? base2.JavaScript.namespace : ""; - // This string should be evaluated immediately after creating a Package object. - _private.imports = Array2.reduce(csv(this.imports), function(namespace, name) { - var ns = lookup(name) || lookup("JavaScript." + name); - ;;; assert(ns, format("Object not found: '%1'.", name), ReferenceError); - return namespace += ns.namespace; - }, "var base2=(function(){return this.base2})();" + base2.namespace + JSNamespace) + lang.namespace; - - // This string should be evaluated after you have created all of the objects - // that are being exported. - _private.exports = Array2.reduce(csv(this.exports), function(namespace, name) { - var fullName = this.name + "." + name; - this.namespace += "var " + name + "=" + fullName + ";"; - return namespace += "if(!" + fullName + ")" + fullName + "=" + name + ";"; - }, "", this) + "this._label_" + this.name + "();"; - - var pkg = this; - var packageName = String2.slice(this, 1, -1); - _private["_label_" + this.name] = function() { - Package.forEach (pkg, function(object, name) { - if (object && object.ancestorOf == Base.ancestorOf) { - object.toString = K(format("[%1.%2]", packageName, name)); - if (object.prototype.toString == Base.prototype.toString) { - object.prototype.toString = K(format("[object %1.%2]", packageName, name)); - } - } - }); - }; - } - - function lookup(names) { - names = names.split("."); - var value = base2, i = 0; - while (value && names[i] != null) { - value = value[names[i++]]; - } - return value; - }; - }, - - exports: "", - imports: "", - name: "", - namespace: "", - parent: null, - - addName: function(name, value) { - if (!this[name]) { - this[name] = value; - this.exports += "," + name; - this.namespace += format("var %1=%2.%1;", name, this.name); - } - }, - - addPackage: function(name) { - this.addName(name, new Package(null, {name: name, parent: this})); - }, - - toString: function() { - return format("[%1]", this.parent ? String2.slice(this.parent, 1, -1) + "." + this.name : this.name); - } -}); - -// ========================================================================= -// base2/Abstract.js -// ========================================================================= - -var Abstract = Base.extend({ - constructor: function() { - throw new TypeError("Abstract class cannot be instantiated."); - } -}); - -// ========================================================================= -// base2/Module.js -// ========================================================================= - -var _moduleCount = 0; - -var Module = Abstract.extend(null, { - namespace: "", - - extend: function(_interface, _static) { - // Extend a module to create a new module. - var module = this.base(); - var index = _moduleCount++; - module.namespace = ""; - module.partial = this.partial; - module.toString = K("[base2.Module[" + index + "]]"); - Module[index] = module; - // Inherit class methods. - module.implement(this); - // Implement module (instance AND static) methods. - if (_interface) module.implement(_interface); - // Implement static properties and methods. - if (_static) { - extend(module, _static); - if (module.init) module.init(); - } - return module; - }, - - forEach: function(block, context) { - _Function_forEach (Module, this.prototype, function(method, name) { - if (typeOf(method) == "function") { - block.call(context, this[name], name, this); - } - }, this); - }, - - implement: function(_interface) { - var module = this; - var id = module.toString().slice(1, -1); - if (typeof _interface == "function") { - if (!_ancestorOf(_interface, module)) { - this.base(_interface); - } - if (_ancestorOf(Module, _interface)) { - // Implement static methods. - for (var name in _interface) { - if (module[name] === undefined) { - var property = _interface[name]; - if (typeof property == "function" && property.call && _interface.prototype[name]) { - property = _staticModuleMethod(_interface, name); - } - module[name] = property; - } - } - module.namespace += _interface.namespace.replace(/base2\.Module\[\d+\]/g, id); - } - } else { - // Add static interface. - extend(module, _interface); - // Add instance interface. - _extendModule(module, _interface); - } - return module; - }, - - partial: function() { - var module = Module.extend(); - var id = module.toString().slice(1, -1); - // partial methods are already bound so remove the binding to speed things up - module.namespace = this.namespace.replace(/(\w+)=b[^\)]+\)/g, "$1=" + id + ".$1"); - this.forEach(function(method, name) { - module[name] = partial(bind(method, module)); - }); - return module; - } -}); - -function _extendModule(module, _interface) { - var proto = module.prototype; - var id = module.toString().slice(1, -1); - for (var name in _interface) { - var property = _interface[name], namespace = ""; - if (name.charAt(0) == "@") { // object detection - if (detect(name.slice(1))) _extendModule(module, property); - } else if (!proto[name]) { - if (name == name.toUpperCase()) { - namespace = "var " + name + "=" + id + "." + name + ";"; - } else if (typeof property == "function" && property.call) { - namespace = "var " + name + "=base2.lang.bind('" + name + "'," + id + ");"; - proto[name] = _moduleMethod(module, name); - ;;; proto[name]._module = module; // introspection - } - if (module.namespace.indexOf(namespace) == -1) { - module.namespace += namespace; - } - } - } -}; - -function _staticModuleMethod(module, name) { - return function() { - return module[name].apply(module, arguments); - }; -}; - -function _moduleMethod(module, name) { - return function() { - var args = _slice.call(arguments); - args.unshift(this); - return module[name].apply(module, args); - }; -}; - -// ========================================================================= -// base2/Enumerable.js -// ========================================================================= - -var Enumerable = Module.extend({ - every: function(object, test, context) { - var result = true; - try { - forEach (object, function(value, key) { - result = test.call(context, value, key, object); - if (!result) throw StopIteration; - }); - } catch (error) { - if (error != StopIteration) throw error; - } - return !!result; // cast to boolean - }, - - filter: function(object, test, context) { - var i = 0; - return this.reduce(object, function(result, value, key) { - if (test.call(context, value, key, object)) { - result[i++] = value; - } - return result; - }, []); - }, - - invoke: function(object, method) { - // Apply a method to each item in the enumerated object. - var args = _slice.call(arguments, 2); - return this.map(object, (typeof method == "function") ? function(item) { - return item == null ? undefined : method.apply(item, args); - } : function(item) { - return item == null ? undefined : item[method].apply(item, args); - }); - }, - - map: function(object, block, context) { - var result = [], i = 0; - forEach (object, function(value, key) { - result[i++] = block.call(context, value, key, object); - }); - return result; - }, - - pluck: function(object, key) { - return this.map(object, function(item) { - return item == null ? undefined : item[key]; - }); - }, - - reduce: function(object, block, result, context) { - var initialised = arguments.length > 2; - forEach (object, function(value, key) { - if (initialised) { - result = block.call(context, result, value, key, object); - } else { - result = value; - initialised = true; - } - }); - return result; - }, - - some: function(object, test, context) { - return !this.every(object, not(test), context); - } -}); - -// ========================================================================= -// base2/Map.js -// ========================================================================= - -// http://wiki.ecmascript.org/doku.php?id=proposals:dictionary - -var _HASH = "#"; - -var Map = Base.extend({ - constructor: function(values) { - if (values) this.merge(values); - }, - - clear: function() { - for (var key in this) if (key.indexOf(_HASH) == 0) { - delete this[key]; - } - }, - - copy: function() { - base2.__prototyping = true; // not really prototyping but it stops [[construct]] being called - var copy = new this.constructor; - delete base2.__prototyping; - for (var i in this) if (this[i] !== copy[i]) { - copy[i] = this[i]; - } - return copy; - }, - - forEach: function(block, context) { - for (var key in this) if (key.indexOf(_HASH) == 0) { - block.call(context, this[key], key.slice(1), this); - } - }, - - get: function(key) { - return this[_HASH + key]; - }, - - getKeys: function() { - return this.map(II); - }, - - getValues: function() { - return this.map(I); - }, - - // Ancient browsers throw an error if we use "in" as an operator. - has: function(key) { - /*@cc_on @*/ - /*@if (@_jscript_version < 5.5) - return $Legacy.has(this, _HASH + key); - @else @*/ - return _HASH + key in this; - /*@end @*/ - }, - - merge: function(values) { - var put = flip(this.put); - forEach (arguments, function(values) { - forEach (values, put, this); - }, this); - return this; - }, - - put: function(key, value) { - // create the new entry (or overwrite the old entry). - this[_HASH + key] = value; - }, - - remove: function(key) { - delete this[_HASH + key]; - }, - - size: function() { - // this is expensive because we are not storing the keys - var size = 0; - for (var key in this) if (key.indexOf(_HASH) == 0) size++; - return size; - }, - - union: function(values) { - return this.merge.apply(this.copy(), arguments); - } -}); - -Map.implement(Enumerable); - -Map.prototype.filter = function(test, context) { - return this.reduce(function(result, value, key) { - if (!test.call(context, value, key, this)) { - result.remove(key); - } - return result; - }, this.copy(), this); -}; - -// ========================================================================= -// base2/Collection.js -// ========================================================================= - -// A Map that is more array-like (accessible by index). - -// Collection classes have a special (optional) property: Item -// The Item property points to a constructor function. -// Members of the collection must be an instance of Item. - -// The static create() method is responsible for all construction of collection items. -// Instance methods that add new items (add, put, insertAt, putAt) pass *all* of their arguments -// to the static create() method. If you want to modify the way collection items are -// created then you only need to override this method for custom collections. - -var _KEYS = "~"; - -var Collection = Map.extend({ - constructor: function(values) { - this[_KEYS] = new Array2; - this.base(values); - }, - - add: function(key, item) { - // Duplicates not allowed using add(). - // But you can still overwrite entries using put(). - assert(!this.has(key), "Duplicate key '" + key + "'."); - this.put.apply(this, arguments); - }, - - clear: function() { - this.base(); - this[_KEYS].length = 0; - }, - - copy: function() { - var copy = this.base(); - copy[_KEYS] = this[_KEYS].copy(); - return copy; - }, - - forEach: function(block, context) { - var keys = this[_KEYS]; - var length = keys.length; - for (var i = 0; i < length; i++) { - block.call(context, this[_HASH + keys[i]], keys[i], this); - } - }, - - getAt: function(index) { - var key = this[_KEYS].item(index); - return (key === undefined) ? undefined : this[_HASH + key]; - }, - - getKeys: function() { - return this[_KEYS].copy(); - }, - - indexOf: function(key) { - return this[_KEYS].indexOf(String(key)); - }, - - insertAt: function(index, key, item) { - assert(this[_KEYS].item(index) !== undefined, "Index out of bounds."); - assert(!this.has(key), "Duplicate key '" + key + "'."); - this[_KEYS].insertAt(index, String(key)); - this[_HASH + key] = null; // placeholder - this.put.apply(this, _slice.call(arguments, 1)); - }, - - item: function(keyOrIndex) { - return this[typeof keyOrIndex == "number" ? "getAt" : "get"](keyOrIndex); - }, - - put: function(key, item) { - if (!this.has(key)) { - this[_KEYS].push(String(key)); - } - var klass = this.constructor; - if (klass.Item && !instanceOf(item, klass.Item)) { - item = klass.create.apply(klass, arguments); - } - this[_HASH + key] = item; - }, - - putAt: function(index, item) { - arguments[0] = this[_KEYS].item(index); - assert(arguments[0] !== undefined, "Index out of bounds."); - this.put.apply(this, arguments); - }, - - remove: function(key) { - // The remove() method of the Array object can be slow so check if the key exists first. - if (this.has(key)) { - this[_KEYS].remove(String(key)); - delete this[_HASH + key]; - } - }, - - removeAt: function(index) { - var key = this[_KEYS].item(index); - if (key !== undefined) { - this[_KEYS].removeAt(index); - delete this[_HASH + key]; - } - }, - - reverse: function() { - this[_KEYS].reverse(); - return this; - }, - - size: function() { - return this[_KEYS].length; - }, - - slice: function(start, end) { - var sliced = this.copy(); - if (arguments.length > 0) { - var keys = this[_KEYS], removed = keys; - sliced[_KEYS] = Array2(_slice.apply(keys, arguments)); - if (sliced[_KEYS].length) { - removed = removed.slice(0, start); - if (arguments.length > 1) { - removed = removed.concat(keys.slice(end)); - } - } - for (var i = 0; i < removed.length; i++) { - delete sliced[_HASH + removed[i]]; - } - } - return sliced; - }, - - sort: function(compare) { // optimised (refers to _HASH) - if (compare) { - this[_KEYS].sort(bind(function(key1, key2) { - return compare(this[_HASH + key1], this[_HASH + key2], key1, key2); - }, this)); - } else this[_KEYS].sort(); - return this; - }, - - toString: function() { - return "(" + (this[_KEYS] || "") + ")"; - } -}, { - Item: null, // If specified, all members of the collection must be instances of Item. - - create: function(key, item) { - return this.Item ? new this.Item(key, item) : item; - }, - - extend: function(_instance, _static) { - var klass = this.base(_instance); - klass.create = this.create; - if (_static) extend(klass, _static); - if (!klass.Item) { - klass.Item = this.Item; - } else if (typeof klass.Item != "function") { - klass.Item = (this.Item || Base).extend(klass.Item); - } - if (klass.init) klass.init(); - return klass; - } -}); - -// ========================================================================= -// base2/RegGrp.js -// ========================================================================= - -// A collection of regular expressions and their associated replacement values. -// A Base class for creating parsers. - -var _RG_BACK_REF = /\\(\d+)/g, - _RG_ESCAPE_CHARS = /\\./g, - _RG_ESCAPE_BRACKETS = /\(\?[:=!]|\[[^\]]+\]/g, - _RG_BRACKETS = /\(/g, - _RG_LOOKUP = /\$(\d+)/, - _RG_LOOKUP_SIMPLE = /^\$\d+$/; - -var RegGrp = Collection.extend({ - constructor: function(values, ignoreCase) { - this.base(values); - this.ignoreCase = !!ignoreCase; - }, - - ignoreCase: false, - - exec: function(string, override) { // optimised (refers to _HASH/_KEYS) - string += ""; // type-safe - var items = this, keys = this[_KEYS]; - if (!keys.length) return string; - if (override == RegGrp.IGNORE) override = 0; - return string.replace(new RegExp(this, this.ignoreCase ? "gi" : "g"), function(match) { - var item, offset = 1, i = 0; - // Loop through the RegGrp items. - while ((item = items[_HASH + keys[i++]])) { - var next = offset + item.length + 1; - if (arguments[offset]) { // do we have a result? - var replacement = override == null ? item.replacement : override; - switch (typeof replacement) { - case "function": - return replacement.apply(items, _slice.call(arguments, offset, next)); - case "number": - return arguments[offset + replacement]; - default: - return replacement; - } - } - offset = next; - } - return match; - }); - }, - - insertAt: function(index, expression, replacement) { - if (instanceOf(expression, RegExp)) { - arguments[1] = expression.source; - } - return base(this, arguments); - }, - - test: function(string) { - // The slow way to do it. Hopefully, this isn't called too often. :-) - return this.exec(string) != string; - }, - - toString: function() { - var offset = 1; - return "(" + this.map(function(item) { - // Fix back references. - var expression = (item + "").replace(_RG_BACK_REF, function(match, index) { - return "\\" + (offset + Number(index)); - }); - offset += item.length + 1; - return expression; - }).join(")|(") + ")"; - } -}, { - IGNORE: "$0", - - init: function() { - forEach ("add,get,has,put,remove".split(","), function(name) { - _override(this, name, function(expression) { - if (instanceOf(expression, RegExp)) { - arguments[0] = expression.source; - } - return base(this, arguments); - }); - }, this.prototype); - }, - - Item: { - constructor: function(expression, replacement) { - if (replacement == null) replacement = RegGrp.IGNORE; - else if (replacement.replacement != null) replacement = replacement.replacement; - else if (typeof replacement != "function") replacement = String(replacement); - - // does the pattern use sub-expressions? - if (typeof replacement == "string" && _RG_LOOKUP.test(replacement)) { - // a simple lookup? (e.g. "$2") - if (_RG_LOOKUP_SIMPLE.test(replacement)) { - // store the index (used for fast retrieval of matched strings) - replacement = parseInt(replacement.slice(1)); - } else { // a complicated lookup (e.g. "Hello $2 $1") - // build a function to do the lookup - // Improved version by Alexei Gorkov: - var Q = '"'; - replacement = replacement - .replace(/\\/g, "\\\\") - .replace(/"/g, "\\x22") - .replace(/\n/g, "\\n") - .replace(/\r/g, "\\r") - .replace(/\$(\d+)/g, Q + "+(arguments[$1]||" + Q+Q + ")+" + Q) - .replace(/(['"])\1\+(.*)\+\1\1$/, "$1"); - replacement = new Function("return " + Q + replacement + Q); - } - } - - this.length = RegGrp.count(expression); - this.replacement = replacement; - this.toString = K(expression + ""); - }, - - length: 0, - replacement: "" - }, - - count: function(expression) { - // Count the number of sub-expressions in a RegExp/RegGrp.Item. - expression = (expression + "").replace(_RG_ESCAPE_CHARS, "").replace(_RG_ESCAPE_BRACKETS, ""); - return match(expression, _RG_BRACKETS).length; - } -}); - -// ========================================================================= -// lang/package.js -// ========================================================================= - -var lang = { - name: "lang", - version: base2.version, - exports: "assert,assertArity,assertType,base,bind,copy,extend,forEach,format,instanceOf,match,pcopy,rescape,trim,typeOf", - namespace: "" // fixed later -}; - -// ========================================================================= -// lang/assert.js -// ========================================================================= - -function assert(condition, message, ErrorClass) { - if (!condition) { - throw new (ErrorClass || Error)(message || "Assertion failed."); - } -}; - -function assertArity(args, arity, message) { - if (arity == null) arity = args.callee.length; - if (args.length < arity) { - throw new SyntaxError(message || "Not enough arguments."); - } -}; - -function assertType(object, type, message) { - if (type && (typeof type == "function" ? !instanceOf(object, type) : typeOf(object) != type)) { - throw new TypeError(message || "Invalid type."); - } -}; - -// ========================================================================= -// lang/copy.js -// ========================================================================= - -function copy(object) { - // a quick copy - var copy = {}; - for (var i in object) { - copy[i] = object[i]; - } - return copy; -}; - -function pcopy(object) { - // Doug Crockford / Richard Cornford - _dummy.prototype = object; - return new _dummy; -}; - -function _dummy(){}; - -// ========================================================================= -// lang/extend.js -// ========================================================================= - -function base(object, args) { - return object.base.apply(object, args); -}; - -function extend(object, source) { // or extend(object, key, value) - if (object && source) { - if (arguments.length > 2) { // Extending with a key/value pair. - var key = source; - source = {}; - source[key] = arguments[2]; - } - var proto = global[(typeof source == "function" ? "Function" : "Object")].prototype; - // Add constructor, toString etc - if (base2.__prototyping) { - var i = _HIDDEN.length, key; - while ((key = _HIDDEN[--i])) { - var value = source[key]; - if (value != proto[key]) { - if (_BASE.test(value)) { - _override(object, key, value) - } else { - object[key] = value; - } - } - } - } - // Copy each of the source object's properties to the target object. - for (key in source) { - if (proto[key] === undefined) { - var value = source[key]; - // Object detection. - if (key.charAt(0) == "@") { - if (detect(key.slice(1))) extend(object, value); - } else { - // Check for method overriding. - var ancestor = object[key]; - if (ancestor && typeof value == "function") { - if (value != ancestor) { - if (_BASE.test(value)) { - _override(object, key, value); - } else { - value.ancestor = ancestor; - object[key] = value; - } - } - } else { - object[key] = value; - } - } - } - } - } - return object; -}; - -function _ancestorOf(ancestor, fn) { - // Check if a function is in another function's inheritance chain. - while (fn) { - if (!fn.ancestor) return false; - fn = fn.ancestor; - if (fn == ancestor) return true; - } - return false; -}; - -function _override(object, name, method) { - // Override an existing method. - var ancestor = object[name]; - var superObject = base2.__prototyping; // late binding for prototypes - if (superObject && ancestor != superObject[name]) superObject = null; - function _base() { - var previous = this.base; - this.base = superObject ? superObject[name] : ancestor; - var returnValue = method.apply(this, arguments); - this.base = previous; - return returnValue; - }; - _base.method = method; - _base.ancestor = ancestor; - object[name] = _base; - // introspection (removed when packed) - ;;; _base.toString = K(method + ""); -}; - -// ========================================================================= -// lang/forEach.js -// ========================================================================= - -// http://dean.edwards.name/weblog/2006/07/enum/ - -if (typeof StopIteration == "undefined") { - StopIteration = new Error("StopIteration"); -} - -function forEach(object, block, context, fn) { - if (object == null) return; - if (!fn) { - if (typeof object == "function" && object.call) { - // Functions are a special case. - fn = Function; - } else if (typeof object.forEach == "function" && object.forEach != arguments.callee) { - // The object implements a custom forEach method. - object.forEach(block, context); - return; - } else if (typeof object.length == "number") { - // The object is array-like. - _Array_forEach(object, block, context); - return; - } - } - _Function_forEach(fn || Object, object, block, context); -}; - -forEach.csv = function(string, block, context) { - forEach (csv(string), block, context); -}; - -forEach.detect = function(object, block, context) { - forEach (object, function(value, key) { - if (key.charAt(0) == "@") { // object detection - if (detect(key.slice(1))) forEach (value, arguments.callee); - } else block.call(context, value, key, object); - }); -}; - -// These are the two core enumeration methods. All other forEach methods -// eventually call one of these two. - -function _Array_forEach(array, block, context) { - if (array == null) array = global; - var length = array.length || 0, i; // preserve length - if (typeof array == "string") { - for (i = 0; i < length; i++) { - block.call(context, array.charAt(i), i, array); - } - } else { // Cater for sparse arrays. - for (i = 0; i < length; i++) { - /*@cc_on @*/ - /*@if (@_jscript_version < 5.2) - if ($Legacy.has(array, i)) - @else @*/ - if (i in array) - /*@end @*/ - block.call(context, array[i], i, array); - } - } -}; - -function _Function_forEach(fn, object, block, context) { - // http://code.google.com/p/base2/issues/detail?id=10 - - // Run the test for Safari's buggy enumeration. - var Temp = function(){this.i=1}; - Temp.prototype = {i:1}; - var count = 0; - for (var i in new Temp) count++; - - // Overwrite the main function the first time it is called. - _Function_forEach = (count > 1) ? function(fn, object, block, context) { - // Safari fix (pre version 3) - var processed = {}; - for (var key in object) { - if (!processed[key] && fn.prototype[key] === undefined) { - processed[key] = true; - block.call(context, object[key], key, object); - } - } - } : function(fn, object, block, context) { - // Enumerate an object and compare its keys with fn's prototype. - for (var key in object) { - if (fn.prototype[key] === undefined) { - block.call(context, object[key], key, object); - } - } - }; - - _Function_forEach(fn, object, block, context); -}; - -// ========================================================================= -// lang/instanceOf.js -// ========================================================================= - -function instanceOf(object, klass) { - // Handle exceptions where the target object originates from another frame. - // This is handy for JSON parsing (amongst other things). - - if (typeof klass != "function") { - throw new TypeError("Invalid 'instanceOf' operand."); - } - - if (object == null) return false; - - /*@cc_on - // COM objects don't have a constructor - if (typeof object.constructor != "function") { - return typeOf(object) == typeof klass.prototype.valueOf(); - } - @*/ - if (object.constructor == klass) return true; - if (klass.ancestorOf) return klass.ancestorOf(object.constructor); - /*@if (@_jscript_version < 5.1) - // do nothing - @else @*/ - if (object instanceof klass) return true; - /*@end @*/ - - // If the class is a base2 class then it would have passed the test above. - if (Base.ancestorOf == klass.ancestorOf) return false; - - // base2 objects can only be instances of Object. - if (Base.ancestorOf == object.constructor.ancestorOf) return klass == Object; - - switch (klass) { - case Array: // This is the only troublesome one. - return !!(typeof object == "object" && object.join && object.splice); - case Function: - return typeOf(object) == "function"; - case RegExp: - return typeof object.constructor.$1 == "string"; - case Date: - return !!object.getTimezoneOffset; - case String: - case Number: - case Boolean: - return typeOf(object) == typeof klass.prototype.valueOf(); - case Object: - return true; - } - - return false; -}; - -// ========================================================================= -// lang/typeOf.js -// ========================================================================= - -// http://wiki.ecmascript.org/doku.php?id=proposals:typeof - -function typeOf(object) { - var type = typeof object; - switch (type) { - case "object": - return object == null - ? "null" - : typeof object.constructor == "undefined" // COM object - ? _MSIE_NATIVE_FUNCTION.test(object) - ? "function" - : type - : typeof object.constructor.prototype.valueOf(); // underlying type - case "function": - return typeof object.call == "function" ? type : "object"; - default: - return type; - } -}; - -// ========================================================================= -// JavaScript/package.js -// ========================================================================= - -var JavaScript = { - name: "JavaScript", - version: base2.version, - exports: "Array2,Date2,Function2,String2", - namespace: "", // fixed later - - bind: function(host) { - var top = global; - global = host; - forEach.csv(this.exports, function(name2) { - var name = name2.slice(0, -1); - extend(host[name], this[name2]); - this[name2](host[name].prototype); // cast - }, this); - global = top; - return host; - } -}; - -function _createObject2(Native, constructor, generics, extensions) { - // Clone native objects and extend them. - - // Create a Module that will contain all the new methods. - var INative = Module.extend(); - var id = INative.toString().slice(1, -1); - // http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6#Array_and_String_generics - forEach.csv(generics, function(name) { - INative[name] = unbind(Native.prototype[name]); - INative.namespace += format("var %1=%2.%1;", name, id); - }); - forEach (_slice.call(arguments, 3), INative.implement, INative); - - // create a faux constructor that augments the native object - var Native2 = function() { - return INative(this.constructor == INative ? constructor.apply(null, arguments) : arguments[0]); - }; - Native2.prototype = INative.prototype; - - // Remove methods that are already implemented. - for (var name in INative) { - if (name != "prototype" && Native[name]) { - INative[name] = Native[name]; - delete INative.prototype[name]; - } - Native2[name] = INative[name]; - } - Native2.ancestor = Object; - delete Native2.extend; - - // remove "lang.bind.." - Native2.namespace = Native2.namespace.replace(/(var (\w+)=)[^,;]+,([^\)]+)\)/g, "$1$3.$2"); - - return Native2; -}; - -// ========================================================================= -// JavaScript/~/Date.js -// ========================================================================= - -// Fix Date.get/setYear() (IE5-7) - -if ((new Date).getYear() > 1900) { - Date.prototype.getYear = function() { - return this.getFullYear() - 1900; - }; - Date.prototype.setYear = function(year) { - return this.setFullYear(year + 1900); - }; -} - -// https://bugs.webkit.org/show_bug.cgi?id=9532 - -var _testDate = new Date(Date.UTC(2006, 1, 20)); -_testDate.setUTCDate(15); -if (_testDate.getUTCHours() != 0) { - forEach.csv("FullYear,Month,Date,Hours,Minutes,Seconds,Milliseconds", function(type) { - extend(Date.prototype, "setUTC" + type, function() { - var value = base(this, arguments); - if (value >= 57722401000) { - value -= 3600000; - this.setTime(value); - } - return value; - }); - }); -} - -// ========================================================================= -// JavaScript/~/Function.js -// ========================================================================= - -// Some browsers don't define this. - -Function.prototype.prototype = {}; - -// ========================================================================= -// JavaScript/~/String.js -// ========================================================================= - -// A KHTML bug. - -if ("".replace(/^/, K("$$")) == "$") { - extend(String.prototype, "replace", function(expression, replacement) { - if (typeof replacement == "function") { - var fn = replacement; - replacement = function() { - return String(fn.apply(null, arguments)).split("$").join("$$"); - }; - } - return this.base(expression, replacement); - }); -} - -// ========================================================================= -// JavaScript/Array2.js -// ========================================================================= - -var Array2 = _createObject2( - Array, - Array, - "concat,join,pop,push,reverse,shift,slice,sort,splice,unshift", // generics - Enumerable, { - combine: function(keys, values) { - // Combine two arrays to make a hash. - if (!values) values = keys; - return Array2.reduce(keys, function(hash, key, index) { - hash[key] = values[index]; - return hash; - }, {}); - }, - - contains: function(array, item) { - return Array2.indexOf(array, item) != -1; - }, - - copy: function(array) { - var copy = _slice.call(array); - if (!copy.swap) Array2(copy); // cast to Array2 - return copy; - }, - - flatten: function(array) { - var i = 0; - return Array2.reduce(array, function(result, item) { - if (Array2.like(item)) { - Array2.reduce(item, arguments.callee, result); - } else { - result[i++] = item; - } - return result; - }, []); - }, - - forEach: _Array_forEach, - - indexOf: function(array, item, fromIndex) { - var length = array.length; - if (fromIndex == null) { - fromIndex = 0; - } else if (fromIndex < 0) { - fromIndex = Math.max(0, length + fromIndex); - } - for (var i = fromIndex; i < length; i++) { - if (array[i] === item) return i; - } - return -1; - }, - - insertAt: function(array, index, item) { - Array2.splice(array, index, 0, item); - return item; - }, - - item: function(array, index) { - if (index < 0) index += array.length; // starting from the end - return array[index]; - }, - - lastIndexOf: function(array, item, fromIndex) { - var length = array.length; - if (fromIndex == null) { - fromIndex = length - 1; - } else if (fromIndex < 0) { - fromIndex = Math.max(0, length + fromIndex); - } - for (var i = fromIndex; i >= 0; i--) { - if (array[i] === item) return i; - } - return -1; - }, - - map: function(array, block, context) { - var result = []; - Array2.forEach (array, function(item, index) { - result[index] = block.call(context, item, index, array); - }); - return result; - }, - - remove: function(array, item) { - var index = Array2.indexOf(array, item); - if (index != -1) Array2.removeAt(array, index); - }, - - removeAt: function(array, index) { - Array2.splice(array, index, 1); - }, - - swap: function(array, index1, index2) { - if (index1 < 0) index1 += array.length; // starting from the end - if (index2 < 0) index2 += array.length; - var temp = array[index1]; - array[index1] = array[index2]; - array[index2] = temp; - return array; - } - } -); - -Array2.reduce = Enumerable.reduce; // Mozilla does not implement the thisObj argument - -Array2.like = function(object) { - // is the object like an array? - return typeOf(object) == "object" && typeof object.length == "number"; -}; - -// introspection (removed when packed) -;;; Enumerable["#implemented_by"].pop(); -;;; Enumerable["#implemented_by"].push(Array2); - -// ========================================================================= -// JavaScript/Date2.js -// ========================================================================= - -// http://developer.mozilla.org/es4/proposals/date_and_time.html - -// big, ugly, regular expression -var _DATE_PATTERN = /^((-\d+|\d{4,})(-(\d{2})(-(\d{2}))?)?)?T((\d{2})(:(\d{2})(:(\d{2})(\.(\d{1,3})(\d)?\d*)?)?)?)?(([+-])(\d{2})(:(\d{2}))?|Z)?$/; -var _DATE_PARTS = { // indexes to the sub-expressions of the RegExp above - FullYear: 2, - Month: 4, - Date: 6, - Hours: 8, - Minutes: 10, - Seconds: 12, - Milliseconds: 14 -}; -var _TIMEZONE_PARTS = { // idem, but without the getter/setter usage on Date object - Hectomicroseconds: 15, // :-P - UTC: 16, - Sign: 17, - Hours: 18, - Minutes: 20 -}; - -var _TRIM_ZEROES = /(((00)?:0+)?:0+)?\.0+$/; -var _TRIM_TIMEZONE = /(T[0-9:.]+)$/; - -var Date2 = _createObject2( - Date, - function(yy, mm, dd, h, m, s, ms) { - switch (arguments.length) { - case 0: return new Date; - case 1: return typeof yy == "number" ? new Date(yy) : Date2.parse(yy); - default: return new Date(yy, mm, arguments.length == 2 ? 1 : dd, h || 0, m || 0, s || 0, ms || 0); - } - }, "", { - toISOString: function(date) { - var string = "####-##-##T##:##:##.###"; - for (var part in _DATE_PARTS) { - string = string.replace(/#+/, function(digits) { - var value = date["getUTC" + part](); - if (part == "Month") value++; // js month starts at zero - return ("000" + value).slice(-digits.length); // pad - }); - } - // remove trailing zeroes, and remove UTC timezone, when time's absent - return string.replace(_TRIM_ZEROES, "").replace(_TRIM_TIMEZONE, "$1Z"); - } - } -); - -delete Date2.forEach; - -Date2.now = function() { - return (new Date).valueOf(); // milliseconds since the epoch -}; - -Date2.parse = function(string, defaultDate) { - if (arguments.length > 1) { - assertType(defaultDate, "number", "default date should be of type 'number'.") - } - // parse ISO date - var parts = match(string, _DATE_PATTERN); - if (parts.length) { - if (parts[_DATE_PARTS.Month]) parts[_DATE_PARTS.Month]--; // js months start at zero - // round milliseconds on 3 digits - if (parts[_TIMEZONE_PARTS.Hectomicroseconds] >= 5) parts[_DATE_PARTS.Milliseconds]++; - var date = new Date(defaultDate || 0); - var prefix = parts[_TIMEZONE_PARTS.UTC] || parts[_TIMEZONE_PARTS.Hours] ? "UTC" : ""; - for (var part in _DATE_PARTS) { - var value = parts[_DATE_PARTS[part]]; - if (!value) continue; // empty value - // set a date part - date["set" + prefix + part](value); - // make sure that this setting does not overflow - if (date["get" + prefix + part]() != parts[_DATE_PARTS[part]]) { - return NaN; - } - } - // timezone can be set, without time being available - // without a timezone, local timezone is respected - if (parts[_TIMEZONE_PARTS.Hours]) { - var hours = Number(parts[_TIMEZONE_PARTS.Sign] + parts[_TIMEZONE_PARTS.Hours]); - var minutes = Number(parts[_TIMEZONE_PARTS.Sign] + (parts[_TIMEZONE_PARTS.Minutes] || 0)); - date.setUTCMinutes(date.getUTCMinutes() + (hours * 60) + minutes); - } - return date.valueOf(); - } else { - return Date.parse(string); - } -}; - -// ========================================================================= -// JavaScript/String2.js -// ========================================================================= - -var String2 = _createObject2( - String, - function(string) { - return new String(arguments.length == 0 ? "" : string); - }, - "charAt,charCodeAt,concat,indexOf,lastIndexOf,match,replace,search,slice,split,substr,substring,toLowerCase,toUpperCase", - { - csv: csv, - format: format, - rescape: rescape, - trim: trim - } -); - -delete String2.forEach; - -// http://blog.stevenlevithan.com/archives/faster-trim-javascript -function trim(string) { - return String(string).replace(_LTRIM, "").replace(_RTRIM, ""); -}; - -function csv(string) { - return string ? (string + "").split(/\s*,\s*/) : []; -}; - -function format(string) { - // Replace %n with arguments[n]. - // e.g. format("%1 %2%3 %2a %1%3", "she", "se", "lls"); - // ==> "she sells sea shells" - // Only %1 - %9 supported. - var args = arguments; - var pattern = new RegExp("%([1-" + (arguments.length - 1) + "])", "g"); - return (string + "").replace(pattern, function(match, index) { - return args[index]; - }); -}; - -function match(string, expression) { - // Same as String.match() except that this function will return an empty - // array if there is no match. - return (string + "").match(expression) || []; -}; - -function rescape(string) { - // Make a string safe for creating a RegExp. - return (string + "").replace(_RESCAPE, "\\$1"); -}; - -// ========================================================================= -// JavaScript/Function2.js -// ========================================================================= - -var Function2 = _createObject2( - Function, - Function, - "", { - I: I, - II: II, - K: K, - bind: bind, - compose: compose, - delegate: delegate, - flip: flip, - not: not, - partial: partial, - unbind: unbind - } -); - -function I(i) { // return first argument - return i; -}; - -function II(i, ii) { // return second argument - return ii; -}; - -function K(k) { - return function() { - return k; - }; -}; - -function bind(fn, context) { - var lateBound = typeof fn != "function"; - if (arguments.length > 2) { - var args = _slice.call(arguments, 2); - return function() { - return (lateBound ? context[fn] : fn).apply(context, args.concat.apply(args, arguments)); - }; - } else { // faster if there are no additional arguments - return function() { - return (lateBound ? context[fn] : fn).apply(context, arguments); - }; - } -}; - -function compose() { - var fns = _slice.call(arguments); - return function() { - var i = fns.length, result = fns[--i].apply(this, arguments); - while (i--) result = fns[i].call(this, result); - return result; - }; -}; - -function delegate(fn, context) { - return function() { - var args = _slice.call(arguments); - args.unshift(this); - return fn.apply(context, args); - }; -}; - -function flip(fn) { - return function() { - return fn.apply(this, Array2.swap(arguments, 0, 1)); - }; -}; - -function not(fn) { - return function() { - return !fn.apply(this, arguments); - }; -}; - -function partial(fn) { - var args = _slice.call(arguments, 1); - // based on Oliver Steele's version - return function() { - var specialised = args.concat(), i = 0, j = 0; - while (i < args.length && j < arguments.length) { - if (specialised[i] === undefined) specialised[i] = arguments[j++]; - i++; - } - while (j < arguments.length) { - specialised[i++] = arguments[j++]; - } - if (Array2.contains(specialised, undefined)) { - specialised.unshift(fn); - return partial.apply(null, specialised); - } - return fn.apply(this, specialised); - }; -}; - -function unbind(fn) { - return function(context) { - return fn.apply(context, _slice.call(arguments, 1)); - }; -}; - -// ========================================================================= -// base2/detect.js -// ========================================================================= - -function detect() { - // Two types of detection: - // 1. Object detection - // e.g. detect("(java)"); - // e.g. detect("!(document.addEventListener)"); - // 2. Platform detection (browser sniffing) - // e.g. detect("MSIE"); - // e.g. detect("MSIE|opera"); - - var jscript = NaN/*@cc_on||@_jscript_version@*/; // http://dean.edwards.name/weblog/2007/03/sniff/#comment85164 - var javaEnabled = global.java ? true : false; - if (global.navigator) { // browser - var MSIE = /MSIE[\d.]+/g; - var element = document.createElement("span"); - // Close up the space between name and version number. - // e.g. MSIE 6 -> MSIE6 - var userAgent = navigator.userAgent.replace(/([a-z])[\s\/](\d)/gi, "$1$2"); - // Fix opera's (and others) user agent string. - if (!jscript) userAgent = userAgent.replace(MSIE, ""); - if (MSIE.test(userAgent)) userAgent = userAgent.match(MSIE)[0] + " " + userAgent.replace(MSIE, ""); - base2.userAgent = navigator.platform + " " + userAgent.replace(/like \w+/gi, ""); - javaEnabled &= navigator.javaEnabled(); -//} else if (java) { // rhino -// var System = java.lang.System; -// base2.userAgent = "Rhino " + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version"); -//} else if (jscript) { // Windows Scripting Host -// base2.userAgent = "WSH"; - } - - var _cache = {}; - detect = function(expression) { - if (_cache[expression] == null) { - var returnValue = false, test = expression; - var not = test.charAt(0) == "!"; - if (not) test = test.slice(1); - if (test.charAt(0) == "(") { - try { - returnValue = new Function("element,jscript,java,global", "return !!" + test)(element, jscript, javaEnabled, global); - } catch (ex) { - // the test failed - } - } else { - // Browser sniffing. - returnValue = new RegExp("(" + test + ")", "i").test(base2.userAgent); - } - _cache[expression] = !!(not ^ returnValue); - } - return _cache[expression]; - }; - - return detect(arguments[0]); -}; - -// ========================================================================= -// base2/init.js -// ========================================================================= - -base2 = global.base2 = new Package(this, base2); -var exports = this.exports; - -lang = new Package(this, lang); -exports += this.exports; - -JavaScript = new Package(this, JavaScript); -eval(exports + this.exports); - -lang.base = base; -lang.extend = extend; - -}; //////////////////// END: CLOSURE ///////////////////////////////////// diff --git a/build/packer/packer.js b/build/packer/packer.js deleted file mode 100644 index 7a3bada..0000000 --- a/build/packer/packer.js +++ /dev/null @@ -1,559 +0,0 @@ -/* - Packer version 3.1 - copyright 2004-2008, Dean Edwards - http://www.opensource.org/licenses/mit-license.php -*/ - -// timestamp: Mon, 30 Mar 2009 18:26:18 - -new function() { /////////////// BEGIN: CLOSURE /////////////// - -new base2.Package(this, { - imports: "Function2,Enumerable" -}); - -eval(this.imports); - -var IGNORE = RegGrp.IGNORE; -var KEYS = "~"; -var REMOVE = ""; -var SPACE = " "; - -// ========================================================================= -// packer/Parser.js -// ========================================================================= - -var Parser = RegGrp.extend({ - put: function(expression, replacement) { - if (typeOf(expression) == "string") { - expression = Parser.dictionary.exec(expression); - } - this.base(expression, replacement); - } -}, { - dictionary: new RegGrp({ - OPERATOR: /return|typeof|[\[(\^=,{}:;&|!*?]/.source, - CONDITIONAL: /\/\*@\w*|\w*@\*\/|\/\/@\w*|@\w+/.source, - COMMENT1: /\/\/[^\n]*/.source, - COMMENT2: /\/\*[^*]*\*+([^\/][^*]*\*+)*\//.source, - REGEXP: /\/(\\[\/\\]|[^*\/])(\\.|[^\/\n\\])*\/[gim]*/.source, - STRING1: /'(\\.|[^'\\])*'/.source, - STRING2: /"(\\.|[^"\\])*"/.source - }) -}); - -// ========================================================================= -// packer/Words.js -// ========================================================================= - -var Words = Collection.extend({ - add: function(word) { - if (!this.has(word)) this.base(word); - word = this.get(word); - if (!word.index) { - word.index = this.size(); - } - word.count++; - return word; - }, - - sort: function(sorter) { - return this.base(sorter || function(word1, word2) { - // sort by frequency - return (word2.count - word1.count) || (word1.index - word2.index); - }); - } -}, { - Item: { - constructor: function(word) { - this.toString = K(word); - }, - - index: 0, - count: 0, - encoded: "" - } -}); - -// ========================================================================= -// packer/Encoder.js -// ========================================================================= - -var Encoder = Base.extend({ - constructor: function(pattern, encoder, ignore) { - this.parser = new Parser(ignore); - if (pattern) this.parser.put(pattern, ""); - this.encoder = encoder; - }, - - parser: null, - encoder: Undefined, - - search: function(script) { - var words = new Words; - this.parser.putAt(-1, function(word) { - words.add(word); - }); - this.parser.exec(script); - return words; - }, - - encode: function(script) { - var words = this.search(script); - words.sort(); - var index = 0; - forEach (words, function(word) { - word.encoded = this.encoder(index++); - }, this); - this.parser.putAt(-1, function(word) { - return words.get(word).encoded; - }); - return this.parser.exec(script); - } -}); - -// ========================================================================= -// packer/Privates.js -// ========================================================================= - -var Privates = Encoder.extend({ - constructor: function() { - return this.base(Privates.PATTERN, function(index) { - return "_" + Packer.encode62(index); - }, Privates.IGNORE); - } -}, { - IGNORE: { - CONDITIONAL: IGNORE, - "(OPERATOR)(REGEXP)": IGNORE - }, - - PATTERN: /\b_[\da-zA-Z$][\w$]*\b/g -}); - -// ========================================================================= -// packer/Base62.js -// ========================================================================= - -var Base62 = Encoder.extend({ - encode: function(script) { - var words = this.search(script); - - words.sort(); - - var encoded = new Collection; // a dictionary of base62 -> base10 - var size = words.size(); - for (var i = 0; i < size; i++) { - encoded.put(Packer.encode62(i), i); - } - - function replacement(word) { - return words["#" + word].replacement; - }; - - var empty = K(""); - var index = 0; - forEach (words, function(word) { - if (encoded.has(word)) { - word.index = encoded.get(word); - word.toString = empty; - } else { - while (words.has(Packer.encode62(index))) index++; - word.index = index++; - if (word.count == 1) { - word.toString = empty; - } - } - word.replacement = Packer.encode62(word.index); - if (word.replacement.length == word.toString().length) { - word.toString = empty; - } - }); - - // sort by encoding - words.sort(function(word1, word2) { - return word1.index - word2.index; - }); - - // trim unencoded words - words = words.slice(0, this.getKeyWords(words).split("|").length); - - script = script.replace(this.getPattern(words), replacement); - - /* build the packed script */ - - var p = this.escape(script); - var a = "[]"; - var c = this.getCount(words); - var k = this.getKeyWords(words); - var e = this.getEncoder(words); - var d = this.getDecoder(words); - - // the whole thing - return format(Base62.UNPACK, p,a,c,k,e,d); - }, - - search: function(script) { - var words = new Words; - forEach (script.match(Base62.WORDS), words.add, words); - return words; - }, - - escape: function(script) { - // Single quotes wrap the final string so escape them. - // Also, escape new lines (required by conditional comments). - return script.replace(/([\\'])/g, "\\$1").replace(/[\r\n]+/g, "\\n"); - }, - - getCount: function(words) { - return words.size() || 1; - }, - - getDecoder: function(words) { - // returns a pattern used for fast decoding of the packed script - var trim = new RegGrp({ - "(\\d)(\\|\\d)+\\|(\\d)": "$1-$3", - "([a-z])(\\|[a-z])+\\|([a-z])": "$1-$3", - "([A-Z])(\\|[A-Z])+\\|([A-Z])": "$1-$3", - "\\|": "" - }); - var pattern = trim.exec(words.map(function(word) { - if (word.toString()) return word.replacement; - return ""; - }).slice(0, 62).join("|")); - - if (!pattern) return "^$"; - - pattern = "[" + pattern + "]"; - - var size = words.size(); - if (size > 62) { - pattern = "(" + pattern + "|"; - var c = Packer.encode62(size).charAt(0); - if (c > "9") { - pattern += "[\\\\d"; - if (c >= "a") { - pattern += "a"; - if (c >= "z") { - pattern += "-z"; - if (c >= "A") { - pattern += "A"; - if (c > "A") pattern += "-" + c; - } - } else if (c == "b") { - pattern += "-" + c; - } - } - pattern += "]"; - } else if (c == 9) { - pattern += "\\\\d"; - } else if (c == 2) { - pattern += "[12]"; - } else if (c == 1) { - pattern += "1"; - } else { - pattern += "[1-" + c + "]"; - } - - pattern += "\\\\w)"; - } - return pattern; - }, - - getEncoder: function(words) { - var size = words.size(); - return Base62["ENCODE" + (size > 10 ? size > 36 ? 62 : 36 : 10)]; - }, - - getKeyWords: function(words) { - return words.map(String).join("|").replace(/\|+$/, ""); - }, - - getPattern: function(words) { - words = words.map(String).join("|").replace(/\|{2,}/g, "|").replace(/^\|+|\|+$/g, "") || "\\x0"; - return new RegExp("\\b(" + words + ")\\b", "g"); - } -}, { - WORDS: /\b[\da-zA-Z]\b|\w{2,}/g, - - ENCODE10: "String", - ENCODE36: "function(c){return c.toString(36)}", - ENCODE62: "function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))}", - - UNPACK: "eval(function(p,a,c,k,e,r){e=%5;if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];" + - "k=[function(e){return r[e]||e}];e=function(){return'%6'};c=1};while(c--)if(k[c])p=p." + - "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%1',%2,%3,'%4'.split('|'),0,{}))" -}); - -// ========================================================================= -// packer/Packer.js -// ========================================================================= - -global.Packer = Base.extend({ - constructor: function() { - this.minifier = new Minifier; - this.shrinker = new Shrinker; - this.privates = new Privates; - this.base62 = new Base62; - }, - - minifier: null, - shrinker: null, - privates: null, - base62: null, - - pack: function(script, base62, shrink, privates) { - script = this.minifier.minify(script); - if (shrink) script = this.shrinker.shrink(script); - if (privates) script = this.privates.encode(script); - if (base62) script = this.base62.encode(script); - return script; - } -}, { - version: "3.1", - - init: function() { - eval("var e=this.encode62=" + Base62.ENCODE62); - }, - - data: new Parser({ - "STRING1": IGNORE, - 'STRING2': IGNORE, - "CONDITIONAL": IGNORE, // conditional comments - "(OPERATOR)\\s*(REGEXP)": "$1$2" - }), - - encode52: function(c) { - // Base52 encoding (a-Z) - function encode(c) { - return (c < 52 ? '' : encode(parseInt(c / 52))) + - ((c = c % 52) > 25 ? String.fromCharCode(c + 39) : String.fromCharCode(c + 97)); - }; - var encoded = encode(c); - if (/^(do|if|in)$/.test(encoded)) encoded = encoded.slice(1) + 0; - return encoded; - } -}); - -// ========================================================================= -// packer/Minifier.js -// ========================================================================= - -var Minifier = Base.extend({ - minify: function(script) { - // packing with no additional options - script += "\n"; - script = script.replace(Minifier.CONTINUE, ""); - script = Minifier.comments.exec(script); - script = Minifier.clean.exec(script); - script = Minifier.whitespace.exec(script); - script = Minifier.concat.exec(script); - return script; - } -}, { - CONTINUE: /\\\r?\n/g, - - init: function() { - this.concat = new Parser(this.concat).merge(Packer.data); - extend(this.concat, "exec", function(script) { - var parsed = this.base(script); - while (parsed != script) { - script = parsed; - parsed = this.base(script); - } - return parsed; - }); - forEach.csv("comments,clean,whitespace", function(name) { - this[name] = Packer.data.union(new Parser(this[name])); - }, this); - this.conditionalComments = this.comments.copy(); - this.conditionalComments.putAt(-1, " $3"); - this.whitespace.removeAt(2); // conditional comments - this.comments.removeAt(2); - }, - - clean: { - "\\(\\s*([^;)]*)\\s*;\\s*([^;)]*)\\s*;\\s*([^;)]*)\\)": "($1;$2;$3)", // for (;;) loops - "throw[^};]+[};]": IGNORE, // a safari 1.3 bug - ";+\\s*([};])": "$1" - }, - - comments: { - ";;;[^\\n]*\\n": REMOVE, - "(COMMENT1)\\n\\s*(REGEXP)?": "\n$3", - "(COMMENT2)\\s*(REGEXP)?": function(match, comment, $2, regexp) { - if (/^\/\*@/.test(comment) && /@\*\/$/.test(comment)) { - comment = Minifier.conditionalComments.exec(comment); - } else { - comment = ""; - } - return comment + " " + (regexp || ""); - } - }, - - concat: { - "(STRING1)\\+(STRING1)": function(match, a, $2, b) { - return a.slice(0, -1) + b.slice(1); - }, - "(STRING2)\\+(STRING2)": function(match, a, $2, b) { - return a.slice(0, -1) + b.slice(1); - } - }, - - whitespace: { - "\\/\\/@[^\\n]*\\n": IGNORE, - "@\\s+\\b": "@ ", // protect conditional comments - "\\b\\s+@": " @", - "(\\d)\\s+(\\.\\s*[a-z\\$_\\[(])": "$1 $2", // http://dean.edwards.name/weblog/2007/04/packer3/#comment84066 - "([+-])\\s+([+-])": "$1 $2", // c = a++ +b; - "(\\w)\\s+([\\u0080-\\uffff])": "$1 $2", // http://code.google.com/p/base2/issues/detail?id=78 - "\\b\\s+\\$\\s+\\b": " $ ", // var $ in - "\\$\\s+\\b": "$ ", // object$ in - "\\b\\s+\\$": " $", // return $object -// "\\b\\s+#": " #", // CSS - "\\b\\s+\\b": SPACE, - "\\s+": REMOVE - } -}); - -// ========================================================================= -// packer/Shrinker.js -// ========================================================================= - -var Shrinker = Base.extend({ - decodeData: function(script) { - // put strings and regular expressions back - var data = this._data; // encoded strings and regular expressions - delete this._data; - return script.replace(Shrinker.ENCODED_DATA, function(match, index) { - return data[index]; - }); - }, - - encodeData: function(script) { - // encode strings and regular expressions - var data = this._data = []; // encoded strings and regular expressions - return Packer.data.exec(script, function(match, operator, regexp) { - var replacement = "\x01" + data.length + "\x01"; - if (regexp) { - replacement = operator + replacement; - match = regexp; - } - data.push(match); - return replacement; - }); - }, - - shrink: function(script) { - script = this.encodeData(script); - - // Windows Scripting Host cannot do regexp.test() on global regexps. - function global(regexp) { - // This function creates a global version of the passed regexp. - return new RegExp(regexp.source, "g"); - }; - - // identify blocks, particularly identify function blocks (which define scope) - var BLOCK = /((catch|do|if|while|with|function)\b[^~{};]*(\(\s*[^{};]*\s*\))\s*)?(\{[^{}]*\})/; - var BLOCK_g = global(BLOCK); - var BRACKETS = /\{[^{}]*\}|\[[^\[\]]*\]|\([^\(\)]*\)|~[^~]+~/; - var BRACKETS_g = global(BRACKETS); - var ENCODED_BLOCK = /~#?(\d+)~/; - var IDENTIFIER = /[a-zA-Z_$][\w\$]*/g; - var SCOPED = /~#(\d+)~/; - var VAR_g = /\bvar\b/g; - var VARS = /\bvar\s+[\w$]+[^;#]*|\bfunction\s+[\w$]+/g; - var VAR_TIDY = /\b(var|function)\b|\sin\s+[^;]+/g; - var VAR_EQUAL = /\s*=[^,;]*/g; - - var blocks = []; // store program blocks (anything between braces {}) - var total = 0; - // encoder for program blocks - function encodeBlocks($, prefix, blockType, args, block) { - if (!prefix) prefix = ""; - if (blockType == "function") { - // decode the function block (THIS IS THE IMPORTANT BIT) - // We are retrieving all sub-blocks and will re-parse them in light - // of newly shrunk variables - block = args + decodeBlocks(block, SCOPED); - prefix = prefix.replace(BRACKETS, ""); - - // create the list of variable and argument names - args = args.slice(1, -1); - - if (args != "_no_shrink_") { - var vars = match(block, VARS).join(";").replace(VAR_g, ";var"); - while (BRACKETS.test(vars)) { - vars = vars.replace(BRACKETS_g, ""); - } - vars = vars.replace(VAR_TIDY, "").replace(VAR_EQUAL, ""); - } - block = decodeBlocks(block, ENCODED_BLOCK); - - // process each identifier - if (args != "_no_shrink_") { - var count = 0, shortId; - var ids = match([args, vars], IDENTIFIER); - var processed = {}; - for (var i = 0; i < ids.length; i++) { - id = ids[i]; - if (!processed["#" + id]) { - processed["#" + id] = true; - id = rescape(id); - // encode variable names - while (new RegExp(Shrinker.PREFIX + count + "\\b").test(block)) count++; - var reg = new RegExp("([^\\w$.])" + id + "([^\\w$:])"); - while (reg.test(block)) { - block = block.replace(global(reg), "$1" + Shrinker.PREFIX + count + "$2"); - } - var reg = new RegExp("([^{,\\w$.])" + id + ":", "g"); - block = block.replace(reg, "$1" + Shrinker.PREFIX + count + ":"); - count++; - } - } - total = Math.max(total, count); - } - var replacement = prefix + "~" + blocks.length + "~"; - blocks.push(block); - } else { - var replacement = "~#" + blocks.length + "~"; - blocks.push(prefix + block); - } - return replacement; - }; - - // decoder for program blocks - function decodeBlocks(script, encoded) { - while (encoded.test(script)) { - script = script.replace(global(encoded), function(match, index) { - return blocks[index]; - }); - } - return script; - }; - - // encode blocks, as we encode we replace variable and argument names - while (BLOCK.test(script)) { - script = script.replace(BLOCK_g, encodeBlocks); - } - - // put the blocks back - script = decodeBlocks(script, ENCODED_BLOCK); - - var shortId, count = 0; - var shrunk = new Encoder(Shrinker.SHRUNK, function() { - // find the next free short name - do shortId = Packer.encode52(count++); - while (new RegExp("[^\\w$.]" + shortId + "[^\\w$:]").test(script)); - return shortId; - }); - script = shrunk.encode(script); - - return this.decodeData(script); - } -}, { - ENCODED_DATA: /\x01(\d+)\x01/g, - PREFIX: "\x02", - SHRUNK: /\x02\d+\b/g -}); - -}; //////////////////// END: CLOSURE ///////////////////////////////////// diff --git a/build/scripts/nwjslint.sh b/build/scripts/nwjslint.sh deleted file mode 100755 index 69ebf4b..0000000 --- a/build/scripts/nwjslint.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -echo -ne "NW: running javascript lint on sources" - -# set sources -SOURCES=${1} -if [ "${SOURCES}x" == 'x' ]; then - pushd . &> /dev/null - cd ..; SOURCES=`pwd` - popd &> /dev/null -fi - -# check platform -PLATFORM=`uname -s` - -# set platform specifick executables -if [ $PLATFORM == 'Darwin' ]; then - JSL=bin/jsl_macos -elif [ $PLATFORM == 'Linux' ]; then - JSL=bin/jsl_linux -elif [ $PLATFORM == 'Windows' ]; then - JSL=bin/jsl.exe -fi - -# save current working path -pusdh . &> /dev/null - -# enter root path -cd $SOURCES - -> $SOURCES/dist/lint.log - -for item in `find $SOURCES/src -type d` -do { - sources=`ls $item/*.js 2>/dev/null` - for file in $sources - do { - $JSL -conf build/conf/jsl.conf -nologo -nofilelisting -nosummary -process $file >> $SOURCES/dist/lint.log - echo -ne "." - } done -} done - -echo "" - -popd &> /dev/null - -exit 0 diff --git a/build/scripts/nwpacker.js b/build/scripts/nwpacker.js deleted file mode 100755 index 006849c..0000000 --- a/build/scripts/nwpacker.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SpiderMonkey wrapper to compress Javascript source files, - * uses Dean Edwards (http://dean.edwards.name/packer/) - * - * Author: Diego Perini - * - * Usage: fusecomp.js < input > output (uses stdin/stdout) - * - * Depends on correctly installed SpiderMonkey executable (js) - * - * Released under the Creative Commons license: - * http://creativecommons.org/licenses/by/3.0/ - */ - -// import Dean Edwards project files -load("build/packer/base2.js"); -load("build/packer/packer.js"); - -// read file from stdin -function _readFile(from) { - var outstr = '', line = ''; - while ((line = readline()) != null) { - outstr += line + '\x0a'; - } - return outstr; -} - -// read from input pack and send to output -(function minify() { - // create new packer instance - var packer = new Packer(); - // shrink! - print(packer.pack(_readFile(), 0, 1, 1)); -})(); diff --git a/build/scripts/nwpackjs.sh b/build/scripts/nwpackjs.sh deleted file mode 100755 index 83e6c6e..0000000 --- a/build/scripts/nwpackjs.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -echo -ne "NW: building distribution from sources" - -# retrieve build infos -VERSION=`cat ${SOURCES}build/VERSION` -REVISION=1 - -# set used variables -BASEDIR=`pwd` -PKGNAME="nwsapi" -RELEASE=`date +%Y%m%d%H%M%S` - -# set sources -SOURCES=${1} -if [ "${SOURCES}x" == 'x' ]; then - pushd . &> /dev/null - cd ..; SOURCES=`pwd` - popd &> /dev/null -fi - -# check working platform -PLATFORM=`uname -s` - -# set platform specifick executables -if [ $PLATFORM == 'Darwin' ]; then - JSMIN=bin/jsmin_macos - JSVM=bin/js_macos -elif [ $PLATFORM == 'Linux' ]; then - JSMIN=bin/jsmin_linux - JSVM=bin/js_linux -elif [ $PLATFORM == 'Windows' ]; then - JSMIN=bin/jsmin.exe - JSVM=bin/js.exe -fi - -pushd . &> /dev/null - -cd $SOURCES - -# ensure empty -#> dist/$PKGNAME-src.js -> dist/$PKGNAME.min.js - -cat build/HEADER >> dist/$PKGNAME.min.js - -# add selector engine to source file -#cat src/nwsapi.js >> dist/$PKGNAME-src.js - -# add selector engine to minified file -cat src/nwsapi.js | $JSMIN | tr -d "\n" >> dist/$PKGNAME.min.js -echo >> dist/$PKGNAME.min.js - -# minification of variables and privates -echo "" -echo -ne "NW: starting minification, takes time please wait, " -#$JSVM build/scripts/nwpacker.js < dist/nwsapi-src.js >> dist/nwsapi-pac.js -echo -ne "complete..." -echo "" - -# build a compressed version of the minified file -#gzip -c -n9 dist/$PKGNAME-pac.js > dist/$PKGNAME-zip.js - -# build a versioned file name of the minified file -#cp dist/$PKGNAME-pac.js dist/$PKGNAME-$RELEASE.js - -# now copy packed file to the real nwsapi.js -#cp dist/nwsapi-pac.js dist/nwsapi.js - -popd &> /dev/null - -exit 0 diff --git a/dist/lint.log b/dist/lint.log deleted file mode 100644 index e69de29..0000000 diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..0efe697 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,33 @@ +import { defineConfig } from "eslint/config"; +import js from '@eslint/js'; +import globals from 'globals'; + +export default defineConfig([ + js.configs.recommended, + { + files: ['src/**/*.js', 'test/**/*.js'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'script', + globals: { + ...globals.browser, + ...globals.commonjs, + ...globals.amd, + NW: 'readonly', + }, + }, + rules: { + 'no-console': 'off', + 'no-unused-vars': 'off', + 'no-cond-assign': 'off', + 'no-control-regex': 'off', + 'no-useless-escape': 'off', + 'no-redeclare': 'off', + + 'default-case': 'error', + 'no-duplicate-case': 'error', + 'radix': 'error', + 'no-with': 'error', + }, + }, +]); diff --git a/package.json b/package.json index cf814d2..58e66f0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "nwsapi", "version": "2.2.23", "description": "Fast CSS Selectors API Engine", - "homepage": "https://javascript.nwbox.com/nwsapi/", + "homepage": "https://github.com/dperini/nwsapi/", "main": "./src/nwsapi", "keywords": [ "css", @@ -11,12 +11,6 @@ "matcher", "selector" ], - "licenses": [ - { - "type": "MIT", - "url": "https://javascript.nwbox.com/nwsapi/MIT-LICENSE" - } - ], "license": "MIT", "author": { "name": "Diego Perini", @@ -38,6 +32,12 @@ "url": "git://github.com/dperini/nwsapi.git" }, "scripts": { - "lint": "eslint ./src/nwsapi.js" + "clean": "node ./scripts/clean.mjs", + "min": "node ./scripts/min.mjs", + "lint": "npx eslint src/nwsapi.js" + }, + "devDependencies": { + "eslint": "9.31.0", + "terser": "5.46.1" } } diff --git a/scripts/clean.mjs b/scripts/clean.mjs new file mode 100644 index 0000000..e0387f6 --- /dev/null +++ b/scripts/clean.mjs @@ -0,0 +1,4 @@ +import { rm } from 'node:fs/promises'; + +await rm('./dist', { recursive: true, force: true }); +console.log('removed ./dist'); diff --git a/scripts/min.mjs b/scripts/min.mjs new file mode 100644 index 0000000..cd87e91 --- /dev/null +++ b/scripts/min.mjs @@ -0,0 +1,31 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { minify } from 'terser'; + +const pkg = JSON.parse(await readFile('./package.json', 'utf8')); +const source = await readFile('./src/nwsapi.js', 'utf8'); + +const year = new Date().getFullYear(); +const banner = [ + '/*!', + ` * NWSAPI ${pkg.version} - ${pkg.description}`, + ` * Copyright (c) 2007-${year} Diego Perini`, + ' * See https://github.com/dperini/nwsapi', + ' */', +].join('\n'); + +const result = await minify(source, { + compress: true, + mangle: true, + format: { + comments: false, + }, +}); + +if (!result.code) { + throw new Error('terser produced no output'); +} + +await mkdir('./dist', { recursive: true }); +await writeFile('./dist/nwsapi.min.js', `${banner}\n${result.code}\n`, 'utf8'); + +console.log('wrote ./dist/nwsapi.min.js'); diff --git a/src/nwsapi.js b/src/nwsapi.js index 82f9393..84909c9 100644 --- a/src/nwsapi.js +++ b/src/nwsapi.js @@ -486,7 +486,7 @@ } else nodes = none; } return !Config.NODE_LIST ? - nodes : isInstanceof(nodes) ? + nodes : isInstanceOf(nodes) ? nodes : toNodeList(nodes); }, From 8515497709b58088b43b6e8e257f46973d7cc8c9 Mon Sep 17 00:00:00 2001 From: koal44 Date: Mon, 6 Apr 2026 03:22:23 -0700 Subject: [PATCH 02/19] add Playwright harness and migrate css3-compat tests --- .gitignore | 2 + eslint.config.mjs | 3 +- package.json | 6 +- playwright.config.ts | 7 + test_new/browser/css3-compat.test.ts | 605 +++++++++++++++++++++++++++ test_new/browser/harness.ts | 233 +++++++++++ test_new/global.d.ts | 9 + test_new/tsconfig.json | 12 + 8 files changed, 875 insertions(+), 2 deletions(-) create mode 100644 playwright.config.ts create mode 100644 test_new/browser/css3-compat.test.ts create mode 100644 test_new/browser/harness.ts create mode 100644 test_new/global.d.ts create mode 100644 test_new/tsconfig.json diff --git a/.gitignore b/.gitignore index 504afef..585a5f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules/ package-lock.json +test-results/ +.last-run.json diff --git a/eslint.config.mjs b/eslint.config.mjs index 0efe697..f0bc75b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,7 +5,7 @@ import globals from 'globals'; export default defineConfig([ js.configs.recommended, { - files: ['src/**/*.js', 'test/**/*.js'], + files: ['src/**/*.js', 'scripts/**/*.js', 'test/**/*.js'], languageOptions: { ecmaVersion: 'latest', sourceType: 'script', @@ -23,6 +23,7 @@ export default defineConfig([ 'no-control-regex': 'off', 'no-useless-escape': 'off', 'no-redeclare': 'off', + 'no-empty': 'off', 'default-case': 'error', 'no-duplicate-case': 'error', diff --git a/package.json b/package.json index 58e66f0..6863226 100644 --- a/package.json +++ b/package.json @@ -34,10 +34,14 @@ "scripts": { "clean": "node ./scripts/clean.mjs", "min": "node ./scripts/min.mjs", - "lint": "npx eslint src/nwsapi.js" + "lint": "npx eslint src/nwsapi.js", + "test:browser": "npx playwright test" }, "devDependencies": { + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", "eslint": "9.31.0", + "playwright": "^1.59.1", "terser": "5.46.1" } } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..c74adf5 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: 'test_new/browser', + testMatch: /.*\.test\.ts/, + reporter: 'list', +}); diff --git a/test_new/browser/css3-compat.test.ts b/test_new/browser/css3-compat.test.ts new file mode 100644 index 0000000..453549a --- /dev/null +++ b/test_new/browser/css3-compat.test.ts @@ -0,0 +1,605 @@ +import { runScenarios, type SelectorScenario } from "./harness"; + +const scenarios: SelectorScenario[] = +[ + // { + // name: 'issue 160 adjacent-descendant regression', + // html: ` + // + // `, + // tests: [ + // { selector: '.neighbor + div .target', expect: { count: 1 } }, + // { selector: '.neighbor + * .target', expect: { count: 1 } }, + // ], + // }, + { + name: 'test 0 basic selectors', + html: ` +
+

CSS 3 Selectors tests

+

Original CSS work by Daniel Glazman (c) Disruptive Innovations 2008

+

Testing code written by Diego Perini. It should help improve the consistency with querySelectorAll results and match browsers internal CSS selectors behavior. Selection method calls are wrapped in a try/catch block for all libs to avoid possible errors preventing the tests to complete. Moving the mouse over the red/lime blocks will show the tooltips with info on the tests.

+
+ +
+
+
+ +
+
+
+ + `, + setupPage: async (page) => { await page.evaluate(() => { location.hash = 'target'; });}, + cases: [ + /* element type selector */ + { selector: 'body', expect: { count: 1 } }, + { selector: 'div', expect: { count: 6 } }, + { selector: 'div.header', expect: { count: 1 } }, + { selector: 'div.footer', expect: { count: 1 } }, + { selector: 'h3, h4, p, ul', expect: { count: 5 } }, + + /* class selector */ + { selector: '.unitTest', expect: { count: 2 } }, + { selector: '.test', expect: { count: 2 } }, + + /* group of selectors */ + { selector: '.unitTest, .test', expect: { count: 4 } }, + + /* :target selector */ + { selector: '.target :target', expect: { count: 1 } }, + ], + }, + { + name: 'test 1 childhood selector', + htmlMode: 'document', + html: ` + + + +
+
+
+ + + `, + cases: [ + /* test 1 : childhood selector */ + { selector: 'html > body', expect: { count: 1 } }, + { selector: '.test > .blox1', expect: { count: 1 } }, + ], + }, + { + name: 'test 2 attribute existence selector', + html: ` +
+
+
+
+
+
+ `, + cases: [ + /* attribute with a value */ + { selector: '.blox2[align]', expect: { count: 1 } }, + + /* attribute with empty value */ + { selector: '.blox3[align]', expect: { count: 1 } }, + + /* attribute with almost similar name */ + { selector: '.blox4, .blox5', expect: { count: 2 } }, + { selector: '.blox4[align], .blox5[align]', expect: { count: 0 } }, + ], + }, + { + name: 'test 3 attribute value selector', + html: ` +
+
+
+
+
+ `, + cases: [ + /* test 3 : attribute value selector */ + { selector: '.blox6[align="center"]', expect: { count: 1 } }, + { selector: '.blox6[align="c"]', expect: { count: 0 } }, + { selector: '.blox6[align="centera"]', expect: { count: 0 } }, + { selector: '.blox6[foo="\\e9"]', expect: { count: 1 } }, + { selector: '.blox6[\\_foo="\\e9"]', expect: { count: 1 } }, + ], + }, + { + name: 'test 4 space-separated attribute selector', + html: ` +
+
+
+
+
+
+ `, + cases: [ + /* test 4 : [~=] */ + { selector: '.blox7[class~="foo"]', expect: { count: 1 } }, + { selector: '.blox8, .blox9, .blox10', expect: { count: 3 } }, + { selector: '.blox8[class~=""]', expect: { count: 0 } }, + { selector: '.blox9[foo~=""]', expect: { count: 0 } }, + { selector: '.blox10[foo~="foo"]', expect: { count: 0 } }, + ], + }, + { + name: 'test 5 attribute starts-with selector', + html: ` +
+
+
+
+
+
+ `, + cases: [ + /* test5 [^=] */ + { selector: '.attrStart > .t3', expect: { count: 1 } }, + { selector: '.attrStart > .t1[class^="unit"]', expect: { count: 1 } }, + { selector: '.attrStart > .t2', expect: { count: 1 } }, + { selector: '.attrStart > .t2[class^="nit"]', expect: { count: 0 } }, + { selector: '.attrStart > .t3[align^=""]', expect: { count: 0 } }, + { selector: '.attrStart > .t4[foo^="\\e9"]', expect: { count: 1 } }, + ], + }, + { + name: 'test 6 attribute ends-with selector', + html: ` +
+
+
+
+
+
+ `, + cases: [ + /* test6 [$=] */ + { selector: '.attrEnd > .t3', expect: { count: 1 } }, + { selector: '.attrEnd > .t1[class$="t1"]', expect: { count: 1 } }, + { selector: '.attrEnd > .t2', expect: { count: 1 } }, + { selector: '.attrEnd > .t2[class$="unit"]', expect: { count: 0 } }, + { selector: '.attrEnd > .t3[align$=""]', expect: { count: 0 } }, + { selector: '.attrEnd > .t4[foo$="\\e9"]', expect: { count: 1 } }, + ], + }, + { + name: 'test 7 attribute contains selector', + html: ` +
+
+
+
+
+
+ `, + cases: [ + /* test7 [*=] */ + { selector: '.attrMiddle > .t3', expect: { count: 1 } }, + { selector: '.attrMiddle > .t1[class*="t t"]', expect: { count: 1 } }, + { selector: '.attrMiddle > .t2', expect: { count: 1 } }, + { selector: '.attrMiddle > .t2[class*="a"]', expect: { count: 0 } }, + { selector: '.attrMiddle > .t3[align*=""]', expect: { count: 0 } }, + { selector: '.attrMiddle > .t4[foo*="\\e9"]', expect: { count: 1 } }, + ], + }, + { + name: 'first-child selector', + html: ` +
+
+
+
+
+ `, + cases: [ + /* :first-child tests */ + { selector: '.firstChild .unitTest:first-child', expect: { count: 1 } }, + { selector: '.blox12:first-child', expect: { count: 0 } }, + { selector: '.blox13:first-child', expect: { count: 0 } }, + { selector: '.blox12, .blox13', expect: { count: 2 } }, + ], + }, + { + name: ':nth-child(n) and :nth-of-type tests', + html: ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+

+

+
+

+
+
+
+
+
+

+

+
+

+
+
+
+
+
+

+

+
+

+
+
+

+
+
+
+ `, + cases: [ + /* :nth-child(n) tests */ + { selector: '.nthchild1 > :nth-last-child(odd)', expect: { count: 3 } }, + { selector: '.nthchild1 > :nth-child(odd)', expect: { count: 3 } }, + + { selector: '.nthchild2 > :nth-last-child(even)', expect: { count: 3 } }, + { selector: '.nthchild2 > :nth-child(even)', expect: { count: 3 } }, + + { selector: '.nthchild3 > :nth-child(3n+2)', expect: { count: 2 } }, + { selector: '.nthchild3 > :nth-last-child(3n+1)', expect: { count: 2 } }, + { selector: '.nthchild3 > :nth-last-child(3n+3)', expect: { count: 2 } }, + + { selector: '.nthoftype1 > div:nth-of-type(odd)', expect: { count: 2 } }, + { selector: '.nthoftype1 > div:nth-last-of-type(odd)', expect: { count: 2 } }, + { selector: '.nthoftype1 > p', expect: { count: 3 } }, + + { selector: '.nthoftype2 > div:nth-of-type(even)', expect: { count: 2 } }, + { selector: '.nthoftype2 > div:nth-last-of-type(even)', expect: { count: 2 } }, + { selector: '.nthoftype2 > p', expect: { count: 3 } }, + + { selector: '.nthoftype3 > div:nth-of-type(3n+1)', expect: { count: 2 } }, + { selector: '.nthoftype3 > div:nth-last-of-type(3n+1)', expect: { count: 2 } }, + { selector: '.nthoftype3 > div:nth-last-of-type(3n+2)', expect: { count: 2 } }, + { selector: '.nthoftype3 > p', expect: { count: 4 } }, + ], + }, + { + name: 'not pseudo-class selector', + html: ` +
+
+
+
+
+ `, + cases: [ + /* :not() tests */ + { selector: '.blox14:not(span)', expect: { count: 1 } }, + { selector: '.blox15:not([foo="blox14"])', expect: { count: 1 } }, + { selector: '.blox16', expect: { count: 1 } }, + { selector: '.blox16:not(.blox15)', expect: { count: 1 } }, + ], + }, + { + name: ':only-of-type tests', + html: ` +
+
+

+

+
+ `, + cases: [ + /* :only-of-type tests */ + { selector: '.blox17', expect: { count: 1 } }, + { selector: '.blox17:only-of-type', expect: { count: 1 } }, + { selector: '.blox18:only-of-type', expect: { count: 0 } }, + { selector: '.blox18:not(:only-of-type)', expect: { count: 2 } }, + ], + }, + { + name: ':last-child tests', + html: ` +
+

+
  +
+ `, + cases: [ + /* :last-child tests */ + { selector: '.lastChild > p', expect: { count: 1 } }, + { selector: '.lastChild > :last-child', expect: { count: 1 } }, + { selector: '.lastChild > :not(:last-child)', expect: { count: 1 } }, + ], + }, + { + name: ':first-of-type tests', + html: ` +
+

+
+

+
+
+ `, + cases: [ + /* :first-of-type tests */ + { selector: '.firstOfType > p', expect: { count: 2 } }, + { selector: '.firstOfType > *:first-of-type', expect: { count: 2 } }, + { selector: '*.firstOfType > :not(:first-of-type)', expect: { count: 2 } }, + ], + }, + + + { + name: ':last-of-type tests', + html: ` +
+

+
+

+
+
+ `, + cases: [ + /* :last-of-type tests */ + { selector: '.lastOfType > p', expect: { count: 2 } }, + { selector: '.lastOfType > *:last-of-type', expect: { count: 2 } }, + { selector: '*.lastOfType > :not(:last-of-type)', expect: { count: 2 } }, + ], + }, + { + name: ':only-child tests', + html: ` +
+
+
+
+
+
+ `, + cases: [ + /* :only-child tests */ + { selector: '.onlyChild > *:not(:only-child)', expect: { count: 2 } }, + { selector: '.onlyChild > .unitTest > *:only-child', expect: { count: 1 } }, + ], + }, + { + name: ':only-of-type tests 2', + html: ` +
+

+
+
+
+
+
+ `, + cases: [ + /* :only-of-type tests */ + { selector: '.onlyOfType *:only-of-type', expect: { count: 2 } }, + { selector: '.onlyOfType *:not(:only-of-type)', expect: { count: 2 } }, + ], + }, + + { + name: ':empty tests', + html: ` +
+
+
+
+
+
 
+
+ `, + cases: [ + /* :empty tests */ + { selector: '.empty > .isEmpty', expect: { count: 2 } }, + { selector: '.empty > *.isEmpty:empty', expect: { count: 2 } }, + { selector: '.empty > .isNotEmpty', expect: { count: 3 } }, + { selector: '.empty > .isNotEmpty:empty', expect: { count: 0 } }, + { selector: '.empty > .isNotEmpty:not(:empty)', expect: { count: 3 } }, + ], + }, + { + name: ':lang() tests', + html: ` +
+
+
+
+
+
+ `, + setupPage: async (page) => { + await page.evaluate(() => { + document.documentElement.lang = 'en'; + }); + }, + cases: [ + /* :lang() tests */ + { selector: '.lang :lang(en)', expect: { count: 2 } }, + { selector: '.lang :lang(fr)', expect: { count: 1 } }, + { selector: '.lang .t1', expect: { count: 1 } }, + { selector: '.lang .t1:lang(es)', expect: { count: 1 } }, + { selector: '.lang :lang(es-AR)', expect: { count: 0 } }, + ], + }, + { + name: '[|=] tests', + html: ` +
+
+
+
+
+
+ `, + setupPage: async (page) => { + await page.evaluate(() => { + document.documentElement.lang = 'en'; + }); + }, + cases: [ + /* [|=] tests */ + { selector: '.attrLang .t1', expect: { count: 1 } }, + { selector: '.attrLang .t1[lang|="en"]', expect: { count: 0 } }, + { selector: '.attrLang [lang|="fr"]', expect: { count: 1 } }, + { selector: '.attrLang .t2[lang|="en"]', expect: { count: 1 } }, + { selector: '.attrLang .t3', expect: { count: 1 } }, + { selector: '.attrLang .t3[lang|="es"]', expect: { count: 1 } }, + { selector: '.attrLang [lang|="es-AR"]', expect: { count: 0 } }, + ], + }, + + { + name: 'UI tests', + html: ` +
+ + +
+
+
+ the previous square should be green when the checkbox is checked and become red when you uncheck it +
+
+
+ the previous square should be green when the checkbox is NOT checked and become red when you check it +
+ `, + cases: [ + /* UI tests */ + { selector: '.UI .t1:enabled > .unitTest', expect: { count: 1 } }, + { selector: '.UI .t2:disabled > .unitTest', expect: { count: 1 } }, + { selector: '.UI .t3:checked + div', expect: { count: 1 } }, + { selector: '.UI .t4:not(:checked) + div', expect: { count: 1 } }, + ], + }, + { + name: '~ combinator tests', + html: ` +
+
+
+
+
+ the three last squares should be green and become red when the pointer hovers over the white square +
+ `, + cases: [ + /* ~ combinator tests */ + { selector: '.tilda .t1', expect: { count: 1 } }, + { selector: '.tilda .t1 ~ .unitTest', expect: { count: 3 } }, + ], + }, + { + name: '+ combinator tests', + html: ` +
+
+
+
+ the last square should be green and become red when the pointer hovers over the FIRST white square +
+ `, + cases: [ + /* + combinator tests */ + { selector: '.plus .t1, .plus .t2', expect: { count: 2 } }, + { selector: '.plus .t1 + .unitTest + .unitTest', expect: { count: 1 } }, + ], + }, + { + name: 'attribute case sensitivity identifier tests', + html: ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `, + cases: [ + { selector: '.blox23s1[foo="blox" i]', expect: { count: 1 } }, + { selector: '.blox23s2[foo="blox" i]', expect: { count: 1 } }, + { selector: '.blox23s3[foo="blox" i]', expect: { count: 1 } }, + { selector: '.blox23s4[foo="blox" i]', expect: { count: 1 } }, + { selector: '.blox23s5[foo="blox" i]', expect: { count: 1 } }, + { selector: '.blox23s6[foo="blox" i]', expect: { count: 1 } }, + { selector: '.blox23s1[foo="blox" erroneous]', expect: { throws: true } }, + { selector: '.blox19[class="BLOX19 UNITTEST" i]', expect: { count: 1 } }, + { selector: '.blox20[class="BLOX20 UNITTEST" i]', expect: { count: 1 } }, + { selector: '.blox20[class="blox20 unitTest" s]', expect: { throws: true } }, + { selector: '.blox21[class*="21 UN" i]', expect: { count: 1 } }, + { selector: '.blox22[class*="22 unitt" s]', expect: { throws: true } }, + { selector: '.blox22[class*="22 unitT" s]', expect: { throws: true } }, + { selector: '.blox24[class^="BLOX" i]', expect: { count: 1 } }, + { selector: '.blox25[class^="BLOX"]', expect: { count: 0 } }, + { selector: '.blox25[class^="blox" s]', expect: { throws: true } }, + { selector: '.blox26[class$="tEST" i]', expect: { count: 1 } }, + { selector: '.blox27[class$="TEst" s]', expect: { throws: true } }, + { selector: '.blox27[class$="Test" s]', expect: { throws: true } }, + { selector: '.blox28[class~="unitTEST" i]', expect: { count: 1 } }, + ], + }, +]; + +runScenarios('css3 compat', scenarios); diff --git a/test_new/browser/harness.ts b/test_new/browser/harness.ts new file mode 100644 index 0000000..d7d1424 --- /dev/null +++ b/test_new/browser/harness.ts @@ -0,0 +1,233 @@ +import test, { chromium, expect, firefox, webkit } from '@playwright/test'; +import type { Browser, Page } from '@playwright/test'; + +export type SelectorScenario = { + name: string; + html: string; + htmlMode?: 'body' | 'document'; + browsers?: BrowserName[]; + cases: SelectorCase[]; + setupPage?: (page: Page) => void | Promise; +}; + +const browserNames = ['chromium', 'firefox', 'webkit'] as const; +type BrowserName = typeof browserNames[number]; + +type SelectorCase = { + selector: string; + root?: SelectorRoot; + expect?: SelectorExpectation; +}; + +type SelectorExpectation = { + exact?: boolean; + count?: number; + ids?: string[]; + includesIds?: string[]; + excludesIds?: string[]; + throws?: boolean; +}; + +type SelectorResult = { + exactMatch: boolean; + mismatchMsg: string; + native: { + count: number; + ids: string[]; + threw: boolean; + }; + nw: { + count: number; + ids: string[]; + threw: boolean; + }; +}; + +type SelectorRoot = + | { kind: 'document' } + | { kind: 'id'; value: string } + | { kind: 'selector'; value: string }; + +export function runScenarios(label: string, scenarios: SelectorScenario[]): void { + test.describe(label, () => { + let browsers: Record; + let pages: Record; + + test.beforeAll(async () => { + browsers = { + chromium: await chromium.launch(), + firefox: await firefox.launch(), + webkit: await webkit.launch(), + }; + + pages = { + chromium: await browsers.chromium.newPage(), + firefox: await browsers.firefox.newPage(), + webkit: await browsers.webkit.newPage(), + }; + + for (const page of Object.values(pages)) { + await page.setContent(''); + await page.addScriptTag({ path: 'src/nwsapi.js' }); + } + }); + + test.afterAll(async () => { + await Promise.all(browserNames.map((name) => browsers[name].close())); + }); + + for (const s of scenarios) { + test(s.name, async () => { + await runScenario(s, pages); + }); + } + }); +} + +async function runScenario(s: SelectorScenario, pages: Record): Promise { + const scenarioBrowsers = s.browsers ?? browserNames; + + for (const browserName of scenarioBrowsers) { + const page = pages[browserName]; + await setupPage(page, s); + + for (const c of s.cases) { + const result = await evalSelector(page, c); + const exp = c.expect ?? { exact: true }; + const msg = `[${browserName}] ${s.name} :: ${c.selector}\n${result.mismatchMsg}`; + + if (exp.throws) { + expect(result.nw.threw, msg).toBe(true); + continue; + } else { + expect(result.nw.threw, msg).toBe(false); + } + + const skipNative = result.native.threw; + + if (exp.count !== undefined) { + if (!skipNative) expect(result.native.count, msg).toBe(exp.count); + expect(result.nw.count, msg).toBe(exp.count); + } + + if (exp.ids) { + if (!skipNative) expect(result.native.ids, msg).toEqual(exp.ids); + expect(result.nw.ids, msg).toEqual(exp.ids); + } + + if (exp.includesIds) { + for (const id of exp.includesIds) { + if (!skipNative) expect(result.native.ids, msg).toContain(id); + expect(result.nw.ids, msg).toContain(id); + } + } + + if (exp.excludesIds) { + for (const id of exp.excludesIds) { + if (!skipNative) expect(result.native.ids, msg).not.toContain(id); + expect(result.nw.ids, msg).not.toContain(id); + } + } + + if ((exp.exact ?? true) && !skipNative) { + expect(result.exactMatch, msg).toBe(true); + } + } + } +} + +async function setupPage(page: Page, scenario: SelectorScenario): Promise { + if (scenario.htmlMode === 'document') { + await page.setContent(scenario.html); + await page.addScriptTag({ path: 'src/nwsapi.js' }); + if (scenario.setupPage) await scenario.setupPage(page); + return; + } + + await page.evaluate((bodyHtml) => { + document.body.innerHTML = bodyHtml; + }, scenario.html); + if (scenario.setupPage) await scenario.setupPage(page); +} + +async function evalSelector(page: Page, selCase: SelectorCase): Promise { + return await page.evaluate((t) => { + const root = + t.root?.kind === 'document' || !t.root ? document + : t.root.kind === 'id' ? document.getElementById(t.root.value) + : document.querySelector(t.root.value); + + if (!root) { + return { + exactMatch: false, + mismatchMsg: `Root element not found for selector: ${JSON.stringify(t.root)}`, + native: { count: 0, ids: [], threw: false }, + nw: { count: 0, ids: [], threw: false }, + }; + } + + let native: Element[] = []; + let nw: Element[] = []; + let nativeError = ''; + let nwError = ''; + + try { native = [...root.querySelectorAll(t.selector)]; } + catch (e) { nativeError = e instanceof Error ? e.message : String(e); } + + try { nw = NW.Dom.select(t.selector, root); } + catch (e) { nwError = e instanceof Error ? e.message : String(e); } + + if (nwError) { + return { + exactMatch: false, + mismatchMsg: nativeError && nwError + ? `Both threw:\n native error: ${nativeError}\n nw error: ${nwError}` + : `NW threw an error while native did not: ${nwError}`, + native: { count: 0, ids: [], threw: !!nativeError }, + nw: { count: 0, ids: [], threw: !!nwError }, + }; + } + + const nativeIds = native.map((el) => el.id); + const nwIds = nw.map((el: Element) => el.id); + + const describe = (el: Element | undefined) => { + if (!el) return '(missing)'; + return { + tag: el.tagName.toLowerCase(), + id: el.id || null, + className: el.className || '', + html: el.outerHTML.replace(/\s+/g, ' ').slice(0, 120), + }; + }; + + let exactMatch = true; + let mismatchMsg = ''; + + if (native.length !== nw.length) { + exactMatch = false; + mismatchMsg = + `count mismatch: native=${native.length}, nw=${nw.length}\n` + + `first native=${JSON.stringify(describe(native[0]))}\n` + + `first nw=${JSON.stringify(describe(nw[0]))}`; + } else { + for (let i = 0; i < native.length; ++i) { + if (native[i] !== nw[i]) { + exactMatch = false; + mismatchMsg = + `element mismatch at index ${i}\n` + + `native=${JSON.stringify(describe(native[i]))}\n` + + `nw=${JSON.stringify(describe(nw[i]))}`; + break; + } + } + } + + return { + exactMatch, + mismatchMsg, + native: { count: native.length, ids: nativeIds, threw: !!nativeError }, + nw: { count: nw.length, ids: nwIds, threw: !!nwError }, + }; + }, selCase); +} diff --git a/test_new/global.d.ts b/test_new/global.d.ts new file mode 100644 index 0000000..360338b --- /dev/null +++ b/test_new/global.d.ts @@ -0,0 +1,9 @@ +export {}; + +declare global { + const NW: { + Dom: { + select(selector: string, root: Document | Element): Element[]; + }; + } +} \ No newline at end of file diff --git a/test_new/tsconfig.json b/test_new/tsconfig.json new file mode 100644 index 0000000..7371cd6 --- /dev/null +++ b/test_new/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "nodenext", + "moduleResolution": "nodenext", + "strict": true, + "noEmit": true, + "lib": ["esnext", "DOM"], + "types": ["node"] + }, + "include": ["./**/*.ts"] +} \ No newline at end of file From bafeef94f258411d915724d7d6e2c0a14f43ef98 Mon Sep 17 00:00:00 2001 From: koal44 Date: Mon, 6 Apr 2026 17:00:21 -0700 Subject: [PATCH 03/19] migrate css3-escape tests into the playwright harness --- test_new/browser/css3-compat.test.ts | 46 +-- test_new/browser/css3-escape.test.ts | 412 +++++++++++++++++++++++++++ test_new/browser/harness.ts | 114 ++++++-- test_new/browser/issues.test.ts | 22 ++ test_new/global.d.ts | 1 + 5 files changed, 545 insertions(+), 50 deletions(-) create mode 100644 test_new/browser/css3-escape.test.ts create mode 100644 test_new/browser/issues.test.ts diff --git a/test_new/browser/css3-compat.test.ts b/test_new/browser/css3-compat.test.ts index 453549a..985517d 100644 --- a/test_new/browser/css3-compat.test.ts +++ b/test_new/browser/css3-compat.test.ts @@ -1,24 +1,6 @@ -import { runScenarios, type SelectorScenario } from "./harness"; +import { runScenarios } from "./harness"; -const scenarios: SelectorScenario[] = -[ - // { - // name: 'issue 160 adjacent-descendant regression', - // html: ` - //
- //
- //
- // - // - // - //
- //
- // `, - // tests: [ - // { selector: '.neighbor + div .target', expect: { count: 1 } }, - // { selector: '.neighbor + * .target', expect: { count: 1 } }, - // ], - // }, +runScenarios('css3 compat', 'normal', [ { name: 'test 0 basic selectors', html: ` @@ -600,6 +582,24 @@ const scenarios: SelectorScenario[] = { selector: '.blox28[class~="unitTEST" i]', expect: { count: 1 } }, ], }, -]; - -runScenarios('css3 compat', scenarios); + { + name: 'attribute s-flag divergence', + modifier: 'fail', + html: ` +
+
+
+
+
+
+ `, + cases: [ + { selector: '.blox20[class="blox20 unitTest" s]' }, + { selector: '.blox22[class*="22 unitt" s]' }, + { selector: '.blox22[class*="22 unitT" s]' }, + { selector: '.blox25[class^="blox" s]' }, + { selector: '.blox27[class$="TEst" s]' }, + { selector: '.blox27[class$="Test" s]' }, + ], + }, +]); diff --git a/test_new/browser/css3-escape.test.ts b/test_new/browser/css3-escape.test.ts new file mode 100644 index 0000000..c2e291b --- /dev/null +++ b/test_new/browser/css3-escape.test.ts @@ -0,0 +1,412 @@ +import { type Page } from "@playwright/test"; +import { runScenarios } from "./harness"; + +const setupNw = async (page: Page) => { + await page.evaluate(() => { + NW.Dom.configure({ + SELECTOR3: true, + NON_ASCII: true, + UNICODE16: true, + ESCAPECHR: true, + VERBOSITY: false, + }); + }); +} + +runScenarios('css3 escaped identifiers', 'normal', [ + { + name: 'non-escaped identifier', + html: `
`, + setupPage: setupNw, + cases: [ + // 4.3.7 from https://www.w3.org/TR/css-syntax-3/#consume-an-escaped-code-point + { selector: '#nonescaped', expect: { count: 1 } }, + { selector: '.nonescaped', expect: { count: 1 } }, + ], + }, + { + name: 'escape hex digit identifier', + html: ` +
+ + + + +
`, + setupPage: setupNw, + cases: [ + // - escape hex digit + { selector: '#\\30 nextIsWhiteSpace', expect: { count: 1 } }, + { selector: '.\\30 nextIsWhiteSpace', expect: { count: 1 } }, + + { selector: '#\\30nextIsNotHexLetters', expect: { count: 1 } }, + { selector: '.\\30nextIsNotHexLetters', expect: { count: 1 } }, + + { selector: '#\\000030connectHexMoreThan6Hex', expect: { count: 1 } }, + { selector: '.\\000030connectHexMoreThan6Hex', expect: { count: 1 } }, + + { selector: '#\\000030 spaceMoreThan6Hex', expect: { count: 1 } }, + { selector: '.\\000030 spaceMoreThan6Hex', expect: { count: 1 } }, + ], + }, + // { + // // - hex digit special replacement + // } + { + name: 'zero points', + html: '', // set up page in setupPage callback to avoid issues with unrepresentable characters in HTML source + // html: ` + //
+ // + // + // + // + //
`, + setupPage: async (page) => { + setupNw(page); + await page.evaluate(() => { + document.body.innerHTML = ''; + const root = document.createElement('div'); + + const one = document.createElement('span'); + one.id = one.className = 'one\uFFFD'; + + const two = document.createElement('span'); + two.id = two.className = 'two\u0000'; + + const three = document.createElement('span'); + three.id =three.className = 'three\uFFFD'; + + const four = document.createElement('span'); + four.id = four.className = 'four\u0000'; + + root.append(one, two, three, four); + document.body.appendChild(root); + }); + }, + cases: [ + // 1. zero points + { selector: '#one\\0', expect: { count: 1 } }, + { selector: '.one\\0', expect: { count: 1 } }, + + { selector: '#two\\0', expect: { count: 0 } }, + { selector: '.two\\0', expect: { count: 0 } }, + + { selector: '#three\\000000', expect: { count: 1 } }, + { selector: '.three\\000000', expect: { count: 1 } }, + + { selector: '#four\\000000', expect: { count: 0 } }, + { selector: '.four\\000000', expect: { count: 0 } }, + ], + }, + { + name: 'surrogate points', + html: ` +
+ + + + + + + + +
`, + setupPage: setupNw, + cases: [ + // 2. surrogate points + { selector: '#\\d83d surrogateFirstA', expect: { count: 1, ids: ['\u{fffd}surrogateFirstA'] } }, + { selector: '.\\d83d surrogateFirstA', expect: { count: 1, ids: ['\u{fffd}surrogateFirstA'] } }, + { selector: '#\\d83d surrogateFirstB', expect: { count: 0 } }, + { selector: '.\\d83d surrogateFirstB', expect: { count: 0 } }, + + { selector: '#surrogateSecondC\\dd11', expect: { count: 1, ids: ['surrogateSecondC\u{fffd}'] } }, + { selector: '.surrogateSecondC\\dd11', expect: { count: 1, ids: ['surrogateSecondC\u{fffd}'] } }, + { selector: '#surrogateSecondD\\dd11', expect: { count: 0 } }, + { selector: '.surrogateSecondD\\dd11', expect: { count: 0 } }, + + { selector: '#surrogatePairE\\d83d\\dd11', expect: { count: 1, ids: ['surrogatePairE\u{fffd}\u{fffd}'] } }, + { selector: '.surrogatePairE\\d83d\\dd11', expect: { count: 1, ids: ['surrogatePairE\u{fffd}\u{fffd}'] } }, + { selector: '#surrogatePairF\\d83d\\dd11', expect: { count: 0 } }, + { selector: '.surrogatePairF\\d83d\\dd11', expect: { count: 0 } }, + ], + }, + { + name: 'out of range points', + html: ` +
+ + +
`, + setupPage: setupNw, + cases: [ + // 3. out of range points + { selector: '#outOfRangeA\\110000', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { selector: '.outOfRangeA\\110000', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + + { selector: '#outOfRangeA\\110030', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { selector: '.outOfRangeA\\110030', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + + { selector: '#outOfRangeB\\110030', expect: { count: 0 } }, + { selector: '.outOfRangeB\\110030', expect: { count: 0 } }, + + { selector: '#outOfRangeA\\555555', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { selector: '.outOfRangeA\\555555', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + + { selector: '#outOfRangeA\\ffffff', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { selector: '.outOfRangeA\\ffffff', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + ], + }, + { + name: 'escape eof', + html: ` +
+ + +
`, + setupPage: setupNw, + cases: [ + // trailing backslash escapes EOF -> U+FFFD + { selector: '#eofA\\', expect: { count: 1, ids: ['eofA\u{fffd}'] } }, + { selector: '.eofA\\', expect: { count: 1, ids: ['eofA\u{fffd}'] } }, + + { selector: '#eofB\\', expect: { count: 0 } }, + { selector: '.eofB\\', expect: { count: 0 } }, + ], + }, + { + name: 'escape anything else', + html: ` +
+ + + +
`, + setupPage: setupNw, + cases: [ + { selector: '#\\.comma', expect: { count: 1, ids: ['.comma'] } }, + { selector: '.\\.comma', expect: { count: 1, ids: ['.comma'] } }, + + { selector: '#\\-minus', expect: { count: 1, ids: ['-minus'] } }, + { selector: '.\\-minus', expect: { count: 1, ids: ['-minus'] } }, + + { selector: '#\\g', expect: { count: 1, ids: ['g'] } }, + { selector: '.\\g', expect: { count: 1, ids: ['g'] } }, + ], + }, + { + name: 'non edge cases', + html: ` +
+ + + + + + + + + + +
`, + setupPage: setupNw, + cases: [ + { selector: '#\\61 BMPRegular', expect: { count: 1, ids: ['aBMPRegular'] } }, + { selector: '.\\61 BMPRegular', expect: { count: 1, ids: ['aBMPRegular'] } }, + + { selector: '#\\1f511 nonBMP', expect: { count: 1, ids: ['\u{1f511}nonBMP'] } }, + { selector: '.\\1f511 nonBMP', expect: { count: 1, ids: ['\u{1f511}nonBMP'] } }, + + { selector: '#\\30\\30 continueEscapesA', expect: { count: 1, ids: ['00continueEscapesA'] } }, + { selector: '.\\30\\30 continueEscapesA', expect: { count: 1, ids: ['00continueEscapesA'] } }, + + { selector: '#\\30 \\30 continueEscapesB', expect: { count: 1, ids: ['00continueEscapesB'] } }, + { selector: '.\\30 \\30 continueEscapesB', expect: { count: 1, ids: ['00continueEscapesB'] } }, + + { selector: '#continueEscapesC\\30 \\30 ', expect: { count: 1, ids: ['continueEscapesC00'] } }, + { selector: '.continueEscapesC\\30 \\30 ', expect: { count: 1, ids: ['continueEscapesC00'] } }, + + { selector: '#continueEscapesD\\30 \\30', expect: { count: 1, ids: ['continueEscapesD00'] } }, + { selector: '.continueEscapesD\\30 \\30', expect: { count: 1, ids: ['continueEscapesD00'] } }, + + { selector: '#continueEscapesE\\30\\30 ', expect: { count: 1, ids: ['continueEscapesE00'] } }, + { selector: '.continueEscapesE\\30\\30 ', expect: { count: 1, ids: ['continueEscapesE00'] } }, + + { selector: '#continueEscapesF\\30\\30', expect: { count: 1, ids: ['continueEscapesF00'] } }, + { selector: '.continueEscapesF\\30\\30', expect: { count: 1, ids: ['continueEscapesF00'] } }, + ], + }, + + { + name: 'chromium ident cases 1', + html: ` +
+ + + + + + + + + + + + + + + + + + + + +
`, + setupPage: setupNw, + cases: [ + // ident tests case from CSS tests of chromium source: https://goo.gl/3Cxdov + { selector: '#hel\\6CoA', expect: { count: 1, ids: ['helloA'] } }, + { selector: '.hel\\6CoA', expect: { count: 1, ids: ['helloA'] } }, + + { selector: '#hel\\6C oB', expect: { count: 1, ids: ['helloB'] } }, + { selector: '.hel\\6C oB', expect: { count: 1, ids: ['helloB'] } }, + + { selector: '#\\26 B', expect: { count: 1, ids: ['&B'] } }, + { selector: '.\\26 B', expect: { count: 1, ids: ['&B'] } }, + + { selector: '#spac\\65\r\nsA', expect: { count: 1, ids: ['spacesA'] } }, + { selector: '.spac\\65\r\nsA', expect: { count: 1, ids: ['spacesA'] } }, + + { selector: '#sp\\61\tc\\65\fsB', expect: { count: 1, ids: ['spacesB'] } }, + { selector: '.sp\\61\tc\\65\fsB', expect: { count: 1, ids: ['spacesB'] } }, + + { selector: '#\\E000', expect: { count: 1, ids: ['\u{E000}'] } }, + { selector: '.\\E000', expect: { count: 1, ids: ['\u{E000}'] } }, + + { selector: '#testA\\D799', expect: { count: 1, ids: ['testA\u{D799}'] } }, + { selector: '.testA\\D799', expect: { count: 1, ids: ['testA\u{D799}'] } }, + + { selector: '#te\\s\\tB', expect: { count: 1, ids: ['testB'] } }, + { selector: '.te\\s\\tB', expect: { count: 1, ids: ['testB'] } }, + + { selector: '#\\.\\,\\:\\!', expect: { count: 1, ids: ['.,:!'] } }, + { selector: '.\\.\\,\\:\\!', expect: { count: 1, ids: ['.,:!'] } }, + + { selector: '#nullA\\0', expect: { count: 1, ids: ['nullA\u{fffd}'] } }, + { selector: '.nullA\\0', expect: { count: 1, ids: ['nullA\u{fffd}'] } }, + + { selector: '#nullB\\0000', expect: { count: 1, ids: ['nullB\u{fffd}'] } }, + { selector: '.nullB\\0000', expect: { count: 1, ids: ['nullB\u{fffd}'] } }, + + { selector: '#largeA\\110000', expect: { count: 1, ids: ['largeA\u{fffd}'] } }, + { selector: '.largeA\\110000', expect: { count: 1, ids: ['largeA\u{fffd}'] } }, + + { selector: '#largeB\\23456a', expect: { count: 1, ids: ['largeB\u{fffd}'] } }, + { selector: '.largeB\\23456a', expect: { count: 1, ids: ['largeB\u{fffd}'] } }, + + { selector: '#surrogateA\\D800', expect: { count: 1, ids: ['surrogateA\u{fffd}'] } }, + { selector: '.surrogateA\\D800', expect: { count: 1, ids: ['surrogateA\u{fffd}'] } }, + + { selector: '#surrogateB\\0DBAC', expect: { count: 1, ids: ['surrogateB\u{fffd}'] } }, + { selector: '.surrogateB\\0DBAC', expect: { count: 1, ids: ['surrogateB\u{fffd}'] } }, + + { selector: '#\\00DFFFsurrogateC', expect: { count: 1, ids: ['\u{fffd}surrogateC'] } }, + { selector: '.\\00DFFFsurrogateC', expect: { count: 1, ids: ['\u{fffd}surrogateC'] } }, + + { selector: '#\\10fFfF', expect: { count: 1, ids: ['\u{10ffff}'] } }, + { selector: '.\\10fFfF', expect: { count: 1, ids: ['\u{10ffff}'] } }, + + { selector: '#\\10fFfF0', expect: { count: 1, ids: ['\u{10ffff}0'] } }, + { selector: '.\\10fFfF0', expect: { count: 1, ids: ['\u{10ffff}0'] } }, + + { selector: '#\\10000000', expect: { count: 1, ids: ['\u{100000}00'] } }, + { selector: '.\\10000000', expect: { count: 1, ids: ['\u{100000}00'] } }, + ], + }, + { + name: 'chromium ident cases 2', + html: ` +
+ + + + + + + + + + + + + + + +
`, + setupPage: setupNw, + cases: [ + // ident tests case from CSS tests of chromium source: https://goo.gl/3Cxdov + { selector: '#simple-ident', expect: { count: 1, ids: ['simple-ident'] } }, + { selector: '.simple-ident', expect: { count: 1, ids: ['simple-ident'] } }, + + { selector: '#testing123', expect: { count: 1, ids: ['testing123'] } }, + { selector: '.testing123', expect: { count: 1, ids: ['testing123'] } }, + + { selector: '#_underscore', expect: { count: 1, ids: ['_underscore'] } }, + { selector: '._underscore', expect: { count: 1, ids: ['_underscore'] } }, + + { selector: '#-text', expect: { count: 1, ids: ['-text'] } }, + { selector: '.-text', expect: { count: 1, ids: ['-text'] } }, + + { selector: '#-\\6d', expect: { count: 1, ids: ['-m'] } }, + { selector: '.-\\6d', expect: { count: 1, ids: ['-m'] } }, + + { selector: '#--abc', expect: { count: 1, ids: ['--abc'] } }, + { selector: '.--abc', expect: { count: 1, ids: ['--abc'] } }, + + { selector: '#--', expect: { count: 1, ids: ['--'] } }, + { selector: '.--', expect: { count: 1, ids: ['--'] } }, + + { selector: '#--11', expect: { count: 1, ids: ['--11'] } }, + { selector: '.--11', expect: { count: 1, ids: ['--11'] } }, + + { selector: '#---', expect: { count: 1, ids: ['---'] } }, + { selector: '.---', expect: { count: 1, ids: ['---'] } }, + + { selector: '#\u{2003}', expect: { count: 1, ids: ['\u{2003}'] } }, + { selector: '.\u{2003}', expect: { count: 1, ids: ['\u{2003}'] } }, + + { selector: '#\u{A0}', expect: { count: 1, ids: ['\u{A0}'] } }, + { selector: '.\u{A0}', expect: { count: 1, ids: ['\u{A0}'] } }, + + { selector: '#\u{1234}', expect: { count: 1, ids: ['\u{1234}'] } }, + { selector: '.\u{1234}', expect: { count: 1, ids: ['\u{1234}'] } }, + + { selector: '#\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, + { selector: '.\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, + + { selector: '#\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, + { selector: '.\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, + + { selector: '#ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, + { selector: '.ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, + ], + }, + { + name: 'spaces in ident id selector', + html: `
`, + setupPage: setupNw, + cases: [ + { selector: '#spaces\\ in\\\tident', expect: { count: 1, ids: ['spaces in\tident'] } }, + ], + }, + { + name: 'spaces in ident class selector mismatch', + modifier: 'fail', + html: `
`, + setupPage: setupNw, + cases: [ + { selector: '.spaces\\ in\\\tident', expect: { count: 1, ids: ['spaces in\tident'] } }, + ], + }, +]); \ No newline at end of file diff --git a/test_new/browser/harness.ts b/test_new/browser/harness.ts index d7d1424..0982e38 100644 --- a/test_new/browser/harness.ts +++ b/test_new/browser/harness.ts @@ -8,10 +8,11 @@ export type SelectorScenario = { browsers?: BrowserName[]; cases: SelectorCase[]; setupPage?: (page: Page) => void | Promise; + modifier?: TestModifier; }; -const browserNames = ['chromium', 'firefox', 'webkit'] as const; -type BrowserName = typeof browserNames[number]; +const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; +type BrowserName = typeof BROWSER_NAMES[number]; type SelectorCase = { selector: string; @@ -19,6 +20,9 @@ type SelectorCase = { expect?: SelectorExpectation; }; +type DescribeModifier = 'normal' | 'skip' | 'only' | 'fixme'; +type TestModifier = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; + type SelectorExpectation = { exact?: boolean; count?: number; @@ -31,16 +35,15 @@ type SelectorExpectation = { type SelectorResult = { exactMatch: boolean; mismatchMsg: string; - native: { - count: number; - ids: string[]; - threw: boolean; - }; - nw: { - count: number; - ids: string[]; - threw: boolean; - }; +} & Record; + +const ENGINES = ['native', 'nw'] as const; +type Engine = typeof ENGINES[number]; + +type EngineResult = { + count: number; + ids: string[]; + threw: boolean; }; type SelectorRoot = @@ -48,8 +51,13 @@ type SelectorRoot = | { kind: 'id'; value: string } | { kind: 'selector'; value: string }; -export function runScenarios(label: string, scenarios: SelectorScenario[]): void { - test.describe(label, () => { +export function runScenarios( + label: string, + modifier: DescribeModifier, + scenarios: SelectorScenario[], +): void { + const describeFn = getDescribeFn(modifier); + describeFn(label, () => { let browsers: Record; let pages: Record; @@ -73,11 +81,12 @@ export function runScenarios(label: string, scenarios: SelectorScenario[]): void }); test.afterAll(async () => { - await Promise.all(browserNames.map((name) => browsers[name].close())); + await Promise.all(BROWSER_NAMES.map((name) => browsers[name].close())); }); for (const s of scenarios) { - test(s.name, async () => { + const testFn = getTestFn(s.modifier); + testFn(s.name, async () => { await runScenario(s, pages); }); } @@ -85,7 +94,7 @@ export function runScenarios(label: string, scenarios: SelectorScenario[]): void } async function runScenario(s: SelectorScenario, pages: Record): Promise { - const scenarioBrowsers = s.browsers ?? browserNames; + const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; for (const browserName of scenarioBrowsers) { const page = pages[browserName]; @@ -99,33 +108,37 @@ async function runScenario(s: SelectorScenario, pages: Record if (exp.throws) { expect(result.nw.threw, msg).toBe(true); continue; - } else { - expect(result.nw.threw, msg).toBe(false); } + expect(result.nw.threw, msg).toBe(false); + const skipNative = result.native.threw; if (exp.count !== undefined) { - if (!skipNative) expect(result.native.count, msg).toBe(exp.count); - expect(result.nw.count, msg).toBe(exp.count); + expectEngines(result, msg, 'count', (r, label) => { + expect(r.count, label).toEqual(exp.count); + }); } if (exp.ids) { - if (!skipNative) expect(result.native.ids, msg).toEqual(exp.ids); - expect(result.nw.ids, msg).toEqual(exp.ids); + expectEngines(result, msg, 'ids', (r, label) => { + expect(r.ids, label).toEqual(exp.ids); + }); } if (exp.includesIds) { for (const id of exp.includesIds) { - if (!skipNative) expect(result.native.ids, msg).toContain(id); - expect(result.nw.ids, msg).toContain(id); + expectEngines(result, msg, 'ids', (r, label) => { + expect(r.ids, label).toContain(id); + }); } } if (exp.excludesIds) { for (const id of exp.excludesIds) { - if (!skipNative) expect(result.native.ids, msg).not.toContain(id); - expect(result.nw.ids, msg).not.toContain(id); + expectEngines(result, msg, 'ids', (r, label) => { + expect(r.ids, label).not.toContain(id); + }); } } @@ -136,6 +149,49 @@ async function runScenario(s: SelectorScenario, pages: Record } } +function expectEngines( + result: SelectorResult, + baseMsg: string, + key: keyof EngineResult, + check: (r: EngineResult, label: string) => void +): void { + for (const engine of ENGINES) { + const r = result[engine]; + if (r.threw) continue; + + const sameIds = (a: string[], b: string[]): boolean => + a.length === b.length && a.every((x, i) => x === b[i]); + + const enginesWithSameOutcome = ENGINES.filter((e) => { + if (result[e].threw) return false; + switch (key) { + case 'count': return r.count === result[e].count; + case 'ids': return sameIds(r.ids, result[e].ids); + case 'threw': return r.threw === result[e].threw; + default: assertNever(key); + } + }); + + const label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; + check(r, label); + } +} + +function getDescribeFn(mode?: DescribeModifier) { + if (mode === 'skip') return test.describe.skip; + if (mode === 'only') return test.describe.only; + if (mode === 'fixme') return test.describe.fixme; + return test.describe; +} + +function getTestFn(mode?: TestModifier) { + if (mode === 'skip') return test.skip; + if (mode === 'only') return test.only; + if (mode === 'fixme') return test.fixme; + if (mode === 'fail') return test.fail; + return test; +} + async function setupPage(page: Page, scenario: SelectorScenario): Promise { if (scenario.htmlMode === 'document') { await page.setContent(scenario.html); @@ -231,3 +287,7 @@ async function evalSelector(page: Page, selCase: SelectorCase): Promise +
+
+ + + +
+ + `, + cases: [ + { selector: '.neighbor + div .target', expect: { count: 1 } }, + { selector: '.neighbor + * .target', expect: { count: 1 } }, + ], + }, +]); \ No newline at end of file diff --git a/test_new/global.d.ts b/test_new/global.d.ts index 360338b..29a7a0b 100644 --- a/test_new/global.d.ts +++ b/test_new/global.d.ts @@ -4,6 +4,7 @@ declare global { const NW: { Dom: { select(selector: string, root: Document | Element): Element[]; + configure(options: Record): void; }; } } \ No newline at end of file From 75e2c6803baa39aaf06b88b3e9709414daf3935c Mon Sep 17 00:00:00 2001 From: koal44 Date: Mon, 6 Apr 2026 22:35:13 -0700 Subject: [PATCH 04/19] migrate html5 and jquery tests into the playwright harness --- eslint.config.mjs | 26 +- test_new/browser/harness.ts | 10 +- test_new/browser/html5.test.ts | 199 ++++++++ test_new/browser/jquery.test.ts | 835 ++++++++++++++++++++++++++++++++ test_new/global.d.ts | 6 +- 5 files changed, 1070 insertions(+), 6 deletions(-) create mode 100644 test_new/browser/html5.test.ts create mode 100644 test_new/browser/jquery.test.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index f0bc75b..7ab8263 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,7 +5,7 @@ import globals from 'globals'; export default defineConfig([ js.configs.recommended, { - files: ['src/**/*.js', 'scripts/**/*.js', 'test/**/*.js'], + files: ['src/**/*.js', 'scripts/**/*.js', 'test_new/**/*.js'], languageOptions: { ecmaVersion: 'latest', sourceType: 'script', @@ -31,4 +31,28 @@ export default defineConfig([ 'no-with': 'error', }, }, + { + files: ['test/**/*.js'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'script', + globals: { + ...globals.browser, + ...globals.commonjs, + ...globals.amd, + NW: 'readonly', + }, + }, + rules: { + 'no-console': 'off', + 'no-unused-vars': 'off', + 'no-cond-assign': 'off', + 'no-control-regex': 'off', + 'no-useless-escape': 'off', + 'no-redeclare': 'off', + 'no-empty': 'off', + 'no-undef': 'off', + radix: 'off', + }, + }, ]); diff --git a/test_new/browser/harness.ts b/test_new/browser/harness.ts index 0982e38..39dbd70 100644 --- a/test_new/browser/harness.ts +++ b/test_new/browser/harness.ts @@ -244,10 +244,12 @@ async function evalSelector(page: Page, selCase: SelectorCase): Promise el.id); - const nwIds = nw.map((el: Element) => el.id); + const nativeIds = native.map((el) => el.getAttribute('id') ?? ''); + const nwIds = nw.map((el) => el.getAttribute('id') ?? ''); + // const nativeIds = native.map((el) => el.id); + // const nwIds = nw.map((el: Element) => el.id); - const describe = (el: Element | undefined) => { + let describe = (el: Element | undefined) => { if (!el) return '(missing)'; return { tag: el.tagName.toLowerCase(), @@ -290,4 +292,4 @@ async function evalSelector(page: Page, selCase: SelectorCase): PromiseNWSAPI HTML5 elements selection test + +

Usual nonsense content...

+ +

+ IBA + Located at 116 Messina Avenue, London NW6 4LD + UK +

+ +
+
    +
  • First
  • +
  • Second
  • +
  • Last
  • +
+
+ +
+ `, + cases: [ + { selector: 'abbr:first-of-type', expect: { ids: ['IBA'] } }, + { selector: 'abbr:last-of-type', expect: { ids: ['UK'] } }, + { selector: 'mark:only-of-type', expect: { ids: ['NUM'] } }, + { selector: 'abbr:nth-of-type(1)', expect: { ids: ['IBA'] } }, + { selector: 'abbr:nth-of-type(2)', expect: { ids: ['UK'] } }, + { selector: 'abbr:nth-last-of-type(1)', expect: { ids: ['UK'] } }, + { selector: 'abbr:nth-last-of-type(2)', expect: { ids: ['IBA'] } }, + { selector: 'section li:first-of-type', expect: { ids: ['first'] } }, + { selector: 'section li:last-of-type', expect: { ids: ['last'] } }, + ], + }, + + { + name: 'table :not() selector test', + modifier: 'fixme', + html: ` +
+

 Your Search Results

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
You searched for: all records
Showing matches 1 to 10 of 1405
 
PhotoReg Number Make & ModelColourStolen From Date / Time stolen 
AV11UXAMercedes-benz E Class Convertible White
London23rd April 2015 Between midday and 3pm
+
+
LD64FKOHonda CB500F Black
London22nd April 2015 in the afternoon
+
+
YN58 LXVPeugeot XPS CT 125 Black
Shropshire20th April 2015 Between midnight and 3am
+
+
YA08JZRPEUYGEOT PARTNER ORIGIN White
Staffordshire17th April 2015 Between 3am and 6am
+
+
DF03 KXBBaimo RSR 125 Blue
Hampshire19th April 2015 Between midnight and 3am
+
+
YG62 CM0Audi A4 Grey
West Midlands14th April 2015 Between midnight and 3am
+
+
FT10 FDVSeat Ibiza White
South Yorkshire9th April 2015 in the afternoon
+
+
RJ61 WREVolkswagen Golf 1.6 TDi Bluemotion Tech Match Red
Dyfed5th March 2015 Between 3pm and 6pm
+
+
SR11 HLOMazda Mazda3 Takuya Silver
London2nd April 2015 Between 9pm and midnight
+
+
TNZ 9284Citroen Berlingo GB9HXC White
Bedfordshire16th March 2015 time unknown
+
+
+
+ `, + cases: [ + { + selector: 'tbody > tr:nth-of-type(n+6):not(:nth-of-type(17)) > td:nth-of-type(2) > a:not(:nth-of-type(2))', + expect: { ids: ['AV11UXA'] }, + }, + ], + }, +]); \ No newline at end of file diff --git a/test_new/browser/jquery.test.ts b/test_new/browser/jquery.test.ts new file mode 100644 index 0000000..53f7508 --- /dev/null +++ b/test_new/browser/jquery.test.ts @@ -0,0 +1,835 @@ +import { expect } from "@playwright/test"; +import { runScenarios } from "./harness"; + +const html = ` + + + + + jQuery Test Suite + + + + + +

jQuery Test Suite

+ +

+ + +
+
+
+ + + + + +
+
+
+ fadeIn +
fadeIn
+
+
+ fadeOut +
fadeOut
+
+ +
+ show +
show
+
+
+ hide +
hide
+
+ +
+ togglein +
togglein
+
+
+ toggleout +
toggleout
+
+ +
+ slideUp +
slideUp
+
+
+ slideDown +
slideDown
+
+ +
+ slideToggleIn +
slideToggleIn
+
+
+ slideToggleOut +
slideToggleOut
+
+
+ +
+
+ +
    + + +`; + +runScenarios('jquery', 'normal', [ + { + name: 'element', + html: html, + htmlMode: 'document', + cases: [ + // Compare universal selection directly against native behavior. + { selector: '*' }, + + { selector: 'p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'body', expect: { ids: ['body'] } }, + { selector: 'html', expect: { ids: ['html'] } }, + { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + // scoped selection + { selector: 'param', root: { kind: 'id', value: 'object1' }, expect: { count: 2 } }, + + // Consistency checks for multiple selector groups + { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + { selector: '#length', expect: { count: 1 } }, + { selector: '#lengthtest input', expect: { count: 2 } }, + + // Duplicate / sort-order checks from the original suite, expressed as counts. + { selector: '*', expect: { count: 187 } }, + { selector: '*, *', expect: { count: 187 } }, + + { selector: 'p', expect: { count: 6 } }, + { selector: 'p, div p', expect: { count: 6 } }, + + { selector: 'h2, h1', expect: { ids: ['header', 'banner', 'userAgent'] } }, + { selector: 'p, p a', expect: { ids: ['firstp', 'simon1', 'ap', 'google', 'groups', 'anchor1', 'mark', 'sndp', 'en', 'yahoo', 'sap', 'anchor2', 'simon', 'first'] } }, + + // jQuery extension pseudo, not native CSS + // { selector: 'h2:first, h1:first', expect: { ids: ['header', 'banner'] } }, + ], + }, + + { + name: 'broken selectors', + html: html, + htmlMode: 'document', + cases: [ + { selector: '[', expect: { throws: true } }, + { selector: '(', expect: { throws: true } }, + { selector: '{', expect: { throws: true } }, + { selector: '<', expect: { throws: true } }, + { selector: '()', expect: { throws: true } }, + { selector: '<>', expect: { throws: true } }, + + { selector: ':nth-child(2n+-0)', expect: { throws: true } }, + { selector: ':nth-child(- 1n)', expect: { throws: true } }, + { selector: ':nth-child(-1 n)', expect: { throws: true } }, + { selector: ':first-child(n)', expect: { throws: true } }, + { selector: ':last-child(n)', expect: { throws: true } }, + { selector: ':only-child(n)', expect: { throws: true } }, + { selector: ':nth-child(2+0)', expect: { throws: true } }, + ], + }, + + { + name: 'id selectors', + html: html, + htmlMode: 'document', + cases: [ + { selector: '#body', expect: { ids: ['body'] } }, + { selector: 'body#body', expect: { ids: ['body'] } }, + { selector: 'ul#first', expect: { ids: [] } }, + + { selector: '#firstp #simon1', expect: { ids: ['simon1'] } }, + { selector: '#firstp #foobar', expect: { ids: [] } }, + + { selector: '#台北Táiběi', expect: { ids: ['台北Táiběi'] } }, + { selector: '#台北Táiběi, #台北', expect: { ids: ['台北Táiběi', '台北'] } }, + { selector: 'div #台北', expect: { ids: ['台北'] } }, + { selector: 'form > #台北', expect: { ids: ['台北'] } }, + + { selector: '#foo\\:bar', expect: { ids: ['foo:bar'] } }, + { selector: '#test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { selector: 'div #foo\\:bar', expect: { ids: ['foo:bar'] } }, + { selector: 'div #test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { selector: 'form > #foo\\:bar', expect: { ids: ['foo:bar'] } }, + { selector: 'form > #test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + + { selector: '#form > #radio1', expect: { ids: ['radio1'] } }, // bug #267 + { selector: '#form #first', expect: { ids: [] } }, + { selector: '#form > #option1a', expect: { ids: [] } }, + + { selector: '#foo > *', expect: { ids: ['sndp', 'en', 'sap'] } }, + { selector: '#firstUL > *', expect: { ids: [] } }, + + { selector: '#lengthtest', expect: { ids: ['lengthtest'] } }, + { selector: '#asdfasdf #foobar', expect: { ids: [] } }, // bug #986 + + { selector: 'body div#form', expect: { ids: [] } }, + + { selector: '#types_all', expect: { ids: ['types_all'] } }, + { selector: '#fx-queue', expect: { ids: ['fx-queue'] } }, + { selector: '#name\\+value', expect: { ids: ['name+value'] } }, + ], + }, + { + name: 'id selectors after document fragment append', + html: html, + htmlMode: 'document', + setupPage: async (page) => { + await page.evaluate(() => { + const main = document.getElementById('main'); + if (!main) throw new Error('#main not found'); + + const orphan = document.createElement('div'); + orphan.innerHTML = + 'tName1 A' + + 'tName2 A' + + '
    tName1 Div
    '; + + const fragment = document.createDocumentFragment(); + while (orphan.firstChild) { + fragment.appendChild(orphan.firstChild); + } + + main.appendChild(fragment); + }); + }, + cases: [ + { selector: '#tName1', expect: { ids: ['tName1'] } }, + { selector: '#tName2', expect: { ids: [] } }, + ], + }, + + { + name: 'class selectors', + html: html, + htmlMode: 'document', + cases: [ + { selector: '.blog', expect: { ids: ['mark', 'simon'] } }, + { selector: '.GROUPS', expect: { ids: ['groups'] } }, + { selector: '.blog.link', expect: { ids: ['simon'] } }, + { selector: 'a.blog', expect: { ids: ['mark', 'simon'] } }, + { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + + // Repeated as in the original test + { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + + { selector: '.台北Táiběi', expect: { ids: ['utf8class1'] } }, + { selector: '.台北', expect: { ids: ['utf8class1', 'utf8class2'] } }, + { selector: '.台北Táiběi.台北', expect: { ids: ['utf8class1'] } }, + { selector: '.台北Táiběi, .台北', expect: { ids: ['utf8class1', 'utf8class2'] } }, + { selector: 'div .台北Táiběi', expect: { ids: ['utf8class1'] } }, + { selector: 'form > .台北Táiběi', expect: { ids: ['utf8class1'] } }, + + { selector: '.foo\\:bar', expect: { ids: ['foo:bar'] } }, + { selector: '.test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { selector: 'div .foo\\:bar', expect: { ids: ['foo:bar'] } }, + { selector: 'div .test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { selector: 'form > .foo\\:bar', expect: { ids: ['foo:bar'] } }, + { selector: 'form > .test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + ], + }, + + { + name: 'class selectors in detached subtree', + html: '', + cases: [], + setupPage: async (page) => { + const result = await page.evaluate(() => { + const div = document.createElement('div'); + div.innerHTML = "
    "; + + const first = NW.Dom.select('.e', div).map(el => el.getAttribute('id')); + div.lastChild && ((div.lastChild as Element).className = 'e'); + const second = NW.Dom.select('.e', div).map(el => el.getAttribute('id')); + + return { first, second }; + }); + + expect(result.first).toEqual(['first']); + expect(result.second).toEqual(['first', 'second']); + }, + }, + + { + name: 'name selectors', + html: html, + htmlMode: 'document', + setupPage: async (page) => { + await page.evaluate(() => { + const main = document.getElementById('main'); + if (!main) throw new Error('#main not found'); + + const orphan = document.createElement('div'); + orphan.innerHTML = + 'tName1 A' + + 'tName2 A' + + '
    tName1 Div
    '; + + const fragment = document.createDocumentFragment(); + while (orphan.firstChild) { + fragment.appendChild(orphan.firstChild); + } + + main.appendChild(fragment); + }); + }, + cases: [ + { selector: 'input[name=action]', expect: { ids: ['text1'] } }, + { selector: "input[name='action']", expect: { ids: ['text1'] } }, + { selector: 'input[name="action"]', expect: { ids: ['text1'] } }, + + { selector: '[name=test]', expect: { ids: ['length', 'fx-queue'] } }, + { selector: '[name=div]', expect: { ids: ['fadein'] } }, + { selector: '*[name=iframe]', expect: { ids: ['iframe'] } }, + + { selector: "input[name='types[]']", expect: { ids: ['types_all', 'types_anime', 'types_movie'] } }, + + { selector: '#form input[name=action]', expect: { ids: ['text1'] } }, + { selector: "#form input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, + + { selector: '[name=tName1]', expect: { ids: ['tName1ID'] } }, + { selector: '[name=tName2]', expect: { ids: ['tName2ID'] } }, + ], + }, + + { + name: 'multiple selectors', + html: html, + htmlMode: 'document', + cases: [ + { selector: 'h2, p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'h2,p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + ], + }, + + { + name: 'child and adjacent selectors', + html: html, + htmlMode: 'document', + cases: [ + { selector: 'p > a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { selector: 'p> a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { selector: 'p >a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { selector: 'p>a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + + { selector: 'p > a.blog', expect: { ids: ['mark', 'simon'] } }, + { selector: 'code > *', expect: { ids: ['anchor1', 'anchor2'] } }, + { selector: 'p > * > *', expect: { ids: ['anchor1', 'anchor2'] } }, + + { selector: 'a + a', expect: { ids: ['groups'] } }, + { selector: 'a +a', expect: { ids: ['groups'] } }, + { selector: 'a+ a', expect: { ids: ['groups'] } }, + { selector: 'a+a', expect: { ids: ['groups'] } }, + + { selector: 'p + p', expect: { ids: ['ap', 'en', 'sap'] } }, + { selector: 'p#firstp + p', expect: { ids: ['ap'] } }, + { selector: 'p[lang=en] + p', expect: { ids: ['sap'] } }, + { selector: 'a.GROUPS + code + a', expect: { ids: ['mark'] } }, + + { selector: 'a + a, code > a', expect: { ids: ['groups', 'anchor1', 'anchor2'] } }, + + { selector: 'div.blah > p > a', expect: { ids: [] } }, + { selector: 'div.foo > span > a', expect: { ids: [] } }, + { selector: '.container div:not(.excluded) div', expect: { ids: [] } }, + + { selector: '* > :first-child', root: { kind: 'id', value: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { selector: '* > :nth-child(1)', root: { kind: 'id', value: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { selector: '* > *:first-child', root: { kind: 'id', value: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + + { selector: '.fototab > .thumbnails > a', expect: { ids: [] } }, + + { selector: 'p:first-child', expect: { ids: ['firstp', 'sndp'] } }, + { selector: 'p:nth-child(1)', expect: { ids: ['firstp', 'sndp'] } }, + { selector: 'p:not(:nth-child(1))', expect: { ids: ['ap', 'en', 'sap', 'first'] } }, + ], + }, + + { + name: 'first-child cache invalidation', + html: html, + htmlMode: 'document', + setupPage: async (page) => { + await page.evaluate(() => { + const div = document.createElement('div'); + let divs = NW.Dom.select('p:first-child', null, null); + + for (let i = 0; i < divs.length; i++) { + divs[i].parentNode?.insertBefore(div.cloneNode(false), divs[i].nextSibling); + } + + divs = NW.Dom.select('p:first-child', null, null); + + for (let i = 0; i < divs.length; i++) { + const inserted = divs[i].parentNode?.insertBefore(div.cloneNode(false), divs[i]); + const p = inserted?.nextSibling; + p?.parentNode?.removeChild(p); + } + }); + }, + cases: [ + { selector: 'p:first-child', expect: { ids: [] } }, + ], + }, + + { + name: 'last-child and nth-child selectors', + html: html, + htmlMode: 'document', + cases: [ + { selector: 'p:last-child', expect: { ids: ['sap'] } }, + { selector: 'a:last-child', expect: { ids: ['simon1', 'anchor1', 'mark', 'yahoo', 'anchor2', 'simon', 'liveLink1', 'liveLink2'] } }, + + { selector: '#main form#form > *:nth-child(2)', expect: { ids: ['text1'] } }, + { selector: '#main form#form > :nth-child(2)', expect: { ids: ['text1'] } }, + + // changed `select:first` to `select:first-of-type`; `:first` is jQuery-only + { selector: '#form select:first-of-type option:nth-child(3)', expect: { ids: ['option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(0n+3)', expect: { ids: ['option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(1n+0)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(1n)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(n)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(even)', expect: { ids: ['option1b', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(odd)', expect: { ids: ['option1a', 'option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(2n)', expect: { ids: ['option1b', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(2n+1)', expect: { ids: ['option1a', 'option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(3n)', expect: { ids: ['option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(3n+1)', expect: { ids: ['option1a', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(3n+2)', expect: { ids: ['option1b'] } }, + { selector: '#form select:first-of-type option:nth-child(3n+3)', expect: { ids: ['option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(3n-1)', expect: { ids: ['option1b'] } }, + { selector: '#form select:first-of-type option:nth-child(3n-2)', expect: { ids: ['option1a', 'option1d'] } }, + { selector: '#form select:first-of-type option:nth-child(3n-3)', expect: { ids: ['option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(3n+0)', expect: { ids: ['option1c'] } }, + { selector: '#form select:first-of-type option:nth-child(-n+3)', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, + ], + }, + + { + name: 'attribute selectors', + html: html, + htmlMode: 'document', + setupPage: async (page) => { + await page.evaluate(() => { + const anchor2 = document.getElementById('anchor2') as HTMLAnchorElement | null; + if (anchor2) anchor2.href = '#2'; + + const inputs = NW.Dom.select('form input'); + if (inputs[0]) (inputs[0] as HTMLInputElement & { test?: number }).test = 0; + if (inputs[1]) (inputs[1] as HTMLInputElement & { test?: number }).test = 1; + }); + }, + cases: [ + { selector: 'a[title]', expect: { ids: ['google'] } }, + { selector: '*[title]', expect: { ids: ['google'] } }, + { selector: '[title]', expect: { ids: ['google'] } }, + { selector: 'a[ title ]', expect: { ids: ['google'] } }, + + { selector: "a[rel='bookmark']", expect: { ids: ['simon1'] } }, + { selector: 'a[rel="bookmark"]', expect: { ids: ['simon1'] } }, + { selector: 'a[rel=bookmark]', expect: { ids: ['simon1'] } }, + { selector: "a[href='http://www.google.com/']", expect: { ids: ['google'] } }, + { selector: "a[ rel = 'bookmark' ]", expect: { ids: ['simon1'] } }, + + { selector: "p a[href^='#']", expect: { ids: ['anchor2'] } }, + { selector: 'p a[href*="#"]', expect: { ids: ['simon1', 'anchor2'] } }, + + { selector: 'form label[for]', expect: { ids: ['label-for'] } }, + { selector: '#form [for=action]', expect: { ids: ['label-for'] } }, + + // Disabled tests - expandos don't work in all browsers + // { selector: 'form input[test]', expect: { ids: ['text1', 'text2'] } }, + // { selector: 'form input[test=0]', expect: { ids: ['text1'] } }, + // { selector: 'form input[test=1]', expect: { ids: ['text2'] } }, + + { selector: "input[name^='foo[']", expect: { ids: ['hidden2'] } }, + { selector: "input[name^='foo[bar]']", expect: { ids: ['hidden2'] } }, + { selector: "input[name*='[bar]']", expect: { ids: ['hidden2'] } }, + { selector: "input[name$='bar]']", expect: { ids: ['hidden2'] } }, + { selector: "input[name$='[bar]']", expect: { ids: ['hidden2'] } }, + { selector: "input[name$='foo[bar]']", expect: { ids: ['hidden2'] } }, + { selector: "input[name*='foo[bar]']", expect: { ids: ['hidden2'] } }, + + { selector: "#form input[type='radio'], #form input[type='hidden']", expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, + { selector: '#form input[type=\'radio\'], #form input[type="hidden"]', expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, + { selector: "#form input[type='radio'], #form input[type=hidden]", expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, + + { selector: 'span[lang=中文]', expect: { ids: ['台北'] } }, + + { selector: "a[href ^= 'http://www']", expect: { ids: ['google', 'yahoo'] } }, + { selector: "a[href $= 'org/']", expect: { ids: ['mark'] } }, + { selector: "a[href *= 'google']", expect: { ids: ['google', 'groups'] } }, + { selector: "#ap a:not([hreflang='en'])", expect: { ids: ['google', 'groups', 'anchor1'] } }, + + { selector: "#select1 option[value='']", expect: { ids: ['option1a'] } }, + { selector: "#select1 option:not([value=''])", expect: { ids: ['option1b', 'option1c', 'option1d'] } }, + + { selector: '#select1 option:checked', expect: { ids: ['option1a'] } }, + { selector: '#select2 option:checked', expect: { ids: ['option2d'] } }, + { selector: '#select3 option:checked', expect: { ids: ['option3b', 'option3c'] } }, + + { selector: "input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, + + { selector: '#form select:not([multiple])', expect: { ids: ['select1', 'select2'] } }, + { selector: '#form select:not([name=select1])', expect: { ids: ['select2', 'select3'] } }, + { selector: "#form select:not([name='select1'])", expect: { ids: ['select2', 'select3'] } }, + ], + }, + + { + name: 'pseudo selectors 1', + html: html, + htmlMode: 'document', + cases: [ + { selector: 'p:first-child', expect: { ids: ['firstp', 'sndp'] } }, + { selector: 'p:last-child', expect: { ids: ['sap'] } }, + { selector: 'a:only-child', expect: { ids: ['simon1', 'anchor1', 'yahoo', 'anchor2', 'liveLink1', 'liveLink2'] } }, + { selector: 'ul:empty', expect: { ids: ['firstUL'] } }, + + { selector: '#form input:not([type=hidden]):enabled', expect: { ids: ['text1', 'radio1', 'radio2', 'check1', 'check2', 'hidden2', 'name', 'search'] } }, + { selector: '#form input:disabled', expect: { ids: ['text2'] } }, + { selector: '#form input:checked', expect: { ids: ['radio2', 'check1'] } }, + + { selector: '#form option:checked', expect: { ids: ['option1a', 'option2d', 'option3b', 'option3c'] } }, + + // jQuery-only text pseudos; not valid in the native-parity harness + // { selector: "a:contains('Google')", expect: { ids: ['google', 'groups'] } }, + // { selector: "a:contains('Google Groups')", expect: { ids: ['groups'] } }, + // { selector: "a:contains('Google Groups (Link)')", expect: { ids: ['groups'] } }, + // { selector: "a:contains('(Link)')", expect: { ids: ['groups'] } }, + + { selector: 'p ~ div', expect: { ids: ['foo', 'moretests', 'tabindex-tests', 'liveHandlerOrder'] } }, + { selector: 'a.blog:not(.link)', expect: { ids: ['mark'] } }, + // { selector: "#form option:not(:contains('Nothing'),#option1b,:checked)", expect: { ids: ['option1c', 'option1d', 'option2b', 'option2c', 'option3d', 'option3e'] } }, + { selector: "#form option:not([id^='opt']:nth-child(-n+3))", expect: { ids: ['option1d', 'option2d', 'option3d', 'option3e'] } }, + { selector: "#form option:not(:not(:checked))[id^='option3']", expect: { ids: ['option3b', 'option3c'] } }, + { selector: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + // invalid compound selector inside :not() pseudo-class + { selector: 'p:not(div.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(p.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + // invalid compound selector inside :not() pseudo-class + { selector: 'p:not(div#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(p#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + { selector: 'p:not(a)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(a, b)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(a, b, div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { selector: 'p:not(p)', expect: { ids: [] } }, + { selector: 'p:not(a,p)', expect: { ids: [] } }, + { selector: 'p:not(p,a)', expect: { ids: [] } }, + { selector: 'p:not(a,p,b)', expect: { ids: [] } }, + // { selector: ':input:not(:image,:input,:submit)', expect: { ids: [] } }, + + // jQuery-only positional pseudos; keep parked for now + // { selector: 'p:nth(1)', expect: { ids: ['ap'] } }, + // { selector: 'p:first', expect: { ids: ['firstp'] } }, + // { selector: 'p:last', expect: { ids: ['first'] } }, + // { selector: 'p:even', expect: { ids: ['firstp', 'sndp', 'sap'] } }, + // { selector: 'p:odd', expect: { ids: ['ap', 'en', 'first'] } }, + // { selector: 'p:eq(1)', expect: { ids: ['ap'] } }, + // { selector: 'p:gt(0)', expect: { ids: ['ap', 'sndp', 'en', 'sap', 'first'] } }, + // { selector: 'p:lt(3)', expect: { ids: ['firstp', 'ap', 'sndp'] } }, + // { selector: 'p:parent', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + // { selector: '#form input:visible', expect: { ids: [] } }, + // { selector: 'div:visible:not(.testrunner-toolbar):lt(2)', expect: { ids: ['nothiddendiv', 'nothiddendivchild'] } }, + // { selector: '#form input:hidden', expect: { ids: ['text1', 'text2', 'radio1', 'radio2', 'check1', 'check2', 'hidden1', 'hidden2', 'name', 'search'] } }, + // { selector: '#main:hidden', expect: { ids: ['main'] } }, + // { selector: '#dl:hidden', expect: { ids: ['dl'] } }, + ], + }, + + // Legacy jQuery visibility / position pseudo tests. + // Preserved from the original suite but not active in the native-parity harness, + { + name: 'jquery visibility and position pseudos (legacy)', + modifier: 'skip', + html: html, + htmlMode: 'document', + setupPage: async (page) => { + await page.evaluate(() => { + const div = NW.Dom.select('#nothiddendivchild')[0] as HTMLElement | undefined; + if (!div) throw new Error('#nothiddendivchild not found'); + + div.style.fontSize = '0'; + div.style.lineHeight = '0'; + div.style.width = '0'; + div.style.height = '0'; + expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); + expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual([]); + + div.style.width = '1px'; + div.style.height = '0'; + expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); + expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual([]); + + div.style.width = '0'; + div.style.height = '1px'; + expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); + expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual([]); + + div.style.width = '1px'; + div.style.height = '1px'; + expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); + expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual([]); + + div.style.width = ''; + div.style.height = ''; + div.style.fontSize = ''; + div.style.lineHeight = ''; + }); + }, + cases: [ + { selector: 'div#nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, + { selector: 'div#nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, + { selector: 'div#nothiddendiv:not(:gt(0))', expect: { ids: ['nothiddendiv'] } }, + { selector: '#foo > :not(:first)', expect: { ids: ['en', 'sap'] } }, + { selector: 'select > :not(:gt(2))', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, + { selector: 'select:lt(2) :not(:first)', expect: { ids: ['option1b', 'option1c', 'option1d', 'option2a', 'option2b', 'option2c', 'option2d'] } }, + { selector: 'div.nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, + { selector: 'div.nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, + { selector: 'div.nothiddendiv:not(:lt(0))', expect: { ids: ['nothiddendiv'] } }, + + { selector: 'div div:eq(0)', expect: { ids: ['nothiddendivchild'] } }, + { selector: 'div div:eq(5)', expect: { ids: ['t2037'] } }, + { selector: 'div div:eq(27)', expect: { ids: ['hide'] } }, + { selector: 'div div:first', expect: { ids: ['nothiddendivchild'] } }, + { selector: 'div > div:first', expect: { ids: ['nothiddendivchild'] } }, + { selector: '#dl div:first div:first', expect: { ids: ['foo'] } }, + { selector: '#dl div:first > div:first', expect: { ids: ['foo'] } }, + { selector: 'div#nothiddendiv:first > div:first', expect: { ids: ['nothiddendivchild'] } }, + ], + }, + + { + name: 'form and header pseudo selectors', + modifier: 'skip', // form pseudo selectors are jQuery-only; + html: html, + htmlMode: 'document', + cases: [ + { selector: '#form :input', expect: { ids: ['text1', 'text2', 'radio1', 'radio2', 'check1', 'check2', 'hidden1', 'hidden2', 'name', 'search', 'button', 'area1', 'select1', 'select2', 'select3'] } }, + { selector: '#form :radio', expect: { ids: ['radio1', 'radio2'] } }, + { selector: '#form :checkbox', expect: { ids: ['check1', 'check2'] } }, + { selector: '#form :text:not(#search)', expect: { ids: ['text1', 'text2', 'hidden2', 'name'] } }, + { selector: '#form :radio:checked', expect: { ids: ['radio2'] } }, + { selector: '#form :checkbox:checked', expect: { ids: ['check1'] } }, + { selector: '#form :radio:checked, #form :checkbox:checked', expect: { ids: ['radio2', 'check1'] } }, + + { selector: ':header', expect: { ids: ['header', 'banner', 'userAgent'] } }, + ], + }, + + { + name: ':has() selector', + html: html, + htmlMode: 'document', + cases: [ + { selector: 'p:has(a)', expect: { ids: ['firstp', 'ap', 'en', 'sap'] } }, + ], + }, + +]); diff --git a/test_new/global.d.ts b/test_new/global.d.ts index 29a7a0b..15fbe5f 100644 --- a/test_new/global.d.ts +++ b/test_new/global.d.ts @@ -3,7 +3,11 @@ export {}; declare global { const NW: { Dom: { - select(selector: string, root: Document | Element): Element[]; + select( + selector: string, + root?: ParentNode | Document | Element | null, + callback?: ((element: Element) => boolean | void) | null, + ): Element[]; configure(options: Record): void; }; } From f8133371b2f4996293ac48a31abdfc6dc21751c4 Mon Sep 17 00:00:00 2001 From: koal44 Date: Tue, 7 Apr 2026 21:47:07 -0700 Subject: [PATCH 05/19] migrate jsvm, prototype, quirks, scope, and scotch --- build/scripts/nwtestjs.sh | 45 --- test_new/browser/haness.test.ts | 18 ++ test_new/browser/harness.ts | 238 +++++++++----- test_new/browser/jsvm.test.ts | 27 ++ test_new/browser/prototype.test.ts | 488 +++++++++++++++++++++++++++++ test_new/browser/quirks.test.ts | 61 ++++ test_new/browser/scope.test.ts | 128 ++++++++ test_new/browser/scotch.test.ts | 472 ++++++++++++++++++++++++++++ 8 files changed, 1349 insertions(+), 128 deletions(-) delete mode 100755 build/scripts/nwtestjs.sh create mode 100644 test_new/browser/haness.test.ts create mode 100644 test_new/browser/jsvm.test.ts create mode 100644 test_new/browser/prototype.test.ts create mode 100644 test_new/browser/quirks.test.ts create mode 100644 test_new/browser/scope.test.ts create mode 100644 test_new/browser/scotch.test.ts diff --git a/build/scripts/nwtestjs.sh b/build/scripts/nwtestjs.sh deleted file mode 100755 index 378114b..0000000 --- a/build/scripts/nwtestjs.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -echo -ne "NW: VM loading test, building test units..." - -# retrieve build infos -VERSION=`cat ${SOURCES}build/VERSION` -REVISION=1 - -# set used variables -BASEDIR=`pwd` -PKGNAME="nwsapi" -RELEASE=`date +%Y%m%d%H%M%S` - -# set source -SOURCES=${1} -if [ "${SOURCES}x" == 'x' ]; then - pushd . &> /dev/null - cd ..; SOURCES=`pwd` - popd &> /dev/null -fi - -# check platform -PLATFORM=`uname -s` - -if [ $PLATFORM == 'Darwin' ]; then - JS=bin/js_macos -elif [ $PLATFORM == 'Linux' ]; then - JS=bin/js_linux -elif [ $PLATFORM == 'Windows' ]; then - JS=bin/js.exe -fi - -pusdh . &> /dev/null - -$JS test/jsvm/load_test.js < test/jsvm/load_test.html - -if [ $? == '0' ]; then - echo 'PASSED' -elif [ $? != '0' ]; then - echo 'FAILED' -fi - -popd &> /dev/null - -exit 0 diff --git a/test_new/browser/haness.test.ts b/test_new/browser/haness.test.ts new file mode 100644 index 0000000..e35b671 --- /dev/null +++ b/test_new/browser/haness.test.ts @@ -0,0 +1,18 @@ +import { runScenarios } from "./harness"; + +runScenarios('scope', 'normal', [ + { + name: 'classes', + html: ` +
    +
    +
    + `, + cases: [ + { selector: 'div', expect: { classes: ['outer primary', 'other-outer', ''] } }, + { selector: 'div', expect: { includesClasses: ['other-outer'] } }, + { selector: 'div', expect: { includesClasses: ['outer', 'primary'] } }, + { selector: 'div', expect: { excludesClasses: ['missing-class'] } }, + ], + } +]); \ No newline at end of file diff --git a/test_new/browser/harness.ts b/test_new/browser/harness.ts index 39dbd70..e335413 100644 --- a/test_new/browser/harness.ts +++ b/test_new/browser/harness.ts @@ -24,17 +24,20 @@ type DescribeModifier = 'normal' | 'skip' | 'only' | 'fixme'; type TestModifier = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; type SelectorExpectation = { - exact?: boolean; + allowMismatch?: boolean; count?: number; ids?: string[]; includesIds?: string[]; excludesIds?: string[]; + classes?: string[]; + includesClasses?: string[]; + excludesClasses?: string[]; throws?: boolean; + equivalentTo?: { selector: string; root?: SelectorRoot }; }; type SelectorResult = { - exactMatch: boolean; - mismatchMsg: string; + mismatchMsg?: string; } & Record; const ENGINES = ['native', 'nw'] as const; @@ -43,7 +46,9 @@ type Engine = typeof ENGINES[number]; type EngineResult = { count: number; ids: string[]; + classes: string[]; threw: boolean; + equivalentToFailMsg?: string; }; type SelectorRoot = @@ -102,48 +107,80 @@ async function runScenario(s: SelectorScenario, pages: Record for (const c of s.cases) { const result = await evalSelector(page, c); - const exp = c.expect ?? { exact: true }; - const msg = `[${browserName}] ${s.name} :: ${c.selector}\n${result.mismatchMsg}`; - if (exp.throws) { + const expectation = c.expect ?? {}; + const allowMismatch = expectation.allowMismatch ?? false; + const msg = `[${browserName}] ${s.name} :: ${c.selector}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; + + if (expectation.throws) { expect(result.nw.threw, msg).toBe(true); continue; } expect(result.nw.threw, msg).toBe(false); - const skipNative = result.native.threw; + const nativeThrew = result.native.threw; - if (exp.count !== undefined) { + if (expectation.count !== undefined) { expectEngines(result, msg, 'count', (r, label) => { - expect(r.count, label).toEqual(exp.count); + expect(r.count, label).toEqual(expectation.count); }); } - if (exp.ids) { + if (expectation.ids) { expectEngines(result, msg, 'ids', (r, label) => { - expect(r.ids, label).toEqual(exp.ids); + expect(r.ids, label).toEqual(expectation.ids); }); } - if (exp.includesIds) { - for (const id of exp.includesIds) { + if (expectation.includesIds) { + for (const id of expectation.includesIds) { expectEngines(result, msg, 'ids', (r, label) => { expect(r.ids, label).toContain(id); }); } } - if (exp.excludesIds) { - for (const id of exp.excludesIds) { + if (expectation.excludesIds) { + for (const id of expectation.excludesIds) { expectEngines(result, msg, 'ids', (r, label) => { expect(r.ids, label).not.toContain(id); }); } } - if ((exp.exact ?? true) && !skipNative) { - expect(result.exactMatch, msg).toBe(true); + if (expectation.classes) { + expectEngines(result, msg, 'classes', (r, label) => { + expect(r.classes, label).toEqual(expectation.classes); + }); + } + + if (expectation.includesClasses) { + for (const cls of expectation.includesClasses) { + expectEngines(result, msg, 'classes', (r, label) => { + const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); + expect(classTokens, label).toContain(cls); + }); + } + } + + if (expectation.excludesClasses) { + for (const cls of expectation.excludesClasses) { + expectEngines(result, msg, 'classes', (r, label) => { + const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); + expect(classTokens, label).not.toContain(cls); + }); + } + } + + if (expectation.equivalentTo) { + expectEngines(result, msg, 'equivalentToFailMsg', (r, label) => { + expect(r.equivalentToFailMsg, label).toBeUndefined(); + }); + } + + if (!allowMismatch && !nativeThrew) { + expect(result.mismatchMsg, msg).toBeUndefined(); } } } @@ -167,12 +204,15 @@ function expectEngines( switch (key) { case 'count': return r.count === result[e].count; case 'ids': return sameIds(r.ids, result[e].ids); + case 'classes': return sameIds(r.classes, result[e].classes); case 'threw': return r.threw === result[e].threw; + case 'equivalentToFailMsg': return e === engine; default: assertNever(key); } }); - const label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; + let label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; + if (r.equivalentToFailMsg && key === 'equivalentToFailMsg') label += `\n${r.equivalentToFailMsg}`; check(r, label); } } @@ -207,49 +247,20 @@ async function setupPage(page: Page, scenario: SelectorScenario): Promise } async function evalSelector(page: Page, selCase: SelectorCase): Promise { - return await page.evaluate((t) => { - const root = - t.root?.kind === 'document' || !t.root ? document - : t.root.kind === 'id' ? document.getElementById(t.root.value) - : document.querySelector(t.root.value); - - if (!root) { - return { - exactMatch: false, - mismatchMsg: `Root element not found for selector: ${JSON.stringify(t.root)}`, - native: { count: 0, ids: [], threw: false }, - nw: { count: 0, ids: [], threw: false }, - }; - } - - let native: Element[] = []; - let nw: Element[] = []; - let nativeError = ''; - let nwError = ''; - - try { native = [...root.querySelectorAll(t.selector)]; } - catch (e) { nativeError = e instanceof Error ? e.message : String(e); } - - try { nw = NW.Dom.select(t.selector, root); } - catch (e) { nwError = e instanceof Error ? e.message : String(e); } - - if (nwError) { - return { - exactMatch: false, - mismatchMsg: nativeError && nwError - ? `Both threw:\n native error: ${nativeError}\n nw error: ${nwError}` - : `NW threw an error while native did not: ${nwError}`, - native: { count: 0, ids: [], threw: !!nativeError }, - nw: { count: 0, ids: [], threw: !!nwError }, - }; - } - - const nativeIds = native.map((el) => el.getAttribute('id') ?? ''); - const nwIds = nw.map((el) => el.getAttribute('id') ?? ''); - // const nativeIds = native.map((el) => el.id); - // const nwIds = nw.map((el: Element) => el.id); + return await page.evaluate((c) => { + const native: EngineResult = { count: 0, ids: [], classes: [], threw: false }; + const nw: EngineResult = { count: 0, ids: [], classes: [], threw: false }; + + type QueryResult = { elements: Element[]; error: string }; + const runQuery = (query: () => Element[]): QueryResult => { + try { + return { elements: query(), error: '' }; + } catch (e) { + return { elements: [], error: e instanceof Error ? e.message : String(e) }; + } + }; - let describe = (el: Element | undefined) => { + const describe = (el: Element | undefined) => { if (!el) return '(missing)'; return { tag: el.tagName.toLowerCase(), @@ -259,34 +270,95 @@ async function evalSelector(page: Page, selCase: SelectorCase): Promise { + if (a.result.error || b.result.error) { + if (a.result.error && b.result.error) return `Both threw:\n ${a.name} error: ${a.result.error}\n ${b.name} error: ${b.result.error}`; + return a.result.error + ? `${a.name} threw while ${b.name} did not: ${a.result.error}` + : `${b.name} threw while ${a.name} did not: ${b.result.error}`; + } + + const aElems = a.result.elements; + const bElems = b.result.elements; + + let mismatchMsg: string | undefined; + if (aElems.length !== bElems.length) { + mismatchMsg = `Count mismatch: ${a.name}=${aElems.length}, ${b.name}=${bElems.length}`; + } + + const maxLen = Math.max(aElems.length, bElems.length); + for (let i = 0; i < maxLen; ++i) { + if (aElems[i] !== bElems[i]) { + mismatchMsg = mismatchMsg ? mismatchMsg + '\n' : ''; + mismatchMsg += `Element mismatch at index ${i}:\n` + + `${a.name}[${i}] = ${JSON.stringify(describe(aElems[i]))}\n` + + `${b.name}[${i}] = ${JSON.stringify(describe(bElems[i]))}`; break; } } - } - return { - exactMatch, - mismatchMsg, - native: { count: native.length, ids: nativeIds, threw: !!nativeError }, - nw: { count: nw.length, ids: nwIds, threw: !!nwError }, + return mismatchMsg; }; + + const getRoot = (root: SelectorRoot | undefined): ParentNode | null => { + return !root || root.kind === 'document' ? document + : root.kind === 'id' ? document.getElementById(root.value) + : root.kind === 'selector' ? document.querySelector(root.value) + : null; // impossible case + } + + const root = getRoot(c.root); + + if (!root) { + return { + native, nw, + mismatchMsg: `Root element not found for selector: ${JSON.stringify(c.root)}`, + }; + } + + const nativeRes = runQuery(() => [...root.querySelectorAll(c.selector)]); + const nwRes = runQuery(() => NW.Dom.select(c.selector, root)); + [{ engine: native, res: nativeRes }, { engine: nw, res: nwRes }] + .forEach(({ engine, res }) => { + engine.count = res.elements.length; + engine.ids = res.elements.map((el) => el.getAttribute('id') ?? ''); + engine.classes = res.elements.map((el) => el.getAttribute('class') ?? ''); + engine.threw = !!res.error; + }); + + const mismatchMsg = compareQueryResults( + { name: 'native', result: nativeRes }, + { name: 'nw', result: nwRes }, + ); + + if (c.expect?.equivalentTo) { + const eqSelector = c.expect.equivalentTo.selector; + const eqRoot = getRoot(c.expect.equivalentTo.root); + + let eqNativeRes: QueryResult; + let eqNwRes: QueryResult; + if (!eqRoot) { + const error = `Equivalent selector root not found: ${JSON.stringify(c.expect.equivalentTo.root)}`; + eqNativeRes = { elements: [], error }; + eqNwRes = { elements: [], error }; + } else { + eqNativeRes = runQuery(() => [...eqRoot.querySelectorAll(eqSelector)]); + eqNwRes = runQuery(() => NW.Dom.select(eqSelector, eqRoot)); + } + + native.equivalentToFailMsg = compareQueryResults( + { name: `native(${c.selector})`, result: nativeRes }, + { name: `native(${eqSelector})`, result: eqNativeRes }, + ); + + nw.equivalentToFailMsg = compareQueryResults( + { name: `nw(${c.selector})`, result: nwRes }, + { name: `nw(${eqSelector})`, result: eqNwRes }, + ); + } + + return { mismatchMsg, native, nw }; }, selCase); } diff --git a/test_new/browser/jsvm.test.ts b/test_new/browser/jsvm.test.ts new file mode 100644 index 0000000..671a71e --- /dev/null +++ b/test_new/browser/jsvm.test.ts @@ -0,0 +1,27 @@ +import { runScenarios } from "./harness"; + +runScenarios('jsvm', 'normal', [ + { + name: 'load test selectors', + html: ` +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + `, + cases: [ + { selector: '#test1 div:not(:empty)', expect: { count: 4 } }, + { selector: '#test1 div:nth-child(even):empty', expect: { count: 2 } }, + ], + }, +]); \ No newline at end of file diff --git a/test_new/browser/prototype.test.ts b/test_new/browser/prototype.test.ts new file mode 100644 index 0000000..2ef074e --- /dev/null +++ b/test_new/browser/prototype.test.ts @@ -0,0 +1,488 @@ +import { runScenarios } from "./harness"; +import { expect } from "@playwright/test"; + +runScenarios('prototype 1', 'normal', [ + { + name: 'selector engine basics', + html: ` +
    +
    +
    +
    `, + cases: [ + { selector: '.test_class', expect: { count: 2, ids: ['test_div_parent', 'test_div_child'] } }, + { + selector: '.test_class', + root: { kind: 'id', value: 'test_div_parent' }, + expect: { count: 1, ids: ['test_div_child'] } + }, + { selector: '.non_existent', expect: { count: 0, ids: [] } }, + ], + }, +]); + +const nwsapiProtoTestHtml = ` + + + + Unit test file | Selector | default template | 2010-03-04 16:13 + + + + + + + + + + + +
    + + + + + + +`; + +runScenarios('prototype 2', 'normal', [ + { + name: 'standard prototype selectors', + html: nwsapiProtoTestHtml, + htmlMode: 'document', + cases: [ + // testSelectorWithTagName + { selector: 'li' }, + { selector: 'strong', expect: { ids: ['strong'] } }, + { selector: 'nonexistent', expect: { count: 0, ids: [] } }, + { selector: '*' }, + + // testSelectorWithId + { selector: '#fixtures', expect: { ids: ['fixtures'] } }, + { selector: '#nonexistent', expect: { count: 0, ids: [] } }, + { selector: '#troubleForm', expect: { ids: ['troubleForm'] } }, + + // testSelectorWithClassName + { selector: '.first', expect: { ids: ['p', 'link_1', 'item_1'] } }, + { selector: '.second', expect: { count: 0, ids: [] } }, + + // testSelectorWithTagNameAndId + { selector: 'strong#strong', expect: { ids: ['strong'] } }, + { selector: 'p#strong', expect: { count: 0, ids: [] } }, + + // testSelectorWithTagNameAndClassName + { selector: 'a.internal', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'a.internal.highlight', expect: { ids: ['link_2'] } }, + { selector: 'a.highlight.internal', expect: { ids: ['link_2'] } }, + { selector: 'a.highlight.internal.nonexistent', expect: { count: 0, ids: [] } }, + + // testSelectorWithIdAndClassName + { selector: '#link_2.internal', expect: { ids: ['link_2'] } }, + { selector: '.internal#link_2', expect: { ids: ['link_2'] } }, + { selector: '#link_2.internal.highlight', expect: { ids: ['link_2'] } }, + { selector: '#link_2.internal.nonexistent', expect: { count: 0, ids: [] } }, + + // testSelectorWithTagNameAndIdAndClassName + { selector: 'a#link_2.internal', expect: { ids: ['link_2'] } }, + { selector: 'a.internal#link_2', expect: { ids: ['link_2'] } }, + { selector: 'li#item_1.first', expect: { ids: ['item_1'] } }, + { selector: 'li#item_1.nonexistent', expect: { count: 0, ids: [] } }, + { selector: 'li#item_1.first.nonexistent', expect: { count: 0, ids: [] } }, + + // test$$MatchesAncestryWithTokensSeparatedByWhitespace + { selector: '#fixtures a *', expect: { ids: ['em2', 'em', 'span'] } }, + { selector: 'div#fixtures p', expect: { ids: ['p'] } }, + + // test$$CombinesResultsWhenMultipleExpressionsArePassed + { selector: '#p a, ul#list li', expect: { ids: ['link_1', 'link_2', 'item_1', 'item_2', 'item_3'] } }, + + // testSelectorWithTagNameAndAttributeExistence + { selector: 'h1[class]', expect: { equivalentTo: { selector: '#fixtures h1' } } }, + { selector: 'h1[CLASS]', expect: { equivalentTo: { selector: '#fixtures h1' } } }, + { selector: 'li#item_3[class]', expect: { ids: ['item_3'] } }, + + // testSelectorWithTagNameAndSpecificAttributeValue + { selector: 'a[href="#"]', expect: { ids: ['link_1', 'link_2', 'link_3'] } }, + { selector: "a[href='#']", expect: { ids: ['link_1', 'link_2', 'link_3'] } }, + + // testSelectorWithTagNameAndWhitespaceTokenizedAttributeValue + { selector: 'a[class~="internal"]', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'a[class~=internal]', expect: { ids: ['link_1', 'link_2'] } }, + + // testSelectorWithAttributeAndNoTagName + { selector: '[href]', root: { kind: 'selector', value: 'body' }, expect: { equivalentTo: { selector: 'a[href]' } } }, + { selector: '[class~=internal]', expect: { equivalentTo: { selector: 'a[class~="internal"]' } } }, + { selector: '[id]', expect: { equivalentTo: { selector: '*[id]' } } }, + { selector: '[type=radio]', expect: { ids: ['checked_radio', 'unchecked_radio'] } }, + { selector: '[type=checkbox]', expect: { equivalentTo: { selector: '*[type=checkbox]' } } }, + { selector: '[title]', expect: { ids: ['with_title', 'commaParent'] } }, + { selector: '#troubleForm [type=radio]', expect: { equivalentTo: { selector: '#troubleForm *[type=radio]' } } }, + { selector: '#troubleForm [type]', expect: { equivalentTo: { selector: '#troubleForm *[type]' } } }, + + // testSelectorWithAttributeContainingDash + { selector: '[foo-bar]', expect: { ids: ['attr_with_dash'] } }, // attribute with hyphen + + // testSelectorWithUniversalAndHyphenTokenizedAttributeValue + { selector: '*[xml:lang|="es"]', expect: { ids: ['item_3'] } }, + { selector: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] } }, + + // testSelectorWithTagNameAndNegatedAttributeValue + { selector: 'a:not([href="#"])', expect: { count: 0, ids: [] } }, + + // testSelectorWithBracketAttributeValue + { selector: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, + { selector: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, + { selector: '#troubleForm2 input[name="brackets[5][]"][value="2"]', expect: { ids: ['chk_2'] } }, + { selector: '#troubleForm2 input[name=brackets\\[5\\]\\[\\]]', expect: { equivalentTo: { selector: '#troubleForm2 input[name="brackets[5][]"]' }, count: 2 } }, + + // test$$WithNestedAttributeSelectors + { selector: 'div[style] p[id] strong', expect: { ids: ['strong'] } }, + + // testSelectorWithMultipleConditions + { selector: 'a[class~=external][href="#"]', expect: { ids: ['link_3'] } }, + { selector: 'a[class~=external]:not([href="#"])', expect: { count: 0, ids: [] } }, + + // derived from testSelectorMatchElements + { selector: '#list li', expect: { ids: ['item_1', 'item_2', 'item_3'] } }, + { selector: '#fixtures a.internal', expect: { ids: ['link_1', 'link_2'] } }, + { selector: '#fixtures p.last', expect: { count: 0, ids: [] } }, + { selector: '#fixtures .inexistant, #fixtures a.internal', expect: { ids: ['link_1', 'link_2'] } }, + + // derived from testSelectorFindElement + { selector: '#list li', expect: { ids: ['item_1', 'item_2', 'item_3'] } }, + { selector: '#list li#item_3', expect: { ids: ['item_3'] } }, + { selector: '#list em', expect: { count: 0, ids: [] } }, + + // derived from testElementMatch + { selector: 'span', expect: { includesIds: ['dupL1'] } }, + { selector: 'span#dupL1', expect: { includesIds: ['dupL1'] } }, + { selector: 'div > span', expect: { includesIds: ['dupL1'] } }, // child combinator + { selector: '#dupContainer span', expect: { includesIds: ['dupL1'] } }, // descendant combinator + { selector: '#dupL1', expect: { includesIds: ['dupL1'] } }, // ID only + { selector: 'span.span_foo', expect: { includesIds: ['dupL1'] } }, // class name 1 + { selector: 'span.span_bar', expect: { includesIds: ['dupL1'] } }, // class name 2 + { selector: 'span:first-child', expect: { includesIds: ['dupL1'] } }, // first-child pseudoclass + + { selector: 'span.span_wtf', expect: { excludesIds: ['dupL1'] } }, // bogus class name + { selector: '#dupL2', expect: { excludesIds: ['dupL1'] } }, // different ID + { selector: 'div', expect: { excludesIds: ['dupL1'] } }, // different tag name + { selector: 'span span', expect: { excludesIds: ['dupL1'] } }, // different ancestry + { selector: 'span > span', expect: { excludesIds: ['dupL1'] } }, // different parent + { selector: 'span:nth-child(5)', expect: { excludesIds: ['dupL1'] } }, // different pseudoclass + + { selector: 'a[rel^=external]', expect: { includesIds: ['link_1'], excludesIds: ['link_2'] } }, + { selector: 'a[rel^="external"]', expect: { includesIds: ['link_1'] } }, + { selector: "a[rel^='external']", expect: { includesIds: ['link_1'] } }, + + // testSelectorWithSpaceInAttributeValue + { selector: 'cite[title="hello world!"]', expect: { ids: ['with_title'] } }, + + // testSelectorWithNamespacedAttributes + { + selector: '[xml:lang]', + expect: { + allowMismatch: true, // some browsers don't support selecting by namespaced attributes + count: 2, + includesIds: ['item_3'], + equivalentTo: { selector: '*[xml:lang]' } + } + }, + + // testSelectorWithChild + { selector: 'p.first > a', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'div#grandfather > div', expect: { ids: ['father', 'uncle'] } }, + { selector: '#level1>span', expect: { ids: ['level2_1', 'level2_2'] } }, + { selector: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, + { selector: '#level2_1 > *', expect: { ids: ['level3_1', 'level3_2'] } }, + { selector: 'div > #nonexistent', expect: { count: 0, ids: [] } }, + { selector: '#level1 > span' }, + + // testSelectorWithAdjacence + { selector: 'div.brothers + div.brothers', expect: { ids: ['uncle'] } }, + { selector: 'div.brothers + div', expect: { ids: ['uncle'] } }, + { selector: '#level2_1+span', expect: { ids: ['level2_2'] } }, + { selector: '#level2_1 + span', expect: { ids: ['level2_2'] } }, + { selector: '#level2_1 + *', expect: { ids: ['level2_2'] } }, + { selector: '#level2_2 + span', expect: { count: 0, ids: [] } }, + { selector: '#level3_1 + span', expect: { ids: ['level3_2'] } }, + { selector: '#level3_1 + *', expect: { ids: ['level3_2'] } }, + { selector: '#level3_2 + *', expect: { count: 0, ids: [] } }, + { selector: '#level3_1 + em', expect: { count: 0, ids: [] } }, + + // testSelectorWithLaterSibling + { selector: 'h1 ~ ul', expect: { ids: ['list'] } }, + { selector: '#level2_1 ~ span', expect: { ids: ['level2_2'] } }, + { selector: '#level2_1 ~ *', expect: { ids: ['level2_2', 'level2_3'] } }, + { selector: '#level2_2 ~ span', expect: { count: 0, ids: [] } }, + { selector: '#level3_2 ~ *', expect: { count: 0, ids: [] } }, + { selector: '#level3_1 ~ em', expect: { count: 0, ids: [] } }, + { selector: '#level3_1 ~ #level3_2', expect: { ids: ['level3_2'] } }, + { selector: 'span ~ #level3_2', expect: { ids: ['level3_2'] } }, + { selector: 'div ~ #level3_2', expect: { count: 0, ids: [] } }, + { selector: 'div ~ #level2_3', expect: { count: 0, ids: [] } }, + + // testSelectorWithNewAttributeOperators + { selector: 'div[class^=bro]', expect: { ids: ['father', 'uncle'] } }, // matching beginning of string + { selector: 'div[class$=men]', expect: { ids: ['father', 'uncle'] } }, // matching end of string + { selector: 'div[class*="ers m"]', expect: { ids: ['father', 'uncle'] } }, // matching substring + { selector: '#level1 *[id^="level2_"]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + { selector: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + { selector: '#level1 *[id$="_1"]', expect: { ids: ['level2_1', 'level3_1'] } }, + { selector: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, + { selector: '#level1 *[id*="2"]', expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, + { selector: "#level1 *[id*='2']", expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, + + // benchmark(function() { $$('#level1 *[id^=level2_]') }, 1000, '[^=]') + { selector: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + // benchmark(function() { $$('#level1 *[id$=_1]') }, 1000, '[$=]') + { selector: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, + // benchmark(function() { $$('#level1 *[id*=_2]') }, 1000, '[*=]') + { selector: '#level1 *[id*=_2]', expect: { ids: ['level3_2', 'level2_2'] } }, + + // testSelectorWithDuplicates + { selector: 'div div' }, + { selector: '#dupContainer span span', expect: { ids: ['dupL2', 'dupL3', 'dupL4', 'dupL5'] } }, + + // benchmark(function() { $$('#dupContainer span span') }, 1000) + { selector: '#dupContainer span span', expect: { ids: ['dupL2', 'dupL3', 'dupL4', 'dupL5'] } }, + + // testSelectorWithFirstLastOnlyNthNthLastChild + { selector: '#level1>*:first-child', expect: { ids: ['level2_1'] } }, + { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + { selector: '#level1>*:last-child', expect: { ids: ['level2_3'] } }, + { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + { selector: '#level1>div:last-child', expect: { ids: ['level2_3'] } }, + { selector: '#level1 div:last-child', expect: { ids: ['level2_3'] } }, + { selector: '#level1>div:first-child', expect: { count: 0, ids: [] } }, + { selector: '#level1>span:last-child', expect: { count: 0, ids: [] } }, + { selector: '#level1 span:first-child', expect: { ids: ['level2_1', 'level3_1'] } }, + { selector: '#level1:first-child', expect: { count: 0, ids: [] } }, + { selector: '#level1>*:only-child', expect: { count: 0, ids: [] } }, + { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + { selector: '#level1:only-child', expect: { count: 0, ids: [] } }, + { selector: '#p *:nth-last-child(2)', expect: { ids: ['link_2'] } }, // nth-last-child + { selector: '#p *:nth-child(3)', expect: { ids: ['link_2'] } }, // nth-child + { selector: '#p a:nth-child(3)', expect: { ids: ['link_2'] } }, // nth-child + { selector: '#list > li:nth-child(n+2)', expect: { ids: ['item_2', 'item_3'] } }, + { selector: '#list > li:nth-child(-n+2)', expect: { ids: ['item_1', 'item_2'] } }, + + // benchmark(function() { $$('#level1 *:first-child') }, 1000, ':first-child') + { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + // benchmark(function() { $$('#level1 *:last-child') }, 1000, ':last-child') + { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + // benchmark(function() { $$('#level1 *:only-child') }, 1000, ':only-child') + { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + + // testSelectorWithFirstLastNthNthLastOfType + { selector: '#p a:nth-of-type(2)', expect: { ids: ['link_2'] } }, // nth-of-type + { selector: '#p a:nth-of-type(1)', expect: { ids: ['link_1'] } }, // nth-of-type + { selector: '#p a:nth-last-of-type(1)', expect: { ids: ['link_2'] } }, // nth-last-of-type + { selector: '#p a:first-of-type', expect: { ids: ['link_1'] } }, // first-of-type + { selector: '#p a:last-of-type', expect: { ids: ['link_2'] } }, // last-of-type + + // testSelectorWithNot + { selector: '#p a:not(:first-of-type)', expect: { ids: ['link_2'] } }, // first-of-type + { selector: '#p a:not(:last-of-type)', expect: { ids: ['link_1'] } }, // last-of-type + { selector: '#p a:not(:nth-of-type(1))', expect: { ids: ['link_2'] } }, // nth-of-type + { selector: '#p a:not(:nth-last-of-type(1))', expect: { ids: ['link_1'] } }, // nth-last-of-type + { selector: '#p a:not([rel~=nofollow])', expect: { ids: ['link_2'] } }, // attribute 1 + { selector: '#p a:not([rel^=external])', expect: { ids: ['link_2'] } }, // attribute 2 + { selector: '#p a:not([rel$=nofollow])', expect: { ids: ['link_2'] } }, // attribute 3 + { selector: '#p a:not([rel$="nofollow"]) > em', expect: { ids: ['em'] } }, // attribute 4 + { selector: '#list li:not(#item_1):not(#item_3)', expect: { ids: ['item_2'] } }, // adjacent :not clauses + { selector: '#grandfather > div:not(#uncle) #son', expect: { ids: ['son'] } }, + { selector: '#p a:not([rel$="nofollow"]) em', expect: { ids: ['em'] } }, // attribute 4 + all descendants + { selector: '#p a:not([rel$="nofollow"])>em', expect: { ids: ['em'] } }, // attribute 4 (without whitespace) + + // testSelectorWithEnabledDisabledChecked + { selector: '#troubleForm > *:disabled', expect: { ids: ['disabled_text_field'] } }, + { selector: '#troubleForm > *:enabled', expect: { ids: ['hidden', 'enabled_text_field', 'checked_box', 'unchecked_box', 'checked_radio', 'unchecked_radio'] } }, + { selector: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, + + // testIdenticalResultsFromEquivalentSelectors + { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers]' } } }, + { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers].brothers' } } }, + { selector: 'div:not(.brothers)', expect: { equivalentTo: { selector: 'div:not([class~=brothers])' } } }, + { selector: 'li ~ li', expect: { equivalentTo: { selector: 'li:not(:first-child)' } } }, + { selector: 'ul > li', expect: { equivalentTo: { selector: 'ul > li:nth-child(n)' } } }, + { selector: 'ul > li:nth-child(even)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n)' } } }, + { selector: 'ul > li:nth-child(odd)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n+1)' } } }, + { selector: 'ul > li:first-child', expect: { equivalentTo: { selector: 'ul > li:nth-child(1)' } } }, + { selector: 'ul > li:last-child', expect: { equivalentTo: { selector: 'ul > li:nth-last-child(1)' } } }, + { selector: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { selector: 'ul > li' } } }, + { selector: 'ul > li:nth-child(n-999)', expect: { equivalentTo: { selector: 'ul > li' } } }, + { selector: 'ul>li', expect: { equivalentTo: { selector: 'ul > li' } } }, + { selector: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { selector: '#p a:not([rel$="nofollow"]) > em' } } }, + + // testSelectorsThatShouldReturnNothing + { selector: 'span:empty > *', expect: { count: 0, ids: [] } }, + { selector: 'div.brothers:not(.brothers)', expect: { count: 0, ids: [] } }, + { selector: '#level2_2 :only-child:not(:last-child)', expect: { count: 0, ids: [] } }, + { selector: '#level2_2 :only-child:not(:first-child)', expect: { count: 0, ids: [] } }, + + // testCommasFor$$ + { selector: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { selector: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + + // testElementDownWithDotAndColon + { selector: '#dupContainer\\.withdot\\:active #dupL4_dotcolon', expect: { ids: ['dupL4_dotcolon'] } } + ], + }, + + { + name: 'empty pseudo after mutation', + htmlMode: 'document', + html: nwsapiProtoTestHtml, + setupPage: async (page) => { + await page.evaluate(() => { + const el = document.getElementById('level3_1'); + if (el) el.innerHTML = ''; + }); + }, + cases: [ + // testSelectorWithEmpty + { selector: '#level1 *:empty', expect: { ids: ['level3_1', 'level3_2', 'level2_3'] } }, + { selector: '#level_only_child:empty', expect: { count: 0, ids: [] } }, // newlines count as content + ], + }, + + { + name: 'selectors on detached nodes', + html: '', + cases: [], + setupPage: async (page) => { + const result = await page.evaluate(() => { + const wrapper = document.createElement('div'); + wrapper.innerHTML = "
    "; + + return { + byAttr: NW.Dom.select('[id=myTD]', wrapper).map(el => el.getAttribute('id')), + byTag: NW.Dom.select('td', wrapper).map(el => el.getAttribute('id')), + byId: NW.Dom.select('#myTD', wrapper).map(el => el.getAttribute('id')), + }; + }); + + expect(result.byAttr).toEqual(['myTD']); + expect(result.byTag).toEqual(['myTD']); + expect(result.byId).toEqual(['myTD']); + }, + }, + + { + name: 'descendant selector on dynamic subtree', + html: '', + cases: [], + setupPage: async (page) => { + const result = await page.evaluate(() => { + const el = document.createElement('div'); + el.innerHTML = '
    '; + document.body.appendChild(el); + + const nativeCount = el.querySelectorAll('ul li').length; + const nwCount = NW.Dom.select('ul li', el).length; + + document.body.removeChild(el); + return { nativeCount, nwCount }; + }); + + expect(result.nativeCount).toBe(2); + expect(result.nwCount).toBe(2); + }, + }, +]); \ No newline at end of file diff --git a/test_new/browser/quirks.test.ts b/test_new/browser/quirks.test.ts new file mode 100644 index 0000000..138f8eb --- /dev/null +++ b/test_new/browser/quirks.test.ts @@ -0,0 +1,61 @@ +import { runScenarios } from "./harness"; + +runScenarios('quirks', 'normal', [ + { + name: 'class token matching in standard mode', + htmlMode: 'document', + html: ` + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + `, + cases: [ + { selector: '#dom [class~=foobar]', expect: { ids: ['d1'] } }, + { selector: '#qsa [class~=foobar]', expect: { ids: ['q1'] } }, + { selector: '#dom [class~=foobar i]', expect: { ids: ['d1', 'd2', 'd3'] } }, + { selector: '#qsa [class~=foobar i]', expect: { ids: ['q1', 'q2', 'q3'] } }, + ], + }, + { + name: 'class token matching in quirks mode', + htmlMode: 'document', + html: ` + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + `, + cases: [ + { selector: '#dom [class~=foobar]', expect: { ids: ['d1'] } }, + { selector: '#qsa [class~=foobar]', expect: { ids: ['q1'] } }, + { selector: '#dom [class~=foobar i]', expect: { ids: ['d1', 'd2', 'd3'] } }, + { selector: '#qsa [class~=foobar i]', expect: { ids: ['q1', 'q2', 'q3'] } }, + ], + } +]); diff --git a/test_new/browser/scope.test.ts b/test_new/browser/scope.test.ts new file mode 100644 index 0000000..2d92426 --- /dev/null +++ b/test_new/browser/scope.test.ts @@ -0,0 +1,128 @@ +import { runScenarios } from "./harness"; + +runScenarios('scope', 'normal', [ + { + name: 'scope-01a', + html: ` + + `, + cases: [ + { selector: 'ul a', expect: { count: 2 } }, + { selector: '#scope', expect: { count: 1 } }, + { selector: 'a', root: { kind: 'id', value: 'scope' }, expect: { count: 1 } }, + ], + }, + + { + name: 'scope-01b', + html: ` + + `, + cases: [ + { selector: '#scope', expect: { count: 1 } }, + { selector: ':scope ul a', root: { kind: 'id', value: 'scope' }, expect: { count: 0, ids: [] } }, + { selector: ':scope body ul a', root: { kind: 'id', value: 'scope' }, expect: { count: 0, ids: [] } }, + { selector: ':scope a', root: { kind: 'id', value: 'scope' }, expect: { count: 1 } }, + ], + }, + + { + name: 'scope-02a/02b', + html: ` +
    +
    +
    +
    + `, + cases: [ + { selector: '.a', expect: { count: 1 } }, + { selector: 'body div', root: { kind: 'selector', value: '.a' }, expect: { count: 2 } }, + { selector: ':scope body div', root: { kind: 'selector', value: '.a' }, expect: { count: 0, ids: [] } }, + ], + }, + + { + name: 'scope-03a', + html: ` +
    +

    + hello +

    +
    + `, + cases: [ + { selector: 'div', expect: { count: 1 } }, + { selector: ':scope > p', root: { kind: 'selector', value: 'div' }, expect: { count: 1 } }, + { selector: ':scope > span', root: { kind: 'selector', value: 'div' }, expect: { count: 0, ids: [] } }, + ], + }, + + { + name: 'scope-03b', + html: ` +
    +
    +
    +
    + `, + cases: [ + { selector: 'body div', root: { kind: 'selector', value: '.a' }, expect: { count: 2 } }, + { selector: ':scope body div', root: { kind: 'selector', value: '.a' }, expect: { count: 0, ids: [] } }, + { selector: ':scope > .a1, :scope > .a2', root: { kind: 'selector', value: '.a' }, expect: { count: 2 } }, + ], + }, + + { + name: 'scope-04a', + modifier: 'fixme', + htmlMode: 'document', + html: ` + + + + test nwsapi + + + + + +
    + + + `, + cases: [ + { selector: ':scope > [data-test="foo"]', root: { kind: 'selector', value: 'body' }, expect: { count: 1 } }, + ], + }, + + { + name: 'scope-04b', + modifier: 'fixme', + html: ` +
    +
    +
    +
    +
    +
    + `, + cases: [ + { + selector: ':scope > div', + root: { kind: 'selector', value: 'div' }, + expect: { + classes: ['outer', 'other-outer'] + }, + }, + ], + }, + +]); \ No newline at end of file diff --git a/test_new/browser/scotch.test.ts b/test_new/browser/scotch.test.ts new file mode 100644 index 0000000..2f18333 --- /dev/null +++ b/test_new/browser/scotch.test.ts @@ -0,0 +1,472 @@ +import { Page } from "playwright/test"; +import { runScenarios } from "./harness"; + +const html = ` + + + + + NWSAPI Tests + + + + + + +
    +
    + + +
    + + `; + +const setupNw = async (page: Page) => { + await page.evaluate(() => { + NW.Dom.configure({ + 'SIMPLENOT': true, + 'VERBOSITY': true + }); + }); +} + +runScenarios('scotch', 'normal', [ + { + name: 'Basic Selectors', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + // * — Universal selector + { selector: '*' }, + + // E — Type selector + { selector: 'li' }, + { selector: 'strong', root: { kind: 'id', value: 'fixtures' }, expect: { ids: ['strong'] } }, + { selector: 'nonexistent', expect: { count: 0 } }, + + // #id — ID selector + { selector: '#fixtures', expect: { ids: ['fixtures'] } }, + { selector: 'nonexistent', expect: { count: 0 } }, + { selector: '#troubleForm', expect: { ids: ['troubleForm'] } }, + + // .class — Class selector + { selector: '.first', expect: { ids: ['p', 'link_1', 'item_1'] } }, + { selector: '.second', expect: { count: 0 } }, + + // E#id + { selector: 'strong#strong', expect: { ids: ['strong'] } }, + { selector: 'p#strong', expect: { count: 0 } }, + + // E.class + { selector: 'a.internal', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'a.internal.highlight', expect: { ids: ['link_2'] } }, + { selector: 'a.highlight.internal', expect: { ids: ['link_2'] } }, + { selector: 'a.highlight.internal.nonexistent', expect: { count: 0 } }, + + // #id.class + { selector: '#link_2.internal', expect: { ids: ['link_2'] } }, + { selector: '.internal#link_2', expect: { ids: ['link_2'] } }, + { selector: '#link_2.internal.highlight', expect: { ids: ['link_2'] } }, + { selector: '#link_2.internal.nonexistent', expect: { count: 0 } }, + + // E#id.class + { selector: 'a#link_2.internal', expect: { ids: ['link_2'] } }, + { selector: 'a.internal#link_2', expect: { ids: ['link_2'] } }, + { selector: 'li#item_1.first', expect: { ids: ['item_1'] } }, + { selector: 'li#item_1.nonexistent', expect: { count: 0 } }, + { selector: 'li#item_1.first.nonexistent', expect: { count: 0 } }, + ], + }, + { + name: 'Attribute Selectors', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + // [foo] + { selector: '[href]', root: { kind: 'selector', value: 'body' }, expect: { equivalentTo: { selector: 'a[href]', root: { kind: 'selector', value: 'body' } } } }, + { selector: '[class~=internal]', expect: { equivalentTo: { selector: 'a[class~="internal"]' } } }, + { selector: '[id]', expect: { equivalentTo: { selector: '*[id]' } } }, + { selector: '[type=radio]', expect: { ids: ['checked_radio', 'unchecked_radio'] } }, + { selector: '[type=checkbox]', expect: { equivalentTo: { selector: '*[type=checkbox]' } } }, + { selector: '[title]', expect: { ids: ['with_title', 'commaParent'] } }, + { selector: '#troubleForm [type=radio]', expect: { equivalentTo: { selector: '#troubleForm *[type=radio]' } } }, + { selector: '#troubleForm [type]', expect: { equivalentTo: { selector: '#troubleForm *[type]' } } }, + + // E[foo] + { selector: 'h1[class]', expect: { equivalentTo: { selector: '#fixtures h1' } } }, + { selector: 'h1[CLASS]', expect: { equivalentTo: { selector: '#fixtures h1' } } }, + { selector: 'li#item_3[class]', expect: { ids: ['item_3'] } }, + { selector: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, + { selector: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, + { selector: 'cite[title="hello world!"]', expect: { ids: ['with_title'] } }, + { selector: '[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'], equivalentTo: { selector: '*[xml:lang]' } } }, + { selector: '*[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'] } }, + + // E[foo="bar"] + { selector: 'a[href="#"]', expect: { ids: ['link_1', 'link_2', 'link_3'] } }, + { selector: 'a[href=#]', expect: { throws: true } }, + { selector: '#troubleForm2 input[name="brackets[5][]"][value="2"]', expect: { ids: ['chk_2'] } }, + + // E[foo~="bar"] + { selector: 'a[class~="internal"]', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'a[class~=internal]', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'a[class~=external][href="#"]', expect: { ids: ['link_3'] } }, + + // E[foo|="en"] + { selector: '*[xml:lang|="es"]', expect: { ids: ['item_3'] } }, + { selector: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] } }, + + // E[foo^="bar"] + { selector: 'div[class^=bro]', expect: { ids: ['father', 'uncle'] } }, + { selector: '#level1 *[id^="level2_"]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + { selector: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + + // benchmark(function(){ select('#level1 *[id^=level2_]'); }, 1000) + { selector: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + + // E[foo$="bar"] + { selector: 'div[class$=men]', expect: { ids: ['father', 'uncle'] } }, + { selector: '#level1 *[id$="_1"]', expect: { ids: ['level2_1', 'level3_1'] } }, + { selector: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, + + // benchmark(function(){ select('#level1 *[id$=_1]'); }, 1000) + { selector: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, + + // E[foo*="bar"] + { selector: 'div[class*="ers m"]', expect: { ids: ['father', 'uncle'] } }, + { selector: '#level1 *[id*="2"]', expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, + { selector: '#level1 *[id*=2]', expect: { throws: true } }, + // benchmark(function(){ select('#level1 *[id*=2]'); }, 1000) + { selector: '#level1 *[id*=2]', expect: { throws: true } }, + + // E[id=-1] — should throw SYNTAX_ERR + // { selector: '#level1 *[id=-1]', expect: { throws: true }, }, + { selector: '#level1 *[id=9]', expect: { throws: true } }, + + // E[class=-45deg] — should throw SYNTAX_ERR + // { selector: '#level1 *[class=-45deg]', expect: { throws: true } }, + + // E[class=8mm] — should throw SYNTAX_ERR + { selector: '#level1 *[class=8mm]', expect: { throws: true } }, + ], + }, + + { + name: 'Attribute Selectors — invalid unquoted values', + html, + htmlMode: 'document', + setupPage: setupNw, + modifier: 'fixme', + cases: [ + // should throw SYNTAX_ERR + { selector: '#level1 *[id=-1]', expect: { throws: true } }, + { selector: '#level1 *[id=9]', expect: { throws: true } }, + { selector: '#level1 *[class=-45deg]', expect: { throws: true } }, + { selector: '#level1 *[class=8mm]', expect: { throws: true } }, + ], + }, + + { + name: 'Structural pseudo-classes', + html, + setupPage: setupNw, + cases: [ + // E:first-child + { selector: '#level1>*:first-child', expect: { ids: ['level2_1'] } }, + { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + { selector: '#level1>div:first-child', expect: { count: 0 } }, + { selector: '#level1 span:first-child', expect: { ids: ['level2_1', 'level3_1'] } }, + { selector: '#level1:first-child', expect: { count: 0 } }, + + // benchmark(function(){ select('#level1 *:first-child'); }, 1000) + { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + + // E:last-child + { selector: '#level1>*:last-child', expect: { ids: ['level2_3'] } }, + { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + { selector: '#level1>div:last-child', expect: { ids: ['level2_3'] } }, + { selector: '#level1 div:last-child', expect: { ids: ['level2_3'] } }, + { selector: '#level1>span:last-child', expect: { count: 0 } }, + + // benchmark(function(){ select('#level1 *:last-child'); }, 1000) + { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + + // E:nth-child(n) + { selector: '#p *:nth-child(3)', expect: { ids: ['link_2'] } }, + { selector: '#p a:nth-child(3)', expect: { ids: ['link_2'] } }, + { selector: '#list > li:nth-child(n+2)', expect: { ids: ['item_2', 'item_3'] } }, + { selector: '#list > li:nth-child(-n+2)', expect: { ids: ['item_1', 'item_2'] } }, + + // E:nth-of-type(n) + { selector: '#p a:nth-of-type(2)', expect: { ids: ['link_2'] } }, + { selector: '#p a:nth-of-type(1)', expect: { ids: ['link_1'] } }, + + // E:nth-last-of-type(n) + { selector: '#p a:nth-last-of-type(1)', expect: { ids: ['link_2'] } }, + + // E:first-of-type + { selector: '#p a:first-of-type', expect: { ids: ['link_1'] } }, + + // E:last-of-type + { selector: '#p a:last-of-type', expect: { ids: ['link_2'] } }, + + // E:only-child + { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + { selector: '#level1>*:only-child', expect: { count: 0 } }, + { selector: '#level1:only-child', expect: { count: 0 } }, + { selector: '#level2_2 :only-child:not(:last-child)', expect: { count: 0 } }, + { selector: '#level2_2 :only-child:not(:first-child)', expect: { count: 0 } }, + + // benchmark(function(){ select('#level1 *:only-child'); }, 1000) + { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + ], + }, + + { + name: 'Structural pseudo-classes — E:empty', + html, + htmlMode: 'document', + setupPage: async (page) => { + await setupNw(page); + await page.evaluate(() => { + const el = document.getElementById('level3_1'); + if (el) el.innerHTML = ''; + }); + }, + cases: [ + // E:empty + { selector: '#level1 *:empty', expect: { ids: ['level3_1', 'level3_2', 'level2_3'] } }, + { selector: '#level_only_child:empty', expect: { count: 0 } }, + { selector: 'span:empty > *', expect: { count: 0 } }, + ], + }, + + { + name: 'E:not(s)', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + { selector: 'a:not([href="#"])', expect: { count: 0 } }, + { selector: 'div.brothers:not(.brothers)', expect: { count: 0 } }, + { selector: 'a[class~=external]:not([href="#"])', expect: { count: 0 } }, + { selector: '#p a:not(:first-of-type)', expect: { ids: ['link_2'] } }, + { selector: '#p a:not(:last-of-type)', expect: { ids: ['link_1'] } }, + { selector: '#p a:not(:nth-of-type(1))', expect: { ids: ['link_2'] } }, + { selector: '#p a:not(:nth-last-of-type(1))', expect: { ids: ['link_1'] } }, + { selector: '#p a:not([rel~=nofollow])', expect: { ids: ['link_2'] } }, + { selector: '#p a:not([rel^=external])', expect: { ids: ['link_2'] } }, + { selector: '#p a:not([rel$=nofollow])', expect: { ids: ['link_2'] } }, + { selector: '#p a:not([rel$="nofollow"]) > em', expect: { ids: ['em'] } }, + { selector: '#list li:not(#item_1):not(#item_3)', expect: { ids: ['item_2'] } }, + { selector: '#grandfather > div:not(#uncle) #son', expect: { ids: ['son'] } }, + { selector: '#p a:not([rel$="nofollow"]) em', expect: { ids: ['em'] } }, + { selector: '#p a:not([rel$="nofollow"])>em', expect: { ids: ['em'] } }, + ], + }, + + { + name: 'UI element states pseudo-classes', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + // E:disabled + { selector: '#troubleForm > p > *:disabled', expect: { ids: ['disabled_text_field'] } }, + + // E:checked + { selector: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, + ], + }, + + { + name: 'Combinators', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + // E F — Descendant + { selector: '#fixtures a *', expect: { ids: ['em2', 'em', 'span'] } }, + { selector: 'div#fixtures p', expect: { includesIds: ['p'] } }, + + // E + F — Adjacent sibling + { selector: 'div.brothers + div.brothers', expect: { includesIds: ['uncle'] } }, + { selector: 'div.brothers + div', expect: { includesIds: ['uncle'] } }, + { selector: '#level2_1+span', expect: { includesIds: ['level2_2'] } }, + { selector: '#level2_1 + span', expect: { includesIds: ['level2_2'] } }, + { selector: '#level2_1 + *', expect: { includesIds: ['level2_2'] } }, + { selector: '#level2_2 + span', expect: { count: 0 } }, + { selector: '#level3_1 + span', expect: { includesIds: ['level3_2'] } }, + { selector: '#level3_1 + *', expect: { includesIds: ['level3_2'] } }, + { selector: '#level3_2 + *', expect: { count: 0 } }, + { selector: '#level3_1 + em', expect: { count: 0 } }, + + // benchmark(function(){ select('#level3_1 + span'); }, 1000) + { selector: '#level3_1 + span', expect: { includesIds: ['level3_2'] } }, + + // E > F — Child + { selector: 'p.first > a', expect: { ids: ['link_1', 'link_2'] } }, + { selector: 'div#grandfather > div', expect: { ids: ['father', 'uncle'] } }, + { selector: '#level1>span', expect: { ids: ['level2_1', 'level2_2'] } }, + { selector: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, + { selector: '#level2_1 > *', expect: { ids: ['level3_1', 'level3_2'] } }, + { selector: 'div > #nonexistent', expect: { count: 0 } }, + + // benchmark(function(){ select('#level1 > span'); }, 1000) + { selector: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, + + // E ~ F — General sibling + { selector: 'h1 ~ ul', expect: { includesIds: ['list'] } }, + { selector: '#level2_2 ~ span', expect: { count: 0 } }, + { selector: '#level3_2 ~ *', expect: { count: 0 } }, + { selector: '#level3_1 ~ em', expect: { count: 0 } }, + { selector: 'div ~ #level3_2', expect: { count: 0 } }, + { selector: 'div ~ #level2_3', expect: { count: 0 } }, + { selector: '#level2_1 ~ span', expect: { includesIds: ['level2_2'] } }, + { selector: '#level2_1 ~ *', expect: { ids: ['level2_2', 'level2_3'] } }, + { selector: '#level3_1 ~ #level3_2', expect: { includesIds: ['level3_2'] } }, + { selector: 'span ~ #level3_2', expect: { includesIds: ['level3_2'] } }, + + // benchmark(function(){ select('#level2_1 ~ span'); }, 1000) + { selector: '#level2_1 ~ span', expect: { includesIds: ['level2_2'] } }, + ], + }, + + { + name: 'NW.Dom.match', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + { selector: 'span', expect: { includesIds: ['dupL1'] } }, + { selector: 'span#dupL1', expect: { includesIds: ['dupL1'] } }, + { selector: 'div > span', expect: { includesIds: ['dupL1'] } }, // child combinator + { selector: '#dupContainer span', expect: { includesIds: ['dupL1'] } }, // descendant combinator + { selector: '#dupL1', expect: { includesIds: ['dupL1'] } }, // ID only + { selector: 'span.span_foo', expect: { includesIds: ['dupL1'] } }, // class name 1 + { selector: 'span.span_bar', expect: { includesIds: ['dupL1'] } }, // class name 2 + { selector: 'span:first-child', expect: { includesIds: ['dupL1'] } }, // first-child pseudoclass + + { selector: 'span.span_wtf', expect: { excludesIds: ['dupL1'] } }, // bogus class name + { selector: '#dupL2', expect: { excludesIds: ['dupL1'] } }, // different ID + { selector: 'div', expect: { excludesIds: ['dupL1'] } }, // different tag name + { selector: 'span span', expect: { excludesIds: ['dupL1'] } }, // different ancestry + { selector: 'span > span', expect: { excludesIds: ['dupL1'] } }, // different parent + { selector: 'span:nth-child(5)', expect: { excludesIds: ['dupL1'] } }, // different pseudoclass + + { selector: 'a[rel^=external]', expect: { includesIds: ['link_1'], excludesIds: ['link_2'] } }, + { selector: 'a[rel^="external"]', expect: { includesIds: ['link_1'] } }, + { selector: "a[rel^='external']", expect: { includesIds: ['link_1'] } }, + ], + }, + + { + name: 'Equivalent Selectors', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers]' } } }, + { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers].brothers' } } }, + { selector: 'div:not(.brothers)', expect: { equivalentTo: { selector: 'div:not([class~=brothers])' } } }, + { selector: 'li ~ li', expect: { equivalentTo: { selector: 'li:not(:first-child)' } } }, + { selector: 'ul > li', expect: { equivalentTo: { selector: 'ul > li:nth-child(n)' } } }, + { selector: 'ul > li:nth-child(even)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n)' } } }, + { selector: 'ul > li:nth-child(odd)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n+1)' } } }, + { selector: 'ul > li:first-child', expect: { equivalentTo: { selector: 'ul > li:nth-child(1)' } } }, + { selector: 'ul > li:last-child', expect: { equivalentTo: { selector: 'ul > li:nth-last-child(1)' } } }, + { selector: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { selector: 'ul > li' } } }, + { selector: 'ul>li', expect: { equivalentTo: { selector: 'ul > li' } } }, + { selector: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { selector: '#p a:not([rel$="nofollow"]) > em' } } }, + ], + }, + + { + name: 'Multiple Selectors', + html, + htmlMode: 'document', + setupPage: setupNw, + cases: [ + { selector: '#list, .first,*[xml:lang="es-us"] , #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { selector: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + ], + }, +]); From f5f1a7a4e55eb1948b8e788ca1c7e2b141ed5b51 Mon Sep 17 00:00:00 2001 From: koal44 Date: Wed, 8 Apr 2026 20:21:47 -0700 Subject: [PATCH 06/19] add speed coverage and start w3c migration --- test_new/browser/speed.test.ts | 286 ++ test_new/browser/speed_selectors.html | 5055 +++++++++++++++++++++++++ test_new/browser/w3c.test.ts | 473 +++ 3 files changed, 5814 insertions(+) create mode 100644 test_new/browser/speed.test.ts create mode 100644 test_new/browser/speed_selectors.html create mode 100644 test_new/browser/w3c.test.ts diff --git a/test_new/browser/speed.test.ts b/test_new/browser/speed.test.ts new file mode 100644 index 0000000..f864205 --- /dev/null +++ b/test_new/browser/speed.test.ts @@ -0,0 +1,286 @@ +import { readFileSync } from 'node:fs'; +import { runScenarios } from './harness'; + +const html = readFileSync('test_new/browser/speed_selectors.html', 'utf8'); + +runScenarios('speed', 'normal', [ + { + name: 'default', + html, + htmlMode: 'document', + cases: [ + { selector: '#title' }, + { selector: 'h1#title' }, + { selector: 'div #title' }, + { selector: '#changes + #contents' }, + { selector: '.note' }, + { selector: 'div.note' }, + { selector: 'body .note' }, + { selector: '.no-toc + .toc' }, + { selector: 'div.example' }, + { selector: '.title' }, + { selector: '.toc' }, + { selector: 'p + .note' }, + { selector: 'div.example, p.note' }, + { selector: 'body' }, + { selector: 'div' }, + { selector: 'body div' }, + { selector: 'div p' }, + { selector: 'div > p' }, + { selector: 'div + p' }, + { selector: 'div ~ p' }, + { selector: 'div[class^=exa][class$=mple]' }, + { selector: 'p a' }, + { selector: 'div p a' }, + { selector: 'div > p > a' }, + { selector: 'div.example > p > a' }, + { selector: 'div + p + ul' }, + { selector: 'div ~ p ~ ul' }, + { selector: 'div, ul, a' }, + { selector: 'h1, h2, h3, h4, h5, h6' }, + { selector: 'a[href][lang][class]' }, + { selector: 'div[class]' }, + { selector: 'div[class=example]' }, + { selector: 'div[class^=exa]' }, + { selector: 'div[class$=mple]' }, + { selector: 'div[class*=e]' }, + { selector: 'div[class~=example]' }, + { selector: 'div:not(.example)' }, + { selector: 'div:nth-child(even)' }, + { selector: 'div:nth-child(2n)' }, + { selector: 'div:nth-child(odd)' }, + { selector: 'div:nth-child(2n+1)' }, + { selector: 'div:nth-child(n)' }, + { selector: 'p:only-child' }, + { selector: 'p:last-child' }, + { selector: 'p:first-child' }, + ], + }, + { + name: 'descendants_only', + html, + htmlMode: 'document', + cases: [ + { selector: 'ul li a span' }, + { selector: 'ul li span' }, + { selector: 'li a span' }, + { selector: 'ul a span' }, + { selector: 'ul li a' }, + { selector: 'ul li' }, + { selector: 'li a' }, + { selector: 'ul a' }, + { selector: 'dl dt code' }, + { selector: 'dl dd a' }, + { selector: 'dl dd' }, + { selector: 'dl dt' }, + { selector: 'table tr td' }, + { selector: 'table tr' }, + { selector: 'tr td a' }, + { selector: 'td a' }, + ], + }, + { + name: 'descendant_and_siblings', + html, + htmlMode: 'document', + cases: [ + { selector: 'div ul li a span' }, + { selector: 'div ul li a' }, + { selector: 'div ul li' }, + { selector: 'div ul' }, + { selector: 'ul li a span' }, + { selector: 'ul li a' }, + { selector: 'ul li' }, + { selector: 'li a' }, + { selector: 'div > ul > li > a > span' }, + { selector: 'div > ul > li > a' }, + { selector: 'div > ul > li' }, + { selector: 'div > ul' }, + { selector: 'ul > li > a > span' }, + { selector: 'ul > li > a' }, + { selector: 'ul > li' }, + { selector: 'li > a' }, + { selector: 'h2 + p + ul + p' }, + { selector: 'h2 + p + ul' }, + { selector: 'p + ul + p' }, + { selector: 'li + li' }, + { selector: 'h2 + ul' }, + { selector: 'h2 + p' }, + { selector: 'ul + p' }, + { selector: 'p + p' }, + { selector: 'h2 ~ p ~ ul ~ p' }, + { selector: 'h2 ~ p ~ ul' }, + { selector: 'p ~ ul ~ p' }, + { selector: 'li ~ li' }, + { selector: 'h2 ~ ul' }, + { selector: 'h2 ~ p' }, + { selector: 'ul ~ p' }, + { selector: 'p ~ p' }, + ], + }, + { + name: 'tree-structural_child-indexed', + html, + htmlMode: 'document', + cases: [ + { selector: 'div:last-child' }, + { selector: 'div:only-child' }, + { selector: 'div:first-child' }, + { selector: 'div:nth-child(n)' }, + { selector: 'div:nth-child(3)' }, + { selector: 'div:nth-child(n+3)' }, + { selector: 'div:nth-child(+n-3)' }, + { selector: 'div:nth-child(2n)' }, + { selector: 'div:nth-child(odd)' }, + { selector: 'div:nth-child(2n+1)' }, + { selector: 'div:nth-child(even)' }, + { selector: 'div:nth-last-child(n)' }, + { selector: 'div:nth-last-child(3)' }, + { selector: 'div:nth-last-child(n+3)' }, + { selector: 'div:nth-last-child(+n-3)' }, + { selector: 'div:nth-last-child(2n)' }, + { selector: 'div:nth-last-child(odd)' }, + { selector: 'div:nth-last-child(2n+1)' }, + { selector: 'div:nth-last-child(even)' }, + ], + }, + { + name: 'tree-structural_typed_child-indexed', + html, + htmlMode: 'document', + cases: [ + { selector: 'div:last-of-type' }, + { selector: 'div:only-of-type' }, + { selector: 'div:first-of-type' }, + { selector: 'div:nth-of-type(n)' }, + { selector: 'div:nth-of-type(3)' }, + { selector: 'div:nth-of-type(n+3)' }, + { selector: 'div:nth-of-type(+n-3)' }, + { selector: 'div:nth-of-type(2n)' }, + { selector: 'div:nth-of-type(odd)' }, + { selector: 'div:nth-of-type(2n+1)' }, + { selector: 'div:nth-of-type(even)' }, + { selector: 'div:nth-last-of-type(n)' }, + { selector: 'div:nth-last-of-type(3)' }, + { selector: 'div:nth-last-of-type(n+3)' }, + { selector: 'div:nth-last-of-type(+n-3)' }, + { selector: 'div:nth-last-of-type(2n)' }, + { selector: 'div:nth-last-of-type(odd)' }, + { selector: 'div:nth-last-of-type(2n+1)' }, + { selector: 'div:nth-last-of-type(even)' }, + ], + }, + { + name: 'comma-separated_group_or_selector_list', + html, + htmlMode: 'document', + cases: [ + { selector: 'div, ul, li, a' }, + { selector: 'div, ul, li' }, + { selector: 'ul, li, a' }, + { selector: 'div, li' }, + { selector: 'div, a' }, + { selector: 'ul, li' }, + { selector: 'ul, a' }, + { selector: 'li, a' }, + { selector: 'div, p, a, em' }, + { selector: 'div, p, em' }, + { selector: 'div, p, a' }, + { selector: 'div, p' }, + { selector: 'div, a' }, + { selector: 'p, em' }, + { selector: 'a, em' }, + { selector: 'p, a' }, + ], + }, + + { + name: 'negation_tree-structural_child-indexed', + html, + htmlMode: 'document', + cases: [ + { selector: 'div:not(:last-child)' }, + { selector: 'div:not(:only-child)' }, + { selector: 'div:not(:first-child)' }, + { selector: 'div:not(:nth-child(n))' }, + { selector: 'div:not(:nth-child(3))' }, + { selector: 'div:not(:nth-child(n+3))' }, + { selector: 'div:not(:nth-child(+n-3))' }, + { selector: 'div:not(:nth-child(2n))' }, + { selector: 'div:not(:nth-child(odd))' }, + { selector: 'div:not(:nth-child(2n+1))' }, + { selector: 'div:not(:nth-child(even))' }, + { selector: 'div:not(:nth-last-child(n))' }, + { selector: 'div:not(:nth-last-child(3))' }, + { selector: 'div:not(:nth-last-child(n+3))' }, + { selector: 'div:not(:nth-last-child(+n-3))' }, + { selector: 'div:not(:nth-last-child(2n))' }, + { selector: 'div:not(:nth-last-child(odd))' }, + { selector: 'div:not(:nth-last-child(2n+1))' }, + { selector: 'div:not(:nth-last-child(even))' }, + ], + }, + { + name: 'negation_tree-structural_typed_child-indexed', + html, + htmlMode: 'document', + cases: [ + { selector: 'div:not(:last-of-type)' }, + { selector: 'div:not(:only-of-type)' }, + { selector: 'div:not(:first-of-type)' }, + { selector: 'div:not(:nth-of-type(n))' }, + { selector: 'div:not(:nth-of-type(3))' }, + { selector: 'div:not(:nth-of-type(n+3))' }, + { selector: 'div:not(:nth-of-type(+n-3))' }, + { selector: 'div:not(:nth-of-type(2n))' }, + { selector: 'div:not(:nth-of-type(odd))' }, + { selector: 'div:not(:nth-of-type(2n+1))' }, + { selector: 'div:not(:nth-of-type(even))' }, + { selector: 'div:not(:nth-last-of-type(n))' }, + { selector: 'div:not(:nth-last-of-type(3))' }, + { selector: 'div:not(:nth-last-of-type(n+3))' }, + { selector: 'div:not(:nth-last-of-type(+n-3))' }, + { selector: 'div:not(:nth-last-of-type(2n))' }, + { selector: 'div:not(:nth-last-of-type(odd))' }, + { selector: 'div:not(:nth-last-of-type(2n+1))' }, + { selector: 'div:not(:nth-last-of-type(even))' }, + ], + }, + { + name: 'matches_and_negation_dynamic_and_user_actions', + html, + htmlMode: 'document', + cases: [ + { selector: ':lang(fr)' }, + { selector: ':visited' }, + { selector: ':target' }, + // { selector: ':active' }, + { selector: ':hover' }, + // { selector: ':focus' }, + { selector: ':empty' }, + { selector: ':link' }, + { selector: ':not(:lang(fr))' }, + { selector: ':not(:visited)' }, + { selector: ':not(:target)' }, + // { selector: ':not(:active)' }, + { selector: ':not(:hover)' }, + // { selector: ':not(:focus)' }, + { selector: ':not(:empty)' }, + { selector: ':not(:link)' }, + ], + }, + { + // active/focus does not seem to be stable. also, not supported in any of the browsers. + name: 'matches_and_negation_dynamic_and_user_actions - active/focus', + modifier: 'fixme', + html, + htmlMode: 'document', + cases: [ + { selector: ':active' }, + { selector: ':focus' }, + { selector: ':not(:active)' }, + { selector: ':not(:focus)' }, + ], + }, + +]); \ No newline at end of file diff --git a/test_new/browser/speed_selectors.html b/test_new/browser/speed_selectors.html new file mode 100644 index 0000000..f9e0e41 --- /dev/null +++ b/test_new/browser/speed_selectors.html @@ -0,0 +1,5055 @@ + + + + Selectors Level 4 + + + + + + + + + + + + + + + + +

    Jump to Table of Contents Collapse Sidebar

    +
    +

    +

    Selectors Level 4

    +

    W3C Working Draft,

    + +
    + +
    +
    +
    +

    Abstract

    +

    Selectors + are patterns that match against elements in a tree, and as such form +one of several technologies that can be used to select nodes in a +document. Selectors have been optimized for use with HTML and XML, and +are designed to be usable in performance-critical code. They are a core +component of CSS (Cascading Style Sheets), which uses Selectors to bind style properties to elements in the docu ment. + +Selectors Level 4 describes the selectors that already exist in [SELECT], and further introduces new selectors for CSS and other languages that may need them.

    + CSS is a language for describing the rendering of structured documents +(such as HTML and XML) +on screen, on paper, in speech, etc. +
    +

    Status of this document

    +
    +

    This section describes the status of this document at the time of + its publication. Other documents may supersede this document. A list of + current W3C publications and the latest revision of this technical report + can be found in the W3C technical reports + index at https://www.w3.org/TR/.

    +

    Publication as a Working Draft does not imply endorsement by the W3C + Membership. This is a draft document and may be updated, replaced or + obsoleted by other documents at any time. It is inappropriate to cite this + document as other than work in progress.

    +

    GitHub Issues are preferred for discussion of this specification. + When filing an issue, please put the text “selectors” in the title, + preferably like this: + “[selectors] …summary of comment…”. + All issues and comments are archived, + and there is also a historical archive.

    +

    This document was produced by the CSS Working Group (part of + the Style Activity).

    +

    This document was produced by a group operating under the W3C Patent Policy. + W3C maintains a public list of any patent disclosures made in + connection with the deliverables of the group; that page also includes + instructions for disclosing a patent. An individual who has actual + knowledge of a patent which the individual believes contains Essential + Claim(s) must disclose the information in accordance with section + 6 of the W3C Patent Policy.

    +

    This document is governed by the 1 March 2017 W3C Process Document.

    +

    +
    +
    +

    The following features are at-risk, and may be dropped during the CR period:

    + +

    “At-risk” is a W3C Process term-of-art, and does not necessarily +imply that the feature is in danger of being dropped or delayed. It +means that the WG believes the feature may have difficulty being +interoperably implemented in a timely manner, and marking it as such +allows the WG to drop the feature if necessary when transitioning to the + Proposed Rec stage, without having to publish a new Candidate Rec +without the feature first.

    +
    + +
    +

    1. Introduction

    +

    This section is not normative.

    +

    A selector is a boolean predicate + that takes an element in a tree structure + and tests whether the element matches the selector or not.

    +

    These expressions may be used for many things:

    +
      +
    • directly on an element to test whether it matches some criteria, + such as in the element.matches() function defined in [DOM] +
    • applied to an entire tree of elements + to filter it into a set of elements that match the criteria, + such as in the document.queryAll() function defined in [DOM] or the selector of a CSS style rule. +
    • used "in reverse" to generate markup that would match a given selector, + such as in HAML or Emmet. +
    +

    Selectors Levels 1, 2, and 3 are defined as the subsets of selector + functionality defined in the CSS1, CSS2.1, and Selectors Level 3 specifications, respectively. This module defines Selectors Level 4.

    +

    1.1. Module Interactions

    +

    This module replaces the definitions of + and extends the set of selectors defined for CSS in [SELECT] and [CSS21].

    +

    Pseudo-element selectors, + which define abstract elements in a rendering tree, + are not part of this specification: + their generic syntax is described here, + but, due to their close integration with the rendering model and irrelevance to other uses such as DOM queries, + they will be defined in other modules.

    +

    2. Selectors Overview

    +

    This section is non-normative, as it merely summarizes the + following sections.

    +

    A selector represents a structure. This structure can be used as a + condition (e.g. in a CSS rule) that determines which elements a + selector matches in the document tree, or as a flat description of the + HTML or XML fragment corresponding to that structure.

    +

    Selectors may range from simple element names to rich contextual + representations.

    +

    The following table summarizes the Selector syntax:

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Pattern + Represents + Section + Level +
    * + any element + §5.2 Universal selector + 2 +
    E + an element of type E + §5.1 Type (tag name) selector + 1 +
    E:not(s1, s2) + an E element that does not match either compound selector s1 or compound selector s2 + §4.3 The Negation Pseudo-class: :not() + 3/4 +
    E:matches(s1, s2) + an E element that matches compound selector s1 and/or compound selector s2 + §4.2 The Matches-any Pseudo-class: :matches() + 4 +
    E:something(s1, s2) + an E element that matches compound selector s1 and/or compound selector s2 but contributes no specificity. + §4.4 The Specificity-adjustment Pseudo-class: :something() + 4 +
    E:has(rs1, rs2) + an E element, + if either of the relative selectors rs1 or rs2, + when evaluated with E as the :scope elements, + match an element + §4.5 The Relational Pseudo-class: :has() + 4 +
    E.warning + an E element belonging to the class warning (the document language specifies how class is determined). + §6.6 Class selectors + 1 +
    E#myid + an E element with ID equal to myid. + §6.7 ID selectors + 1 +
    E[foo] + an E element with a foo attribute + §6 Attribute selectors + 2 +
    E[foo="bar"] + an E element whose foo attribute value is + exactly equal to bar + §6 Attribute selectors + 2 +
    E[foo="bar" i] + an E element whose foo attribute value is + exactly equal to any (ASCII-range) case-permutation of bar + §6.3 Case-sensitivity + 4 +
    E[foo~="bar"] + an E element whose foo attribute value is + a list of whitespace-separated values, one of which is + exactly equal to bar + §6 Attribute selectors + 2 +
    E[foo^="bar"] + an E element whose foo attribute value + begins exactly with the string bar + §6.2 Substring matching attribute selectors + 3 +
    E[foo$="bar"] + an E element whose foo attribute value + ends exactly with the string bar + §6.2 Substring matching attribute selectors + 3 +
    E[foo*="bar"] + an E element whose foo attribute value + contains the substring bar + §6.2 Substring matching attribute selectors + 3 +
    E[foo|="en"] + an E element whose foo attribute value is + a hyphen-separated list of values beginning with en + §6 Attribute selectors + 2 +
    E:dir(ltr) + an element of type E in with left-to-right directionality + (the document language specifies how directionality is determined) + §7.1 The Directionality Pseudo-class: :dir() + 4 +
    E:lang(zh, "*-hant") + an element of type E tagged as being either in Chinese + (any dialect or writing system) + or otherwise written with traditional Chinese characters + §7.2 The Language Pseudo-class: :lang() + 2/4 +
    E:any-link + an E element being the source anchor of a hyperlink + §8.1 The Hyperlink Pseudo-class: :any-link + 4 +
    E:link + an E element being the source anchor of a hyperlink + of which the target is not yet visited + §8.2 The Link History Pseudo-classes: :link and :visited + 1 +
    E:visited + an E element being the source anchor of a hyperlink + of which the target is already visited + §8.2 The Link History Pseudo-classes: :link and :visited + 1 +
    E:local-link + an E element being the source anchor of a hyperlink + targetting the current URL + §8.3 The Local Link Pseudo-class: :local-link + 4 +
    E:target + an E element being the target of the current URL + §8.4 The Target Pseudo-class: :target + 3 +
    E:target-within + an E element that is the target of the current URL or contains an element that does. + §8.5 The Target Container Pseudo-class: :target-within + 4 +
    E:scope + an E element being a designated reference element + §8.6 The Reference Element Pseudo-class: :scope + 4 +
    E:current + an E element that is currently presented in a time-dimensional canvas + §10 Time-dimensional Pseudo-classes + 4 +
    E:current(s) + an E element that is the deepest :current element that + matches selector s + §10 Time-dimensional Pseudo-classes + 4 +
    E:past + an E element that is in the past in a time-dimensional canvas + §10 Time-dimensional Pseudo-classes + 4 +
    E:future + an E element that is in the future in a time-dimensional canvas + §10 Time-dimensional Pseudo-classes + 4 +
    E:active + an E element that is in an activated state + §9 User Action Pseudo-classes + 1 +
    E:hover + an E element that is under the cursor, + or that has a descendant under the cursor + §9 User Action Pseudo-classes + 2 +
    E:focus + an E element that has user input focus + §9 User Action Pseudo-classes + 2 +
    E:focus-within + an E element that has user input focus or contains an element that has input focus. + §9.5 The Focus Container Pseudo-class: :focus-within + 4 +
    E:focus-visible + an E element that has user input focus, + and the UA has determined that a focus ring or other indicator + should be drawn for that element + §9 User Action Pseudo-classes + 4 +
    E:drop + an E element that can possibly receive a drop + §9.6 The Drop Target Pseudo-class: :drop and :drop() + 4 +
    E:drop(active) + an E element that is the current drop target for the item being dragged + §9.6 The Drop Target Pseudo-class: :drop and :drop() + 4 +
    E:drop(valid) + an E element that could receive the item currently being dragged + §9.6 The Drop Target Pseudo-class: :drop and :drop() + 4 +
    E:drop(invalid) + an E element that cannot receive the item currently being dragged, but could receive some other item + §9.6 The Drop Target Pseudo-class: :drop and :drop() + 4 +
    E:enabled
    E:disabled
    +
    a user interface element E that is enabled or disabled, respectively + §12.1.1 The :enabled and :disabled Pseudo-classes + 3 +
    E:read-write
    E:read-only +
    a user interface element E that is user alterable, or not + §12.1.2 The Mutability Pseudo-classes: :read-only and :read-write + 3-UI/4 +
    E:placeholder-shown + an input control currently showing placeholder text + §12.1.2 The Mutability Pseudo-classes: :read-only and :read-write + 3-UI/4 +
    E:default + a user interface element E that is the default item in a group of related choices + §12.1.4 The Default-option Pseudo-class: :default + 3-UI/4 +
    E:checked + a user interface element E that is checked/selected + (for instance a radio-button or checkbox) + §12.2.1 The Selected-option Pseudo-class: :checked + 3 +
    E:indeterminate + a user interface element E that is in an indeterminate state + (neither checked nor unchecked) + §12.2.2 The Indeterminate-value Pseudo-class: :indeterminate + 4 +
    E:valid
    E:invalid +
    a user-input element E that meets, or doesn’t, its data validity semantics + §12.3.2 The Range Pseudo-classes: :in-range and :out-of-range + 3-UI/4 +
    E:in-range
    E:out-of-range +
    a user-input element E whose value is in-range/out-of-range + §12.3.2 The Range Pseudo-classes: :in-range and :out-of-range + 3-UI/4 +
    E:required
    E:optional +
    a user-input element E that requires/does not require input + §12.3.3 The Optionality Pseudo-classes: :required and :optional + 3-UI/4 +
    E:user-invalid + a user-altered user-input element E with incorrect input (invalid, out-of-range, omitted-but-required) + §12.3.4 The User-interaction Pseudo-class: :user-invalid + 4 +
    E:root + an E element, root of the document + §13 Tree-Structural pseudo-classes + 3 +
    E:empty + an E element that has no children (not even text nodes) + §13 Tree-Structural pseudo-classes + 3 +
    E:blank + an E element that has no content except maybe white space + §13 Tree-Structural pseudo-classes + 4 +
    E:nth-child(n [of S]?) + an E element, the n-th child of its parent matching S + §13.4 Child-indexed Pseudo-classes + 3/4 +
    E:nth-last-child(n [of S]?) + an E element, the n-th child of its parent matching S, + counting from the last one + §13.4 Child-indexed Pseudo-classes + 3/4 +
    E:first-child + an E element, first child of its parent + §13.4 Child-indexed Pseudo-classes + 2 +
    E:last-child + an E element, last child of its parent + §13.4 Child-indexed Pseudo-classes + 3 +
    E:only-child + an E element, only child of its parent + §13.4 Child-indexed Pseudo-classes + 3 +
    E:nth-of-type(n) + an E element, the n-th sibling of its type + §13.5 Typed Child-indexed Pseudo-classes + 3 +
    E:nth-last-of-type(n) + an E element, the n-th sibling of its type, + counting from the last one + §13.5 Typed Child-indexed Pseudo-classes + 3 +
    E:first-of-type + an E element, first sibling of its type + §13.5 Typed Child-indexed Pseudo-classes + 3 +
    E:last-of-type + an E element, last sibling of its type + §13.5 Typed Child-indexed Pseudo-classes + 3 +
    E:only-of-type + an E element, only sibling of its type + §13.5 Typed Child-indexed Pseudo-classes + 3 +
    E F + an F element descendant of an E element + §14.1 Descendant combinator ( ) + 1 +
    E > F + an F element child of an E element + §14.2 Child combinator (>) + 2 +
    E + F + an F element immediately preceded by an E element + §14.3 Next-sibling combinator (+) + 2 +
    E ~ F + an F element preceded by an E element + §14.4 Subsequent-sibling combinator (~) + 3 +
    F || E + an E element that represents a cell in a grid/table + belonging to a column represented by an element F + §15 Grid-Structural Selectors + 4 +
    E:nth-col(n) + an E element that represents a cell belonging to the nth column in a grid/table + §15 Grid-Structural Selectors + 4 +
    E:nth-last-col(n) + an E element that represents a cell belonging to the nth column in a grid/table, counting from the last one + §15 Grid-Structural Selectors + 4 +
    +

    Note: Some Level 4 selectors (noted above as "3-UI") were introduced in [CSS3UI].

    +

    2.1. Live vs Snapshot Selector Profiles

    +

    Selectors are used in many different contexts, + with wildly varying performance characteristics. + Some powerful selectors are unfortunately too slow + to realistically include in the more performance-sensitive contexts. + To accommodate this, two profiles of the Selectors spec are defined:

    +
    +
    live profile +
    + The live profile is appropriate for use in any context, + including browser CSS selector matching, which is live. + It includes every selector defined in this document, + except for: + +
    snapshot profile +
    The snapshot profile is appropriate for contexts which aren’t extremely performance sensitive; + in particular, it’s appropriate for contexts which evaluate selectors against a static document tree. + For example, the query() method defined in [DOM] should use the snapshot profile. + It includes all of the selectors defined in this document. +
    +

    CSS implementations conformant to Selectors Level 4 must use the live profile for CSS selection. + Implementations using the live profile must treat selectors that are not included in the profile + as unknown and invalid.

    +

    The categorization of things into the “live” or snapshot profiles needs implementor review. + If some things currently not in the live profile can reasonably be done in CSS Selectors, + we should move them.

    +

    3. Selector Syntax and Structure

    +

    3.1. Structure and Terminology

    +

    A selector represents + a particular pattern of element(s) in a tree structure. + The term selector can refer to a simple selector, compound selector, complex selector, or selector list. + The subject of a selector is + any element that selector is defined to be about; + that is, any element matching that selector.

    +

    A simple selector is a single condition on an element. + A type selector, universal selector, attribute selector, class selector, ID selector, + or pseudo-class is a simple selector. + (It is represented by <simple-selector> in the selectors grammar.) + A given element is said to match a simple selector when that simple selector, + as defined in this specification and in accordance with the document language, + accurately describes the element.

    +

    A compound selector is a sequence of simple selectors that are not separated by a combinator, + and represents a set of simultaneous conditions on a single element. + If it contains a type selector or universal selector, + that selector must come first in the sequence. + Only one type selector or universal selector is allowed in the sequence. + (A compound selector is represented by <compound-selector> in the selectors grammar.) + A given element is said to match a compound selector when it matches all simple selectors in the compound selector.

    +

    Note: As whitespace represents the descendant combinator, + no whitespace is allowed between the simple selectors in a compound selector.

    +

    A combinator is a condition of relationship between two elements + represented by the compound selectors on either side. + Combinators in Selectors Level 4 include: + the descendant combinator (white space), + the child combinator (U+003E, >), + the next-sibling combinator (U+002B, +), + and the subsequent-sibling combinator (U+007E, ~). + Two given elements are said to match a combinator when the condition of relationship between these elements is true.

    +

    A complex selector is + a sequence of one or more compound selectors separated by combinators. + It represents a set of simultaneous conditions + on a set of elements in the particular relationships + described by its combinators. + (Complex selectors are represented by <complex-selector> in the selectors grammar.) + A given element is said to match a complex selector when there exists a list of elements, + each matching a corresponding compound selector in the complex selector, + with their relationships matching the combinators between them, + and with the given element matching the last compound selector.

    +

    Note: Thus, a selector consisting of a single compound selector matches any element satisfying the requirements + of its constituent simple selectors. + Prepending another compound selector and a combinator to a sequence imposes additional matching constraints, + such that the subjects of a complex selector are always + a subset of the elements represented by its last compound selector.

    +

    A list of simple/compound/complex selectors is a comma-separated list of simple, compound, + or complex selectors. + This is also called just a selector list when the type is either unimportant or specified in the surrounding prose; + if the type is important and unspecified, + it defaults to meaning a list of complex selectors. + (See §4.1 Selector Lists for additional information on selector lists and the various <*-selector-list> productions in the grammar for their formal syntax.) + A given element is said to match a selector list when it matches any (at least one) of the selectors in that selector list.

    +

    Pseudo-elements aren’t handled here, and should be.

    +

    3.2. Data Model

    +

    Selectors are evaluated against an element tree such as the DOM. [DOM] Within this specification, + this may be referred to as the "document tree" or "source document".

    +

    Each element may have any of the following five aspects, + which can be selected against, + all of which are matched as strings:

    +
      +
    • The element’s type (also known as its tag name). +
    • The element’s namespace. +
    • An ID. +
    • Classes (named groups) to which it belongs. +
    • Attributes, which are name-value pairs. +
    +

    While individual elements may lack any of the above features, + some elements are featureless. + A featureless element does not match any selector at all, + except those it is explicitly defined to match. + If a given selector is allowed to match a featureless element, + it must do so while ignoring the default namespace. [CSS3NAMESPACE]

    +
    For example, the shadow host in a shadow tree is featureless, + and can’t be matched by any pseudo-class except for :host and :host-context().)
    +

    Many of the selectors depend on the semantics of the document language (i.e. the language and semantics of the document tree) + and/or the semantics of the host language (i.e. the language that is using selectors syntax). + For example, the :lang() selector depends on the document language (e.g. HTML) + to define how an element is associated with a language. + As a slightly different example, the ::first-line pseudo-element + depends on the host language (e.g. CSS) + to define what a ::first-line pseudo-element represents + and what it can do.

    +

    3.3. Scoped Selectors

    +

    Some host applications may choose to scope selectors + to a particular subtree or fragment of the document. + The root of the scoping subtree is called the scoping root, + and may be either a true element (the scoping element) + or a virtual one (such as a DocumentFragment).

    +

    When a selector is scoped, + it matches an element only if the element is a descendant of the scoping root. + (The rest of the selector can match unrestricted; + it’s only the final matched elements that must be within the scope.)

    +
    + For example, + the element.querySelector() function defined in [DOM] allows the author to evaluate a scoped selector + relative to the element it’s called on. +

    A call like widget.querySelector("a") will thus only find a elements inside of the widget element, + ignoring any other as that might be scattered throughout the document.

    +
    +

    Note: If the context does not explicitly define any :scope elements for the selector, + the scoping root is a :scope element.

    +

    3.4. Relative Selectors

    +

    Certain contexts may accept relative selectors, + which are a shorthand for selectors that represent elements relative to a :scope element (i.e. an element that matches :scope). + In a relative selector, + “:scope ” (the :scope pseudo-class followed by a space) + is implied at the beginning of each complex selector that does not already contain the :scope pseudo-class. + This allows the selector to begin syntactically with a combinator. + However, it must be absolutized before matching.

    +

    Relative selectors, once absolutized, + can additionally be scoped.

    +

    Relative selectors are represented by <relative-selector> in the selectors grammar.

    +

    3.4.1. Absolutizing a Relative Selector

    +

    To absolutize a relative selector:

    +

    If there are no :scope elements and the selector is scoped to a virtual scoping root:

    +

    This needs a sane definition.

    +

    Otherwise:

    +
      +
    1. If the selector starts with a combinator other than the white space form of the descendant combinator, + prepend :scope as the initial compound selector. +
    2. Otherwise, if the selector does not contain any instance of the :scope pseudo-class + (either at the top-level or as an argument to a functional pseudo-class), + prepend :scope followed by the white space form of the descendant combinator. +
    3. Otherwise, the selector is already absolute. +
    +

    To absolutize a relative selector list, + absolutize each relative selector in the list.

    +

    3.5. Pseudo-classes

    +

    Pseudo-classes are simple selectors that permit selection based on + information that lies outside of the document tree + or that can be awkward or impossible to express using the other simple selectors. + They can also be dynamic, + in the sense that an element can acquire or lose a pseudo-class + while a user interacts with the document, + without the document itself changing. Pseudo-classes do not appear in or modify the document source or document tree.

    +

    The syntax of a pseudo-class consists of a ":" (U+003A COLON) + followed by the name of the pseudo-class as a CSS identifier, + and, in the case of a functional pseudo-class, + a pair of parentheses containing its arguments.

    +

    For example, :valid is a regular pseudo-class, + and :lang() is a functional pseudo-class.

    +

    Like all CSS keywords, pseudo-class names are ASCII case-insensitive. + No white space is allowed between the colon and the name of the pseudo-class, + nor, as usual for CSS syntax, + between a functional pseudo-class’s name and its opening parenthesis + (which thus form a CSS function token). + Also as usual, white space is allowed around the arguments inside the parentheses + of a functional pseudo-class + unless otherwise specified.

    +

    Like other simple selectors, pseudo-classes are allowed in all compound selectors contained in a selector, + and must follow the type selector or universal selector, if present.

    +

    Note: Some pseudo-classes are mutually exclusive + (such that a compound selector containing them, while valid, will never match anything), + while others can apply simultaneously to the same element.

    +

    3.6. Pseudo-elements

    +

    Similar to how certain pseudo-classes represent additional state information + not directly present in the document tree, + a pseudo-element represents an element + not directly present in the document tree. + They are used to create abstractions about the document tree + beyond those provided by the document tree. + For example, + pseudo-elements can be used to select portions of the document + that do not correspond to a document-language element + (including such ranges as don’t align to element boundaries or fit +within its tree structure); + that represent content not in the document tree or in an alternate +projection of the document tree; + or that rely on information provided by styling, layout, user +interaction, and other processes that are not reflected in the document +tree.

    +
    + For instance, document languages do not offer mechanisms to access + the first letter or first line of an element’s content, + but there exist pseudo-elements (::first-letter and ::first-line) + that allow those things to be styled. + Notice especially that in the case of ::first-line, + which portion of content is represented by the pseudo-element + depends on layout information + that cannot be inferred from the document tree. +

    Pseudo-elements can also represent content that doesn’t exist in the source document at all, + such as the ::before and ::after pseudo-elements + which allow additional content to be inserted before or after the contents of any element.

    +
    +

    Like pseudo-classes pseudo-elements do not appear in or modify the document source or document tree. + Accordingly, they also do not affect the interpretation of structural pseudo-classes or other selectors pertaining to their originating element or its tree.

    +

    The host language defines which pseudo-elements exist, their type, and their abilities. + Pseudo-elements that exist in CSS + are defined in [CSS21] (Level 2), [SELECT] (Level 3), and [CSS-PSEUDO-4] (Level 4).

    +

    3.6.1. Syntax

    +

    The syntax of a pseudo-element is "::" (two U+003A COLON characters) + followed by the name of the pseudo-element as an identifier. Pseudo-element names are ASCII case-insensitive. + No white space is allowed between the two colons, or between the colons and the name.

    +

    Because CSS Level 1 and CSS Level 2 conflated pseudo-elements and pseudo-classes by sharing a single-colon syntax for both, + user agents must also accept the previous one-colon notation + for the Level 1 & 2 pseudo-elements + (::before, ::after, ::first-line, and ::first-letter). + This compatibility notation is not allowed any other pseudo-elements. + However, as this syntax is deprecated, + authors should use the Level 3+ double-colon syntax for these pseudo-elements.

    +

    Pseudo-elements are featureless, + and so can’t be matched by any other selector.

    +

    3.6.2. Binding to the Document Tree

    +

    Pseudo-elements do not exist independently in the tree: + they are always bound to another element on the page, + called their originating element. + Syntactically, a pseudo-element immediately follows + the compound selector representing its originating element. + If this compound selector is omitted, + it is assumed to be the universal selector *.

    +
    + For example, in the selector div a::before, + the a elements matched by the selector are the originating elements for the ::before pseudo-elements attached to them. +

    The selector ::first-line is equivalent to *::first-line, + which selects the ::first-line pseudo-element on every element in the document.

    +
    +

    When a pseudo-element is encountered in a selector, + the part of the selector before the pseudo-element selects the originating element for the pseudo-element; + the part of the selector after it, if any, applies to the pseudo-element itself. + (See below.)

    +

    3.6.3. Pseudo-classing Pseudo-elements

    +

    A pseudo-element may be immediately followed + by any combination of the user action pseudo-classes, + in which case the pseudo-element is represented only when it is in the corresponding state. + Whether these pseudo-classes can match on the pseudo-element depends on the pseudo-class and pseudo-element’s definitions: + unless otherwise-specified, none of these pseudo-classes will match on the pseudo-element.

    +

    Clarify that :not() and :matches() can be used when containing above-mentioned pseudos.

    +
    + For example, since the :hover pseudo-class specifies + that it can apply to any pseudo-element, ::first-line:hover will match when the first line is hovered. + However, since neither :focus nor ::first-line define that :focus can apply to ::first-line, + the selector ::first-line:focus will never match anything. +

    Does ::first-line:not(:focus) match anything?

    +

    Notice that ::first-line:hover is very different from :hover::first-line, + which matches the first line of any originating element that is hovered! + For example, :hover::first-line also matches the first line of a paragraph + when the second line of the paragraph is hovered, + whereas ::first-line:hover only matches if the first line itself is hovered.

    +
    +

    Note: Note that, unless otherwise specified in a future specification, + pseudo-classes other than the user action pseudo-classes are not valid when compounded to a pseudo-element; + so, for example, ::before:first-child is an invalid selector.

    +

    3.6.4. Internal Structure

    +

    Some pseudo-elements are defined to have internal structure. + These pseudo-elements may be followed by child/descendant combinators + to express those relationships. + Selectors containing combinators after the pseudo-element + are otherwise invalid.

    +
    For example, ::first-letter + span and ::first-letter em are invalid selectors. + However, since ::shadow is defined to have internal structure, ::shadow > p is a valid selector.
    +

    Note: A future specification may expand the capabilities of existing pseudo-elements, + so some of these currently-invalid selectors (e.g. ::first-line :any-link) + may become valid in the future.

    +

    The children of such pseudo-elements can simultaneously be children of other elements, too. + However, at least in CSS, their rendering must be defined so as to maintain the tree-ness of the box tree.

    +
    + For example, + the ::content pseudo-element treats elements distributed to it as its children. + This means that, given the following fragment: +
    <div>
    +  <span>foo</span>
    +  <"shadow root">
    +    <content></content>
    +  </"shadow root">
    +</div>
    +
    +

    the selectors div > span and div::shadow ::content > span select the same element via different paths.

    +

    However, when rendered, + the <span> element generates boxes as if it were the child of the <content> element, + rather than the <div> element, + so the tree structure of the box tree is maintained.

    +
    +

    3.7. Characters and case sensitivity

    +

    All Selectors syntax is case-insensitive within the ASCII range + (i.e. [a-z] and [A-Z] are equivalent), + except for the parts + that are not under the control of Selectors: + specifically, + the case-sensitivity of + document language element names, + attribute names, + and attribute values + depends on the document language.

    +
    For example, in HTML, element and attribute names are ASCII case-insensitive, + but in XML, they are case-sensitive.
    +

    Case sensitivity of namespace prefixes is defined in [CSS3NAMESPACE]. + Case sensitivity of language ranges is defined in the :lang() section.

    +

    White space in Selectors consists of the + code points SPACE (U+0020), TAB (U+0009), LINE FEED (U+000A), + CARRIAGE RETURN (U+000D), and FORM FEED (U+000C). + Other space-like code points, such as EM SPACE (U+2003) and + IDEOGRAPHIC SPACE (U+3000), are never considered syntactic white space.

    +

    Code points in Selectors can be escaped with a backslash + according to the same escaping rules as CSS. [CSS21] Note that escaping a code point “cancels out” + any special meaning it may have in Selectors. + For example, the selector #foo>a contains a combinator, + but #foo\>a instead selects an element with the id foo>a.

    +

    3.8. Declaring Namespace Prefixes

    +

    Certain selectors support namespace prefixes. + The mechanism by which namespace prefixes are declared should be specified by the language that uses Selectors. + If the language does not specify a namespace prefix declaration mechanism, + then no prefixes are declared. + In CSS, namespace prefixes are declared with the @namespacerule. [CSS3NAMESPACE]

    +

    3.9. Invalid Selectors and Error Handling

    +

    User agents must observe the rules for handling invalid selectors:

    +
      +
    • a parsing error in a selector, + e.g. an unrecognized token or a token which is not allowed at the current parsing point + (see §17 Grammar), + causes that selector to be invalid. +
    • a simple selector containing an undeclared namespace prefix is invalid +
    • a selector containing an invalid simple selector, an invalid combinator + or an invalid token is invalid. +
    • a selector list containing an invalid selector is invalid. +
    • an empty selector, i.e. one that contains no compound selector, is invalid. +
    +

    Note: Consistent with CSS’s forwards-compatible parsing principle, + UAs must treat as invalid any pseudo-classes, pseudo-elements, combinators, or other syntactic constructs + for which they have no usable level of support. + See Partial Implementations.

    +

    An invalid selector represents, and therefore matches, nothing.

    +

    4. Logical Combinations

    +

    4.1. Selector Lists

    +

    A comma-separated list of selectors represents the union of all + elements selected by each of the individual selectors in the selector list. + (A comma is U+002C.) For example, in CSS when several selectors share + the same declarations, they may be grouped into a comma-separated + list. White space may appear before and/or after the comma.

    +
    + CSS example: + In this example, we condense three rules with identical + declarations into one. Thus, +
    h1 { font-family: sans-serif }
    +h2 { font-family: sans-serif }
    +h3 { font-family: sans-serif }
    +
    +

    is equivalent to:

    +
    h1, h2, h3 { font-family: sans-serif }
    +
    +
    +

    Warning: the equivalence is true in this example + because all the selectors are valid selectors. If just one of these + selectors were invalid, the entire selector list would be + invalid. This would invalidate the rule for all three heading + elements, whereas in the former case only one of the three individual + heading rules would be invalidated.

    +
    + Invalid CSS example: +
    h1 { font-family: sans-serif }
    +h2..foo { font-family: sans-serif }
    +h3 { font-family: sans-serif }
    +
    +

    is not equivalent to:

    +
    h1, h2..foo, h3 { font-family: sans-serif } 
    +

    because the above selector (h1, h2..foo, h3) + is entirely invalid and the entire style rule is dropped. (When + the selectors are not grouped, only the rule for h2..foo is dropped.)

    +
    +

    4.2. The Matches-any Pseudo-class: :matches()

    +

    The matches-any pseudo-class, :matches(), + is a functional pseudo-class taking a selector list as its argument. + It represents an element that is represented by its argument.

    +

    Note: The specificity of the :matches() pseudo-class + is replaced by the specificity of its argument. + Thus, a selector written with :matches() has equivalent specificity + to the equivalent selector written without :matches() For example, :matches(ul, ol, .list) > [hidden] and ul > [hidden], ol > [hidden], .list > [hidden] are equivalent in both their matching behavior and specificity. + See §16 Calculating a selector’s specificity. + ISSUE: See also issue 1027.

    +

    Pseudo-elements cannot be represented by the matches-any pseudo-class; + they are not valid within :matches().

    +

    Default namespace declarations do not affect the compound selector representing the subject of any selector + within a :matches() pseudo-class, + unless that compound selector contains + an explicit universal selector or type selector.

    +

    Why this exception for the explicit universal selector?

    +
    + For example, the following selector matches any element that is being + hovered or focused, regardless of its namespace. In particular, it + is not limited to only matching elements in the default namespace + that are being hovered or focused. +
    *|*:matches(:hover, :focus) 
    +

    The following selector, however, represents only hovered or focused + elements that are in the default namespace, because it uses an explicit + universal selector within the :matches() notation:

    +
    *|*:matches(*:hover, *:focus) 
    +
    +

    4.3. The Negation Pseudo-class: :not()

    +

    The negation pseudo-class, :not(), + is a functional pseudo-class taking a selector list as an argument. + It represents an element that is not represented by its argument.

    +

    Note: In Selectors Level 3, + only a single simple selector was allowed as the argument to :not().

    +

    Note: The specificity of the :not() pseudo-class + is replaced by the specificity of the most specific selector in its argument; + thus it has the exact behavior of :not(:matches(argument)). + See §16 Calculating a selector’s specificity.

    +

    Pseudo-elements cannot be represented by the negation pseudo-class; + they are not valid within :not().

    +
    + For example, the following selector matches all button elements in an HTML document + that are not disabled. +
    button:not([DISABLED]) 
    +

    The following selector represents all but FOO elements.

    +
    *:not(FOO)
    +

    The following compound selector represents all HTML elements + except links.

    +
    html|*:not(:link):not(:visited)
    +
    +

    As with :matches(), + default namespace declarations do not affect the compound selector representing the subject of any selector + within a :not() pseudo-class, + unless that compound selector contains + an explicit universal selector or type selector. + (See :matches() for examples.)

    +

    Note: The :not() pseudo-class allows useless selectors to be written. + For instance :not(*|*), which represents no element at all, + or div:not(span), which is equivalent to div but with a higher specificity.

    +

    4.4. The Specificity-adjustment Pseudo-class: :something()

    +

    The Specificity-adjustment pseudo-class, :something(), + is a functional pseudo-class + with the same syntax and functionality as :matches(). + Unlike :matches(), neither the :something pseudo-class, nor any of its arguments + contribute to the specificity of the selector—its specificity is always zero.

    +

    This is useful for introducing filters in a selector + while keeping the associated style declarations easy to override.

    +

    This pseudo-class needs a name. See previous discussion, open issue.

    +
    + Below is a common example where the specificity heuristic fails + to match author expectations: +
    a:not(:hover) {
    +  text-decoration: none;
    +}
    +
    +nav a {
    +  /* Has no effect */
    +  text-decoration: underline;
    +}
    +
    +

    However, by using :something() the author can explicitly declare their intent:

    +
    a:something(:not(:hover)) {
    +  text-decoration: none;
    +}
    +
    +nav a {
    +  /* Works now! */
    +  text-decoration: underline;
    +}
    +
    +
    +

    Note: Future levels of Selectors may introduce an additional argument + to explicitly set the specificity of that instance of the pseudo-class.

    +

    4.5. The Relational Pseudo-class: :has()

    +

    The relational pseudo-class, :has(), + is a functional pseudo-class taking a relative selector list as an argument. + It represents an element if any of the relative selectors, + when absolutized and evaluated with the element as the :scope elements, + would match at least one element.

    +
    + For example, the following selector matches only <a> elements that contain an <img> child: +
    a:has(> img)
    +

    The following selector matches a <dt> element + immediately followed by another <dt> element:

    +
    dt:has(+ dt)
    +

    The following selector matches <section> elements + that don’t contain any heading elements:

    +
    section:not(:has(h1, h2, h3, h4, h5, h6))
    +

    Note that ordering matters in the above selector. + Swapping the nesting of the two pseudo-classes, like:

    +
    section:has(:not(h1, h2, h3, h4, h5, h6))
    +

    ...would result matching any <section> element + which contains anything that’s not a header element.

    +
    +

    5. Elemental selectors

    +

    5.1. Type (tag name) selector

    +

    A type selector is the name of a document language element type, + and represents an instance of that element type in the document tree.

    +
    For example, the selector h1 represents an h1 element in the document.
    +

    A type selector is written as a CSS qualified name: + an identifier with an optional namespace prefix. [CSS3NAMESPACE] (See §5.3 Namespaces in Elemental Selectors.)

    +

    5.2. Universal selector

    +

    The universal selector is a special type selector, + that represents an element of any element type.

    +

    It is written a CSS qualified name with an asterisk (* U+002A) as the local name. + Like a type selector, + the universal selector can be qualified by a namespace, + restricting it to only elements belonging to that namespace, + and is affected by a default namespace as defined in §5.3 Namespaces in Elemental Selectors.

    +

    Unless an element is featureless, + the presence of a universal selector has no effect on whether the element matches the selector. + (Featureless elements do not match any selector, + including the universal selector.)

    +
    + +
      +
    • *[hreflang|=en] and [hreflang|=en] are equivalent, +
    • *.warning and .warning are equivalent, +
    • *#myid and #myid are equivalent. +
    +
    +

    The universal selector follows the same syntax rules as other type selectors: + only one can appear per compound selector, + and it must be the first simple selector in the compound selector.

    +

    Note: In some cases, adding a universal selector can make a selector easier to read, + even though it has no effect on the matching behavior. + For example, div :first-child and div:first-child are somewhat difficult to tell apart at a quick glance, + but writing the former as div *:first-child makes the difference obvious.

    +

    5.3. Namespaces in Elemental Selectors

    +

    Type selectors and universal selectors allow an optional namespace component: + a namespace prefix that has been previously declared may be prepended to the element name separated by the namespace separator “vertical bar” (| U+007C). + (See, e.g., [XML-NAMES] for the use of namespaces in XML.) + It has the following meaning in each form:

    +
    +
    ns|E +
    elements with name E in namespace ns +
    *|E +
    elements with name E in any namespace, + including those without a namespace +
    |E +
    elements with name E without a namespace +
    E +
    if no default namespace has been declared for selectors, + this is equivalent to *|E. + Otherwise it is equivalent to ns|E + where ns is the default namespace. +
    +
    + CSS examples: +
    @namespace foo url(http://www.example.com);
    +foo|h1 { color: blue }  /* first rule */
    +foo|* { color: yellow } /* second rule */
    +|h1 { color: red }      /* ...*/
    +*|h1 { color: green }
    +h1 { color: green }
    +
    +

    The first rule (not counting the @namespace at-rule) + will match only h1 elements in the + "http://www.example.com" namespace.

    +

    The second rule will match all elements in the + "http://www.example.com" namespace.

    +

    The third rule will match only h1 elements with + no namespace.

    +

    The fourth rule will match h1 elements in any + namespace (including those without any namespace).

    +

    The last rule is equivalent to the fourth rule because no default + namespace has been defined.

    +
    +

    If a default namespace is declared, compound selectors without type selectors in them + still only match elements in that default namespace.

    +
    + For example, + in the following style sheet: +
    @namespace url("http://example.com/foo");
    +
    +.special { ... }
    +
    +

    The .special selector only matches elements in the "http://example.com/foo" namespace, + even though no reference to the type name (which is paired with the namespace in the DOM) appeared.

    +
    +

    A type selector or universal selector containing a namespace prefix + that has not been previously declared is an invalid selector.

    +

    6. Attribute selectors

    +

    Selectors allow the representation of an element’s attributes. When + a selector is used as an expression to match against an element, + an attribute selector must be considered to match an element if that + element has an attribute that matches the attribute represented by the + attribute selector.

    +

    Add comma-separated syntax for multiple-value matching? + e.g. [rel ~= next, prev, up, first, last]

    +

    6.1. Attribute presence and value selectors

    +

    CSS2 introduced four attribute selectors:

    +
    +
    [att] +
    Represents an element with the att attribute, + whatever the value of the attribute. +
    [att=val] +
    Represents an element with the att attribute + whose value is exactly "val". +
    [att~=val] +
    Represents an element with the att attribute + whose value is a whitespace-separated list of words, + one of which is exactly "val". + If "val" contains whitespace, + it will never represent anything + (since the words are separated by spaces). + Also if "val" is the empty string, + it will never represent anything. +
    [att|=val] +
    Represents an element with the att attribute, + its value either being exactly "val" + or beginning with "val" immediately followed by "-" (U+002D). + This is primarily intended to allow language subcode matches + (e.g., the hreflang attribute on the a element in HTML) + as described in BCP 47 ([BCP47]) or its successor. + For lang (or xml:lang) language subcode matching, + please see the :lang pseudo-class. +
    +

    Attribute values must be <ident-token>s or <string-token>s. [CSS3SYN]

    +
    + Examples: +

    The following attribute selector represents an h1 element + that carries the title attribute, + whatever its value:

    +
    h1[title]
    +

    In the following example, the selector represents a span element whose class attribute has + exactly the value "example":

    +
    span[class="example"]
    +

    Multiple attribute selectors can be used to represent several + attributes of an element, or several conditions on the same + attribute. Here, the selector represents a span element + whose hello attribute has exactly the value "Cleveland" + and whose goodbye attribute has exactly the value + "Columbus":

    +
    span[hello="Cleveland"][goodbye="Columbus"]
    +

    The following CSS rules illustrate the differences between + "=" and "~=". The first selector would match, for example, an a element with the value "copyright copyleft + copyeditor" on a rel attribute. The second selector + would only match an a element with an href attribute having the exact value "http://www.w3.org/".

    +
    a[rel~="copyright"] { ... }
    +a[href="http://www.w3.org/"] { ... }
    +
    +

    The following selector represents an a element + whose hreflang attribute is exactly "fr".

    +
    a[hreflang=fr] 
    +

    The following selector represents an a element for + which the value of the hreflang attribute begins with + "en", including "en", "en-US", and "en-scouse":

    +
    a[hreflang|="en"] 
    +

    The following selectors represent a DIALOGUE element + whenever it has one of two different values for an attribute character:

    +
    DIALOGUE[character=romeo]
    +DIALOGUE[character=juliet]
    +
    +
    +

    6.2. Substring matching attribute selectors

    +

    Three additional attribute selectors are provided for matching + substrings in the value of an attribute:

    +
    +
    [att^=val] +
    Represents an element with the att attribute + whose value begins with the prefix "val". + If "val" is the empty string + then the selector does not represent anything. +
    [att$=val] +
    Represents an element with the att attribute + whose value ends with the suffix "val". + If "val" is the empty string + then the selector does not represent anything. +
    [att*=val] +
    Represents an element with the att attribute + whose value contains at least one instance of the substring "val". + If "val" is the empty string + then the selector does not represent anything. +
    +

    Attribute values must be <ident-token>s or <string-token>s.

    +
    + Examples: + The following selector represents an HTML object element, + referencing an image: +
    object[type^="image/"] 
    +

    The following selector represents an HTML a element + with an href attribute whose value ends with ".html".

    +
    a[href$=".html"] 
    +

    The following selector represents an HTML paragraph + with a title attribute whose value contains the substring "hello"

    +
    p[title*="hello"] 
    +
    +

    6.3. Case-sensitivity

    +

    By default case-sensitivity of attribute names and values in selectors + depends on the document language. To match attribute values case-insensitively + regardless of document language rules, the attribute selector may include the + identifier i before the closing bracket (]). + When this flag is present, UAs must match the attribute’s value + case-insensitively within the ASCII range. + Like the rest of Selectors syntax, + the i identifier is case-insensitive within the ASCII range.

    +
    + The following rule will style the frame attribute when it + has a value of hsides, whether that value is represented + as hsides, HSIDES, hSides, etc. + even in an XML environment where attribute values are case-sensitive. +
    [frame=hsides i] { border-style: solid none; } 
    +
    +

    6.4. Attribute selectors and namespaces

    +

    The attribute name in an attribute selector is given as a CSS qualified + name: a namespace prefix that has been previously declared may be prepended to the attribute name separated by the namespace + separator "vertical bar" (|). In keeping with + the Namespaces in the XML recommendation, default namespaces do not + apply to attributes, therefore attribute selectors without a namespace + component apply only to attributes that have no namespace (equivalent + to |attr). An asterisk may be used for + the namespace prefix indicating that the selector is to match all + attribute names without regard to the attribute’s namespace.

    +

    An attribute selector with an attribute name containing a namespace + prefix that has not been previously declared is + an invalid selector.

    +
    + CSS examples: +
    @namespace foo "http://www.example.com";
    +[foo|att=val] { color: blue }
    +[*|att] { color: yellow }
    +[|att] { color: green }
    +[att] { color: green }
    +
    +

    The first rule will match only elements with the attribute att in the "http://www.example.com" namespace with the + value "val".

    +

    The second rule will match only elements with the attribute att regardless of the namespace of the attribute + (including no namespace).

    +

    The last two rules are equivalent and will match only elements + with the attribute att where the attribute is not + in a namespace.

    +
    +

    6.5. Default attribute values in DTDs

    +

    Attribute selectors represent attribute values in the document tree. + How that document tree is constructed is outside the scope of Selectors. + In some document formats default attribute values can be defined in a DTD or + elsewhere, but these can only be selected by attribute selectors if they + appear in the document tree. Selectors should be designed so that they + work whether or not the default values are included in the document tree.

    +

    For example, a XML UA may, but is not required to, + read an “external subset” of the DTD, but is required to + look for default attribute values in the document’s “internal subset”. + (See, e.g., [XML10] for definitions of these subsets.) + Depending on the UA, a default attribute value defined in the external subset of the DTD + might or might not appear in the document tree.

    +

    A UA that recognizes an XML namespace may, but is not required to use its + knowledge of that namespace to treat default attribute values as if + they were present in the document. (For example, an XHTML UA is not + required to use its built-in knowledge of the XHTML DTD. See, e.g., [XML-NAMES] for details on namespaces in XML + 1.0.)

    +

    Note: Typically, implementations + choose to ignore external subsets. This corresponds to the behavior + of non-validating processors as defined by the XML specification.

    +
    + Example: +

    Consider an element EXAMPLE with an attribute radix that has a default value of "decimal". The DTD fragment might be

    +
    <!ATTLIST EXAMPLE radix (decimal,octal) "decimal"> 
    +

    If the style sheet contains the rules

    +
    EXAMPLE[radix=decimal] { /*... default property settings ...*/ }
    +EXAMPLE[radix=octal]   { /*... other settings...*/ }
    +
    +

    the first rule might not match elements whose radix attribute is + set by default, i.e. not set explicitly. To catch all cases, the + attribute selector for the default value must be dropped:

    +
    EXAMPLE                { /*... default property settings ...*/ }
    +EXAMPLE[radix=octal]   { /*... other settings...*/ }
    +
    +

    Here, because the selector ''EXAMPLE[radix=octal]'' is + more specific than the type selector alone, the style declarations in + the second rule will override those in the first for elements that + have a radix attribute value of "octal". Care has to be taken that + all property declarations that are to apply only to the default case + are overridden in the non-default cases' style rules.

    +
    +

    6.6. Class selectors

    +

    The class selector is given as a full stop (. U+002E) immediately + followed by an identifier. It represents an element belonging to the + class identified by the identifier, as defined by the document language. + For example, in [HTML5], [SVG11], and [MATHML] membership in a + class is given by the class attribute: in these languages + it is equivalent to the ~= notation applied to the + local class attribute + (i.e. [class~=identifier]).

    +
    + CSS examples: +

    We can assign style information to all elements with class~="pastoral" as follows:

    +
    *.pastoral { color: green }  /* all elements with class~=pastoral */ 
    +

    or just

    +
    .pastoral { color: green }  /* all elements with class~=pastoral */ 
    +

    The following assigns style only to H1 elements with class~="pastoral":

    +
    H1.pastoral { color: green }  /* H1 elements with class~=pastoral */ 
    +

    Given these rules, the first H1 instance below would not have + green text, while the second would:

    +
    <H1>Not green</H1>
    +<H1 class="pastoral">Very green</H1>
    +
    +

    The following rule matches any P element whose class attribute has been assigned a list of whitespace-separated values that includes both pastoral and marine:

    +
    p.pastoral.marine { color: green } 
    +

    This rule matches when class="pastoral blue aqua + marine" but does not match for class="pastoral + blue".

    +
    +

    Note: Because CSS gives considerable + power to the "class" attribute, authors could conceivably design their + own "document language" based on elements with almost no associated + presentation (such as div and span in HTML) + and assigning style + information through the "class" attribute. Authors should avoid this + practice since the structural elements of a document language often + have recognized and accepted meanings and author-defined classes may + not.

    +

    Note: If an element has multiple + class attributes, their values must be concatenated with spaces + between the values before searching for the class. As of this time the + working group is not aware of any manner in which this situation can + be reached, however, so this behavior is explicitly non-normative in + this specification.

    +

    When matching against a document which is in quirks mode, + class names must be matched ASCII case-insensitively; + class selectors are otherwise case-sensitive.

    +

    6.7. ID selectors

    +

    Document languages may contain attributes that are declared to be of type ID. + What makes attributes of type ID special + is that no two such attributes can have the same value in a conformant document, + regardless of the type of the elements that carry them; + whatever the document language, + an ID typed attribute can be used to uniquely identify its element. + In HTML all ID attributes are named id; + XML applications may name ID attributes differently, + but the same restriction applies. + Which attribute on an element is considered the “ID attribute“ is defined by the document language.

    +

    An ID selector consists of a “number sign” (U+0023, #) + immediately followed by the ID value, + which must be a CSS identifier. + An ID selector represents an element instance that has an identifier that matches the identifier in the ID selector. + (It is possible in non-conforming documents for multiple elements to match a single ID selector.)

    +
    + Examples: + The following ID selector represents an h1 element + whose ID-typed attribute has the value "chapter1": +
    h1#chapter1 
    +

    The following ID selector represents any element whose ID-typed + attribute has the value "chapter1":

    +
    #chapter1 
    +

    The following selector represents any element whose ID-typed + attribute has the value "z98y".

    +
    *#z98y 
    +
    +

    Note: In XML 1.0 [XML10], the information about which attribute + contains an element’s IDs is contained in a DTD or a schema. When + parsing XML, UAs do not always read the DTD, and thus may not know + what the ID of an element is (though a UA may have namespace-specific + knowledge that allows it to determine which attribute is the ID + attribute for that namespace). If a style sheet author knows or + suspects that a UA may not know what the ID of an element is, he + should use normal attribute selectors instead: + ''[name=p371] instead of #p371''.

    +

    If an element has multiple ID attributes, all of them must be + treated as IDs for that element for the purposes of the ID + selector. Such a situation could be reached using mixtures of xml:id, + DOM3 Core, XML DTDs, and namespace-specific knowledge.

    +

    When matching against a document which is in quirks mode, + IDs must be matched ASCII case-insensitively; + ID selectors are otherwise case-sensitive.

    +

    7. Linguistic Pseudo-classes

    +

    7.1. The Directionality Pseudo-class: :dir()

    +

    The :dir() pseudo-class allows the author to write + selectors that represent an element based on its directionality + as determined by the document language. + For example, [HTML5] defines how to determine the directionality of an element, + based on a combination of the dir attribute, the surrounding text, and other factors. + As another example, the its:dir and dirRule element + of the Internationalization Tag Set [ITS20] are able to define the directionality of an element in [XML10].

    +

    The :dir() pseudo-class does not select based on stylistic + states—for example, the CSS direction property does not affect + whether it matches.

    +

    The pseudo-class :dir(ltr) represents an element that + has a directionality of left-to-right (ltr). The + pseudo-class :dir(rtl) represents an element that has + a directionality of right-to-left (rtl). The argument to :dir() must be a single identifier, otherwise the selector + is invalid. White space is optionally allowed between the identifier + and the parentheses. Values other than ltr and rtl are not invalid, but do not match anything. (If a + future markup spec defines other directionalities, then Selectors may + be extended to allow corresponding values.)

    +

    The difference between :dir(C) and ''[dir=C]'' + is that ''[dir=C]'' only performs a comparison against a given + attribute on the element, while the :dir(C) pseudo-class + uses the UAs knowledge of the document’s semantics to perform the + comparison. For example, in HTML, the directionality of an element + inherits so that a child without a dir attribute will have + the same directionality as its closest ancestor with a valid dir attribute. As another example, in HTML, + an element that matches ''[dir=auto]'' will match either :dir(ltr) or :dir(rtl) depending on the resolved + directionality of the elements as determined by its contents. [HTML5]

    +

    7.2. The Language Pseudo-class: :lang()

    +

    If the document language specifies how + the (human) content language of an element is determined, + it is possible to write selectors that + represent an element based on its content language. + The :lang() pseudo-class represents an element that + is in one of the languages listed in its argument. It accepts + a comma-separated list of one or more language ranges as its + argument. Each language range in :lang() must be a valid CSS <ident> or <string>. + (Language ranges containing asterisks, for example, + must be quoted as strings.)

    +

    Note: The content language of an element is defined by the document language. + For example, in HTML [HTML5], the content language is determined by a + combination of the lang attribute, information from meta elements, and possibly also the protocol (e.g. + from HTTP headers). XML languages can use the xml:lang attribute to indicate language information for an element. [XML10]

    +

    The element’s content language matches a language range if + its content language (normalized to BCP 47 syntax if necessary) + matches the given language range in an extended filtering operation per [RFC4647] Matching of Language Tags (section 3.3.2). + The matching is performed case-insensitively within the ASCII range. + The language range does not need to be a valid language code to + perform this comparison.

    +

    Note: It is recommended that + documents and protocols indicate language using codes from BCP 47 [BCP47] or its successor, and by means of xml:lang attributes in the + case of XML-based documents [XML10]. See "FAQ: Two-letter or three-letter language codes."

    +
    + Examples: + The two following selectors represent an HTML document that is in + Belgian French or German. The two next selectors represent q quotations in an arbitrary element in Belgian French + or German. +
    html:lang(fr-be)
    +html:lang(de)
    +:lang(fr-be) > q
    +:lang(de) > q
    +
    +
    +

    Note: One difference between :lang(C) and the ''|='' operator + is that the ''|='' operator only performs a comparison against a given + attribute on the element, while the :lang(C) pseudo-class + uses the UAs knowledge of the document’s semantics to perform the + comparison.

    +
    + In this HTML example, only the BODY matches + ''[lang|=fr]'' (because it has a LANG attribute) but both + the BODY and the P match :lang(fr) (because both are in + French). The P does not match the ''[lang|=fr]'' because it + does not have a LANG attribute. +
    <body lang=fr>
    +  <p>Je suis français.</p>
    +</body>
    +
    +
    +
    + Another difference between :lang(C) and the ''|='' operator + is that :lang(C) performs implicit wildcard matching. +

    For example, :lang(de-DE) will match all of de-DE, de-DE-1996, de-Latn-DE, de-Latf-DE, de-Latn-DE-1996, + whereas of those ''[lang|=de-DE] will only match de-DE'' and de-DE-1996.

    +

    To perform wildcard matching on the first subtag (the primary language), + an asterisk must be used: *-CH will match all of de-CH, it-CH, fr-CH, and rm-CH.

    +

    To select against an element’s lang attribute value + using this type of language range match, + use both the attribute selector and language pseudo-class together, + e.g. [lang]:lang(de-DE).

    +
    +

    Note: Wildcard language matching and comma-separated lists are new in Level 4.

    +

    8. Location Pseudo-classes

    + +

    The :any-link pseudo-class represents an element + that acts as the source anchor of a hyperlink. + For example, in [HTML5], any a, area, or link elements with an href attribute + are hyperlinks, and thus match :any-link. + It matches an element if the element would match either :link or :visited, + and is equivalent to :matches(:link, :visited).

    + +

    User agents commonly display unvisited hyperlinks differently from + previously visited ones. Selectors + provides the pseudo-classes :link and :visited to distinguish them:

    +
      +
    • The :link pseudo-class applies to links that have + not yet been visited. +
    • The :visited pseudo-class applies once the link has + been visited by the user. +
    +

    After some amount of time, user agents may choose to return a + visited link to the (unvisited) :link state.

    +

    The two states are mutually exclusive.

    +
    + The following selector represents links carrying class footnote and already visited: +
    .footnote:visited 
    +
    +

    Since it is possible for style sheet authors to abuse the :link and :visited pseudo-classes + to determine which sites a user has visited without the user’s consent, + UAs may treat all links as unvisited links + or implement other measures to preserve the user’s privacy + while rendering visited and unvisited links differently.

    + +

    The :local-link pseudo-class + allows authors to style hyperlinks based on the users current location within a site. + It represents an element that is + the source anchor of a hyperlink whose target’s absolute URL + matches the element’s own document URL. + If the hyperlink’s target includes a fragment URL, + then the fragment URL of the current URL must also match; + if it does not, then the fragment URL portion of the current URL + is not taken into account in the comparison.

    +
    + For example, the following rule prevents links targetting the + current page from being underlined when they are part of the + navigation list: +
    nav :local-link { text-decoration: none; } 
    +
    +

    Note: The current URL of a page can change as a result of user actions + such as activating a link targetting a different fragment within the same page; + or by use of the pushState API; + as well as by the more obvious actions of navigating to a different page + or following a redirect (which could be initiated by protocols such as HTTP, + markup instructions such as <meta http-equiv="...">, + or scripting instructions ). + UAs must ensure that :local-link, + as well as the :target and :target-within pseudo-classes below, + respond correctly to all such changes in state.

    +

    8.4. The Target Pseudo-class: :target

    +

    In some document languages, + the document’s URL can further point to specific elements within the document + via the URL’s fragment. + The elements pointed to in this way are the target elements of the document.

    +
    In HTML the fragment points to the element in the page with the same ID. + The url https://example.com/index.html#section2, + for example, + points to the element with id="section2" in the document at https://example.com/index.html.
    +

    The :target pseudo-class matches the document’s target elements. + If the document’s URL has no fragment identifier, then the document has no target elements.

    +
    + Example: +
    p.note:target 
    +

    This selector represents a p element of class note that is the target element of the referring + URL.

    +
    +
    + CSS example: + Here, the :target pseudo-class is used to make the + target element red and place an image before it, if there is one: +
    :target { color : red }
    +:target::before { content : url(target.png) }
    +
    +
    +

    8.5. The Target Container Pseudo-class: :target-within

    +

    The :target-within pseudo-class + applies to elements for which the :target pseudo class applies + as well as to an element whose descendant in the flat tree (including non-element nodes, such as text nodes) + matches the conditions for matching :target-within.

    +

    8.6. The Reference Element Pseudo-class: :scope

    +

    In some contexts, selectors can be matched with an explicit set of :scope elements. + This is is a (potentially empty) set of elements + that provide a reference point for selectors to match against, + such as that specified by the querySelector() call in [DOM].

    +

    The :scope pseudo-class represents any element that is a :scope element. + If the :scope elements are not explicitly specified, + but the selector is scoped and the scoping root is an element, + then :scope represents the scoping root; + otherwise, it represents the root of the document + (equivalent to :root). + Specifications intending for this pseudo-class to match specific elements + rather than the document’s root element + must define either a scoping root (if using scoped selectors) or an explicit set of :scope elements.

    +

    9. User Action Pseudo-classes

    +

    Interactive user interfaces sometimes change the rendering in response to user actions. + Selectors provides several user action pseudo-classes for the selection of an element the user is acting on. + (In non-interactive user agents, these pseudo-classes are valid, but never match any element.)

    +

    These pseudo-classes are not mutually exclusive. + An element can match several such pseudo-classes at the same time.

    +
    + Examples: +
    a:link    /* unvisited links */
    +a:visited /* visited links */
    +a:hover   /* user hovers */
    +a:active  /* active links */
    +
    +

    An example of combining dynamic pseudo-classes:

    +
    a:focus
    +a:focus:hover
    +
    +

    The last selector matches a elements that are in + the pseudo-class :focus and in the pseudo-class :hover.

    +
    +

    Note: The specifics of hit-testing, + necessary to know when several of the pseudo-classes defined in this section apply, + are not yet defined, + but will be in the future.

    +

    9.1. The Pointer Hover Pseudo-class: :hover

    +

    The :hover pseudo-class applies + while the user designates an element with a pointing device, + but does not necessarily activate it. + For example, a visual user agent could apply this pseudo-class + when the cursor (mouse pointer) hovers over a box generated by the element. + Interactive user agents that cannot detect hovering due to hardware limitations + (e.g., a pen device that does not detect hovering) + are still conforming; + the selector will simply never match in such a UA.

    +

    An element also matches :hover if one of its descendants in the flat tree (including non-element nodes, such as text nodes) + matches the above conditions.

    +

    Document languages may define additional ways in which an element can match :hover. + For example, [HTML5] defines a labeled control element as matching :hover when its label is hovered.

    +

    Note: Since the :hover state can apply to an element + because its child is designated by a pointing device, + it is possible for :hover to apply + to an element that is not underneath the pointing device.

    +

    The :hover pseudo-class can apply to any pseudo-element.

    +

    9.2. The Activation Pseudo-class: :active

    +

    The :active pseudo-class applies while an element + is being activated by the user. For example, between the times the + user presses the mouse button and releases it. On systems with more + than one mouse button, :active applies only to the + primary or primary activation button (typically the "left" mouse + button), and any aliases thereof.

    +

    There may be document-language or implementation-specific limits on + which elements can become :active. + For example, [HTML5] defines a list of activatable elements.

    +

    An element also matches :active if one of its descendants in the flat tree (including non-element nodes, such as text nodes) + matches the above conditions.

    +

    Document languages may define additional ways in which an element can match :active.

    +

    Note: An element can be both :visited and :active (or :link and :active).

    +

    9.3. The Input Focus Pseudo-class: :focus

    +

    The :focus pseudo-class applies + while an element has the focus + (accepts keyboard or mouse events, or other forms of input).

    +

    There may be document language or implementation specific limits on + which elements can acquire :focus. + For example, [HTML] defines a list of focusable areas.

    +

    Document languages may define additional ways in which an element can match :focus, + except that the :focus pseudo class must not automatically propagate to the parent element—see :focus-within if matching on the parent is desired. + (It may still apply to the parent element + if made to propagate due to other mechanisms, + but not merely due to being the parent.)

    +

    There’s a desire from authors to propagate :focus from a form control to its associated label element; + the main objection seems to be implementation difficulty. + See CSSWG issue (CSS) and WHATWG issue (HTML).

    +

    9.4. The Focus-Indicated Pseudo-class: :focus-visible

    +

    The :focus-visible pseudo-class applies + while an element matches the :focus pseudo-class and the UA determines via heuristics + that the focus should be made evident on the element. + (Many browsers show a “focus ring” by default in this case.)

    +
    + For example, UAs typically display focus indicators on text fields + any time they’re focused, + to draw attention to the fact that keyboard input will affect their contents. +

    On the other hand, UAs typically only display focus indicators on buttons + when they were focused by a keyboard interaction + (such as tabbing through the document)—because it’s not always immediately obvious + where the focus has gone after such an interaction, + but is sufficiently self-evident when the button was focused + by more obviously-targetted interactions, + like clicking on the button with a mouse pointer.

    +
    +
    + Page authors should follow these guidelines + when deciding whether to use :focus or :focus-visible to style the focused state of an element: +
      +
    • +

      If the element has “native” focus indicator behavior +(such as text fields or buttons), +use :focus-visible.

      +
    • +

      Otherwise, if the element is emulating a text input, +or something else that is intended to receive keyboard interaction, +use :focus.

      +
    • +

      Otherwise, +use :focus-visible.

      +
    +
    +

    When UAs choose to specially indicate focus on an element, + or whether they specially indicate focus at all, + is UA-dependent. + Different UAs, + the same UA on different operating systems, + or the same UA on the same OS, + but with different user settings, + can make different choices as to when an element matches :focus-visible.

    +
    + The following guidelines are suggested heuristics + for choosing when to apply :focus-visible to elements without “native” focus indicator behavior: +
      +
    • +

      If the element received focus via a keyboard interaction, +including indirectly, +such as triggering a dialog +by pressing a button using the keyboard, +apply :focus-visible.

      +
    • +

      If a keyboard event occurs while an element is focused, +even if the element wasn’t focused by a keyboard interaction, +apply :focus-visible.

      +
    +
    +

    9.5. The Focus Container Pseudo-class: :focus-within

    +

    The :focus-within pseudo-class + applies to any element for which the :focus pseudo class applies + as well as to an element whose descendant in the flat tree (including non-element nodes, such as text nodes) + matches the conditions for matching :focus.

    +

    9.6. The Drop Target Pseudo-class: :drop and :drop()

    +

    The :drop pseudo-class applies to all elements + that are drop targets, + as defined by the document language, + while the user is “dragging” + or otherwise conceptually carrying an item + to be “dropped”.

    +

    The :drop() functional pseudo-class is identical to :drop, + but allows additional filters to be specified that can exclude some drop targets. + Its syntax is:

    +
    :drop( [ active || valid || invalid ]? ) 
    +

    The keywords have the following meanings:

    +
    +
    active +
    The drop target is the current drop target for the drag operation. + That is, if the user were to release the drag, + it would be dropped onto this drop target. +
    valid +
    + If the document language has a concept of “valid” and “invalid” drop targets, + this only matches if the drop target is valid for the object currently being dragged. + Otherwise, it matches all drop targets. +

    For example, HTML’s dropzone attribute can specify that the drop target only accepts strings or files that are set to a given type.

    +
    invalid +
    If the document language has a concept of “valid” and “invalid” drop targets, + this only matches if the drop target is invalid for the object currently being dragged. + Otherwise, it matches nothing. +
    +

    Multiple keywords can be combined in the argument, + representing only drop targets that satisfy all of the keywords. + For example, :drop(valid active) will match the active drop target if it’s valid, + but not if it’s invalid.

    +

    If no keywords are given in the argument, :drop() has the same meaning as :dropit matches every drop target.

    +

    Turn this scenario into an example.

    +

    10. Time-dimensional Pseudo-classes

    +

    These pseudo-classes classify elements with respect to the + currently-displayed or active position in some timeline, such as + during speech rendering of a document, or during the display of + a video using WebVTT to render subtitles.

    +

    CSS does not define this timeline; + the host language must do so. + If there is no timeline defined for an element, + these pseudo-classes must not match the element.

    +

    Note: Ancestors of a :current element are also :current, + but ancestors of a :past or :future element are not necessarily :past or :future as well. + A given element matches at most one of :current, :past, or :future.

    +

    10.1. The Current-element Pseudo-class: :current

    +

    The :current pseudo-class represents the + element, or an ancestor of the element, that is currently being displayed.

    +

    Its alternate form :current(), like :matches(), + takes a list of compound selectors as its argument: it represents the :current element that matches the argument or, if that does + not match, the innermost ancestor of the :current element + that does. (If neither the :current element nor its ancestors + match the argument, then the selector does not represent anything.)

    +
    + For example, the following rule will highlight whichever paragraph + or list item is being read aloud in a speech rendering of the document: +
    :current(p, li, dt, dd) {
    +  background: yellow;
    +}
    +
    +
    +

    10.2. The Past-element Pseudo-class: :past

    +

    The :past pseudo-class represents any element that is + defined to occur entirely prior to a :current element. + For example, the WebVTT spec defines the :past pseudo-class relative to the current playback position of a media element. + If a time-based order of elements is not defined by the document language, + then this represents any element that is a (possibly indirect) previous + sibling of a :current element.

    +

    10.3. The Future-element Pseudo-class: :future

    +

    The :future pseudo-class represents any element that is + defined to occur entirely after a :current element. + For example, the WebVTT spec defines the :future pseudo-class relative to the current playback position of a media element. + If a time-based order of elements is not defined by the document language, + then this represents any element that is a (possibly indirect) next + sibling of a :current element.

    +

    11. Resource State Pseudos

    +

    The pseudo-classes in this section apply to elements that represent loaded resources, + particularly images/videos, + and allow authors to select them based on some quality of their state.

    +

    11.1. Video/Audio Play State: the :playing and :paused pseudo-classes

    +

    The :playing pseudo-class represents an element + representing an audio, video, or similar resource + that is capable of being “played” or “paused”, + when that element is “playing”. + (This includes both when the element is explicitly playing, + and when it’s temporarily stopped for some reason not connected to user intent, + but will automatically resume when that reason is resolved, + such as a “buffering” state.)

    +

    The :paused pseudo-class represents the same elements, + but instead match when the element is not “playing”. + (This includes both an explicit “paused” state, + and other non-playing states like “loaded, hasn’t been activated yet”, etc.)

    +

    12. The Input Pseudo-classes

    +

    The pseudo-classes in this section mostly apply to elements that take user input, + such as HTML’s input element.

    +

    12.1. Input Control States

    +

    12.1.1. The :enabled and :disabled Pseudo-classes

    +

    The :enabled pseudo-class represents + user interface elements that are in an enabled state; + such elements must have a corresponding disabled state.

    +

    Conversely, the :disabled pseudo-class represents + user interface elements that are in a disabled state; + such elements must have a corresponding enabled state.

    +

    What constitutes an enabled state, a disabled state, and a user interface + element is host-language-dependent. In a typical document most elements will be + neither :enabled nor :disabled. + For example, [HTML5] defines non-disabled interactive elements to be :enabled, + and any such elements that are explicitly disabled to be :disabled.

    +

    Note: CSS properties that might affect a user’s ability + to interact with a given user interface element do not affect whether it + matches :enabled or :disabled; e.g., the display and visibility properties have no effect + on the enabled/disabled state of an element.

    +

    12.1.2. The Mutability Pseudo-classes: :read-only and :read-write

    +

    An element matches :read-write if it is user-alterable, + as defined by the document language. + Otherwise, it is :read-only.

    +

    For example, in [HTML5] a non-disabled non-readonly <input> element is :read-write, + as is any element with the contenteditable attribute set to the true state.

    +

    12.1.3. The Placeholder-shown Pseudo-class: :placeholder-shown

    +

    Input elements can sometimes show placeholder text + as a hint to the user on what to type in. + See, for example, the placeholder attribute in [HTML5]. + The :placeholder-shown pseudo-class + matches an input element that is showing such placeholder text.

    +

    12.1.4. The Default-option Pseudo-class: :default

    +

    The :default pseudo-class applies to the one or more UI elements + that are the default among a set of similar elements. Typically applies to + context menu items, buttons and select lists/menus.

    +

    One example is the default submit button among a set of buttons. + Another example is the default option from a popup menu. + In a select-many group (such as for pizza toppings), multiple elements can match :default. + For example, [HTML5] defines that :default matches the “default button” in a form, + the initially-selected <option>(s) in a <select>, + and a few other elements.

    +

    12.2. Input Value States

    +

    12.2.1. The Selected-option Pseudo-class: :checked

    +

    Radio and checkbox elements can be toggled by the user. + Some menu items are “checked” when the user selects them. + When such elements are toggled “on” + the :checked pseudo-class applies. + For example, [HTML5] defines that checked checkboxes, radio buttons, and selected <option> elements match :checked.

    +

    While the :checked pseudo-class is dynamic in nature, + and can altered by user action, + since it can also be based on the presence of semantic attributes in the document + (such as the selected and checked attributes in [HTML5]), + it applies to all media.

    +
    + An unchecked checkbox can be selected by using the negation + pseudo-class: +
    input[type=checkbox]:not(:checked)
    +
    +

    12.2.2. The Indeterminate-value Pseudo-class: :indeterminate

    +

    The :indeterminate pseudo-class applies to UI elements whose + value is in an indeterminate state. + For example, radio and checkbox elements can be toggled between checked and unchecked states, + but are sometimes in an indeterminate state, neither checked nor unchecked. + Similarly a progress meter can be in an indeterminate state when the percent completion is unknown. + For example, [HTML5] defines how checkboxes can be made to match :indeterminate.

    +

    Like the :checked pseudo-class, :indeterminate applies to all media. Components of a radio-group initialized with no + pre-selected choice, for example, would be :indeterminate even in a static display.

    +

    12.3. Input Value-checking

    +

    12.3.1. The Validity Pseudo-classes: :valid and :invalid

    +

    An element is :valid or :invalid when its contents or value is, respectively, + valid or invalid with respect to data validity semantics defined by the document language + (e.g. [XFORMS11] or [HTML5]). + An element which lacks data validity semantics is neither :valid nor :invalid.

    +

    Note: There is a difference between an element which has no constraints, + and thus would always be :valid, + and one which has no data validity semantics at all, + and thus is neither :valid nor :invalid. + In HTML, for example, an <input type="text"> element may have no constraints, + but a p element has no validity semantics at all, + and so it never matches either of these pseudo-classes.

    +

    12.3.2. The Range Pseudo-classes: :in-range and :out-of-range

    +

    The :in-range and :out-of-range pseudo-classes + apply only to elements that have range limitations. An element is :in-range or :out-of-range when the value + that the element is bound to is in range or out of range with respect + to its range limits as defined by the document language. An element + that lacks data range limits or is not a form control is neither :in-range nor :out-of-range. + E.g. a slider element with a value of 11 presented as a slider control + that only represents the values from 1-10 is :out-of-range. Another + example is a menu element with a value of "E" that happens to be + presented in a popup menu that only has choices "A", "B" and "C".

    +

    12.3.3. The Optionality Pseudo-classes: :required and :optional

    +

    A form element is :required or :optional if a value for it is, respectively, required or optional before the + form it belongs to can be validly submitted. Elements that are not + form elements are neither required nor optional.

    +

    12.3.4. The User-interaction Pseudo-class: :user-invalid

    +

    The :user-invalid pseudo-class + represents an element with incorrect input, but only after the user has significantly interacted with it. + The :user-invalid pseudo-class + must match an :invalid, :out-of-range, or blank-but-:required elements + between the time the user has attempted to submit the form + and before the user has interacted again with the form element. + User-agents may allow it to match such elements at other times, + as would be appropriate for highlighting an error to the user. + For example, a UA may choose to have :user-invalid match + an :invalid element once the user has typed some text into it + and changed the focus to another element, + and to stop matching only after the user has successfully corrected the input.

    +
    + For example, the input in the following document fragment + would match :invalid as soon as the page is loaded + (because it the initial value violates the max-constraint), + but it won’t match :user-invalid until the user significantly interacts with the element, + or attempts to submit the form it’s part of. +
    <form>
    +  <label>
    +    Volume:
    +    <input name='vol' type=number min=0 max=10 value=11>
    +  </label>
    +  ...
    +</form>
    +
    +
    +

    Cross-check with :-moz-ui-invalid.

    +

    Add :-moz-ui-valid as :user-valid per WG resolution.

    +

    Evaluate proposed :dirty pseudo-class

    +

    Clarify that this (and :invalid/:valid) can apply to form and fieldset elements.

    +

    13. Tree-Structural pseudo-classes

    +

    Selectors introduces the concept of structural pseudo-classes to permit selection based on extra information that lies in + the document tree but cannot be represented by other simple selectors or + combinators.

    +

    Standalone text and other non-element + nodes are not counted when calculating the position of an element in the list + of children of its parent. When calculating the position of an element in + the list of children of its parent, the index numbering starts at 1.

    +

    The structural pseudo-classes only apply to elements in the document tree; + they must never match pseudo-elements.

    +

    13.1. :root pseudo-class

    +

    The :root pseudo-class represents an element that is + the root of the document.

    +

    For example, in a DOM document, + the :root pseudo-class matches the root element of the Document object. + In HTML, this would be the html element + (unless scripting has been used to modify the document).

    +

    13.2. :empty pseudo-class

    +

    The :empty pseudo-class represents an element that has no children at all. + In terms of the document tree, + only element nodes and content nodes + (such as [DOM] text nodes, and entity references) + whose data has a non-zero length must be considered as affecting emptiness; + comments, processing instructions, and other nodes + must not affect whether an element is considered empty or not.

    +
    + Examples: p:empty is a valid representation of the following fragment: +
    <p></p>
    +

    foo:empty is not a valid representation for the + following fragments:

    +
    <foo>bar</foo>
    +
    <foo><bar>bla</bar></foo>
    +
    <foo>this is not <bar>:empty</bar></foo>
    +
    +

    The WG is considering whether to allow elements containing only white space to match this selector. + The advantage would be that—as white space is largely collapsible +in HTML and is therefore used for source code formatting, and especially + because elements with omitted end tags are likely to absorb such white +space into their DOM text contents—many elements which authors +perceive of as empty would be selected by this selector, as they expect. + The disadvantages are a potential conflict with Web-compat if there +exist pages that depend on this selector excluding white space; + and that one might consider uncollapsed white space to be significant +content, but the selector cannot change its behavior based on the white-space property. + See discussion.

    +

    13.3. :blank pseudo-class

    +

    The :blank pseudo-class is like the :empty pseudo-class, + except that it additionally matches elements that only contain code points affected by whitespace processing. [CSS3TEXT]

    +
    + For example, the following element matches :blank, + but not :empty, + because it contains at least one linebreak, and possibly other whitespace: +
    <p>    
    </p>
    +
    +

    The WG is considering whether to rename this or file its definition under the existing :empty pseudo-class. + See discussion. + There’s also a related issue on a selector for empty input fields which might legitimately steal this name.

    +

    13.4. Child-indexed Pseudo-classes

    +

    The pseudo-classes defined in this section select elements + based on their index amongst their inclusive siblings.

    +

    Note: Selectors 3 described these selectors as selecting elements based on their index in the child list of their parents. + (This description survives in the name of this very section, and the names of several of the pseudo-classes.) + As there was no reason to exclude them from matching elements without parents, + or with non-element parents, + they have been rephrased to refer to an element’s relative index amongst its siblings.

    +

    13.4.1. :nth-child() pseudo-class

    +

    The :nth-child(An+B [of S]? ) pseudo-class notation represents elements that + are among An+Bth elements + from the list composed of + their inclusive siblings that match the selector list S. + If S is omitted, + it defaults to *|*.

    +

    The An+B notation and its interpretation + are defined in CSS Syntax 3 §6 The An+B microsyntax; + it represents any index i = An + B for any positive integer n.

    +

    For example, this selector could address every other row in a table, + and could be used to alternate the color of paragraph text in a cycle of four.

    +
    + Examples: +
    :nth-child(even)   /* represents the 2nd, 4th, 6th, etc elements
    +:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc elements */
    +:nth-child(10n+9)  /* Same */
    +:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */
    +
    +
    +

    Note: The specificity of the :nth-child() pseudo-class is + the specificity of a single pseudo-class plus + the specificity of its selector argument S, if any. + See §16 Calculating a selector’s specificity. + Thus S:nth-child(An+B) and :nth-child(An+B of S) have the exact same specificity, + although they do differ in behavior + (see example below).

    +
    + By passing a selector argument, + we can select the Nth element that matches that selector. + For example, the following selector matches the first three “important” list items, + denoted by the .important class: +
    :nth-child(-n+3 of li.important)
    +

    Note that this is different from moving the selector outside of the function, like:

    +
    li.important:nth-child(-n+3)
    +

    This selector instead just selects the first three children + if they also happen to be "important" list items.

    +
    +
    + Here’s another example of using the selector argument, + to ensure that zebra-striping a table works correctly. +

    Normally, to zebra-stripe a table’s rows, + an author would use CSS similar to the following:

    +
    tr {
    +  background: white;
    +}
    +tr:nth-child(even) {
    +  background: silver;
    +}
    +
    +

    However, if some of the rows are hidden and not displayed, + this can break up the pattern, + causing multiple adjacent rows to have the same background color. + Assuming that rows are hidden with the [hidden] attribute in HTML, + the following CSS would zebra-stripe the table rows robustly, + maintaining a proper alternating background + regardless of which rows are hidden:

    +
    tr {
    +  background: white;
    +}
    +tr:nth-child(even of :not([hidden])) {
    +  background: silver;
    +}
    +
    +
    +

    13.4.2. :nth-last-child() pseudo-class

    +

    The :nth-last-child(An+B [of S]? ) pseudo-class notation represents elements that + are among An+Bth elements + from the list composed of + their inclusive siblings that match the selector list S, + counting backwards from the end. + If S is omitted, + it defaults to *|*.

    +

    Note: The specificity of the :nth-last-child() pseudo-class, + like the :nth-child() pseudo-class, + combines the specificity of a regular pseudo-class + with that of its selector argument S. + See §16 Calculating a selector’s specificity.

    +

    The CSS Syntax Module [CSS3SYN] defines the An+B notation.

    +
    + Examples: +
    tr:nth-last-child(-n+2)    /* represents the two last rows of an HTML table */
    +
    +foo:nth-last-child(odd)    /* represents all odd foo elements in their parent element,
    +                              counting from the last one */
    +
    +
    +

    13.4.3. :first-child pseudo-class

    +

    The :first-child pseudo-class + represents an element that if first among its inclusive siblings. + Same as :nth-child(1).

    +
    + Examples: + The following selector represents a p element that is + the first child of a div element: +
    div > p:first-child
    +

    This selector can represent the p inside the div of the following fragment:

    +
    <p> The last P before the note.</p>
    +<div class="note">
    +   <p> The first P inside the note.</p>
    +</div>
    +
    +

    but cannot represent the second p in the following fragment:

    +
    <p> The last P before the note.</p>
    +<div class="note">
    +   <h2> Note </h2>
    +   <p> The first P inside the note.</p>
    +</div>
    +
    +

    The following two selectors are usually equivalent:

    +
    * > a:first-child /* a is first child of any element */
    +a:first-child /* Same (assuming a is not the root element) */
    +
    +
    +

    13.4.4. :last-child pseudo-class

    +

    The :last-child pseudo-class + represents an element that is last among its inclusive siblings. + Same as :nth-last-child(1).

    +
    + Example: + The following selector represents a list item li that + is the last child of an ordered list ol. +
    ol > li:last-child
    +
    +

    13.4.5. :only-child pseudo-class

    +

    The :only-child pseudo-class + represents an element that has no siblings. + Same as :first-child:last-child or :nth-child(1):nth-last-child(1), + but with a lower specificity.

    +

    13.5. Typed Child-indexed Pseudo-classes

    +

    The pseudo-elements in this section are similar to the Child Index Pseudo-classes, + but they resolve based on an element’s index among elements of the same type (tag name) in their sibling list.

    +

    13.5.1. :nth-of-type() pseudo-class

    +

    The :nth-of-type(An+B) pseudo-class notation + represents the same elements that would be matched by :nth-child(|An+B| of S), + where S is a type selector and namespace prefix matching the element in question. + For example, + when considering whether an HTML img element matches this pseudo-class, + the S in question is html|img (assuming an appropriate html namespace is declared).

    +
    + CSS example: + This allows an author to alternate the position of floated images: +
    img:nth-of-type(2n+1) { float: right; }
    +img:nth-of-type(2n) { float: left; }
    +
    +
    +

    Note: If the type of the element is known ahead of time, + this pseudo-class is equivalent to using :nth-child() with a type selector. + That is, img:nth-of-type(2) is equivalent to *:nth-child(2 of img).

    +

    13.5.2. :nth-last-of-type() pseudo-class

    +

    The :nth-last-of-type(An+B) pseudo-class notation + represents the same elements that would be matched by :nth-last-child(|An+B| of S), + where S is a type selector and namespace prefix matching the element in question. + For example, + when considering whether an HTML img element matches this pseudo-class, + the S in question is html|img (assuming an appropriate html namespace is declared).

    +
    + Example: + To represent all h2 children of an XHTML body except the first and last, one could use the + following selector: +
    body > h2:nth-of-type(n+2):nth-last-of-type(n+2) 
    +

    In this case, one could also use :not(), although the + selector ends up being just as long:

    +
    body > h2:not(:first-of-type):not(:last-of-type) 
    +
    +

    13.5.3. :first-of-type pseudo-class

    +

    The :first-of-type pseudo-class + represents the same element as :nth-of-type(1).

    +
    + Example: + The following selector represents a definition title dt inside a definition list dl, this dt being the first of its type in the list of children of + its parent element. +
    dl dt:first-of-type
    +

    It is a valid description for the first two dt elements in the following example but not for the third one:

    +
    <dl>
    +  <dt>gigogne</dt>
    +  <dd>
    +    <dl>
    +      <dt>fusée</dt>
    +      <dd>multistage rocket</dd>
    +      <dt>table</dt>
    +      <dd>nest of tables</dd>
    +    </dl>
    +  </dd>
    +</dl>
    +
    +
    +

    13.5.4. :last-of-type pseudo-class

    +

    The :last-of-type pseudo-class + represents the same element as :nth-last-of-type(1).

    +
    + Example: + The following selector represents the last data cell td of a table row tr. +
    tr > td:last-of-type
    +
    +

    13.5.5. :only-of-type pseudo-class

    +

    The :only-of-type pseudo-class + represents the same element as :first-of-type:last-of-type.

    +

    14. Combinators

    +

    14.1. Descendant combinator ( )

    +

    At times, authors may want selectors to describe an element that is + the descendant of another element in the document tree (e.g., "an em element that is contained within an H1 element"). + The descendant combinator expresses such a relationship.

    +

    A descendant combinator is whitespace that separates two compound selectors.

    +

    A selector of the form A B represents an element B that is an + arbitrary descendant of some ancestor element A.

    +
    + Examples: + For example, consider the following selector: +
    h1 em
    +

    It represents an em element being the descendant of + an h1 element. It is a correct and valid, but partial, + description of the following fragment:

    +
    <h1>This <span class="myclass">headline
    +is <em>very</em> important</span></h1>
    +
    +

    The following selector:

    +
    div * p
    +

    represents a p element that is a grandchild or later + descendant of a div element. Note the whitespace on + either side of the "*" is not part of the universal selector; the + whitespace is a combinator indicating that the div must be the + ancestor of some element, and that that element must be an ancestor + of the p. + The following selector, which combines descendant combinators and attribute selectors, represents an + element that (1) has the href attribute set and (2) is + inside a p that is itself inside a div:

    +
    div p *[href]
    +
    +

    14.2. Child combinator (>)

    +

    A child combinator describes a childhood relationship + between two elements. A child combinator is made of the + "greater-than sign" (U+003E, >) code point and + separates two compound selectors.

    +
    + Examples: + The following selector represents a p element that is + child of body: +
    body > p
    +

    The following example combines descendant combinators and child + combinators.

    +
    div ol>li p
    +

    It represents a p element that is a descendant of an li element; the li element must be the + child of an ol element; the ol element must + be a descendant of a div. Notice that the optional white + space around the ">" combinator has been left out.

    +
    +

    For information on selecting the first child of an element, + please see the section on the :first-child pseudo-class above.

    +

    14.3. Next-sibling combinator (+)

    +

    The next-sibling combinator is made of the “plus sign” + (U+002B, +) code point that separates two compound selectors. + The elements represented by the two compound selectors share the same parent in the document tree + and the element represented by the first compound selector immediately precedes the element represented by the second one. + Non-element nodes (e.g. text between elements) + are ignored when considering the adjacency of elements.

    +
    + Examples: + The following selector represents a p element + immediately following a math element: +
    math + p
    +

    The following selector is conceptually similar to the one in the + previous example, except that it adds an attribute selector — it + adds a constraint to the h1 element, that it must have class="opener":

    +
    h1.opener + h2
    +
    +

    14.4. Subsequent-sibling combinator (~)

    +

    The subsequent-sibling combinator is made of the "tilde" + (U+007E, ~) code point that separates two compound selectors. + The elements represented by the two compound selectors share + the same parent in the document tree and the element represented by + the first compound selector precedes (not necessarily immediately) the element + represented by the second one.

    +
    + +
    h1 ~ pre
    +

    represents a pre element following an h1. It + is a correct and valid, but partial, description of:

    +
    <h1>Definition of the function a</h1>
    +<p>Function a(x) has to be applied to all figures in the table.</p>
    +<pre>function a(x) = 12x/13.5</pre>
    +
    +
    +

    15. Grid-Structural Selectors

    +

    The double-association of a cell in a 2D grid (to its row and column) + cannot be represented by parentage in a hierarchical markup language. + Only one of those associations can be represented hierarchically: the + other must be explicitly or implicitly defined in the document language + semantics. In both HTML and DocBook, two of the most common hierarchical + markup languages, the markup is row-primary (that is, the row associations + are represented hierarchically); the columns must be implied. + To be able to represent such implied column-based relationships, the column combinator and the :nth-col() and :nth-last-col() pseudo-classes + are defined. + In a column-primary format, these pseudo-classes match against row associations instead.

    +

    15.1. Column combinator

    +

    The column combinator, which consists of two pipes (||) + represents the relationship of a column element + to a cell element belonging to the column it represents. + Column membership is determined based on the semantics of the document language only: + whether and how the elements are presented is not considered. + If a cell element belongs to more than one column, + it is represented by a selector indicating membership in any of those columns.

    +
    + The following example makes cells C, E, and G gray. +
    col.selected || td {
    +  background: gray;
    +  color: white;
    +  font-weight: bold;
    +}
    +
    +
    <table>
    +  <col span="2">
    +  <col class="selected">
    +  <tr><td>A <td>B <td>C
    +  <tr><td colspan="2">D <td>E
    +  <tr><td>F <td colspan="2">G
    +</table>
    +
    +
    +

    15.2. :nth-col() pseudo-class

    +

    The :nth-col(An+B) pseudo-class notation represents a cell element belonging to a column + that has An+B-1 columns before it, for any positive + integer or zero value of n. Column membership is determined + based on the semantics of the document language only: whether and how the + elements are presented is not considered. If a cell element belongs to + more than one column, it is represented by a selector indicating any of + those columns.

    +

    The CSS Syntax Module [CSS3SYN] defines the An+B notation.

    +

    15.3. :nth-last-col() pseudo-class

    +

    The :nth-last-col(An+B) pseudo-class notation represents a cell element belonging to a column + that has An+B-1 columns after it, for any positive + integer or zero value of n. Column membership is determined + based on the semantics of the document language only: whether and how the + elements are presented is not considered. If a cell element belongs to + more than one column, it is represented by a selector indicating any of + those columns.

    +

    The CSS Syntax Module [CSS3SYN] defines the An+B notation.

    +

    16. Calculating a selector’s specificity

    +

    A selector’s specificity is calculated for a given element as follows:

    +
      +
    • count the number of ID selectors in the selector (= A) +
    • count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B) +
    • count the number of type selectors and pseudo-elements in the selector (= C) +
    • ignore the universal selector +
    +

    If the selector is a selector list, + this number is calculated for each selector in the list. + For a given matching process against the list, the specificity in effect + is that of the most specific selector in the list that matches.

    +

    A few pseudo-classes provide “evaluation contexts” for other selectors, + and so have their specificity defined by their contents and how they match:

    +

    See also issue 1027.

    +
      +
    • The specificity of a :matches() or :has() pseudo-class is replaced by + the specificity applicable to its selector list argument. + (When :matches() contains only compound selectors, + the full selector’s specificity is thus equivalent to + expanding out all the combinations in full, without :matches(); + fully generalized, the specificity of :matches() is + the specificity of the most specific selector within it that matches. + The behavior for comma-separated arguments to :has() is analogous.) +
    • Analogously, the specificity of an :nth-child() or :nth-last-child() selector + is the specificity of the pseudo class itself (counting as one pseudo-class selector) + plus the specificity of its selector list argument (if any). +
    • The specificity of a :not() pseudo-class is replaced by + the specificity of the most specific complex selector in its selector list argument. +
    • The specificity of a :something() pseudo-class is replaced by zero. +
    +
    + For example: +
      +
    • :matches(em, #foo) has + a specificity of (0,0,1)--like a tag selector--when matched against <em>, + and a specificity of (1,0,0)--like an ID selector--when matched against <em id=foo>. +
    • div:something(em, #foo#bar#baz) has + a specificity of (0,0,1): only the div contributes to selector specificity. +
    • :nth-child(even of li, .item) has + a specificity of (0,1,1)--like a tag selector plus a pseudo-class--when matched against <li>, + and a specificity of (0,2,0)--like a class selector plus a pseudo-class--when matched against <li class=item id=foo>. +
    • :not(em, #foo) has + a specificity of (1,0,0)--like an ID selector--whenever it matches any element. +
    +
    +

    Specificities are compared by comparing the three components in order: + the specificity with a larger A value is more specific; + if the two A values are tied, + then the specificity with a larger B value is more specific; + if the two B values are also tied, + then the specificity with a larger C value is more specific; + if all the values are tied, + the two specificities are equal.

    +

    Due to storage limitations, + implementations may have limitations on the size of A, B, or C. + If so, values higher than the limit must be clamped to that limit, + and not overflow.

    +
    + Examples: +
    *               /* a=0 b=0 c=0 */
    +LI              /* a=0 b=0 c=1 */
    +UL LI           /* a=0 b=0 c=2 */
    +UL OL+LI        /* a=0 b=0 c=3 */
    +H1 + *[REL=up]  /* a=0 b=1 c=1 */
    +UL OL LI.red    /* a=0 b=1 c=3 */
    +LI.red.level    /* a=0 b=2 c=1 */
    +#x34y           /* a=1 b=0 c=0 */
    +#s12:not(FOO)   /* a=1 b=0 c=1 */
    +.foo :matches(.bar, #baz)
    +                /* Either a=1 b=1 c=0
    +                   or a=0 b=2 c=0, depending
    +                   on the element being matched. */
    +
    +
    +

    Note: Repeated occurrences of the + same simple selector are allowed and do increase specificity.

    +

    Note: The specificity of the styles + specified in an HTML style attribute is described in CSS Style Attributes. [CSSSTYLEATTR]

    +

    17. Grammar

    +

    Selectors are parsed according to the following grammar:

    +
    <selector-list> = <complex-selector-list>
    +
    +<complex-selector-list> = <complex-selector>#
    +
    +<compound-selector-list> = <compound-selector>#
    +
    +<simple-selector-list> = <simple-selector>#
    +
    +<relative-selector-list> = <relative-selector>#
    +
    +
    +<complex-selector> = <compound-selector> [ <combinator>? <compound-selector> ]*
    +
    +<relative-selector> = <combinator>? <complex-selector>
    +
    +<compound-selector> = [ <type-selector>? <subclass-selector>*
    +                        [ <pseudo-element-selector> <pseudo-class-selector>* ]* ]!
    +
    +<simple-selector> = <type-selector> | <subclass-selector>
    +
    +
    +<combinator> = '>' | '+' | '~' | [ '||' ]
    +
    +<type-selector> = <wq-name> | <ns-prefix>? '*'
    +
    +<ns-prefix> = [ <ident-token> | '*' ]? '|'
    +
    +<wq-name> = <ns-prefix>? <ident-token>
    +
    +<subclass-selector> = <id-selector> | <class-selector> |
    +                      <attribute-selector> | <pseudo-class-selector>
    +
    +<id-selector> = <hash-token>
    +
    +<class-selector> = '.' <ident-token>
    +
    +<attribute-selector> = '[' <wq-name> ']' |
    +                       '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'
    +
    +<attr-matcher> = [ '~' | '|' | '^' | '$' | '*' ]? '='
    +
    +<attr-modifier> = i
    +
    +<pseudo-class-selector> = ':' <ident-token> |
    +                          ':' <function-token> <any-value> ')'
    +
    +<pseudo-element-selector> = ':' <pseudo-class-selector>
    +
    +

    In interpreting the above grammar, + the following rules apply:

    + +

    Note: A selector is also subject to a variety of more specific syntactic constraints, + and adherence to the grammar above is necessary but not sufficient for the selector to be considered valid. + See §3.9 Invalid Selectors and Error Handling for additional rules for parsing selectors.

    +

    Note: The grammar above states that a combinator is optional + between two <compound-selector>s in a <complex-selector>. + This is only for grammatical purposes, + as the CSS Value Definition Syntax’s lax treatment of whitespace + makes it difficult to indicate that a grammar term can be whitespace. + "Omitting" a combinator is actually just specifying the descendant combinator.

    +

    Note: In general, + a <pseudo-element-selector> is only valid + if placed at the end of the last <compound-selector> in a <complex-selector>. + In some circumstances, however, + it can be followed by more <pseudo-element-selector>s or <pseudo-class-selector>s; + but these are specified on a case-by-case basis. + (For example, the user action pseudo-classes are allowed after any pseudo-element, + and the tree-abiding pseudo-elements are allowed after the ::slotted() pseudo-element)

    +

    18. API Hooks

    +

    To aid in the writing of specs that use Selectors concepts, + this section defines several API hooks that can be invoked by other specifications.

    +

    Are these still necessary now that we have more rigorous definitions for match and invalid selector? + Nouns are a lot easier to coordinate across specification than predicates, + and details like the exact order of elements returned from querySelector seem to make more sense being defined in the DOM specification than in Selectors.

    +

    18.1. Parse A Selector

    +

    This section defines how to parse a selector from a string source. + It returns either a complex selector list, + or failure.

    +
      +
    1. Let selector be the result of parsing source as a <selector-list>. + If it does not match the grammar, + return failure. +
    2. Otherwise, + if any simple selectors in selector are not recognized by the user agent, + or selector is otherwise invalid in some way + (such as, for example, containing an undeclared namespace prefix), + return failure. +
    3. Otherwise, + return selector. +
    +

    18.2. Parse A Relative Selector

    +

    This section defines how to parse a relative selector from a string source, + against :scope elements refs. + It returns either a complex selector list, + or failure.

    +
      +
    1. Let selector be the result of parsing source as a <relative-selector-list>. + If it does not match the grammar, + return failure. +
    2. Otherwise, + if any simple selectors in selector are not recognized by the user agent, + or selector is otherwise invalid in some way + (such as, for example, containing an undeclared namespace prefix), + return failure. +
    3. Otherwise, absolutize selector with refs as the :scope elements. +
    4. Return selector. +
    +

    18.3. Match a Selector Against an Element

    +

    This section defines how to match a selector against an element.

    +

    APIs using this algorithm must provide a selector and an element.

    +

    Callers may optionally provide:

    +
      +
    • + a set of :scope elements, + for resolving the :scope pseudo-class against. + If not specified, + the set defaults to being empty. +

      Should it instead default to the root element, + to match the definition of :scope?

      +

      If the selector is a relative selector, + the set of :scope elements must not be empty.

      +
    +

    This algorithm returns either success or failure.

    +

    For each complex selector in the given selector (which is taken to be a list of complex selectors), + match the complex selector against element, + as described in the following paragraph. + If the matching returns success for any complex selector, + then the algorithm return success; otherwise it returns failure.

    +

    To match a complex selector against an element, + process it compound selector at a time, + in right-to-left order. + This process is defined recursively as follows:

    +
      +
    • If any simple selectors in the rightmost compound selector + does not match the element, return failure. +
    • Otherwise, if there is only one compound selector in the + complex selector, return success. +
    • Otherwise, consider all possible elements + that could be related to this element by the rightmost combinator. + If the operation of matching the selector consisting of this selector + with the rightmost compound selector and rightmost combinator removed + against any one of these elements returns success, then return success. + Otherwise, return failure. +
    +

    18.4. Match a Selector Against a Pseudo-element

    +

    This section defines how to match a selector against a pseudo-element.

    +

    APIs using this algorithm must provide a selector and a pseudo-element. + They may optionally provide the same things they may optionally provide + to the algorithm to match a selector against an element.

    +

    This algorithm returns success or failure.

    +

    For each complex selector in the given selector, if both:

    +
      +
    • the rightmost simple selector in the complex selector + matches pseudo-element, and +
    • the result of running match a complex selector against an element on the remainder of the complex selector (with just the rightmost simple selector + of its rightmost complex selector removed), pseudo-element’s corresponding element, + and any optional parameters provided to this algorithm + returns success, +
    + then return success. +

    Otherwise + (that is, if this doesn’t happen for any of the complex selectors in selector), + return failure.

    +

    18.5. Match a Selector Against a Tree

    +

    This section defines how to match a selector against a tree.

    +

    APIs using this algorithm must provide a selector, + and one or more root elements indicating the trees that will be searched by the selector. + All of the root elements must share the same root, + or else calling this algorithm is invalid.

    +

    They may optionally provide:

    +
      +
    • + A scoping root indicating the selector is scoped. + If not specified, + the selector defaults to being unscoped. +

      This is now redundant with the root elements.

      +
    • + A set of :scope elements, + which will match the :scope pseudo-class. + If not specified, + then if the selector is a scoped selector, + the set of :scope elements default to the scoping root; + otherwise, + it defaults to the root elements. +

      Note: Note that if the selector is scoped, + the scoping root is automatically taken as the :scope element, + so it doesn’t have to be provided explicitly + unless a different result is desired.

      +
    • + Which pseudo-elements are allowed to show up in the match list. + If not specified, this defaults to allowing all pseudo-elements. +

      Only the ::before and ::after pseudo-elements are really + handled in any way remotely like this.

      +
    +

    This algorithm returns a (possibly empty) list of elements.

    +
      +
    1. Start with a list of candidate elements, + which are the the root elements and all of their descendant elements, + sorted in shadow-including tree order, + unless otherwise specified. +
    2. If an optional scoping root was provided, + then remove from the candidate elements any elements that are not descendants of the scoping root. +
    3. Initialize the selector match list to empty. +
    4. + For each element in the set of candidate elements: +
        +
      1. If the result of match a selector against an element for element and selector is success, + add element to the selector match list. +
      2. + For each possible pseudo-element associated with element that is one of the pseudo-elements allowed to show up in the match list, + if the result of match a selector against a pseudo-element for the pseudo-element and selector is success, + add the pseudo-element to the selector match list. +

        The relative position of pseudo-elements + in selector match list is undefined. + There’s not yet a context that exposes this information, + but we need to decide on something eventually, + before something is exposed.

        +
      +
    +

    19. Appendix A: Guidance on Mapping Source Documents & Data to an Element Tree

    +

    This section is informative.

    +

    The element tree structure described by the DOM is powerful and useful, + but generic enough to model pretty much any language that describes tree-based data + (or even graph-based, with a suitable interpretation).

    +

    Some languages, like HTML, already have well-defined procedures + for producing a DOM object from a resource. + If a given language does not, + such a procedure must be defined + in order for Selectors to apply to documents in that language.

    +

    At minimum, + the document language must define what maps to the DOM concept of an "element".

    +

    The primary one-to-many relationship between nodes—parent/child in tree-based structures, + element/neighbors in graph-based structures—should be reflected as the child nodes of an element.

    +

    Other features of the element should be mapped + to something that serves a similar purpose to the same feature in DOM:

    +
    +
    type +
    + If the elements in the document language have some notion of "type" + as a basic distinguisher between different groups of elements, + it should be reflected as the "type" feature. +

    If this "type" can be separated into a "basic" name + and a "namespace" that groups names into higher-level groups, + the latter should be reflected as the "namespace" feature. + Otherwise, the element shouldn’t have a "namespace" feature, + and the entire name should be reflected as the "type" feature.

    +
    id +
    + If some aspect of the element functions as a unique identifier across the document, + it should be mapped to the "id" feature. +

    Note: While HTML only allows an element to have a single ID, + this should not be taken as a general restriction. + The important quality of an ID is that each ID should be associated with a single element; + a single element can validly have multiple IDs.

    +
    classes and attributes +
    Aspects of the element that are useful for identifying the element, + but are not generally unique to elements within a document, + should be mapped to the "class" or "attribute" features + depending on if they’re something equivalent to a "label" (a string by itself) + or a "property" (a name/value pair) +
    pseudo-classes and pseudo-attributes +
    + If any elements match any pseudo-classes or have any pseudo-elements, + that must be explicitly defined. +

    Some pseudo-classes are *syntactical*, + like :has() and :matches(), + and thus should always work. + Need to indicate that somewhere. + Probably the structural pseudos always work + whenever the child list is ordered.

    +
    +
    + For example, JSONSelect is a library that uses selectors + to extract information from JSON documents. +
      +
    • The "elements" of the JSON document + are each array, object, boolean, string, number, or null. + The array and object elements have their contents as children. +
    • Each element’s type is its JS type name: + "array", "object", etc. +
    • Children of an object + have their key as a class. +
    • Children of an array match the :first-child, :nth-child(), etc pseudo-classes. +
    • The root object matches :root. +
    • It additionally defines :val() and :contains() pseudo-classes, + for matching boolean/number/string elements with a particular value + or which contain a particular substring. +
    +

    This structure is sufficient to allow powerful, compact querying of JSON documents with selectors.

    +
    +

    20. Changes

    +

    Significant changes since the 2 May 2013 Working Draft include:

    +
      +
    • Added the :target-within, :focus-within, :focus-visible, :playing, and :paused pseudo-classes. +
    • Added a zero-specificity :matches()-type pseudo-class, with name TBD. +
    • Replaced subject indicator (!) feature with :has(). +
    • Replaced the :nth-match() and :nth-last-match() selectors + with :nth-child(… of selector) and :nth-last-child(… of selector). +
    • Changed the :active-drop-target, :valid-drop-target, :invalid-drop-target with :drop(). +
    • Sketched out an empty-or-whitespace-only selector for discussion (:blank). + (See open issue.) +
    • Renamed :user-error to :user-invalid. + (See Discussion) +
    • Renamed :nth-column()/:nth-last-column() to :nth-col()/:nth-last-col() to avoid naming confusion with a potential ::column pseudo-class. +
    • Changed the non-functional form of the :local-link pseudo-class to account for fragment URLs. +
    • Removed the functional form of the :local-link() pseudo-class and reference combinator for lack of interest. +
    • Rewrote selectors grammar using the CSS Value Definition Syntax. +
    • Split out relative selectors from scoped selectors, + as these are different concepts that can be independently invoked. +
    • + Moved definition of <An+B> microsyntax to CSS Syntax. +

      Semantic definition should probably move back here.

      +
    • + Added new sections: +
        +
      • + §3.2 Data Model +

        Need to define tree for XML.

        +
      • + §18 API Hooks +
          +
        • Note that earlier versions of this section defined a section on evaluating a selector, + but that section is no longer present. + Specifications referencing that section should instead reference + the algorithm to match a selector against a tree. +
        +
      +
    • Removed restriction on combinators within :matches() and :not(); + see discussion. +
    • Defined specificity of a selector list. (Why?) +
    • Required quotes around :lang() values involving an asterisk; + only language codes which happen to bhe CSS identifiers can be used unquoted. +
    +

    Note: The 1 February 2018 draft included an inadvertent commit of unfinished work; + 2 February 2018 has reverted this commit (and fixed some links because why not).

    +

    21. Acknowledgements

    +

    The CSS working group would like to thank everyone who contributed + to the previous Selectors specifications over the years, + as those specifications formed the basis for this one. + In particular, the working group would like to extend special thanks + to the following for their specific contributions to Selectors Level 4: + L. David Baron, + Andrew Fedoniouk, + Ian Hickson, + Grey Hodge, + Lachlan Hunt, + Jason Cranford Teague

    +

    22. Privacy and Security Considerations

    +

    This specification introduces no new privacy or security considerations, +as selectors do not provide any ability not already possible by walking the DOM manually.

    +
    +

    Conformance

    +

    Document conventions

    +

    Conformance requirements are expressed with a combination of + descriptive assertions and RFC 2119 terminology. The key words “MUST”, + “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, + “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this + document are to be interpreted as described in RFC 2119. + However, for readability, these words do not appear in all uppercase + letters in this specification.

    +

    All of the text of this specification is normative except sections + explicitly marked as non-normative, examples, and notes. [RFC2119]

    +

    Examples in this specification are introduced with the words “for example” + or are set apart from the normative text with class="example", + like this:

    +
    + +

    This is an example of an informative example.

    +
    +

    Informative notes begin with the word “Note” and are set apart from the + normative text with class="note", like this:

    +

    Note, this is an informative note.

    +

    Advisements are normative sections styled to evoke special attention and are + set apart from other normative text with <strong class="advisement">, like + this: UAs MUST provide an accessible alternative.

    +

    Conformance classes

    +

    Conformance to this specification + is defined for three conformance classes:

    +
    +
    style sheet +
    A CSS + style sheet. +
    renderer +
    A UA that interprets the semantics of a style sheet and renders + documents that use them. +
    authoring tool +
    A UA that writes a style sheet. +
    +

    A style sheet is conformant to this specification + if all of its statements that use syntax defined in this module are valid + according to the generic CSS grammar and the individual grammars of each + feature defined in this module.

    +

    A renderer is conformant to this specification + if, in addition to interpreting the style sheet as defined by the + appropriate specifications, it supports all the features defined + by this specification by parsing them correctly + and rendering the document accordingly. However, the inability of a + UA to correctly render a document due to limitations of the device + does not make the UA non-conformant. (For example, a UA is not + required to render color on a monochrome monitor.)

    +

    An authoring tool is conformant to this specification + if it writes style sheets that are syntactically correct according to the + generic CSS grammar and the individual grammars of each feature in + this module, and meet all other conformance requirements of style sheets + as described in this module.

    +

    Requirements for Responsible Implementation of CSS

    +

    The following sections define several conformance requirements + for implementing CSS responsibly, + in a way that promotes interoperability in the present and future.

    +

    Partial Implementations

    +

    So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid + (and ignore as appropriate) + any at-rules, properties, property values, keywords, and other syntactic constructs + for which they have no usable level of support. + In particular, user agents must not selectively ignore + unsupported property values and honor supported values in a single multi-value property declaration: + if any value is considered invalid (as unsupported values must be), + CSS requires that the entire declaration be ignored.

    +

    Implementations of Unstable and Proprietary Features

    +

    To avoid clashes with future stable CSS features, + the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.

    +

    Implementations of CR-level Features

    +

    Once a specification reaches the Candidate Recommendation stage, + implementers should release an unprefixed implementation + of any CR-level feature they can demonstrate + to be correctly implemented according to spec, + and should avoid exposing a prefixed variant of that feature.

    +

    To establish and maintain the interoperability of CSS across + implementations, the CSS Working Group requests that non-experimental + CSS renderers submit an implementation report (and, if necessary, the + testcases used for that implementation report) to the W3C before + releasing an unprefixed implementation of any CSS features. Testcases + submitted to W3C are subject to review and correction by the CSS + Working Group.

    +

    + Further information on submitting testcases and implementation reports + can be found from on the CSS Working Group’s website at https://www.w3.org/Style/CSS/Test/. + Questions should be directed to the public-css-testsuite@w3.org mailing list. + +

    +

    Index

    +

    Terms defined by this specification

    + +

    Terms defined by reference

    + +

    References

    +

    Normative References

    +
    +
    [CSS-DISPLAY-3] +
    Elika Etemad. CSS Display Module Level 3. 20 July 2017. WD. URL: https://www.w3.org/TR/css-display-3/ +
    [CSS-PSEUDO-4] +
    Daniel Glazman; Elika Etemad; Alan Stearns. CSS Pseudo-Elements Module Level 4. 7 June 2016. WD. URL: https://www.w3.org/TR/css-pseudo-4/ +
    [CSS-SCOPING-1] +
    Tab Atkins Jr.; Elika Etemad. CSS Scoping Module Level 1. 3 April 2014. WD. URL: https://www.w3.org/TR/css-scoping-1/ +
    [CSS-VALUES-3] +
    Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 29 September 2016. CR. URL: https://www.w3.org/TR/css-values-3/ +
    [CSS-WRITING-MODES-3] +
    Elika Etemad; Koji Ishii. CSS Writing Modes Level 3. 15 December 2015. CR. URL: https://www.w3.org/TR/css-writing-modes-3/ +
    [CSS21] +
    Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 7 June 2011. REC. URL: https://www.w3.org/TR/CSS2/ +
    [CSS3NAMESPACE] +
    Elika Etemad. CSS Namespaces Module Level 3. 20 March 2014. REC. URL: https://www.w3.org/TR/css-namespaces-3/ +
    [CSS3SYN] +
    Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 20 February 2014. CR. URL: https://www.w3.org/TR/css-syntax-3/ +
    [CSS3TEXT] +
    Elika Etemad; Koji Ishii. CSS Text Module Level 3. 22 August 2017. WD. URL: https://www.w3.org/TR/css-text-3/ +
    [DOM] +
    Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/ +
    [HTML] +
    Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/ +
    [INFRA] +
    Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/ +
    [RFC2119] +
    S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119 +
    [SELECT] +
    Tantek Çelik; et al. Selectors Level 3. 29 September 2011. REC. URL: https://www.w3.org/TR/css3-selectors/ +
    [SVG2] +
    Nikos Andronikos; et al. Scalable Vector Graphics (SVG) 2. 15 September 2016. CR. URL: https://www.w3.org/TR/SVG2/ +
    [URL] +
    Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/ +
    +

    Informative References

    +
    +
    [BCP47] +
    A. Phillips; M. Davis. Tags for Identifying Languages. September 2009. IETF Best Current Practice. URL: https://tools.ietf.org/html/bcp47 +
    [CSS22] +
    Bert Bos. Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification. 12 April 2016. WD. URL: https://www.w3.org/TR/CSS22/ +
    [CSS3UI] +
    Tantek Çelik; Florian Rivoal. CSS Basic User Interface Module Level 3 (CSS3 UI). 2 March 2017. CR. URL: https://www.w3.org/TR/css-ui-3/ +
    [CSSSTYLEATTR] +
    Tantek Çelik; Elika Etemad. CSS Style Attributes. 7 November 2013. REC. URL: https://www.w3.org/TR/css-style-attr/ +
    [HTML5] +
    Ian Hickson; et al. HTML5. 28 October 2014. REC. URL: https://www.w3.org/TR/html5/ +
    [ITS20] +
    David Filip; et al. Internationalization Tag Set (ITS) Version 2.0. 29 October 2013. REC. URL: https://www.w3.org/TR/its20/ +
    [MATHML] +
    Patrick D F Ion; Robert R Miner. Mathematical Markup Language (MathML) 1.01 Specification. 7 July 1999. REC. URL: https://www.w3.org/TR/REC-MathML/ +
    [RFC4647] +
    A. Phillips; M. Davis. Matching of Language Tags. September 2006. Best Current Practice. URL: https://tools.ietf.org/html/rfc4647 +
    [SVG11] +
    Erik Dahlström; et al. Scalable Vector Graphics (SVG) 1.1 (Second Edition). 16 August 2011. REC. URL: https://www.w3.org/TR/SVG11/ +
    [XFORMS11] +
    John Boyer. XForms 1.1. 20 October 2009. REC. URL: https://www.w3.org/TR/xforms11/ +
    [XML-NAMES] +
    Tim Bray; et al. Namespaces in XML 1.0 (Third Edition). 8 December 2009. REC. URL: https://www.w3.org/TR/xml-names/ +
    [XML10] +
    Tim Bray; et al. Extensible Markup Language (XML) 1.0 (Fifth Edition). 26 November 2008. REC. URL: https://www.w3.org/TR/xml/ +
    +

    Issues Index

    +
    +
    The categorization of things into the “live” or snapshot profiles needs implementor review. + If some things currently not in the live profile can reasonably be done in CSS Selectors, + we should move them.
    +
    Pseudo-elements aren’t handled here, and should be.
    + +
    Clarify that :not() and :matches() can be used when containing above-mentioned pseudos.
    +
    Does ::first-line:not(:focus) match anything?
    +
    Why this exception for the explicit universal selector?
    +
    This pseudo-class needs a name. See previous discussion, open issue.
    +
    Add comma-separated syntax for multiple-value matching? + e.g. [rel ~= next, prev, up, first, last]
    +
    There’s a desire from authors to propagate :focus from a form control to its associated label element; + the main objection seems to be implementation difficulty. + See CSSWG issue (CSS) and WHATWG issue (HTML).
    +
    Turn this scenario into an example.
    +
    Cross-check with :-moz-ui-invalid.
    +
    Add :-moz-ui-valid as :user-valid per WG resolution.
    +
    Evaluate proposed :dirty pseudo-class
    +
    Clarify that this (and :invalid/:valid) can apply to form and fieldset elements.
    +
    The WG is considering whether to allow elements containing only white space to match this selector. + The advantage would be that—as white space is largely collapsible +in HTML and is therefore used for source code formatting, and especially + because elements with omitted end tags are likely to absorb such white +space into their DOM text contents—many elements which authors +perceive of as empty would be selected by this selector, as they expect. + The disadvantages are a potential conflict with Web-compat if there +exist pages that depend on this selector excluding white space; + and that one might consider uncollapsed white space to be significant +content, but the selector cannot change its behavior based on the white-space property. + See discussion.
    +
    The WG is considering whether to rename this or file its definition under the existing :empty pseudo-class. + See discussion. + There’s also a related issue on a selector for empty input fields which might legitimately steal this name.
    +
    See also issue 1027.
    +
    Are these still necessary now that we have more rigorous definitions for match and invalid selector? + Nouns are a lot easier to coordinate across specification than predicates, + and details like the exact order of elements returned from querySelector seem to make more sense being defined in the DOM specification than in Selectors.
    +
    Should it instead default to the root element, + to match the definition of :scope?
    +
    This is now redundant with the root elements.
    +
    Only the ::before and ::after pseudo-elements are really + handled in any way remotely like this.
    +
    The relative position of pseudo-elements + in selector match list is undefined. + There’s not yet a context that exposes this information, + but we need to decide on something eventually, + before something is exposed.
    +
    Some pseudo-classes are *syntactical*, + like :has() and :matches(), + and thus should always work. + Need to indicate that somewhere. + Probably the structural pseudos always work + whenever the child list is ordered.
    +
    Semantic definition should probably move back here.
    +
    Need to define tree for XML.
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts new file mode 100644 index 0000000..60eae9b --- /dev/null +++ b/test_new/browser/w3c.test.ts @@ -0,0 +1,473 @@ +import { expect } from 'playwright/test'; +import { runScenarios } from './harness'; +import { run } from 'node:test'; + +runScenarios('w3c iframes', 'normal', [ + { + name: 'syntax', + html: ` + + Selectors: syntax of case-sensitivity attribute selector + + +
    +
    + + + `, + setupPage: async (page) => { + page.on('console', (msg) => { console.log(`[browser:${msg.type()}] ${msg.text()}`); }); + page.on('pageerror', (err) => { console.log(`[pageerror] ${err.message}`); }); + + const results = await page.evaluate(async () => { + + const valid = [ + "[foo='BAR'] /* sanity check (valid) */", + "[foo='bar' i]", + "[foo='bar' I]", + "[foo=bar i]", + '[foo="bar" i]', + "[foo='bar'i]", + "[foo='bar'i ]", + "[foo='bar' i ]", + "[foo='bar' /**/ i]", + "[foo='bar' i /**/ ]", + "[foo='bar'/**/i/**/]", + "[foo=bar/**/i]", + "[foo='bar'\ti\t] /* \\t */", + "[foo='bar'\ni\n] /* \\n */", + "[foo='bar'\ri\r] /* \\r */", + "[foo='bar' \\i]", + "[foo='bar' \\69]", + "[foo~='bar' i]", + "[foo^='bar' i]", + "[foo$='bar' i]", + "[foo*='bar' i]", + "[foo|='bar' i]", + "[|foo='bar' i]", + "[*|foo='bar' i]", + ]; + + const invalid = [ + "[foo[ /* sanity check (invalid) */", + "[foo='bar' i i]", + "[foo i ='bar']", + "[foo= i 'bar']", + "[i foo='bar']", + "[foo='bar' i\u0000] /* \\0 */", + "[foo='bar' \u0130]", + "[foo='bar' \u0131]", + "[foo='bar' ii]", + "[foo='bar' ij]", + "[foo='bar' j]", + "[foo='bar' \\\\i]", + "[foo='bar' \\\\69]", + "[foo='bar' i()]", + "[foo='bar' i ()]", + "[foo='bar' () i]", + "[foo='bar' (i)]", + "[foo='bar' i []]", + "[foo='bar' [] i]", + "[foo='bar' [i]]", + "[foo='bar' i {}]", + "[foo='bar' {} i]", + "[foo='bar' {i}]", + "[foo='bar' 1i]", + "[foo='bar' 1]", + "[foo='bar' 'i']", + "[foo='bar' url(i)]", + "[foo='bar' ,i]", + "[foo='bar' i,]", + "[foo='bar']i", + "[foo='bar' |i]", + "[foo='bar' \\|i]", + "[foo='bar' *|i]", + "[foo='bar' \\*|i]", + "[foo='bar' *]", + "[foo='bar' \\*]", + "[foo i]", + "[foo/**/i]", + ]; + + const quirksFrame = document.querySelector('#quirks')!; + const xmlFrame = document.querySelector('#xml')!; + + const waitForLoad = (frame: HTMLIFrameElement) => + new Promise((resolve) => { + frame.addEventListener('load', () => resolve(), { once: true }); + }); + + const quirksLoaded = waitForLoad(quirksFrame); + const xmlLoaded = waitForLoad(xmlFrame); + + quirksFrame.srcdoc = [ + '', + '
    ', + '', + ].join('\n'); + + xmlFrame.srcdoc = [ + '', + ' ', + ' ', + ' ', + ' ', + '
    ', + ' ', + ' ', + '', + ].join('\n'); + + await quirksLoaded; + await xmlLoaded; + + type Win = Window & typeof globalThis & { mode: string }; + const win = window as Win; + const quirks = quirksFrame.contentWindow as Win; + const xml = xmlFrame.contentWindow as Win; + + win.mode = 'top'; + quirks.mode = 'quirks'; + xml.mode = 'XML'; + + const results: [string, string, boolean][] = []; + const assert_equals = (actual: unknown, expected: unknown, testName: string, failMsg: string) => { + const pass = actual === expected; + results.push([testName, failMsg, pass]); + }; + const assert_throws = (expectedError: string, func: () => void, testName: string, failMsg: string) => { + let threw = false; + try { + func(); + } catch (e) { + threw = true; + assert_equals((e as Error).name, expectedError, testName, failMsg + ' threw wrong error'); + } + assert_equals(threw, true, testName, failMsg + ' did not throw'); + }; + + [win, quirks, xml].forEach(function(global) { + const style = global.document.getElementsByTagName('style')[0]!; + const elm = global.document.getElementById('test')!; + function clean_slate(testName: string) { + style.textContent = ''; + assert_equals(style.sheet?.cssRules.length, 0, testName, 'CSSOM was not empty for empty stylesheet'); + assert_equals(global.getComputedStyle(elm).visibility, 'visible', testName, 'computed style for empty stylesheet'); + } + valid.forEach(function(s) { + { + const testName = s + ' in ' + global.mode; + clean_slate(testName); + style.textContent = s + ' { visibility:hidden }'; + assert_equals(style.sheet?.cssRules.length, 1, testName, 'valid rule didn\'t parse into CSSOM'); + assert_equals(global.getComputedStyle(elm).visibility, 'hidden', testName, 'valid selector didn\'t match'); + }; + { + const testName = s + ' with querySelector in ' + global.mode; + assert_equals(global.document.querySelector(s), elm, testName, 'valid selector'); + }; + }); + invalid.forEach(function(s) { + { + const testName = s + ' in ' + global.mode; + clean_slate(testName); + style.textContent = s + ' { visibility:hidden }'; + assert_equals(style.sheet?.cssRules.length, 0, testName, 'invalid rule parsed into CSSOM'); + assert_equals(global.getComputedStyle(elm).visibility, 'visible', testName, 'invalid selector matched'); + }; + { + const testName = s + ' with querySelector in ' + global.mode; + assert_throws("SyntaxError", function() { + global.document.querySelector(s); + }, testName, 'invalid selector'); + }; + }); + }); + + return results; + }); + + // console.log(results); + for (const [testName, failMsg, pass] of results) { + expect(pass, `${testName}: ${failMsg}`).toBe(true); + } + }, + cases: [], + }, +]); + +runScenarios('w3c', 'normal', [ + { + name: 'syntax', + modifier: 'fixme', + html: ` + + Selectors: syntax of case-sensitivity attribute selector + + +
    +
    + + + `, + cases: [ + { selector: "[foo='BAR'] /* sanity check (valid) */" }, + { selector: "[foo='bar' i]" }, + { selector: "[foo='bar' I]" }, + { selector: "[foo=bar i]" }, + { selector: '[foo="bar" i]' }, + { selector: "[foo='bar'i]" }, + { selector: "[foo='bar'i ]" }, + { selector: "[foo='bar' i ]" }, + { selector: "[foo='bar' /**/ i]" }, + { selector: "[foo='bar' i /**/ ]" }, + { selector: "[foo='bar'/**/i/**/]" }, + { selector: "[foo=bar/**/i]" }, + { selector: "[foo='bar'\ti\t] /* \\t */" }, + { selector: "[foo='bar'\ni\n] /* \\n */" }, + { selector: "[foo='bar'\ri\r] /* \\r */" }, + { selector: "[foo='bar' \\i]" }, + { selector: "[foo='bar' \\69]" }, + { selector: "[foo~='bar' i]" }, + { selector: "[foo^='bar' i]" }, + { selector: "[foo$='bar' i]" }, + { selector: "[foo*='bar' i]" }, + { selector: "[foo|='bar' i]" }, + { selector: "[|foo='bar' i]" }, + { selector: "[*|foo='bar' i]" }, + + { selector: "[foo[ /* sanity check (invalid) */", expect: { throws: true } }, + { selector: "[foo='bar' i i]", expect: { throws: true } }, + { selector: "[foo i ='bar']", expect: { throws: true } }, + { selector: "[foo= i 'bar']", expect: { throws: true } }, + { selector: "[i foo='bar']", expect: { throws: true } }, + { selector: "[foo='bar' i\u0000] /* \\0 */", expect: { throws: true } }, + { selector: "[foo='bar' \u0130]", expect: { throws: true } }, + { selector: "[foo='bar' \u0131]", expect: { throws: true } }, + { selector: "[foo='bar' ii]", expect: { throws: true } }, + { selector: "[foo='bar' ij]", expect: { throws: true } }, + { selector: "[foo='bar' j]", expect: { throws: true } }, + { selector: "[foo='bar' \\\\i]", expect: { throws: true } }, + { selector: "[foo='bar' \\\\69]", expect: { throws: true } }, + { selector: "[foo='bar' i()]", expect: { throws: true } }, + { selector: "[foo='bar' i ()]", expect: { throws: true } }, + { selector: "[foo='bar' () i]", expect: { throws: true } }, + { selector: "[foo='bar' (i)]", expect: { throws: true } }, + { selector: "[foo='bar' i []]", expect: { throws: true } }, + { selector: "[foo='bar' [] i]", expect: { throws: true } }, + { selector: "[foo='bar' [i]]", expect: { throws: true } }, + { selector: "[foo='bar' i {}]", expect: { throws: true } }, + { selector: "[foo='bar' {} i]", expect: { throws: true } }, + { selector: "[foo='bar' {i}]", expect: { throws: true } }, + { selector: "[foo='bar' 1i]", expect: { throws: true } }, + { selector: "[foo='bar' 1]", expect: { throws: true } }, + { selector: "[foo='bar' 'i']", expect: { throws: true } }, + { selector: "[foo='bar' url(i)]", expect: { throws: true } }, + { selector: "[foo='bar' ,i]", expect: { throws: true } }, + { selector: "[foo='bar' i,]", expect: { throws: true } }, + { selector: "[foo='bar']i", expect: { throws: true } }, + { selector: "[foo='bar' |i]", expect: { throws: true } }, + { selector: "[foo='bar' \\|i]", expect: { throws: true } }, + { selector: "[foo='bar' *|i]", expect: { throws: true } }, + { selector: "[foo='bar' \\*|i]", expect: { throws: true } }, + { selector: "[foo='bar' *]", expect: { throws: true } }, + { selector: "[foo='bar' \\*]", expect: { throws: true } }, + { selector: "[foo i]", expect: { throws: true } }, + { selector: "[foo/**/i]", expect: { throws: true } }, + ], + }, + { + name: 'semantics', + modifier: 'fixme', + html: `
    `, + cases: [], + setupPage: async (page) => { + page.on('console', (msg) => { console.log(`[browser:${msg.type()}] ${msg.text()}`); }); + + const results = await page.evaluate(async () => { + type Attr = [ns: string, name: string, value: string]; + type TestCase = [selector: string, ...attrs: Attr[]]; + + const match: TestCase[] = [ + ["[foo='BAR'] /* sanity check (match) */", ["", "foo", "BAR"]], + ["[foo='bar' i]", ["", "foo", "BAR"]], + ["[foo='' i]", ["", "foo", ""]], + ["[foo='a\u0308' i] /* COMBINING in both */", ["", "foo", "A\u0308"]], + ["[foo='A\u0308' i] /* COMBINING in both */", ["", "foo", "a\u0308"]], + ["[*|foo='bar' i]", ["", "foo", "x"], ["a", "foo", "x"], ["b", "foo", "BAR"], ["c", "foo", "x"]], + ["[*|foo='bar' i]", ["", "foo", "BAR"], ["a", "foo", "x"], ["b", "foo", "x"], ["c", "foo", "x"]], + ["[align='left' i]", ["", "align", "LEFT"]], + ["[align='LEFT' i]", ["", "align", "left"]], + ["[class~='a' i]", ["", "class", "X A B"]], + ["[class~='A' i]", ["", "class", "x a b"]], + ["[id^='a' i]", ["", "id", "AB"]], + ["[id$='A' i]", ["", "id", "xa"]], + ["[lang|='a' i]", ["", "lang", "A-B"]], + ["[lang*='A' i]", ["", "lang", "xab"]], + ["[*|lang='a' i]", ["http://www.w3.org/XML/1998/namespace", "lang", "A"]], + ["[*|lang='A' i]", ["http://www.w3.org/XML/1998/namespace", "lang", "a"]], + // ["@namespace x 'http://www.w3.org/XML/1998/namespace'; [x|lang='A' i]", ["http://www.w3.org/XML/1998/namespace", "lang", "a"]], + ["[foo='bar' i][foo='bar' i]", ["", "foo", "BAR"]], + ["[foo='BAR'][foo='bar' i]", ["", "foo", "BAR"]], + ["[foo='bar' i][foo='BAR']", ["", "foo", "BAR"]], + ["[foo='bar' i]", ["", "FOO", "bar"]], + ]; + const nomatch: TestCase[] = [ + ["[missingattr] /* sanity check (no match) */", ["", "foo", "BAR"]], + ["[foo='' i]", ["", "foo", "BAR"]], + ["[foo='\u0000' i] /* \\0 in selector */", ["", "foo", ""]], + ["[foo='' i] /* \\0 in attribute */", ["", "foo", "\u0000"]], + ["[foo='\u00E4' i]", ["", "foo", "\u00C4"]], + ["[foo='\u00C4' i]", ["", "foo", "\u00E4"]], + ["[foo='a\u0308' i] /* COMBINING in selector */", ["", "foo", "\u00C4"]], + ["[foo~='a\u0308' i] /* COMBINING in selector */", ["", "foo", "\u00E4"]], + ["[foo^='A\u0308' i] /* COMBINING in selector */", ["", "foo", "\u00C4"]], + ["[foo$='A\u0308' i] /* COMBINING in selector */", ["", "foo", "\u00E4"]], + ["[foo*='\u00E4' i] /* COMBINING in attribute */", ["", "foo", "a\u0308"]], + ["[foo|='\u00E4' i] /* COMBINING in attribute */", ["", "foo", "A\u0308"]], + ["[foo='\u00C4' i] /* COMBINING in attribute */", ["", "foo", "a\u0308"]], + ["[foo='\u00C4' i] /* COMBINING in attribute */", ["", "foo", "A\u0308"]], + ["[foo='a\u0308' i] /* COMBINING in selector */", ["", "foo", "a"]], + ["[foo='a\u0308' i] /* COMBINING in selector */", ["", "foo", "A"]], + ["[foo='A\u0308' i] /* COMBINING in selector */", ["", "foo", "a"]], + ["[foo='A\u0308' i] /* COMBINING in selector */", ["", "foo", "A"]], + ["[foo='a' i] /* COMBINING in attribute */", ["", "foo", "a\u0308"]], + ["[foo='A' i] /* COMBINING in attribute */", ["", "foo", "a\u0308"]], + ["[foo='a' i] /* COMBINING in attribute */", ["", "foo", "A\u0308"]], + ["[foo='A' i] /* COMBINING in attribute */", ["", "foo", "A\u0308"]], + ["[foo='i' i]", ["", "foo", "\u0130"]], + ["[foo='i' i]", ["", "foo", "\u0131"]], + ["[foo='I' i]", ["", "foo", "\u0130"]], + ["[foo='I' i]", ["", "foo", "\u0131"]], + ["[foo='\u0130' i]", ["", "foo", "i"]], + ["[foo='\u0131' i]", ["", "foo", "i"]], + ["[foo='\u0130' i]", ["", "foo", "I"]], + ["[foo='\u0131' i]", ["", "foo", "I"]], + ["[foo='bar' i]", ["", "foo", "x"], ["a", "foo", "BAR"]], + ["[|foo='bar' i]", ["", "foo", "x"], ["a", "foo", "BAR"]], + // ["[foo='bar' i]", ["", "FOO", "bar"]], + ["[foo='\t' i] /* tab in selector */", ["", "foo", " "]], + ["[foo=' ' i] /* tab in attribute */", ["", "foo", "\t"]], + // ["@namespace x 'a'; [x|foo='' i]", ["A", "foo", ""]], + // ["@namespace x 'A'; [x|foo='' i]", ["a", "foo", ""]], + ["[foo='bar' i][foo='bar']", ["", "foo", "BAR"]], + ["[foo='bar' i]", ["", "baz", "BAR"]], + ]; + + type Result = 'match' | 'nomatch' | 'throws'; + const results: { selector: string, attrsLabel: string, expected: Result, nw: Result, native: Result }[] = []; + + const runTestCase = (testCase: TestCase, expected: Result) => { + const [selector, ...attrs] = testCase; + + const span = document.createElement('span'); + for (const [ns, name, value] of attrs) { + if (ns) { + span.setAttributeNS(ns, name, value); + } else { + span.setAttribute(name, value); + } + } + const div = document.createElement('div'); + div.appendChild(span); + + let native: Result = 'nomatch'; + try { + native = div.querySelector(selector) === span ? 'match' : 'nomatch'; + } catch (e) { + native = 'throws'; + } + + let nw: Result = 'nomatch'; + try { + nw = NW.Dom.select(selector, div)[0] === span ? 'match' : 'nomatch'; + } catch (e) { + nw = 'throws'; + } + + const attrsLabel = attrs.map(([ns, name, value]) => `${ns ? `${ns}:` : ''}${name}="${value}"`).join(' '); + results.push({ selector, attrsLabel, expected, native, nw }); + } + + match.forEach(testCase => runTestCase(testCase, 'match')); + nomatch.forEach(testCase => runTestCase(testCase, 'nomatch')); + + return results; + }); + + // console.log(results); + for (const r of results) { + const label = `${r.selector} on `; + expect(r.nw, `${label}: nw/native mismatch (${r.nw} vs ${r.native})`).toBe(r.native); + expect(r.native, `${label}: native expected ${r.expected}, got ${r.native}`).toBe(r.expected); + // expect(r.nw, `${label}: nw expected ${r.expected}, got ${r.nw}`).toBe(r.expected); + } + }, + }, + + { + name: 'child-indexed-pseudo-class', + html: `
    `, + cases: [ + { selector: 'div:first-child' }, + { selector: 'div:last-child' }, + { selector: 'div:only-child' }, + { selector: 'div:first-of-type' }, + { selector: 'div:last-of-type' }, + { selector: 'div:only-of-type' }, + { selector: 'div:nth-child(1)' }, + { selector: 'div:nth-child(n)' }, + { selector: 'div:nth-last-child(1)' }, + { selector: 'div:nth-last-child(n)' }, + { selector: 'div:nth-of-type(1)' }, + { selector: 'div:nth-of-type(n)' }, + { selector: 'div:nth-last-of-type(1)' }, + { selector: 'div:nth-last-of-type(n)' }, + + { selector: 'div:nth-child(2)', expect: { count: 0 } }, + { selector: 'div:nth-last-child(2)', expect: { count: 0 } }, + { selector: 'div:nth-of-type(2)', expect: { count: 0 } }, + { selector: 'div:nth-last-of-type(2)', expect: { count: 0 } }, + ], + }, + + { + name: 'missing-right-token', + htmlMode: 'document', + html: ` + + + + + + +
    + + +
    + + + `, + cases: [ + { selector: 'meta[charset="utf-8"', expect: { ids: ['expected'] } }, + { selector: 'meta[charset="utf-8', expect: { ids: ['expected'] } }, + { selector: 'span:not([class])', root: { kind: 'id', value: 'container' }, expect: { count: 1 } }, + { selector: 'span:not([class)', root: { kind: 'id', value: 'container' }, expect: { throws: true } }, + ], + }, +]); + + + + + + + + + From 33c92ce34ca7061073424984bf6de5f07dd35c37 Mon Sep 17 00:00:00 2001 From: koal44 Date: Thu, 9 Apr 2026 16:14:03 -0700 Subject: [PATCH 07/19] prepare browser harness for additional case types --- test_new/browser/css3-compat.test.ts | 276 +++++++-------- test_new/browser/css3-escape.test.ts | 280 +++++++-------- test_new/browser/haness.test.ts | 10 +- test_new/browser/harness.ts | 367 ------------------- test_new/browser/harness/browser.ts | 144 ++++++++ test_new/browser/harness/scenarios.ts | 239 +++++++++++++ test_new/browser/harness/select.ts | 36 ++ test_new/browser/html5.test.ts | 24 +- test_new/browser/issues.test.ts | 8 +- test_new/browser/jquery.test.ts | 488 +++++++++++++------------- test_new/browser/jsvm.test.ts | 6 +- test_new/browser/prototype.test.ts | 388 ++++++++++---------- test_new/browser/quirks.test.ts | 18 +- test_new/browser/scope.test.ts | 44 +-- test_new/browser/scotch.test.ts | 362 +++++++++---------- test_new/browser/speed.test.ts | 406 ++++++++++----------- test_new/browser/w3c.test.ts | 178 +++++----- test_new/global.d.ts | 5 + test_new/utils/type.ts | 3 + 19 files changed, 1671 insertions(+), 1611 deletions(-) delete mode 100644 test_new/browser/harness.ts create mode 100644 test_new/browser/harness/browser.ts create mode 100644 test_new/browser/harness/scenarios.ts create mode 100644 test_new/browser/harness/select.ts create mode 100644 test_new/utils/type.ts diff --git a/test_new/browser/css3-compat.test.ts b/test_new/browser/css3-compat.test.ts index 985517d..9af5c6c 100644 --- a/test_new/browser/css3-compat.test.ts +++ b/test_new/browser/css3-compat.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('css3 compat', 'normal', [ { @@ -30,21 +30,21 @@ runScenarios('css3 compat', 'normal', [ setupPage: async (page) => { await page.evaluate(() => { location.hash = 'target'; });}, cases: [ /* element type selector */ - { selector: 'body', expect: { count: 1 } }, - { selector: 'div', expect: { count: 6 } }, - { selector: 'div.header', expect: { count: 1 } }, - { selector: 'div.footer', expect: { count: 1 } }, - { selector: 'h3, h4, p, ul', expect: { count: 5 } }, + { select: 'body', expect: { count: 1 } }, + { select: 'div', expect: { count: 6 } }, + { select: 'div.header', expect: { count: 1 } }, + { select: 'div.footer', expect: { count: 1 } }, + { select: 'h3, h4, p, ul', expect: { count: 5 } }, /* class selector */ - { selector: '.unitTest', expect: { count: 2 } }, - { selector: '.test', expect: { count: 2 } }, + { select: '.unitTest', expect: { count: 2 } }, + { select: '.test', expect: { count: 2 } }, /* group of selectors */ - { selector: '.unitTest, .test', expect: { count: 4 } }, + { select: '.unitTest, .test', expect: { count: 4 } }, /* :target selector */ - { selector: '.target :target', expect: { count: 1 } }, + { select: '.target :target', expect: { count: 1 } }, ], }, { @@ -62,8 +62,8 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* test 1 : childhood selector */ - { selector: 'html > body', expect: { count: 1 } }, - { selector: '.test > .blox1', expect: { count: 1 } }, + { select: 'html > body', expect: { count: 1 } }, + { select: '.test > .blox1', expect: { count: 1 } }, ], }, { @@ -78,14 +78,14 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* attribute with a value */ - { selector: '.blox2[align]', expect: { count: 1 } }, + { select: '.blox2[align]', expect: { count: 1 } }, /* attribute with empty value */ - { selector: '.blox3[align]', expect: { count: 1 } }, + { select: '.blox3[align]', expect: { count: 1 } }, /* attribute with almost similar name */ - { selector: '.blox4, .blox5', expect: { count: 2 } }, - { selector: '.blox4[align], .blox5[align]', expect: { count: 0 } }, + { select: '.blox4, .blox5', expect: { count: 2 } }, + { select: '.blox4[align], .blox5[align]', expect: { count: 0 } }, ], }, { @@ -99,11 +99,11 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* test 3 : attribute value selector */ - { selector: '.blox6[align="center"]', expect: { count: 1 } }, - { selector: '.blox6[align="c"]', expect: { count: 0 } }, - { selector: '.blox6[align="centera"]', expect: { count: 0 } }, - { selector: '.blox6[foo="\\e9"]', expect: { count: 1 } }, - { selector: '.blox6[\\_foo="\\e9"]', expect: { count: 1 } }, + { select: '.blox6[align="center"]', expect: { count: 1 } }, + { select: '.blox6[align="c"]', expect: { count: 0 } }, + { select: '.blox6[align="centera"]', expect: { count: 0 } }, + { select: '.blox6[foo="\\e9"]', expect: { count: 1 } }, + { select: '.blox6[\\_foo="\\e9"]', expect: { count: 1 } }, ], }, { @@ -118,11 +118,11 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* test 4 : [~=] */ - { selector: '.blox7[class~="foo"]', expect: { count: 1 } }, - { selector: '.blox8, .blox9, .blox10', expect: { count: 3 } }, - { selector: '.blox8[class~=""]', expect: { count: 0 } }, - { selector: '.blox9[foo~=""]', expect: { count: 0 } }, - { selector: '.blox10[foo~="foo"]', expect: { count: 0 } }, + { select: '.blox7[class~="foo"]', expect: { count: 1 } }, + { select: '.blox8, .blox9, .blox10', expect: { count: 3 } }, + { select: '.blox8[class~=""]', expect: { count: 0 } }, + { select: '.blox9[foo~=""]', expect: { count: 0 } }, + { select: '.blox10[foo~="foo"]', expect: { count: 0 } }, ], }, { @@ -137,12 +137,12 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* test5 [^=] */ - { selector: '.attrStart > .t3', expect: { count: 1 } }, - { selector: '.attrStart > .t1[class^="unit"]', expect: { count: 1 } }, - { selector: '.attrStart > .t2', expect: { count: 1 } }, - { selector: '.attrStart > .t2[class^="nit"]', expect: { count: 0 } }, - { selector: '.attrStart > .t3[align^=""]', expect: { count: 0 } }, - { selector: '.attrStart > .t4[foo^="\\e9"]', expect: { count: 1 } }, + { select: '.attrStart > .t3', expect: { count: 1 } }, + { select: '.attrStart > .t1[class^="unit"]', expect: { count: 1 } }, + { select: '.attrStart > .t2', expect: { count: 1 } }, + { select: '.attrStart > .t2[class^="nit"]', expect: { count: 0 } }, + { select: '.attrStart > .t3[align^=""]', expect: { count: 0 } }, + { select: '.attrStart > .t4[foo^="\\e9"]', expect: { count: 1 } }, ], }, { @@ -157,12 +157,12 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* test6 [$=] */ - { selector: '.attrEnd > .t3', expect: { count: 1 } }, - { selector: '.attrEnd > .t1[class$="t1"]', expect: { count: 1 } }, - { selector: '.attrEnd > .t2', expect: { count: 1 } }, - { selector: '.attrEnd > .t2[class$="unit"]', expect: { count: 0 } }, - { selector: '.attrEnd > .t3[align$=""]', expect: { count: 0 } }, - { selector: '.attrEnd > .t4[foo$="\\e9"]', expect: { count: 1 } }, + { select: '.attrEnd > .t3', expect: { count: 1 } }, + { select: '.attrEnd > .t1[class$="t1"]', expect: { count: 1 } }, + { select: '.attrEnd > .t2', expect: { count: 1 } }, + { select: '.attrEnd > .t2[class$="unit"]', expect: { count: 0 } }, + { select: '.attrEnd > .t3[align$=""]', expect: { count: 0 } }, + { select: '.attrEnd > .t4[foo$="\\e9"]', expect: { count: 1 } }, ], }, { @@ -177,12 +177,12 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* test7 [*=] */ - { selector: '.attrMiddle > .t3', expect: { count: 1 } }, - { selector: '.attrMiddle > .t1[class*="t t"]', expect: { count: 1 } }, - { selector: '.attrMiddle > .t2', expect: { count: 1 } }, - { selector: '.attrMiddle > .t2[class*="a"]', expect: { count: 0 } }, - { selector: '.attrMiddle > .t3[align*=""]', expect: { count: 0 } }, - { selector: '.attrMiddle > .t4[foo*="\\e9"]', expect: { count: 1 } }, + { select: '.attrMiddle > .t3', expect: { count: 1 } }, + { select: '.attrMiddle > .t1[class*="t t"]', expect: { count: 1 } }, + { select: '.attrMiddle > .t2', expect: { count: 1 } }, + { select: '.attrMiddle > .t2[class*="a"]', expect: { count: 0 } }, + { select: '.attrMiddle > .t3[align*=""]', expect: { count: 0 } }, + { select: '.attrMiddle > .t4[foo*="\\e9"]', expect: { count: 1 } }, ], }, { @@ -196,10 +196,10 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :first-child tests */ - { selector: '.firstChild .unitTest:first-child', expect: { count: 1 } }, - { selector: '.blox12:first-child', expect: { count: 0 } }, - { selector: '.blox13:first-child', expect: { count: 0 } }, - { selector: '.blox12, .blox13', expect: { count: 2 } }, + { select: '.firstChild .unitTest:first-child', expect: { count: 1 } }, + { select: '.blox12:first-child', expect: { count: 0 } }, + { select: '.blox13:first-child', expect: { count: 0 } }, + { select: '.blox12, .blox13', expect: { count: 2 } }, ], }, { @@ -263,28 +263,28 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :nth-child(n) tests */ - { selector: '.nthchild1 > :nth-last-child(odd)', expect: { count: 3 } }, - { selector: '.nthchild1 > :nth-child(odd)', expect: { count: 3 } }, + { select: '.nthchild1 > :nth-last-child(odd)', expect: { count: 3 } }, + { select: '.nthchild1 > :nth-child(odd)', expect: { count: 3 } }, - { selector: '.nthchild2 > :nth-last-child(even)', expect: { count: 3 } }, - { selector: '.nthchild2 > :nth-child(even)', expect: { count: 3 } }, + { select: '.nthchild2 > :nth-last-child(even)', expect: { count: 3 } }, + { select: '.nthchild2 > :nth-child(even)', expect: { count: 3 } }, - { selector: '.nthchild3 > :nth-child(3n+2)', expect: { count: 2 } }, - { selector: '.nthchild3 > :nth-last-child(3n+1)', expect: { count: 2 } }, - { selector: '.nthchild3 > :nth-last-child(3n+3)', expect: { count: 2 } }, + { select: '.nthchild3 > :nth-child(3n+2)', expect: { count: 2 } }, + { select: '.nthchild3 > :nth-last-child(3n+1)', expect: { count: 2 } }, + { select: '.nthchild3 > :nth-last-child(3n+3)', expect: { count: 2 } }, - { selector: '.nthoftype1 > div:nth-of-type(odd)', expect: { count: 2 } }, - { selector: '.nthoftype1 > div:nth-last-of-type(odd)', expect: { count: 2 } }, - { selector: '.nthoftype1 > p', expect: { count: 3 } }, + { select: '.nthoftype1 > div:nth-of-type(odd)', expect: { count: 2 } }, + { select: '.nthoftype1 > div:nth-last-of-type(odd)', expect: { count: 2 } }, + { select: '.nthoftype1 > p', expect: { count: 3 } }, - { selector: '.nthoftype2 > div:nth-of-type(even)', expect: { count: 2 } }, - { selector: '.nthoftype2 > div:nth-last-of-type(even)', expect: { count: 2 } }, - { selector: '.nthoftype2 > p', expect: { count: 3 } }, + { select: '.nthoftype2 > div:nth-of-type(even)', expect: { count: 2 } }, + { select: '.nthoftype2 > div:nth-last-of-type(even)', expect: { count: 2 } }, + { select: '.nthoftype2 > p', expect: { count: 3 } }, - { selector: '.nthoftype3 > div:nth-of-type(3n+1)', expect: { count: 2 } }, - { selector: '.nthoftype3 > div:nth-last-of-type(3n+1)', expect: { count: 2 } }, - { selector: '.nthoftype3 > div:nth-last-of-type(3n+2)', expect: { count: 2 } }, - { selector: '.nthoftype3 > p', expect: { count: 4 } }, + { select: '.nthoftype3 > div:nth-of-type(3n+1)', expect: { count: 2 } }, + { select: '.nthoftype3 > div:nth-last-of-type(3n+1)', expect: { count: 2 } }, + { select: '.nthoftype3 > div:nth-last-of-type(3n+2)', expect: { count: 2 } }, + { select: '.nthoftype3 > p', expect: { count: 4 } }, ], }, { @@ -298,10 +298,10 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :not() tests */ - { selector: '.blox14:not(span)', expect: { count: 1 } }, - { selector: '.blox15:not([foo="blox14"])', expect: { count: 1 } }, - { selector: '.blox16', expect: { count: 1 } }, - { selector: '.blox16:not(.blox15)', expect: { count: 1 } }, + { select: '.blox14:not(span)', expect: { count: 1 } }, + { select: '.blox15:not([foo="blox14"])', expect: { count: 1 } }, + { select: '.blox16', expect: { count: 1 } }, + { select: '.blox16:not(.blox15)', expect: { count: 1 } }, ], }, { @@ -315,10 +315,10 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :only-of-type tests */ - { selector: '.blox17', expect: { count: 1 } }, - { selector: '.blox17:only-of-type', expect: { count: 1 } }, - { selector: '.blox18:only-of-type', expect: { count: 0 } }, - { selector: '.blox18:not(:only-of-type)', expect: { count: 2 } }, + { select: '.blox17', expect: { count: 1 } }, + { select: '.blox17:only-of-type', expect: { count: 1 } }, + { select: '.blox18:only-of-type', expect: { count: 0 } }, + { select: '.blox18:not(:only-of-type)', expect: { count: 2 } }, ], }, { @@ -331,9 +331,9 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :last-child tests */ - { selector: '.lastChild > p', expect: { count: 1 } }, - { selector: '.lastChild > :last-child', expect: { count: 1 } }, - { selector: '.lastChild > :not(:last-child)', expect: { count: 1 } }, + { select: '.lastChild > p', expect: { count: 1 } }, + { select: '.lastChild > :last-child', expect: { count: 1 } }, + { select: '.lastChild > :not(:last-child)', expect: { count: 1 } }, ], }, { @@ -348,9 +348,9 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :first-of-type tests */ - { selector: '.firstOfType > p', expect: { count: 2 } }, - { selector: '.firstOfType > *:first-of-type', expect: { count: 2 } }, - { selector: '*.firstOfType > :not(:first-of-type)', expect: { count: 2 } }, + { select: '.firstOfType > p', expect: { count: 2 } }, + { select: '.firstOfType > *:first-of-type', expect: { count: 2 } }, + { select: '*.firstOfType > :not(:first-of-type)', expect: { count: 2 } }, ], }, @@ -367,9 +367,9 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :last-of-type tests */ - { selector: '.lastOfType > p', expect: { count: 2 } }, - { selector: '.lastOfType > *:last-of-type', expect: { count: 2 } }, - { selector: '*.lastOfType > :not(:last-of-type)', expect: { count: 2 } }, + { select: '.lastOfType > p', expect: { count: 2 } }, + { select: '.lastOfType > *:last-of-type', expect: { count: 2 } }, + { select: '*.lastOfType > :not(:last-of-type)', expect: { count: 2 } }, ], }, { @@ -384,8 +384,8 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :only-child tests */ - { selector: '.onlyChild > *:not(:only-child)', expect: { count: 2 } }, - { selector: '.onlyChild > .unitTest > *:only-child', expect: { count: 1 } }, + { select: '.onlyChild > *:not(:only-child)', expect: { count: 2 } }, + { select: '.onlyChild > .unitTest > *:only-child', expect: { count: 1 } }, ], }, { @@ -401,8 +401,8 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :only-of-type tests */ - { selector: '.onlyOfType *:only-of-type', expect: { count: 2 } }, - { selector: '.onlyOfType *:not(:only-of-type)', expect: { count: 2 } }, + { select: '.onlyOfType *:only-of-type', expect: { count: 2 } }, + { select: '.onlyOfType *:not(:only-of-type)', expect: { count: 2 } }, ], }, @@ -419,11 +419,11 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* :empty tests */ - { selector: '.empty > .isEmpty', expect: { count: 2 } }, - { selector: '.empty > *.isEmpty:empty', expect: { count: 2 } }, - { selector: '.empty > .isNotEmpty', expect: { count: 3 } }, - { selector: '.empty > .isNotEmpty:empty', expect: { count: 0 } }, - { selector: '.empty > .isNotEmpty:not(:empty)', expect: { count: 3 } }, + { select: '.empty > .isEmpty', expect: { count: 2 } }, + { select: '.empty > *.isEmpty:empty', expect: { count: 2 } }, + { select: '.empty > .isNotEmpty', expect: { count: 3 } }, + { select: '.empty > .isNotEmpty:empty', expect: { count: 0 } }, + { select: '.empty > .isNotEmpty:not(:empty)', expect: { count: 3 } }, ], }, { @@ -443,11 +443,11 @@ runScenarios('css3 compat', 'normal', [ }, cases: [ /* :lang() tests */ - { selector: '.lang :lang(en)', expect: { count: 2 } }, - { selector: '.lang :lang(fr)', expect: { count: 1 } }, - { selector: '.lang .t1', expect: { count: 1 } }, - { selector: '.lang .t1:lang(es)', expect: { count: 1 } }, - { selector: '.lang :lang(es-AR)', expect: { count: 0 } }, + { select: '.lang :lang(en)', expect: { count: 2 } }, + { select: '.lang :lang(fr)', expect: { count: 1 } }, + { select: '.lang .t1', expect: { count: 1 } }, + { select: '.lang .t1:lang(es)', expect: { count: 1 } }, + { select: '.lang :lang(es-AR)', expect: { count: 0 } }, ], }, { @@ -467,13 +467,13 @@ runScenarios('css3 compat', 'normal', [ }, cases: [ /* [|=] tests */ - { selector: '.attrLang .t1', expect: { count: 1 } }, - { selector: '.attrLang .t1[lang|="en"]', expect: { count: 0 } }, - { selector: '.attrLang [lang|="fr"]', expect: { count: 1 } }, - { selector: '.attrLang .t2[lang|="en"]', expect: { count: 1 } }, - { selector: '.attrLang .t3', expect: { count: 1 } }, - { selector: '.attrLang .t3[lang|="es"]', expect: { count: 1 } }, - { selector: '.attrLang [lang|="es-AR"]', expect: { count: 0 } }, + { select: '.attrLang .t1', expect: { count: 1 } }, + { select: '.attrLang .t1[lang|="en"]', expect: { count: 0 } }, + { select: '.attrLang [lang|="fr"]', expect: { count: 1 } }, + { select: '.attrLang .t2[lang|="en"]', expect: { count: 1 } }, + { select: '.attrLang .t3', expect: { count: 1 } }, + { select: '.attrLang .t3[lang|="es"]', expect: { count: 1 } }, + { select: '.attrLang [lang|="es-AR"]', expect: { count: 0 } }, ], }, @@ -499,10 +499,10 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* UI tests */ - { selector: '.UI .t1:enabled > .unitTest', expect: { count: 1 } }, - { selector: '.UI .t2:disabled > .unitTest', expect: { count: 1 } }, - { selector: '.UI .t3:checked + div', expect: { count: 1 } }, - { selector: '.UI .t4:not(:checked) + div', expect: { count: 1 } }, + { select: '.UI .t1:enabled > .unitTest', expect: { count: 1 } }, + { select: '.UI .t2:disabled > .unitTest', expect: { count: 1 } }, + { select: '.UI .t3:checked + div', expect: { count: 1 } }, + { select: '.UI .t4:not(:checked) + div', expect: { count: 1 } }, ], }, { @@ -518,8 +518,8 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* ~ combinator tests */ - { selector: '.tilda .t1', expect: { count: 1 } }, - { selector: '.tilda .t1 ~ .unitTest', expect: { count: 3 } }, + { select: '.tilda .t1', expect: { count: 1 } }, + { select: '.tilda .t1 ~ .unitTest', expect: { count: 3 } }, ], }, { @@ -534,8 +534,8 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* + combinator tests */ - { selector: '.plus .t1, .plus .t2', expect: { count: 2 } }, - { selector: '.plus .t1 + .unitTest + .unitTest', expect: { count: 1 } }, + { select: '.plus .t1, .plus .t2', expect: { count: 2 } }, + { select: '.plus .t1 + .unitTest + .unitTest', expect: { count: 1 } }, ], }, { @@ -560,31 +560,31 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ - { selector: '.blox23s1[foo="blox" i]', expect: { count: 1 } }, - { selector: '.blox23s2[foo="blox" i]', expect: { count: 1 } }, - { selector: '.blox23s3[foo="blox" i]', expect: { count: 1 } }, - { selector: '.blox23s4[foo="blox" i]', expect: { count: 1 } }, - { selector: '.blox23s5[foo="blox" i]', expect: { count: 1 } }, - { selector: '.blox23s6[foo="blox" i]', expect: { count: 1 } }, - { selector: '.blox23s1[foo="blox" erroneous]', expect: { throws: true } }, - { selector: '.blox19[class="BLOX19 UNITTEST" i]', expect: { count: 1 } }, - { selector: '.blox20[class="BLOX20 UNITTEST" i]', expect: { count: 1 } }, - { selector: '.blox20[class="blox20 unitTest" s]', expect: { throws: true } }, - { selector: '.blox21[class*="21 UN" i]', expect: { count: 1 } }, - { selector: '.blox22[class*="22 unitt" s]', expect: { throws: true } }, - { selector: '.blox22[class*="22 unitT" s]', expect: { throws: true } }, - { selector: '.blox24[class^="BLOX" i]', expect: { count: 1 } }, - { selector: '.blox25[class^="BLOX"]', expect: { count: 0 } }, - { selector: '.blox25[class^="blox" s]', expect: { throws: true } }, - { selector: '.blox26[class$="tEST" i]', expect: { count: 1 } }, - { selector: '.blox27[class$="TEst" s]', expect: { throws: true } }, - { selector: '.blox27[class$="Test" s]', expect: { throws: true } }, - { selector: '.blox28[class~="unitTEST" i]', expect: { count: 1 } }, + { select: '.blox23s1[foo="blox" i]', expect: { count: 1 } }, + { select: '.blox23s2[foo="blox" i]', expect: { count: 1 } }, + { select: '.blox23s3[foo="blox" i]', expect: { count: 1 } }, + { select: '.blox23s4[foo="blox" i]', expect: { count: 1 } }, + { select: '.blox23s5[foo="blox" i]', expect: { count: 1 } }, + { select: '.blox23s6[foo="blox" i]', expect: { count: 1 } }, + { select: '.blox23s1[foo="blox" erroneous]', expect: { throws: true } }, + { select: '.blox19[class="BLOX19 UNITTEST" i]', expect: { count: 1 } }, + { select: '.blox20[class="BLOX20 UNITTEST" i]', expect: { count: 1 } }, + { select: '.blox20[class="blox20 unitTest" s]', expect: { throws: true } }, + { select: '.blox21[class*="21 UN" i]', expect: { count: 1 } }, + { select: '.blox22[class*="22 unitt" s]', expect: { throws: true } }, + { select: '.blox22[class*="22 unitT" s]', expect: { throws: true } }, + { select: '.blox24[class^="BLOX" i]', expect: { count: 1 } }, + { select: '.blox25[class^="BLOX"]', expect: { count: 0 } }, + { select: '.blox25[class^="blox" s]', expect: { throws: true } }, + { select: '.blox26[class$="tEST" i]', expect: { count: 1 } }, + { select: '.blox27[class$="TEst" s]', expect: { throws: true } }, + { select: '.blox27[class$="Test" s]', expect: { throws: true } }, + { select: '.blox28[class~="unitTEST" i]', expect: { count: 1 } }, ], }, { name: 'attribute s-flag divergence', - modifier: 'fail', + status: 'fail', html: `
    @@ -594,12 +594,12 @@ runScenarios('css3 compat', 'normal', [
    `, cases: [ - { selector: '.blox20[class="blox20 unitTest" s]' }, - { selector: '.blox22[class*="22 unitt" s]' }, - { selector: '.blox22[class*="22 unitT" s]' }, - { selector: '.blox25[class^="blox" s]' }, - { selector: '.blox27[class$="TEst" s]' }, - { selector: '.blox27[class$="Test" s]' }, + { select: '.blox20[class="blox20 unitTest" s]' }, + { select: '.blox22[class*="22 unitt" s]' }, + { select: '.blox22[class*="22 unitT" s]' }, + { select: '.blox25[class^="blox" s]' }, + { select: '.blox27[class$="TEst" s]' }, + { select: '.blox27[class$="Test" s]' }, ], }, ]); diff --git a/test_new/browser/css3-escape.test.ts b/test_new/browser/css3-escape.test.ts index c2e291b..4d8fb75 100644 --- a/test_new/browser/css3-escape.test.ts +++ b/test_new/browser/css3-escape.test.ts @@ -1,5 +1,5 @@ import { type Page } from "@playwright/test"; -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; const setupNw = async (page: Page) => { await page.evaluate(() => { @@ -20,8 +20,8 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // 4.3.7 from https://www.w3.org/TR/css-syntax-3/#consume-an-escaped-code-point - { selector: '#nonescaped', expect: { count: 1 } }, - { selector: '.nonescaped', expect: { count: 1 } }, + { select: '#nonescaped', expect: { count: 1 } }, + { select: '.nonescaped', expect: { count: 1 } }, ], }, { @@ -36,17 +36,17 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // - escape hex digit - { selector: '#\\30 nextIsWhiteSpace', expect: { count: 1 } }, - { selector: '.\\30 nextIsWhiteSpace', expect: { count: 1 } }, + { select: '#\\30 nextIsWhiteSpace', expect: { count: 1 } }, + { select: '.\\30 nextIsWhiteSpace', expect: { count: 1 } }, - { selector: '#\\30nextIsNotHexLetters', expect: { count: 1 } }, - { selector: '.\\30nextIsNotHexLetters', expect: { count: 1 } }, + { select: '#\\30nextIsNotHexLetters', expect: { count: 1 } }, + { select: '.\\30nextIsNotHexLetters', expect: { count: 1 } }, - { selector: '#\\000030connectHexMoreThan6Hex', expect: { count: 1 } }, - { selector: '.\\000030connectHexMoreThan6Hex', expect: { count: 1 } }, + { select: '#\\000030connectHexMoreThan6Hex', expect: { count: 1 } }, + { select: '.\\000030connectHexMoreThan6Hex', expect: { count: 1 } }, - { selector: '#\\000030 spaceMoreThan6Hex', expect: { count: 1 } }, - { selector: '.\\000030 spaceMoreThan6Hex', expect: { count: 1 } }, + { select: '#\\000030 spaceMoreThan6Hex', expect: { count: 1 } }, + { select: '.\\000030 spaceMoreThan6Hex', expect: { count: 1 } }, ], }, // { @@ -86,17 +86,17 @@ runScenarios('css3 escaped identifiers', 'normal', [ }, cases: [ // 1. zero points - { selector: '#one\\0', expect: { count: 1 } }, - { selector: '.one\\0', expect: { count: 1 } }, + { select: '#one\\0', expect: { count: 1 } }, + { select: '.one\\0', expect: { count: 1 } }, - { selector: '#two\\0', expect: { count: 0 } }, - { selector: '.two\\0', expect: { count: 0 } }, + { select: '#two\\0', expect: { count: 0 } }, + { select: '.two\\0', expect: { count: 0 } }, - { selector: '#three\\000000', expect: { count: 1 } }, - { selector: '.three\\000000', expect: { count: 1 } }, + { select: '#three\\000000', expect: { count: 1 } }, + { select: '.three\\000000', expect: { count: 1 } }, - { selector: '#four\\000000', expect: { count: 0 } }, - { selector: '.four\\000000', expect: { count: 0 } }, + { select: '#four\\000000', expect: { count: 0 } }, + { select: '.four\\000000', expect: { count: 0 } }, ], }, { @@ -115,20 +115,20 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // 2. surrogate points - { selector: '#\\d83d surrogateFirstA', expect: { count: 1, ids: ['\u{fffd}surrogateFirstA'] } }, - { selector: '.\\d83d surrogateFirstA', expect: { count: 1, ids: ['\u{fffd}surrogateFirstA'] } }, - { selector: '#\\d83d surrogateFirstB', expect: { count: 0 } }, - { selector: '.\\d83d surrogateFirstB', expect: { count: 0 } }, - - { selector: '#surrogateSecondC\\dd11', expect: { count: 1, ids: ['surrogateSecondC\u{fffd}'] } }, - { selector: '.surrogateSecondC\\dd11', expect: { count: 1, ids: ['surrogateSecondC\u{fffd}'] } }, - { selector: '#surrogateSecondD\\dd11', expect: { count: 0 } }, - { selector: '.surrogateSecondD\\dd11', expect: { count: 0 } }, - - { selector: '#surrogatePairE\\d83d\\dd11', expect: { count: 1, ids: ['surrogatePairE\u{fffd}\u{fffd}'] } }, - { selector: '.surrogatePairE\\d83d\\dd11', expect: { count: 1, ids: ['surrogatePairE\u{fffd}\u{fffd}'] } }, - { selector: '#surrogatePairF\\d83d\\dd11', expect: { count: 0 } }, - { selector: '.surrogatePairF\\d83d\\dd11', expect: { count: 0 } }, + { select: '#\\d83d surrogateFirstA', expect: { count: 1, ids: ['\u{fffd}surrogateFirstA'] } }, + { select: '.\\d83d surrogateFirstA', expect: { count: 1, ids: ['\u{fffd}surrogateFirstA'] } }, + { select: '#\\d83d surrogateFirstB', expect: { count: 0 } }, + { select: '.\\d83d surrogateFirstB', expect: { count: 0 } }, + + { select: '#surrogateSecondC\\dd11', expect: { count: 1, ids: ['surrogateSecondC\u{fffd}'] } }, + { select: '.surrogateSecondC\\dd11', expect: { count: 1, ids: ['surrogateSecondC\u{fffd}'] } }, + { select: '#surrogateSecondD\\dd11', expect: { count: 0 } }, + { select: '.surrogateSecondD\\dd11', expect: { count: 0 } }, + + { select: '#surrogatePairE\\d83d\\dd11', expect: { count: 1, ids: ['surrogatePairE\u{fffd}\u{fffd}'] } }, + { select: '.surrogatePairE\\d83d\\dd11', expect: { count: 1, ids: ['surrogatePairE\u{fffd}\u{fffd}'] } }, + { select: '#surrogatePairF\\d83d\\dd11', expect: { count: 0 } }, + { select: '.surrogatePairF\\d83d\\dd11', expect: { count: 0 } }, ], }, { @@ -141,20 +141,20 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // 3. out of range points - { selector: '#outOfRangeA\\110000', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '.outOfRangeA\\110000', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '#outOfRangeA\\110000', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '.outOfRangeA\\110000', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '#outOfRangeA\\110030', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '.outOfRangeA\\110030', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '#outOfRangeA\\110030', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '.outOfRangeA\\110030', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '#outOfRangeB\\110030', expect: { count: 0 } }, - { selector: '.outOfRangeB\\110030', expect: { count: 0 } }, + { select: '#outOfRangeB\\110030', expect: { count: 0 } }, + { select: '.outOfRangeB\\110030', expect: { count: 0 } }, - { selector: '#outOfRangeA\\555555', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '.outOfRangeA\\555555', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '#outOfRangeA\\555555', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '.outOfRangeA\\555555', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '#outOfRangeA\\ffffff', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, - { selector: '.outOfRangeA\\ffffff', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '#outOfRangeA\\ffffff', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, + { select: '.outOfRangeA\\ffffff', expect: { count: 1, ids: ['outOfRangeA\u{fffd}'] } }, ], }, { @@ -167,11 +167,11 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // trailing backslash escapes EOF -> U+FFFD - { selector: '#eofA\\', expect: { count: 1, ids: ['eofA\u{fffd}'] } }, - { selector: '.eofA\\', expect: { count: 1, ids: ['eofA\u{fffd}'] } }, + { select: '#eofA\\', expect: { count: 1, ids: ['eofA\u{fffd}'] } }, + { select: '.eofA\\', expect: { count: 1, ids: ['eofA\u{fffd}'] } }, - { selector: '#eofB\\', expect: { count: 0 } }, - { selector: '.eofB\\', expect: { count: 0 } }, + { select: '#eofB\\', expect: { count: 0 } }, + { select: '.eofB\\', expect: { count: 0 } }, ], }, { @@ -184,14 +184,14 @@ runScenarios('css3 escaped identifiers', 'normal', [ `, setupPage: setupNw, cases: [ - { selector: '#\\.comma', expect: { count: 1, ids: ['.comma'] } }, - { selector: '.\\.comma', expect: { count: 1, ids: ['.comma'] } }, + { select: '#\\.comma', expect: { count: 1, ids: ['.comma'] } }, + { select: '.\\.comma', expect: { count: 1, ids: ['.comma'] } }, - { selector: '#\\-minus', expect: { count: 1, ids: ['-minus'] } }, - { selector: '.\\-minus', expect: { count: 1, ids: ['-minus'] } }, + { select: '#\\-minus', expect: { count: 1, ids: ['-minus'] } }, + { select: '.\\-minus', expect: { count: 1, ids: ['-minus'] } }, - { selector: '#\\g', expect: { count: 1, ids: ['g'] } }, - { selector: '.\\g', expect: { count: 1, ids: ['g'] } }, + { select: '#\\g', expect: { count: 1, ids: ['g'] } }, + { select: '.\\g', expect: { count: 1, ids: ['g'] } }, ], }, { @@ -211,29 +211,29 @@ runScenarios('css3 escaped identifiers', 'normal', [ `, setupPage: setupNw, cases: [ - { selector: '#\\61 BMPRegular', expect: { count: 1, ids: ['aBMPRegular'] } }, - { selector: '.\\61 BMPRegular', expect: { count: 1, ids: ['aBMPRegular'] } }, + { select: '#\\61 BMPRegular', expect: { count: 1, ids: ['aBMPRegular'] } }, + { select: '.\\61 BMPRegular', expect: { count: 1, ids: ['aBMPRegular'] } }, - { selector: '#\\1f511 nonBMP', expect: { count: 1, ids: ['\u{1f511}nonBMP'] } }, - { selector: '.\\1f511 nonBMP', expect: { count: 1, ids: ['\u{1f511}nonBMP'] } }, + { select: '#\\1f511 nonBMP', expect: { count: 1, ids: ['\u{1f511}nonBMP'] } }, + { select: '.\\1f511 nonBMP', expect: { count: 1, ids: ['\u{1f511}nonBMP'] } }, - { selector: '#\\30\\30 continueEscapesA', expect: { count: 1, ids: ['00continueEscapesA'] } }, - { selector: '.\\30\\30 continueEscapesA', expect: { count: 1, ids: ['00continueEscapesA'] } }, + { select: '#\\30\\30 continueEscapesA', expect: { count: 1, ids: ['00continueEscapesA'] } }, + { select: '.\\30\\30 continueEscapesA', expect: { count: 1, ids: ['00continueEscapesA'] } }, - { selector: '#\\30 \\30 continueEscapesB', expect: { count: 1, ids: ['00continueEscapesB'] } }, - { selector: '.\\30 \\30 continueEscapesB', expect: { count: 1, ids: ['00continueEscapesB'] } }, + { select: '#\\30 \\30 continueEscapesB', expect: { count: 1, ids: ['00continueEscapesB'] } }, + { select: '.\\30 \\30 continueEscapesB', expect: { count: 1, ids: ['00continueEscapesB'] } }, - { selector: '#continueEscapesC\\30 \\30 ', expect: { count: 1, ids: ['continueEscapesC00'] } }, - { selector: '.continueEscapesC\\30 \\30 ', expect: { count: 1, ids: ['continueEscapesC00'] } }, + { select: '#continueEscapesC\\30 \\30 ', expect: { count: 1, ids: ['continueEscapesC00'] } }, + { select: '.continueEscapesC\\30 \\30 ', expect: { count: 1, ids: ['continueEscapesC00'] } }, - { selector: '#continueEscapesD\\30 \\30', expect: { count: 1, ids: ['continueEscapesD00'] } }, - { selector: '.continueEscapesD\\30 \\30', expect: { count: 1, ids: ['continueEscapesD00'] } }, + { select: '#continueEscapesD\\30 \\30', expect: { count: 1, ids: ['continueEscapesD00'] } }, + { select: '.continueEscapesD\\30 \\30', expect: { count: 1, ids: ['continueEscapesD00'] } }, - { selector: '#continueEscapesE\\30\\30 ', expect: { count: 1, ids: ['continueEscapesE00'] } }, - { selector: '.continueEscapesE\\30\\30 ', expect: { count: 1, ids: ['continueEscapesE00'] } }, + { select: '#continueEscapesE\\30\\30 ', expect: { count: 1, ids: ['continueEscapesE00'] } }, + { select: '.continueEscapesE\\30\\30 ', expect: { count: 1, ids: ['continueEscapesE00'] } }, - { selector: '#continueEscapesF\\30\\30', expect: { count: 1, ids: ['continueEscapesF00'] } }, - { selector: '.continueEscapesF\\30\\30', expect: { count: 1, ids: ['continueEscapesF00'] } }, + { select: '#continueEscapesF\\30\\30', expect: { count: 1, ids: ['continueEscapesF00'] } }, + { select: '.continueEscapesF\\30\\30', expect: { count: 1, ids: ['continueEscapesF00'] } }, ], }, @@ -265,62 +265,62 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // ident tests case from CSS tests of chromium source: https://goo.gl/3Cxdov - { selector: '#hel\\6CoA', expect: { count: 1, ids: ['helloA'] } }, - { selector: '.hel\\6CoA', expect: { count: 1, ids: ['helloA'] } }, + { select: '#hel\\6CoA', expect: { count: 1, ids: ['helloA'] } }, + { select: '.hel\\6CoA', expect: { count: 1, ids: ['helloA'] } }, - { selector: '#hel\\6C oB', expect: { count: 1, ids: ['helloB'] } }, - { selector: '.hel\\6C oB', expect: { count: 1, ids: ['helloB'] } }, + { select: '#hel\\6C oB', expect: { count: 1, ids: ['helloB'] } }, + { select: '.hel\\6C oB', expect: { count: 1, ids: ['helloB'] } }, - { selector: '#\\26 B', expect: { count: 1, ids: ['&B'] } }, - { selector: '.\\26 B', expect: { count: 1, ids: ['&B'] } }, + { select: '#\\26 B', expect: { count: 1, ids: ['&B'] } }, + { select: '.\\26 B', expect: { count: 1, ids: ['&B'] } }, - { selector: '#spac\\65\r\nsA', expect: { count: 1, ids: ['spacesA'] } }, - { selector: '.spac\\65\r\nsA', expect: { count: 1, ids: ['spacesA'] } }, + { select: '#spac\\65\r\nsA', expect: { count: 1, ids: ['spacesA'] } }, + { select: '.spac\\65\r\nsA', expect: { count: 1, ids: ['spacesA'] } }, - { selector: '#sp\\61\tc\\65\fsB', expect: { count: 1, ids: ['spacesB'] } }, - { selector: '.sp\\61\tc\\65\fsB', expect: { count: 1, ids: ['spacesB'] } }, + { select: '#sp\\61\tc\\65\fsB', expect: { count: 1, ids: ['spacesB'] } }, + { select: '.sp\\61\tc\\65\fsB', expect: { count: 1, ids: ['spacesB'] } }, - { selector: '#\\E000', expect: { count: 1, ids: ['\u{E000}'] } }, - { selector: '.\\E000', expect: { count: 1, ids: ['\u{E000}'] } }, + { select: '#\\E000', expect: { count: 1, ids: ['\u{E000}'] } }, + { select: '.\\E000', expect: { count: 1, ids: ['\u{E000}'] } }, - { selector: '#testA\\D799', expect: { count: 1, ids: ['testA\u{D799}'] } }, - { selector: '.testA\\D799', expect: { count: 1, ids: ['testA\u{D799}'] } }, + { select: '#testA\\D799', expect: { count: 1, ids: ['testA\u{D799}'] } }, + { select: '.testA\\D799', expect: { count: 1, ids: ['testA\u{D799}'] } }, - { selector: '#te\\s\\tB', expect: { count: 1, ids: ['testB'] } }, - { selector: '.te\\s\\tB', expect: { count: 1, ids: ['testB'] } }, + { select: '#te\\s\\tB', expect: { count: 1, ids: ['testB'] } }, + { select: '.te\\s\\tB', expect: { count: 1, ids: ['testB'] } }, - { selector: '#\\.\\,\\:\\!', expect: { count: 1, ids: ['.,:!'] } }, - { selector: '.\\.\\,\\:\\!', expect: { count: 1, ids: ['.,:!'] } }, + { select: '#\\.\\,\\:\\!', expect: { count: 1, ids: ['.,:!'] } }, + { select: '.\\.\\,\\:\\!', expect: { count: 1, ids: ['.,:!'] } }, - { selector: '#nullA\\0', expect: { count: 1, ids: ['nullA\u{fffd}'] } }, - { selector: '.nullA\\0', expect: { count: 1, ids: ['nullA\u{fffd}'] } }, + { select: '#nullA\\0', expect: { count: 1, ids: ['nullA\u{fffd}'] } }, + { select: '.nullA\\0', expect: { count: 1, ids: ['nullA\u{fffd}'] } }, - { selector: '#nullB\\0000', expect: { count: 1, ids: ['nullB\u{fffd}'] } }, - { selector: '.nullB\\0000', expect: { count: 1, ids: ['nullB\u{fffd}'] } }, + { select: '#nullB\\0000', expect: { count: 1, ids: ['nullB\u{fffd}'] } }, + { select: '.nullB\\0000', expect: { count: 1, ids: ['nullB\u{fffd}'] } }, - { selector: '#largeA\\110000', expect: { count: 1, ids: ['largeA\u{fffd}'] } }, - { selector: '.largeA\\110000', expect: { count: 1, ids: ['largeA\u{fffd}'] } }, + { select: '#largeA\\110000', expect: { count: 1, ids: ['largeA\u{fffd}'] } }, + { select: '.largeA\\110000', expect: { count: 1, ids: ['largeA\u{fffd}'] } }, - { selector: '#largeB\\23456a', expect: { count: 1, ids: ['largeB\u{fffd}'] } }, - { selector: '.largeB\\23456a', expect: { count: 1, ids: ['largeB\u{fffd}'] } }, + { select: '#largeB\\23456a', expect: { count: 1, ids: ['largeB\u{fffd}'] } }, + { select: '.largeB\\23456a', expect: { count: 1, ids: ['largeB\u{fffd}'] } }, - { selector: '#surrogateA\\D800', expect: { count: 1, ids: ['surrogateA\u{fffd}'] } }, - { selector: '.surrogateA\\D800', expect: { count: 1, ids: ['surrogateA\u{fffd}'] } }, + { select: '#surrogateA\\D800', expect: { count: 1, ids: ['surrogateA\u{fffd}'] } }, + { select: '.surrogateA\\D800', expect: { count: 1, ids: ['surrogateA\u{fffd}'] } }, - { selector: '#surrogateB\\0DBAC', expect: { count: 1, ids: ['surrogateB\u{fffd}'] } }, - { selector: '.surrogateB\\0DBAC', expect: { count: 1, ids: ['surrogateB\u{fffd}'] } }, + { select: '#surrogateB\\0DBAC', expect: { count: 1, ids: ['surrogateB\u{fffd}'] } }, + { select: '.surrogateB\\0DBAC', expect: { count: 1, ids: ['surrogateB\u{fffd}'] } }, - { selector: '#\\00DFFFsurrogateC', expect: { count: 1, ids: ['\u{fffd}surrogateC'] } }, - { selector: '.\\00DFFFsurrogateC', expect: { count: 1, ids: ['\u{fffd}surrogateC'] } }, + { select: '#\\00DFFFsurrogateC', expect: { count: 1, ids: ['\u{fffd}surrogateC'] } }, + { select: '.\\00DFFFsurrogateC', expect: { count: 1, ids: ['\u{fffd}surrogateC'] } }, - { selector: '#\\10fFfF', expect: { count: 1, ids: ['\u{10ffff}'] } }, - { selector: '.\\10fFfF', expect: { count: 1, ids: ['\u{10ffff}'] } }, + { select: '#\\10fFfF', expect: { count: 1, ids: ['\u{10ffff}'] } }, + { select: '.\\10fFfF', expect: { count: 1, ids: ['\u{10ffff}'] } }, - { selector: '#\\10fFfF0', expect: { count: 1, ids: ['\u{10ffff}0'] } }, - { selector: '.\\10fFfF0', expect: { count: 1, ids: ['\u{10ffff}0'] } }, + { select: '#\\10fFfF0', expect: { count: 1, ids: ['\u{10ffff}0'] } }, + { select: '.\\10fFfF0', expect: { count: 1, ids: ['\u{10ffff}0'] } }, - { selector: '#\\10000000', expect: { count: 1, ids: ['\u{100000}00'] } }, - { selector: '.\\10000000', expect: { count: 1, ids: ['\u{100000}00'] } }, + { select: '#\\10000000', expect: { count: 1, ids: ['\u{100000}00'] } }, + { select: '.\\10000000', expect: { count: 1, ids: ['\u{100000}00'] } }, ], }, { @@ -346,50 +346,50 @@ runScenarios('css3 escaped identifiers', 'normal', [ setupPage: setupNw, cases: [ // ident tests case from CSS tests of chromium source: https://goo.gl/3Cxdov - { selector: '#simple-ident', expect: { count: 1, ids: ['simple-ident'] } }, - { selector: '.simple-ident', expect: { count: 1, ids: ['simple-ident'] } }, + { select: '#simple-ident', expect: { count: 1, ids: ['simple-ident'] } }, + { select: '.simple-ident', expect: { count: 1, ids: ['simple-ident'] } }, - { selector: '#testing123', expect: { count: 1, ids: ['testing123'] } }, - { selector: '.testing123', expect: { count: 1, ids: ['testing123'] } }, + { select: '#testing123', expect: { count: 1, ids: ['testing123'] } }, + { select: '.testing123', expect: { count: 1, ids: ['testing123'] } }, - { selector: '#_underscore', expect: { count: 1, ids: ['_underscore'] } }, - { selector: '._underscore', expect: { count: 1, ids: ['_underscore'] } }, + { select: '#_underscore', expect: { count: 1, ids: ['_underscore'] } }, + { select: '._underscore', expect: { count: 1, ids: ['_underscore'] } }, - { selector: '#-text', expect: { count: 1, ids: ['-text'] } }, - { selector: '.-text', expect: { count: 1, ids: ['-text'] } }, + { select: '#-text', expect: { count: 1, ids: ['-text'] } }, + { select: '.-text', expect: { count: 1, ids: ['-text'] } }, - { selector: '#-\\6d', expect: { count: 1, ids: ['-m'] } }, - { selector: '.-\\6d', expect: { count: 1, ids: ['-m'] } }, + { select: '#-\\6d', expect: { count: 1, ids: ['-m'] } }, + { select: '.-\\6d', expect: { count: 1, ids: ['-m'] } }, - { selector: '#--abc', expect: { count: 1, ids: ['--abc'] } }, - { selector: '.--abc', expect: { count: 1, ids: ['--abc'] } }, + { select: '#--abc', expect: { count: 1, ids: ['--abc'] } }, + { select: '.--abc', expect: { count: 1, ids: ['--abc'] } }, - { selector: '#--', expect: { count: 1, ids: ['--'] } }, - { selector: '.--', expect: { count: 1, ids: ['--'] } }, + { select: '#--', expect: { count: 1, ids: ['--'] } }, + { select: '.--', expect: { count: 1, ids: ['--'] } }, - { selector: '#--11', expect: { count: 1, ids: ['--11'] } }, - { selector: '.--11', expect: { count: 1, ids: ['--11'] } }, + { select: '#--11', expect: { count: 1, ids: ['--11'] } }, + { select: '.--11', expect: { count: 1, ids: ['--11'] } }, - { selector: '#---', expect: { count: 1, ids: ['---'] } }, - { selector: '.---', expect: { count: 1, ids: ['---'] } }, + { select: '#---', expect: { count: 1, ids: ['---'] } }, + { select: '.---', expect: { count: 1, ids: ['---'] } }, - { selector: '#\u{2003}', expect: { count: 1, ids: ['\u{2003}'] } }, - { selector: '.\u{2003}', expect: { count: 1, ids: ['\u{2003}'] } }, + { select: '#\u{2003}', expect: { count: 1, ids: ['\u{2003}'] } }, + { select: '.\u{2003}', expect: { count: 1, ids: ['\u{2003}'] } }, - { selector: '#\u{A0}', expect: { count: 1, ids: ['\u{A0}'] } }, - { selector: '.\u{A0}', expect: { count: 1, ids: ['\u{A0}'] } }, + { select: '#\u{A0}', expect: { count: 1, ids: ['\u{A0}'] } }, + { select: '.\u{A0}', expect: { count: 1, ids: ['\u{A0}'] } }, - { selector: '#\u{1234}', expect: { count: 1, ids: ['\u{1234}'] } }, - { selector: '.\u{1234}', expect: { count: 1, ids: ['\u{1234}'] } }, + { select: '#\u{1234}', expect: { count: 1, ids: ['\u{1234}'] } }, + { select: '.\u{1234}', expect: { count: 1, ids: ['\u{1234}'] } }, - { selector: '#\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, - { selector: '.\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, + { select: '#\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, + { select: '.\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, - { selector: '#\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, - { selector: '.\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, + { select: '#\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, + { select: '.\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, - { selector: '#ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, - { selector: '.ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, + { select: '#ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, + { select: '.ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, ], }, { @@ -397,16 +397,16 @@ runScenarios('css3 escaped identifiers', 'normal', [ html: `
    `, setupPage: setupNw, cases: [ - { selector: '#spaces\\ in\\\tident', expect: { count: 1, ids: ['spaces in\tident'] } }, + { select: '#spaces\\ in\\\tident', expect: { count: 1, ids: ['spaces in\tident'] } }, ], }, { name: 'spaces in ident class selector mismatch', - modifier: 'fail', + status: 'fail', html: `
    `, setupPage: setupNw, cases: [ - { selector: '.spaces\\ in\\\tident', expect: { count: 1, ids: ['spaces in\tident'] } }, + { select: '.spaces\\ in\\\tident', expect: { count: 1, ids: ['spaces in\tident'] } }, ], }, ]); \ No newline at end of file diff --git a/test_new/browser/haness.test.ts b/test_new/browser/haness.test.ts index e35b671..1b06b09 100644 --- a/test_new/browser/haness.test.ts +++ b/test_new/browser/haness.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('scope', 'normal', [ { @@ -9,10 +9,10 @@ runScenarios('scope', 'normal', [
    `, cases: [ - { selector: 'div', expect: { classes: ['outer primary', 'other-outer', ''] } }, - { selector: 'div', expect: { includesClasses: ['other-outer'] } }, - { selector: 'div', expect: { includesClasses: ['outer', 'primary'] } }, - { selector: 'div', expect: { excludesClasses: ['missing-class'] } }, + { select: 'div', expect: { classes: ['outer primary', 'other-outer', ''] } }, + { select: 'div', expect: { includesClasses: ['other-outer'] } }, + { select: 'div', expect: { includesClasses: ['outer', 'primary'] } }, + { select: 'div', expect: { excludesClasses: ['missing-class'] } }, ], } ]); \ No newline at end of file diff --git a/test_new/browser/harness.ts b/test_new/browser/harness.ts deleted file mode 100644 index e335413..0000000 --- a/test_new/browser/harness.ts +++ /dev/null @@ -1,367 +0,0 @@ -import test, { chromium, expect, firefox, webkit } from '@playwright/test'; -import type { Browser, Page } from '@playwright/test'; - -export type SelectorScenario = { - name: string; - html: string; - htmlMode?: 'body' | 'document'; - browsers?: BrowserName[]; - cases: SelectorCase[]; - setupPage?: (page: Page) => void | Promise; - modifier?: TestModifier; -}; - -const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; -type BrowserName = typeof BROWSER_NAMES[number]; - -type SelectorCase = { - selector: string; - root?: SelectorRoot; - expect?: SelectorExpectation; -}; - -type DescribeModifier = 'normal' | 'skip' | 'only' | 'fixme'; -type TestModifier = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; - -type SelectorExpectation = { - allowMismatch?: boolean; - count?: number; - ids?: string[]; - includesIds?: string[]; - excludesIds?: string[]; - classes?: string[]; - includesClasses?: string[]; - excludesClasses?: string[]; - throws?: boolean; - equivalentTo?: { selector: string; root?: SelectorRoot }; -}; - -type SelectorResult = { - mismatchMsg?: string; -} & Record; - -const ENGINES = ['native', 'nw'] as const; -type Engine = typeof ENGINES[number]; - -type EngineResult = { - count: number; - ids: string[]; - classes: string[]; - threw: boolean; - equivalentToFailMsg?: string; -}; - -type SelectorRoot = - | { kind: 'document' } - | { kind: 'id'; value: string } - | { kind: 'selector'; value: string }; - -export function runScenarios( - label: string, - modifier: DescribeModifier, - scenarios: SelectorScenario[], -): void { - const describeFn = getDescribeFn(modifier); - describeFn(label, () => { - let browsers: Record; - let pages: Record; - - test.beforeAll(async () => { - browsers = { - chromium: await chromium.launch(), - firefox: await firefox.launch(), - webkit: await webkit.launch(), - }; - - pages = { - chromium: await browsers.chromium.newPage(), - firefox: await browsers.firefox.newPage(), - webkit: await browsers.webkit.newPage(), - }; - - for (const page of Object.values(pages)) { - await page.setContent(''); - await page.addScriptTag({ path: 'src/nwsapi.js' }); - } - }); - - test.afterAll(async () => { - await Promise.all(BROWSER_NAMES.map((name) => browsers[name].close())); - }); - - for (const s of scenarios) { - const testFn = getTestFn(s.modifier); - testFn(s.name, async () => { - await runScenario(s, pages); - }); - } - }); -} - -async function runScenario(s: SelectorScenario, pages: Record): Promise { - const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; - - for (const browserName of scenarioBrowsers) { - const page = pages[browserName]; - await setupPage(page, s); - - for (const c of s.cases) { - const result = await evalSelector(page, c); - - const expectation = c.expect ?? {}; - const allowMismatch = expectation.allowMismatch ?? false; - const msg = `[${browserName}] ${s.name} :: ${c.selector}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; - - if (expectation.throws) { - expect(result.nw.threw, msg).toBe(true); - continue; - } - - expect(result.nw.threw, msg).toBe(false); - - const nativeThrew = result.native.threw; - - if (expectation.count !== undefined) { - expectEngines(result, msg, 'count', (r, label) => { - expect(r.count, label).toEqual(expectation.count); - }); - } - - if (expectation.ids) { - expectEngines(result, msg, 'ids', (r, label) => { - expect(r.ids, label).toEqual(expectation.ids); - }); - } - - if (expectation.includesIds) { - for (const id of expectation.includesIds) { - expectEngines(result, msg, 'ids', (r, label) => { - expect(r.ids, label).toContain(id); - }); - } - } - - if (expectation.excludesIds) { - for (const id of expectation.excludesIds) { - expectEngines(result, msg, 'ids', (r, label) => { - expect(r.ids, label).not.toContain(id); - }); - } - } - - if (expectation.classes) { - expectEngines(result, msg, 'classes', (r, label) => { - expect(r.classes, label).toEqual(expectation.classes); - }); - } - - if (expectation.includesClasses) { - for (const cls of expectation.includesClasses) { - expectEngines(result, msg, 'classes', (r, label) => { - const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, label).toContain(cls); - }); - } - } - - if (expectation.excludesClasses) { - for (const cls of expectation.excludesClasses) { - expectEngines(result, msg, 'classes', (r, label) => { - const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, label).not.toContain(cls); - }); - } - } - - if (expectation.equivalentTo) { - expectEngines(result, msg, 'equivalentToFailMsg', (r, label) => { - expect(r.equivalentToFailMsg, label).toBeUndefined(); - }); - } - - if (!allowMismatch && !nativeThrew) { - expect(result.mismatchMsg, msg).toBeUndefined(); - } - } - } -} - -function expectEngines( - result: SelectorResult, - baseMsg: string, - key: keyof EngineResult, - check: (r: EngineResult, label: string) => void -): void { - for (const engine of ENGINES) { - const r = result[engine]; - if (r.threw) continue; - - const sameIds = (a: string[], b: string[]): boolean => - a.length === b.length && a.every((x, i) => x === b[i]); - - const enginesWithSameOutcome = ENGINES.filter((e) => { - if (result[e].threw) return false; - switch (key) { - case 'count': return r.count === result[e].count; - case 'ids': return sameIds(r.ids, result[e].ids); - case 'classes': return sameIds(r.classes, result[e].classes); - case 'threw': return r.threw === result[e].threw; - case 'equivalentToFailMsg': return e === engine; - default: assertNever(key); - } - }); - - let label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; - if (r.equivalentToFailMsg && key === 'equivalentToFailMsg') label += `\n${r.equivalentToFailMsg}`; - check(r, label); - } -} - -function getDescribeFn(mode?: DescribeModifier) { - if (mode === 'skip') return test.describe.skip; - if (mode === 'only') return test.describe.only; - if (mode === 'fixme') return test.describe.fixme; - return test.describe; -} - -function getTestFn(mode?: TestModifier) { - if (mode === 'skip') return test.skip; - if (mode === 'only') return test.only; - if (mode === 'fixme') return test.fixme; - if (mode === 'fail') return test.fail; - return test; -} - -async function setupPage(page: Page, scenario: SelectorScenario): Promise { - if (scenario.htmlMode === 'document') { - await page.setContent(scenario.html); - await page.addScriptTag({ path: 'src/nwsapi.js' }); - if (scenario.setupPage) await scenario.setupPage(page); - return; - } - - await page.evaluate((bodyHtml) => { - document.body.innerHTML = bodyHtml; - }, scenario.html); - if (scenario.setupPage) await scenario.setupPage(page); -} - -async function evalSelector(page: Page, selCase: SelectorCase): Promise { - return await page.evaluate((c) => { - const native: EngineResult = { count: 0, ids: [], classes: [], threw: false }; - const nw: EngineResult = { count: 0, ids: [], classes: [], threw: false }; - - type QueryResult = { elements: Element[]; error: string }; - const runQuery = (query: () => Element[]): QueryResult => { - try { - return { elements: query(), error: '' }; - } catch (e) { - return { elements: [], error: e instanceof Error ? e.message : String(e) }; - } - }; - - const describe = (el: Element | undefined) => { - if (!el) return '(missing)'; - return { - tag: el.tagName.toLowerCase(), - id: el.id || null, - className: el.className || '', - html: el.outerHTML.replace(/\s+/g, ' ').slice(0, 120), - }; - }; - - type NamedQueryResult = { name: string; result: QueryResult }; - const compareQueryResults = (a: NamedQueryResult, b: NamedQueryResult): string | undefined => { - if (a.result.error || b.result.error) { - if (a.result.error && b.result.error) return `Both threw:\n ${a.name} error: ${a.result.error}\n ${b.name} error: ${b.result.error}`; - return a.result.error - ? `${a.name} threw while ${b.name} did not: ${a.result.error}` - : `${b.name} threw while ${a.name} did not: ${b.result.error}`; - } - - const aElems = a.result.elements; - const bElems = b.result.elements; - - let mismatchMsg: string | undefined; - if (aElems.length !== bElems.length) { - mismatchMsg = `Count mismatch: ${a.name}=${aElems.length}, ${b.name}=${bElems.length}`; - } - - const maxLen = Math.max(aElems.length, bElems.length); - for (let i = 0; i < maxLen; ++i) { - if (aElems[i] !== bElems[i]) { - mismatchMsg = mismatchMsg ? mismatchMsg + '\n' : ''; - mismatchMsg += `Element mismatch at index ${i}:\n` + - `${a.name}[${i}] = ${JSON.stringify(describe(aElems[i]))}\n` + - `${b.name}[${i}] = ${JSON.stringify(describe(bElems[i]))}`; - break; - } - } - - return mismatchMsg; - }; - - const getRoot = (root: SelectorRoot | undefined): ParentNode | null => { - return !root || root.kind === 'document' ? document - : root.kind === 'id' ? document.getElementById(root.value) - : root.kind === 'selector' ? document.querySelector(root.value) - : null; // impossible case - } - - const root = getRoot(c.root); - - if (!root) { - return { - native, nw, - mismatchMsg: `Root element not found for selector: ${JSON.stringify(c.root)}`, - }; - } - - const nativeRes = runQuery(() => [...root.querySelectorAll(c.selector)]); - const nwRes = runQuery(() => NW.Dom.select(c.selector, root)); - [{ engine: native, res: nativeRes }, { engine: nw, res: nwRes }] - .forEach(({ engine, res }) => { - engine.count = res.elements.length; - engine.ids = res.elements.map((el) => el.getAttribute('id') ?? ''); - engine.classes = res.elements.map((el) => el.getAttribute('class') ?? ''); - engine.threw = !!res.error; - }); - - const mismatchMsg = compareQueryResults( - { name: 'native', result: nativeRes }, - { name: 'nw', result: nwRes }, - ); - - if (c.expect?.equivalentTo) { - const eqSelector = c.expect.equivalentTo.selector; - const eqRoot = getRoot(c.expect.equivalentTo.root); - - let eqNativeRes: QueryResult; - let eqNwRes: QueryResult; - if (!eqRoot) { - const error = `Equivalent selector root not found: ${JSON.stringify(c.expect.equivalentTo.root)}`; - eqNativeRes = { elements: [], error }; - eqNwRes = { elements: [], error }; - } else { - eqNativeRes = runQuery(() => [...eqRoot.querySelectorAll(eqSelector)]); - eqNwRes = runQuery(() => NW.Dom.select(eqSelector, eqRoot)); - } - - native.equivalentToFailMsg = compareQueryResults( - { name: `native(${c.selector})`, result: nativeRes }, - { name: `native(${eqSelector})`, result: eqNativeRes }, - ); - - nw.equivalentToFailMsg = compareQueryResults( - { name: `nw(${c.selector})`, result: nwRes }, - { name: `nw(${eqSelector})`, result: eqNwRes }, - ); - } - - return { mismatchMsg, native, nw }; - }, selCase); -} - -function assertNever(x: never): never { - throw new Error(`Unexpected key: ${x}`); -} diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts new file mode 100644 index 0000000..4443d8a --- /dev/null +++ b/test_new/browser/harness/browser.ts @@ -0,0 +1,144 @@ +import type { Engine, EquivalentTo, ScopeRef } from "./scenarios"; + +export interface PwHelpers { + resolveScope(ref: ScopeRef | undefined): ParentNode | null; + runQuery(query: () => Element[]): QueryResult; + compareQueryResults( + a: NamedQueryResult, + b: NamedQueryResult + ): string | undefined; + toEngineResult(res: QueryResult): EngineResult; + getResults( + engines: EngineQueries, + query: string, + ref?: ScopeRef, + equivTo?: EquivalentTo + ): EngineQueryResults; +} + +export type EvalResult = { + info: string; + mismatchMsg?: string; +} & Record; + +export type EngineResult = { + count: number; + ids: string[]; + classes: string[]; + threw: boolean; + equivalentToFailMsg?: string; +}; + +export type EngineQueries = Record; +export type EngineQuery = (query: string, scope: ParentNode) => () => Element[]; +export type EngineQueryResults = Record; +export type NamedQueryResult = { name: string; result: QueryResult; }; +export type QueryResult = { elements: Element[]; error: string }; + +export function installBrowserHelpers(): void { + function runQuery(query: () => Element[]) { + try { + return { elements: query(), error: '' }; + } catch (e) { + return { elements: [], error: e instanceof Error ? e.message : String(e) }; + } + } + + function describe(el: Element | undefined) { + if (!el) return '(missing)'; + return { + tag: el.tagName.toLowerCase(), + id: el.id || null, + className: el.className || '', + html: el.outerHTML.replace(/\s+/g, ' ').slice(0, 120), + }; + } + + function compareQueryResults(a: NamedQueryResult, b: NamedQueryResult): string | undefined { + if (a.result.error || b.result.error) { + if (a.result.error && b.result.error) { + return `Both threw:\n ${a.name} error: ${a.result.error}\n ${b.name} error: ${b.result.error}`; + } + return a.result.error + ? `${a.name} threw while ${b.name} did not: ${a.result.error}` + : `${b.name} threw while ${a.name} did not: ${b.result.error}`; + } + + const aElems = a.result.elements; + const bElems = b.result.elements; + + let mismatchMsg: string | undefined; + if (aElems.length !== bElems.length) { + mismatchMsg = `Count mismatch: ${a.name}=${aElems.length}, ${b.name}=${bElems.length}`; + } + + const maxLen = Math.max(aElems.length, bElems.length); + for (let i = 0; i < maxLen; ++i) { + if (aElems[i] !== bElems[i]) { + mismatchMsg = mismatchMsg ? mismatchMsg + '\n' : ''; + mismatchMsg += `Element mismatch at index ${i}:\n` + + `${a.name}[${i}] = ${JSON.stringify(describe(aElems[i]))}\n` + + `${b.name}[${i}] = ${JSON.stringify(describe(bElems[i]))}`; + break; + } + } + + return mismatchMsg; + } + + function resolveScope(ref?: ScopeRef): ParentNode | null { + return !ref || ref.by === 'document' ? document + : ref.by === 'id' ? document.getElementById(ref.id) + : ref.by === 'first' ? document.querySelector(ref.selector) + : null; + } + + function toEngineResult(res: QueryResult): EngineResult { + return { + count: res.elements.length, + ids: res.elements.map((el) => el.getAttribute('id') ?? ''), + classes: res.elements.map((el) => el.getAttribute('class') ?? ''), + threw: !!res.error, + }; + } + + function getResults(engines: EngineQueries, query: string, ref?: ScopeRef, equivTo?: EquivalentTo): EngineQueryResults{ + const scope = resolveScope(ref); + const equivScope = resolveScope(equivTo?.scope); + + const build = (name: Engine, queryFn: EngineQuery) => { + const queryResult: QueryResult = scope + ? runQuery(queryFn(query, scope)) + : { elements: [], error: `Scope target not found for ref: ${JSON.stringify(ref)}` }; + + const engineResult = toEngineResult(queryResult); + + if (equivTo) { + const equivQuery = equivTo.search; + const equivRes: QueryResult = equivScope + ? runQuery(queryFn(equivQuery, equivScope)) + : { elements: [], error: `Equivalent scope target not found: ${JSON.stringify(equivTo.scope)}` }; + + engineResult.equivalentToFailMsg = compareQueryResults( + { name: `${name}(${query})`, result: queryResult }, + { name: `${name}(${equivQuery})`, result: equivRes }, + ); + } + + return { queryResult, engineResult }; + }; + + return { + native: build('native', engines.native), + nw: build('nw', engines.nw), + }; + } + + window.__pwHelpers = { + resolveScope, + runQuery, + compareQueryResults, + toEngineResult, + getResults, + }; +}; diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts new file mode 100644 index 0000000..ae0fc1e --- /dev/null +++ b/test_new/browser/harness/scenarios.ts @@ -0,0 +1,239 @@ +import test, { chromium, expect, firefox, webkit } from '@playwright/test'; +import type { Browser, Page } from '@playwright/test'; +import { evalSelectCase, type SelectCase } from './select'; +import { assertNever } from '../../utils/type'; +import { installBrowserHelpers, type EngineResult, type EvalResult } from './browser'; + +type Case = SelectCase; +const isSelectCase = (c: Case): c is SelectCase => 'select' in c; + +export type Scenario = { + name: string; + html: string; + htmlMode?: 'body' | 'document'; + browsers?: BrowserName[]; + cases: Case[]; + setupPage?: (page: Page) => void | Promise; + status?: TestStatus; +}; + +export type Expectation = { + allowMismatch?: boolean; + count?: number; + ids?: string[]; + includesIds?: string[]; + excludesIds?: string[]; + classes?: string[]; + includesClasses?: string[]; + excludesClasses?: string[]; + throws?: boolean; + equivalentTo?: EquivalentTo; +}; + +export type EquivalentTo = { search: string; scope?: ScopeRef }; + +const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; +export type BrowserName = typeof BROWSER_NAMES[number]; + +type DescribeStatus = 'normal' | 'skip' | 'only' | 'fixme'; +type TestStatus = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; + +export const ENGINES = ['native', 'nw'] as const; +export type Engine = typeof ENGINES[number]; + +export type ScopeRef = + | { by: 'document' } + | { by: 'id'; id: string } + | { by: 'first'; selector: string }; + +export function runScenarios(label: string, status: DescribeStatus, scenarios: Scenario[]): void { + const describeFn = getDescribeFn(status); + describeFn(label, () => { + let browsers: Record; + let pages: Record; + + test.beforeAll(async () => { + browsers = { + chromium: await chromium.launch(), + firefox: await firefox.launch(), + webkit: await webkit.launch(), + }; + + pages = { + chromium: await browsers.chromium.newPage(), + firefox: await browsers.firefox.newPage(), + webkit: await browsers.webkit.newPage(), + }; + + for (const page of Object.values(pages)) { + await page.setContent(''); + await page.evaluate(installBrowserHelpers); + await page.addScriptTag({ path: 'src/nwsapi.js' }); + } + }); + + test.afterAll(async () => { + await Promise.all(BROWSER_NAMES.map((name) => browsers[name].close())); + }); + + for (const s of scenarios) { + const testFn = getTestFn(s.status); + testFn(s.name, async () => { + await runScenario(s, pages); + }); + } + }); +} + +export async function runScenario(s: Scenario, pages: Record): Promise { + const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; + + for (const browserName of scenarioBrowsers) { + const page = pages[browserName]; + await setupPage(page, s); + for (const c of s.cases) { + let result: EvalResult | undefined; + if (isSelectCase(c)) result = await evalSelectCase(page, c); + else throw new Error(`Unsupported case type in scenario "${s.name}" for browser "${browserName}"`); + + const expectation = c.expect ?? {}; + checkResult(result, expectation, browserName, s.name); + } + } +} + +function getDescribeFn(mode?: DescribeStatus) { + if (mode === 'skip') return test.describe.skip; + if (mode === 'only') return test.describe.only; + if (mode === 'fixme') return test.describe.fixme; + return test.describe; +} + +function getTestFn(mode?: TestStatus) { + if (mode === 'skip') return test.skip; + if (mode === 'only') return test.only; + if (mode === 'fixme') return test.fixme; + if (mode === 'fail') return test.fail; + return test; +} + +async function setupPage(page: Page, scenario: Scenario): Promise { + if (scenario.htmlMode === 'document') { + await page.setContent(scenario.html); + await page.addScriptTag({ path: 'src/nwsapi.js' }); + if (scenario.setupPage) await scenario.setupPage(page); + return; + } + + await page.evaluate((bodyHtml) => { + document.body.innerHTML = bodyHtml; + }, scenario.html); + if (scenario.setupPage) await scenario.setupPage(page); +} + +export function expectEngines( + result: EvalResult, + baseMsg: string, + key: keyof EngineResult, + check: (r: EngineResult, label: string) => void +): void { + for (const engine of ENGINES) { + const r = result[engine]; + if (r.threw) continue; + + const sameIds = (a: string[], b: string[]): boolean => + a.length === b.length && a.every((x, i) => x === b[i]); + + const enginesWithSameOutcome = ENGINES.filter((e) => { + if (result[e].threw) return false; + switch (key) { + case 'count': return r.count === result[e].count; + case 'ids': return sameIds(r.ids, result[e].ids); + case 'classes': return sameIds(r.classes, result[e].classes); + case 'threw': return r.threw === result[e].threw; + case 'equivalentToFailMsg': return e === engine; + default: assertNever(key); + } + }); + + let label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; + if (r.equivalentToFailMsg && key === 'equivalentToFailMsg') label += `\n${r.equivalentToFailMsg}`; + check(r, label); + } +} + +function checkResult(result: EvalResult, expectation: Expectation, browserName: BrowserName, scenarioName: string): void { + const allowMismatch = expectation.allowMismatch ?? false; + const msg = `[${browserName}] ${scenarioName} :: ${result.info}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; + + if (expectation.throws) { + expect(result.nw.threw, msg).toBe(true); + return; + } + + expect(result.nw.threw, msg).toBe(false); + + const nativeThrew = result.native.threw; + + if (expectation.count !== undefined) { + expectEngines(result, msg, 'count', (r, label) => { + expect(r.count, label).toEqual(expectation.count); + }); + } + + if (expectation.ids) { + expectEngines(result, msg, 'ids', (r, label) => { + expect(r.ids, label).toEqual(expectation.ids); + }); + } + + if (expectation.includesIds) { + for (const id of expectation.includesIds) { + expectEngines(result, msg, 'ids', (r, label) => { + expect(r.ids, label).toContain(id); + }); + } + } + + if (expectation.excludesIds) { + for (const id of expectation.excludesIds) { + expectEngines(result, msg, 'ids', (r, label) => { + expect(r.ids, label).not.toContain(id); + }); + } + } + + if (expectation.classes) { + expectEngines(result, msg, 'classes', (r, label) => { + expect(r.classes, label).toEqual(expectation.classes); + }); + } + + if (expectation.includesClasses) { + for (const cls of expectation.includesClasses) { + expectEngines(result, msg, 'classes', (r, label) => { + const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); + expect(classTokens, label).toContain(cls); + }); + } + } + + if (expectation.excludesClasses) { + for (const cls of expectation.excludesClasses) { + expectEngines(result, msg, 'classes', (r, label) => { + const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); + expect(classTokens, label).not.toContain(cls); + }); + } + } + + if (expectation.equivalentTo) { + expectEngines(result, msg, 'equivalentToFailMsg', (r, label) => { + expect(r.equivalentToFailMsg, label).toBeUndefined(); + }); + } + + if (!allowMismatch && !nativeThrew) { + expect(result.mismatchMsg, msg).toBeUndefined(); + } +} diff --git a/test_new/browser/harness/select.ts b/test_new/browser/harness/select.ts new file mode 100644 index 0000000..e9caa7c --- /dev/null +++ b/test_new/browser/harness/select.ts @@ -0,0 +1,36 @@ +import type { Page } from '@playwright/test'; +import type { ScopeRef, Expectation } from './scenarios'; +import type { EngineQueries, EvalResult } from './browser'; + +export type SelectCase = { + select: string; + scope?: ScopeRef; + expect?: SelectExpectation; +}; + +type SelectExpectation = Expectation; + +export async function evalSelectCase(page: Page, selCase: SelectCase): Promise { + return await page.evaluate((c) => { + const pw = window.__pwHelpers; + const info = c.select; + + const engines: EngineQueries = { + native: (search, scope) => () => [...scope.querySelectorAll(search)], + nw: (search, scope) => () => NW.Dom.select(search, scope), + }; + + const res = pw.getResults(engines, c.select, c.scope, c.expect?.equivalentTo); + + const mismatchMsg = pw.compareQueryResults( + { name: 'native', result: res.native.queryResult }, + { name: 'nw', result: res.nw.queryResult }, + ); + + return { + info, mismatchMsg, + native: res.native.engineResult, + nw: res.nw.engineResult + }; + }, selCase); +} diff --git a/test_new/browser/html5.test.ts b/test_new/browser/html5.test.ts index 3aa6c8e..4f475d0 100644 --- a/test_new/browser/html5.test.ts +++ b/test_new/browser/html5.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('html5', 'normal', [ { @@ -25,21 +25,21 @@ runScenarios('html5', 'normal', [
    `, cases: [ - { selector: 'abbr:first-of-type', expect: { ids: ['IBA'] } }, - { selector: 'abbr:last-of-type', expect: { ids: ['UK'] } }, - { selector: 'mark:only-of-type', expect: { ids: ['NUM'] } }, - { selector: 'abbr:nth-of-type(1)', expect: { ids: ['IBA'] } }, - { selector: 'abbr:nth-of-type(2)', expect: { ids: ['UK'] } }, - { selector: 'abbr:nth-last-of-type(1)', expect: { ids: ['UK'] } }, - { selector: 'abbr:nth-last-of-type(2)', expect: { ids: ['IBA'] } }, - { selector: 'section li:first-of-type', expect: { ids: ['first'] } }, - { selector: 'section li:last-of-type', expect: { ids: ['last'] } }, + { select: 'abbr:first-of-type', expect: { ids: ['IBA'] } }, + { select: 'abbr:last-of-type', expect: { ids: ['UK'] } }, + { select: 'mark:only-of-type', expect: { ids: ['NUM'] } }, + { select: 'abbr:nth-of-type(1)', expect: { ids: ['IBA'] } }, + { select: 'abbr:nth-of-type(2)', expect: { ids: ['UK'] } }, + { select: 'abbr:nth-last-of-type(1)', expect: { ids: ['UK'] } }, + { select: 'abbr:nth-last-of-type(2)', expect: { ids: ['IBA'] } }, + { select: 'section li:first-of-type', expect: { ids: ['first'] } }, + { select: 'section li:last-of-type', expect: { ids: ['last'] } }, ], }, { name: 'table :not() selector test', - modifier: 'fixme', + status: 'fixme', html: `

     Your Search Results

    @@ -191,7 +191,7 @@ runScenarios('html5', 'normal', [ `, cases: [ { - selector: 'tbody > tr:nth-of-type(n+6):not(:nth-of-type(17)) > td:nth-of-type(2) > a:not(:nth-of-type(2))', + select: 'tbody > tr:nth-of-type(n+6):not(:nth-of-type(17)) > td:nth-of-type(2) > a:not(:nth-of-type(2))', expect: { ids: ['AV11UXA'] }, }, ], diff --git a/test_new/browser/issues.test.ts b/test_new/browser/issues.test.ts index 50eecaa..2289a55 100644 --- a/test_new/browser/issues.test.ts +++ b/test_new/browser/issues.test.ts @@ -1,9 +1,9 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('issues', 'normal', [ { name: 'issue 160 adjacent-descendant regression', - modifier: 'fixme', + status: 'fixme', html: `
    @@ -15,8 +15,8 @@ runScenarios('issues', 'normal', [
    `, cases: [ - { selector: '.neighbor + div .target', expect: { count: 1 } }, - { selector: '.neighbor + * .target', expect: { count: 1 } }, + { select: '.neighbor + div .target', expect: { count: 1 } }, + { select: '.neighbor + * .target', expect: { count: 1 } }, ], }, ]); \ No newline at end of file diff --git a/test_new/browser/jquery.test.ts b/test_new/browser/jquery.test.ts index 53f7508..0f36410 100644 --- a/test_new/browser/jquery.test.ts +++ b/test_new/browser/jquery.test.ts @@ -1,5 +1,5 @@ import { expect } from "@playwright/test"; -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; const html = ` @@ -268,33 +268,33 @@ runScenarios('jquery', 'normal', [ htmlMode: 'document', cases: [ // Compare universal selection directly against native behavior. - { selector: '*' }, + { select: '*' }, - { selector: 'p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'body', expect: { ids: ['body'] } }, - { selector: 'html', expect: { ids: ['html'] } }, - { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'body', expect: { ids: ['body'] } }, + { select: 'html', expect: { ids: ['html'] } }, + { select: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, // scoped selection - { selector: 'param', root: { kind: 'id', value: 'object1' }, expect: { count: 2 } }, + { select: 'param', scope: { by: 'id', id: 'object1' }, expect: { count: 2 } }, // Consistency checks for multiple selector groups - { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: '#length', expect: { count: 1 } }, - { selector: '#lengthtest input', expect: { count: 2 } }, + { select: '#length', expect: { count: 1 } }, + { select: '#lengthtest input', expect: { count: 2 } }, // Duplicate / sort-order checks from the original suite, expressed as counts. - { selector: '*', expect: { count: 187 } }, - { selector: '*, *', expect: { count: 187 } }, + { select: '*', expect: { count: 187 } }, + { select: '*, *', expect: { count: 187 } }, - { selector: 'p', expect: { count: 6 } }, - { selector: 'p, div p', expect: { count: 6 } }, + { select: 'p', expect: { count: 6 } }, + { select: 'p, div p', expect: { count: 6 } }, - { selector: 'h2, h1', expect: { ids: ['header', 'banner', 'userAgent'] } }, - { selector: 'p, p a', expect: { ids: ['firstp', 'simon1', 'ap', 'google', 'groups', 'anchor1', 'mark', 'sndp', 'en', 'yahoo', 'sap', 'anchor2', 'simon', 'first'] } }, + { select: 'h2, h1', expect: { ids: ['header', 'banner', 'userAgent'] } }, + { select: 'p, p a', expect: { ids: ['firstp', 'simon1', 'ap', 'google', 'groups', 'anchor1', 'mark', 'sndp', 'en', 'yahoo', 'sap', 'anchor2', 'simon', 'first'] } }, // jQuery extension pseudo, not native CSS // { selector: 'h2:first, h1:first', expect: { ids: ['header', 'banner'] } }, @@ -306,20 +306,20 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: '[', expect: { throws: true } }, - { selector: '(', expect: { throws: true } }, - { selector: '{', expect: { throws: true } }, - { selector: '<', expect: { throws: true } }, - { selector: '()', expect: { throws: true } }, - { selector: '<>', expect: { throws: true } }, - - { selector: ':nth-child(2n+-0)', expect: { throws: true } }, - { selector: ':nth-child(- 1n)', expect: { throws: true } }, - { selector: ':nth-child(-1 n)', expect: { throws: true } }, - { selector: ':first-child(n)', expect: { throws: true } }, - { selector: ':last-child(n)', expect: { throws: true } }, - { selector: ':only-child(n)', expect: { throws: true } }, - { selector: ':nth-child(2+0)', expect: { throws: true } }, + { select: '[', expect: { throws: true } }, + { select: '(', expect: { throws: true } }, + { select: '{', expect: { throws: true } }, + { select: '<', expect: { throws: true } }, + { select: '()', expect: { throws: true } }, + { select: '<>', expect: { throws: true } }, + + { select: ':nth-child(2n+-0)', expect: { throws: true } }, + { select: ':nth-child(- 1n)', expect: { throws: true } }, + { select: ':nth-child(-1 n)', expect: { throws: true } }, + { select: ':first-child(n)', expect: { throws: true } }, + { select: ':last-child(n)', expect: { throws: true } }, + { select: ':only-child(n)', expect: { throws: true } }, + { select: ':nth-child(2+0)', expect: { throws: true } }, ], }, @@ -328,40 +328,40 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: '#body', expect: { ids: ['body'] } }, - { selector: 'body#body', expect: { ids: ['body'] } }, - { selector: 'ul#first', expect: { ids: [] } }, + { select: '#body', expect: { ids: ['body'] } }, + { select: 'body#body', expect: { ids: ['body'] } }, + { select: 'ul#first', expect: { ids: [] } }, - { selector: '#firstp #simon1', expect: { ids: ['simon1'] } }, - { selector: '#firstp #foobar', expect: { ids: [] } }, + { select: '#firstp #simon1', expect: { ids: ['simon1'] } }, + { select: '#firstp #foobar', expect: { ids: [] } }, - { selector: '#台北Táiběi', expect: { ids: ['台北Táiběi'] } }, - { selector: '#台北Táiběi, #台北', expect: { ids: ['台北Táiběi', '台北'] } }, - { selector: 'div #台北', expect: { ids: ['台北'] } }, - { selector: 'form > #台北', expect: { ids: ['台北'] } }, + { select: '#台北Táiběi', expect: { ids: ['台北Táiběi'] } }, + { select: '#台北Táiběi, #台北', expect: { ids: ['台北Táiběi', '台北'] } }, + { select: 'div #台北', expect: { ids: ['台北'] } }, + { select: 'form > #台北', expect: { ids: ['台北'] } }, - { selector: '#foo\\:bar', expect: { ids: ['foo:bar'] } }, - { selector: '#test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, - { selector: 'div #foo\\:bar', expect: { ids: ['foo:bar'] } }, - { selector: 'div #test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, - { selector: 'form > #foo\\:bar', expect: { ids: ['foo:bar'] } }, - { selector: 'form > #test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { select: '#foo\\:bar', expect: { ids: ['foo:bar'] } }, + { select: '#test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { select: 'div #foo\\:bar', expect: { ids: ['foo:bar'] } }, + { select: 'div #test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { select: 'form > #foo\\:bar', expect: { ids: ['foo:bar'] } }, + { select: 'form > #test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, - { selector: '#form > #radio1', expect: { ids: ['radio1'] } }, // bug #267 - { selector: '#form #first', expect: { ids: [] } }, - { selector: '#form > #option1a', expect: { ids: [] } }, + { select: '#form > #radio1', expect: { ids: ['radio1'] } }, // bug #267 + { select: '#form #first', expect: { ids: [] } }, + { select: '#form > #option1a', expect: { ids: [] } }, - { selector: '#foo > *', expect: { ids: ['sndp', 'en', 'sap'] } }, - { selector: '#firstUL > *', expect: { ids: [] } }, + { select: '#foo > *', expect: { ids: ['sndp', 'en', 'sap'] } }, + { select: '#firstUL > *', expect: { ids: [] } }, - { selector: '#lengthtest', expect: { ids: ['lengthtest'] } }, - { selector: '#asdfasdf #foobar', expect: { ids: [] } }, // bug #986 + { select: '#lengthtest', expect: { ids: ['lengthtest'] } }, + { select: '#asdfasdf #foobar', expect: { ids: [] } }, // bug #986 - { selector: 'body div#form', expect: { ids: [] } }, + { select: 'body div#form', expect: { ids: [] } }, - { selector: '#types_all', expect: { ids: ['types_all'] } }, - { selector: '#fx-queue', expect: { ids: ['fx-queue'] } }, - { selector: '#name\\+value', expect: { ids: ['name+value'] } }, + { select: '#types_all', expect: { ids: ['types_all'] } }, + { select: '#fx-queue', expect: { ids: ['fx-queue'] } }, + { select: '#name\\+value', expect: { ids: ['name+value'] } }, ], }, { @@ -388,8 +388,8 @@ runScenarios('jquery', 'normal', [ }); }, cases: [ - { selector: '#tName1', expect: { ids: ['tName1'] } }, - { selector: '#tName2', expect: { ids: [] } }, + { select: '#tName1', expect: { ids: ['tName1'] } }, + { select: '#tName2', expect: { ids: [] } }, ], }, @@ -398,31 +398,31 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: '.blog', expect: { ids: ['mark', 'simon'] } }, - { selector: '.GROUPS', expect: { ids: ['groups'] } }, - { selector: '.blog.link', expect: { ids: ['simon'] } }, - { selector: 'a.blog', expect: { ids: ['mark', 'simon'] } }, - { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { select: '.blog', expect: { ids: ['mark', 'simon'] } }, + { select: '.GROUPS', expect: { ids: ['groups'] } }, + { select: '.blog.link', expect: { ids: ['simon'] } }, + { select: 'a.blog', expect: { ids: ['mark', 'simon'] } }, + { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, // Repeated as in the original test - { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - { selector: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - - { selector: '.台北Táiběi', expect: { ids: ['utf8class1'] } }, - { selector: '.台北', expect: { ids: ['utf8class1', 'utf8class2'] } }, - { selector: '.台北Táiběi.台北', expect: { ids: ['utf8class1'] } }, - { selector: '.台北Táiběi, .台北', expect: { ids: ['utf8class1', 'utf8class2'] } }, - { selector: 'div .台北Táiběi', expect: { ids: ['utf8class1'] } }, - { selector: 'form > .台北Táiběi', expect: { ids: ['utf8class1'] } }, - - { selector: '.foo\\:bar', expect: { ids: ['foo:bar'] } }, - { selector: '.test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, - { selector: 'div .foo\\:bar', expect: { ids: ['foo:bar'] } }, - { selector: 'div .test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, - { selector: 'form > .foo\\:bar', expect: { ids: ['foo:bar'] } }, - { selector: 'form > .test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, + + { select: '.台北Táiběi', expect: { ids: ['utf8class1'] } }, + { select: '.台北', expect: { ids: ['utf8class1', 'utf8class2'] } }, + { select: '.台北Táiběi.台北', expect: { ids: ['utf8class1'] } }, + { select: '.台北Táiběi, .台北', expect: { ids: ['utf8class1', 'utf8class2'] } }, + { select: 'div .台北Táiběi', expect: { ids: ['utf8class1'] } }, + { select: 'form > .台北Táiběi', expect: { ids: ['utf8class1'] } }, + + { select: '.foo\\:bar', expect: { ids: ['foo:bar'] } }, + { select: '.test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { select: 'div .foo\\:bar', expect: { ids: ['foo:bar'] } }, + { select: 'div .test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + { select: 'form > .foo\\:bar', expect: { ids: ['foo:bar'] } }, + { select: 'form > .test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, ], }, @@ -471,21 +471,21 @@ runScenarios('jquery', 'normal', [ }); }, cases: [ - { selector: 'input[name=action]', expect: { ids: ['text1'] } }, - { selector: "input[name='action']", expect: { ids: ['text1'] } }, - { selector: 'input[name="action"]', expect: { ids: ['text1'] } }, + { select: 'input[name=action]', expect: { ids: ['text1'] } }, + { select: "input[name='action']", expect: { ids: ['text1'] } }, + { select: 'input[name="action"]', expect: { ids: ['text1'] } }, - { selector: '[name=test]', expect: { ids: ['length', 'fx-queue'] } }, - { selector: '[name=div]', expect: { ids: ['fadein'] } }, - { selector: '*[name=iframe]', expect: { ids: ['iframe'] } }, + { select: '[name=test]', expect: { ids: ['length', 'fx-queue'] } }, + { select: '[name=div]', expect: { ids: ['fadein'] } }, + { select: '*[name=iframe]', expect: { ids: ['iframe'] } }, - { selector: "input[name='types[]']", expect: { ids: ['types_all', 'types_anime', 'types_movie'] } }, + { select: "input[name='types[]']", expect: { ids: ['types_all', 'types_anime', 'types_movie'] } }, - { selector: '#form input[name=action]', expect: { ids: ['text1'] } }, - { selector: "#form input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: '#form input[name=action]', expect: { ids: ['text1'] } }, + { select: "#form input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, - { selector: '[name=tName1]', expect: { ids: ['tName1ID'] } }, - { selector: '[name=tName2]', expect: { ids: ['tName2ID'] } }, + { select: '[name=tName1]', expect: { ids: ['tName1ID'] } }, + { select: '[name=tName2]', expect: { ids: ['tName2ID'] } }, ], }, @@ -494,10 +494,10 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: 'h2, p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'h2,p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'h2, p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'h2,p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, ], }, @@ -506,40 +506,40 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: 'p > a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, - { selector: 'p> a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, - { selector: 'p >a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, - { selector: 'p>a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { select: 'p > a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { select: 'p> a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { select: 'p >a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, + { select: 'p>a', expect: { ids: ['simon1', 'google', 'groups', 'mark', 'yahoo', 'simon'] } }, - { selector: 'p > a.blog', expect: { ids: ['mark', 'simon'] } }, - { selector: 'code > *', expect: { ids: ['anchor1', 'anchor2'] } }, - { selector: 'p > * > *', expect: { ids: ['anchor1', 'anchor2'] } }, + { select: 'p > a.blog', expect: { ids: ['mark', 'simon'] } }, + { select: 'code > *', expect: { ids: ['anchor1', 'anchor2'] } }, + { select: 'p > * > *', expect: { ids: ['anchor1', 'anchor2'] } }, - { selector: 'a + a', expect: { ids: ['groups'] } }, - { selector: 'a +a', expect: { ids: ['groups'] } }, - { selector: 'a+ a', expect: { ids: ['groups'] } }, - { selector: 'a+a', expect: { ids: ['groups'] } }, + { select: 'a + a', expect: { ids: ['groups'] } }, + { select: 'a +a', expect: { ids: ['groups'] } }, + { select: 'a+ a', expect: { ids: ['groups'] } }, + { select: 'a+a', expect: { ids: ['groups'] } }, - { selector: 'p + p', expect: { ids: ['ap', 'en', 'sap'] } }, - { selector: 'p#firstp + p', expect: { ids: ['ap'] } }, - { selector: 'p[lang=en] + p', expect: { ids: ['sap'] } }, - { selector: 'a.GROUPS + code + a', expect: { ids: ['mark'] } }, + { select: 'p + p', expect: { ids: ['ap', 'en', 'sap'] } }, + { select: 'p#firstp + p', expect: { ids: ['ap'] } }, + { select: 'p[lang=en] + p', expect: { ids: ['sap'] } }, + { select: 'a.GROUPS + code + a', expect: { ids: ['mark'] } }, - { selector: 'a + a, code > a', expect: { ids: ['groups', 'anchor1', 'anchor2'] } }, + { select: 'a + a, code > a', expect: { ids: ['groups', 'anchor1', 'anchor2'] } }, - { selector: 'div.blah > p > a', expect: { ids: [] } }, - { selector: 'div.foo > span > a', expect: { ids: [] } }, - { selector: '.container div:not(.excluded) div', expect: { ids: [] } }, + { select: 'div.blah > p > a', expect: { ids: [] } }, + { select: 'div.foo > span > a', expect: { ids: [] } }, + { select: '.container div:not(.excluded) div', expect: { ids: [] } }, - { selector: '* > :first-child', root: { kind: 'id', value: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, - { selector: '* > :nth-child(1)', root: { kind: 'id', value: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, - { selector: '* > *:first-child', root: { kind: 'id', value: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { select: '* > :first-child', scope: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { select: '* > :nth-child(1)', scope: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { select: '* > *:first-child', scope: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, - { selector: '.fototab > .thumbnails > a', expect: { ids: [] } }, + { select: '.fototab > .thumbnails > a', expect: { ids: [] } }, - { selector: 'p:first-child', expect: { ids: ['firstp', 'sndp'] } }, - { selector: 'p:nth-child(1)', expect: { ids: ['firstp', 'sndp'] } }, - { selector: 'p:not(:nth-child(1))', expect: { ids: ['ap', 'en', 'sap', 'first'] } }, + { select: 'p:first-child', expect: { ids: ['firstp', 'sndp'] } }, + { select: 'p:nth-child(1)', expect: { ids: ['firstp', 'sndp'] } }, + { select: 'p:not(:nth-child(1))', expect: { ids: ['ap', 'en', 'sap', 'first'] } }, ], }, @@ -566,7 +566,7 @@ runScenarios('jquery', 'normal', [ }); }, cases: [ - { selector: 'p:first-child', expect: { ids: [] } }, + { select: 'p:first-child', expect: { ids: [] } }, ], }, @@ -575,31 +575,31 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: 'p:last-child', expect: { ids: ['sap'] } }, - { selector: 'a:last-child', expect: { ids: ['simon1', 'anchor1', 'mark', 'yahoo', 'anchor2', 'simon', 'liveLink1', 'liveLink2'] } }, + { select: 'p:last-child', expect: { ids: ['sap'] } }, + { select: 'a:last-child', expect: { ids: ['simon1', 'anchor1', 'mark', 'yahoo', 'anchor2', 'simon', 'liveLink1', 'liveLink2'] } }, - { selector: '#main form#form > *:nth-child(2)', expect: { ids: ['text1'] } }, - { selector: '#main form#form > :nth-child(2)', expect: { ids: ['text1'] } }, + { select: '#main form#form > *:nth-child(2)', expect: { ids: ['text1'] } }, + { select: '#main form#form > :nth-child(2)', expect: { ids: ['text1'] } }, // changed `select:first` to `select:first-of-type`; `:first` is jQuery-only - { selector: '#form select:first-of-type option:nth-child(3)', expect: { ids: ['option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(0n+3)', expect: { ids: ['option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(1n+0)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(1n)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(n)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(even)', expect: { ids: ['option1b', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(odd)', expect: { ids: ['option1a', 'option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(2n)', expect: { ids: ['option1b', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(2n+1)', expect: { ids: ['option1a', 'option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(3n)', expect: { ids: ['option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(3n+1)', expect: { ids: ['option1a', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(3n+2)', expect: { ids: ['option1b'] } }, - { selector: '#form select:first-of-type option:nth-child(3n+3)', expect: { ids: ['option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(3n-1)', expect: { ids: ['option1b'] } }, - { selector: '#form select:first-of-type option:nth-child(3n-2)', expect: { ids: ['option1a', 'option1d'] } }, - { selector: '#form select:first-of-type option:nth-child(3n-3)', expect: { ids: ['option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(3n+0)', expect: { ids: ['option1c'] } }, - { selector: '#form select:first-of-type option:nth-child(-n+3)', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, + { select: '#form select:first-of-type option:nth-child(3)', expect: { ids: ['option1c'] } }, + { select: '#form select:first-of-type option:nth-child(0n+3)', expect: { ids: ['option1c'] } }, + { select: '#form select:first-of-type option:nth-child(1n+0)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(1n)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(n)', expect: { ids: ['option1a', 'option1b', 'option1c', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(even)', expect: { ids: ['option1b', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(odd)', expect: { ids: ['option1a', 'option1c'] } }, + { select: '#form select:first-of-type option:nth-child(2n)', expect: { ids: ['option1b', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(2n+1)', expect: { ids: ['option1a', 'option1c'] } }, + { select: '#form select:first-of-type option:nth-child(3n)', expect: { ids: ['option1c'] } }, + { select: '#form select:first-of-type option:nth-child(3n+1)', expect: { ids: ['option1a', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(3n+2)', expect: { ids: ['option1b'] } }, + { select: '#form select:first-of-type option:nth-child(3n+3)', expect: { ids: ['option1c'] } }, + { select: '#form select:first-of-type option:nth-child(3n-1)', expect: { ids: ['option1b'] } }, + { select: '#form select:first-of-type option:nth-child(3n-2)', expect: { ids: ['option1a', 'option1d'] } }, + { select: '#form select:first-of-type option:nth-child(3n-3)', expect: { ids: ['option1c'] } }, + { select: '#form select:first-of-type option:nth-child(3n+0)', expect: { ids: ['option1c'] } }, + { select: '#form select:first-of-type option:nth-child(-n+3)', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, ], }, @@ -618,59 +618,59 @@ runScenarios('jquery', 'normal', [ }); }, cases: [ - { selector: 'a[title]', expect: { ids: ['google'] } }, - { selector: '*[title]', expect: { ids: ['google'] } }, - { selector: '[title]', expect: { ids: ['google'] } }, - { selector: 'a[ title ]', expect: { ids: ['google'] } }, + { select: 'a[title]', expect: { ids: ['google'] } }, + { select: '*[title]', expect: { ids: ['google'] } }, + { select: '[title]', expect: { ids: ['google'] } }, + { select: 'a[ title ]', expect: { ids: ['google'] } }, - { selector: "a[rel='bookmark']", expect: { ids: ['simon1'] } }, - { selector: 'a[rel="bookmark"]', expect: { ids: ['simon1'] } }, - { selector: 'a[rel=bookmark]', expect: { ids: ['simon1'] } }, - { selector: "a[href='http://www.google.com/']", expect: { ids: ['google'] } }, - { selector: "a[ rel = 'bookmark' ]", expect: { ids: ['simon1'] } }, + { select: "a[rel='bookmark']", expect: { ids: ['simon1'] } }, + { select: 'a[rel="bookmark"]', expect: { ids: ['simon1'] } }, + { select: 'a[rel=bookmark]', expect: { ids: ['simon1'] } }, + { select: "a[href='http://www.google.com/']", expect: { ids: ['google'] } }, + { select: "a[ rel = 'bookmark' ]", expect: { ids: ['simon1'] } }, - { selector: "p a[href^='#']", expect: { ids: ['anchor2'] } }, - { selector: 'p a[href*="#"]', expect: { ids: ['simon1', 'anchor2'] } }, + { select: "p a[href^='#']", expect: { ids: ['anchor2'] } }, + { select: 'p a[href*="#"]', expect: { ids: ['simon1', 'anchor2'] } }, - { selector: 'form label[for]', expect: { ids: ['label-for'] } }, - { selector: '#form [for=action]', expect: { ids: ['label-for'] } }, + { select: 'form label[for]', expect: { ids: ['label-for'] } }, + { select: '#form [for=action]', expect: { ids: ['label-for'] } }, // Disabled tests - expandos don't work in all browsers // { selector: 'form input[test]', expect: { ids: ['text1', 'text2'] } }, // { selector: 'form input[test=0]', expect: { ids: ['text1'] } }, // { selector: 'form input[test=1]', expect: { ids: ['text2'] } }, - { selector: "input[name^='foo[']", expect: { ids: ['hidden2'] } }, - { selector: "input[name^='foo[bar]']", expect: { ids: ['hidden2'] } }, - { selector: "input[name*='[bar]']", expect: { ids: ['hidden2'] } }, - { selector: "input[name$='bar]']", expect: { ids: ['hidden2'] } }, - { selector: "input[name$='[bar]']", expect: { ids: ['hidden2'] } }, - { selector: "input[name$='foo[bar]']", expect: { ids: ['hidden2'] } }, - { selector: "input[name*='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name^='foo[']", expect: { ids: ['hidden2'] } }, + { select: "input[name^='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name*='[bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name$='bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name$='[bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name$='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name*='foo[bar]']", expect: { ids: ['hidden2'] } }, - { selector: "#form input[type='radio'], #form input[type='hidden']", expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, - { selector: '#form input[type=\'radio\'], #form input[type="hidden"]', expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, - { selector: "#form input[type='radio'], #form input[type=hidden]", expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, + { select: "#form input[type='radio'], #form input[type='hidden']", expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, + { select: '#form input[type=\'radio\'], #form input[type="hidden"]', expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, + { select: "#form input[type='radio'], #form input[type=hidden]", expect: { ids: ['radio1', 'radio2', 'hidden1'] } }, - { selector: 'span[lang=中文]', expect: { ids: ['台北'] } }, + { select: 'span[lang=中文]', expect: { ids: ['台北'] } }, - { selector: "a[href ^= 'http://www']", expect: { ids: ['google', 'yahoo'] } }, - { selector: "a[href $= 'org/']", expect: { ids: ['mark'] } }, - { selector: "a[href *= 'google']", expect: { ids: ['google', 'groups'] } }, - { selector: "#ap a:not([hreflang='en'])", expect: { ids: ['google', 'groups', 'anchor1'] } }, + { select: "a[href ^= 'http://www']", expect: { ids: ['google', 'yahoo'] } }, + { select: "a[href $= 'org/']", expect: { ids: ['mark'] } }, + { select: "a[href *= 'google']", expect: { ids: ['google', 'groups'] } }, + { select: "#ap a:not([hreflang='en'])", expect: { ids: ['google', 'groups', 'anchor1'] } }, - { selector: "#select1 option[value='']", expect: { ids: ['option1a'] } }, - { selector: "#select1 option:not([value=''])", expect: { ids: ['option1b', 'option1c', 'option1d'] } }, + { select: "#select1 option[value='']", expect: { ids: ['option1a'] } }, + { select: "#select1 option:not([value=''])", expect: { ids: ['option1b', 'option1c', 'option1d'] } }, - { selector: '#select1 option:checked', expect: { ids: ['option1a'] } }, - { selector: '#select2 option:checked', expect: { ids: ['option2d'] } }, - { selector: '#select3 option:checked', expect: { ids: ['option3b', 'option3c'] } }, + { select: '#select1 option:checked', expect: { ids: ['option1a'] } }, + { select: '#select2 option:checked', expect: { ids: ['option2d'] } }, + { select: '#select3 option:checked', expect: { ids: ['option3b', 'option3c'] } }, - { selector: "input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: "input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, - { selector: '#form select:not([multiple])', expect: { ids: ['select1', 'select2'] } }, - { selector: '#form select:not([name=select1])', expect: { ids: ['select2', 'select3'] } }, - { selector: "#form select:not([name='select1'])", expect: { ids: ['select2', 'select3'] } }, + { select: '#form select:not([multiple])', expect: { ids: ['select1', 'select2'] } }, + { select: '#form select:not([name=select1])', expect: { ids: ['select2', 'select3'] } }, + { select: "#form select:not([name='select1'])", expect: { ids: ['select2', 'select3'] } }, ], }, @@ -679,16 +679,16 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: 'p:first-child', expect: { ids: ['firstp', 'sndp'] } }, - { selector: 'p:last-child', expect: { ids: ['sap'] } }, - { selector: 'a:only-child', expect: { ids: ['simon1', 'anchor1', 'yahoo', 'anchor2', 'liveLink1', 'liveLink2'] } }, - { selector: 'ul:empty', expect: { ids: ['firstUL'] } }, + { select: 'p:first-child', expect: { ids: ['firstp', 'sndp'] } }, + { select: 'p:last-child', expect: { ids: ['sap'] } }, + { select: 'a:only-child', expect: { ids: ['simon1', 'anchor1', 'yahoo', 'anchor2', 'liveLink1', 'liveLink2'] } }, + { select: 'ul:empty', expect: { ids: ['firstUL'] } }, - { selector: '#form input:not([type=hidden]):enabled', expect: { ids: ['text1', 'radio1', 'radio2', 'check1', 'check2', 'hidden2', 'name', 'search'] } }, - { selector: '#form input:disabled', expect: { ids: ['text2'] } }, - { selector: '#form input:checked', expect: { ids: ['radio2', 'check1'] } }, + { select: '#form input:not([type=hidden]):enabled', expect: { ids: ['text1', 'radio1', 'radio2', 'check1', 'check2', 'hidden2', 'name', 'search'] } }, + { select: '#form input:disabled', expect: { ids: ['text2'] } }, + { select: '#form input:checked', expect: { ids: ['radio2', 'check1'] } }, - { selector: '#form option:checked', expect: { ids: ['option1a', 'option2d', 'option3b', 'option3c'] } }, + { select: '#form option:checked', expect: { ids: ['option1a', 'option2d', 'option3b', 'option3c'] } }, // jQuery-only text pseudos; not valid in the native-parity harness // { selector: "a:contains('Google')", expect: { ids: ['google', 'groups'] } }, @@ -696,33 +696,33 @@ runScenarios('jquery', 'normal', [ // { selector: "a:contains('Google Groups (Link)')", expect: { ids: ['groups'] } }, // { selector: "a:contains('(Link)')", expect: { ids: ['groups'] } }, - { selector: 'p ~ div', expect: { ids: ['foo', 'moretests', 'tabindex-tests', 'liveHandlerOrder'] } }, - { selector: 'a.blog:not(.link)', expect: { ids: ['mark'] } }, + { select: 'p ~ div', expect: { ids: ['foo', 'moretests', 'tabindex-tests', 'liveHandlerOrder'] } }, + { select: 'a.blog:not(.link)', expect: { ids: ['mark'] } }, // { selector: "#form option:not(:contains('Nothing'),#option1b,:checked)", expect: { ids: ['option1c', 'option1d', 'option2b', 'option2c', 'option3d', 'option3e'] } }, - { selector: "#form option:not([id^='opt']:nth-child(-n+3))", expect: { ids: ['option1d', 'option2d', 'option3d', 'option3e'] } }, - { selector: "#form option:not(:not(:checked))[id^='option3']", expect: { ids: ['option3b', 'option3c'] } }, - { selector: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: "#form option:not([id^='opt']:nth-child(-n+3))", expect: { ids: ['option1d', 'option2d', 'option3d', 'option3e'] } }, + { select: "#form option:not(:not(:checked))[id^='option3']", expect: { ids: ['option3b', 'option3c'] } }, + { select: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, // invalid compound selector inside :not() pseudo-class - { selector: 'p:not(div.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(p.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(div.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(p.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, // invalid compound selector inside :not() pseudo-class - { selector: 'p:not(div#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(p#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - - { selector: 'p:not(a)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(a, b)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(a, b, div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - { selector: 'p:not(p)', expect: { ids: [] } }, - { selector: 'p:not(a,p)', expect: { ids: [] } }, - { selector: 'p:not(p,a)', expect: { ids: [] } }, - { selector: 'p:not(a,p,b)', expect: { ids: [] } }, + { select: 'p:not(div#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(p#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + { select: 'p:not(a)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(a, b)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(a, b, div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(p)', expect: { ids: [] } }, + { select: 'p:not(a,p)', expect: { ids: [] } }, + { select: 'p:not(p,a)', expect: { ids: [] } }, + { select: 'p:not(a,p,b)', expect: { ids: [] } }, // { selector: ':input:not(:image,:input,:submit)', expect: { ids: [] } }, // jQuery-only positional pseudos; keep parked for now @@ -747,7 +747,7 @@ runScenarios('jquery', 'normal', [ // Preserved from the original suite but not active in the native-parity harness, { name: 'jquery visibility and position pseudos (legacy)', - modifier: 'skip', + status: 'skip', html: html, htmlMode: 'document', setupPage: async (page) => { @@ -784,42 +784,42 @@ runScenarios('jquery', 'normal', [ }); }, cases: [ - { selector: 'div#nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, - { selector: 'div#nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, - { selector: 'div#nothiddendiv:not(:gt(0))', expect: { ids: ['nothiddendiv'] } }, - { selector: '#foo > :not(:first)', expect: { ids: ['en', 'sap'] } }, - { selector: 'select > :not(:gt(2))', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, - { selector: 'select:lt(2) :not(:first)', expect: { ids: ['option1b', 'option1c', 'option1d', 'option2a', 'option2b', 'option2c', 'option2d'] } }, - { selector: 'div.nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, - { selector: 'div.nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, - { selector: 'div.nothiddendiv:not(:lt(0))', expect: { ids: ['nothiddendiv'] } }, - - { selector: 'div div:eq(0)', expect: { ids: ['nothiddendivchild'] } }, - { selector: 'div div:eq(5)', expect: { ids: ['t2037'] } }, - { selector: 'div div:eq(27)', expect: { ids: ['hide'] } }, - { selector: 'div div:first', expect: { ids: ['nothiddendivchild'] } }, - { selector: 'div > div:first', expect: { ids: ['nothiddendivchild'] } }, - { selector: '#dl div:first div:first', expect: { ids: ['foo'] } }, - { selector: '#dl div:first > div:first', expect: { ids: ['foo'] } }, - { selector: 'div#nothiddendiv:first > div:first', expect: { ids: ['nothiddendivchild'] } }, + { select: 'div#nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, + { select: 'div#nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, + { select: 'div#nothiddendiv:not(:gt(0))', expect: { ids: ['nothiddendiv'] } }, + { select: '#foo > :not(:first)', expect: { ids: ['en', 'sap'] } }, + { select: 'select > :not(:gt(2))', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, + { select: 'select:lt(2) :not(:first)', expect: { ids: ['option1b', 'option1c', 'option1d', 'option2a', 'option2b', 'option2c', 'option2d'] } }, + { select: 'div.nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, + { select: 'div.nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, + { select: 'div.nothiddendiv:not(:lt(0))', expect: { ids: ['nothiddendiv'] } }, + + { select: 'div div:eq(0)', expect: { ids: ['nothiddendivchild'] } }, + { select: 'div div:eq(5)', expect: { ids: ['t2037'] } }, + { select: 'div div:eq(27)', expect: { ids: ['hide'] } }, + { select: 'div div:first', expect: { ids: ['nothiddendivchild'] } }, + { select: 'div > div:first', expect: { ids: ['nothiddendivchild'] } }, + { select: '#dl div:first div:first', expect: { ids: ['foo'] } }, + { select: '#dl div:first > div:first', expect: { ids: ['foo'] } }, + { select: 'div#nothiddendiv:first > div:first', expect: { ids: ['nothiddendivchild'] } }, ], }, { name: 'form and header pseudo selectors', - modifier: 'skip', // form pseudo selectors are jQuery-only; + status: 'skip', // form pseudo selectors are jQuery-only; html: html, htmlMode: 'document', cases: [ - { selector: '#form :input', expect: { ids: ['text1', 'text2', 'radio1', 'radio2', 'check1', 'check2', 'hidden1', 'hidden2', 'name', 'search', 'button', 'area1', 'select1', 'select2', 'select3'] } }, - { selector: '#form :radio', expect: { ids: ['radio1', 'radio2'] } }, - { selector: '#form :checkbox', expect: { ids: ['check1', 'check2'] } }, - { selector: '#form :text:not(#search)', expect: { ids: ['text1', 'text2', 'hidden2', 'name'] } }, - { selector: '#form :radio:checked', expect: { ids: ['radio2'] } }, - { selector: '#form :checkbox:checked', expect: { ids: ['check1'] } }, - { selector: '#form :radio:checked, #form :checkbox:checked', expect: { ids: ['radio2', 'check1'] } }, - - { selector: ':header', expect: { ids: ['header', 'banner', 'userAgent'] } }, + { select: '#form :input', expect: { ids: ['text1', 'text2', 'radio1', 'radio2', 'check1', 'check2', 'hidden1', 'hidden2', 'name', 'search', 'button', 'area1', 'select1', 'select2', 'select3'] } }, + { select: '#form :radio', expect: { ids: ['radio1', 'radio2'] } }, + { select: '#form :checkbox', expect: { ids: ['check1', 'check2'] } }, + { select: '#form :text:not(#search)', expect: { ids: ['text1', 'text2', 'hidden2', 'name'] } }, + { select: '#form :radio:checked', expect: { ids: ['radio2'] } }, + { select: '#form :checkbox:checked', expect: { ids: ['check1'] } }, + { select: '#form :radio:checked, #form :checkbox:checked', expect: { ids: ['radio2', 'check1'] } }, + + { select: ':header', expect: { ids: ['header', 'banner', 'userAgent'] } }, ], }, @@ -828,7 +828,7 @@ runScenarios('jquery', 'normal', [ html: html, htmlMode: 'document', cases: [ - { selector: 'p:has(a)', expect: { ids: ['firstp', 'ap', 'en', 'sap'] } }, + { select: 'p:has(a)', expect: { ids: ['firstp', 'ap', 'en', 'sap'] } }, ], }, diff --git a/test_new/browser/jsvm.test.ts b/test_new/browser/jsvm.test.ts index 671a71e..d97bb8f 100644 --- a/test_new/browser/jsvm.test.ts +++ b/test_new/browser/jsvm.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('jsvm', 'normal', [ { @@ -20,8 +20,8 @@ runScenarios('jsvm', 'normal', [
    `, cases: [ - { selector: '#test1 div:not(:empty)', expect: { count: 4 } }, - { selector: '#test1 div:nth-child(even):empty', expect: { count: 2 } }, + { select: '#test1 div:not(:empty)', expect: { count: 4 } }, + { select: '#test1 div:nth-child(even):empty', expect: { count: 2 } }, ], }, ]); \ No newline at end of file diff --git a/test_new/browser/prototype.test.ts b/test_new/browser/prototype.test.ts index 2ef074e..2c620db 100644 --- a/test_new/browser/prototype.test.ts +++ b/test_new/browser/prototype.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; import { expect } from "@playwright/test"; runScenarios('prototype 1', 'normal', [ @@ -10,13 +10,13 @@ runScenarios('prototype 1', 'normal', [ `, cases: [ - { selector: '.test_class', expect: { count: 2, ids: ['test_div_parent', 'test_div_child'] } }, + { select: '.test_class', expect: { count: 2, ids: ['test_div_parent', 'test_div_child'] } }, { - selector: '.test_class', - root: { kind: 'id', value: 'test_div_parent' }, + select: '.test_class', + scope: { by: 'id', id: 'test_div_parent' }, expect: { count: 1, ids: ['test_div_child'] } }, - { selector: '.non_existent', expect: { count: 0, ids: [] } }, + { select: '.non_existent', expect: { count: 0, ids: [] } }, ], }, ]); @@ -146,282 +146,282 @@ runScenarios('prototype 2', 'normal', [ htmlMode: 'document', cases: [ // testSelectorWithTagName - { selector: 'li' }, - { selector: 'strong', expect: { ids: ['strong'] } }, - { selector: 'nonexistent', expect: { count: 0, ids: [] } }, - { selector: '*' }, + { select: 'li' }, + { select: 'strong', expect: { ids: ['strong'] } }, + { select: 'nonexistent', expect: { count: 0, ids: [] } }, + { select: '*' }, // testSelectorWithId - { selector: '#fixtures', expect: { ids: ['fixtures'] } }, - { selector: '#nonexistent', expect: { count: 0, ids: [] } }, - { selector: '#troubleForm', expect: { ids: ['troubleForm'] } }, + { select: '#fixtures', expect: { ids: ['fixtures'] } }, + { select: '#nonexistent', expect: { count: 0, ids: [] } }, + { select: '#troubleForm', expect: { ids: ['troubleForm'] } }, // testSelectorWithClassName - { selector: '.first', expect: { ids: ['p', 'link_1', 'item_1'] } }, - { selector: '.second', expect: { count: 0, ids: [] } }, + { select: '.first', expect: { ids: ['p', 'link_1', 'item_1'] } }, + { select: '.second', expect: { count: 0, ids: [] } }, // testSelectorWithTagNameAndId - { selector: 'strong#strong', expect: { ids: ['strong'] } }, - { selector: 'p#strong', expect: { count: 0, ids: [] } }, + { select: 'strong#strong', expect: { ids: ['strong'] } }, + { select: 'p#strong', expect: { count: 0, ids: [] } }, // testSelectorWithTagNameAndClassName - { selector: 'a.internal', expect: { ids: ['link_1', 'link_2'] } }, - { selector: 'a.internal.highlight', expect: { ids: ['link_2'] } }, - { selector: 'a.highlight.internal', expect: { ids: ['link_2'] } }, - { selector: 'a.highlight.internal.nonexistent', expect: { count: 0, ids: [] } }, + { select: 'a.internal', expect: { ids: ['link_1', 'link_2'] } }, + { select: 'a.internal.highlight', expect: { ids: ['link_2'] } }, + { select: 'a.highlight.internal', expect: { ids: ['link_2'] } }, + { select: 'a.highlight.internal.nonexistent', expect: { count: 0, ids: [] } }, // testSelectorWithIdAndClassName - { selector: '#link_2.internal', expect: { ids: ['link_2'] } }, - { selector: '.internal#link_2', expect: { ids: ['link_2'] } }, - { selector: '#link_2.internal.highlight', expect: { ids: ['link_2'] } }, - { selector: '#link_2.internal.nonexistent', expect: { count: 0, ids: [] } }, + { select: '#link_2.internal', expect: { ids: ['link_2'] } }, + { select: '.internal#link_2', expect: { ids: ['link_2'] } }, + { select: '#link_2.internal.highlight', expect: { ids: ['link_2'] } }, + { select: '#link_2.internal.nonexistent', expect: { count: 0, ids: [] } }, // testSelectorWithTagNameAndIdAndClassName - { selector: 'a#link_2.internal', expect: { ids: ['link_2'] } }, - { selector: 'a.internal#link_2', expect: { ids: ['link_2'] } }, - { selector: 'li#item_1.first', expect: { ids: ['item_1'] } }, - { selector: 'li#item_1.nonexistent', expect: { count: 0, ids: [] } }, - { selector: 'li#item_1.first.nonexistent', expect: { count: 0, ids: [] } }, + { select: 'a#link_2.internal', expect: { ids: ['link_2'] } }, + { select: 'a.internal#link_2', expect: { ids: ['link_2'] } }, + { select: 'li#item_1.first', expect: { ids: ['item_1'] } }, + { select: 'li#item_1.nonexistent', expect: { count: 0, ids: [] } }, + { select: 'li#item_1.first.nonexistent', expect: { count: 0, ids: [] } }, // test$$MatchesAncestryWithTokensSeparatedByWhitespace - { selector: '#fixtures a *', expect: { ids: ['em2', 'em', 'span'] } }, - { selector: 'div#fixtures p', expect: { ids: ['p'] } }, + { select: '#fixtures a *', expect: { ids: ['em2', 'em', 'span'] } }, + { select: 'div#fixtures p', expect: { ids: ['p'] } }, // test$$CombinesResultsWhenMultipleExpressionsArePassed - { selector: '#p a, ul#list li', expect: { ids: ['link_1', 'link_2', 'item_1', 'item_2', 'item_3'] } }, + { select: '#p a, ul#list li', expect: { ids: ['link_1', 'link_2', 'item_1', 'item_2', 'item_3'] } }, // testSelectorWithTagNameAndAttributeExistence - { selector: 'h1[class]', expect: { equivalentTo: { selector: '#fixtures h1' } } }, - { selector: 'h1[CLASS]', expect: { equivalentTo: { selector: '#fixtures h1' } } }, - { selector: 'li#item_3[class]', expect: { ids: ['item_3'] } }, + { select: 'h1[class]', expect: { equivalentTo: { search: '#fixtures h1' } } }, + { select: 'h1[CLASS]', expect: { equivalentTo: { search: '#fixtures h1' } } }, + { select: 'li#item_3[class]', expect: { ids: ['item_3'] } }, // testSelectorWithTagNameAndSpecificAttributeValue - { selector: 'a[href="#"]', expect: { ids: ['link_1', 'link_2', 'link_3'] } }, - { selector: "a[href='#']", expect: { ids: ['link_1', 'link_2', 'link_3'] } }, + { select: 'a[href="#"]', expect: { ids: ['link_1', 'link_2', 'link_3'] } }, + { select: "a[href='#']", expect: { ids: ['link_1', 'link_2', 'link_3'] } }, // testSelectorWithTagNameAndWhitespaceTokenizedAttributeValue - { selector: 'a[class~="internal"]', expect: { ids: ['link_1', 'link_2'] } }, - { selector: 'a[class~=internal]', expect: { ids: ['link_1', 'link_2'] } }, + { select: 'a[class~="internal"]', expect: { ids: ['link_1', 'link_2'] } }, + { select: 'a[class~=internal]', expect: { ids: ['link_1', 'link_2'] } }, // testSelectorWithAttributeAndNoTagName - { selector: '[href]', root: { kind: 'selector', value: 'body' }, expect: { equivalentTo: { selector: 'a[href]' } } }, - { selector: '[class~=internal]', expect: { equivalentTo: { selector: 'a[class~="internal"]' } } }, - { selector: '[id]', expect: { equivalentTo: { selector: '*[id]' } } }, - { selector: '[type=radio]', expect: { ids: ['checked_radio', 'unchecked_radio'] } }, - { selector: '[type=checkbox]', expect: { equivalentTo: { selector: '*[type=checkbox]' } } }, - { selector: '[title]', expect: { ids: ['with_title', 'commaParent'] } }, - { selector: '#troubleForm [type=radio]', expect: { equivalentTo: { selector: '#troubleForm *[type=radio]' } } }, - { selector: '#troubleForm [type]', expect: { equivalentTo: { selector: '#troubleForm *[type]' } } }, + { select: '[href]', scope: { by: 'first', selector: 'body' }, expect: { equivalentTo: { search: 'a[href]' } } }, + { select: '[class~=internal]', expect: { equivalentTo: { search: 'a[class~="internal"]' } } }, + { select: '[id]', expect: { equivalentTo: { search: '*[id]' } } }, + { select: '[type=radio]', expect: { ids: ['checked_radio', 'unchecked_radio'] } }, + { select: '[type=checkbox]', expect: { equivalentTo: { search: '*[type=checkbox]' } } }, + { select: '[title]', expect: { ids: ['with_title', 'commaParent'] } }, + { select: '#troubleForm [type=radio]', expect: { equivalentTo: { search: '#troubleForm *[type=radio]' } } }, + { select: '#troubleForm [type]', expect: { equivalentTo: { search: '#troubleForm *[type]' } } }, // testSelectorWithAttributeContainingDash - { selector: '[foo-bar]', expect: { ids: ['attr_with_dash'] } }, // attribute with hyphen + { select: '[foo-bar]', expect: { ids: ['attr_with_dash'] } }, // attribute with hyphen // testSelectorWithUniversalAndHyphenTokenizedAttributeValue - { selector: '*[xml:lang|="es"]', expect: { ids: ['item_3'] } }, - { selector: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] } }, + { select: '*[xml:lang|="es"]', expect: { ids: ['item_3'] } }, + { select: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] } }, // testSelectorWithTagNameAndNegatedAttributeValue - { selector: 'a:not([href="#"])', expect: { count: 0, ids: [] } }, + { select: 'a:not([href="#"])', expect: { count: 0, ids: [] } }, // testSelectorWithBracketAttributeValue - { selector: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, - { selector: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, - { selector: '#troubleForm2 input[name="brackets[5][]"][value="2"]', expect: { ids: ['chk_2'] } }, - { selector: '#troubleForm2 input[name=brackets\\[5\\]\\[\\]]', expect: { equivalentTo: { selector: '#troubleForm2 input[name="brackets[5][]"]' }, count: 2 } }, + { select: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, + { select: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, + { select: '#troubleForm2 input[name="brackets[5][]"][value="2"]', expect: { ids: ['chk_2'] } }, + { select: '#troubleForm2 input[name=brackets\\[5\\]\\[\\]]', expect: { equivalentTo: { search: '#troubleForm2 input[name="brackets[5][]"]' }, count: 2 } }, // test$$WithNestedAttributeSelectors - { selector: 'div[style] p[id] strong', expect: { ids: ['strong'] } }, + { select: 'div[style] p[id] strong', expect: { ids: ['strong'] } }, // testSelectorWithMultipleConditions - { selector: 'a[class~=external][href="#"]', expect: { ids: ['link_3'] } }, - { selector: 'a[class~=external]:not([href="#"])', expect: { count: 0, ids: [] } }, + { select: 'a[class~=external][href="#"]', expect: { ids: ['link_3'] } }, + { select: 'a[class~=external]:not([href="#"])', expect: { count: 0, ids: [] } }, // derived from testSelectorMatchElements - { selector: '#list li', expect: { ids: ['item_1', 'item_2', 'item_3'] } }, - { selector: '#fixtures a.internal', expect: { ids: ['link_1', 'link_2'] } }, - { selector: '#fixtures p.last', expect: { count: 0, ids: [] } }, - { selector: '#fixtures .inexistant, #fixtures a.internal', expect: { ids: ['link_1', 'link_2'] } }, + { select: '#list li', expect: { ids: ['item_1', 'item_2', 'item_3'] } }, + { select: '#fixtures a.internal', expect: { ids: ['link_1', 'link_2'] } }, + { select: '#fixtures p.last', expect: { count: 0, ids: [] } }, + { select: '#fixtures .inexistant, #fixtures a.internal', expect: { ids: ['link_1', 'link_2'] } }, // derived from testSelectorFindElement - { selector: '#list li', expect: { ids: ['item_1', 'item_2', 'item_3'] } }, - { selector: '#list li#item_3', expect: { ids: ['item_3'] } }, - { selector: '#list em', expect: { count: 0, ids: [] } }, + { select: '#list li', expect: { ids: ['item_1', 'item_2', 'item_3'] } }, + { select: '#list li#item_3', expect: { ids: ['item_3'] } }, + { select: '#list em', expect: { count: 0, ids: [] } }, // derived from testElementMatch - { selector: 'span', expect: { includesIds: ['dupL1'] } }, - { selector: 'span#dupL1', expect: { includesIds: ['dupL1'] } }, - { selector: 'div > span', expect: { includesIds: ['dupL1'] } }, // child combinator - { selector: '#dupContainer span', expect: { includesIds: ['dupL1'] } }, // descendant combinator - { selector: '#dupL1', expect: { includesIds: ['dupL1'] } }, // ID only - { selector: 'span.span_foo', expect: { includesIds: ['dupL1'] } }, // class name 1 - { selector: 'span.span_bar', expect: { includesIds: ['dupL1'] } }, // class name 2 - { selector: 'span:first-child', expect: { includesIds: ['dupL1'] } }, // first-child pseudoclass - - { selector: 'span.span_wtf', expect: { excludesIds: ['dupL1'] } }, // bogus class name - { selector: '#dupL2', expect: { excludesIds: ['dupL1'] } }, // different ID - { selector: 'div', expect: { excludesIds: ['dupL1'] } }, // different tag name - { selector: 'span span', expect: { excludesIds: ['dupL1'] } }, // different ancestry - { selector: 'span > span', expect: { excludesIds: ['dupL1'] } }, // different parent - { selector: 'span:nth-child(5)', expect: { excludesIds: ['dupL1'] } }, // different pseudoclass - - { selector: 'a[rel^=external]', expect: { includesIds: ['link_1'], excludesIds: ['link_2'] } }, - { selector: 'a[rel^="external"]', expect: { includesIds: ['link_1'] } }, - { selector: "a[rel^='external']", expect: { includesIds: ['link_1'] } }, + { select: 'span', expect: { includesIds: ['dupL1'] } }, + { select: 'span#dupL1', expect: { includesIds: ['dupL1'] } }, + { select: 'div > span', expect: { includesIds: ['dupL1'] } }, // child combinator + { select: '#dupContainer span', expect: { includesIds: ['dupL1'] } }, // descendant combinator + { select: '#dupL1', expect: { includesIds: ['dupL1'] } }, // ID only + { select: 'span.span_foo', expect: { includesIds: ['dupL1'] } }, // class name 1 + { select: 'span.span_bar', expect: { includesIds: ['dupL1'] } }, // class name 2 + { select: 'span:first-child', expect: { includesIds: ['dupL1'] } }, // first-child pseudoclass + + { select: 'span.span_wtf', expect: { excludesIds: ['dupL1'] } }, // bogus class name + { select: '#dupL2', expect: { excludesIds: ['dupL1'] } }, // different ID + { select: 'div', expect: { excludesIds: ['dupL1'] } }, // different tag name + { select: 'span span', expect: { excludesIds: ['dupL1'] } }, // different ancestry + { select: 'span > span', expect: { excludesIds: ['dupL1'] } }, // different parent + { select: 'span:nth-child(5)', expect: { excludesIds: ['dupL1'] } }, // different pseudoclass + + { select: 'a[rel^=external]', expect: { includesIds: ['link_1'], excludesIds: ['link_2'] } }, + { select: 'a[rel^="external"]', expect: { includesIds: ['link_1'] } }, + { select: "a[rel^='external']", expect: { includesIds: ['link_1'] } }, // testSelectorWithSpaceInAttributeValue - { selector: 'cite[title="hello world!"]', expect: { ids: ['with_title'] } }, + { select: 'cite[title="hello world!"]', expect: { ids: ['with_title'] } }, // testSelectorWithNamespacedAttributes { - selector: '[xml:lang]', + select: '[xml:lang]', expect: { allowMismatch: true, // some browsers don't support selecting by namespaced attributes count: 2, includesIds: ['item_3'], - equivalentTo: { selector: '*[xml:lang]' } + equivalentTo: { search: '*[xml:lang]' } } }, // testSelectorWithChild - { selector: 'p.first > a', expect: { ids: ['link_1', 'link_2'] } }, - { selector: 'div#grandfather > div', expect: { ids: ['father', 'uncle'] } }, - { selector: '#level1>span', expect: { ids: ['level2_1', 'level2_2'] } }, - { selector: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, - { selector: '#level2_1 > *', expect: { ids: ['level3_1', 'level3_2'] } }, - { selector: 'div > #nonexistent', expect: { count: 0, ids: [] } }, - { selector: '#level1 > span' }, + { select: 'p.first > a', expect: { ids: ['link_1', 'link_2'] } }, + { select: 'div#grandfather > div', expect: { ids: ['father', 'uncle'] } }, + { select: '#level1>span', expect: { ids: ['level2_1', 'level2_2'] } }, + { select: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, + { select: '#level2_1 > *', expect: { ids: ['level3_1', 'level3_2'] } }, + { select: 'div > #nonexistent', expect: { count: 0, ids: [] } }, + { select: '#level1 > span' }, // testSelectorWithAdjacence - { selector: 'div.brothers + div.brothers', expect: { ids: ['uncle'] } }, - { selector: 'div.brothers + div', expect: { ids: ['uncle'] } }, - { selector: '#level2_1+span', expect: { ids: ['level2_2'] } }, - { selector: '#level2_1 + span', expect: { ids: ['level2_2'] } }, - { selector: '#level2_1 + *', expect: { ids: ['level2_2'] } }, - { selector: '#level2_2 + span', expect: { count: 0, ids: [] } }, - { selector: '#level3_1 + span', expect: { ids: ['level3_2'] } }, - { selector: '#level3_1 + *', expect: { ids: ['level3_2'] } }, - { selector: '#level3_2 + *', expect: { count: 0, ids: [] } }, - { selector: '#level3_1 + em', expect: { count: 0, ids: [] } }, + { select: 'div.brothers + div.brothers', expect: { ids: ['uncle'] } }, + { select: 'div.brothers + div', expect: { ids: ['uncle'] } }, + { select: '#level2_1+span', expect: { ids: ['level2_2'] } }, + { select: '#level2_1 + span', expect: { ids: ['level2_2'] } }, + { select: '#level2_1 + *', expect: { ids: ['level2_2'] } }, + { select: '#level2_2 + span', expect: { count: 0, ids: [] } }, + { select: '#level3_1 + span', expect: { ids: ['level3_2'] } }, + { select: '#level3_1 + *', expect: { ids: ['level3_2'] } }, + { select: '#level3_2 + *', expect: { count: 0, ids: [] } }, + { select: '#level3_1 + em', expect: { count: 0, ids: [] } }, // testSelectorWithLaterSibling - { selector: 'h1 ~ ul', expect: { ids: ['list'] } }, - { selector: '#level2_1 ~ span', expect: { ids: ['level2_2'] } }, - { selector: '#level2_1 ~ *', expect: { ids: ['level2_2', 'level2_3'] } }, - { selector: '#level2_2 ~ span', expect: { count: 0, ids: [] } }, - { selector: '#level3_2 ~ *', expect: { count: 0, ids: [] } }, - { selector: '#level3_1 ~ em', expect: { count: 0, ids: [] } }, - { selector: '#level3_1 ~ #level3_2', expect: { ids: ['level3_2'] } }, - { selector: 'span ~ #level3_2', expect: { ids: ['level3_2'] } }, - { selector: 'div ~ #level3_2', expect: { count: 0, ids: [] } }, - { selector: 'div ~ #level2_3', expect: { count: 0, ids: [] } }, + { select: 'h1 ~ ul', expect: { ids: ['list'] } }, + { select: '#level2_1 ~ span', expect: { ids: ['level2_2'] } }, + { select: '#level2_1 ~ *', expect: { ids: ['level2_2', 'level2_3'] } }, + { select: '#level2_2 ~ span', expect: { count: 0, ids: [] } }, + { select: '#level3_2 ~ *', expect: { count: 0, ids: [] } }, + { select: '#level3_1 ~ em', expect: { count: 0, ids: [] } }, + { select: '#level3_1 ~ #level3_2', expect: { ids: ['level3_2'] } }, + { select: 'span ~ #level3_2', expect: { ids: ['level3_2'] } }, + { select: 'div ~ #level3_2', expect: { count: 0, ids: [] } }, + { select: 'div ~ #level2_3', expect: { count: 0, ids: [] } }, // testSelectorWithNewAttributeOperators - { selector: 'div[class^=bro]', expect: { ids: ['father', 'uncle'] } }, // matching beginning of string - { selector: 'div[class$=men]', expect: { ids: ['father', 'uncle'] } }, // matching end of string - { selector: 'div[class*="ers m"]', expect: { ids: ['father', 'uncle'] } }, // matching substring - { selector: '#level1 *[id^="level2_"]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, - { selector: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, - { selector: '#level1 *[id$="_1"]', expect: { ids: ['level2_1', 'level3_1'] } }, - { selector: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, - { selector: '#level1 *[id*="2"]', expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, - { selector: "#level1 *[id*='2']", expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, + { select: 'div[class^=bro]', expect: { ids: ['father', 'uncle'] } }, // matching beginning of string + { select: 'div[class$=men]', expect: { ids: ['father', 'uncle'] } }, // matching end of string + { select: 'div[class*="ers m"]', expect: { ids: ['father', 'uncle'] } }, // matching substring + { select: '#level1 *[id^="level2_"]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + { select: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + { select: '#level1 *[id$="_1"]', expect: { ids: ['level2_1', 'level3_1'] } }, + { select: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, + { select: '#level1 *[id*="2"]', expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, + { select: "#level1 *[id*='2']", expect: { ids: ['level2_1', 'level3_2', 'level2_2', 'level2_3'] } }, // benchmark(function() { $$('#level1 *[id^=level2_]') }, 1000, '[^=]') - { selector: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, + { select: '#level1 *[id^=level2_]', expect: { ids: ['level2_1', 'level2_2', 'level2_3'] } }, // benchmark(function() { $$('#level1 *[id$=_1]') }, 1000, '[$=]') - { selector: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, + { select: '#level1 *[id$=_1]', expect: { ids: ['level2_1', 'level3_1'] } }, // benchmark(function() { $$('#level1 *[id*=_2]') }, 1000, '[*=]') - { selector: '#level1 *[id*=_2]', expect: { ids: ['level3_2', 'level2_2'] } }, + { select: '#level1 *[id*=_2]', expect: { ids: ['level3_2', 'level2_2'] } }, // testSelectorWithDuplicates - { selector: 'div div' }, - { selector: '#dupContainer span span', expect: { ids: ['dupL2', 'dupL3', 'dupL4', 'dupL5'] } }, + { select: 'div div' }, + { select: '#dupContainer span span', expect: { ids: ['dupL2', 'dupL3', 'dupL4', 'dupL5'] } }, // benchmark(function() { $$('#dupContainer span span') }, 1000) - { selector: '#dupContainer span span', expect: { ids: ['dupL2', 'dupL3', 'dupL4', 'dupL5'] } }, + { select: '#dupContainer span span', expect: { ids: ['dupL2', 'dupL3', 'dupL4', 'dupL5'] } }, // testSelectorWithFirstLastOnlyNthNthLastChild - { selector: '#level1>*:first-child', expect: { ids: ['level2_1'] } }, - { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, - { selector: '#level1>*:last-child', expect: { ids: ['level2_3'] } }, - { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, - { selector: '#level1>div:last-child', expect: { ids: ['level2_3'] } }, - { selector: '#level1 div:last-child', expect: { ids: ['level2_3'] } }, - { selector: '#level1>div:first-child', expect: { count: 0, ids: [] } }, - { selector: '#level1>span:last-child', expect: { count: 0, ids: [] } }, - { selector: '#level1 span:first-child', expect: { ids: ['level2_1', 'level3_1'] } }, - { selector: '#level1:first-child', expect: { count: 0, ids: [] } }, - { selector: '#level1>*:only-child', expect: { count: 0, ids: [] } }, - { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, - { selector: '#level1:only-child', expect: { count: 0, ids: [] } }, - { selector: '#p *:nth-last-child(2)', expect: { ids: ['link_2'] } }, // nth-last-child - { selector: '#p *:nth-child(3)', expect: { ids: ['link_2'] } }, // nth-child - { selector: '#p a:nth-child(3)', expect: { ids: ['link_2'] } }, // nth-child - { selector: '#list > li:nth-child(n+2)', expect: { ids: ['item_2', 'item_3'] } }, - { selector: '#list > li:nth-child(-n+2)', expect: { ids: ['item_1', 'item_2'] } }, + { select: '#level1>*:first-child', expect: { ids: ['level2_1'] } }, + { select: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + { select: '#level1>*:last-child', expect: { ids: ['level2_3'] } }, + { select: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + { select: '#level1>div:last-child', expect: { ids: ['level2_3'] } }, + { select: '#level1 div:last-child', expect: { ids: ['level2_3'] } }, + { select: '#level1>div:first-child', expect: { count: 0, ids: [] } }, + { select: '#level1>span:last-child', expect: { count: 0, ids: [] } }, + { select: '#level1 span:first-child', expect: { ids: ['level2_1', 'level3_1'] } }, + { select: '#level1:first-child', expect: { count: 0, ids: [] } }, + { select: '#level1>*:only-child', expect: { count: 0, ids: [] } }, + { select: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + { select: '#level1:only-child', expect: { count: 0, ids: [] } }, + { select: '#p *:nth-last-child(2)', expect: { ids: ['link_2'] } }, // nth-last-child + { select: '#p *:nth-child(3)', expect: { ids: ['link_2'] } }, // nth-child + { select: '#p a:nth-child(3)', expect: { ids: ['link_2'] } }, // nth-child + { select: '#list > li:nth-child(n+2)', expect: { ids: ['item_2', 'item_3'] } }, + { select: '#list > li:nth-child(-n+2)', expect: { ids: ['item_1', 'item_2'] } }, // benchmark(function() { $$('#level1 *:first-child') }, 1000, ':first-child') - { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + { select: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, // benchmark(function() { $$('#level1 *:last-child') }, 1000, ':last-child') - { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + { select: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, // benchmark(function() { $$('#level1 *:only-child') }, 1000, ':only-child') - { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + { select: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, // testSelectorWithFirstLastNthNthLastOfType - { selector: '#p a:nth-of-type(2)', expect: { ids: ['link_2'] } }, // nth-of-type - { selector: '#p a:nth-of-type(1)', expect: { ids: ['link_1'] } }, // nth-of-type - { selector: '#p a:nth-last-of-type(1)', expect: { ids: ['link_2'] } }, // nth-last-of-type - { selector: '#p a:first-of-type', expect: { ids: ['link_1'] } }, // first-of-type - { selector: '#p a:last-of-type', expect: { ids: ['link_2'] } }, // last-of-type + { select: '#p a:nth-of-type(2)', expect: { ids: ['link_2'] } }, // nth-of-type + { select: '#p a:nth-of-type(1)', expect: { ids: ['link_1'] } }, // nth-of-type + { select: '#p a:nth-last-of-type(1)', expect: { ids: ['link_2'] } }, // nth-last-of-type + { select: '#p a:first-of-type', expect: { ids: ['link_1'] } }, // first-of-type + { select: '#p a:last-of-type', expect: { ids: ['link_2'] } }, // last-of-type // testSelectorWithNot - { selector: '#p a:not(:first-of-type)', expect: { ids: ['link_2'] } }, // first-of-type - { selector: '#p a:not(:last-of-type)', expect: { ids: ['link_1'] } }, // last-of-type - { selector: '#p a:not(:nth-of-type(1))', expect: { ids: ['link_2'] } }, // nth-of-type - { selector: '#p a:not(:nth-last-of-type(1))', expect: { ids: ['link_1'] } }, // nth-last-of-type - { selector: '#p a:not([rel~=nofollow])', expect: { ids: ['link_2'] } }, // attribute 1 - { selector: '#p a:not([rel^=external])', expect: { ids: ['link_2'] } }, // attribute 2 - { selector: '#p a:not([rel$=nofollow])', expect: { ids: ['link_2'] } }, // attribute 3 - { selector: '#p a:not([rel$="nofollow"]) > em', expect: { ids: ['em'] } }, // attribute 4 - { selector: '#list li:not(#item_1):not(#item_3)', expect: { ids: ['item_2'] } }, // adjacent :not clauses - { selector: '#grandfather > div:not(#uncle) #son', expect: { ids: ['son'] } }, - { selector: '#p a:not([rel$="nofollow"]) em', expect: { ids: ['em'] } }, // attribute 4 + all descendants - { selector: '#p a:not([rel$="nofollow"])>em', expect: { ids: ['em'] } }, // attribute 4 (without whitespace) + { select: '#p a:not(:first-of-type)', expect: { ids: ['link_2'] } }, // first-of-type + { select: '#p a:not(:last-of-type)', expect: { ids: ['link_1'] } }, // last-of-type + { select: '#p a:not(:nth-of-type(1))', expect: { ids: ['link_2'] } }, // nth-of-type + { select: '#p a:not(:nth-last-of-type(1))', expect: { ids: ['link_1'] } }, // nth-last-of-type + { select: '#p a:not([rel~=nofollow])', expect: { ids: ['link_2'] } }, // attribute 1 + { select: '#p a:not([rel^=external])', expect: { ids: ['link_2'] } }, // attribute 2 + { select: '#p a:not([rel$=nofollow])', expect: { ids: ['link_2'] } }, // attribute 3 + { select: '#p a:not([rel$="nofollow"]) > em', expect: { ids: ['em'] } }, // attribute 4 + { select: '#list li:not(#item_1):not(#item_3)', expect: { ids: ['item_2'] } }, // adjacent :not clauses + { select: '#grandfather > div:not(#uncle) #son', expect: { ids: ['son'] } }, + { select: '#p a:not([rel$="nofollow"]) em', expect: { ids: ['em'] } }, // attribute 4 + all descendants + { select: '#p a:not([rel$="nofollow"])>em', expect: { ids: ['em'] } }, // attribute 4 (without whitespace) // testSelectorWithEnabledDisabledChecked - { selector: '#troubleForm > *:disabled', expect: { ids: ['disabled_text_field'] } }, - { selector: '#troubleForm > *:enabled', expect: { ids: ['hidden', 'enabled_text_field', 'checked_box', 'unchecked_box', 'checked_radio', 'unchecked_radio'] } }, - { selector: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, + { select: '#troubleForm > *:disabled', expect: { ids: ['disabled_text_field'] } }, + { select: '#troubleForm > *:enabled', expect: { ids: ['hidden', 'enabled_text_field', 'checked_box', 'unchecked_box', 'checked_radio', 'unchecked_radio'] } }, + { select: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, // testIdenticalResultsFromEquivalentSelectors - { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers]' } } }, - { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers].brothers' } } }, - { selector: 'div:not(.brothers)', expect: { equivalentTo: { selector: 'div:not([class~=brothers])' } } }, - { selector: 'li ~ li', expect: { equivalentTo: { selector: 'li:not(:first-child)' } } }, - { selector: 'ul > li', expect: { equivalentTo: { selector: 'ul > li:nth-child(n)' } } }, - { selector: 'ul > li:nth-child(even)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n)' } } }, - { selector: 'ul > li:nth-child(odd)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n+1)' } } }, - { selector: 'ul > li:first-child', expect: { equivalentTo: { selector: 'ul > li:nth-child(1)' } } }, - { selector: 'ul > li:last-child', expect: { equivalentTo: { selector: 'ul > li:nth-last-child(1)' } } }, - { selector: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { selector: 'ul > li' } } }, - { selector: 'ul > li:nth-child(n-999)', expect: { equivalentTo: { selector: 'ul > li' } } }, - { selector: 'ul>li', expect: { equivalentTo: { selector: 'ul > li' } } }, - { selector: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { selector: '#p a:not([rel$="nofollow"]) > em' } } }, + { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers]' } } }, + { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers].brothers' } } }, + { select: 'div:not(.brothers)', expect: { equivalentTo: { search: 'div:not([class~=brothers])' } } }, + { select: 'li ~ li', expect: { equivalentTo: { search: 'li:not(:first-child)' } } }, + { select: 'ul > li', expect: { equivalentTo: { search: 'ul > li:nth-child(n)' } } }, + { select: 'ul > li:nth-child(even)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n)' } } }, + { select: 'ul > li:nth-child(odd)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n+1)' } } }, + { select: 'ul > li:first-child', expect: { equivalentTo: { search: 'ul > li:nth-child(1)' } } }, + { select: 'ul > li:last-child', expect: { equivalentTo: { search: 'ul > li:nth-last-child(1)' } } }, + { select: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { search: 'ul > li' } } }, + { select: 'ul > li:nth-child(n-999)', expect: { equivalentTo: { search: 'ul > li' } } }, + { select: 'ul>li', expect: { equivalentTo: { search: 'ul > li' } } }, + { select: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { search: '#p a:not([rel$="nofollow"]) > em' } } }, // testSelectorsThatShouldReturnNothing - { selector: 'span:empty > *', expect: { count: 0, ids: [] } }, - { selector: 'div.brothers:not(.brothers)', expect: { count: 0, ids: [] } }, - { selector: '#level2_2 :only-child:not(:last-child)', expect: { count: 0, ids: [] } }, - { selector: '#level2_2 :only-child:not(:first-child)', expect: { count: 0, ids: [] } }, + { select: 'span:empty > *', expect: { count: 0, ids: [] } }, + { select: 'div.brothers:not(.brothers)', expect: { count: 0, ids: [] } }, + { select: '#level2_2 :only-child:not(:last-child)', expect: { count: 0, ids: [] } }, + { select: '#level2_2 :only-child:not(:first-child)', expect: { count: 0, ids: [] } }, // testCommasFor$$ - { selector: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, - { selector: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, - { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, - { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, // testElementDownWithDotAndColon - { selector: '#dupContainer\\.withdot\\:active #dupL4_dotcolon', expect: { ids: ['dupL4_dotcolon'] } } + { select: '#dupContainer\\.withdot\\:active #dupL4_dotcolon', expect: { ids: ['dupL4_dotcolon'] } } ], }, @@ -437,8 +437,8 @@ runScenarios('prototype 2', 'normal', [ }, cases: [ // testSelectorWithEmpty - { selector: '#level1 *:empty', expect: { ids: ['level3_1', 'level3_2', 'level2_3'] } }, - { selector: '#level_only_child:empty', expect: { count: 0, ids: [] } }, // newlines count as content + { select: '#level1 *:empty', expect: { ids: ['level3_1', 'level3_2', 'level2_3'] } }, + { select: '#level_only_child:empty', expect: { count: 0, ids: [] } }, // newlines count as content ], }, diff --git a/test_new/browser/quirks.test.ts b/test_new/browser/quirks.test.ts index 138f8eb..265596d 100644 --- a/test_new/browser/quirks.test.ts +++ b/test_new/browser/quirks.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('quirks', 'normal', [ { @@ -24,10 +24,10 @@ runScenarios('quirks', 'normal', [ `, cases: [ - { selector: '#dom [class~=foobar]', expect: { ids: ['d1'] } }, - { selector: '#qsa [class~=foobar]', expect: { ids: ['q1'] } }, - { selector: '#dom [class~=foobar i]', expect: { ids: ['d1', 'd2', 'd3'] } }, - { selector: '#qsa [class~=foobar i]', expect: { ids: ['q1', 'q2', 'q3'] } }, + { select: '#dom [class~=foobar]', expect: { ids: ['d1'] } }, + { select: '#qsa [class~=foobar]', expect: { ids: ['q1'] } }, + { select: '#dom [class~=foobar i]', expect: { ids: ['d1', 'd2', 'd3'] } }, + { select: '#qsa [class~=foobar i]', expect: { ids: ['q1', 'q2', 'q3'] } }, ], }, { @@ -52,10 +52,10 @@ runScenarios('quirks', 'normal', [ `, cases: [ - { selector: '#dom [class~=foobar]', expect: { ids: ['d1'] } }, - { selector: '#qsa [class~=foobar]', expect: { ids: ['q1'] } }, - { selector: '#dom [class~=foobar i]', expect: { ids: ['d1', 'd2', 'd3'] } }, - { selector: '#qsa [class~=foobar i]', expect: { ids: ['q1', 'q2', 'q3'] } }, + { select: '#dom [class~=foobar]', expect: { ids: ['d1'] } }, + { select: '#qsa [class~=foobar]', expect: { ids: ['q1'] } }, + { select: '#dom [class~=foobar i]', expect: { ids: ['d1', 'd2', 'd3'] } }, + { select: '#qsa [class~=foobar i]', expect: { ids: ['q1', 'q2', 'q3'] } }, ], } ]); diff --git a/test_new/browser/scope.test.ts b/test_new/browser/scope.test.ts index 2d92426..5e51a94 100644 --- a/test_new/browser/scope.test.ts +++ b/test_new/browser/scope.test.ts @@ -1,4 +1,4 @@ -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; runScenarios('scope', 'normal', [ { @@ -11,9 +11,9 @@ runScenarios('scope', 'normal', [ `, cases: [ - { selector: 'ul a', expect: { count: 2 } }, - { selector: '#scope', expect: { count: 1 } }, - { selector: 'a', root: { kind: 'id', value: 'scope' }, expect: { count: 1 } }, + { select: 'ul a', expect: { count: 2 } }, + { select: '#scope', expect: { count: 1 } }, + { select: 'a', scope: { by: 'id', id: 'scope' }, expect: { count: 1 } }, ], }, @@ -27,10 +27,10 @@ runScenarios('scope', 'normal', [ `, cases: [ - { selector: '#scope', expect: { count: 1 } }, - { selector: ':scope ul a', root: { kind: 'id', value: 'scope' }, expect: { count: 0, ids: [] } }, - { selector: ':scope body ul a', root: { kind: 'id', value: 'scope' }, expect: { count: 0, ids: [] } }, - { selector: ':scope a', root: { kind: 'id', value: 'scope' }, expect: { count: 1 } }, + { select: '#scope', expect: { count: 1 } }, + { select: ':scope ul a', scope: { by: 'id', id: 'scope' }, expect: { count: 0, ids: [] } }, + { select: ':scope body ul a', scope: { by: 'id', id: 'scope' }, expect: { count: 0, ids: [] } }, + { select: ':scope a', scope: { by: 'id', id: 'scope' }, expect: { count: 1 } }, ], }, @@ -43,9 +43,9 @@ runScenarios('scope', 'normal', [ `, cases: [ - { selector: '.a', expect: { count: 1 } }, - { selector: 'body div', root: { kind: 'selector', value: '.a' }, expect: { count: 2 } }, - { selector: ':scope body div', root: { kind: 'selector', value: '.a' }, expect: { count: 0, ids: [] } }, + { select: '.a', expect: { count: 1 } }, + { select: 'body div', scope: { by: 'first', selector: '.a' }, expect: { count: 2 } }, + { select: ':scope body div', scope: { by: 'first', selector: '.a' }, expect: { count: 0, ids: [] } }, ], }, @@ -59,9 +59,9 @@ runScenarios('scope', 'normal', [ `, cases: [ - { selector: 'div', expect: { count: 1 } }, - { selector: ':scope > p', root: { kind: 'selector', value: 'div' }, expect: { count: 1 } }, - { selector: ':scope > span', root: { kind: 'selector', value: 'div' }, expect: { count: 0, ids: [] } }, + { select: 'div', expect: { count: 1 } }, + { select: ':scope > p', scope: { by: 'first', selector: 'div' }, expect: { count: 1 } }, + { select: ':scope > span', scope: { by: 'first', selector: 'div' }, expect: { count: 0, ids: [] } }, ], }, @@ -74,15 +74,15 @@ runScenarios('scope', 'normal', [ `, cases: [ - { selector: 'body div', root: { kind: 'selector', value: '.a' }, expect: { count: 2 } }, - { selector: ':scope body div', root: { kind: 'selector', value: '.a' }, expect: { count: 0, ids: [] } }, - { selector: ':scope > .a1, :scope > .a2', root: { kind: 'selector', value: '.a' }, expect: { count: 2 } }, + { select: 'body div', scope: { by: 'first', selector: '.a' }, expect: { count: 2 } }, + { select: ':scope body div', scope: { by: 'first', selector: '.a' }, expect: { count: 0, ids: [] } }, + { select: ':scope > .a1, :scope > .a2', scope: { by: 'first', selector: '.a' }, expect: { count: 2 } }, ], }, { name: 'scope-04a', - modifier: 'fixme', + status: 'fixme', htmlMode: 'document', html: ` @@ -99,13 +99,13 @@ runScenarios('scope', 'normal', [ `, cases: [ - { selector: ':scope > [data-test="foo"]', root: { kind: 'selector', value: 'body' }, expect: { count: 1 } }, + { select: ':scope > [data-test="foo"]', scope: { by: 'first', selector: 'body' }, expect: { count: 1 } }, ], }, { name: 'scope-04b', - modifier: 'fixme', + status: 'fixme', html: `
    @@ -116,8 +116,8 @@ runScenarios('scope', 'normal', [ `, cases: [ { - selector: ':scope > div', - root: { kind: 'selector', value: 'div' }, + select: ':scope > div', + scope: { by: 'first', selector: 'div' }, expect: { classes: ['outer', 'other-outer'] }, diff --git a/test_new/browser/scotch.test.ts b/test_new/browser/scotch.test.ts index 2f18333..fa5260c 100644 --- a/test_new/browser/scotch.test.ts +++ b/test_new/browser/scotch.test.ts @@ -1,5 +1,5 @@ import { Page } from "playwright/test"; -import { runScenarios } from "./harness"; +import { runScenarios } from "./harness/scenarios"; const html = ` *:first-child', expect: { ids: ['level2_1'] } }, - { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, - { selector: '#level1>div:first-child', expect: { count: 0 } }, - { selector: '#level1 span:first-child', expect: { ids: ['level2_1', 'level3_1'] } }, - { selector: '#level1:first-child', expect: { count: 0 } }, + { select: '#level1>*:first-child', expect: { ids: ['level2_1'] } }, + { select: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + { select: '#level1>div:first-child', expect: { count: 0 } }, + { select: '#level1 span:first-child', expect: { ids: ['level2_1', 'level3_1'] } }, + { select: '#level1:first-child', expect: { count: 0 } }, // benchmark(function(){ select('#level1 *:first-child'); }, 1000) - { selector: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, + { select: '#level1 *:first-child', expect: { ids: ['level2_1', 'level3_1', 'level_only_child'] } }, // E:last-child - { selector: '#level1>*:last-child', expect: { ids: ['level2_3'] } }, - { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, - { selector: '#level1>div:last-child', expect: { ids: ['level2_3'] } }, - { selector: '#level1 div:last-child', expect: { ids: ['level2_3'] } }, - { selector: '#level1>span:last-child', expect: { count: 0 } }, + { select: '#level1>*:last-child', expect: { ids: ['level2_3'] } }, + { select: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + { select: '#level1>div:last-child', expect: { ids: ['level2_3'] } }, + { select: '#level1 div:last-child', expect: { ids: ['level2_3'] } }, + { select: '#level1>span:last-child', expect: { count: 0 } }, // benchmark(function(){ select('#level1 *:last-child'); }, 1000) - { selector: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, + { select: '#level1 *:last-child', expect: { ids: ['level3_2', 'level_only_child', 'level2_3'] } }, // E:nth-child(n) - { selector: '#p *:nth-child(3)', expect: { ids: ['link_2'] } }, - { selector: '#p a:nth-child(3)', expect: { ids: ['link_2'] } }, - { selector: '#list > li:nth-child(n+2)', expect: { ids: ['item_2', 'item_3'] } }, - { selector: '#list > li:nth-child(-n+2)', expect: { ids: ['item_1', 'item_2'] } }, + { select: '#p *:nth-child(3)', expect: { ids: ['link_2'] } }, + { select: '#p a:nth-child(3)', expect: { ids: ['link_2'] } }, + { select: '#list > li:nth-child(n+2)', expect: { ids: ['item_2', 'item_3'] } }, + { select: '#list > li:nth-child(-n+2)', expect: { ids: ['item_1', 'item_2'] } }, // E:nth-of-type(n) - { selector: '#p a:nth-of-type(2)', expect: { ids: ['link_2'] } }, - { selector: '#p a:nth-of-type(1)', expect: { ids: ['link_1'] } }, + { select: '#p a:nth-of-type(2)', expect: { ids: ['link_2'] } }, + { select: '#p a:nth-of-type(1)', expect: { ids: ['link_1'] } }, // E:nth-last-of-type(n) - { selector: '#p a:nth-last-of-type(1)', expect: { ids: ['link_2'] } }, + { select: '#p a:nth-last-of-type(1)', expect: { ids: ['link_2'] } }, // E:first-of-type - { selector: '#p a:first-of-type', expect: { ids: ['link_1'] } }, + { select: '#p a:first-of-type', expect: { ids: ['link_1'] } }, // E:last-of-type - { selector: '#p a:last-of-type', expect: { ids: ['link_2'] } }, + { select: '#p a:last-of-type', expect: { ids: ['link_2'] } }, // E:only-child - { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, - { selector: '#level1>*:only-child', expect: { count: 0 } }, - { selector: '#level1:only-child', expect: { count: 0 } }, - { selector: '#level2_2 :only-child:not(:last-child)', expect: { count: 0 } }, - { selector: '#level2_2 :only-child:not(:first-child)', expect: { count: 0 } }, + { select: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + { select: '#level1>*:only-child', expect: { count: 0 } }, + { select: '#level1:only-child', expect: { count: 0 } }, + { select: '#level2_2 :only-child:not(:last-child)', expect: { count: 0 } }, + { select: '#level2_2 :only-child:not(:first-child)', expect: { count: 0 } }, // benchmark(function(){ select('#level1 *:only-child'); }, 1000) - { selector: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, + { select: '#level1 *:only-child', expect: { ids: ['level_only_child'] } }, ], }, @@ -311,9 +311,9 @@ runScenarios('scotch', 'normal', [ }, cases: [ // E:empty - { selector: '#level1 *:empty', expect: { ids: ['level3_1', 'level3_2', 'level2_3'] } }, - { selector: '#level_only_child:empty', expect: { count: 0 } }, - { selector: 'span:empty > *', expect: { count: 0 } }, + { select: '#level1 *:empty', expect: { ids: ['level3_1', 'level3_2', 'level2_3'] } }, + { select: '#level_only_child:empty', expect: { count: 0 } }, + { select: 'span:empty > *', expect: { count: 0 } }, ], }, @@ -323,21 +323,21 @@ runScenarios('scotch', 'normal', [ htmlMode: 'document', setupPage: setupNw, cases: [ - { selector: 'a:not([href="#"])', expect: { count: 0 } }, - { selector: 'div.brothers:not(.brothers)', expect: { count: 0 } }, - { selector: 'a[class~=external]:not([href="#"])', expect: { count: 0 } }, - { selector: '#p a:not(:first-of-type)', expect: { ids: ['link_2'] } }, - { selector: '#p a:not(:last-of-type)', expect: { ids: ['link_1'] } }, - { selector: '#p a:not(:nth-of-type(1))', expect: { ids: ['link_2'] } }, - { selector: '#p a:not(:nth-last-of-type(1))', expect: { ids: ['link_1'] } }, - { selector: '#p a:not([rel~=nofollow])', expect: { ids: ['link_2'] } }, - { selector: '#p a:not([rel^=external])', expect: { ids: ['link_2'] } }, - { selector: '#p a:not([rel$=nofollow])', expect: { ids: ['link_2'] } }, - { selector: '#p a:not([rel$="nofollow"]) > em', expect: { ids: ['em'] } }, - { selector: '#list li:not(#item_1):not(#item_3)', expect: { ids: ['item_2'] } }, - { selector: '#grandfather > div:not(#uncle) #son', expect: { ids: ['son'] } }, - { selector: '#p a:not([rel$="nofollow"]) em', expect: { ids: ['em'] } }, - { selector: '#p a:not([rel$="nofollow"])>em', expect: { ids: ['em'] } }, + { select: 'a:not([href="#"])', expect: { count: 0 } }, + { select: 'div.brothers:not(.brothers)', expect: { count: 0 } }, + { select: 'a[class~=external]:not([href="#"])', expect: { count: 0 } }, + { select: '#p a:not(:first-of-type)', expect: { ids: ['link_2'] } }, + { select: '#p a:not(:last-of-type)', expect: { ids: ['link_1'] } }, + { select: '#p a:not(:nth-of-type(1))', expect: { ids: ['link_2'] } }, + { select: '#p a:not(:nth-last-of-type(1))', expect: { ids: ['link_1'] } }, + { select: '#p a:not([rel~=nofollow])', expect: { ids: ['link_2'] } }, + { select: '#p a:not([rel^=external])', expect: { ids: ['link_2'] } }, + { select: '#p a:not([rel$=nofollow])', expect: { ids: ['link_2'] } }, + { select: '#p a:not([rel$="nofollow"]) > em', expect: { ids: ['em'] } }, + { select: '#list li:not(#item_1):not(#item_3)', expect: { ids: ['item_2'] } }, + { select: '#grandfather > div:not(#uncle) #son', expect: { ids: ['son'] } }, + { select: '#p a:not([rel$="nofollow"]) em', expect: { ids: ['em'] } }, + { select: '#p a:not([rel$="nofollow"])>em', expect: { ids: ['em'] } }, ], }, @@ -348,10 +348,10 @@ runScenarios('scotch', 'normal', [ setupPage: setupNw, cases: [ // E:disabled - { selector: '#troubleForm > p > *:disabled', expect: { ids: ['disabled_text_field'] } }, + { select: '#troubleForm > p > *:disabled', expect: { ids: ['disabled_text_field'] } }, // E:checked - { selector: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, + { select: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, ], }, @@ -362,49 +362,49 @@ runScenarios('scotch', 'normal', [ setupPage: setupNw, cases: [ // E F — Descendant - { selector: '#fixtures a *', expect: { ids: ['em2', 'em', 'span'] } }, - { selector: 'div#fixtures p', expect: { includesIds: ['p'] } }, + { select: '#fixtures a *', expect: { ids: ['em2', 'em', 'span'] } }, + { select: 'div#fixtures p', expect: { includesIds: ['p'] } }, // E + F — Adjacent sibling - { selector: 'div.brothers + div.brothers', expect: { includesIds: ['uncle'] } }, - { selector: 'div.brothers + div', expect: { includesIds: ['uncle'] } }, - { selector: '#level2_1+span', expect: { includesIds: ['level2_2'] } }, - { selector: '#level2_1 + span', expect: { includesIds: ['level2_2'] } }, - { selector: '#level2_1 + *', expect: { includesIds: ['level2_2'] } }, - { selector: '#level2_2 + span', expect: { count: 0 } }, - { selector: '#level3_1 + span', expect: { includesIds: ['level3_2'] } }, - { selector: '#level3_1 + *', expect: { includesIds: ['level3_2'] } }, - { selector: '#level3_2 + *', expect: { count: 0 } }, - { selector: '#level3_1 + em', expect: { count: 0 } }, + { select: 'div.brothers + div.brothers', expect: { includesIds: ['uncle'] } }, + { select: 'div.brothers + div', expect: { includesIds: ['uncle'] } }, + { select: '#level2_1+span', expect: { includesIds: ['level2_2'] } }, + { select: '#level2_1 + span', expect: { includesIds: ['level2_2'] } }, + { select: '#level2_1 + *', expect: { includesIds: ['level2_2'] } }, + { select: '#level2_2 + span', expect: { count: 0 } }, + { select: '#level3_1 + span', expect: { includesIds: ['level3_2'] } }, + { select: '#level3_1 + *', expect: { includesIds: ['level3_2'] } }, + { select: '#level3_2 + *', expect: { count: 0 } }, + { select: '#level3_1 + em', expect: { count: 0 } }, // benchmark(function(){ select('#level3_1 + span'); }, 1000) - { selector: '#level3_1 + span', expect: { includesIds: ['level3_2'] } }, + { select: '#level3_1 + span', expect: { includesIds: ['level3_2'] } }, // E > F — Child - { selector: 'p.first > a', expect: { ids: ['link_1', 'link_2'] } }, - { selector: 'div#grandfather > div', expect: { ids: ['father', 'uncle'] } }, - { selector: '#level1>span', expect: { ids: ['level2_1', 'level2_2'] } }, - { selector: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, - { selector: '#level2_1 > *', expect: { ids: ['level3_1', 'level3_2'] } }, - { selector: 'div > #nonexistent', expect: { count: 0 } }, + { select: 'p.first > a', expect: { ids: ['link_1', 'link_2'] } }, + { select: 'div#grandfather > div', expect: { ids: ['father', 'uncle'] } }, + { select: '#level1>span', expect: { ids: ['level2_1', 'level2_2'] } }, + { select: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, + { select: '#level2_1 > *', expect: { ids: ['level3_1', 'level3_2'] } }, + { select: 'div > #nonexistent', expect: { count: 0 } }, // benchmark(function(){ select('#level1 > span'); }, 1000) - { selector: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, + { select: '#level1 > span', expect: { ids: ['level2_1', 'level2_2'] } }, // E ~ F — General sibling - { selector: 'h1 ~ ul', expect: { includesIds: ['list'] } }, - { selector: '#level2_2 ~ span', expect: { count: 0 } }, - { selector: '#level3_2 ~ *', expect: { count: 0 } }, - { selector: '#level3_1 ~ em', expect: { count: 0 } }, - { selector: 'div ~ #level3_2', expect: { count: 0 } }, - { selector: 'div ~ #level2_3', expect: { count: 0 } }, - { selector: '#level2_1 ~ span', expect: { includesIds: ['level2_2'] } }, - { selector: '#level2_1 ~ *', expect: { ids: ['level2_2', 'level2_3'] } }, - { selector: '#level3_1 ~ #level3_2', expect: { includesIds: ['level3_2'] } }, - { selector: 'span ~ #level3_2', expect: { includesIds: ['level3_2'] } }, + { select: 'h1 ~ ul', expect: { includesIds: ['list'] } }, + { select: '#level2_2 ~ span', expect: { count: 0 } }, + { select: '#level3_2 ~ *', expect: { count: 0 } }, + { select: '#level3_1 ~ em', expect: { count: 0 } }, + { select: 'div ~ #level3_2', expect: { count: 0 } }, + { select: 'div ~ #level2_3', expect: { count: 0 } }, + { select: '#level2_1 ~ span', expect: { includesIds: ['level2_2'] } }, + { select: '#level2_1 ~ *', expect: { ids: ['level2_2', 'level2_3'] } }, + { select: '#level3_1 ~ #level3_2', expect: { includesIds: ['level3_2'] } }, + { select: 'span ~ #level3_2', expect: { includesIds: ['level3_2'] } }, // benchmark(function(){ select('#level2_1 ~ span'); }, 1000) - { selector: '#level2_1 ~ span', expect: { includesIds: ['level2_2'] } }, + { select: '#level2_1 ~ span', expect: { includesIds: ['level2_2'] } }, ], }, @@ -414,25 +414,25 @@ runScenarios('scotch', 'normal', [ htmlMode: 'document', setupPage: setupNw, cases: [ - { selector: 'span', expect: { includesIds: ['dupL1'] } }, - { selector: 'span#dupL1', expect: { includesIds: ['dupL1'] } }, - { selector: 'div > span', expect: { includesIds: ['dupL1'] } }, // child combinator - { selector: '#dupContainer span', expect: { includesIds: ['dupL1'] } }, // descendant combinator - { selector: '#dupL1', expect: { includesIds: ['dupL1'] } }, // ID only - { selector: 'span.span_foo', expect: { includesIds: ['dupL1'] } }, // class name 1 - { selector: 'span.span_bar', expect: { includesIds: ['dupL1'] } }, // class name 2 - { selector: 'span:first-child', expect: { includesIds: ['dupL1'] } }, // first-child pseudoclass - - { selector: 'span.span_wtf', expect: { excludesIds: ['dupL1'] } }, // bogus class name - { selector: '#dupL2', expect: { excludesIds: ['dupL1'] } }, // different ID - { selector: 'div', expect: { excludesIds: ['dupL1'] } }, // different tag name - { selector: 'span span', expect: { excludesIds: ['dupL1'] } }, // different ancestry - { selector: 'span > span', expect: { excludesIds: ['dupL1'] } }, // different parent - { selector: 'span:nth-child(5)', expect: { excludesIds: ['dupL1'] } }, // different pseudoclass - - { selector: 'a[rel^=external]', expect: { includesIds: ['link_1'], excludesIds: ['link_2'] } }, - { selector: 'a[rel^="external"]', expect: { includesIds: ['link_1'] } }, - { selector: "a[rel^='external']", expect: { includesIds: ['link_1'] } }, + { select: 'span', expect: { includesIds: ['dupL1'] } }, + { select: 'span#dupL1', expect: { includesIds: ['dupL1'] } }, + { select: 'div > span', expect: { includesIds: ['dupL1'] } }, // child combinator + { select: '#dupContainer span', expect: { includesIds: ['dupL1'] } }, // descendant combinator + { select: '#dupL1', expect: { includesIds: ['dupL1'] } }, // ID only + { select: 'span.span_foo', expect: { includesIds: ['dupL1'] } }, // class name 1 + { select: 'span.span_bar', expect: { includesIds: ['dupL1'] } }, // class name 2 + { select: 'span:first-child', expect: { includesIds: ['dupL1'] } }, // first-child pseudoclass + + { select: 'span.span_wtf', expect: { excludesIds: ['dupL1'] } }, // bogus class name + { select: '#dupL2', expect: { excludesIds: ['dupL1'] } }, // different ID + { select: 'div', expect: { excludesIds: ['dupL1'] } }, // different tag name + { select: 'span span', expect: { excludesIds: ['dupL1'] } }, // different ancestry + { select: 'span > span', expect: { excludesIds: ['dupL1'] } }, // different parent + { select: 'span:nth-child(5)', expect: { excludesIds: ['dupL1'] } }, // different pseudoclass + + { select: 'a[rel^=external]', expect: { includesIds: ['link_1'], excludesIds: ['link_2'] } }, + { select: 'a[rel^="external"]', expect: { includesIds: ['link_1'] } }, + { select: "a[rel^='external']", expect: { includesIds: ['link_1'] } }, ], }, @@ -442,18 +442,18 @@ runScenarios('scotch', 'normal', [ htmlMode: 'document', setupPage: setupNw, cases: [ - { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers]' } } }, - { selector: 'div.brothers', expect: { equivalentTo: { selector: 'div[class~=brothers].brothers' } } }, - { selector: 'div:not(.brothers)', expect: { equivalentTo: { selector: 'div:not([class~=brothers])' } } }, - { selector: 'li ~ li', expect: { equivalentTo: { selector: 'li:not(:first-child)' } } }, - { selector: 'ul > li', expect: { equivalentTo: { selector: 'ul > li:nth-child(n)' } } }, - { selector: 'ul > li:nth-child(even)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n)' } } }, - { selector: 'ul > li:nth-child(odd)', expect: { equivalentTo: { selector: 'ul > li:nth-child(2n+1)' } } }, - { selector: 'ul > li:first-child', expect: { equivalentTo: { selector: 'ul > li:nth-child(1)' } } }, - { selector: 'ul > li:last-child', expect: { equivalentTo: { selector: 'ul > li:nth-last-child(1)' } } }, - { selector: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { selector: 'ul > li' } } }, - { selector: 'ul>li', expect: { equivalentTo: { selector: 'ul > li' } } }, - { selector: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { selector: '#p a:not([rel$="nofollow"]) > em' } } }, + { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers]' } } }, + { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers].brothers' } } }, + { select: 'div:not(.brothers)', expect: { equivalentTo: { search: 'div:not([class~=brothers])' } } }, + { select: 'li ~ li', expect: { equivalentTo: { search: 'li:not(:first-child)' } } }, + { select: 'ul > li', expect: { equivalentTo: { search: 'ul > li:nth-child(n)' } } }, + { select: 'ul > li:nth-child(even)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n)' } } }, + { select: 'ul > li:nth-child(odd)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n+1)' } } }, + { select: 'ul > li:first-child', expect: { equivalentTo: { search: 'ul > li:nth-child(1)' } } }, + { select: 'ul > li:last-child', expect: { equivalentTo: { search: 'ul > li:nth-last-child(1)' } } }, + { select: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { search: 'ul > li' } } }, + { select: 'ul>li', expect: { equivalentTo: { search: 'ul > li' } } }, + { select: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { search: '#p a:not([rel$="nofollow"]) > em' } } }, ], }, @@ -463,10 +463,10 @@ runScenarios('scotch', 'normal', [ htmlMode: 'document', setupPage: setupNw, cases: [ - { selector: '#list, .first,*[xml:lang="es-us"] , #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, - { selector: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, - { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, - { selector: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + { select: '#list, .first,*[xml:lang="es-us"] , #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, + { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, ], }, ]); diff --git a/test_new/browser/speed.test.ts b/test_new/browser/speed.test.ts index f864205..a3d1e82 100644 --- a/test_new/browser/speed.test.ts +++ b/test_new/browser/speed.test.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'node:fs'; -import { runScenarios } from './harness'; +import { runScenarios } from './harness/scenarios'; const html = readFileSync('test_new/browser/speed_selectors.html', 'utf8'); @@ -9,51 +9,51 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: '#title' }, - { selector: 'h1#title' }, - { selector: 'div #title' }, - { selector: '#changes + #contents' }, - { selector: '.note' }, - { selector: 'div.note' }, - { selector: 'body .note' }, - { selector: '.no-toc + .toc' }, - { selector: 'div.example' }, - { selector: '.title' }, - { selector: '.toc' }, - { selector: 'p + .note' }, - { selector: 'div.example, p.note' }, - { selector: 'body' }, - { selector: 'div' }, - { selector: 'body div' }, - { selector: 'div p' }, - { selector: 'div > p' }, - { selector: 'div + p' }, - { selector: 'div ~ p' }, - { selector: 'div[class^=exa][class$=mple]' }, - { selector: 'p a' }, - { selector: 'div p a' }, - { selector: 'div > p > a' }, - { selector: 'div.example > p > a' }, - { selector: 'div + p + ul' }, - { selector: 'div ~ p ~ ul' }, - { selector: 'div, ul, a' }, - { selector: 'h1, h2, h3, h4, h5, h6' }, - { selector: 'a[href][lang][class]' }, - { selector: 'div[class]' }, - { selector: 'div[class=example]' }, - { selector: 'div[class^=exa]' }, - { selector: 'div[class$=mple]' }, - { selector: 'div[class*=e]' }, - { selector: 'div[class~=example]' }, - { selector: 'div:not(.example)' }, - { selector: 'div:nth-child(even)' }, - { selector: 'div:nth-child(2n)' }, - { selector: 'div:nth-child(odd)' }, - { selector: 'div:nth-child(2n+1)' }, - { selector: 'div:nth-child(n)' }, - { selector: 'p:only-child' }, - { selector: 'p:last-child' }, - { selector: 'p:first-child' }, + { select: '#title' }, + { select: 'h1#title' }, + { select: 'div #title' }, + { select: '#changes + #contents' }, + { select: '.note' }, + { select: 'div.note' }, + { select: 'body .note' }, + { select: '.no-toc + .toc' }, + { select: 'div.example' }, + { select: '.title' }, + { select: '.toc' }, + { select: 'p + .note' }, + { select: 'div.example, p.note' }, + { select: 'body' }, + { select: 'div' }, + { select: 'body div' }, + { select: 'div p' }, + { select: 'div > p' }, + { select: 'div + p' }, + { select: 'div ~ p' }, + { select: 'div[class^=exa][class$=mple]' }, + { select: 'p a' }, + { select: 'div p a' }, + { select: 'div > p > a' }, + { select: 'div.example > p > a' }, + { select: 'div + p + ul' }, + { select: 'div ~ p ~ ul' }, + { select: 'div, ul, a' }, + { select: 'h1, h2, h3, h4, h5, h6' }, + { select: 'a[href][lang][class]' }, + { select: 'div[class]' }, + { select: 'div[class=example]' }, + { select: 'div[class^=exa]' }, + { select: 'div[class$=mple]' }, + { select: 'div[class*=e]' }, + { select: 'div[class~=example]' }, + { select: 'div:not(.example)' }, + { select: 'div:nth-child(even)' }, + { select: 'div:nth-child(2n)' }, + { select: 'div:nth-child(odd)' }, + { select: 'div:nth-child(2n+1)' }, + { select: 'div:nth-child(n)' }, + { select: 'p:only-child' }, + { select: 'p:last-child' }, + { select: 'p:first-child' }, ], }, { @@ -61,22 +61,22 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'ul li a span' }, - { selector: 'ul li span' }, - { selector: 'li a span' }, - { selector: 'ul a span' }, - { selector: 'ul li a' }, - { selector: 'ul li' }, - { selector: 'li a' }, - { selector: 'ul a' }, - { selector: 'dl dt code' }, - { selector: 'dl dd a' }, - { selector: 'dl dd' }, - { selector: 'dl dt' }, - { selector: 'table tr td' }, - { selector: 'table tr' }, - { selector: 'tr td a' }, - { selector: 'td a' }, + { select: 'ul li a span' }, + { select: 'ul li span' }, + { select: 'li a span' }, + { select: 'ul a span' }, + { select: 'ul li a' }, + { select: 'ul li' }, + { select: 'li a' }, + { select: 'ul a' }, + { select: 'dl dt code' }, + { select: 'dl dd a' }, + { select: 'dl dd' }, + { select: 'dl dt' }, + { select: 'table tr td' }, + { select: 'table tr' }, + { select: 'tr td a' }, + { select: 'td a' }, ], }, { @@ -84,38 +84,38 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'div ul li a span' }, - { selector: 'div ul li a' }, - { selector: 'div ul li' }, - { selector: 'div ul' }, - { selector: 'ul li a span' }, - { selector: 'ul li a' }, - { selector: 'ul li' }, - { selector: 'li a' }, - { selector: 'div > ul > li > a > span' }, - { selector: 'div > ul > li > a' }, - { selector: 'div > ul > li' }, - { selector: 'div > ul' }, - { selector: 'ul > li > a > span' }, - { selector: 'ul > li > a' }, - { selector: 'ul > li' }, - { selector: 'li > a' }, - { selector: 'h2 + p + ul + p' }, - { selector: 'h2 + p + ul' }, - { selector: 'p + ul + p' }, - { selector: 'li + li' }, - { selector: 'h2 + ul' }, - { selector: 'h2 + p' }, - { selector: 'ul + p' }, - { selector: 'p + p' }, - { selector: 'h2 ~ p ~ ul ~ p' }, - { selector: 'h2 ~ p ~ ul' }, - { selector: 'p ~ ul ~ p' }, - { selector: 'li ~ li' }, - { selector: 'h2 ~ ul' }, - { selector: 'h2 ~ p' }, - { selector: 'ul ~ p' }, - { selector: 'p ~ p' }, + { select: 'div ul li a span' }, + { select: 'div ul li a' }, + { select: 'div ul li' }, + { select: 'div ul' }, + { select: 'ul li a span' }, + { select: 'ul li a' }, + { select: 'ul li' }, + { select: 'li a' }, + { select: 'div > ul > li > a > span' }, + { select: 'div > ul > li > a' }, + { select: 'div > ul > li' }, + { select: 'div > ul' }, + { select: 'ul > li > a > span' }, + { select: 'ul > li > a' }, + { select: 'ul > li' }, + { select: 'li > a' }, + { select: 'h2 + p + ul + p' }, + { select: 'h2 + p + ul' }, + { select: 'p + ul + p' }, + { select: 'li + li' }, + { select: 'h2 + ul' }, + { select: 'h2 + p' }, + { select: 'ul + p' }, + { select: 'p + p' }, + { select: 'h2 ~ p ~ ul ~ p' }, + { select: 'h2 ~ p ~ ul' }, + { select: 'p ~ ul ~ p' }, + { select: 'li ~ li' }, + { select: 'h2 ~ ul' }, + { select: 'h2 ~ p' }, + { select: 'ul ~ p' }, + { select: 'p ~ p' }, ], }, { @@ -123,25 +123,25 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'div:last-child' }, - { selector: 'div:only-child' }, - { selector: 'div:first-child' }, - { selector: 'div:nth-child(n)' }, - { selector: 'div:nth-child(3)' }, - { selector: 'div:nth-child(n+3)' }, - { selector: 'div:nth-child(+n-3)' }, - { selector: 'div:nth-child(2n)' }, - { selector: 'div:nth-child(odd)' }, - { selector: 'div:nth-child(2n+1)' }, - { selector: 'div:nth-child(even)' }, - { selector: 'div:nth-last-child(n)' }, - { selector: 'div:nth-last-child(3)' }, - { selector: 'div:nth-last-child(n+3)' }, - { selector: 'div:nth-last-child(+n-3)' }, - { selector: 'div:nth-last-child(2n)' }, - { selector: 'div:nth-last-child(odd)' }, - { selector: 'div:nth-last-child(2n+1)' }, - { selector: 'div:nth-last-child(even)' }, + { select: 'div:last-child' }, + { select: 'div:only-child' }, + { select: 'div:first-child' }, + { select: 'div:nth-child(n)' }, + { select: 'div:nth-child(3)' }, + { select: 'div:nth-child(n+3)' }, + { select: 'div:nth-child(+n-3)' }, + { select: 'div:nth-child(2n)' }, + { select: 'div:nth-child(odd)' }, + { select: 'div:nth-child(2n+1)' }, + { select: 'div:nth-child(even)' }, + { select: 'div:nth-last-child(n)' }, + { select: 'div:nth-last-child(3)' }, + { select: 'div:nth-last-child(n+3)' }, + { select: 'div:nth-last-child(+n-3)' }, + { select: 'div:nth-last-child(2n)' }, + { select: 'div:nth-last-child(odd)' }, + { select: 'div:nth-last-child(2n+1)' }, + { select: 'div:nth-last-child(even)' }, ], }, { @@ -149,25 +149,25 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'div:last-of-type' }, - { selector: 'div:only-of-type' }, - { selector: 'div:first-of-type' }, - { selector: 'div:nth-of-type(n)' }, - { selector: 'div:nth-of-type(3)' }, - { selector: 'div:nth-of-type(n+3)' }, - { selector: 'div:nth-of-type(+n-3)' }, - { selector: 'div:nth-of-type(2n)' }, - { selector: 'div:nth-of-type(odd)' }, - { selector: 'div:nth-of-type(2n+1)' }, - { selector: 'div:nth-of-type(even)' }, - { selector: 'div:nth-last-of-type(n)' }, - { selector: 'div:nth-last-of-type(3)' }, - { selector: 'div:nth-last-of-type(n+3)' }, - { selector: 'div:nth-last-of-type(+n-3)' }, - { selector: 'div:nth-last-of-type(2n)' }, - { selector: 'div:nth-last-of-type(odd)' }, - { selector: 'div:nth-last-of-type(2n+1)' }, - { selector: 'div:nth-last-of-type(even)' }, + { select: 'div:last-of-type' }, + { select: 'div:only-of-type' }, + { select: 'div:first-of-type' }, + { select: 'div:nth-of-type(n)' }, + { select: 'div:nth-of-type(3)' }, + { select: 'div:nth-of-type(n+3)' }, + { select: 'div:nth-of-type(+n-3)' }, + { select: 'div:nth-of-type(2n)' }, + { select: 'div:nth-of-type(odd)' }, + { select: 'div:nth-of-type(2n+1)' }, + { select: 'div:nth-of-type(even)' }, + { select: 'div:nth-last-of-type(n)' }, + { select: 'div:nth-last-of-type(3)' }, + { select: 'div:nth-last-of-type(n+3)' }, + { select: 'div:nth-last-of-type(+n-3)' }, + { select: 'div:nth-last-of-type(2n)' }, + { select: 'div:nth-last-of-type(odd)' }, + { select: 'div:nth-last-of-type(2n+1)' }, + { select: 'div:nth-last-of-type(even)' }, ], }, { @@ -175,22 +175,22 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'div, ul, li, a' }, - { selector: 'div, ul, li' }, - { selector: 'ul, li, a' }, - { selector: 'div, li' }, - { selector: 'div, a' }, - { selector: 'ul, li' }, - { selector: 'ul, a' }, - { selector: 'li, a' }, - { selector: 'div, p, a, em' }, - { selector: 'div, p, em' }, - { selector: 'div, p, a' }, - { selector: 'div, p' }, - { selector: 'div, a' }, - { selector: 'p, em' }, - { selector: 'a, em' }, - { selector: 'p, a' }, + { select: 'div, ul, li, a' }, + { select: 'div, ul, li' }, + { select: 'ul, li, a' }, + { select: 'div, li' }, + { select: 'div, a' }, + { select: 'ul, li' }, + { select: 'ul, a' }, + { select: 'li, a' }, + { select: 'div, p, a, em' }, + { select: 'div, p, em' }, + { select: 'div, p, a' }, + { select: 'div, p' }, + { select: 'div, a' }, + { select: 'p, em' }, + { select: 'a, em' }, + { select: 'p, a' }, ], }, @@ -199,25 +199,25 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'div:not(:last-child)' }, - { selector: 'div:not(:only-child)' }, - { selector: 'div:not(:first-child)' }, - { selector: 'div:not(:nth-child(n))' }, - { selector: 'div:not(:nth-child(3))' }, - { selector: 'div:not(:nth-child(n+3))' }, - { selector: 'div:not(:nth-child(+n-3))' }, - { selector: 'div:not(:nth-child(2n))' }, - { selector: 'div:not(:nth-child(odd))' }, - { selector: 'div:not(:nth-child(2n+1))' }, - { selector: 'div:not(:nth-child(even))' }, - { selector: 'div:not(:nth-last-child(n))' }, - { selector: 'div:not(:nth-last-child(3))' }, - { selector: 'div:not(:nth-last-child(n+3))' }, - { selector: 'div:not(:nth-last-child(+n-3))' }, - { selector: 'div:not(:nth-last-child(2n))' }, - { selector: 'div:not(:nth-last-child(odd))' }, - { selector: 'div:not(:nth-last-child(2n+1))' }, - { selector: 'div:not(:nth-last-child(even))' }, + { select: 'div:not(:last-child)' }, + { select: 'div:not(:only-child)' }, + { select: 'div:not(:first-child)' }, + { select: 'div:not(:nth-child(n))' }, + { select: 'div:not(:nth-child(3))' }, + { select: 'div:not(:nth-child(n+3))' }, + { select: 'div:not(:nth-child(+n-3))' }, + { select: 'div:not(:nth-child(2n))' }, + { select: 'div:not(:nth-child(odd))' }, + { select: 'div:not(:nth-child(2n+1))' }, + { select: 'div:not(:nth-child(even))' }, + { select: 'div:not(:nth-last-child(n))' }, + { select: 'div:not(:nth-last-child(3))' }, + { select: 'div:not(:nth-last-child(n+3))' }, + { select: 'div:not(:nth-last-child(+n-3))' }, + { select: 'div:not(:nth-last-child(2n))' }, + { select: 'div:not(:nth-last-child(odd))' }, + { select: 'div:not(:nth-last-child(2n+1))' }, + { select: 'div:not(:nth-last-child(even))' }, ], }, { @@ -225,25 +225,25 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: 'div:not(:last-of-type)' }, - { selector: 'div:not(:only-of-type)' }, - { selector: 'div:not(:first-of-type)' }, - { selector: 'div:not(:nth-of-type(n))' }, - { selector: 'div:not(:nth-of-type(3))' }, - { selector: 'div:not(:nth-of-type(n+3))' }, - { selector: 'div:not(:nth-of-type(+n-3))' }, - { selector: 'div:not(:nth-of-type(2n))' }, - { selector: 'div:not(:nth-of-type(odd))' }, - { selector: 'div:not(:nth-of-type(2n+1))' }, - { selector: 'div:not(:nth-of-type(even))' }, - { selector: 'div:not(:nth-last-of-type(n))' }, - { selector: 'div:not(:nth-last-of-type(3))' }, - { selector: 'div:not(:nth-last-of-type(n+3))' }, - { selector: 'div:not(:nth-last-of-type(+n-3))' }, - { selector: 'div:not(:nth-last-of-type(2n))' }, - { selector: 'div:not(:nth-last-of-type(odd))' }, - { selector: 'div:not(:nth-last-of-type(2n+1))' }, - { selector: 'div:not(:nth-last-of-type(even))' }, + { select: 'div:not(:last-of-type)' }, + { select: 'div:not(:only-of-type)' }, + { select: 'div:not(:first-of-type)' }, + { select: 'div:not(:nth-of-type(n))' }, + { select: 'div:not(:nth-of-type(3))' }, + { select: 'div:not(:nth-of-type(n+3))' }, + { select: 'div:not(:nth-of-type(+n-3))' }, + { select: 'div:not(:nth-of-type(2n))' }, + { select: 'div:not(:nth-of-type(odd))' }, + { select: 'div:not(:nth-of-type(2n+1))' }, + { select: 'div:not(:nth-of-type(even))' }, + { select: 'div:not(:nth-last-of-type(n))' }, + { select: 'div:not(:nth-last-of-type(3))' }, + { select: 'div:not(:nth-last-of-type(n+3))' }, + { select: 'div:not(:nth-last-of-type(+n-3))' }, + { select: 'div:not(:nth-last-of-type(2n))' }, + { select: 'div:not(:nth-last-of-type(odd))' }, + { select: 'div:not(:nth-last-of-type(2n+1))' }, + { select: 'div:not(:nth-last-of-type(even))' }, ], }, { @@ -251,35 +251,35 @@ runScenarios('speed', 'normal', [ html, htmlMode: 'document', cases: [ - { selector: ':lang(fr)' }, - { selector: ':visited' }, - { selector: ':target' }, + { select: ':lang(fr)' }, + { select: ':visited' }, + { select: ':target' }, // { selector: ':active' }, - { selector: ':hover' }, + { select: ':hover' }, // { selector: ':focus' }, - { selector: ':empty' }, - { selector: ':link' }, - { selector: ':not(:lang(fr))' }, - { selector: ':not(:visited)' }, - { selector: ':not(:target)' }, + { select: ':empty' }, + { select: ':link' }, + { select: ':not(:lang(fr))' }, + { select: ':not(:visited)' }, + { select: ':not(:target)' }, // { selector: ':not(:active)' }, - { selector: ':not(:hover)' }, + { select: ':not(:hover)' }, // { selector: ':not(:focus)' }, - { selector: ':not(:empty)' }, - { selector: ':not(:link)' }, + { select: ':not(:empty)' }, + { select: ':not(:link)' }, ], }, { // active/focus does not seem to be stable. also, not supported in any of the browsers. name: 'matches_and_negation_dynamic_and_user_actions - active/focus', - modifier: 'fixme', + status: 'fixme', html, htmlMode: 'document', cases: [ - { selector: ':active' }, - { selector: ':focus' }, - { selector: ':not(:active)' }, - { selector: ':not(:focus)' }, + { select: ':active' }, + { select: ':focus' }, + { select: ':not(:active)' }, + { select: ':not(:focus)' }, ], }, diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts index 60eae9b..94327eb 100644 --- a/test_new/browser/w3c.test.ts +++ b/test_new/browser/w3c.test.ts @@ -1,5 +1,5 @@ import { expect } from 'playwright/test'; -import { runScenarios } from './harness'; +import { runScenarios } from './harness/scenarios'; import { run } from 'node:test'; runScenarios('w3c iframes', 'normal', [ @@ -205,7 +205,7 @@ runScenarios('w3c iframes', 'normal', [ runScenarios('w3c', 'normal', [ { name: 'syntax', - modifier: 'fixme', + status: 'fixme', html: ` Selectors: syntax of case-sensitivity attribute selector @@ -217,74 +217,74 @@ runScenarios('w3c', 'normal', [ `, cases: [ - { selector: "[foo='BAR'] /* sanity check (valid) */" }, - { selector: "[foo='bar' i]" }, - { selector: "[foo='bar' I]" }, - { selector: "[foo=bar i]" }, - { selector: '[foo="bar" i]' }, - { selector: "[foo='bar'i]" }, - { selector: "[foo='bar'i ]" }, - { selector: "[foo='bar' i ]" }, - { selector: "[foo='bar' /**/ i]" }, - { selector: "[foo='bar' i /**/ ]" }, - { selector: "[foo='bar'/**/i/**/]" }, - { selector: "[foo=bar/**/i]" }, - { selector: "[foo='bar'\ti\t] /* \\t */" }, - { selector: "[foo='bar'\ni\n] /* \\n */" }, - { selector: "[foo='bar'\ri\r] /* \\r */" }, - { selector: "[foo='bar' \\i]" }, - { selector: "[foo='bar' \\69]" }, - { selector: "[foo~='bar' i]" }, - { selector: "[foo^='bar' i]" }, - { selector: "[foo$='bar' i]" }, - { selector: "[foo*='bar' i]" }, - { selector: "[foo|='bar' i]" }, - { selector: "[|foo='bar' i]" }, - { selector: "[*|foo='bar' i]" }, - - { selector: "[foo[ /* sanity check (invalid) */", expect: { throws: true } }, - { selector: "[foo='bar' i i]", expect: { throws: true } }, - { selector: "[foo i ='bar']", expect: { throws: true } }, - { selector: "[foo= i 'bar']", expect: { throws: true } }, - { selector: "[i foo='bar']", expect: { throws: true } }, - { selector: "[foo='bar' i\u0000] /* \\0 */", expect: { throws: true } }, - { selector: "[foo='bar' \u0130]", expect: { throws: true } }, - { selector: "[foo='bar' \u0131]", expect: { throws: true } }, - { selector: "[foo='bar' ii]", expect: { throws: true } }, - { selector: "[foo='bar' ij]", expect: { throws: true } }, - { selector: "[foo='bar' j]", expect: { throws: true } }, - { selector: "[foo='bar' \\\\i]", expect: { throws: true } }, - { selector: "[foo='bar' \\\\69]", expect: { throws: true } }, - { selector: "[foo='bar' i()]", expect: { throws: true } }, - { selector: "[foo='bar' i ()]", expect: { throws: true } }, - { selector: "[foo='bar' () i]", expect: { throws: true } }, - { selector: "[foo='bar' (i)]", expect: { throws: true } }, - { selector: "[foo='bar' i []]", expect: { throws: true } }, - { selector: "[foo='bar' [] i]", expect: { throws: true } }, - { selector: "[foo='bar' [i]]", expect: { throws: true } }, - { selector: "[foo='bar' i {}]", expect: { throws: true } }, - { selector: "[foo='bar' {} i]", expect: { throws: true } }, - { selector: "[foo='bar' {i}]", expect: { throws: true } }, - { selector: "[foo='bar' 1i]", expect: { throws: true } }, - { selector: "[foo='bar' 1]", expect: { throws: true } }, - { selector: "[foo='bar' 'i']", expect: { throws: true } }, - { selector: "[foo='bar' url(i)]", expect: { throws: true } }, - { selector: "[foo='bar' ,i]", expect: { throws: true } }, - { selector: "[foo='bar' i,]", expect: { throws: true } }, - { selector: "[foo='bar']i", expect: { throws: true } }, - { selector: "[foo='bar' |i]", expect: { throws: true } }, - { selector: "[foo='bar' \\|i]", expect: { throws: true } }, - { selector: "[foo='bar' *|i]", expect: { throws: true } }, - { selector: "[foo='bar' \\*|i]", expect: { throws: true } }, - { selector: "[foo='bar' *]", expect: { throws: true } }, - { selector: "[foo='bar' \\*]", expect: { throws: true } }, - { selector: "[foo i]", expect: { throws: true } }, - { selector: "[foo/**/i]", expect: { throws: true } }, + { select: "[foo='BAR'] /* sanity check (valid) */" }, + { select: "[foo='bar' i]" }, + { select: "[foo='bar' I]" }, + { select: "[foo=bar i]" }, + { select: '[foo="bar" i]' }, + { select: "[foo='bar'i]" }, + { select: "[foo='bar'i ]" }, + { select: "[foo='bar' i ]" }, + { select: "[foo='bar' /**/ i]" }, + { select: "[foo='bar' i /**/ ]" }, + { select: "[foo='bar'/**/i/**/]" }, + { select: "[foo=bar/**/i]" }, + { select: "[foo='bar'\ti\t] /* \\t */" }, + { select: "[foo='bar'\ni\n] /* \\n */" }, + { select: "[foo='bar'\ri\r] /* \\r */" }, + { select: "[foo='bar' \\i]" }, + { select: "[foo='bar' \\69]" }, + { select: "[foo~='bar' i]" }, + { select: "[foo^='bar' i]" }, + { select: "[foo$='bar' i]" }, + { select: "[foo*='bar' i]" }, + { select: "[foo|='bar' i]" }, + { select: "[|foo='bar' i]" }, + { select: "[*|foo='bar' i]" }, + + { select: "[foo[ /* sanity check (invalid) */", expect: { throws: true } }, + { select: "[foo='bar' i i]", expect: { throws: true } }, + { select: "[foo i ='bar']", expect: { throws: true } }, + { select: "[foo= i 'bar']", expect: { throws: true } }, + { select: "[i foo='bar']", expect: { throws: true } }, + { select: "[foo='bar' i\u0000] /* \\0 */", expect: { throws: true } }, + { select: "[foo='bar' \u0130]", expect: { throws: true } }, + { select: "[foo='bar' \u0131]", expect: { throws: true } }, + { select: "[foo='bar' ii]", expect: { throws: true } }, + { select: "[foo='bar' ij]", expect: { throws: true } }, + { select: "[foo='bar' j]", expect: { throws: true } }, + { select: "[foo='bar' \\\\i]", expect: { throws: true } }, + { select: "[foo='bar' \\\\69]", expect: { throws: true } }, + { select: "[foo='bar' i()]", expect: { throws: true } }, + { select: "[foo='bar' i ()]", expect: { throws: true } }, + { select: "[foo='bar' () i]", expect: { throws: true } }, + { select: "[foo='bar' (i)]", expect: { throws: true } }, + { select: "[foo='bar' i []]", expect: { throws: true } }, + { select: "[foo='bar' [] i]", expect: { throws: true } }, + { select: "[foo='bar' [i]]", expect: { throws: true } }, + { select: "[foo='bar' i {}]", expect: { throws: true } }, + { select: "[foo='bar' {} i]", expect: { throws: true } }, + { select: "[foo='bar' {i}]", expect: { throws: true } }, + { select: "[foo='bar' 1i]", expect: { throws: true } }, + { select: "[foo='bar' 1]", expect: { throws: true } }, + { select: "[foo='bar' 'i']", expect: { throws: true } }, + { select: "[foo='bar' url(i)]", expect: { throws: true } }, + { select: "[foo='bar' ,i]", expect: { throws: true } }, + { select: "[foo='bar' i,]", expect: { throws: true } }, + { select: "[foo='bar']i", expect: { throws: true } }, + { select: "[foo='bar' |i]", expect: { throws: true } }, + { select: "[foo='bar' \\|i]", expect: { throws: true } }, + { select: "[foo='bar' *|i]", expect: { throws: true } }, + { select: "[foo='bar' \\*|i]", expect: { throws: true } }, + { select: "[foo='bar' *]", expect: { throws: true } }, + { select: "[foo='bar' \\*]", expect: { throws: true } }, + { select: "[foo i]", expect: { throws: true } }, + { select: "[foo/**/i]", expect: { throws: true } }, ], }, { name: 'semantics', - modifier: 'fixme', + status: 'fixme', html: `
    `, cases: [], setupPage: async (page) => { @@ -415,25 +415,25 @@ runScenarios('w3c', 'normal', [ name: 'child-indexed-pseudo-class', html: `
    `, cases: [ - { selector: 'div:first-child' }, - { selector: 'div:last-child' }, - { selector: 'div:only-child' }, - { selector: 'div:first-of-type' }, - { selector: 'div:last-of-type' }, - { selector: 'div:only-of-type' }, - { selector: 'div:nth-child(1)' }, - { selector: 'div:nth-child(n)' }, - { selector: 'div:nth-last-child(1)' }, - { selector: 'div:nth-last-child(n)' }, - { selector: 'div:nth-of-type(1)' }, - { selector: 'div:nth-of-type(n)' }, - { selector: 'div:nth-last-of-type(1)' }, - { selector: 'div:nth-last-of-type(n)' }, - - { selector: 'div:nth-child(2)', expect: { count: 0 } }, - { selector: 'div:nth-last-child(2)', expect: { count: 0 } }, - { selector: 'div:nth-of-type(2)', expect: { count: 0 } }, - { selector: 'div:nth-last-of-type(2)', expect: { count: 0 } }, + { select: 'div:first-child' }, + { select: 'div:last-child' }, + { select: 'div:only-child' }, + { select: 'div:first-of-type' }, + { select: 'div:last-of-type' }, + { select: 'div:only-of-type' }, + { select: 'div:nth-child(1)' }, + { select: 'div:nth-child(n)' }, + { select: 'div:nth-last-child(1)' }, + { select: 'div:nth-last-child(n)' }, + { select: 'div:nth-of-type(1)' }, + { select: 'div:nth-of-type(n)' }, + { select: 'div:nth-last-of-type(1)' }, + { select: 'div:nth-last-of-type(n)' }, + + { select: 'div:nth-child(2)', expect: { count: 0 } }, + { select: 'div:nth-last-child(2)', expect: { count: 0 } }, + { select: 'div:nth-of-type(2)', expect: { count: 0 } }, + { select: 'div:nth-last-of-type(2)', expect: { count: 0 } }, ], }, @@ -455,10 +455,10 @@ runScenarios('w3c', 'normal', [ `, cases: [ - { selector: 'meta[charset="utf-8"', expect: { ids: ['expected'] } }, - { selector: 'meta[charset="utf-8', expect: { ids: ['expected'] } }, - { selector: 'span:not([class])', root: { kind: 'id', value: 'container' }, expect: { count: 1 } }, - { selector: 'span:not([class)', root: { kind: 'id', value: 'container' }, expect: { throws: true } }, + { select: 'meta[charset="utf-8"', expect: { ids: ['expected'] } }, + { select: 'meta[charset="utf-8', expect: { ids: ['expected'] } }, + { select: 'span:not([class])', scope: { by: 'id', id: 'container' }, expect: { count: 1 } }, + { select: 'span:not([class)', scope: { by: 'id', id: 'container' }, expect: { throws: true } }, ], }, ]); diff --git a/test_new/global.d.ts b/test_new/global.d.ts index 15fbe5f..c399ecb 100644 --- a/test_new/global.d.ts +++ b/test_new/global.d.ts @@ -1,6 +1,11 @@ +import type { PwHelpers } from './browser/harness/browser'; + export {}; declare global { + interface Window { + __pwHelpers: PwHelpers; + } const NW: { Dom: { select( diff --git a/test_new/utils/type.ts b/test_new/utils/type.ts new file mode 100644 index 0000000..c4bec0f --- /dev/null +++ b/test_new/utils/type.ts @@ -0,0 +1,3 @@ +export function assertNever(x: never): never { + throw new Error(`Unexpected key: ${x}`); +} From d069d101abab9cc155272267053cedd1a6f14e8f Mon Sep 17 00:00:00 2001 From: koal44 Date: Fri, 10 Apr 2026 00:27:02 -0700 Subject: [PATCH 08/19] expand browser harness for multiple selector API cases --- test_new/browser/harness/browser.ts | 242 ++++++++++++++++++++------ test_new/browser/harness/scenarios.ts | 106 +++++++---- test_new/browser/harness/select.ts | 36 ---- test_new/browser/jquery.test.ts | 8 +- test_new/browser/prototype.test.ts | 48 ++--- test_new/browser/scope.test.ts | 26 +-- test_new/browser/scotch.test.ts | 44 ++--- test_new/browser/w3c.test.ts | 4 +- test_new/global.d.ts | 20 ++- test_new/utils/type.ts | 2 + 10 files changed, 346 insertions(+), 190 deletions(-) delete mode 100644 test_new/browser/harness/select.ts diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts index 4443d8a..851518a 100644 --- a/test_new/browser/harness/browser.ts +++ b/test_new/browser/harness/browser.ts @@ -1,24 +1,23 @@ -import type { Engine, EquivalentTo, ScopeRef } from "./scenarios"; +import type { Engine, EquivalentCase, ContextRef } from "./scenarios"; export interface PwHelpers { - resolveScope(ref: ScopeRef | undefined): ParentNode | null; + resolveContext(ref: ContextRef | undefined): QueryContext | null; runQuery(query: () => Element[]): QueryResult; compareQueryResults( a: NamedQueryResult, b: NamedQueryResult ): string | undefined; toEngineResult(res: QueryResult): EngineResult; - getResults( - engines: EngineQueries, - query: string, - ref?: ScopeRef, - equivTo?: EquivalentTo - ): EngineQueryResults; + getResults(queryFn: EngineQuery, query: string, ref?: ContextRef): EngineAndQueryResult; + getEngineQuery(c: EquivalentCase, n: Engine): EngineQuery; + getCaseQuery(c: EquivalentCase): string; + getCaseLabel(c: EquivalentCase, n: Engine): string; } export type EvalResult = { info: string; mismatchMsg?: string; + equivMismatchMsg?: string; } & Record; export type EngineResult = { @@ -26,16 +25,30 @@ export type EngineResult = { ids: string[]; classes: string[]; threw: boolean; - equivalentToFailMsg?: string; }; -export type EngineQueries = Record; -export type EngineQuery = (query: string, scope: ParentNode) => () => Element[]; -export type EngineQueryResults = Record; +export type EngineQuery = (query: string, ctx: QueryContext) => () => Element[]; +export type EngineAndQueryResult = { queryResult: QueryResult; engineResult: EngineResult; }; export type NamedQueryResult = { name: string; result: QueryResult; }; export type QueryResult = { elements: Element[]; error: string }; export function installBrowserHelpers(): void { + function assertNever(x: never): never { + throw new Error(`Unexpected key: ${x}`); + } + + function isElement(x: unknown): x is Element { + return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 1; + } + + // function isDocument(x: unknown): x is Document { + // return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 9; + // } + + function isDocFrag(x: unknown): x is DocumentFragment { + return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 11; + } + function runQuery(query: () => Element[]) { try { return { elements: query(), error: '' }; @@ -46,12 +59,9 @@ export function installBrowserHelpers(): void { function describe(el: Element | undefined) { if (!el) return '(missing)'; - return { - tag: el.tagName.toLowerCase(), - id: el.id || null, - className: el.className || '', - html: el.outerHTML.replace(/\s+/g, ' ').slice(0, 120), - }; + const id = el.getAttribute('id'); + const className = el.getAttribute('class'); + return `<${el.tagName.toLowerCase()}${id ? ` id='${id}'` : ''}${className ? ` class='${className}'` : ''}>`; } function compareQueryResults(a: NamedQueryResult, b: NamedQueryResult): string | undefined { @@ -60,8 +70,8 @@ export function installBrowserHelpers(): void { return `Both threw:\n ${a.name} error: ${a.result.error}\n ${b.name} error: ${b.result.error}`; } return a.result.error - ? `${a.name} threw while ${b.name} did not: ${a.result.error}` - : `${b.name} threw while ${a.name} did not: ${b.result.error}`; + ? `Mismatch\n ${a.name} threw while ${b.name} did not.\n error: ${a.result.error}` + : `Mismatch\n ${b.name} threw while ${a.name} did not.\n error: ${b.result.error}`; } const aElems = a.result.elements; @@ -69,7 +79,7 @@ export function installBrowserHelpers(): void { let mismatchMsg: string | undefined; if (aElems.length !== bElems.length) { - mismatchMsg = `Count mismatch: ${a.name}=${aElems.length}, ${b.name}=${bElems.length}`; + mismatchMsg = `Count mismatch:\n ${a.name} = ${aElems.length}\n ${b.name} = ${bElems.length}`; } const maxLen = Math.max(aElems.length, bElems.length); @@ -77,8 +87,8 @@ export function installBrowserHelpers(): void { if (aElems[i] !== bElems[i]) { mismatchMsg = mismatchMsg ? mismatchMsg + '\n' : ''; mismatchMsg += `Element mismatch at index ${i}:\n` + - `${a.name}[${i}] = ${JSON.stringify(describe(aElems[i]))}\n` + - `${b.name}[${i}] = ${JSON.stringify(describe(bElems[i]))}`; + ` ${a.name}[${i}] = ${describe(aElems[i])}\n` + + ` ${b.name}[${i}] = ${describe(bElems[i])}`; break; } } @@ -86,7 +96,7 @@ export function installBrowserHelpers(): void { return mismatchMsg; } - function resolveScope(ref?: ScopeRef): ParentNode | null { + function resolveContext(ref?: ContextRef): QueryContext | null { return !ref || ref.by === 'document' ? document : ref.by === 'id' ? document.getElementById(ref.id) : ref.by === 'first' ? document.querySelector(ref.selector) @@ -102,43 +112,177 @@ export function installBrowserHelpers(): void { }; } - function getResults(engines: EngineQueries, query: string, ref?: ScopeRef, equivTo?: EquivalentTo): EngineQueryResults{ - const scope = resolveScope(ref); - const equivScope = resolveScope(equivTo?.scope); + function getResults(queryFn: EngineQuery, query: string, ref?: ContextRef): EngineAndQueryResult { + const context = resolveContext(ref); + + const queryResult: QueryResult = context + ? runQuery(queryFn(query, context)) + : { elements: [], error: `Could not resolve context from ref:: ${JSON.stringify(ref)}` }; - const build = (name: Engine, queryFn: EngineQuery) => { - const queryResult: QueryResult = scope - ? runQuery(queryFn(query, scope)) - : { elements: [], error: `Scope target not found for ref: ${JSON.stringify(ref)}` }; + const engineResult = toEngineResult(queryResult); + return { queryResult, engineResult }; + } - const engineResult = toEngineResult(queryResult); + function getEngineQuery(c: EquivalentCase, ng: Engine): EngineQuery { + switch (true) { + case 'select' in c: + if (ng === 'native') return (query, ctx) => () => [...ctx.querySelectorAll(query)]; + if (ng === 'nw') return (query, ctx) => () => NW.Dom.select(query, ctx); + break; - if (equivTo) { - const equivQuery = equivTo.search; - const equivRes: QueryResult = equivScope - ? runQuery(queryFn(equivQuery, equivScope)) - : { elements: [], error: `Equivalent scope target not found: ${JSON.stringify(equivTo.scope)}` }; + case 'first' in c: + if (ng === 'native') { + return (query, ctx) => () => { + const el = ctx.querySelector(query); + return el ? [el] : []; + }; + } + if (ng === 'nw') { + return (query, ctx) => () => { + const el = NW.Dom.first(query, ctx); + return el ? [el] : []; + }; + } + break; - engineResult.equivalentToFailMsg = compareQueryResults( - { name: `${name}(${query})`, result: queryResult }, - { name: `${name}(${equivQuery})`, result: equivRes }, - ); - } + case 'byTag' in c: + if (ng === 'native') { + return (query, ctx) => () => + isDocFrag(ctx) + ? [...ctx.querySelectorAll(query)] + : [...ctx.getElementsByTagName(query)]; + } + if (ng === 'nw') return (query, ctx) => () => NW.Dom.byTag(query, ctx); + break; - return { queryResult, engineResult }; - }; + case 'byClass' in c: + if (ng === 'native') { + return (query, ctx) => () => + isDocFrag(ctx) + ? [...ctx.querySelectorAll(`.${query}`)] + : [...ctx.getElementsByClassName(query)]; + } + if (ng === 'nw') return (query, ctx) => () => NW.Dom.byClass(query, ctx); + break; - return { - native: build('native', engines.native), - nw: build('nw', engines.nw), - }; + case 'byId' in c: + if (ng === 'native') { + return (query, ctx) => () => { + const el = isElement(ctx) + ? ctx.querySelector(`#${CSS.escape(query)}`) + : ctx.getElementById(query); + return el ? [el] : []; + }; + } + if (ng === 'nw') return (query, ctx) => () => NW.Dom.byId(query, ctx); + break; + + case 'match' in c: + if (ng === 'native') { + return (query, ctx) => () => { + if (!isElement(ctx)) throw new Error(`Context for 'match' case must be an Element`); + const el = ctx; + return el.matches(query) ? [el] : []; + }; + } + if (ng === 'nw') { + return (query, ctx) => () => { + if (!isElement(ctx)) throw new Error(`Context for 'match' case must be an Element`); + const el = ctx; + return NW.Dom.match(query, el) ? [el] : []; + }; + } + break; + + case 'closest' in c: + if (ng === 'native') { + return (query, ctx) => () => { + if (!isElement(ctx)) throw new Error(`Context for 'closest' case must be an Element`); + const el = ctx; + const hit = el.closest(query); + return hit ? [hit] : []; + }; + } + if (ng === 'nw') { + return (query, ctx) => () => { + if (!isElement(ctx)) throw new Error(`Context for 'closest' case must be an Element`); + const el = ctx; + const hit = NW.Dom.closest(query, el); + return hit ? [hit] : []; + }; + } + break; + + default: + throw assertNever(c); + } + + throw assertNever(ng); + } + + function getCaseQuery(c: EquivalentCase): string { + switch (true) { + case 'select' in c: return c.select; + case 'first' in c: return c.first; + case 'byTag' in c: return c.byTag; + case 'byClass' in c: return c.byClass; + case 'byId' in c: return c.byId; + case 'match' in c: return c.match; + case 'closest' in c: return c.closest; + default: throw assertNever(c); + } + } + + function getCaseLabel(c: EquivalentCase, engine: Engine): string { + switch (true) { + case 'select' in c: + return engine === 'native' + ? `querySelectorAll(${c.select})` + : `NW.Dom.select(${c.select})`; + + case 'first' in c: + return engine === 'native' + ? `querySelector(${c.first})` + : `NW.Dom.first(${c.first})`; + + case 'byTag' in c: + return engine === 'native' + ? `byTag:${c.byTag}` + : `NW.Dom.byTag(${c.byTag})`; + + case 'byClass' in c: + return engine === 'native' + ? `byClass:${c.byClass}` + : `NW.Dom.byClass(${c.byClass})`; + + case 'byId' in c: + return engine === 'native' + ? `byId:${c.byId}` + : `NW.Dom.byId(${c.byId})`; + + case 'match' in c: + return engine === 'native' + ? `matches(${c.match})` + : `NW.Dom.match(${JSON.stringify(c.match)})`; + + case 'closest' in c: + return engine === 'native' + ? `closest(${c.closest})` + : `NW.Dom.closest(${c.closest})`; + + default: + throw assertNever(c); + } } window.__pwHelpers = { - resolveScope, + resolveContext, runQuery, compareQueryResults, toEngineResult, getResults, + getEngineQuery, + getCaseQuery, + getCaseLabel, }; }; diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index ae0fc1e..b959e38 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -1,18 +1,24 @@ import test, { chromium, expect, firefox, webkit } from '@playwright/test'; import type { Browser, Page } from '@playwright/test'; -import { evalSelectCase, type SelectCase } from './select'; -import { assertNever } from '../../utils/type'; -import { installBrowserHelpers, type EngineResult, type EvalResult } from './browser'; +import { assertNever, type DistributiveOmit } from '../../utils/type'; +import { installBrowserHelpers, type EngineResult, type EvalResult, type EngineQuery } from './browser'; -type Case = SelectCase; -const isSelectCase = (c: Case): c is SelectCase => 'select' in c; +export type SelectCase = { select: string; ref?: ContextRef; expect?: Expectation }; +export type ByIdCase = { byId: string; ref?: ContextRef; expect?: Expectation }; +export type ByTagCase = { byTag: string; ref?: ContextRef; expect?: Expectation }; +export type ByClassCase = { byClass: string; ref?: ContextRef; expect?: Expectation }; +export type MatchCase = { match: string; ref: ContextRef; expect?: Expectation }; +export type FirstCase = { first: string; ref?: ContextRef; expect?: Expectation }; +export type ClosestCase = { closest: string; ref: ContextRef; expect?: Expectation }; + +export type TestCase = SelectCase | ByIdCase | ByTagCase | ByClassCase | MatchCase | FirstCase | ClosestCase; export type Scenario = { name: string; html: string; htmlMode?: 'body' | 'document'; browsers?: BrowserName[]; - cases: Case[]; + cases: TestCase[]; setupPage?: (page: Page) => void | Promise; status?: TestStatus; }; @@ -27,13 +33,14 @@ export type Expectation = { includesClasses?: string[]; excludesClasses?: string[]; throws?: boolean; - equivalentTo?: EquivalentTo; + equivalentCase?: EquivalentCase; }; -export type EquivalentTo = { search: string; scope?: ScopeRef }; +export type EquivalentCase = DistributiveOmit; +// const foo: EquivalentCase = { select: 'div' }; // just to verify the type const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; -export type BrowserName = typeof BROWSER_NAMES[number]; +type BrowserName = typeof BROWSER_NAMES[number]; type DescribeStatus = 'normal' | 'skip' | 'only' | 'fixme'; type TestStatus = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; @@ -41,7 +48,7 @@ type TestStatus = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; export const ENGINES = ['native', 'nw'] as const; export type Engine = typeof ENGINES[number]; -export type ScopeRef = +export type ContextRef = | { by: 'document' } | { by: 'id'; id: string } | { by: 'first'; selector: string }; @@ -85,23 +92,58 @@ export function runScenarios(label: string, status: DescribeStatus, scenarios: S }); } -export async function runScenario(s: Scenario, pages: Record): Promise { +async function runScenario(s: Scenario, pages: Record): Promise { const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; for (const browserName of scenarioBrowsers) { const page = pages[browserName]; await setupPage(page, s); for (const c of s.cases) { - let result: EvalResult | undefined; - if (isSelectCase(c)) result = await evalSelectCase(page, c); - else throw new Error(`Unsupported case type in scenario "${s.name}" for browser "${browserName}"`); - + const result = await evalCase(page, c); const expectation = c.expect ?? {}; checkResult(result, expectation, browserName, s.name); } } } +async function evalCase(page: Page, testCase: TestCase): Promise { + return await page.evaluate((c) => { + const pw = window.__pwHelpers; + const query = pw.getCaseQuery(c); + // const info = pw.getCaseLabel(c); + const equivCase = c.expect?.equivalentCase; + + const nwFn: EngineQuery = pw.getEngineQuery(c, 'nw'); + const nativeFn: EngineQuery = pw.getEngineQuery(c, 'native'); + + const nwRes = pw.getResults(nwFn, query, c.ref); + const nativeRes = pw.getResults(nativeFn, query, c.ref); + + const mismatchMsg = pw.compareQueryResults( + { name: `native:${pw.getCaseLabel(c, 'native')}`, result: nativeRes.queryResult }, + { name: `nw:${pw.getCaseLabel(c, 'nw')}`, result: nwRes.queryResult }, + ); + + let equivMismatchMsg: string | undefined; + if (equivCase) { + const nwEquivFn = pw.getEngineQuery(equivCase, 'nw'); + const nwEquivQuery = pw.getCaseQuery(equivCase); + const nwEquivRes = pw.getResults(nwEquivFn, nwEquivQuery, equivCase.ref); + + equivMismatchMsg = pw.compareQueryResults( + { name: `nw:${pw.getCaseLabel(c, 'nw')}`, result: nwRes.queryResult }, + { name: `nwEquiv:${pw.getCaseLabel(equivCase, 'nw')}`, result: nwEquivRes.queryResult }, + ); + } + + return { + info: query, mismatchMsg, equivMismatchMsg, + native: nativeRes.engineResult, + nw: nwRes.engineResult + }; + }, testCase); +} + function getDescribeFn(mode?: DescribeStatus) { if (mode === 'skip') return test.describe.skip; if (mode === 'only') return test.describe.only; @@ -131,7 +173,7 @@ async function setupPage(page: Page, scenario: Scenario): Promise { if (scenario.setupPage) await scenario.setupPage(page); } -export function expectEngines( +function runEngineChecks( result: EvalResult, baseMsg: string, key: keyof EngineResult, @@ -151,20 +193,18 @@ export function expectEngines( case 'ids': return sameIds(r.ids, result[e].ids); case 'classes': return sameIds(r.classes, result[e].classes); case 'threw': return r.threw === result[e].threw; - case 'equivalentToFailMsg': return e === engine; default: assertNever(key); } }); let label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; - if (r.equivalentToFailMsg && key === 'equivalentToFailMsg') label += `\n${r.equivalentToFailMsg}`; check(r, label); } } function checkResult(result: EvalResult, expectation: Expectation, browserName: BrowserName, scenarioName: string): void { const allowMismatch = expectation.allowMismatch ?? false; - const msg = `[${browserName}] ${scenarioName} :: ${result.info}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; + const msg = `[${browserName}] :: ${scenarioName} :: ${result.info}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; if (expectation.throws) { expect(result.nw.threw, msg).toBe(true); @@ -173,23 +213,21 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: expect(result.nw.threw, msg).toBe(false); - const nativeThrew = result.native.threw; - if (expectation.count !== undefined) { - expectEngines(result, msg, 'count', (r, label) => { + runEngineChecks(result, msg, 'count', (r, label) => { expect(r.count, label).toEqual(expectation.count); }); } if (expectation.ids) { - expectEngines(result, msg, 'ids', (r, label) => { + runEngineChecks(result, msg, 'ids', (r, label) => { expect(r.ids, label).toEqual(expectation.ids); }); } if (expectation.includesIds) { for (const id of expectation.includesIds) { - expectEngines(result, msg, 'ids', (r, label) => { + runEngineChecks(result, msg, 'ids', (r, label) => { expect(r.ids, label).toContain(id); }); } @@ -197,21 +235,21 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: if (expectation.excludesIds) { for (const id of expectation.excludesIds) { - expectEngines(result, msg, 'ids', (r, label) => { + runEngineChecks(result, msg, 'ids', (r, label) => { expect(r.ids, label).not.toContain(id); }); } } if (expectation.classes) { - expectEngines(result, msg, 'classes', (r, label) => { + runEngineChecks(result, msg, 'classes', (r, label) => { expect(r.classes, label).toEqual(expectation.classes); }); } if (expectation.includesClasses) { for (const cls of expectation.includesClasses) { - expectEngines(result, msg, 'classes', (r, label) => { + runEngineChecks(result, msg, 'classes', (r, label) => { const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); expect(classTokens, label).toContain(cls); }); @@ -220,20 +258,20 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: if (expectation.excludesClasses) { for (const cls of expectation.excludesClasses) { - expectEngines(result, msg, 'classes', (r, label) => { + runEngineChecks(result, msg, 'classes', (r, label) => { const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); expect(classTokens, label).not.toContain(cls); }); } } - if (expectation.equivalentTo) { - expectEngines(result, msg, 'equivalentToFailMsg', (r, label) => { - expect(r.equivalentToFailMsg, label).toBeUndefined(); - }); + if (!allowMismatch && !result.native.threw) { + const mismatch = !!result.mismatchMsg; + expect(mismatch, `${msg}\n${result.mismatchMsg}`).toBe(false); } - if (!allowMismatch && !nativeThrew) { - expect(result.mismatchMsg, msg).toBeUndefined(); + if (expectation.equivalentCase) { + const equivMismatch = !!result.equivMismatchMsg; + expect(equivMismatch, `${msg}\n${result.equivMismatchMsg}`).toBe(false); } } diff --git a/test_new/browser/harness/select.ts b/test_new/browser/harness/select.ts deleted file mode 100644 index e9caa7c..0000000 --- a/test_new/browser/harness/select.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { Page } from '@playwright/test'; -import type { ScopeRef, Expectation } from './scenarios'; -import type { EngineQueries, EvalResult } from './browser'; - -export type SelectCase = { - select: string; - scope?: ScopeRef; - expect?: SelectExpectation; -}; - -type SelectExpectation = Expectation; - -export async function evalSelectCase(page: Page, selCase: SelectCase): Promise { - return await page.evaluate((c) => { - const pw = window.__pwHelpers; - const info = c.select; - - const engines: EngineQueries = { - native: (search, scope) => () => [...scope.querySelectorAll(search)], - nw: (search, scope) => () => NW.Dom.select(search, scope), - }; - - const res = pw.getResults(engines, c.select, c.scope, c.expect?.equivalentTo); - - const mismatchMsg = pw.compareQueryResults( - { name: 'native', result: res.native.queryResult }, - { name: 'nw', result: res.nw.queryResult }, - ); - - return { - info, mismatchMsg, - native: res.native.engineResult, - nw: res.nw.engineResult - }; - }, selCase); -} diff --git a/test_new/browser/jquery.test.ts b/test_new/browser/jquery.test.ts index 0f36410..5602b97 100644 --- a/test_new/browser/jquery.test.ts +++ b/test_new/browser/jquery.test.ts @@ -276,7 +276,7 @@ runScenarios('jquery', 'normal', [ { select: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, // scoped selection - { select: 'param', scope: { by: 'id', id: 'object1' }, expect: { count: 2 } }, + { select: 'param', ref: { by: 'id', id: 'object1' }, expect: { count: 2 } }, // Consistency checks for multiple selector groups { select: 'div p', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, @@ -531,9 +531,9 @@ runScenarios('jquery', 'normal', [ { select: 'div.foo > span > a', expect: { ids: [] } }, { select: '.container div:not(.excluded) div', expect: { ids: [] } }, - { select: '* > :first-child', scope: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, - { select: '* > :nth-child(1)', scope: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, - { select: '* > *:first-child', scope: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { select: '* > :first-child', ref: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { select: '* > :nth-child(1)', ref: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, + { select: '* > *:first-child', ref: { by: 'id', id: 'nothiddendiv' }, expect: { ids: ['nothiddendivchild'] } }, { select: '.fototab > .thumbnails > a', expect: { ids: [] } }, diff --git a/test_new/browser/prototype.test.ts b/test_new/browser/prototype.test.ts index 2c620db..dccc814 100644 --- a/test_new/browser/prototype.test.ts +++ b/test_new/browser/prototype.test.ts @@ -13,7 +13,7 @@ runScenarios('prototype 1', 'normal', [ { select: '.test_class', expect: { count: 2, ids: ['test_div_parent', 'test_div_child'] } }, { select: '.test_class', - scope: { by: 'id', id: 'test_div_parent' }, + ref: { by: 'id', id: 'test_div_parent' }, expect: { count: 1, ids: ['test_div_child'] } }, { select: '.non_existent', expect: { count: 0, ids: [] } }, @@ -191,8 +191,8 @@ runScenarios('prototype 2', 'normal', [ { select: '#p a, ul#list li', expect: { ids: ['link_1', 'link_2', 'item_1', 'item_2', 'item_3'] } }, // testSelectorWithTagNameAndAttributeExistence - { select: 'h1[class]', expect: { equivalentTo: { search: '#fixtures h1' } } }, - { select: 'h1[CLASS]', expect: { equivalentTo: { search: '#fixtures h1' } } }, + { select: 'h1[class]', expect: { equivalentCase: { select: '#fixtures h1' } } }, + { select: 'h1[CLASS]', expect: { equivalentCase: { select: '#fixtures h1' } } }, { select: 'li#item_3[class]', expect: { ids: ['item_3'] } }, // testSelectorWithTagNameAndSpecificAttributeValue @@ -204,14 +204,14 @@ runScenarios('prototype 2', 'normal', [ { select: 'a[class~=internal]', expect: { ids: ['link_1', 'link_2'] } }, // testSelectorWithAttributeAndNoTagName - { select: '[href]', scope: { by: 'first', selector: 'body' }, expect: { equivalentTo: { search: 'a[href]' } } }, - { select: '[class~=internal]', expect: { equivalentTo: { search: 'a[class~="internal"]' } } }, - { select: '[id]', expect: { equivalentTo: { search: '*[id]' } } }, + { select: '[href]', ref: { by: 'first', selector: 'body' }, expect: { equivalentCase: { select: 'a[href]' } } }, + { select: '[class~=internal]', expect: { equivalentCase: { select: 'a[class~="internal"]' } } }, + { select: '[id]', expect: { equivalentCase: { select: '*[id]' } } }, { select: '[type=radio]', expect: { ids: ['checked_radio', 'unchecked_radio'] } }, - { select: '[type=checkbox]', expect: { equivalentTo: { search: '*[type=checkbox]' } } }, + { select: '[type=checkbox]', expect: { equivalentCase: { select: '*[type=checkbox]' } } }, { select: '[title]', expect: { ids: ['with_title', 'commaParent'] } }, - { select: '#troubleForm [type=radio]', expect: { equivalentTo: { search: '#troubleForm *[type=radio]' } } }, - { select: '#troubleForm [type]', expect: { equivalentTo: { search: '#troubleForm *[type]' } } }, + { select: '#troubleForm [type=radio]', expect: { equivalentCase: { select: '#troubleForm *[type=radio]' } } }, + { select: '#troubleForm [type]', expect: { equivalentCase: { select: '#troubleForm *[type]' } } }, // testSelectorWithAttributeContainingDash { select: '[foo-bar]', expect: { ids: ['attr_with_dash'] } }, // attribute with hyphen @@ -227,7 +227,7 @@ runScenarios('prototype 2', 'normal', [ { select: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, { select: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, { select: '#troubleForm2 input[name="brackets[5][]"][value="2"]', expect: { ids: ['chk_2'] } }, - { select: '#troubleForm2 input[name=brackets\\[5\\]\\[\\]]', expect: { equivalentTo: { search: '#troubleForm2 input[name="brackets[5][]"]' }, count: 2 } }, + { select: '#troubleForm2 input[name=brackets\\[5\\]\\[\\]]', expect: { equivalentCase: { select: '#troubleForm2 input[name="brackets[5][]"]' }, count: 2 } }, // test$$WithNestedAttributeSelectors { select: 'div[style] p[id] strong', expect: { ids: ['strong'] } }, @@ -278,7 +278,7 @@ runScenarios('prototype 2', 'normal', [ allowMismatch: true, // some browsers don't support selecting by namespaced attributes count: 2, includesIds: ['item_3'], - equivalentTo: { search: '*[xml:lang]' } + equivalentCase: { select: '*[xml:lang]' } } }, @@ -394,19 +394,19 @@ runScenarios('prototype 2', 'normal', [ { select: '#troubleForm *:checked', expect: { ids: ['checked_box', 'checked_radio'] } }, // testIdenticalResultsFromEquivalentSelectors - { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers]' } } }, - { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers].brothers' } } }, - { select: 'div:not(.brothers)', expect: { equivalentTo: { search: 'div:not([class~=brothers])' } } }, - { select: 'li ~ li', expect: { equivalentTo: { search: 'li:not(:first-child)' } } }, - { select: 'ul > li', expect: { equivalentTo: { search: 'ul > li:nth-child(n)' } } }, - { select: 'ul > li:nth-child(even)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n)' } } }, - { select: 'ul > li:nth-child(odd)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n+1)' } } }, - { select: 'ul > li:first-child', expect: { equivalentTo: { search: 'ul > li:nth-child(1)' } } }, - { select: 'ul > li:last-child', expect: { equivalentTo: { search: 'ul > li:nth-last-child(1)' } } }, - { select: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { search: 'ul > li' } } }, - { select: 'ul > li:nth-child(n-999)', expect: { equivalentTo: { search: 'ul > li' } } }, - { select: 'ul>li', expect: { equivalentTo: { search: 'ul > li' } } }, - { select: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { search: '#p a:not([rel$="nofollow"]) > em' } } }, + { select: 'div.brothers', expect: { equivalentCase: { select: 'div[class~=brothers]' } } }, + { select: 'div.brothers', expect: { equivalentCase: { select: 'div[class~=brothers].brothers' } } }, + { select: 'div:not(.brothers)', expect: { equivalentCase: { select: 'div:not([class~=brothers])' } } }, + { select: 'li ~ li', expect: { equivalentCase: { select: 'li:not(:first-child)' } } }, + { select: 'ul > li', expect: { equivalentCase: { select: 'ul > li:nth-child(n)' } } }, + { select: 'ul > li:nth-child(even)', expect: { equivalentCase: { select: 'ul > li:nth-child(2n)' } } }, + { select: 'ul > li:nth-child(odd)', expect: { equivalentCase: { select: 'ul > li:nth-child(2n+1)' } } }, + { select: 'ul > li:first-child', expect: { equivalentCase: { select: 'ul > li:nth-child(1)' } } }, + { select: 'ul > li:last-child', expect: { equivalentCase: { select: 'ul > li:nth-last-child(1)' } } }, + { select: 'ul > li:nth-child(n-128)', expect: { equivalentCase: { select: 'ul > li' } } }, + { select: 'ul > li:nth-child(n-999)', expect: { equivalentCase: { select: 'ul > li' } } }, + { select: 'ul>li', expect: { equivalentCase: { select: 'ul > li' } } }, + { select: '#p a:not([rel$="nofollow"])>em', expect: { equivalentCase: { select: '#p a:not([rel$="nofollow"]) > em' } } }, // testSelectorsThatShouldReturnNothing { select: 'span:empty > *', expect: { count: 0, ids: [] } }, diff --git a/test_new/browser/scope.test.ts b/test_new/browser/scope.test.ts index 5e51a94..67cf023 100644 --- a/test_new/browser/scope.test.ts +++ b/test_new/browser/scope.test.ts @@ -13,7 +13,7 @@ runScenarios('scope', 'normal', [ cases: [ { select: 'ul a', expect: { count: 2 } }, { select: '#scope', expect: { count: 1 } }, - { select: 'a', scope: { by: 'id', id: 'scope' }, expect: { count: 1 } }, + { select: 'a', ref: { by: 'id', id: 'scope' }, expect: { count: 1 } }, ], }, @@ -28,9 +28,9 @@ runScenarios('scope', 'normal', [ `, cases: [ { select: '#scope', expect: { count: 1 } }, - { select: ':scope ul a', scope: { by: 'id', id: 'scope' }, expect: { count: 0, ids: [] } }, - { select: ':scope body ul a', scope: { by: 'id', id: 'scope' }, expect: { count: 0, ids: [] } }, - { select: ':scope a', scope: { by: 'id', id: 'scope' }, expect: { count: 1 } }, + { select: ':scope ul a', ref: { by: 'id', id: 'scope' }, expect: { count: 0, ids: [] } }, + { select: ':scope body ul a', ref: { by: 'id', id: 'scope' }, expect: { count: 0, ids: [] } }, + { select: ':scope a', ref: { by: 'id', id: 'scope' }, expect: { count: 1 } }, ], }, @@ -44,8 +44,8 @@ runScenarios('scope', 'normal', [ `, cases: [ { select: '.a', expect: { count: 1 } }, - { select: 'body div', scope: { by: 'first', selector: '.a' }, expect: { count: 2 } }, - { select: ':scope body div', scope: { by: 'first', selector: '.a' }, expect: { count: 0, ids: [] } }, + { select: 'body div', ref: { by: 'first', selector: '.a' }, expect: { count: 2 } }, + { select: ':scope body div', ref: { by: 'first', selector: '.a' }, expect: { count: 0, ids: [] } }, ], }, @@ -60,8 +60,8 @@ runScenarios('scope', 'normal', [ `, cases: [ { select: 'div', expect: { count: 1 } }, - { select: ':scope > p', scope: { by: 'first', selector: 'div' }, expect: { count: 1 } }, - { select: ':scope > span', scope: { by: 'first', selector: 'div' }, expect: { count: 0, ids: [] } }, + { select: ':scope > p', ref: { by: 'first', selector: 'div' }, expect: { count: 1 } }, + { select: ':scope > span', ref: { by: 'first', selector: 'div' }, expect: { count: 0, ids: [] } }, ], }, @@ -74,9 +74,9 @@ runScenarios('scope', 'normal', [
    `, cases: [ - { select: 'body div', scope: { by: 'first', selector: '.a' }, expect: { count: 2 } }, - { select: ':scope body div', scope: { by: 'first', selector: '.a' }, expect: { count: 0, ids: [] } }, - { select: ':scope > .a1, :scope > .a2', scope: { by: 'first', selector: '.a' }, expect: { count: 2 } }, + { select: 'body div', ref: { by: 'first', selector: '.a' }, expect: { count: 2 } }, + { select: ':scope body div', ref: { by: 'first', selector: '.a' }, expect: { count: 0, ids: [] } }, + { select: ':scope > .a1, :scope > .a2', ref: { by: 'first', selector: '.a' }, expect: { count: 2 } }, ], }, @@ -99,7 +99,7 @@ runScenarios('scope', 'normal', [ `, cases: [ - { select: ':scope > [data-test="foo"]', scope: { by: 'first', selector: 'body' }, expect: { count: 1 } }, + { select: ':scope > [data-test="foo"]', ref: { by: 'first', selector: 'body' }, expect: { count: 1 } }, ], }, @@ -117,7 +117,7 @@ runScenarios('scope', 'normal', [ cases: [ { select: ':scope > div', - scope: { by: 'first', selector: 'div' }, + ref: { by: 'first', selector: 'div' }, expect: { classes: ['outer', 'other-outer'] }, diff --git a/test_new/browser/scotch.test.ts b/test_new/browser/scotch.test.ts index fa5260c..b360b49 100644 --- a/test_new/browser/scotch.test.ts +++ b/test_new/browser/scotch.test.ts @@ -116,7 +116,7 @@ runScenarios('scotch', 'normal', [ // E — Type selector { select: 'li' }, - { select: 'strong', scope: { by: 'id', id: 'fixtures' }, expect: { ids: ['strong'] } }, + { select: 'strong', ref: { by: 'id', id: 'fixtures' }, expect: { ids: ['strong'] } }, { select: 'nonexistent', expect: { count: 0 } }, // #id — ID selector @@ -159,23 +159,23 @@ runScenarios('scotch', 'normal', [ setupPage: setupNw, cases: [ // [foo] - { select: '[href]', scope: { by: 'first', selector: 'body' }, expect: { equivalentTo: { search: 'a[href]', scope: { by: 'first', selector: 'body' } } } }, - { select: '[class~=internal]', expect: { equivalentTo: { search: 'a[class~="internal"]' } } }, - { select: '[id]', expect: { equivalentTo: { search: '*[id]' } } }, + { select: '[href]', ref: { by: 'first', selector: 'body' }, expect: { equivalentCase: { select: 'a[href]', ref: { by: 'first', selector: 'body' } } } }, + { select: '[class~=internal]', expect: { equivalentCase: { select: 'a[class~="internal"]' } } }, + { select: '[id]', expect: { equivalentCase: { select: '*[id]' } } }, { select: '[type=radio]', expect: { ids: ['checked_radio', 'unchecked_radio'] } }, - { select: '[type=checkbox]', expect: { equivalentTo: { search: '*[type=checkbox]' } } }, + { select: '[type=checkbox]', expect: { equivalentCase: { select: '*[type=checkbox]' } } }, { select: '[title]', expect: { ids: ['with_title', 'commaParent'] } }, - { select: '#troubleForm [type=radio]', expect: { equivalentTo: { search: '#troubleForm *[type=radio]' } } }, - { select: '#troubleForm [type]', expect: { equivalentTo: { search: '#troubleForm *[type]' } } }, + { select: '#troubleForm [type=radio]', expect: { equivalentCase: { select: '#troubleForm *[type=radio]' } } }, + { select: '#troubleForm [type]', expect: { equivalentCase: { select: '#troubleForm *[type]' } } }, // E[foo] - { select: 'h1[class]', expect: { equivalentTo: { search: '#fixtures h1' } } }, - { select: 'h1[CLASS]', expect: { equivalentTo: { search: '#fixtures h1' } } }, + { select: 'h1[class]', expect: { equivalentCase: { select: '#fixtures h1' } } }, + { select: 'h1[CLASS]', expect: { equivalentCase: { select: '#fixtures h1' } } }, { select: 'li#item_3[class]', expect: { ids: ['item_3'] } }, { select: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, { select: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, { select: 'cite[title="hello world!"]', expect: { ids: ['with_title'] } }, - { select: '[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'], equivalentTo: { search: '*[xml:lang]' } } }, + { select: '[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'], equivalentCase: { select: '*[xml:lang]' } } }, { select: '*[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'] } }, // E[foo="bar"] @@ -442,18 +442,18 @@ runScenarios('scotch', 'normal', [ htmlMode: 'document', setupPage: setupNw, cases: [ - { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers]' } } }, - { select: 'div.brothers', expect: { equivalentTo: { search: 'div[class~=brothers].brothers' } } }, - { select: 'div:not(.brothers)', expect: { equivalentTo: { search: 'div:not([class~=brothers])' } } }, - { select: 'li ~ li', expect: { equivalentTo: { search: 'li:not(:first-child)' } } }, - { select: 'ul > li', expect: { equivalentTo: { search: 'ul > li:nth-child(n)' } } }, - { select: 'ul > li:nth-child(even)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n)' } } }, - { select: 'ul > li:nth-child(odd)', expect: { equivalentTo: { search: 'ul > li:nth-child(2n+1)' } } }, - { select: 'ul > li:first-child', expect: { equivalentTo: { search: 'ul > li:nth-child(1)' } } }, - { select: 'ul > li:last-child', expect: { equivalentTo: { search: 'ul > li:nth-last-child(1)' } } }, - { select: 'ul > li:nth-child(n-128)', expect: { equivalentTo: { search: 'ul > li' } } }, - { select: 'ul>li', expect: { equivalentTo: { search: 'ul > li' } } }, - { select: '#p a:not([rel$="nofollow"])>em', expect: { equivalentTo: { search: '#p a:not([rel$="nofollow"]) > em' } } }, + { select: 'div.brothers', expect: { equivalentCase: { select: 'div[class~=brothers]' } } }, + { select: 'div.brothers', expect: { equivalentCase: { select: 'div[class~=brothers].brothers' } } }, + { select: 'div:not(.brothers)', expect: { equivalentCase: { select: 'div:not([class~=brothers])' } } }, + { select: 'li ~ li', expect: { equivalentCase: { select: 'li:not(:first-child)' } } }, + { select: 'ul > li', expect: { equivalentCase: { select: 'ul > li:nth-child(n)' } } }, + { select: 'ul > li:nth-child(even)', expect: { equivalentCase: { select: 'ul > li:nth-child(2n)' } } }, + { select: 'ul > li:nth-child(odd)', expect: { equivalentCase: { select: 'ul > li:nth-child(2n+1)' } } }, + { select: 'ul > li:first-child', expect: { equivalentCase: { select: 'ul > li:nth-child(1)' } } }, + { select: 'ul > li:last-child', expect: { equivalentCase: { select: 'ul > li:nth-last-child(1)' } } }, + { select: 'ul > li:nth-child(n-128)', expect: { equivalentCase: { select: 'ul > li' } } }, + { select: 'ul>li', expect: { equivalentCase: { select: 'ul > li' } } }, + { select: '#p a:not([rel$="nofollow"])>em', expect: { equivalentCase: { select: '#p a:not([rel$="nofollow"]) > em' } } }, ], }, diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts index 94327eb..f115abc 100644 --- a/test_new/browser/w3c.test.ts +++ b/test_new/browser/w3c.test.ts @@ -457,8 +457,8 @@ runScenarios('w3c', 'normal', [ cases: [ { select: 'meta[charset="utf-8"', expect: { ids: ['expected'] } }, { select: 'meta[charset="utf-8', expect: { ids: ['expected'] } }, - { select: 'span:not([class])', scope: { by: 'id', id: 'container' }, expect: { count: 1 } }, - { select: 'span:not([class)', scope: { by: 'id', id: 'container' }, expect: { throws: true } }, + { select: 'span:not([class])', ref: { by: 'id', id: 'container' }, expect: { count: 1 } }, + { select: 'span:not([class)', ref: { by: 'id', id: 'container' }, expect: { throws: true } }, ], }, ]); diff --git a/test_new/global.d.ts b/test_new/global.d.ts index c399ecb..9c87667 100644 --- a/test_new/global.d.ts +++ b/test_new/global.d.ts @@ -3,17 +3,25 @@ import type { PwHelpers } from './browser/harness/browser'; export {}; declare global { + type QueryContext = Document | Element | DocumentFragment; + type NwCallback = (element: Element) => boolean | void; + interface Window { __pwHelpers: PwHelpers; } + const NW: { Dom: { - select( - selector: string, - root?: ParentNode | Document | Element | null, - callback?: ((element: Element) => boolean | void) | null, - ): Element[]; + byId(id: string, ctx: QueryContext): Element[]; + byTag(tag: string, ctx: QueryContext): Element[]; + byClass(cls: string, ctx: QueryContext): Element[]; + + select(selector: string, ctx?: QueryContext | null, callback?: NwCallback | null): Element[]; + first(selector: string, ctx?: QueryContext | null, callback?: NwCallback | null): Element | null; + match(selector: string, ctx?: Element | null, callback?: NwCallback | null): boolean; + closest(selector: string, ctx: Element, callback?: NwCallback | null): Element | null; + configure(options: Record): void; }; - } + }; } \ No newline at end of file diff --git a/test_new/utils/type.ts b/test_new/utils/type.ts index c4bec0f..42f34aa 100644 --- a/test_new/utils/type.ts +++ b/test_new/utils/type.ts @@ -1,3 +1,5 @@ +export type DistributiveOmit = T extends unknown ? Omit : never; + export function assertNever(x: never): never { throw new Error(`Unexpected key: ${x}`); } From d5398b40cb120c1479df26f9b256844a7b84720f Mon Sep 17 00:00:00 2001 From: koal44 Date: Fri, 10 Apr 2026 20:01:11 -0700 Subject: [PATCH 09/19] support case fixmes and fixme-focused runs --- package.json | 4 +- test_new/browser/harness/browser.ts | 6 +- test_new/browser/harness/scenarios.ts | 91 +++++++++++++++++++++------ test_new/browser/html5.test.ts | 2 +- 4 files changed, 78 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 6863226..191c2de 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,13 @@ "clean": "node ./scripts/clean.mjs", "min": "node ./scripts/min.mjs", "lint": "npx eslint src/nwsapi.js", - "test:browser": "npx playwright test" + "test:browser": "cross-env HARNESS_MODE=normal playwright test", + "test:browser:fixme": "cross-env HARNESS_MODE=fixme playwright test" }, "devDependencies": { "@playwright/test": "^1.59.1", "@types/node": "^25.5.2", + "cross-env": "^10.1.0", "eslint": "9.31.0", "playwright": "^1.59.1", "terser": "5.46.1" diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts index 851518a..91314a8 100644 --- a/test_new/browser/harness/browser.ts +++ b/test_new/browser/harness/browser.ts @@ -70,8 +70,8 @@ export function installBrowserHelpers(): void { return `Both threw:\n ${a.name} error: ${a.result.error}\n ${b.name} error: ${b.result.error}`; } return a.result.error - ? `Mismatch\n ${a.name} threw while ${b.name} did not.\n error: ${a.result.error}` - : `Mismatch\n ${b.name} threw while ${a.name} did not.\n error: ${b.result.error}`; + ? `Throw mismatch:\n ${a.name} threw while ${b.name} did not.\n error: ${a.result.error}` + : `Throw mismatch:\n ${b.name} threw while ${a.name} did not.\n error: ${b.result.error}`; } const aElems = a.result.elements; @@ -86,7 +86,7 @@ export function installBrowserHelpers(): void { for (let i = 0; i < maxLen; ++i) { if (aElems[i] !== bElems[i]) { mismatchMsg = mismatchMsg ? mismatchMsg + '\n' : ''; - mismatchMsg += `Element mismatch at index ${i}:\n` + + mismatchMsg += `First element mismatch at index ${i}:\n` + ` ${a.name}[${i}] = ${describe(aElems[i])}\n` + ` ${b.name}[${i}] = ${describe(bElems[i])}`; break; diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index b959e38..0bce80f 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -1,15 +1,22 @@ -import test, { chromium, expect, firefox, webkit } from '@playwright/test'; +import { test, chromium, expect, firefox, webkit } from '@playwright/test'; import type { Browser, Page } from '@playwright/test'; import { assertNever, type DistributiveOmit } from '../../utils/type'; import { installBrowserHelpers, type EngineResult, type EvalResult, type EngineQuery } from './browser'; -export type SelectCase = { select: string; ref?: ContextRef; expect?: Expectation }; -export type ByIdCase = { byId: string; ref?: ContextRef; expect?: Expectation }; -export type ByTagCase = { byTag: string; ref?: ContextRef; expect?: Expectation }; -export type ByClassCase = { byClass: string; ref?: ContextRef; expect?: Expectation }; -export type MatchCase = { match: string; ref: ContextRef; expect?: Expectation }; -export type FirstCase = { first: string; ref?: ContextRef; expect?: Expectation }; -export type ClosestCase = { closest: string; ref: ContextRef; expect?: Expectation }; +type HarnessMode = 'normal' | 'fixme'; +const rawHarnessMode = process.env.HARNESS_MODE; +if (rawHarnessMode !== undefined && rawHarnessMode !== 'normal' && rawHarnessMode !== 'fixme') { + throw new Error(`Invalid HARNESS_MODE: ${rawHarnessMode}`); +} +const HARNESS_MODE: HarnessMode = rawHarnessMode ?? 'normal'; + +export type SelectCase = { select: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; +export type ByIdCase = { byId: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; +export type ByTagCase = { byTag: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; +export type ByClassCase = { byClass: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; +export type FirstCase = { first: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; +export type MatchCase = { match: string; ref: ContextRef; expect?: Expectation, status?: CaseStatus }; +export type ClosestCase = { closest: string; ref: ContextRef; expect?: Expectation, status?: CaseStatus }; export type TestCase = SelectCase | ByIdCase | ByTagCase | ByClassCase | MatchCase | FirstCase | ClosestCase; @@ -20,7 +27,7 @@ export type Scenario = { browsers?: BrowserName[]; cases: TestCase[]; setupPage?: (page: Page) => void | Promise; - status?: TestStatus; + status?: ScenarioStatus; }; export type Expectation = { @@ -42,8 +49,9 @@ export type EquivalentCase = DistributiveOmit; const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; type BrowserName = typeof BROWSER_NAMES[number]; -type DescribeStatus = 'normal' | 'skip' | 'only' | 'fixme'; -type TestStatus = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; +type ScenariosStatus = 'normal' | 'skip' | 'only'; // | 'fixme'; +type ScenarioStatus = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; +type CaseStatus = 'normal' | 'skip' | 'fixme' | 'fail'; export const ENGINES = ['native', 'nw'] as const; export type Engine = typeof ENGINES[number]; @@ -53,7 +61,7 @@ export type ContextRef = | { by: 'id'; id: string } | { by: 'first'; selector: string }; -export function runScenarios(label: string, status: DescribeStatus, scenarios: Scenario[]): void { +export function runScenarios(label: string, status: ScenariosStatus, scenarios: Scenario[]): void { const describeFn = getDescribeFn(status); describeFn(label, () => { let browsers: Record; @@ -84,6 +92,9 @@ export function runScenarios(label: string, status: DescribeStatus, scenarios: S }); for (const s of scenarios) { + const hasFixme = s.status === 'fixme' || s.cases.some(c => c.status === 'fixme'); + if (HARNESS_MODE === 'fixme' && !hasFixme) continue; + const testFn = getTestFn(s.status); testFn(s.name, async () => { await runScenario(s, pages); @@ -98,12 +109,48 @@ async function runScenario(s: Scenario, pages: Record): Promi for (const browserName of scenarioBrowsers) { const page = pages[browserName]; await setupPage(page, s); + for (const c of s.cases) { - const result = await evalCase(page, c); - const expectation = c.expect ?? {}; - checkResult(result, expectation, browserName, s.name); + await runCase(page, c, browserName, s); + } + } +} + +async function runCase(page: Page, c: TestCase, browserName: BrowserName, s: Scenario): Promise { + if (c.status === 'skip') return; + if (s.status !== 'fixme' && HARNESS_MODE === 'fixme' && c.status !== 'fixme') { + return; + } + + const result = await evalCase(page, c); + const expectation = c.expect ?? {}; + + let thrown: unknown = undefined; + try { + checkResult(result, expectation, browserName, s.name); + } catch (err) { + thrown = err; + } + + const status = c.status ?? 'normal'; + + if (status === 'normal') { + if (thrown) throw thrown; + return; + } + + if (status === 'fixme' || status === 'fail') { + if (thrown) { + if (HARNESS_MODE === 'fixme') throw thrown; + return; } + throw new Error( + `[${browserName}] :: ${s.name} :: ${result.info}\n` + + `Case is marked '${status}' but unexpectedly passed.`, + ); } + + assertNever(status); } async function evalCase(page: Page, testCase: TestCase): Promise { @@ -144,17 +191,21 @@ async function evalCase(page: Page, testCase: TestCase): Promise { }, testCase); } -function getDescribeFn(mode?: DescribeStatus) { +function getDescribeFn(mode?: ScenariosStatus) { if (mode === 'skip') return test.describe.skip; if (mode === 'only') return test.describe.only; - if (mode === 'fixme') return test.describe.fixme; + // if (mode === 'fixme') return test.describe.fixme; return test.describe; } -function getTestFn(mode?: TestStatus) { +function getTestFn(mode?: ScenarioStatus) { if (mode === 'skip') return test.skip; if (mode === 'only') return test.only; - if (mode === 'fixme') return test.fixme; + // if (mode === 'fixme') return test.fixme; + if (mode === 'fixme') { + if (HARNESS_MODE === 'fixme') return test; + return test.fixme; + } if (mode === 'fail') return test.fail; return test; } @@ -267,7 +318,7 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: if (!allowMismatch && !result.native.threw) { const mismatch = !!result.mismatchMsg; - expect(mismatch, `${msg}\n${result.mismatchMsg}`).toBe(false); + expect(mismatch, `${msg}`).toBe(false); // \n${result.mismatchMsg} } if (expectation.equivalentCase) { diff --git a/test_new/browser/html5.test.ts b/test_new/browser/html5.test.ts index 4f475d0..13c47ae 100644 --- a/test_new/browser/html5.test.ts +++ b/test_new/browser/html5.test.ts @@ -39,7 +39,6 @@ runScenarios('html5', 'normal', [ { name: 'table :not() selector test', - status: 'fixme', html: `

     Your Search Results

    @@ -193,6 +192,7 @@ runScenarios('html5', 'normal', [ { select: 'tbody > tr:nth-of-type(n+6):not(:nth-of-type(17)) > td:nth-of-type(2) > a:not(:nth-of-type(2))', expect: { ids: ['AV11UXA'] }, + status: 'fixme', }, ], }, From 0fd2f5072933008cfd5b929d086f8b3d0648aa40 Mon Sep 17 00:00:00 2001 From: koal44 Date: Sat, 11 Apr 2026 22:21:02 -0700 Subject: [PATCH 10/19] continue w3c migration and expand harness capabilities --- test_new/browser/haness.test.ts | 18 - test_new/browser/harness.test.ts | 116 ++ test_new/browser/harness/browser.ts | 73 +- test_new/browser/harness/scenarios.ts | 135 +- test_new/browser/jquery.test.ts | 4 +- test_new/browser/w3c.test.ts | 1744 ++++++++++++++++++++++++- 6 files changed, 2007 insertions(+), 83 deletions(-) delete mode 100644 test_new/browser/haness.test.ts create mode 100644 test_new/browser/harness.test.ts diff --git a/test_new/browser/haness.test.ts b/test_new/browser/haness.test.ts deleted file mode 100644 index 1b06b09..0000000 --- a/test_new/browser/haness.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { runScenarios } from "./harness/scenarios"; - -runScenarios('scope', 'normal', [ - { - name: 'classes', - html: ` -
    -
    -
    - `, - cases: [ - { select: 'div', expect: { classes: ['outer primary', 'other-outer', ''] } }, - { select: 'div', expect: { includesClasses: ['other-outer'] } }, - { select: 'div', expect: { includesClasses: ['outer', 'primary'] } }, - { select: 'div', expect: { excludesClasses: ['missing-class'] } }, - ], - } -]); \ No newline at end of file diff --git a/test_new/browser/harness.test.ts b/test_new/browser/harness.test.ts new file mode 100644 index 0000000..064e9a3 --- /dev/null +++ b/test_new/browser/harness.test.ts @@ -0,0 +1,116 @@ +import { expect } from "@playwright/test"; +import { runScenarios } from "./harness/scenarios"; + +runScenarios('scope', 'normal', [ + { + name: 'classes', + html: ` +
    +
    +
    + `, + cases: [ + { select: 'div', expect: { classes: ['outer primary', 'other-outer', ''] } }, + { select: 'div', expect: { includesClasses: ['other-outer'] } }, + { select: 'div', expect: { includesClasses: ['outer', 'primary'] } }, + { select: 'div', expect: { excludesClasses: ['missing-class'] } }, + ], + }, + + { + name: 'context-home', + htmlMode: 'document', + html: ` + + + +
    +
    +
    +
    +
    + +
    + +
    + + + `, + cases: [ + // Inherited state should differ by home + { match: ':lang(en)', ref: { by: 'id', id: 'lang-child' }, expect: { ids: ['lang-child'] } }, + { match: ':lang(en)', ref: { by: 'id', id: 'lang-child', home: 'detached' }, expect: { ids: [] } }, + { match: ':lang(en)', ref: { by: 'id', id: 'lang-child', home: 'fragment' }, expect: { ids: [] } }, + + // Element-local matching should survive rehoming + { match: '.foo', ref: { by: 'id', id: 'class-child' }, expect: { ids: ['class-child'] } }, + { match: '.foo', ref: { by: 'id', id: 'class-child', home: 'detached' }, expect: { ids: ['class-child'] } }, + { match: '.foo', ref: { by: 'id', id: 'class-child', home: 'fragment' }, expect: { ids: ['class-child'] } }, + + // Ancestor traversal depends on whether the ancestor chain is preserved + { closest: '.box', ref: { by: 'id', id: 'class-child' }, expect: { ids: ['class-parent'] } }, + { closest: '.box', ref: { by: 'id', id: 'class-child', home: 'detached' }, expect: { ids: [] } }, + { closest: '.box', ref: { by: 'id', id: 'class-child', home: 'fragment' }, expect: { ids: [] } }, + ], + }, + + { + name: 'equivalent cases that are detached should fail', + htmlMode: 'document', + status: 'fail', + html: ` + + + +
    +
    + alpha +
    +
    + + + + + `, + cases: [ + { + select: '.hit', + ref: { by: 'id', id: 'left', home: 'detached' }, + expect: { + count: 1, + equivalentCase: { + select: '.hit', + ref: { by: 'id', id: 'right', home: 'document' }, + }, + }, + }, + ], + }, + + { + name: 'setup page can be used to verify test assumptions', + html: ` +
    +
    + + `, + setupPage: async (page) => { + const result = await page.evaluate(() => ({ + divXCount: document.querySelectorAll('div.x').length, + spanCount: document.querySelectorAll('span').length, + })); + + expect(result.divXCount).toEqual(2); + expect(result.spanCount).toEqual(1); + }, + cases: [ + { select: 'div.x', expect: { count: 2 } }, + { select: 'span', expect: { count: 1 } }, + ] + }, + +]); \ No newline at end of file diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts index 91314a8..0630e6b 100644 --- a/test_new/browser/harness/browser.ts +++ b/test_new/browser/harness/browser.ts @@ -1,17 +1,16 @@ -import type { Engine, EquivalentCase, ContextRef } from "./scenarios"; +import { type Engine, type EquivalentCase, type ContextRef, type ContextHome, NwsapiId } from "./scenarios"; export interface PwHelpers { resolveContext(ref: ContextRef | undefined): QueryContext | null; runQuery(query: () => Element[]): QueryResult; - compareQueryResults( - a: NamedQueryResult, - b: NamedQueryResult - ): string | undefined; + compareQueryResults(a: NamedQueryResult, b: NamedQueryResult): string | undefined; toEngineResult(res: QueryResult): EngineResult; - getResults(queryFn: EngineQuery, query: string, ref?: ContextRef): EngineAndQueryResult; + getResults(queryFn: EngineQuery, query: string, ctx: QueryContext | null, ctxErrorMsg?: string): EngineAndQueryResult; getEngineQuery(c: EquivalentCase, n: Engine): EngineQuery; getCaseQuery(c: EquivalentCase): string; getCaseLabel(c: EquivalentCase, n: Engine): string; + stringify(obj: unknown): string; + isRehomed(ref?: ContextRef): boolean; } export type EvalResult = { @@ -49,9 +48,25 @@ export function installBrowserHelpers(): void { return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 11; } + function isRehomed(ref?: ContextRef): boolean { + return !!ref && ref.by !== 'document' && ref.by !== 'iframe' && !!ref.home && ref.home !== 'document'; + } + + // Source - https://stackoverflow.com/a/65443215 + function stringify(obj: unknown): string { + let json = JSON.stringify(obj, null, 2) as string | undefined; + if (json === undefined) return String(obj); + json = json.replace(/^[\t ]*"[^:\n\r]+(? Element[]) { try { - return { elements: query(), error: '' }; + const id: NwsapiId = 'nwsapi-bootstrap'; + const els = query().filter((el) => el.getAttribute('id') !== id); + return { elements: els, error: '' }; } catch (e) { return { elements: [], error: e instanceof Error ? e.message : String(e) }; } @@ -97,10 +112,36 @@ export function installBrowserHelpers(): void { } function resolveContext(ref?: ContextRef): QueryContext | null { - return !ref || ref.by === 'document' ? document - : ref.by === 'id' ? document.getElementById(ref.id) + if (!ref || ref.by === 'document') return document; + + if (ref.by === 'iframe') { + const iframe = document.getElementById(ref.id); + if (!(iframe instanceof HTMLIFrameElement)) return null; + return iframe.contentDocument ?? null; + } + + const el = ref.by === 'id' ? document.getElementById(ref.id) : ref.by === 'first' ? document.querySelector(ref.selector) : null; + + if (!el) return null; + + const home: ContextHome = ref.home ?? 'document'; + if (home === 'document') return el; + + const clone = el.cloneNode(true); + if (!isElement(clone)) return null; + if (home === 'detached') return clone; + + if (home === 'fragment') { + const frag = document.createDocumentFragment(); + frag.appendChild(clone); + + // Return the clone rehomed so matches/closest stay sane. + return clone; + } + + return null; } function toEngineResult(res: QueryResult): EngineResult { @@ -112,12 +153,10 @@ export function installBrowserHelpers(): void { }; } - function getResults(queryFn: EngineQuery, query: string, ref?: ContextRef): EngineAndQueryResult { - const context = resolveContext(ref); - - const queryResult: QueryResult = context - ? runQuery(queryFn(query, context)) - : { elements: [], error: `Could not resolve context from ref:: ${JSON.stringify(ref)}` }; + function getResults(queryFn: EngineQuery, query: string, ctx: QueryContext | null, ctxErrorMsg?: string): EngineAndQueryResult { + const queryResult: QueryResult = ctx + ? runQuery(queryFn(query, ctx)) + : { elements: [], error: ctxErrorMsg ?? 'No context provided' }; const engineResult = toEngineResult(queryResult); return { queryResult, engineResult }; @@ -263,7 +302,7 @@ export function installBrowserHelpers(): void { case 'match' in c: return engine === 'native' ? `matches(${c.match})` - : `NW.Dom.match(${JSON.stringify(c.match)})`; + : `NW.Dom.match(${c.match})`; case 'closest' in c: return engine === 'native' @@ -284,5 +323,7 @@ export function installBrowserHelpers(): void { getEngineQuery, getCaseQuery, getCaseLabel, + stringify, + isRehomed, }; }; diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index 0bce80f..3073f1d 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -1,7 +1,8 @@ import { test, chromium, expect, firefox, webkit } from '@playwright/test'; import type { Browser, Page } from '@playwright/test'; import { assertNever, type DistributiveOmit } from '../../utils/type'; -import { installBrowserHelpers, type EngineResult, type EvalResult, type EngineQuery } from './browser'; +import { installBrowserHelpers } from './browser'; +import type { EngineResult, EvalResult, EngineQuery } from './browser'; type HarnessMode = 'normal' | 'fixme'; const rawHarnessMode = process.env.HARNESS_MODE; @@ -22,12 +23,12 @@ export type TestCase = SelectCase | ByIdCase | ByTagCase | ByClassCase | MatchCa export type Scenario = { name: string; + status?: ScenarioStatus; html: string; htmlMode?: 'body' | 'document'; browsers?: BrowserName[]; - cases: TestCase[]; setupPage?: (page: Page) => void | Promise; - status?: ScenarioStatus; + cases: TestCase[]; }; export type Expectation = { @@ -58,8 +59,22 @@ export type Engine = typeof ENGINES[number]; export type ContextRef = | { by: 'document' } - | { by: 'id'; id: string } - | { by: 'first'; selector: string }; + | { by: 'id'; id: string; home?: ContextHome } + | { by: 'first'; selector: string; home?: ContextHome } + | { by: 'iframe'; id: string } + +export type ContextHome = 'document' | 'detached' | 'fragment'; +export type NwsapiId = 'nwsapi-bootstrap'; + +type Misfail = { passedEverywhere: boolean; info: string; } +type CaseInfo = { + browser: BrowserName; + scenario: Scenario; + case: TestCase; + caseIndex: number; + misfails: Record; +}; + export function runScenarios(label: string, status: ScenariosStatus, scenarios: Scenario[]): void { const describeFn = getDescribeFn(status); @@ -106,17 +121,34 @@ export function runScenarios(label: string, status: ScenariosStatus, scenarios: async function runScenario(s: Scenario, pages: Record): Promise { const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; + // collection of cases marked as 'fail' and their accumulated outcomes + const misfails: Record = {}; + for (const browserName of scenarioBrowsers) { const page = pages[browserName]; await setupPage(page, s); - for (const c of s.cases) { - await runCase(page, c, browserName, s); + for (let i = 0; i < s.cases.length; ++i) { + const c = s.cases[i]; + await runCase(page, { browser: browserName, scenario: s, case: c, caseIndex: i, misfails: misfails }); + } + } + + for (const [caseIndex, misFail] of Object.entries(misfails)) { + if (misFail.passedEverywhere) { + const i = Number(caseIndex); + throw new Error( + `Case #${i + 1} :: ${s.name} :: ${misFail.info}\n` + + `Case is marked 'fail' but unexpectedly passed.`, + ); } } + } -async function runCase(page: Page, c: TestCase, browserName: BrowserName, s: Scenario): Promise { +async function runCase(page: Page, caseInfo: CaseInfo): Promise { + const { scenario: s, case: c } = caseInfo; + if (c.status === 'skip') return; if (s.status !== 'fixme' && HARNESS_MODE === 'fixme' && c.status !== 'fixme') { return; @@ -127,7 +159,7 @@ async function runCase(page: Page, c: TestCase, browserName: BrowserName, s: Sce let thrown: unknown = undefined; try { - checkResult(result, expectation, browserName, s.name); + checkResult(result, expectation, caseInfo); } catch (err) { thrown = err; } @@ -139,15 +171,20 @@ async function runCase(page: Page, c: TestCase, browserName: BrowserName, s: Sce return; } - if (status === 'fixme' || status === 'fail') { - if (thrown) { - if (HARNESS_MODE === 'fixme') throw thrown; - return; + if (status === 'fail') { + const curPassed = !thrown; + const prevPassed = caseInfo.misfails[caseInfo.caseIndex]?.passedEverywhere ?? true; + const passedEverywhere = curPassed && prevPassed; + const info = caseInfo.misfails[caseInfo.caseIndex]?.info || result.info; + caseInfo.misfails[caseInfo.caseIndex] = { passedEverywhere, info }; + return; + } + + if (status === 'fixme') { + if (thrown && HARNESS_MODE === 'fixme') { + throw thrown; } - throw new Error( - `[${browserName}] :: ${s.name} :: ${result.info}\n` + - `Case is marked '${status}' but unexpectedly passed.`, - ); + return; } assertNever(status); @@ -157,14 +194,16 @@ async function evalCase(page: Page, testCase: TestCase): Promise { return await page.evaluate((c) => { const pw = window.__pwHelpers; const query = pw.getCaseQuery(c); - // const info = pw.getCaseLabel(c); const equivCase = c.expect?.equivalentCase; const nwFn: EngineQuery = pw.getEngineQuery(c, 'nw'); const nativeFn: EngineQuery = pw.getEngineQuery(c, 'native'); - const nwRes = pw.getResults(nwFn, query, c.ref); - const nativeRes = pw.getResults(nativeFn, query, c.ref); + const ctx = pw.resolveContext(c.ref); + const ctxErrorMsg = ctx ? undefined : `Could not resolve context from ref: ${pw.stringify(c.ref)}`; + + const nwRes = pw.getResults(nwFn, query, ctx, ctxErrorMsg); + const nativeRes = pw.getResults(nativeFn, query, ctx, ctxErrorMsg); const mismatchMsg = pw.compareQueryResults( { name: `native:${pw.getCaseLabel(c, 'native')}`, result: nativeRes.queryResult }, @@ -175,12 +214,23 @@ async function evalCase(page: Page, testCase: TestCase): Promise { if (equivCase) { const nwEquivFn = pw.getEngineQuery(equivCase, 'nw'); const nwEquivQuery = pw.getCaseQuery(equivCase); - const nwEquivRes = pw.getResults(nwEquivFn, nwEquivQuery, equivCase.ref); - equivMismatchMsg = pw.compareQueryResults( - { name: `nw:${pw.getCaseLabel(c, 'nw')}`, result: nwRes.queryResult }, - { name: `nwEquiv:${pw.getCaseLabel(equivCase, 'nw')}`, result: nwEquivRes.queryResult }, - ); + const equivCtx = pw.resolveContext(equivCase.ref); + const equivCtxErrorMsg = equivCtx ? undefined : `Could not resolve equivalent context from ref: ${pw.stringify(equivCase.ref)}`; + const nwEquivRes = pw.getResults(nwEquivFn, nwEquivQuery, equivCtx, equivCtxErrorMsg); + + if (pw.isRehomed(c.ref) || pw.isRehomed(equivCase.ref)) { + equivMismatchMsg = + `Equivalent-case assertion unsupported because one or more contexts were rehomed.\n` + + `Identity-based equivalence is only supported for document-backed contexts.\n` + + ` case context: ${pw.stringify(c.ref)}${pw.isRehomed(c.ref) ? ' (rehomed)' : ''}\n` + + ` equivalent case context: ${pw.stringify(equivCase.ref)}${pw.isRehomed(equivCase.ref) ? ' (rehomed)' : ''}` + } else { + equivMismatchMsg = pw.compareQueryResults( + { name: `nw:${pw.getCaseLabel(c, 'nw')}`, result: nwRes.queryResult }, + { name: `nwEquiv:${pw.getCaseLabel(equivCase, 'nw')}`, result: nwEquivRes.queryResult }, + ); + } } return { @@ -201,7 +251,6 @@ function getDescribeFn(mode?: ScenariosStatus) { function getTestFn(mode?: ScenarioStatus) { if (mode === 'skip') return test.skip; if (mode === 'only') return test.only; - // if (mode === 'fixme') return test.fixme; if (mode === 'fixme') { if (HARNESS_MODE === 'fixme') return test; return test.fixme; @@ -213,7 +262,11 @@ function getTestFn(mode?: ScenarioStatus) { async function setupPage(page: Page, scenario: Scenario): Promise { if (scenario.htmlMode === 'document') { await page.setContent(scenario.html); - await page.addScriptTag({ path: 'src/nwsapi.js' }); + const script = await page.addScriptTag({ path: 'src/nwsapi.js' }); + await script.evaluate((el) => { + const id: NwsapiId = 'nwsapi-bootstrap'; + (el as HTMLScriptElement).id = id; + }); if (scenario.setupPage) await scenario.setupPage(page); return; } @@ -253,33 +306,35 @@ function runEngineChecks( } } -function checkResult(result: EvalResult, expectation: Expectation, browserName: BrowserName, scenarioName: string): void { +function checkResult(result: EvalResult, expectation: Expectation, caseInfo: CaseInfo): void { const allowMismatch = expectation.allowMismatch ?? false; - const msg = `[${browserName}] :: ${scenarioName} :: ${result.info}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; + const { caseIndex, browser, scenario: s } = caseInfo; + const header = `Case #${caseIndex + 1}`; + const msg = `[${browser}] :: ${s.name} :: ${result.info}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; if (expectation.throws) { - expect(result.nw.threw, msg).toBe(true); + expect(result.nw.threw, `${header} ${msg}`).toBe(true); return; } - expect(result.nw.threw, msg).toBe(false); + expect(result.nw.threw, `${header} ${msg}`).toBe(false); if (expectation.count !== undefined) { runEngineChecks(result, msg, 'count', (r, label) => { - expect(r.count, label).toEqual(expectation.count); + expect(r.count, `${header} ${label}`).toEqual(expectation.count); }); } if (expectation.ids) { runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, label).toEqual(expectation.ids); + expect(r.ids, `${header} ${label}`).toEqual(expectation.ids); }); } if (expectation.includesIds) { for (const id of expectation.includesIds) { runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, label).toContain(id); + expect(r.ids, `${header} ${label}`).toContain(id); }); } } @@ -287,14 +342,14 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: if (expectation.excludesIds) { for (const id of expectation.excludesIds) { runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, label).not.toContain(id); + expect(r.ids, `${header} ${label}`).not.toContain(id); }); } } if (expectation.classes) { runEngineChecks(result, msg, 'classes', (r, label) => { - expect(r.classes, label).toEqual(expectation.classes); + expect(r.classes, `${header} ${label}`).toEqual(expectation.classes); }); } @@ -302,7 +357,7 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: for (const cls of expectation.includesClasses) { runEngineChecks(result, msg, 'classes', (r, label) => { const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, label).toContain(cls); + expect(classTokens, `${header} ${label}`).toContain(cls); }); } } @@ -311,18 +366,18 @@ function checkResult(result: EvalResult, expectation: Expectation, browserName: for (const cls of expectation.excludesClasses) { runEngineChecks(result, msg, 'classes', (r, label) => { const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, label).not.toContain(cls); + expect(classTokens, `${header} ${label}`).not.toContain(cls); }); } } if (!allowMismatch && !result.native.threw) { const mismatch = !!result.mismatchMsg; - expect(mismatch, `${msg}`).toBe(false); // \n${result.mismatchMsg} + expect(mismatch, `${header} ${msg}`).toBe(false); } if (expectation.equivalentCase) { const equivMismatch = !!result.equivMismatchMsg; - expect(equivMismatch, `${msg}\n${result.equivMismatchMsg}`).toBe(false); + expect(equivMismatch, `${header} ${msg}\n${result.equivMismatchMsg}`).toBe(false); } } diff --git a/test_new/browser/jquery.test.ts b/test_new/browser/jquery.test.ts index 5602b97..87b2a1b 100644 --- a/test_new/browser/jquery.test.ts +++ b/test_new/browser/jquery.test.ts @@ -287,8 +287,8 @@ runScenarios('jquery', 'normal', [ { select: '#lengthtest input', expect: { count: 2 } }, // Duplicate / sort-order checks from the original suite, expressed as counts. - { select: '*', expect: { count: 187 } }, - { select: '*, *', expect: { count: 187 } }, + { select: '*', expect: { count: 186 } }, + { select: '*, *', expect: { count: 186 } }, { select: 'p', expect: { count: 6 } }, { select: 'p, div p', expect: { count: 6 } }, diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts index f115abc..a276538 100644 --- a/test_new/browser/w3c.test.ts +++ b/test_new/browser/w3c.test.ts @@ -1,10 +1,9 @@ -import { expect } from 'playwright/test'; +import { expect } from '@playwright/test'; import { runScenarios } from './harness/scenarios'; -import { run } from 'node:test'; runScenarios('w3c iframes', 'normal', [ { - name: 'syntax', + name: 'css/selectors/syntax', html: ` Selectors: syntax of case-sensitivity attribute selector @@ -204,7 +203,7 @@ runScenarios('w3c iframes', 'normal', [ runScenarios('w3c', 'normal', [ { - name: 'syntax', + name: 'css/selectors/syntax', status: 'fixme', html: ` @@ -282,8 +281,9 @@ runScenarios('w3c', 'normal', [ { select: "[foo/**/i]", expect: { throws: true } }, ], }, + { - name: 'semantics', + name: 'css/selectors/semantics', status: 'fixme', html: `
    `, cases: [], @@ -412,7 +412,7 @@ runScenarios('w3c', 'normal', [ }, { - name: 'child-indexed-pseudo-class', + name: 'dom/nodes/child-indexed-pseudo-class', html: `
    `, cases: [ { select: 'div:first-child' }, @@ -438,7 +438,7 @@ runScenarios('w3c', 'normal', [ }, { - name: 'missing-right-token', + name: 'dome/nodes/missing-right-token', htmlMode: 'document', html: ` @@ -461,6 +461,1736 @@ runScenarios('w3c', 'normal', [ { select: 'span:not([class)', ref: { by: 'id', id: 'container' }, expect: { throws: true } }, ], }, + + { + name: 'dom/nodes/element-closest', + htmlMode: 'document', + html: ` + + + + Test for Element.closest + + +
    + + + `, + cases: [ + { closest: 'select', ref: { by: 'id', id: 'test12' }, expect: { ids: ['test3'] } }, + { closest: 'fieldset', ref: { by: 'id', id: 'test13' }, expect: { ids: ['test2'] } }, + { closest: 'div', ref: { by: 'id', id: 'test13' }, expect: { ids: ['test6'] } }, + { closest: 'body', ref: { by: 'id', id: 'test3' }, expect: { ids: ['body'] } }, + + { closest: '[default]', ref: { by: 'id', id: 'test4' }, expect: { ids: ['test4'] } }, + { closest: '[selected]', ref: { by: 'id', id: 'test4' }, expect: { count: 0 } }, + { closest: '[selected]', ref: { by: 'id', id: 'test11' }, expect: { ids: ['test11'] } }, + { closest: '[name="form-a"]', ref: { by: 'id', id: 'test12' }, expect: { ids: ['test5'] } }, + { closest: 'form[name="form-a"]', ref: { by: 'id', id: 'test13' }, expect: { ids: ['test5'] } }, + { closest: 'input[required]', ref: { by: 'id', id: 'test9' }, expect: { ids: ['test9'] } }, + { closest: 'select[required]', ref: { by: 'id', id: 'test9' }, expect: { count: 0 } }, + + { closest: 'div:not(.div1)', ref: { by: 'id', id: 'test13' }, expect: { ids: ['test7'] } }, + { closest: 'div.div3', ref: { by: 'id', id: 'test6' }, expect: { ids: ['test8'] } }, + { closest: 'div#test7', ref: { by: 'id', id: 'test1' }, expect: { ids: ['test7'] } }, + + { closest: '.div3 > .div2', ref: { by: 'id', id: 'test12' }, expect: { ids: ['test7'] } }, + { closest: '[class="div2"]:has(div)', ref: { by: 'id', id: 'test12' }, expect: { ids: ['test7'] } }, + { closest: '.div3 > .div1', ref: { by: 'id', id: 'test12' }, expect: { count: 0 } }, + { closest: 'form > input[required]', ref: { by: 'id', id: 'test9' }, expect: { count: 0 } }, + { closest: 'fieldset > select[required]', ref: { by: 'id', id: 'test12' }, expect: { ids: ['test3'] } }, + + { closest: 'input + fieldset', ref: { by: 'id', id: 'test6' }, expect: { count: 0 } }, + { closest: 'form + form', ref: { by: 'id', id: 'test3' }, expect: { ids: ['test5'] } }, + { closest: 'form + form', ref: { by: 'id', id: 'test5' }, expect: { ids: ['test5'] } }, + + { closest: ':empty', ref: { by: 'id', id: 'test10' }, expect: { ids: ['test10'] } }, + { closest: ':last-child', ref: { by: 'id', id: 'test11' }, expect: { ids: ['test2'] } }, + { closest: ':first-child', ref: { by: 'id', id: 'test12' }, expect: { ids: ['test3'] } }, + { closest: ':invalid', ref: { by: 'id', id: 'test11' }, expect: { ids: ['test2'] } }, + + { closest: ':scope', ref: { by: 'id', id: 'test4' }, expect: { ids: ['test4'] } }, + { closest: 'select > :scope', ref: { by: 'id', id: 'test4' }, expect: { ids: ['test4'] } }, + { closest: 'div > :scope', ref: { by: 'id', id: 'test4' }, expect: { count: 0 } }, + { closest: ':has(> :scope)', ref: { by: 'id', id: 'test4' }, expect: { ids: ['test3'] } }, + ], + }, + + { + name: 'dom/nodes/selectors', + htmlMode: 'document', + html: ` + + + + + Selectors-API Test Suite: HTML with Selectors Level 2 using TestHarness: Test Document + + + + + + + + +
    +
    + +
    +

    Universal selector tests inside element with id="universal".

    +
    +
    Some preformatted text with some embedded code
    +

    This is a normal link: W3C

    +
    Some more nested elements code hyperlink
    +
    + +
    +
    +
    +
    +
    +

    +
    
    +          
    +
      + + + + +
      + +
      +
      +
      +
      +
      + +
      + + + + + + + + + +
      + +
      +
      + +
      +
      +
      +
      + +
      +
      + + + + + + + + + +

      +
      + +
      +
      +
      +
      +
      +
      + +
      + + + + +
      +
      +
      +
      +
      + +

      +
      + +
      + + + + +
      +
      +
      +
      + +

      +
      + +
      + + + + +
      +
      +
      +
      +
      +
      + +

      +
      + +
      + + + + +
      + +
        +
      1. +
      2. +
      3. +
      4. +
      5. +
      6. +
      7. +
      8. +
      9. +
      10. +
      11. +
      12. +
      + +

      + span1 + em1 + + em2 + span2 + strong1 + em3 + span3 + span4 + strong2 + em4 +

      +
      + +
      +
      +
      +
      + +

      +

      +

      +
      + +
      +

      +

      +

      + +
      +
      +
      +
      + +
      +

      + +

      +

      + + +

      +

      + + + +

      +
      > + +
      +

      +

      +

      +

      Text node

      +

      +
      + + + +
      +
      +
      +
      +
      +
      + +
      + + + + + + + + + + + + + + + + + + + + + + + +
      + +
      +
      +
      +
      + +

      +

      +

      +
      + +
      All pseudo-element tests
      + +
      +

      +

      +

      + + +
      +
      +

      +
      +

      +
      +
      +
      +
      + + + + + + +
      + +
      +
      +
      + +
        +
      • +
      • +
      • +
      • +
      + + + + + + +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +

      +
      +
      +
      +

      +

      +
      + +
      +
      +
      +
      +
      +
      +

      +
      +
      +
      +

      +

      +
      + +
      + + +
      +
      + + + + `, + setupPage: async (page) => { + await page.evaluate(() => { + function setupSpecialElements(doc: Document, parent: Element) { + // Setup null and undefined tests + parent.appendChild(doc.createElement("null")); + parent.appendChild(doc.createElement("undefined")); + + // Setup namespace tests + const anyNS = doc.createElement("div"); + const noNS = doc.createElement("div"); + anyNS.id = "any-namespace"; + noNS.id = "no-namespace"; + + let div = [doc.createElement("div"), + doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), + doc.createElementNS("", "div"), + doc.createElementNS("http://www.example.org/ns", "div")]; + + div[0].id = "any-namespace-div1"; + div[1].id = "any-namespace-div2"; + div[2].setAttribute("id", "any-namespace-div3"); // Non-HTML elements can't use .id property + div[3].setAttribute("id", "any-namespace-div4"); + + for (var i = 0; i < div.length; i++) { + anyNS.appendChild(div[i]) + } + + div = [doc.createElement("div"), + doc.createElementNS("http://www.w3.org/1999/xhtml", "div"), + doc.createElementNS("", "div"), + doc.createElementNS("http://www.example.org/ns", "div")]; + + div[0].id = "no-namespace-div1"; + div[1].id = "no-namespace-div2"; + div[2].setAttribute("id", "no-namespace-div3"); // Non-HTML elements can't use .id property + div[3].setAttribute("id", "no-namespace-div4"); + + for (i = 0; i < div.length; i++) { + noNS.appendChild(div[i]) + } + + parent.appendChild(anyNS); + parent.appendChild(noNS); + + const i1 = doc.getElementById("attr-presence-i1")!; + i1.setAttributeNS("http://www.example.org/ns", "title", ""); + } + + setupSpecialElements(document, document.getElementById("root")!); + + location.hash = '#target'; + }); + }, + cases: [ + // ----------------------------------------------------------------------------- + // Invalid selectors + // ----------------------------------------------------------------------------- + + { select: '', expect: { throws: true } }, + { first: '[', expect: { throws: true } }, + { match: ']', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: '(', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: ')', expect: { throws: true } }, + { first: '{', expect: { throws: true } }, + { match: '}', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: '<', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: '>', expect: { throws: true } }, + { first: '#', expect: { throws: true } }, + { match: 'div,', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: '.', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: '.5cm', expect: { throws: true } }, + { first: '..test', expect: { throws: true } }, + { match: '.foo..quux', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: '.bar.', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: 'div & address, p', expect: { throws: true } }, + { first: 'div ++ address, p', expect: { throws: true } }, + { match: 'div ~~ address, p', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: '[*=test]', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: '[*|*=test]', expect: { throws: true } }, + { first: '[class= space unquoted ]', expect: { throws: true } }, + { match: 'div:example', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: ':example', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: 'div::example', expect: { throws: true } }, + { first: '::example', expect: { throws: true } }, + { match: ':::before', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: ':: before', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: 'ns|div', expect: { throws: true } }, + { first: ':not(ns|div)', expect: { throws: true } }, + { match: '^|div', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + { closest: '$|div', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, + + { select: '>*', expect: { throws: true } }, + + // ----------------------------------------------------------------------------- + // Valid selectors: querySelectorAll / querySelector + // ----------------------------------------------------------------------------- + + // Type Selector + { select: 'html', expect: { ids: ['html'], equivalentCase: { match: 'html', ref: { by: 'id', id: 'html' } } } }, + { select: 'html', ref: { by: 'id', id: 'root' }, expect: { ids: [] } }, + { select: 'body', expect: { ids: ['body'], equivalentCase: { match: 'body', ref: { by: 'id', id: 'body' } } } }, + { select: 'body', ref: { by: 'id', id: 'root' }, expect: { ids: [] } }, + + // Universal Selector + // Testing "*" for entire an entire context node is handled separately. + { select: '#universal>*', expect: { ids: ['universal-p1', 'universal-hr1', 'universal-pre1', 'universal-p2', 'universal-address1'] } }, + { select: '#universal>*>*', expect: { ids: ['universal-code1', 'universal-span1', 'universal-a1', 'universal-code2'] } }, + { select: '#empty>*', expect: { ids: [] } }, + { select: '#universal *', expect: { ids: ['universal-p1', 'universal-code1', 'universal-hr1', 'universal-pre1', 'universal-span1', 'universal-p2', 'universal-a1', 'universal-address1', 'universal-code2', 'universal-a2'] } }, + + // Attribute Selectors + // - presence [att] + { select: '.attr-presence-div1[align]', expect: { ids: ['attr-presence-div1'] } }, + { select: '.attr-presence-div2[align]', expect: { ids: ['attr-presence-div2'] } }, + { select: '#attr-presence [*|TiTlE]', expect: { ids: ['attr-presence-a1', 'attr-presence-span1', 'attr-presence-i1'] } }, + { select: '[data-attr-presence]', expect: { ids: ['attr-presence-pre1', 'attr-presence-blockquote1'] } }, + { select: '.attr-presence-div3[align], .attr-presence-div4[align]', expect: { ids: [] } }, + { select: 'ul[data-中文]', expect: { ids: ['attr-presence-ul1'] } }, + { select: '#attr-presence-select1 option[selected]', expect: { ids: [] } }, + { select: '#attr-presence-select2 option[selected]', expect: { ids: ['attr-presence-select2-option4'] } }, + { select: '#attr-presence-select3 option[selected]', expect: { ids: ['attr-presence-select3-option2', 'attr-presence-select3-option3'] } }, + + // - value [att=val] + { select: '#attr-value [align="center"]', expect: { ids: ['attr-value-div1'] } }, + { select: '#attr-value [align="center"', expect: { ids: ['attr-value-div1'] } }, + { select: '#attr-value [align=""]', expect: { ids: ['attr-value-div2'] } }, + { select: '#attr-value [align="c"]', expect: { ids: [] } }, + { select: '#attr-value [align="centera"]', expect: { ids: [] } }, + { select: '[data-attr-value="\\e9"]', expect: { ids: ['attr-value-div3'] } }, + { select: '[data-attr-value\\_foo="\\e9"]', expect: { ids: ['attr-value-div4'] } }, + { select: "#attr-value input[type='hidden'],#attr-value input[type='radio']", expect: { ids: ['attr-value-input3', 'attr-value-input4', 'attr-value-input6', 'attr-value-input8', 'attr-value-input9'] } }, + { select: "#attr-value input[type=\"hidden\"],#attr-value input[type='radio']", expect: { ids: ['attr-value-input3', 'attr-value-input4', 'attr-value-input6', 'attr-value-input8', 'attr-value-input9'] } }, + { select: '#attr-value input[type=hidden],#attr-value input[type=radio]', expect: { ids: ['attr-value-input3', 'attr-value-input4', 'attr-value-input6', 'attr-value-input8', 'attr-value-input9'] } }, + { select: '[data-attr-value=中文]', expect: { ids: ['attr-value-div5'] } }, + + // - whitespace-separated list [att~=val] + { select: '#attr-whitespace [class~="div1"]', expect: { ids: ['attr-whitespace-div1'] } }, + { select: '#attr-whitespace [class~=""]', expect: { ids: [] } }, + { select: '[data-attr-whitespace~="div"]', expect: { ids: [] } }, + { select: '[data-attr-whitespace~="\\0000e9"]', expect: { ids: ['attr-whitespace-div4'] } }, + { select: '[data-attr-whitespace\\_foo~="\\e9"]', expect: { ids: ['attr-whitespace-div5'] } }, + { select: "#attr-whitespace a[rel~='bookmark'], #attr-whitespace a[rel~='nofollow']", expect: { ids: ['attr-whitespace-a1', 'attr-whitespace-a2', 'attr-whitespace-a3', 'attr-whitespace-a5', 'attr-whitespace-a7'] } }, + { select: "#attr-whitespace a[rel~=\"bookmark\"],#attr-whitespace a[rel~='nofollow']", expect: { ids: ['attr-whitespace-a1', 'attr-whitespace-a2', 'attr-whitespace-a3', 'attr-whitespace-a5', 'attr-whitespace-a7'] } }, + { select: '#attr-whitespace a[rel~=bookmark], #attr-whitespace a[rel~=nofollow]', expect: { ids: ['attr-whitespace-a1', 'attr-whitespace-a2', 'attr-whitespace-a3', 'attr-whitespace-a5', 'attr-whitespace-a7'] } }, + { select: '#attr-whitespace a[rel~="book mark"]', expect: { ids: [] }, status: 'fixme' } , // fixme + { select: '#attr-whitespace [title~=中文]', expect: { ids: ['attr-whitespace-p1'] } }, + + // - hyphen-separated list [att|=val] + { select: '#attr-hyphen-div1[lang|="en"]', expect: { ids: [] } }, + { select: '#attr-hyphen-div2[lang|="fr"]', expect: { ids: ['attr-hyphen-div2'], equivalentCase: { match: '#attr-hyphen-div2[lang|="fr"]', ref: { by: 'id', id: 'attr-hyphen-div2' } } } }, + { select: '#attr-hyphen-div3[lang|="en"]', expect: { ids: ['attr-hyphen-div3'], equivalentCase: { match: '#attr-hyphen-div3[lang|="en"]', ref: { by: 'id', id: 'attr-hyphen-div3' } } } }, + { select: '#attr-hyphen-div4[lang|="es-AR"]', expect: { ids: [] } }, + + // - substring begins-with [att^=val] (Level 3) + { select: '#attr-begins a[href^="http://www"]', expect: { ids: ['attr-begins-a1', 'attr-begins-a3'] } }, + { select: '#attr-begins [lang^="en-"]', expect: { ids: ['attr-begins-div2', 'attr-begins-div4'] } }, + { select: '#attr-begins [class^=""]', expect: { ids: [] } }, + { select: '#attr-begins [class^=apple]', expect: { ids: [] } }, + { select: "#attr-begins [class^=' apple']", expect: { ids: ['attr-begins-p1'], equivalentCase: { match: "#attr-begins [class^=' apple']", ref: { by: 'id', id: 'attr-begins-p1' } } } }, + { select: '#attr-begins [class^=" apple"]', expect: { ids: ['attr-begins-p1'], equivalentCase: { match: '#attr-begins [class^=" apple"]', ref: { by: 'id', id: 'attr-begins-p1' } } } }, + { select: '#attr-begins [class^= apple]', expect: { ids: [] } }, + + // - substring ends-with [att$=val] (Level 3) + { select: '#attr-ends a[href$=".org"]', expect: { ids: ['attr-ends-a1', 'attr-ends-a3'] } }, + { select: '#attr-ends [lang$="-CH"]', expect: { ids: ['attr-ends-div2', 'attr-ends-div4'] } }, + { select: '#attr-ends [class$=""]', expect: { ids: [] } }, + { select: '#attr-ends [class$=apple]', expect: { ids: [] } }, + { select: "#attr-ends [class$='apple ']", expect: { ids: ['attr-ends-p1'], equivalentCase: { match: "#attr-ends [class$='apple ']", ref: { by: 'id', id: 'attr-ends-p1' } } } }, + { select: '#attr-ends [class$="apple "]', expect: { ids: ['attr-ends-p1'], equivalentCase: { match: '#attr-ends [class$="apple "]', ref: { by: 'id', id: 'attr-ends-p1' } } } }, + { select: '#attr-ends [class$=apple ]', expect: { ids: [] } }, + + // - substring contains [att*=val] (Level 3) + { select: '#attr-contains a[href*="http://www"]', expect: { ids: ['attr-contains-a1', 'attr-contains-a3'] } }, + { select: '#attr-contains a[href*=".org"]', expect: { ids: ['attr-contains-a1', 'attr-contains-a2'] } }, + { select: '#attr-contains a[href*=".example."]', expect: { ids: ['attr-contains-a1', 'attr-contains-a3'] } }, + { select: '#attr-contains [lang*="en-"]', expect: { ids: ['attr-contains-div2', 'attr-contains-div6'] } }, + { select: '#attr-contains [lang*="-CH"]', expect: { ids: ['attr-contains-div3', 'attr-contains-div5'] } }, + { select: '#attr-contains [class*=""]', expect: { ids: [] } }, + { select: "#attr-contains [class*=' apple']", expect: { ids: ['attr-contains-p1'], equivalentCase: { match: "#attr-contains [class*=' apple']", ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: "#attr-contains [class*='orange ']", expect: { ids: ['attr-contains-p1'], equivalentCase: { match: "#attr-contains [class*='orange ']", ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: "#attr-contains [class*='ple banana ora']", expect: { ids: ['attr-contains-p1'], equivalentCase: { match: "#attr-contains [class*='ple banana ora']", ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: '#attr-contains [class*=" apple"]', expect: { ids: ['attr-contains-p1'], equivalentCase: { match: '#attr-contains [class*=" apple"]', ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: '#attr-contains [class*="orange "]', expect: { ids: ['attr-contains-p1'], equivalentCase: { match: '#attr-contains [class*="orange "]', ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: '#attr-contains [class*="ple banana ora"]', expect: { ids: ['attr-contains-p1'], equivalentCase: { match: '#attr-contains [class*="ple banana ora"]', ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: '#attr-contains [class*= apple]', expect: { ids: ['attr-contains-p1'], equivalentCase: { match: '#attr-contains [class*= apple]', ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: '#attr-contains [class*=orange ]', expect: { ids: ['attr-contains-p1'], equivalentCase: { match: '#attr-contains [class*=orange ]', ref: { by: 'id', id: 'attr-contains-p1' } } } }, + { select: '#attr-contains [class*= banana ]', expect: { ids: ['attr-contains-p1'], equivalentCase: { match: '#attr-contains [class*= banana ]', ref: { by: 'id', id: 'attr-contains-p1' } } } }, + + // Pseudo-classes + // - :root (Level 3) + { select: ':root', expect: { ids: ['html'], equivalentCase: { match: ':root', ref: { by: 'id', id: 'html' } } } }, + { select: ':root', ref: { by: 'id', id: 'root' }, expect: { ids: [] } }, + + // - :nth-child(n) (Level 3) + // XXX write descriptions + { select: '#pseudo-nth-table1 :nth-child(3)', expect: { ids: ['pseudo-nth-td3', 'pseudo-nth-td9', 'pseudo-nth-tr3', 'pseudo-nth-td15'] } }, + { select: '#pseudo-nth li:nth-child(3n)', expect: { ids: ['pseudo-nth-li3', 'pseudo-nth-li6', 'pseudo-nth-li9', 'pseudo-nth-li12'] } }, + { select: '#pseudo-nth li:nth-child(2n+4)', expect: { ids: ['pseudo-nth-li4', 'pseudo-nth-li6', 'pseudo-nth-li8', 'pseudo-nth-li10', 'pseudo-nth-li12'] } }, + { select: '#pseudo-nth-p1 :nth-child(4n-1)', expect: { ids: ['pseudo-nth-em2', 'pseudo-nth-span3'] } }, + + // - :nth-last-child (Level 3) + { select: '#pseudo-nth-table1 :nth-last-child(3)', expect: { ids: ['pseudo-nth-tr1', 'pseudo-nth-td4', 'pseudo-nth-td10', 'pseudo-nth-td16'] } }, + { select: '#pseudo-nth li:nth-last-child(3n)', expect: { ids: ['pseudo-nth-li1', 'pseudo-nth-li4', 'pseudo-nth-li7', 'pseudo-nth-li10'] } }, + { select: '#pseudo-nth li:nth-last-child(2n+4)', expect: { ids: ['pseudo-nth-li1', 'pseudo-nth-li3', 'pseudo-nth-li5', 'pseudo-nth-li7', 'pseudo-nth-li9'] } }, + { select: '#pseudo-nth-p1 :nth-last-child(4n-1)', expect: { ids: ['pseudo-nth-span2', 'pseudo-nth-span4'] } }, + + // - :nth-of-type(n) (Level 3) + { select: '#pseudo-nth-p1 em:nth-of-type(3)', expect: { ids: ['pseudo-nth-em3'], equivalentCase: { match: '#pseudo-nth-p1 em:nth-of-type(3)', ref: { by: 'id', id: 'pseudo-nth-em3' } } } }, + { select: '#pseudo-nth-p1 :nth-of-type(2n)', expect: { ids: ['pseudo-nth-em2', 'pseudo-nth-span2', 'pseudo-nth-span4', 'pseudo-nth-strong2', 'pseudo-nth-em4'] } }, + { select: '#pseudo-nth-p1 span:nth-of-type(2n-1)', expect: { ids: ['pseudo-nth-span1', 'pseudo-nth-span3'] } }, + + // - :nth-last-of-type(n) (Level 3) + { select: '#pseudo-nth-p1 em:nth-last-of-type(3)', expect: { ids: ['pseudo-nth-em2'], equivalentCase: { match: '#pseudo-nth-p1 em:nth-last-of-type(3)', ref: { by: 'id', id: 'pseudo-nth-em2' } } } }, + { select: '#pseudo-nth-p1 :nth-last-of-type(2n)', expect: { ids: ['pseudo-nth-span1', 'pseudo-nth-em1', 'pseudo-nth-strong1', 'pseudo-nth-em3', 'pseudo-nth-span3'] } }, + { select: '#pseudo-nth-p1 span:nth-last-of-type(2n-1)', expect: { ids: ['pseudo-nth-span2', 'pseudo-nth-span4'] } }, + + // - :first-of-type (Level 3) + { select: '#pseudo-nth-p1 em:first-of-type', expect: { ids: ['pseudo-nth-em1'], equivalentCase: { match: '#pseudo-nth-p1 em:first-of-type', ref: { by: 'id', id: 'pseudo-nth-em1' } } } }, + { select: '#pseudo-nth-p1 :first-of-type', expect: { ids: ['pseudo-nth-span1', 'pseudo-nth-em1', 'pseudo-nth-strong1'] } }, + { select: '#pseudo-nth-table1 tr :first-of-type', expect: { ids: ['pseudo-nth-td1', 'pseudo-nth-td7', 'pseudo-nth-td13'] } }, + + // - :last-of-type (Level 3) + { select: '#pseudo-nth-p1 em:last-of-type', expect: { ids: ['pseudo-nth-em4'], equivalentCase: { match: '#pseudo-nth-p1 em:last-of-type', ref: { by: 'id', id: 'pseudo-nth-em4' } } } }, + { select: '#pseudo-nth-p1 :last-of-type', expect: { ids: ['pseudo-nth-span4', 'pseudo-nth-strong2', 'pseudo-nth-em4'] } }, + { select: '#pseudo-nth-table1 tr :last-of-type', expect: { ids: ['pseudo-nth-td6', 'pseudo-nth-td12', 'pseudo-nth-td18'] } }, + + // - :first-child + { select: '#pseudo-first-child div:first-child', expect: { ids: ['pseudo-first-child-div1'], equivalentCase: { match: '#pseudo-first-child div:first-child', ref: { by: 'id', id: 'pseudo-first-child-div1' } } } }, + { select: '.pseudo-first-child-div2:first-child, .pseudo-first-child-div3:first-child', expect: { ids: [] } }, + { select: '#pseudo-first-child span:first-child', expect: { ids: ['pseudo-first-child-span1', 'pseudo-first-child-span3', 'pseudo-first-child-span5'] } }, + + // - :last-child (Level 3) + { select: '#pseudo-last-child div:last-child', expect: { ids: ['pseudo-last-child-div3'], equivalentCase: { match: '#pseudo-last-child div:last-child', ref: { by: 'id', id: 'pseudo-last-child-div3' } } } }, + { select: '.pseudo-last-child-div1:last-child, .pseudo-last-child-div2:first-child', expect: { ids: [] } }, + { select: '#pseudo-last-child span:last-child', expect: { ids: ['pseudo-last-child-span2', 'pseudo-last-child-span4', 'pseudo-last-child-span6'] } }, + + // - :only-child (Level 3) + { select: '#pseudo-only :only-child', expect: { ids: ['pseudo-only-span1'], equivalentCase: { match: '#pseudo-only :only-child', ref: { by: 'id', id: 'pseudo-only-span1' } } } }, + { select: '#pseudo-only em:only-child', expect: { ids: [] } }, + + // - :only-of-type (Level 3) + { select: '#pseudo-only :only-of-type', expect: { ids: ['pseudo-only-span1', 'pseudo-only-em1'] } }, + { select: '#pseudo-only em:only-of-type', expect: { ids: ['pseudo-only-em1'], equivalentCase: { match: '#pseudo-only em:only-of-type', ref: { by: 'id', id: 'pseudo-only-em1' } } } }, + + // - :empty (Level 3) + { select: '#pseudo-empty p:empty', expect: { ids: ['pseudo-empty-p1', 'pseudo-empty-p2'] } }, + { select: '#pseudo-empty :empty', expect: { ids: ['pseudo-empty-p1', 'pseudo-empty-p2', 'pseudo-empty-span1'] } }, + + // - :link and :visited + // Implementations may treat all visited links as unvisited, so these cannot be tested separately. + // The only guarantee is that ":link,:visited" matches the set of all visited and unvisited links and that they are individually mutually exclusive sets. + { select: '#pseudo-link :link, #pseudo-link :visited', expect: { ids: ['pseudo-link-a1', 'pseudo-link-a2', 'pseudo-link-area1'] } }, + { select: '#head :link, #head :visited', expect: { ids: ['pseudo-link-link1', 'pseudo-link-link2'] }, status: 'fail' }, // spec issue: `:link` no longer matches `` + { select: '#head :link, #head :visited', ref: { by: 'id', id: 'root' }, expect: { ids: [] } }, + { select: ':link:visited', ref: { by: 'id', id: 'root' }, expect: { ids: [] } }, + + // - :target (Level 3) + { select: ':target', ref: { by: 'id', id: 'target', home: 'detached' }, expect: { ids: [] } }, + { select: ':target', expect: { ids: ['target'], equivalentCase: { match: ':target', ref: { by: 'id', id: 'target' } } } }, + + // - :lang() + { select: '#pseudo-lang-div1:lang(en)', expect: { ids: ['pseudo-lang-div1'], equivalentCase: { match: '#pseudo-lang-div1:lang(en)', ref: { by: 'id', id: 'pseudo-lang-div1' } } } }, + { select: '#pseudo-lang-div1:lang(en)', ref: { by: 'id', id: 'root', home: 'fragment' }, expect: { ids: [] } }, + { select: '#pseudo-lang-div2:lang(fr)', expect: { ids: ['pseudo-lang-div2'], equivalentCase: { match: '#pseudo-lang-div2:lang(fr)', ref: { by: 'id', id: 'pseudo-lang-div2' } } } }, + { select: '#pseudo-lang-div3:lang(en)', expect: { ids: ['pseudo-lang-div3'], equivalentCase: { match: '#pseudo-lang-div3:lang(en)', ref: { by: 'id', id: 'pseudo-lang-div3' } } } }, + { select: '#pseudo-lang-div4:lang(es-AR)', expect: { ids: [] } }, + + // - :enabled (Level 3) + { select: '#pseudo-ui :enabled', expect: { ids: ['pseudo-ui-input1', 'pseudo-ui-input2', 'pseudo-ui-input3', 'pseudo-ui-input4', 'pseudo-ui-input5', 'pseudo-ui-input6', 'pseudo-ui-input7', 'pseudo-ui-input8', 'pseudo-ui-input9', 'pseudo-ui-textarea1', 'pseudo-ui-button1'] } }, + + // - :disabled (Level 3) + { select: '#pseudo-ui :disabled', expect: { ids: ['pseudo-ui-input10', 'pseudo-ui-input11', 'pseudo-ui-input12', 'pseudo-ui-input13', 'pseudo-ui-input14', 'pseudo-ui-input15', 'pseudo-ui-input16', 'pseudo-ui-input17', 'pseudo-ui-input18', 'pseudo-ui-textarea2', 'pseudo-ui-button2'] } }, + + // - :checked (Level 3) + { select: '#pseudo-ui :checked', expect: { ids: ['pseudo-ui-input4', 'pseudo-ui-input6', 'pseudo-ui-input13', 'pseudo-ui-input15'] } }, + + // - :not(s) (Level 3) + { select: '#not>:not(div)', expect: { ids: ['not-p1', 'not-p2', 'not-p3'] } }, + { select: '#not * :not(:first-child)', expect: { ids: ['not-em1', 'not-em2', 'not-em3'] } }, + { select: ':not(*)', expect: { ids: [] } }, + { select: ':not(*|*)', expect: { ids: [] }, status: 'fixme' }, + + // Pseudo-elements + // - ::first-line + { select: '#pseudo-element:first-line', expect: { ids: [] } }, + { select: '#pseudo-element::first-line', expect: { ids: [] } }, + + // - ::first-letter + { select: '#pseudo-element:first-letter', expect: { ids: [] } }, + { select: '#pseudo-element::first-letter', expect: { ids: [] } }, + + // - ::before + { select: '#pseudo-element:before', expect: { ids: [] } }, + { select: '#pseudo-element::before', expect: { ids: [] } }, + + // - ::after + { select: '#pseudo-element:after', expect: { ids: [] } }, + { select: '#pseudo-element::after', expect: { ids: [] } }, + + // Class Selectors + { select: '.class-p', expect: { ids: ['class-p1', 'class-p2', 'class-p3'] } }, + { select: '#class .apple.orange.banana', expect: { ids: ['class-div1', 'class-div2', 'class-p4', 'class-div3', 'class-p6', 'class-div4'] } }, + { select: 'div.apple.banana.orange', expect: { ids: ['class-div1', 'class-div2', 'class-div3', 'class-div4'] } }, + { select: '.\u53F0\u5317Ta\u0301ibe\u030Ci', expect: { ids: ['class-span1'], equivalentCase: { match: '.\u53F0\u5317Ta\u0301ibe\u030Ci', ref: { by: 'id', id: 'class-span1' } } } }, + { select: '.\u53F0\u5317', expect: { ids: ['class-span1', 'class-span2'] } }, + { select: '.\u53F0\u5317Ta\u0301ibe\u030Ci.\u53F0\u5317', expect: { ids: ['class-span1'], equivalentCase: { match: '.\u53F0\u5317Ta\u0301ibe\u030Ci.\u53F0\u5317', ref: { by: 'id', id: 'class-span1' } } } }, + { select: '.foo\\:bar', expect: { ids: ['class-span3'], equivalentCase: { match: '.foo\\:bar', ref: { by: 'id', id: 'class-span3' } } } }, + { select: '.test\\.foo\\[5\\]bar', expect: { ids: ['class-span4'], equivalentCase: { match: '.test\\.foo\\[5\\]bar', ref: { by: 'id', id: 'class-span4' } } } }, + + // ID Selectors + { select: '#id #id-div1', expect: { ids: ['id-div1'], equivalentCase: { match: '#id #id-div1', ref: { by: 'id', id: 'id-div1' } } } }, + { select: '#id-div1, #id-div1', expect: { ids: ['id-div1'], equivalentCase: { match: '#id-div1, #id-div1', ref: { by: 'id', id: 'id-div1' } } } }, + { select: '#id-div1, #id-div2', expect: { ids: ['id-div1', 'id-div2'] } }, + { select: 'div#id-div1, div#id-div2', expect: { ids: ['id-div1', 'id-div2'] } }, + { select: '#id #none', expect: { ids: [] } }, + { select: '#none #id-div1', expect: { ids: [] } }, + { select: '#id-li-duplicate', expect: { ids: ['id-li-duplicate', 'id-li-duplicate', 'id-li-duplicate', 'id-li-duplicate'] } }, + + { select: '#\u53F0\u5317Ta\u0301ibe\u030Ci', expect: { ids: ['\u53F0\u5317Ta\u0301ibe\u030Ci'], equivalentCase: { match: '#\u53F0\u5317Ta\u0301ibe\u030Ci', ref: { by: 'id', id: '\u53F0\u5317Ta\u0301ibe\u030Ci' } } } }, + { select: '#\u53F0\u5317', expect: { ids: ['\u53F0\u5317'], equivalentCase: { match: '#\u53F0\u5317', ref: { by: 'id', id: '\u53F0\u5317' } } } }, + { select: '#\u53F0\u5317Ta\u0301ibe\u030Ci, #\u53F0\u5317', expect: { ids: ['\u53F0\u5317Ta\u0301ibe\u030Ci', '\u53F0\u5317'] } }, + + // XXX runMatchesTest() in level2-lib.js can't handle this because obtaining the expected nodes requires escaping characters when generating the selector from 'expect' values + { select: '#\\#foo\\:bar', expect: { ids: ['#foo:bar'] } }, + { select: '#test\\.foo\\[5\\]bar', expect: { ids: ['test.foo[5]bar'] } }, + + // Namespaces + // XXX runMatchesTest() in level2-lib.js can't handle these because non-HTML elements don't have a recognised id + { select: '#any-namespace *|div', expect: { ids: ['any-namespace-div1', 'any-namespace-div2', 'any-namespace-div3', 'any-namespace-div4'] }, status: 'fixme' }, + { select: '#no-namespace |div', expect: { ids: ['no-namespace-div3'] }, status: 'fixme' }, + { select: '#no-namespace |*', expect: { ids: ['no-namespace-div3'] }, status: 'fixme' }, + + // Combinators + // - Descendant combinator ' ' + { select: '#descendant div', expect: { ids: ['descendant-div1', 'descendant-div2', 'descendant-div3', 'descendant-div4'] } }, + { select: 'body #descendant-div1', expect: { ids: ['descendant-div1'], equivalentCase: { match: 'body #descendant-div1', ref: { by: 'id', id: 'descendant-div1' } } } }, + { select: 'div #descendant-div1', expect: { ids: ['descendant-div1'], equivalentCase: { match: 'div #descendant-div1', ref: { by: 'id', id: 'descendant-div1' } } } }, + { select: '#descendant #descendant-div2', expect: { ids: ['descendant-div2'], equivalentCase: { match: '#descendant #descendant-div2', ref: { by: 'id', id: 'descendant-div2' } } } }, + { select: '#descendant .descendant-div2', expect: { ids: ['descendant-div2'], equivalentCase: { match: '#descendant .descendant-div2', ref: { by: 'id', id: 'descendant-div2' } } } }, + { select: '.descendant-div1 .descendant-div3', expect: { ids: ['descendant-div3'], equivalentCase: { match: '.descendant-div1 .descendant-div3', ref: { by: 'id', id: 'descendant-div3' } } } }, + { select: '#descendant-div1 #descendant-div4', expect: { ids: [] } }, + { select: '#descendant\t\r\n#descendant-div2', expect: { ids: ['descendant-div2'], equivalentCase: { match: '#descendant\t\r\n#descendant-div2', ref: { by: 'id', id: 'descendant-div2' } } } }, + + /* The future of this combinator is uncertain, see + * https://github.com/w3c/csswg-drafts/issues/641 + * These tests are commented out until a final decision is made on whether to + * keep the feature in the spec. + * + * Removed from Selectors 4; 4/11/2026 + */ + + // - Descendant combinator '>>' + { select: '#descendant>>div', expect: { ids: ['descendant-div1', 'descendant-div2', 'descendant-div3', 'descendant-div4'] }, status: 'fail' }, + { select: 'body>>#descendant-div1', expect: { ids: ['descendant-div1'] }, status: 'fail' }, + { select: 'div>>#descendant-div1', expect: { ids: ['descendant-div1'] }, status: 'fail' }, + { select: '#descendant>>#descendant-div2', expect: { ids: ['descendant-div2'] }, status: 'fail' }, + { select: '#descendant>>.descendant-div2', expect: { ids: ['descendant-div2'] }, status: 'fail' }, + { select: '.descendant-div1>>.descendant-div3', expect: { ids: ['descendant-div3'] }, status: 'fail' }, + { select: '#descendant-div1>>#descendant-div4', expect: { ids: [] }, status: 'fail' }, + + // - Child combinator '>' + { select: '#child>div', expect: { ids: ['child-div1', 'child-div4'] } }, + { select: 'div>#child-div1', expect: { ids: ['child-div1'], equivalentCase: { match: 'div>#child-div1', ref: { by: 'id', id: 'child-div1' } } } }, + { select: '#child>#child-div1', expect: { ids: ['child-div1'], equivalentCase: { match: '#child>#child-div1', ref: { by: 'id', id: 'child-div1' } } } }, + { select: '#child-div1>.child-div2', expect: { ids: ['child-div2'], equivalentCase: { match: '#child-div1>.child-div2', ref: { by: 'id', id: 'child-div2' } } } }, + { select: '.child-div1>.child-div2', expect: { ids: ['child-div2'], equivalentCase: { match: '.child-div1>.child-div2', ref: { by: 'id', id: 'child-div2' } } } }, + { select: '#child>#child-div3', expect: { ids: [] } }, + { select: '#child-div1>.child-div3', expect: { ids: [] } }, + { select: '.child-div1>.child-div3', expect: { ids: [] } }, + { select: '#child-div1\t\r\n>\t\r\n#child-div2', expect: { ids: ['child-div2'], equivalentCase: { match: '#child-div1\t\r\n>\t\r\n#child-div2', ref: { by: 'id', id: 'child-div2' } } } }, + { select: '#child-div1>\t\r\n#child-div2', expect: { ids: ['child-div2'], equivalentCase: { match: '#child-div1>\t\r\n#child-div2', ref: { by: 'id', id: 'child-div2' } } } }, + { select: '#child-div1\t\r\n>#child-div2', expect: { ids: ['child-div2'], equivalentCase: { match: '#child-div1\t\r\n>#child-div2', ref: { by: 'id', id: 'child-div2' } } } }, + { select: '#child-div1>#child-div2', expect: { ids: ['child-div2'], equivalentCase: { match: '#child-div1>#child-div2', ref: { by: 'id', id: 'child-div2' } } } }, + + // - Adjacent sibling combinator '+' + { select: '#adjacent-div2+div', expect: { ids: ['adjacent-div4'] } }, + { select: 'div+#adjacent-div4', expect: { ids: ['adjacent-div4'], equivalentCase: { match: 'div+#adjacent-div4', ref: { by: 'id', id: 'adjacent-div4' } } } }, + { select: '#adjacent-div2+#adjacent-div4', expect: { ids: ['adjacent-div4'], equivalentCase: { match: '#adjacent-div2+#adjacent-div4', ref: { by: 'id', id: 'adjacent-div4' } } } }, + { select: '#adjacent-div2+.adjacent-div4', expect: { ids: ['adjacent-div4'], equivalentCase: { match: '#adjacent-div2+.adjacent-div4', ref: { by: 'id', id: 'adjacent-div4' } } } }, + { select: '.adjacent-div2+.adjacent-div4', expect: { ids: ['adjacent-div4'], equivalentCase: { match: '.adjacent-div2+.adjacent-div4', ref: { by: 'id', id: 'adjacent-div4' } } } }, + { select: '#adjacent div+p', expect: { ids: ['adjacent-p2'] } }, + { select: '#adjacent-div2+#adjacent-p2, #adjacent-div2+#adjacent-div1', expect: { ids: [] } }, + { select: '#adjacent-p2\t\r\n+\t\r\n#adjacent-p3', expect: { ids: ['adjacent-p3'], equivalentCase: { match: '#adjacent-p2\t\r\n+\t\r\n#adjacent-p3', ref: { by: 'id', id: 'adjacent-p3' } } } }, + { select: '#adjacent-p2+\t\r\n#adjacent-p3', expect: { ids: ['adjacent-p3'], equivalentCase: { match: '#adjacent-p2+\t\r\n#adjacent-p3', ref: { by: 'id', id: 'adjacent-p3' } } } }, + { select: '#adjacent-p2\t\r\n+#adjacent-p3', expect: { ids: ['adjacent-p3'], equivalentCase: { match: '#adjacent-p2\t\r\n+#adjacent-p3', ref: { by: 'id', id: 'adjacent-p3' } } } }, + { select: '#adjacent-p2+#adjacent-p3', expect: { ids: ['adjacent-p3'], equivalentCase: { match: '#adjacent-p2+#adjacent-p3', ref: { by: 'id', id: 'adjacent-p3' } } } }, + + // - General sibling combinator ~ (Level 3) + { select: '#sibling-div2~div', expect: { ids: ['sibling-div4', 'sibling-div6'] } }, + { select: 'div~#sibling-div4', expect: { ids: ['sibling-div4'], equivalentCase: { match: 'div~#sibling-div4', ref: { by: 'id', id: 'sibling-div4' } } } }, + { select: '#sibling-div2~#sibling-div4', expect: { ids: ['sibling-div4'], equivalentCase: { match: '#sibling-div2~#sibling-div4', ref: { by: 'id', id: 'sibling-div4' } } } }, + { select: '#sibling-div2~.sibling-div', expect: { ids: ['sibling-div4', 'sibling-div6'] } }, + { select: '#sibling div~p', expect: { ids: ['sibling-p2', 'sibling-p3'] } }, + { select: '#sibling>p~div', expect: { ids: [] } }, + { select: '#sibling-div2~#sibling-div3, #sibling-div2~#sibling-div1', expect: { ids: [] } }, + { select: '#sibling-p2\t\r\n~\t\r\n#sibling-p3', expect: { ids: ['sibling-p3'], equivalentCase: { match: '#sibling-p2\t\r\n~\t\r\n#sibling-p3', ref: { by: 'id', id: 'sibling-p3' } } } }, + { select: '#sibling-p2~\t\r\n#sibling-p3', expect: { ids: ['sibling-p3'], equivalentCase: { match: '#sibling-p2~\t\r\n#sibling-p3', ref: { by: 'id', id: 'sibling-p3' } } } }, + { select: '#sibling-p2\t\r\n~#sibling-p3', expect: { ids: ['sibling-p3'], equivalentCase: { match: '#sibling-p2\t\r\n~#sibling-p3', ref: { by: 'id', id: 'sibling-p3' } } } }, + { select: '#sibling-p2~#sibling-p3', expect: { ids: ['sibling-p3'], equivalentCase: { match: '#sibling-p2~#sibling-p3', ref: { by: 'id', id: 'sibling-p3' } } } }, + + // Group of selectors (comma) + { select: '#group em\t\r \n,\t\r \n#group strong', expect: { ids: ['group-em1', 'group-strong1'] } }, + { select: '#group em,\t\r\n#group strong', expect: { ids: ['group-em1', 'group-strong1'] } }, + { select: '#group em\t\r\n,#group strong', expect: { ids: ['group-em1', 'group-strong1'] } }, + { select: '#group em,#group strong', expect: { ids: ['group-em1', 'group-strong1'] } }, + + // ----------------------------------------------------------------------------- + // Scoped Selectors + // ----------------------------------------------------------------------------- + + /* + * Scoped selectors -- RIP, 2010–2013. + * + * The abandoned Selectors API Level 2 draft included `find()`, `findAll()`, + * and `matches(selector, refNodes)`. + * + * That spec never advanced beyond Working Draft / Working Group Note status + * and was retired in 2013. Modern DOM standardized the narrower one-argument + * selector APIs instead: `querySelector(All)`, `matches(selector)`, and + * `closest(selector)`. + * + * Unable to migrate the rest of the legacy tests. + */ + ], + }, + + { + name: 'html/semantics/scripting-1/template-end-tag-without-start-one-in-body', + html: ` + +
      The file contains several </template> tag in HTML body without start one
      + + `, + cases: [ + { select: 'template', expect: { ids: [] } }, + { select: 'div', expect: { count: 1 } }, + ] + }, + + { + name: 'html/semantics/scripting-1/html-start-tag-in-body', + html: ` + + `, + cases: [ + { select: 'template', expect: { ids: ['tmpl'] } }, + { select: 'html', expect: { count: 1 } }, + { select: '.htmlClass', expect: { ids: [] } }, + { select: 'template > *', expect: { count: 0 } }, + ] + }, + + { + name: 'html/semantics/scripting-1/template-contents-body', + html: ` + + `, + // BODY tokens inside template contents are ignored + cases: [ + { select: 'template', expect: { count: 1 } }, + { select: 'template > *', expect: { count: 0 } }, + ] + }, + + { + name: 'html/semantics/scripting-1/template-contents-div-no-end-tag', + html: ` + + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const template = document.querySelector('template'); + if (!(template instanceof HTMLTemplateElement)) { + return null; + } + + return { + divCount: template.content.querySelectorAll('div').length, + }; + }); + + expect(result).not.toBeNull(); + expect(result!.divCount).toEqual(1); + }, + cases: [ + { select: 'template', expect: { count: 1 } }, + { select: 'template > div', expect: { count: 1 }, status: 'fail' }, // outside template.content + ] + }, + + { + name: 'html/semantics/scripting-1/template-contents-frameset', + // FRAMESET inside template is ignored; template stays empty. + html: ` + + `, + cases: [ + { select: 'template', expect: { count: 1 } }, + { select: 'template > *', expect: { count: 0 } }, + ] + }, + + { + name: 'html/semantics/scripting-1/template-contents-head', + // HEAD inside template is ignored; template stays empty. + html: ` + + `, + cases: [ + { select: 'template', expect: { count: 1 } }, + { select: 'template > *', expect: { count: 0 } }, + ] + }, + + { + name: 'html/semantics/scripting-1/template-contents-html', + // HTML inside template is ignored; template stays empty. + html: ` + + `, + cases: [ + { select: 'template', expect: { count: 1 } }, + { select: 'template > *', expect: { count: 0 } }, + ] + }, + + { + name: 'html/semantics/scripting-1/template-contents-table-no-end-tag', + html: ` + + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const template = document.querySelector('template'); + if (!(template instanceof HTMLTemplateElement)) { + return null; + } + + return { + tableCount: template.content.querySelectorAll('table').length, + trCount: template.content.querySelectorAll('tr').length, + tdCount: template.content.querySelectorAll('td').length, + }; + }); + + expect(result).not.toBeNull(); + expect(result!.tableCount).toEqual(1); + expect(result!.trCount).toEqual(1); + expect(result!.tdCount).toEqual(1); + }, + cases: [ + { select: 'template', expect: { count: 1 } }, + { select: 'table', expect: { count: 1 }, status: 'fail' }, // outside template.content + { select: 'tr', expect: { count: 1 }, status: 'fail' }, // outside template.content + { select: 'td', expect: { count: 1 }, status: 'fail' }, // outside template.content + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/checked-001-manual', + html: ` +

      X

      +

      X X

      +

      +

      + `, + cases: [ + { select: ':checked', expect: { count: 4 } }, + { select: ':checked + span', expect: { count: 2 } }, + { select: ':checked, :checked + span', expect: { count: 6 } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/checked-type-change', + html: ` + + This text should be green. + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const input = document.getElementById('checked') as HTMLInputElement; + const beforeSibling = document.querySelectorAll(':checked + span').length; + input.type = 'radio'; + const afterSibling = document.querySelectorAll(':checked + span').length; + + return { + beforeSibling, + afterSibling, + }; + }); + + expect(result.beforeSibling).toEqual(0); + expect(result.afterSibling).toEqual(1); + }, + cases: [] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/checked', + html: ` + + + + + + +
      +

      + + + + + + + +
      + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const nativeIds = () => [...document.querySelectorAll(':checked')].map(el => el.id); + const nwIds = () => NW.Dom.select(':checked', document).map(el => el.id); + + const native_initial = nativeIds(); + const nw_initial = nwIds(); + + (document.getElementById('checkbox1') as HTMLInputElement).removeAttribute('type'); + (document.getElementById('radio1') as HTMLInputElement).removeAttribute('type'); + + const native_afterRemoveType = nativeIds(); + const nw_afterRemoveType = nwIds(); + + (document.getElementById('option2') as HTMLOptionElement).selected = true; + (document.getElementById('checkbox2') as HTMLInputElement).click(); + (document.getElementById('radio2') as HTMLInputElement).click(); + + const native_afterClick = nativeIds(); + const nw_afterClick = nwIds(); + + return { + native_initial, + nw_initial, + native_afterRemoveType, + nw_afterRemoveType, + native_afterClick, + nw_afterClick, + }; + }); + + expect(result.native_initial).toEqual(['option1', 'checkbox1', 'radio1']); + expect(result.nw_initial).toEqual(['option1', 'checkbox1', 'radio1']); + + expect(result.native_afterRemoveType).toEqual(['option1']); + expect(result.nw_afterRemoveType).toEqual(['option1']); + + expect(result.native_afterClick).toEqual(['option2', 'checkbox2', 'radio2']); + expect(result.nw_afterClick).toEqual(['option2', 'checkbox2', 'radio2']); + }, + cases: [ + { select: ':checked', expect: { ids: ['option2', 'checkbox2', 'radio2'] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/default', + html: ` +
      + + +
      +
      + + +
      + +
      + +
      + +
      + + +
      +
      + + +
      +
      + +
      + + + + + + + + + + +
      + + +
      +
      + + +
      + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const nativeIds = () => [...document.querySelectorAll(':default')].map(el => el.id); + const nwIds = () => NW.Dom.select(':default', document).map(el => el.id); + + const native_initial = nativeIds(); + const nw_initial = nwIds(); + + (document.getElementById('button1') as HTMLButtonElement).type = 'submit'; + + return { + native_initial, + nw_initial, + }; + }); + + expect(result.native_initial).toEqual(['button2', 'button4', 'input3', 'input5', 'input7', 'checkbox1', 'radio1', 'option2', 'button6', 'button8']); + expect(result.nw_initial).toEqual(['button2', 'button4', 'input3', 'input5', 'input7', 'checkbox1', 'radio1', 'option2', 'button6', 'button8']); + }, + cases: [ + { select: ':default', expect: { ids: ['button1', 'button4', 'input3', 'input5', 'input7', 'checkbox1', 'radio1', 'option2', 'button6', 'button8'] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/dir', + html: ` + + + + + Selector: pseudo-classes (:dir(ltr), :dir(rtl)) + + + + + + + + +
      + WERBEH + HEBREW + HEBREW + WERBEH + HEBREW + إيان + WERBEH + WERBEH + HEBREW + ‮WERBEH‬ + WERBEH + HEBREW + HEBREW + إيان + עברית + + + + + `, + htmlMode: 'document', + setupPage: async (page) => { + const result = await page.evaluate(() => { + const native_ltr = [...document.querySelectorAll(':dir(ltr)')].map(el => el.id); + const nw_ltr = NW.Dom.select(':dir(ltr)', document).map(el => el.id); + + const bdo = document.createElement('bdo'); + bdo.setAttribute('dir', 'ltr'); + + const native_ltr_afterDetached = [...document.querySelectorAll(':dir(ltr)')].map(el => el.id); + const nw_ltr_afterDetached = NW.Dom.select(':dir(ltr)', document).map(el => el.id); + + return { + native_ltr, + nw_ltr, + native_ltr_afterDetached, + nw_ltr_afterDetached, + }; + }); + + expect(result.native_ltr).toEqual(result.native_ltr_afterDetached); + expect(result.nw_ltr).toEqual(result.nw_ltr_afterDetached); + }, + cases: [ + { select: ':dir(rtl)', expect: { ids: ['bdo1', 'bdi2', 'bdi4', 'span2', 'span5', 'bdo4'] } }, + { select: ':dir(ltr)', expect: { ids: [ + "html", "head", "meta", "title", "link1", "link2", "script1", "script2", "script3", "style", "body", + "log", "bdo2", "bdi1", "bdi3", "span1", "span3", "span4", "span6", "bdo3", "bdo5", "script4" + ] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/dir01', + html: ` +
      This text is left to right
      this is right to left
      +
      This text is left to rightthis is left to right
      + `, + cases: [ + { select: ':dir(ltr)', expect: { equivalentCase: { select: '*' } } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/disabled', + html: ` + +
      + + + + + + + + + + +
      +
      + +

      +

      +
      + + + + + + + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const nativeIds = (sel: string) => [...document.querySelectorAll(sel)].map(el => el.id); + const nwIds = (sel: string) => NW.Dom.select(sel, document).map(el => el.id); + + const native_initial = nativeIds(':disabled'); + const nw_initial = nwIds(':disabled'); + + document.getElementById('button2')!.removeAttribute('disabled'); + const native_afterRemove = nativeIds(':disabled'); + const nw_afterRemove = nwIds(':disabled'); + + document.getElementById('button1')!.setAttribute('disabled', 'disabled'); + const native_afterSet = nativeIds(':disabled'); + const nw_afterSet = nwIds(':disabled'); + + document.getElementById('button1')!.setAttribute('disabled', 'disabled'); + const native_afterSetTwice = nativeIds(':disabled'); + const nw_afterSetTwice = nwIds(':disabled'); + + (document.getElementById('input2') as HTMLInputElement).setAttribute('type', 'submit'); + const native_afterTypeChange = nativeIds(':disabled'); + const nw_afterTypeChange = nwIds(':disabled'); + + const input = document.createElement('input'); + input.setAttribute('disabled', 'disabled'); + const native_afterDetached = nativeIds(':disabled'); + const nw_afterDetached = nwIds(':disabled'); + + const fieldset = document.createElement('fieldset'); + fieldset.id = 'fieldset_nested'; + fieldset.innerHTML = ` + + + + + + +
      + +
      + `; + document.getElementById('fieldset2')!.appendChild(fieldset); + + return { + native_initial, + nw_initial, + native_afterRemove, + nw_afterRemove, + native_afterSet, + nw_afterSet, + native_afterSetTwice, + nw_afterSetTwice, + native_afterTypeChange, + nw_afterTypeChange, + native_afterDetached, + nw_afterDetached, + }; + }); + + expect(result.native_initial).toEqual(['button2', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + expect(result.nw_initial).toEqual(['button2', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + + expect(result.native_afterRemove).toEqual(['input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + expect(result.nw_afterRemove).toEqual(['input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + + expect(result.native_afterSet).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + expect(result.nw_afterSet).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + + expect(result.native_afterSetTwice).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + expect(result.nw_afterSetTwice).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + + expect(result.native_afterTypeChange).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + expect(result.nw_afterTypeChange).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + + expect(result.native_afterDetached).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + expect(result.nw_afterDetached).toEqual(['button1', 'input2', 'select2', 'optgroup2', 'option2', 'textarea2', 'fieldset2', 'clubname', 'clubnum']); + }, + cases: [ + // { select: '#fieldset2 :disabled', expect: { ids: ['clubname', 'clubnum', 'fieldset_nested', 'input_nested', 'button_nested', 'select_nested', 'textarea_nested', 'fieldset_nested2', 'input_nested2'] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/enabled', + html: ` + + + + + + + + + + + + + + +
      +

      + + + + + +
      +
      +
      + `, + cases: [ + { select: ':enabled', expect: { ids: ['button1', 'input1', 'select1', 'optgroup1', 'option1', 'textarea1', 'submitbutton', 'fieldset1'] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/autofocus', + html: ` + + `, + setupPage: async (page) => { + await new Promise((resolve) => setTimeout(resolve, 10)); + }, + cases: [ + { select: ':focus', expect: { ids: ['input1'] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/focus-none', + html: ` + + `, + cases: [ + { select: ':focus', expect: { ids: [] }, status: 'fixme'}, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/focus', + // browsers: ['chromium', 'webkit'], // Firefox doesn't support :focus inside iframe ?? + html: ` + + Selector: pseudo-classes (:focus) + + + + + + + + + +
      hello
      +
      content
      + + + `, + htmlMode: 'document', + setupPage: async (page) => { + const result = await page.evaluate(async () => { + const iframe = document.getElementById('iframe') as HTMLIFrameElement; + const inputiframe = iframe.contentDocument?.getElementById('inputiframe'); + if (!inputiframe) throw new Error('Failed to find input inside iframe'); + + const nativeIds = () => [...document.querySelectorAll(':focus')].map(el => el.id); + const nwIds = () => NW.Dom.select(':focus', document).map(el => el.id); + + (document.getElementById('input1'))?.focus(); + const native_input1 = nativeIds(); + const nw_input1 = nwIds(); + + (document.getElementById('div1'))?.focus(); + const native_div1 = nativeIds(); + const nw_div1 = nwIds(); + + (document.getElementById('div2'))?.focus(); + const native_div2 = nativeIds(); + const nw_div2 = nwIds(); + + (document.getElementById('body'))?.focus(); + const native_body = nativeIds(); + const nw_body = nwIds(); + + inputiframe.focus(); + const native_iframe = nativeIds(); + const nw_iframe = nwIds(); + + return { + native_input1, nw_input1, + native_div1, nw_div1, + native_div2, nw_div2, + native_body, nw_body, + native_iframe, nw_iframe, + }; + }); + + expect(result.native_input1).toEqual(['input1']); + expect(result.nw_input1).toEqual(['input1']); + + expect(result.native_div1).toEqual(['div1']); + expect(result.nw_div1).toEqual(['div1']); + + expect(result.native_div2).toEqual(['div2']); + expect(result.nw_div2).toEqual(['div2']); + + expect(result.native_body).toEqual(['body']); + expect(result.nw_body).toEqual(['body']); + + // expect(result.native_iframe).toEqual(['inputiframe']); + // expect(result.nw_iframe).toEqual(['inputiframe']); + }, + cases: [ + { select: 'input', ref: { by: 'iframe', id: 'iframe' }, expect: { ids: ['inputiframe'] } }, + { select: 'input:focus', ref: { by: 'iframe', id: 'iframe' }, expect: { ids: ['inputiframe'] }, status: 'fail' }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/indeterminate-radio', + html: ` + +
      + + `, + setupPage: async (page) => { + await page.evaluate(() => { + (document.getElementById('radio1') as HTMLInputElement).indeterminate = true; + }); + }, + cases: [ + { select: 'input:indeterminate + #test', expect: { count: 0 } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/indeterminate-type-change', + html: ` + + This text should be green. + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const native_before = [...document.querySelectorAll(':indeterminate + span')].map(el => el.id); + const nw_before = NW.Dom.select(':indeterminate + span', document).map(el => el.id); + + (document.getElementById('indeterminate') as HTMLInputElement).type = 'radio'; + + return { + native_before, + nw_before, + }; + }); + + expect(result.native_before).toEqual([]); + expect(result.nw_before).toEqual([]); + }, + cases: [ + { select: ':indeterminate + span', expect: { ids: ['sibling'] }, status: 'fixme'}, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/indeterminate', + html: ` + + + + + + + + + + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const nativeIds = () => [...document.querySelectorAll(':indeterminate')].map(el => el.id); + const nwIds = () => NW.Dom.select(':indeterminate', document).map(el => el.id); + + const native_initial = nativeIds(); + const nw_initial = nwIds(); + + document.getElementById('radio2')?.setAttribute('checked', 'checked'); + const native_afterCheckedAttr = nativeIds(); + const nw_afterCheckedAttr = nwIds(); + + (document.getElementById('radio4') as HTMLInputElement)?.click(); + const native_afterRadio4Click = nativeIds(); + const nw_afterRadio4Click = nwIds(); + + document.getElementById('progress1')?.setAttribute('value', '20'); + const native_afterProgress1Value = nativeIds(); + const nw_afterProgress1Value = nwIds(); + + document.getElementById('progress2')?.removeAttribute('value'); + const native_afterProgress2Remove = nativeIds(); + const nw_afterProgress2Remove = nwIds(); + + (document.getElementById('checkbox1') as HTMLInputElement).indeterminate = true; + + return { + native_initial, + nw_initial, + native_afterCheckedAttr, + nw_afterCheckedAttr, + native_afterRadio4Click, + nw_afterRadio4Click, + native_afterProgress1Value, + nw_afterProgress1Value, + native_afterProgress2Remove, + nw_afterProgress2Remove, + }; + }); + + expect(result.native_initial).toEqual(['radio2', 'radio3', 'radio4', 'radio5', 'progress1']); + expect(result.nw_initial).toEqual(['radio2', 'radio3', 'radio4', 'radio5', 'progress1']); + + expect(result.native_afterCheckedAttr).toEqual(['radio4', 'radio5', 'progress1']); + expect(result.nw_afterCheckedAttr).toEqual(['radio4', 'radio5', 'progress1']); + + expect(result.native_afterRadio4Click).toEqual(['progress1']); + expect(result.nw_afterRadio4Click).toEqual(['progress1']); + + expect(result.native_afterProgress1Value).toEqual([]); + expect(result.nw_afterProgress1Value).toEqual([]); + + expect(result.native_afterProgress2Remove).toEqual(['progress2']); + expect(result.nw_afterProgress2Remove).toEqual(['progress2']); + }, + cases: [ + { select: ':indeterminate', expect: { ids: ['checkbox1', 'progress2'] } }, + ] + }, + + { + name: 'html/semantics/selectors/pseudo-classes/inrange-outofrange-type-change', + html: ` + + This text should be green. + + This text should be green. + `, + setupPage: async (page) => { + const result = await page.evaluate(() => { + const native_inRange_before = [...document.querySelectorAll('#t1:in-range + span')].map(el => el.id); + const nw_inRange_before = NW.Dom.select('#t1:in-range + span', document).map(el => el.id); + + const native_outOfRange_before = [...document.querySelectorAll('#t2:out-of-range + span')].map(el => el.id); + const nw_outOfRange_before = NW.Dom.select('#t2:out-of-range + span', document).map(el => el.id); + + (document.getElementById('t1') as HTMLInputElement).type = 'number'; + (document.getElementById('t2') as HTMLInputElement).type = 'number'; + + return { + native_inRange_before, + nw_inRange_before, + native_outOfRange_before, + nw_outOfRange_before, + }; + }); + + expect(result.native_inRange_before).toEqual([]); + expect(result.nw_inRange_before).toEqual([]); + + expect(result.native_outOfRange_before).toEqual([]); + expect(result.nw_outOfRange_before).toEqual([]); + }, + cases: [ + { select: '#t1:in-range + span', expect: { ids: ['sibling1'] } }, + { select: '#t2:out-of-range + span', expect: { ids: ['sibling2'] } }, + ] + }, + + + + + + + + + + + + + + + ]); From dd60bb22e51883316298b14d3146b6cec395622d Mon Sep 17 00:00:00 2001 From: koal44 Date: Sun, 12 Apr 2026 14:36:10 -0700 Subject: [PATCH 11/19] add case-level only and improve failure labels --- test_new/browser/harness.test.ts | 27 ++ test_new/browser/harness/scenarios.ts | 136 ++++--- test_new/browser/w3c.test.ts | 488 +++++++++++++++++++++++++- 3 files changed, 605 insertions(+), 46 deletions(-) diff --git a/test_new/browser/harness.test.ts b/test_new/browser/harness.test.ts index 064e9a3..0543667 100644 --- a/test_new/browser/harness.test.ts +++ b/test_new/browser/harness.test.ts @@ -113,4 +113,31 @@ runScenarios('scope', 'normal', [ ] }, + { + name: 'steps/basic-accumulation', + html: ` +
      + `, + steps: [ + { + setupPage: async (page) => { + await page.evaluate(() => { document.getElementById('box')!.classList.add('a'); }); + }, + cases: [ + { select: '#box.a', expect: { ids: ['box'] } }, + { select: '#box.b', expect: { ids: [] } }, + ], + }, + { + setupPage: async (page) => { + await page.evaluate(() => { document.getElementById('box')!.classList.add('b'); }); + }, + cases: [ + { select: '#box.a', expect: { ids: ['box'] } }, + { select: '#box.b', expect: { ids: ['box'] } }, + ], + }, + ], + }, + ]); \ No newline at end of file diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index 3073f1d..05ea5a6 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -11,13 +11,14 @@ if (rawHarnessMode !== undefined && rawHarnessMode !== 'normal' && rawHarnessMod } const HARNESS_MODE: HarnessMode = rawHarnessMode ?? 'normal'; -export type SelectCase = { select: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; -export type ByIdCase = { byId: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; -export type ByTagCase = { byTag: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; -export type ByClassCase = { byClass: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; -export type FirstCase = { first: string; ref?: ContextRef; expect?: Expectation, status?: CaseStatus }; -export type MatchCase = { match: string; ref: ContextRef; expect?: Expectation, status?: CaseStatus }; -export type ClosestCase = { closest: string; ref: ContextRef; expect?: Expectation, status?: CaseStatus }; +type CaseBase = { expect?: Expectation; status?: CaseStatus; browsers?: BrowserName[]; }; +export type SelectCase = { select: string; ref?: ContextRef; } & CaseBase; +export type ByIdCase = { byId: string; ref?: ContextRef; } & CaseBase; +export type ByTagCase = { byTag: string; ref?: ContextRef; } & CaseBase; +export type ByClassCase = { byClass: string; ref?: ContextRef; } & CaseBase; +export type FirstCase = { first: string; ref?: ContextRef; } & CaseBase; +export type MatchCase = { match: string; ref: ContextRef; } & CaseBase; +export type ClosestCase = { closest: string; ref: ContextRef; } & CaseBase; export type TestCase = SelectCase | ByIdCase | ByTagCase | ByClassCase | MatchCase | FirstCase | ClosestCase; @@ -27,6 +28,14 @@ export type Scenario = { html: string; htmlMode?: 'body' | 'document'; browsers?: BrowserName[]; + steps?: ScenarioStep[]; + + // ergonomic sugar for simple scenarios that don't require multiple steps + setupPage?: (page: Page) => void | Promise; + cases?: TestCase[]; +}; + +type ScenarioStep = { setupPage?: (page: Page) => void | Promise; cases: TestCase[]; }; @@ -44,7 +53,7 @@ export type Expectation = { equivalentCase?: EquivalentCase; }; -export type EquivalentCase = DistributiveOmit; +export type EquivalentCase = DistributiveOmit; // const foo: EquivalentCase = { select: 'div' }; // just to verify the type const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; @@ -52,7 +61,7 @@ type BrowserName = typeof BROWSER_NAMES[number]; type ScenariosStatus = 'normal' | 'skip' | 'only'; // | 'fixme'; type ScenarioStatus = 'normal' | 'skip' | 'only' | 'fixme' | 'fail'; -type CaseStatus = 'normal' | 'skip' | 'fixme' | 'fail'; +type CaseStatus = 'normal' | 'skip' | 'fixme' | 'fail' | 'only'; export const ENGINES = ['native', 'nw'] as const; export type Engine = typeof ENGINES[number]; @@ -66,16 +75,17 @@ export type ContextRef = export type ContextHome = 'document' | 'detached' | 'fragment'; export type NwsapiId = 'nwsapi-bootstrap'; -type Misfail = { passedEverywhere: boolean; info: string; } +type Misfail = { passedEverywhere: boolean; resultInfo: string; stepIndex: number; caseIndex: number; }; type CaseInfo = { browser: BrowserName; scenario: Scenario; case: TestCase; + stepIndex: number; caseIndex: number; + stepCaseIndex: number; misfails: Record; }; - export function runScenarios(label: string, status: ScenariosStatus, scenarios: Scenario[]): void { const describeFn = getDescribeFn(status); describeFn(label, () => { @@ -106,10 +116,20 @@ export function runScenarios(label: string, status: ScenariosStatus, scenarios: await Promise.all(BROWSER_NAMES.map((name) => browsers[name].close())); }); + const scenarioHas = (s: Scenario, status: 'only' | 'fixme'): boolean => + s.status === status || + !!s.cases?.some(c => c.status === status) || + !!s.steps?.some(step => step.cases.some(c => c.status === status)); + + const hasScenariosOnly = scenarios.some(s => scenarioHas(s, 'only')); + for (const s of scenarios) { - const hasFixme = s.status === 'fixme' || s.cases.some(c => c.status === 'fixme'); + const hasFixme = scenarioHas(s, 'fixme'); if (HARNESS_MODE === 'fixme' && !hasFixme) continue; - + + const hasOnly = scenarioHas(s, 'only'); + if (hasScenariosOnly && !hasOnly) continue; + const testFn = getTestFn(s.status); testFn(s.name, async () => { await runScenario(s, pages); @@ -121,29 +141,49 @@ export function runScenarios(label: string, status: ScenariosStatus, scenarios: async function runScenario(s: Scenario, pages: Record): Promise { const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; - // collection of cases marked as 'fail' and their accumulated outcomes + // cases marked 'fail' and whether they passed in every applicable browser so far const misfails: Record = {}; + const steps: ScenarioStep[] = [ + ...(s.steps ?? []), + ...(s.cases?.length ? [{ cases: s.cases }] : []), + ]; + + const hasOnlyCases = steps.some(step => step.cases.some(c => c.status === 'only')); + for (const browserName of scenarioBrowsers) { const page = pages[browserName]; - await setupPage(page, s); - - for (let i = 0; i < s.cases.length; ++i) { - const c = s.cases[i]; - await runCase(page, { browser: browserName, scenario: s, case: c, caseIndex: i, misfails: misfails }); + await initPage(page, s); + + let stepCaseIndex = 0; + for (let stepIndex = 0; stepIndex < steps.length; ++stepIndex) { + const step = steps[stepIndex]; + if (step.setupPage) await step.setupPage(page); + for (let caseIndex = 0; caseIndex < step.cases.length; ++caseIndex) { + const c = step.cases[caseIndex]; + if (hasOnlyCases && c.status !== 'only') continue; + await runCase( + page, + { + browser: browserName, scenario: s, case: c, + stepIndex, caseIndex, stepCaseIndex, misfails + } + ); + stepCaseIndex++; + } } } - for (const [caseIndex, misFail] of Object.entries(misfails)) { + // At the end of the scenario, check if any 'fail' cases passed unexpectedly in all browsers + for (const [_scIndex, misFail] of Object.entries(misfails)) { if (misFail.passedEverywhere) { - const i = Number(caseIndex); + const { stepIndex, caseIndex } = misFail; throw new Error( - `Case #${i + 1} :: ${s.name} :: ${misFail.info}\n` + - `Case is marked 'fail' but unexpectedly passed.`, + `Step #${stepIndex + 1}, Case #${caseIndex + 1} was marked 'fail' but unexpectedly passed in every applicable browser.\n` + + `${s.name} :: ${misFail.resultInfo}\n` ); } } - } async function runCase(page: Page, caseInfo: CaseInfo): Promise { @@ -153,6 +193,7 @@ async function runCase(page: Page, caseInfo: CaseInfo): Promise { if (s.status !== 'fixme' && HARNESS_MODE === 'fixme' && c.status !== 'fixme') { return; } + if (c.browsers && !c.browsers.includes(caseInfo.browser)) return; const result = await evalCase(page, c); const expectation = c.expect ?? {}; @@ -166,17 +207,18 @@ async function runCase(page: Page, caseInfo: CaseInfo): Promise { const status = c.status ?? 'normal'; - if (status === 'normal') { + if (status === 'normal' || status === 'only') { if (thrown) throw thrown; return; } if (status === 'fail') { const curPassed = !thrown; - const prevPassed = caseInfo.misfails[caseInfo.caseIndex]?.passedEverywhere ?? true; + const prevPassed = caseInfo.misfails[caseInfo.stepCaseIndex]?.passedEverywhere ?? true; const passedEverywhere = curPassed && prevPassed; - const info = caseInfo.misfails[caseInfo.caseIndex]?.info || result.info; - caseInfo.misfails[caseInfo.caseIndex] = { passedEverywhere, info }; + const resultInfo = caseInfo.misfails[caseInfo.stepCaseIndex]?.resultInfo ?? result.info; + const { stepIndex, caseIndex } = caseInfo; + caseInfo.misfails[caseInfo.stepCaseIndex] = { passedEverywhere, resultInfo, stepIndex, caseIndex }; return; } @@ -216,7 +258,9 @@ async function evalCase(page: Page, testCase: TestCase): Promise { const nwEquivQuery = pw.getCaseQuery(equivCase); const equivCtx = pw.resolveContext(equivCase.ref); - const equivCtxErrorMsg = equivCtx ? undefined : `Could not resolve equivalent context from ref: ${pw.stringify(equivCase.ref)}`; + const equivCtxErrorMsg = !equivCtx + ? `Could not resolve equivalent context from ref: ${pw.stringify(equivCase.ref)}` + : undefined; const nwEquivRes = pw.getResults(nwEquivFn, nwEquivQuery, equivCtx, equivCtxErrorMsg); if (pw.isRehomed(c.ref) || pw.isRehomed(equivCase.ref)) { @@ -259,7 +303,7 @@ function getTestFn(mode?: ScenarioStatus) { return test; } -async function setupPage(page: Page, scenario: Scenario): Promise { +async function initPage(page: Page, scenario: Scenario): Promise { if (scenario.htmlMode === 'document') { await page.setContent(scenario.html); const script = await page.addScriptTag({ path: 'src/nwsapi.js' }); @@ -301,40 +345,42 @@ function runEngineChecks( } }); - let label = `[engine=${enginesWithSameOutcome.join('+')}] ${baseMsg}`; + let label = ` · engines=${enginesWithSameOutcome.join('+')}${baseMsg}`; check(r, label); } } function checkResult(result: EvalResult, expectation: Expectation, caseInfo: CaseInfo): void { const allowMismatch = expectation.allowMismatch ?? false; - const { caseIndex, browser, scenario: s } = caseInfo; - const header = `Case #${caseIndex + 1}`; - const msg = `[${browser}] :: ${s.name} :: ${result.info}${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; + const { stepIndex, caseIndex, browser, scenario: s } = caseInfo; + const header = `${s.name}\nStep #${stepIndex + 1}, Case #${caseIndex + 1} · browser=${browser}`; + const msg = + `\nQuery: ${result.info}` + + `${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; if (expectation.throws) { - expect(result.nw.threw, `${header} ${msg}`).toBe(true); + expect(result.nw.threw, `${header}${msg}`).toBe(true); return; } - expect(result.nw.threw, `${header} ${msg}`).toBe(false); + expect(result.nw.threw, `${header}${msg}`).toBe(false); if (expectation.count !== undefined) { runEngineChecks(result, msg, 'count', (r, label) => { - expect(r.count, `${header} ${label}`).toEqual(expectation.count); + expect(r.count, `${header}${label}`).toEqual(expectation.count); }); } if (expectation.ids) { runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, `${header} ${label}`).toEqual(expectation.ids); + expect(r.ids, `${header}${label}`).toEqual(expectation.ids); }); } if (expectation.includesIds) { for (const id of expectation.includesIds) { runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, `${header} ${label}`).toContain(id); + expect(r.ids, `${header}${label}`).toContain(id); }); } } @@ -342,14 +388,14 @@ function checkResult(result: EvalResult, expectation: Expectation, caseInfo: Cas if (expectation.excludesIds) { for (const id of expectation.excludesIds) { runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, `${header} ${label}`).not.toContain(id); + expect(r.ids, `${header}${label}`).not.toContain(id); }); } } if (expectation.classes) { runEngineChecks(result, msg, 'classes', (r, label) => { - expect(r.classes, `${header} ${label}`).toEqual(expectation.classes); + expect(r.classes, `${header}${label}`).toEqual(expectation.classes); }); } @@ -357,7 +403,7 @@ function checkResult(result: EvalResult, expectation: Expectation, caseInfo: Cas for (const cls of expectation.includesClasses) { runEngineChecks(result, msg, 'classes', (r, label) => { const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, `${header} ${label}`).toContain(cls); + expect(classTokens, `${header}${label}`).toContain(cls); }); } } @@ -366,18 +412,18 @@ function checkResult(result: EvalResult, expectation: Expectation, caseInfo: Cas for (const cls of expectation.excludesClasses) { runEngineChecks(result, msg, 'classes', (r, label) => { const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, `${header} ${label}`).not.toContain(cls); + expect(classTokens, `${header}${label}`).not.toContain(cls); }); } } if (!allowMismatch && !result.native.threw) { const mismatch = !!result.mismatchMsg; - expect(mismatch, `${header} ${msg}`).toBe(false); + expect(mismatch, `${header}${msg}`).toBe(false); } if (expectation.equivalentCase) { const equivMismatch = !!result.equivMismatchMsg; - expect(equivMismatch, `${header} ${msg}\n${result.equivMismatchMsg}`).toBe(false); + expect(equivMismatch, `${header}${msg}\n${result.equivMismatchMsg}`).toBe(false); } } diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts index a276538..6b1702d 100644 --- a/test_new/browser/w3c.test.ts +++ b/test_new/browser/w3c.test.ts @@ -1924,7 +1924,7 @@ runScenarios('w3c', 'normal', [ `, setupPage: async (page) => { - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise((resolve) => setTimeout(resolve, 100)); }, cases: [ { select: ':focus', expect: { ids: ['input1'] } }, @@ -2177,17 +2177,503 @@ runScenarios('w3c', 'normal', [ ] }, + { + name: 'html/semantics/selectors/pseudo-classes/inrange-outofrange', + browsers: ['chromium'], // Firefox and WebKit are flaky here. + html: ` + + + Selector: pseudo-classes (:in-range, :out-of-range) + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `, + steps: [ + { + cases: [ + { select: ':in-range', expect: { ids: ['number1', 'datein', 'timein', 'weekin', 'monthin', 'datetimelocalin', 'range0', 'range1', 'range2', 'range3'] } }, + { select: ':out-of-range', expect: { ids: ['number3', 'number4', 'dateunder', 'dateover', 'timeunder', 'timeover', 'weekunder', 'weekover', 'monthunder', 'monthover', 'datetimelocalunder', 'datetimelocalover'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('number1') as HTMLInputElement).value = '-10'; }); }, + cases: [ + { select: ':in-range', expect: { ids: ['datein', 'timein', 'weekin', 'monthin', 'datetimelocalin', 'range0', 'range1', 'range2', 'range3'] } }, + { select: ':out-of-range', expect: { ids: ['number1', 'number3', 'number4', 'dateunder', 'dateover', 'timeunder', 'timeover', 'weekunder', 'weekover', 'monthunder', 'monthover', 'datetimelocalunder', 'datetimelocalover'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('number3') as HTMLInputElement).min = '0'; }); }, + cases: [ + { select: ':in-range', expect: { ids: ['number3', 'datein', 'timein', 'weekin', 'monthin', 'datetimelocalin', 'range0', 'range1', 'range2', 'range3'] } }, + { select: ':out-of-range', expect: { ids: ['number1', 'number4', 'dateunder', 'dateover', 'timeunder', 'timeover', 'weekunder', 'weekover', 'monthunder', 'monthover', 'datetimelocalunder', 'datetimelocalover'] } }, + ], + }, + ], + }, + { + name: 'html/semantics/selectors/pseudo-classes/link', + status: 'fixme', // the original test is sus. link3 doesn't even exist in the DOM... + html: ` + + + Selector: pseudo-classes (:link) + + +
      + + + + + + + + `, + htmlMode: 'document', + steps: [ + { + cases: [ + { select: ':link', expect: { ids: ['link1', 'link2', 'link3', 'link7', 'link8', 'link9', 'link10'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.getElementById('link9')?.removeAttribute('href'); }); }, + cases: [ + { select: ':link', expect: { ids: ['link1', 'link2', 'link3', 'link7', 'link8', 'link10'] } }, + ], + }, + ], + }, + { + name: 'html/semantics/selectors/pseudo-classes/placeholder-shown-type-change', + html: ` + + This text should be green. + `, + steps: [ + { + cases: [ + { select: ':placeholder-shown + span', expect: { ids: [] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('input') as HTMLInputElement).type = 'text'; }); }, + cases: [ + { select: ':placeholder-shown + span', expect: { ids: ['sibling'] } }, + ], + }, + ], + }, + { + // source filename is misleading, this test is about both :read-only and :read-write + name: 'html/semantics/selectors/pseudo-classes/readwrite-readonly-type-change', + html: ` + + This text should be green on lime background. + `, + steps: [ + { + cases: [ + { select: ':required + span', expect: { ids: [] }, status: 'fixme' }, + { select: ':not(:optional) + span', expect: { ids: [] }, status: 'fixme' }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('hiddenInput') as HTMLInputElement).type = 'text'; }); }, + cases: [ + { select: ':required + span', expect: { ids: ['sibling'] } }, + { select: ':not(:optional) + span', expect: { ids: ['sibling'] } }, + ], + }, + ], + }, + { + name: 'html/semantics/selectors/pseudo-classes/readwrite-readonly', + // browsers: ['webkit'], + html: ` +
      + + + + + + + + + + + +
      +
      + + + + + +
      +
      + + +
      +
      + + +
      +
      +

      paragraph1.

      +

      paragraph2.

      +
      + `, + htmlMode: 'document', + steps: [ + { + cases: [ + // WebKit differs here on input[type=color]; expected browser variance. + { select: '#set0 :read-write', expect: { ids: [] }, status: 'fail' }, + { select: '#set0 :read-only', expect: { ids: ['checkbox1', 'hidden1', 'range1', 'color1', 'radio1', 'file1', 'submit1', 'image1', 'button1', 'reset1'] }, status: 'fail' }, + { select: '#set1 :read-write', expect: { ids: ['input1'] } }, + { select: '#set1 :read-only', expect: { ids: ['input2', 'input3', 'input4', 'input5'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.getElementById('input1')?.setAttribute('readonly', 'readonly'); }); }, + cases: [ + { select: '#set1 :read-write', expect: { ids: [] } }, + { select: '#set1 :read-only', expect: { ids: ['input1', 'input2', 'input3', 'input4', 'input5'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.getElementById('input1')?.removeAttribute('readonly'); }); }, + cases: [ + { select: '#set1 :read-write', expect: { ids: ['input1'] } }, + { select: '#set1 :read-only', expect: { ids: ['input2', 'input3', 'input4', 'input5'] } }, + ], + }, + { + cases: [ + { select: '#set2 :read-write', expect: { ids: ['textarea1'] } }, + { select: '#set2 :read-only', expect: { ids: ['textarea2'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.getElementById('textarea1')?.setAttribute('readonly', 'readonly'); }); }, + cases: [ + { select: '#set2 :read-write', expect: { ids: [] } }, + { select: '#set2 :read-only', expect: { ids: ['textarea1', 'textarea2'] } }, + ], + }, + { + cases: [ + { select: '#set3 :read-write', expect: { ids: ['textarea3'] } }, + { select: '#set3 :read-only', expect: { ids: ['textarea4'] } }, + { select: '#set4 :read-write', expect: { ids: ['p2'] } }, + { select: '#set4 :read-only', expect: { ids: ['p1'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.designMode = 'on'; }); }, + cases: [ + { select: '#set4 :read-write', expect: { ids: ['p1', 'p2'] }, status: 'fixme' }, + { select: '#set4 :read-only', expect: { ids: [] }, status: 'fixme' }, + ], + }, + ], + }, + { + name: 'html/semantics/selectors/pseudo-classes/required-optional-hidden', + html: ` + + This text should be green on lime background. + `, + steps: [ + { + cases: [ + { select: ':required + span', expect: { ids: [] }, status: 'fixme' }, + { select: ':not(:optional) + span', expect: { ids: [] }, status: 'fixme' }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('hiddenInput') as HTMLInputElement).type = 'text'; }); }, + cases: [ + { select: ':required + span', expect: { ids: ['sibling'] } }, + { select: ':not(:optional) + span', expect: { ids: ['sibling'] } }, + ], + }, + ], + }, + { + name: 'html/semantics/selectors/pseudo-classes/required-optional', + html: ` + + + + + + + + `, + steps: [ + { + cases: [ + { select: ':required', expect: { ids: ['text1', 'text2', 'select1', 'textarea1'] } }, + { select: ':optional', expect: { ids: ['text3', 'select2', 'textarea2'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.getElementById('text1')?.removeAttribute('required'); }); }, + cases: [ + { select: ':required', expect: { ids: ['text2', 'select1', 'textarea1'] } }, + { select: ':optional', expect: { ids: ['text1', 'text3', 'select2', 'textarea2'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { document.getElementById('select2')?.setAttribute('required', 'required'); }); }, + cases: [ + { select: ':required', expect: { ids: ['text2', 'select1', 'select2', 'textarea1'] } }, + { select: ':optional', expect: { ids: ['text1', 'text3', 'textarea2'] } }, + ], + }, + ], + }, + { + name: 'html/semantics/selectors/pseudo-classes/valid-invalid', + html: ` + + + + + Selector: pseudo-classes (:valid, :invalid) + + + + + + + + +
      +
      + + +
      +
      +
      + +
      +
      + +
      +
      +
      +
      + +
      +
      + +
      +
      +
      + + +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + `, + htmlMode: 'document', + steps: [ + { + cases: [ + { select: '#simpleConstraints :valid', expect: { ids: ['text1'] } }, + { select: '#FormSelection :valid', expect: { ids: ['form1', 'text3'] } }, + { select: '#FieldSetSelection :valid', expect: { ids: ['fieldset1', 'text5'] } }, + { select: '#patternConstraints :valid', expect: { ids: ['text8'] } }, + { select: '#numberConstraints :valid', expect: { ids: ['number2'] } }, + + { select: '#simpleConstraints :invalid', expect: { ids: ['text2'] } }, + { select: '#FormSelection :invalid', expect: { ids: ['form2', 'text4'] } }, + { select: '#FieldSetSelection :invalid', expect: { ids: ['fieldset2', 'text6'] } }, + { select: '#patternConstraints :invalid', expect: { ids: ['text7'] } }, + { select: '#numberConstraints :invalid', expect: { ids: ['number1'] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('text7') as HTMLInputElement).value = '0BBB'; }); }, + cases: [ + { select: '#patternConstraints :valid', expect: { ids: ['text7', 'text8'] } }, + { select: '#patternConstraints :invalid', expect: { ids: [] } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { (document.getElementById('text8') as HTMLInputElement).value = 'BBB'; }); }, + cases: [ + { select: '#patternConstraints :valid', expect: { ids: ['text7'] } }, + { select: '#patternConstraints :invalid', expect: { ids: ['text8'] } }, + ], + }, + + { + cases: [ + { select: '#styleTests form:valid', expect: { count: 2 } }, + { select: '#styleTests form:invalid', expect: { count: 1 } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { + const elems = document.querySelectorAll('#styleTests form'); + const empty = elems[0]; + const valid = elems[1]; + const invalid = elems[2]; + const validInput = valid.querySelector('input')!; + const invalidInput = invalid.querySelector('input')!; + + empty.appendChild(validInput.cloneNode()); + empty.appendChild(invalidInput.cloneNode()); + + (validInput as HTMLInputElement).type = 'number'; + (invalidInput as HTMLInputElement).type = 'text'; + }); }, + cases: [ + { select: '#styleTests form:valid', expect: { count: 1 } }, + { select: '#styleTests form:invalid', expect: { count: 2 } }, + ], + }, + + { + cases: [ + { select: '#styleTests fieldset:valid', expect: { count: 2 }, status: 'fixme' }, + { select: '#styleTests fieldset:invalid', expect: { count: 1 } }, + ], + }, + { + setupPage: async (page) => { await page.evaluate(() => { + const elems = document.querySelectorAll('#styleTests fieldset'); + const empty = elems[0]; + const valid = elems[1]; + const invalid = elems[2]; + const validInput = valid.querySelector('input')!; + const invalidInput = invalid.querySelector('input')!; + + empty.appendChild(validInput.cloneNode()); + empty.appendChild(invalidInput.cloneNode()); + + (validInput as HTMLInputElement).type = 'number'; + (invalidInput as HTMLInputElement).type = 'text'; + }); }, + cases: [ + { select: '#styleTests fieldset:valid', expect: { count: 1 }, status: 'fixme' }, + { select: '#styleTests fieldset:invalid', expect: { count: 2 } }, + ], + }, + ], + }, From 6f12c97402b08259460b5cbcfc3ec10d62551981 Mon Sep 17 00:00:00 2001 From: koal44 Date: Sun, 12 Apr 2026 23:58:10 -0700 Subject: [PATCH 12/19] finish w3c test migration --- test_new/browser/harness/browser.ts | 44 +- test_new/browser/harness/scenarios.ts | 7 +- test_new/browser/w3c.test.ts | 723 ++++++++++++++++++++++++++ 3 files changed, 761 insertions(+), 13 deletions(-) diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts index 0630e6b..5cb9215 100644 --- a/test_new/browser/harness/browser.ts +++ b/test_new/browser/harness/browser.ts @@ -40,16 +40,21 @@ export function installBrowserHelpers(): void { return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 1; } - // function isDocument(x: unknown): x is Document { - // return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 9; - // } + function isDocument(x: unknown): x is Document { + return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 9; + } function isDocFrag(x: unknown): x is DocumentFragment { return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 11; } function isRehomed(ref?: ContextRef): boolean { - return !!ref && ref.by !== 'document' && ref.by !== 'iframe' && !!ref.home && ref.home !== 'document'; + if (!ref || ref.by === 'document') return false; + if (isRehomed(ref.within)) return true; + return ref.by !== 'iframe' + && ref.by !== 'template' + && !!ref.home + && ref.home !== 'document'; } // Source - https://stackoverflow.com/a/65443215 @@ -111,17 +116,33 @@ export function installBrowserHelpers(): void { return mismatchMsg; } + function queryId(base: QueryContext, id: string): Element | null { + if ('getElementById' in base && typeof base.getElementById === 'function') { + return base.getElementById(id); + } + return base.querySelector(`#${CSS.escape(id)}`); + } + function resolveContext(ref?: ContextRef): QueryContext | null { if (!ref || ref.by === 'document') return document; + const base = ref.within ? resolveContext(ref.within) : document; + if (!base) return null; + if (ref.by === 'iframe') { - const iframe = document.getElementById(ref.id); + const iframe = queryId(base, ref.id); if (!(iframe instanceof HTMLIFrameElement)) return null; return iframe.contentDocument ?? null; } - const el = ref.by === 'id' ? document.getElementById(ref.id) - : ref.by === 'first' ? document.querySelector(ref.selector) + if (ref.by === 'template') { + const tmpl = queryId(base, ref.id); + if (!(tmpl instanceof HTMLTemplateElement)) return null; + return tmpl.content; + } + + const el = ref.by === 'id' ? queryId(base, ref.id) + : ref.by === 'first' ? base.querySelector(ref.selector) : null; if (!el) return null; @@ -207,9 +228,12 @@ export function installBrowserHelpers(): void { case 'byId' in c: if (ng === 'native') { return (query, ctx) => () => { - const el = isElement(ctx) - ? ctx.querySelector(`#${CSS.escape(query)}`) - : ctx.getElementById(query); + const useFragmentBase = c.ref && 'home' in c.ref && c.ref.home === 'fragment' && isDocFrag(ctx.parentNode); + const base = useFragmentBase ? ctx.parentNode : ctx; + const el = isElement(base) + ? base.querySelector(`#${CSS.escape(query)}`) + : base.getElementById(query); + return el ? [el] : []; }; } diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index 05ea5a6..c5ad9b5 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -68,9 +68,10 @@ export type Engine = typeof ENGINES[number]; export type ContextRef = | { by: 'document' } - | { by: 'id'; id: string; home?: ContextHome } - | { by: 'first'; selector: string; home?: ContextHome } - | { by: 'iframe'; id: string } + | { by: 'id'; id: string; home?: ContextHome; within?: ContextRef } + | { by: 'first'; selector: string; home?: ContextHome; within?: ContextRef } + | { by: 'iframe'; id: string; within?: ContextRef } + | { by: 'template'; id: string; within?: ContextRef } export type ContextHome = 'document' | 'detached' | 'fragment'; export type NwsapiId = 'nwsapi-bootstrap'; diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts index 6b1702d..48832ea 100644 --- a/test_new/browser/w3c.test.ts +++ b/test_new/browser/w3c.test.ts @@ -2675,6 +2675,729 @@ runScenarios('w3c', 'normal', [ ], }, + { + name: 'html/syntax/parsing/template/template-is-a-foster-parent-element', + html: ` +
      + +
      + `, + cases: [ + // foster-parented before the table in template content + { select: '#orphanDiv', expect: { ids: [] } }, + { select: '#orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['orphanDiv'] } }, + { select: 'table #orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: [] } }, + { select: '#orphanDiv + table', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['tbl'] } }, + ], + }, + + { + name: 'html/syntax/parsing/template/template-is-a-foster-parent-element-without-table', + // status: 'only', + html: ` +
      + +
      + `, + cases: [ + // without a table, rows and div remain sibling content + { select: '#orphanDiv', expect: { ids: [] } }, + { select: '#orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['orphanDiv'] } }, + { select: 'table', ref: { by: 'template', id: 'tmpl1' }, expect: { count: 0 } }, + { select: 'tr', ref: { by: 'template', id: 'tmpl1' }, expect: { count: 2 } }, + { select: 'tr + #orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['orphanDiv'] } }, + { select: 'tr ~ #orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['orphanDiv'] } }, + ], + }, + + { + name: 'html/syntax/parsing/template/template-is-not-a-foster-parent-element', + html: ` +
      + +
      + `, + cases: [ + // orphanDiv is foster-parented to fosterParent inside template content + { select: '#orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['orphanDiv'] } }, + { select: 'table #orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: [] } }, + { select: '#fosterParent > #orphanDiv', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['orphanDiv'] } }, + { select: '#orphanDiv + table', ref: { by: 'template', id: 'tmpl1' }, expect: { ids: ['tbl'] } }, + ], + }, + + { + name: 'html/syntax/parsing/template/template-is-not-a-foster-parent-element-lower-in-stack', + html: ` +
      + + + +
      Orphan div content
      + +
      Cell 2
      +
      + `, + cases: [ + // normal foster-parenting applies; template does not capture orphanDiv + { select: '#tmpl1', expect: { ids: ['tmpl1'] } }, + { select: '#orphanDiv', expect: { ids: ['orphanDiv'] } }, + { select: 'table #orphanDiv', expect: { ids: [] } }, + { select: '#fosterParent > #orphanDiv', expect: { ids: ['orphanDiv'] } }, + { select: '#orphanDiv + table', expect: { ids: ['tbl'] } }, + ], + }, + + { + name: 'html/syntax/parsing/template/generating-of-implied-end-tags', + html: `
      `, + steps: [ + { + setupPage: async (page) => { + await page.evaluate(() => { + document.body.innerHTML = ''; + }); + }, + cases: [ + { select: '#tpl', expect: { ids: ['tpl'] } }, + { select: '#tbl', expect: { ids: [] } }, + { select: '#tr', expect: { ids: [] } }, + { select: '#td', expect: { ids: [] } }, + { select: '#tbl', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['tbl'] } }, + { select: '#tr', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['tr'] } }, + { select: '#td', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['td'] } }, + ], + }, + { + setupPage: async (page) => { + await page.evaluate(() => { + document.body.innerHTML = ''; + }); + }, + cases: [ + { select: '#tpl', expect: { ids: ['tpl'] } }, + { select: '#dv', expect: { ids: [] } }, + { select: '#dv', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['dv'] } }, + ], + }, + { + setupPage: async (page) => { + await page.evaluate(() => { + document.body.innerHTML = ''; + }); + }, + cases: [ + { select: '#tpl', expect: { ids: ['tpl'] } }, + { select: '#dv', expect: { ids: [] } }, + { select: '#dv', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['dv'] } }, + ], + }, + { + setupPage: async (page) => { + await page.evaluate(() => { + document.body.innerHTML = ''; + }); + }, + cases: [ + { select: '#tpl', expect: { ids: ['tpl'] } }, + { select: '#dv', expect: { ids: [] } }, + { select: '#dv', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['dv'] } }, + ], + }, + ], + }, + + { + name: 'html/syntax/parsing/template/ignore-body-token', + html: ` + + `, + steps: [ + { + setupPage: async (page) => { await page.evaluate(() => { + const template = document.getElementById('tpl') as HTMLTemplateElement; + template.innerHTML = ''; + }); }, + cases: [ + { select: 'body', ref: { by: 'template', id: 'tpl' }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 'tpl' }, expect: { count: 0 } }, + ], + }, + + { + setupPage: async (page) => { await page.evaluate(() => { + const template = document.getElementById('tpl') as HTMLTemplateElement; + template.innerHTML = '
      Some content
      '; + }); }, + cases: [ + { select: 'body', ref: { by: 'template', id: 'tpl' }, expect: { count: 0 } }, + { select: '#div1', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['div1'] } }, + { select: 'div', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['div1'] } }, + ], + }, + + { + setupPage: async (page) => { await page.evaluate(() => { + const template = document.getElementById('tpl') as HTMLTemplateElement; + template.innerHTML = '
      Some content
      Some valid content
      '; + }); }, + cases: [ + { select: 'body', ref: { by: 'template', id: 'tpl' }, expect: { count: 0 } }, + { select: '#div1', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['div1'] } }, + { select: '#div2', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['div2'] } }, + { select: 'div', ref: { by: 'template', id: 'tpl' }, expect: { count: 2 } }, + ], + }, + + { + setupPage: async (page) => { await page.evaluate(() => { + const template = document.getElementById('tpl') as HTMLTemplateElement; + template.innerHTML = '
      Some valid content
      Some content
      '; + }); }, + cases: [ + { select: 'body', ref: { by: 'template', id: 'tpl' }, expect: { count: 0 } }, + { select: '#div1', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['div1'] } }, + { select: '#div2', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['div2'] } }, + { select: 'div', ref: { by: 'template', id: 'tpl' }, expect: { count: 2 } }, + ], + }, + + { + setupPage: async (page) => { await page.evaluate(() => { + const template = document.getElementById('tpl') as HTMLTemplateElement; + template.innerHTML = ''; + }); }, + cases: [ + { select: '#t2', ref: { by: 'template', id: 'tpl' }, expect: { ids: ['t2'] } }, + { select: 'body', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl' } }, expect: { count: 0 } }, + { select: 'span', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl' } }, expect: { count: 2 } }, + ], + }, + ], + }, + + { + name: 'html/syntax/parsing/template/ignore-frameset-token', + html: ` + + + + + + + + `, + steps: [ + // frameset is ignored inside template content + { + cases: [ + { select: 'frameset', ref: { by: 'template', id: 'tpl-frameset-only' }, expect: { count: 0 } }, + { select: 'frame', ref: { by: 'template', id: 'tpl-frameset-only' }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 'tpl-frameset-only' }, expect: { count: 0 } }, + ], + }, + + // valid content before frameset survives + { + cases: [ + { select: 'frameset', ref: { by: 'template', id: 'tpl-valid-before-frameset' }, expect: { count: 0 } }, + { select: 'frame', ref: { by: 'template', id: 'tpl-valid-before-frameset' }, expect: { count: 0 } }, + { select: '#div-before', ref: { by: 'template', id: 'tpl-valid-before-frameset' }, expect: { ids: ['div-before'] } }, + { select: 'div', ref: { by: 'template', id: 'tpl-valid-before-frameset' }, expect: { ids: ['div-before'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-valid-before-frameset' }, expect: { count: 1 } }, + ], + }, + + // valid content after frameset survives + { + cases: [ + { select: 'frameset', ref: { by: 'template', id: 'tpl-valid-after-frameset' }, expect: { count: 0 } }, + { select: 'frame', ref: { by: 'template', id: 'tpl-valid-after-frameset' }, expect: { count: 0 } }, + { select: '#div-after', ref: { by: 'template', id: 'tpl-valid-after-frameset' }, expect: { ids: ['div-after'] } }, + { select: 'div', ref: { by: 'template', id: 'tpl-valid-after-frameset' }, expect: { ids: ['div-after'] } }, + ], + }, + + // nested template survives; frameset inside it is ignored + { + cases: [ + { select: '#t2', ref: { by: 'template', id: 'tpl-nested-template' }, expect: { ids: ['t2'] } }, + { select: 'frameset', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-template' } }, expect: { count: 0 } }, + { select: 'frame', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-template' } }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-template' } }, expect: { count: 0 } }, + ], + }, + ], + }, + + { + name: 'html/syntax/parsing/template/ignore-head-token', + html: ` + + + + + + + + + + `, + steps: [ + // empty head is ignored + { + cases: [ + { select: 'head', ref: { by: 'template', id: 'tpl-head-empty' }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 'tpl-head-empty' }, expect: { count: 0 } }, + ], + }, + + // children of ignored head survive + { + cases: [ + { select: 'head', ref: { by: 'template', id: 'tpl-head-title' }, expect: { count: 0 } }, + { select: 'title', ref: { by: 'template', id: 'tpl-head-title' }, expect: { count: 1 } }, + { select: '*', ref: { by: 'template', id: 'tpl-head-title' }, expect: { count: 1 } }, + ], + }, + + // valid content before head survives + { + cases: [ + { select: 'head', ref: { by: 'template', id: 'tpl-valid-before-head' }, expect: { count: 0 } }, + { select: '#div-before', ref: { by: 'template', id: 'tpl-valid-before-head' }, expect: { ids: ['div-before'] } }, + { select: 'title', ref: { by: 'template', id: 'tpl-valid-before-head' }, expect: { count: 1 } }, + { select: '*', ref: { by: 'template', id: 'tpl-valid-before-head' }, expect: { count: 2 } }, + ], + }, + + // valid content after head survives + { + cases: [ + { select: 'head', ref: { by: 'template', id: 'tpl-head-before-valid' }, expect: { count: 0 } }, + { select: 'title', ref: { by: 'template', id: 'tpl-head-before-valid' }, expect: { count: 1 } }, + { select: '#div-after', ref: { by: 'template', id: 'tpl-head-before-valid' }, expect: { ids: ['div-after'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-head-before-valid' }, expect: { count: 2 } }, + ], + }, + + // nested template survives; head inside it is ignored + { + cases: [ + { select: '#t2', ref: { by: 'template', id: 'tpl-nested-head' }, expect: { ids: ['t2'] } }, + { select: 'head', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-head' } }, expect: { count: 0 } }, + { select: 'title', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-head' } }, expect: { count: 1 } }, + { select: '*', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-head' } }, expect: { count: 1 } }, + ], + }, + ], + }, + + { + name: 'html/syntax/parsing/template/ignore-html-token', + html: ` + + + + + + + + + + + + + + `, + steps: [ + // empty html/body is ignored + { + cases: [ + { select: 'html', ref: { by: 'template', id: 'tpl-html-empty' }, expect: { count: 0 } }, + { select: 'body', ref: { by: 'template', id: 'tpl-html-empty' }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 'tpl-html-empty' }, expect: { count: 0 } }, + ], + }, + + // valid content before html survives + { + cases: [ + { select: 'html', ref: { by: 'template', id: 'tpl-valid-before-html' }, expect: { count: 0 } }, + { select: 'body', ref: { by: 'template', id: 'tpl-valid-before-html' }, expect: { count: 0 } }, + { select: '#div-before', ref: { by: 'template', id: 'tpl-valid-before-html' }, expect: { ids: ['div-before'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-valid-before-html' }, expect: { count: 1 } }, + ], + }, + + // valid content after html survives + { + cases: [ + { select: 'html', ref: { by: 'template', id: 'tpl-html-before-valid' }, expect: { count: 0 } }, + { select: 'body', ref: { by: 'template', id: 'tpl-html-before-valid' }, expect: { count: 0 } }, + { select: '#div-after', ref: { by: 'template', id: 'tpl-html-before-valid' }, expect: { ids: ['div-after'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-html-before-valid' }, expect: { count: 1 } }, + ], + }, + + // nested template survives; html/body inside it are ignored + { + cases: [ + { select: '#t2', ref: { by: 'template', id: 'tpl-nested-html' }, expect: { ids: ['t2'] } }, + { select: 'html', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-html' } }, expect: { count: 0 } }, + { select: 'body', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-html' } }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 't2', within: { by: 'template', id: 'tpl-nested-html' } }, expect: { count: 0 } }, + ], + }, + + // valid content inside ignored html survives + { + cases: [ + { select: 'html', ref: { by: 'template', id: 'tpl-valid-inside-html' }, expect: { count: 0 } }, + { select: '#div-inside-html', ref: { by: 'template', id: 'tpl-valid-inside-html' }, expect: { ids: ['div-inside-html'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-valid-inside-html' }, expect: { count: 1 } }, + ], + }, + + // valid content inside ignored html/body survives + { + cases: [ + { select: 'html', ref: { by: 'template', id: 'tpl-valid-inside-html-body' }, expect: { count: 0 } }, + { select: 'body', ref: { by: 'template', id: 'tpl-valid-inside-html-body' }, expect: { count: 0 } }, + { select: '#div-inside-body', ref: { by: 'template', id: 'tpl-valid-inside-html-body' }, expect: { ids: ['div-inside-body'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-valid-inside-html-body' }, expect: { count: 1 } }, + ], + }, + + // valid content both before and inside ignored body survives + { + cases: [ + { select: 'html', ref: { by: 'template', id: 'tpl-valid-between-html-body' }, expect: { count: 0 } }, + { select: 'body', ref: { by: 'template', id: 'tpl-valid-between-html-body' }, expect: { count: 0 } }, + { select: '#span1', ref: { by: 'template', id: 'tpl-valid-between-html-body' }, expect: { ids: ['span1'] } }, + { select: '#div1', ref: { by: 'template', id: 'tpl-valid-between-html-body' }, expect: { ids: ['div1'] } }, + { select: '*', ref: { by: 'template', id: 'tpl-valid-between-html-body' }, expect: { count: 2 } }, + ], + }, + ], + }, + + { + name: 'html/syntax/parsing/template/start-tag-body', + html: ` + + + + + + + + `, + steps: [ + // bare body tag is ignored + { + cases: [ + { select: 'body', ref: { by: 'template', id: 'tmpl-body-only' }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 'tmpl-body-only' }, expect: { count: 0 } }, + ], + }, + + // body tag is ignored but its text survives + { + cases: [ + { select: 'body', ref: { by: 'template', id: 'tmpl-body-text' }, expect: { count: 0 } }, + { select: '*', ref: { by: 'template', id: 'tmpl-body-text' }, expect: { count: 0 } }, + ], + }, + + // body tag is ignored but child elements survive + { + cases: [ + { select: 'body', ref: { by: 'template', id: 'tmpl-body-elements' }, expect: { count: 0 } }, + { select: '#div1', ref: { by: 'template', id: 'tmpl-body-elements' }, expect: { ids: ['div1'] } }, + { select: '#div2', ref: { by: 'template', id: 'tmpl-body-elements' }, expect: { ids: ['div2'] } }, + { select: '*', ref: { by: 'template', id: 'tmpl-body-elements' }, expect: { count: 2 } }, + ], + }, + + // nested template: body is ignored inside nested template content too + { + cases: [ + { select: '#tmpl2', ref: { by: 'template', id: 'tmpl-nested-body' }, expect: { ids: ['tmpl2'] } }, + { select: 'body', ref: { by: 'template', id: 'tmpl2', within: { by: 'template', id: 'tmpl-nested-body' } }, expect: { count: 0 } }, + { select: '#nested-div1', ref: { by: 'template', id: 'tmpl2', within: { by: 'template', id: 'tmpl-nested-body' } }, expect: { ids: ['nested-div1'] } }, + { select: '#nested-div2', ref: { by: 'template', id: 'tmpl2', within: { by: 'template', id: 'tmpl-nested-body' } }, expect: { ids: ['nested-div2'] } }, + { select: '*', ref: { by: 'template', id: 'tmpl2', within: { by: 'template', id: 'tmpl-nested-body' } }, expect: { count: 2 } }, + ], + }, + ], + }, + + { + name: 'html/syntax/parsing/template/template-end-tag-without-start-one', + html: '', + steps: [ + // lone stray is ignored + { + setupPage: async (page) => { await page.evaluate(() => { + document.body.innerHTML = ''; + }); }, + cases: [ + { select: 'body *', expect: { count: 0 } }, + ], + }, + + // stray after a valid template is ignored + { + setupPage: async (page) => { await page.evaluate(() => { + document.body.innerHTML = ''; + }); }, + cases: [ + { select: '#tmpl', expect: { ids: ['tmpl'] } }, + { select: 'body *', expect: { count: 1 } }, + ], + }, + + // stray before a valid template is ignored + { + setupPage: async (page) => { await page.evaluate(() => { + document.body.innerHTML = ''; + }); }, + cases: [ + { select: '#tmpl', expect: { ids: ['tmpl'] } }, + { select: 'body *', expect: { count: 1 } }, + ], + }, + + // stray before valid template and title is ignored + { + setupPage: async (page) => { await page.evaluate(() => { + document.body.innerHTML = ''; + }); }, + cases: [ + { select: '#tmpl', expect: { ids: ['tmpl'] } }, + { select: '#title1', expect: { ids: ['title1'] } }, + { select: 'body *', expect: { count: 2 } }, + ], + }, + + // stray after valid template and title is ignored + { + setupPage: async (page) => { await page.evaluate(() => { + document.body.innerHTML = ''; + }); }, + cases: [ + { select: '#tmpl', expect: { ids: ['tmpl'] } }, + { select: '#title1', expect: { ids: ['title1'] } }, + { select: 'body *', expect: { count: 2 } }, + ], + }, + ], + }, + + { + name: 'jsdom/dom/nodes/documentfragment-getelementbyid', + html: ` +
      + +
      +
      + +
      + +
      + hello +
      +
      + + + `, + steps: [ + { + cases: [ + { select: '#foo', ref: { by: 'id', id: 'frag-root', home: 'fragment' }, expect: { count: 0 } }, + { byId: 'foo', ref: { by: 'id', id: 'frag-root', home: 'fragment' }, expect: { count: 0 } }, + ], + }, + { + cases: [ + { select: '#foo', ref: { by: 'id', id: 'frag-dupes', home: 'fragment' }, expect: { count: 2 } }, + { byId: 'foo', ref: { by: 'id', id: 'frag-dupes', home: 'fragment' }, expect: { ids: ['foo'] }, status: 'fixme' }, + ], + }, + { + cases: [ + { byId: '', ref: { by: 'id', id: 'frag-empty-id', home: 'fragment' }, expect: { count: 0 }, status: 'fixme' }, + ], + }, + { + cases: [ + { select: '#foo', ref: { by: 'template', id: 'tmpl' }, expect: { count: 4 } }, + { byId: 'foo', ref: { by: 'template', id: 'tmpl' }, expect: { ids: ['foo'], classes: ['first-foo'] }, status: 'fixme' }, + ], + }, + ], + }, + + { + name: 'jsdom/dom/nodes/queryselector', + html: `
      `, + steps: [ + { + setupPage: async (page) => { await page.evaluate(() => { + document.body.innerHTML = '
      '; + + const host = document.getElementById('host')!; + const g = document.createElement('_g'); + const b = document.createElement('b'); + + b.className = 'hit'; + b.appendChild(document.createTextNode('hey')); + g.appendChild(b); + host.appendChild(g); + }); }, + cases: [ + { select: '_g > b', ref: { by: 'id', id: 'host' }, expect: { count: 1, classes: ['hit'] } }, + { select: '_g > b', ref: { by: 'id', id: 'host', home: 'fragment' }, expect: { count: 1, classes: ['hit'] } }, + ], + }, + ], + }, + + { + name: 'jsdom/dom/nodes/svg-template-query-selector', + html: ` + + + + `, + cases: [ + { select: 'div', ref: { by: 'template', id: 'template1' }, expect: { count: 1 } }, + { select: 'svg', ref: { by: 'template', id: 'template2' }, expect: { count: 1, classes: ['svg-hit'] } }, + { select: 'svg', ref: { by: 'id', id: 'wrap', within: { by: 'template', id: 'template3' } }, expect: { count: 1, classes: ['nested-svg-hit'] } }, + { select: 'div > svg', ref: { by: 'template', id: 'template3' }, expect: { count: 1, classes: ['nested-svg-hit'] } }, + ], + }, + + { + name: 'jsdom/svg/element-svg', + html: ` + + + + + `, + steps: [ + { + cases: [ + { select: '#group', expect: { ids: ['group'] } }, + { select: '#group', ref: { by: 'first', selector: 'svg' }, expect: { count: 1 } }, + { select: ':scope > *', ref: { by: 'first', selector: 'svg' }, expect: { count: 1 }, status: 'fixme' }, + { select: '#group', ref: { by: 'first', selector: 'svg' }, expect: { equivalentCase: { first: ':scope > *', ref: { by: 'first', selector: 'svg' } } }, status: 'fixme' }, + { select: '#group', expect: { equivalentCase: { first: '#first > *'} } }, + ], + }, + ], + }, ]); From 31bd9c9f7195634d4c102f26c6ad7e3e4e639473 Mon Sep 17 00:00:00 2001 From: koal44 Date: Mon, 13 Apr 2026 12:02:14 -0700 Subject: [PATCH 13/19] migrate xml tests --- test_new/browser/xml.test.ts | 157 +++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 test_new/browser/xml.test.ts diff --git a/test_new/browser/xml.test.ts b/test_new/browser/xml.test.ts new file mode 100644 index 0000000..e6f6a32 --- /dev/null +++ b/test_new/browser/xml.test.ts @@ -0,0 +1,157 @@ +import { expect } from "@playwright/test"; +import { runScenarios } from "./harness/scenarios"; + +runScenarios('xml', 'normal', [ + { + name: 'jsdom/svg-test', + html: ` + + + `, + htmlMode: 'document', + cases: [ + { select: '[*|href]', expect: { count: 1 } }, + { select: '[xlink:href=foo]', expect: { count: 1 } }, + { select: '[xlink\\:href=foo]', expect: { count: 1 }, status: 'fixme' }, // maybe not interesting? + ], + }, + + { + name: 'jsdom/xml-test', + html: `
      `, + steps: [ + { + setupPage: async (page) => { await page.evaluate(() => { + const parser = new DOMParser(); + const xml = ``; + const dom = parser.parseFromString(xml, 'text/xml'); + document.getElementById('host')!.appendChild(document.importNode(dom.documentElement, true)); + }); }, + cases: [ + { select: 'coreProperties', ref: { by: 'id', id: 'host' }, expect: { count: 1 }, status: 'fixme' }, + { select: '*|coreProperties', ref: { by: 'id', id: 'host' }, expect: { count: 1 }, status: 'fixme' }, + { select: '|coreProperties', ref: { by: 'id', id: 'host' }, expect: { count: 0 }, status: 'fixme' }, + ], + }, + ], + }, + + { + name: 'xml-test2', + html: ` +
      +
      + `, + steps: [ + { + setupPage: async (page) => { await page.evaluate(() => { + const xml1 = ''; + const xml2 = xml1.toLowerCase(); + + const parser = new DOMParser(); + const dom1 = parser.parseFromString(xml1, 'text/xml'); + const dom2 = parser.parseFromString(xml2, 'text/xml'); + + document.getElementById('upper-box')!.appendChild( + document.importNode(dom1.documentElement, true) + ); + document.getElementById('lower-box')!.appendChild( + document.importNode(dom2.documentElement, true) + ); + }); }, + cases: [ + // + { select: 'Foo', ref: { by: 'id', id: 'upper-box' }, expect: { count: 1 } }, + { select: 'foo', ref: { by: 'id', id: 'upper-box' }, expect: { count: 0 }, status: 'fixme' }, + { select: 'bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 1 } }, + { select: 'foo bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 0 }, status: 'fixme' }, + { select: 'Foo bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 1 }, status: 'fixme' }, + + // + { select: 'bar', ref: { by: 'id', id: 'lower-box' }, expect: { count: 1 } }, + { select: 'Bar', ref: { by: 'id', id: 'lower-box' }, expect: { count: 0 }, status: 'fixme' }, + { select: 'foo bar', ref: { by: 'id', id: 'lower-box' }, expect: { count: 1 } }, + { select: 'Foo bar', ref: { by: 'id', id: 'lower-box' }, expect: { count: 0 }, status: 'fixme' }, + { select: 'FOO bar', ref: { by: 'id', id: 'lower-box' }, expect: { count: 0 }, status: 'fixme' }, + { select: 'foo BAR', ref: { by: 'id', id: 'lower-box' }, expect: { count: 0 }, status: 'fixme' }, + { select: 'FOO BAR', ref: { by: 'id', id: 'lower-box' }, expect: { count: 0 }, status: 'fixme' }, + ], + }, + ], + }, + + { + name: 'xml-test2-literal', + html: '', + setupPage: async (page) => { + const result = await page.evaluate(() => { + const xml1 = ''; + const xml2 = xml1.toLowerCase(); + + const parser = new DOMParser(); + const dom1 = parser.parseFromString(xml1, 'text/xml'); + const dom2 = parser.parseFromString(xml2, 'text/xml'); + + const run = (selector: string, dom: Document) => { + let nativeCount = -1; + let nwCount = -1; + let nwError = ''; + + nativeCount = dom.querySelectorAll(selector).length; + try { + nwCount = NW.Dom.select(selector, dom).length; + } catch (err) { + nwError = String(err); + } + + return { nativeCount, nwCount, nwError }; + }; + + return { + upperFoo: run('Foo', dom1), + upperfoo: run('foo', dom1), + upperbar: run('bar', dom1), + upperFoobar: run('Foo bar', dom1), + upperfoobar: run('foo bar', dom1), + + lowerbar: run('bar', dom2), + lowerBar: run('Bar', dom2), + lowerfoobar: run('foo bar', dom2), + lowerFoobar: run('Foo bar', dom2), + lowerFOObar: run('FOO bar', dom2), + lowerfooBAR: run('foo BAR', dom2), + lowerFOOBAR: run('FOO BAR', dom2), + }; + }); + + expect(result.upperFoo.nativeCount).toEqual(1); + expect(result.upperfoo.nativeCount).toEqual(0); + expect(result.upperbar.nativeCount).toEqual(1); + expect(result.upperfoobar.nativeCount).toEqual(0); + expect(result.upperFoobar.nativeCount).toEqual(1); + + expect(result.lowerbar.nativeCount).toEqual(1); + expect(result.lowerBar.nativeCount).toEqual(0); + expect(result.lowerfoobar.nativeCount).toEqual(1); + expect(result.lowerFoobar.nativeCount).toEqual(0); + expect(result.lowerFOObar.nativeCount).toEqual(0); + expect(result.lowerfooBAR.nativeCount).toEqual(0); + expect(result.lowerFOOBAR.nativeCount).toEqual(0); + + expect(result.upperFoo.nwCount).toEqual(1); + expect(result.upperfoo.nwCount).toEqual(0); + expect(result.upperbar.nwCount).toEqual(1); + expect(result.upperfoobar.nwCount).toEqual(0); + expect(result.upperFoobar.nwCount).toEqual(1); + + expect(result.lowerbar.nwCount).toEqual(1); + expect(result.lowerBar.nwCount).toEqual(0); + expect(result.lowerfoobar.nwCount).toEqual(1); + + expect(result.lowerFoobar.nwCount).toEqual(0); // likely fixme + expect(result.lowerFOObar.nwCount).toEqual(0); // likely fixme + expect(result.lowerfooBAR.nwCount).toEqual(0); + expect(result.lowerFOOBAR.nwCount).toEqual(0); + }, + }, +]); From 02fb5add3e09525be80190875757caf23876b440 Mon Sep 17 00:00:00 2001 From: koal44 Date: Tue, 14 Apr 2026 02:10:39 -0700 Subject: [PATCH 14/19] generalize harness engine handling and track stale fixmes --- test_new/browser/css3-compat.test.ts | 80 ++++++++-- test_new/browser/css3-escape.test.ts | 2 +- test_new/browser/harness/browser.ts | 19 ++- test_new/browser/harness/scenarios.ts | 208 ++++++++++++++------------ test_new/browser/prototype.test.ts | 10 +- test_new/browser/scotch.test.ts | 12 +- test_new/browser/w3c-selector.test.ts | 21 +++ test_new/browser/w3c.test.ts | 23 ++- test_new/browser/xml.test.ts | 6 +- test_new/utils/type.ts | 4 + 10 files changed, 248 insertions(+), 137 deletions(-) create mode 100644 test_new/browser/w3c-selector.test.ts diff --git a/test_new/browser/css3-compat.test.ts b/test_new/browser/css3-compat.test.ts index 9af5c6c..292fa5a 100644 --- a/test_new/browser/css3-compat.test.ts +++ b/test_new/browser/css3-compat.test.ts @@ -202,6 +202,14 @@ runScenarios('css3 compat', 'normal', [ { select: '.blox12, .blox13', expect: { count: 2 } }, ], }, + { + name: 'root selector', + html: '', + cases: [ + /* :root tests */ + { select: 'html', expect: { equivalentCase: { select: ':root' } } }, + ], + }, { name: ':nth-child(n) and :nth-of-type tests', html: ` @@ -302,6 +310,8 @@ runScenarios('css3 compat', 'normal', [ { select: '.blox15:not([foo="blox14"])', expect: { count: 1 } }, { select: '.blox16', expect: { count: 1 } }, { select: '.blox16:not(.blox15)', expect: { count: 1 } }, + { select: '.blox16:not(.blox15[foo="blox14"])', expect: { count: 1 } }, + { select: '.unitTest:not(.blox15[foo="blox15"])', expect: { count: 2 } }, ], }, { @@ -499,6 +509,8 @@ runScenarios('css3 compat', 'normal', [ `, cases: [ /* UI tests */ + { select: '.UI', expect: { count: 3 } }, + { select: '.UI > *', expect: { count: 6 } }, { select: '.UI .t1:enabled > .unitTest', expect: { count: 1 } }, { select: '.UI .t2:disabled > .unitTest', expect: { count: 1 } }, { select: '.UI .t3:checked + div', expect: { count: 1 } }, @@ -509,33 +521,62 @@ runScenarios('css3 compat', 'normal', [ name: '~ combinator tests', html: `
      -
      +
      the three last squares should be green and become red when the pointer hovers over the white square
      `, - cases: [ - /* ~ combinator tests */ - { select: '.tilda .t1', expect: { count: 1 } }, - { select: '.tilda .t1 ~ .unitTest', expect: { count: 3 } }, + steps: [ + { + // ensure baseline starts non-hovered; native :hover may already match if the pointer begins over the target + setupPage: async (page) => { await page.mouse.move(200, 200); }, + cases: [ + /* ~ combinator tests */ + { select: '.tilda', expect: { count: 1 } }, + { select: '.tilda .t1', expect: { count: 1 } }, + { select: '.tilda .t1 ~ .unitTest', expect: { count: 3 } }, + { select: '.tilda .t1:hover ~ .unitTest', expect: { count: 0 } }, + ], + }, + { + setupPage: async (page) => { await page.locator('.tilda .t1').hover(); }, + cases: [ + { select: '.tilda .t1:hover', expect: { count: 1 } }, + { select: '.tilda .t1:hover ~ .unitTest', expect: { count: 3 } }, + ], + }, ], }, { name: '+ combinator tests', html: `
      -
      +
      the last square should be green and become red when the pointer hovers over the FIRST white square
      `, - cases: [ - /* + combinator tests */ - { select: '.plus .t1, .plus .t2', expect: { count: 2 } }, - { select: '.plus .t1 + .unitTest + .unitTest', expect: { count: 1 } }, + steps: [ + { + // ensure baseline starts non-hovered; + setupPage: async (page) => { await page.mouse.move(200, 200); }, + cases: [ + /* + combinator tests */ + { select: '.plus', expect: { count: 1 } }, + { select: '.plus .t1, .plus .t2', expect: { count: 2 } }, + { select: '.plus .t1 + .unitTest + .unitTest', expect: { count: 1 } }, + { select: '.plus .t1:hover + .unitTest + .unitTest', expect: { count: 0 } }, + ], + }, + { + setupPage: async (page) => { await page.locator('.plus .t1').hover(); }, + cases: [ + { select: '.plus .t1:hover + .unitTest + .unitTest', expect: { count: 1 } }, + ], + }, ], }, { @@ -569,16 +610,16 @@ runScenarios('css3 compat', 'normal', [ { select: '.blox23s1[foo="blox" erroneous]', expect: { throws: true } }, { select: '.blox19[class="BLOX19 UNITTEST" i]', expect: { count: 1 } }, { select: '.blox20[class="BLOX20 UNITTEST" i]', expect: { count: 1 } }, - { select: '.blox20[class="blox20 unitTest" s]', expect: { throws: true } }, + { select: '.blox20[class="blox20 unitTest" s]', status: 'fixme' }, { select: '.blox21[class*="21 UN" i]', expect: { count: 1 } }, - { select: '.blox22[class*="22 unitt" s]', expect: { throws: true } }, - { select: '.blox22[class*="22 unitT" s]', expect: { throws: true } }, + { select: '.blox22[class*="22 unitt" s]', status: 'fixme' }, + { select: '.blox22[class*="22 unitT" s]', status: 'fixme' }, { select: '.blox24[class^="BLOX" i]', expect: { count: 1 } }, { select: '.blox25[class^="BLOX"]', expect: { count: 0 } }, - { select: '.blox25[class^="blox" s]', expect: { throws: true } }, + { select: '.blox25[class^="blox" s]', status: 'fixme' }, { select: '.blox26[class$="tEST" i]', expect: { count: 1 } }, - { select: '.blox27[class$="TEst" s]', expect: { throws: true } }, - { select: '.blox27[class$="Test" s]', expect: { throws: true } }, + { select: '.blox27[class$="TEst" s]', status: 'fixme' }, + { select: '.blox27[class$="Test" s]', status: 'fixme' }, { select: '.blox28[class~="unitTEST" i]', expect: { count: 1 } }, ], }, @@ -602,4 +643,11 @@ runScenarios('css3 compat', 'normal', [ { select: '.blox27[class$="Test" s]' }, ], }, + { + name: 'double-negation not selector', + html: `
      `, + cases: [ + { select: 'div:not(:not(div))', expect: { ids: ['a'] } }, + ], + }, ]); diff --git a/test_new/browser/css3-escape.test.ts b/test_new/browser/css3-escape.test.ts index 4d8fb75..dee1497 100644 --- a/test_new/browser/css3-escape.test.ts +++ b/test_new/browser/css3-escape.test.ts @@ -385,7 +385,7 @@ runScenarios('css3 escaped identifiers', 'normal', [ { select: '#\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, { select: '.\u{12345}', expect: { count: 1, ids: ['\u{12345}'] } }, - { select: '#\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, + { select: '#\u{0}', expect: { count: 1, ids: ['\u{fffd}'] }, status: 'fixme' }, { select: '.\u{0}', expect: { count: 1, ids: ['\u{fffd}'] } }, { select: '#ab\u{0}c', expect: { count: 1, ids: ['ab\u{fffd}c'] } }, diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts index 5cb9215..0ff5ff9 100644 --- a/test_new/browser/harness/browser.ts +++ b/test_new/browser/harness/browser.ts @@ -17,7 +17,8 @@ export type EvalResult = { info: string; mismatchMsg?: string; equivMismatchMsg?: string; -} & Record; + engineResults: Partial>; +}; export type EngineResult = { count: number; @@ -48,14 +49,11 @@ export function installBrowserHelpers(): void { return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 11; } - function isRehomed(ref?: ContextRef): boolean { - if (!ref || ref.by === 'document') return false; - if (isRehomed(ref.within)) return true; - return ref.by !== 'iframe' - && ref.by !== 'template' - && !!ref.home - && ref.home !== 'document'; - } +function isRehomed(ref?: ContextRef): boolean { + if (!ref) return false; + if ('within' in ref && isRehomed(ref.within)) return true; + return 'home' in ref && !!ref.home && ref.home !== 'document'; +} // Source - https://stackoverflow.com/a/65443215 function stringify(obj: unknown): string { @@ -126,7 +124,7 @@ export function installBrowserHelpers(): void { function resolveContext(ref?: ContextRef): QueryContext | null { if (!ref || ref.by === 'document') return document; - const base = ref.within ? resolveContext(ref.within) : document; + const base = 'within' in ref && ref.within ? resolveContext(ref.within) : document; if (!base) return null; if (ref.by === 'iframe') { @@ -143,6 +141,7 @@ export function installBrowserHelpers(): void { const el = ref.by === 'id' ? queryId(base, ref.id) : ref.by === 'first' ? base.querySelector(ref.selector) + : ref.by === 'documentElement' ? document.documentElement : null; if (!el) return null; diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index c5ad9b5..d7a5745 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -1,8 +1,8 @@ import { test, chromium, expect, firefox, webkit } from '@playwright/test'; import type { Browser, Page } from '@playwright/test'; -import { assertNever, type DistributiveOmit } from '../../utils/type'; +import { assertNever, type Permutations, type DistributiveOmit } from '../../utils/type'; import { installBrowserHelpers } from './browser'; -import type { EngineResult, EvalResult, EngineQuery } from './browser'; +import type { EngineResult, EvalResult, EngineAndQueryResult, NamedQueryResult } from './browser'; type HarnessMode = 'normal' | 'fixme'; const rawHarnessMode = process.env.HARNESS_MODE; @@ -11,7 +11,6 @@ if (rawHarnessMode !== undefined && rawHarnessMode !== 'normal' && rawHarnessMod } const HARNESS_MODE: HarnessMode = rawHarnessMode ?? 'normal'; -type CaseBase = { expect?: Expectation; status?: CaseStatus; browsers?: BrowserName[]; }; export type SelectCase = { select: string; ref?: ContextRef; } & CaseBase; export type ByIdCase = { byId: string; ref?: ContextRef; } & CaseBase; export type ByTagCase = { byTag: string; ref?: ContextRef; } & CaseBase; @@ -20,6 +19,13 @@ export type FirstCase = { first: string; ref?: ContextRef; } & CaseBase; export type MatchCase = { match: string; ref: ContextRef; } & CaseBase; export type ClosestCase = { closest: string; ref: ContextRef; } & CaseBase; +type CaseBase = { + expect?: Expectation; + status?: CaseStatus; + browsers?: BrowserName[]; + engines?: Engine[]; +}; + export type TestCase = SelectCase | ByIdCase | ByTagCase | ByClassCase | MatchCase | FirstCase | ClosestCase; export type Scenario = { @@ -41,7 +47,6 @@ type ScenarioStep = { }; export type Expectation = { - allowMismatch?: boolean; count?: number; ids?: string[]; includesIds?: string[]; @@ -53,7 +58,7 @@ export type Expectation = { equivalentCase?: EquivalentCase; }; -export type EquivalentCase = DistributiveOmit; +export type EquivalentCase = DistributiveOmit; // const foo: EquivalentCase = { select: 'div' }; // just to verify the type const BROWSER_NAMES = ['chromium', 'firefox', 'webkit'] as const; @@ -70,13 +75,15 @@ export type ContextRef = | { by: 'document' } | { by: 'id'; id: string; home?: ContextHome; within?: ContextRef } | { by: 'first'; selector: string; home?: ContextHome; within?: ContextRef } + | { by: 'documentElement'; home?: ContextHome; } | { by: 'iframe'; id: string; within?: ContextRef } | { by: 'template'; id: string; within?: ContextRef } export type ContextHome = 'document' | 'detached' | 'fragment'; export type NwsapiId = 'nwsapi-bootstrap'; -type Misfail = { passedEverywhere: boolean; resultInfo: string; stepIndex: number; caseIndex: number; }; +type PassTracker = { passedEverywhere: boolean; resultInfo: string; stepIndex: number; caseIndex: number; }; + type CaseInfo = { browser: BrowserName; scenario: Scenario; @@ -84,7 +91,8 @@ type CaseInfo = { stepIndex: number; caseIndex: number; stepCaseIndex: number; - misfails: Record; + misfails: Record; + misfixes: Record; }; export function runScenarios(label: string, status: ScenariosStatus, scenarios: Scenario[]): void { @@ -142,8 +150,9 @@ export function runScenarios(label: string, status: ScenariosStatus, scenarios: async function runScenario(s: Scenario, pages: Record): Promise { const scenarioBrowsers = s.browsers ?? BROWSER_NAMES; - // cases marked 'fail' and whether they passed in every applicable browser so far - const misfails: Record = {}; + // cases marked fail/fixme and whether they passed in every applicable browser so far + const misfails: Record = {}; + const misfixes: Record = {}; const steps: ScenarioStep[] = [ ...(s.steps ?? []), @@ -167,7 +176,7 @@ async function runScenario(s: Scenario, pages: Record): Promi page, { browser: browserName, scenario: s, case: c, - stepIndex, caseIndex, stepCaseIndex, misfails + stepIndex, caseIndex, stepCaseIndex, misfails, misfixes, } ); stepCaseIndex++; @@ -175,20 +184,24 @@ async function runScenario(s: Scenario, pages: Record): Promi } } - // At the end of the scenario, check if any 'fail' cases passed unexpectedly in all browsers - for (const [_scIndex, misFail] of Object.entries(misfails)) { - if (misFail.passedEverywhere) { - const { stepIndex, caseIndex } = misFail; + // At the end of the scenario, check if any 'fail' or 'fixme' cases passed unexpectedly + const throwOnUnexpectedPass = (kind: 'fail' | 'fixme', trackers: Record) => { + for (const tracker of Object.values(trackers)) { + if (!tracker.passedEverywhere) continue; throw new Error( - `Step #${stepIndex + 1}, Case #${caseIndex + 1} was marked 'fail' but unexpectedly passed in every applicable browser.\n` + - `${s.name} :: ${misFail.resultInfo}\n` + `${s.name}\n` + + `Step #${tracker.stepIndex + 1}, Case #${tracker.caseIndex + 1} · Marked '${kind}' but passed unexpectedly.\n` + + `Query: ${tracker.resultInfo}` ); } - } + }; + + throwOnUnexpectedPass('fail', misfails); + throwOnUnexpectedPass('fixme', misfixes); } async function runCase(page: Page, caseInfo: CaseInfo): Promise { - const { scenario: s, case: c } = caseInfo; + const { scenario: s, case: c, stepIndex, caseIndex, stepCaseIndex } = caseInfo; if (c.status === 'skip') return; if (s.status !== 'fixme' && HARNESS_MODE === 'fixme' && c.status !== 'fixme') { @@ -213,20 +226,24 @@ async function runCase(page: Page, caseInfo: CaseInfo): Promise { return; } + const updatePassTracker = (trackers: Record) => { + const prevPassed = trackers[stepCaseIndex]?.passedEverywhere ?? true; + trackers[stepCaseIndex] = { + passedEverywhere: !thrown && prevPassed, + resultInfo: trackers[stepCaseIndex]?.resultInfo ?? result.info, + stepIndex, + caseIndex, + }; + }; + if (status === 'fail') { - const curPassed = !thrown; - const prevPassed = caseInfo.misfails[caseInfo.stepCaseIndex]?.passedEverywhere ?? true; - const passedEverywhere = curPassed && prevPassed; - const resultInfo = caseInfo.misfails[caseInfo.stepCaseIndex]?.resultInfo ?? result.info; - const { stepIndex, caseIndex } = caseInfo; - caseInfo.misfails[caseInfo.stepCaseIndex] = { passedEverywhere, resultInfo, stepIndex, caseIndex }; + updatePassTracker(caseInfo.misfails); return; } if (status === 'fixme') { - if (thrown && HARNESS_MODE === 'fixme') { - throw thrown; - } + updatePassTracker(caseInfo.misfixes); + if (thrown && HARNESS_MODE === 'fixme') throw thrown; return; } @@ -236,52 +253,59 @@ async function runCase(page: Page, caseInfo: CaseInfo): Promise { async function evalCase(page: Page, testCase: TestCase): Promise { return await page.evaluate((c) => { const pw = window.__pwHelpers; - const query = pw.getCaseQuery(c); - const equivCase = c.expect?.equivalentCase; - - const nwFn: EngineQuery = pw.getEngineQuery(c, 'nw'); - const nativeFn: EngineQuery = pw.getEngineQuery(c, 'native'); + const query = pw.getCaseQuery(c); const ctx = pw.resolveContext(c.ref); const ctxErrorMsg = ctx ? undefined : `Could not resolve context from ref: ${pw.stringify(c.ref)}`; - const nwRes = pw.getResults(nwFn, query, ctx, ctxErrorMsg); - const nativeRes = pw.getResults(nativeFn, query, ctx, ctxErrorMsg); - - const mismatchMsg = pw.compareQueryResults( - { name: `native:${pw.getCaseLabel(c, 'native')}`, result: nativeRes.queryResult }, - { name: `nw:${pw.getCaseLabel(c, 'nw')}`, result: nwRes.queryResult }, - ); - - let equivMismatchMsg: string | undefined; - if (equivCase) { - const nwEquivFn = pw.getEngineQuery(equivCase, 'nw'); - const nwEquivQuery = pw.getCaseQuery(equivCase); - - const equivCtx = pw.resolveContext(equivCase.ref); - const equivCtxErrorMsg = !equivCtx - ? `Could not resolve equivalent context from ref: ${pw.stringify(equivCase.ref)}` - : undefined; - const nwEquivRes = pw.getResults(nwEquivFn, nwEquivQuery, equivCtx, equivCtxErrorMsg); - - if (pw.isRehomed(c.ref) || pw.isRehomed(equivCase.ref)) { - equivMismatchMsg = - `Equivalent-case assertion unsupported because one or more contexts were rehomed.\n` + - `Identity-based equivalence is only supported for document-backed contexts.\n` + - ` case context: ${pw.stringify(c.ref)}${pw.isRehomed(c.ref) ? ' (rehomed)' : ''}\n` + - ` equivalent case context: ${pw.stringify(equivCase.ref)}${pw.isRehomed(equivCase.ref) ? ' (rehomed)' : ''}` - } else { - equivMismatchMsg = pw.compareQueryResults( - { name: `nw:${pw.getCaseLabel(c, 'nw')}`, result: nwRes.queryResult }, - { name: `nwEquiv:${pw.getCaseLabel(equivCase, 'nw')}`, result: nwEquivRes.queryResult }, + const equivCase = c.expect?.equivalentCase; + const equivQuery = equivCase ? pw.getCaseQuery(equivCase) : undefined; + const equivCtx = equivCase ? pw.resolveContext(equivCase?.ref) : null; + const equivCtxErrorMsg = equivCase && !equivCtx + ? `Could not resolve equivalent context from ref: ${pw.stringify(equivCase.ref)}` + : undefined; + let equivMismatchMsg = equivCase && (pw.isRehomed(c.ref) || pw.isRehomed(equivCase.ref)) + ? `Equivalent-case assertion unsupported because one or more contexts were rehomed.\n` + + `Identity-based equivalence is only supported for document-backed contexts.\n` + + ` case context: ${pw.stringify(c.ref)}${pw.isRehomed(c.ref) ? ' (rehomed)' : ''}\n` + + ` equivalent case context: ${pw.stringify(equivCase.ref)}${pw.isRehomed(equivCase.ref) ? ' (rehomed)' : ''}` + : undefined; + + const allEngines: Permutations = ['native', 'nw']; + const engines = c.engines ?? allEngines; + const engineResults: Partial> = {}; + const makeNamedQr = (tc: TestCase, ng: Engine, res: EngineAndQueryResult, suffix = ''): NamedQueryResult => + ({ name: `${ng}${suffix}:${pw.getCaseLabel(tc, ng)}`, result: res.queryResult }); + + let mismatchMsg: string | undefined; + let firstNamedQr: NamedQueryResult | undefined; + + for (const engine of engines) { + const fn = pw.getEngineQuery(c, engine); + const res = pw.getResults(fn, query, ctx, ctxErrorMsg); + engineResults[engine] = res; + + const namedQr = makeNamedQr(c, engine, res); + if (!mismatchMsg && firstNamedQr) { + mismatchMsg = pw.compareQueryResults(firstNamedQr, namedQr); + } + firstNamedQr ??= namedQr; + + if (!equivMismatchMsg && equivCase && equivQuery) { + const equivFn = pw.getEngineQuery(equivCase, engine); + const equivRes = pw.getResults(equivFn, equivQuery, equivCtx, equivCtxErrorMsg); + equivMismatchMsg ??= pw.compareQueryResults( + namedQr, + makeNamedQr(equivCase, engine, equivRes, 'Equiv') ); } } return { info: query, mismatchMsg, equivMismatchMsg, - native: nativeRes.engineResult, - nw: nwRes.engineResult + engineResults: Object.fromEntries( + engines.map((engine) => [engine, engineResults[engine]!.engineResult]) + ), }; }, testCase); } @@ -328,43 +352,40 @@ function runEngineChecks( key: keyof EngineResult, check: (r: EngineResult, label: string) => void ): void { - for (const engine of ENGINES) { - const r = result[engine]; - if (r.threw) continue; - - const sameIds = (a: string[], b: string[]): boolean => - a.length === b.length && a.every((x, i) => x === b[i]); - - const enginesWithSameOutcome = ENGINES.filter((e) => { - if (result[e].threw) return false; - switch (key) { - case 'count': return r.count === result[e].count; - case 'ids': return sameIds(r.ids, result[e].ids); - case 'classes': return sameIds(r.classes, result[e].classes); - case 'threw': return r.threw === result[e].threw; - default: assertNever(key); - } - }); - - let label = ` · engines=${enginesWithSameOutcome.join('+')}${baseMsg}`; + const entries = Object.entries(result.engineResults) as [Engine, EngineResult][]; + + const sameIds = (a: string[], b: string[]): boolean => + a.length === b.length && a.every((x, i) => x === b[i]); + + for (const [, r] of entries) { + const enginesWithSameOutcome = entries + .filter(([, other]) => { + switch (key) { + case 'count': return r.count === other.count; + case 'ids': return sameIds(r.ids, other.ids); + case 'classes': return sameIds(r.classes, other.classes); + case 'threw': return r.threw === other.threw; + default: return assertNever(key); + } + }) + .map(([engine]) => engine); + + const label = ` · engines=${enginesWithSameOutcome.join('+')}${baseMsg}`; check(r, label); } } function checkResult(result: EvalResult, expectation: Expectation, caseInfo: CaseInfo): void { - const allowMismatch = expectation.allowMismatch ?? false; const { stepIndex, caseIndex, browser, scenario: s } = caseInfo; const header = `${s.name}\nStep #${stepIndex + 1}, Case #${caseIndex + 1} · browser=${browser}`; - const msg = + const msg = `\nQuery: ${result.info}` + - `${result.mismatchMsg && !allowMismatch ? `\n${result.mismatchMsg}` : ''}`; + `${result.mismatchMsg ? `\n${result.mismatchMsg}` : ''}`; - if (expectation.throws) { - expect(result.nw.threw, `${header}${msg}`).toBe(true); - return; - } - - expect(result.nw.threw, `${header}${msg}`).toBe(false); + runEngineChecks(result, msg, 'threw', (r, label) => { + expect(r.threw, `${header}${label}`).toBe(expectation.throws ?? false); + }); + if (expectation.throws) return; if (expectation.count !== undefined) { runEngineChecks(result, msg, 'count', (r, label) => { @@ -418,10 +439,7 @@ function checkResult(result: EvalResult, expectation: Expectation, caseInfo: Cas } } - if (!allowMismatch && !result.native.threw) { - const mismatch = !!result.mismatchMsg; - expect(mismatch, `${header}${msg}`).toBe(false); - } + expect(!!result.mismatchMsg, `${header}${msg}`).toBe(false); if (expectation.equivalentCase) { const equivMismatch = !!result.equivMismatchMsg; diff --git a/test_new/browser/prototype.test.ts b/test_new/browser/prototype.test.ts index dccc814..18967fd 100644 --- a/test_new/browser/prototype.test.ts +++ b/test_new/browser/prototype.test.ts @@ -217,8 +217,8 @@ runScenarios('prototype 2', 'normal', [ { select: '[foo-bar]', expect: { ids: ['attr_with_dash'] } }, // attribute with hyphen // testSelectorWithUniversalAndHyphenTokenizedAttributeValue - { select: '*[xml:lang|="es"]', expect: { ids: ['item_3'] } }, - { select: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] } }, + { select: '*[xml:lang|="es"]', expect: { ids: ['item_3'] }, status: 'fixme' }, + { select: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] }, status: 'fixme' }, // testSelectorWithTagNameAndNegatedAttributeValue { select: 'a:not([href="#"])', expect: { count: 0, ids: [] } }, @@ -274,8 +274,8 @@ runScenarios('prototype 2', 'normal', [ // testSelectorWithNamespacedAttributes { select: '[xml:lang]', + status: 'fixme', expect: { - allowMismatch: true, // some browsers don't support selecting by namespaced attributes count: 2, includesIds: ['item_3'], equivalentCase: { select: '*[xml:lang]' } @@ -415,8 +415,8 @@ runScenarios('prototype 2', 'normal', [ { select: '#level2_2 :only-child:not(:first-child)', expect: { count: 0, ids: [] } }, // testCommasFor$$ - { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, - { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] }, status: 'fixme' }, + { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] }, status: 'fixme' }, { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, diff --git a/test_new/browser/scotch.test.ts b/test_new/browser/scotch.test.ts index b360b49..4910b05 100644 --- a/test_new/browser/scotch.test.ts +++ b/test_new/browser/scotch.test.ts @@ -175,8 +175,8 @@ runScenarios('scotch', 'normal', [ { select: '#troubleForm2 input[name="brackets[5][]"]', expect: { ids: ['chk_1', 'chk_2'] } }, { select: '#troubleForm2 input[name="brackets[5][]"]:checked', expect: { ids: ['chk_1'] } }, { select: 'cite[title="hello world!"]', expect: { ids: ['with_title'] } }, - { select: '[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'], equivalentCase: { select: '*[xml:lang]' } } }, - { select: '*[xml:lang]', expect: { allowMismatch: true, count: 2, includesIds: ['item_3'] } }, + { select: '[xml:lang]', expect: { count: 2, includesIds: ['item_3'], equivalentCase: { select: '*[xml:lang]' } }, status: 'fixme' }, + { select: '*[xml:lang]', expect: { count: 2, includesIds: ['item_3'] }, status: 'fixme' }, // E[foo="bar"] { select: 'a[href="#"]', expect: { ids: ['link_1', 'link_2', 'link_3'] } }, @@ -189,8 +189,8 @@ runScenarios('scotch', 'normal', [ { select: 'a[class~=external][href="#"]', expect: { ids: ['link_3'] } }, // E[foo|="en"] - { select: '*[xml:lang|="es"]', expect: { ids: ['item_3'] } }, - { select: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] } }, + { select: '*[xml:lang|="es"]', expect: { ids: ['item_3'] }, status: 'fixme' }, + { select: '*[xml:lang|="ES"]', expect: { ids: ['item_3'] }, status: 'fixme' }, // E[foo^="bar"] { select: 'div[class^=bro]', expect: { ids: ['father', 'uncle'] } }, @@ -463,8 +463,8 @@ runScenarios('scotch', 'normal', [ htmlMode: 'document', setupPage: setupNw, cases: [ - { select: '#list, .first,*[xml:lang="es-us"] , #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, - { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] } }, + { select: '#list, .first,*[xml:lang="es-us"] , #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] }, status: 'fixme' }, + { select: '#list, .first, *[xml:lang="es-us"], #troubleForm', expect: { ids: ['p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'] }, status: 'fixme' }, { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, { select: 'form[title*="commas,"], input[value="#commaOne,#commaTwo"]', expect: { ids: ['commaParent', 'commaChild'] } }, ], diff --git a/test_new/browser/w3c-selector.test.ts b/test_new/browser/w3c-selector.test.ts new file mode 100644 index 0000000..170aa99 --- /dev/null +++ b/test_new/browser/w3c-selector.test.ts @@ -0,0 +1,21 @@ +// import { expect } from '@playwright/test'; +import { runScenarios } from './harness/scenarios'; + +runScenarios('w3c - selector', 'normal', [ + { + name: ':root selector contexts', + html: `
      `, + cases: [ + { select: ':root', expect: { count: 1 } }, + { select: ':root', ref: { by: 'id', id: 'foo' }, expect: { count: 0 } }, + { select: ':root', ref: { by: 'id', id: 'foo', home: 'detached' }, expect: { count: 0 } }, + { select: ':root', ref: { by: 'id', id: 'foo', home: 'fragment' }, expect: { count: 0 } }, + + { select: ':root span', ref: { by: 'id', id: 'foo' }, expect: { count: 1 } }, + { select: ':root span', ref: { by: 'id', id: 'foo', home: 'detached' }, expect: { count: 0 } }, + { select: ':root span', ref: { by: 'id', id: 'foo', home: 'fragment' }, expect: { count: 0 } }, + + { closest: ':root > *', ref: { by: 'id', id: 'foo', home: 'detached' }, expect: { count: 0 } }, + ], + }, +]); diff --git a/test_new/browser/w3c.test.ts b/test_new/browser/w3c.test.ts index 48832ea..3a91813 100644 --- a/test_new/browser/w3c.test.ts +++ b/test_new/browser/w3c.test.ts @@ -991,11 +991,12 @@ runScenarios('w3c', 'normal', [ { closest: '.', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, { select: '.5cm', expect: { throws: true } }, + { select: '//.5cm', expect: { throws: true } }, { first: '..test', expect: { throws: true } }, { match: '.foo..quux', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, { closest: '.bar.', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, - { select: 'div & address, p', expect: { throws: true } }, + { select: 'div & address, p', expect: { throws: true }, status: 'fixme' }, { first: 'div ++ address, p', expect: { throws: true } }, { match: 'div ~~ address, p', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, { closest: '[*=test]', ref: { by: 'id', id: 'root' }, expect: { throws: true } }, @@ -1017,6 +1018,26 @@ runScenarios('w3c', 'normal', [ { select: '>*', expect: { throws: true } }, + // deduping + { select: 'foo & address, p', expect: { throws: true } }, + { select: 'div:subject', expect: { throws: true } }, + { select: ':canvas', expect: { throws: true } }, + { select: ':viewport', expect: { throws: true } }, + { select: ':window', expect: { throws: true } }, + { select: ':menu', expect: { throws: true } }, + { select: ':table', expect: { throws: true } }, + { select: ':select', expect: { throws: true } }, + { select: '::canvas', expect: { throws: true } }, + { select: '::viewport', expect: { throws: true } }, + { select: '::window', expect: { throws: true } }, + { select: '::menu', expect: { throws: true } }, + { select: '::table', expect: { throws: true } }, + { select: '::select', expect: { throws: true } }, + { select: '', expect: { throws: true } }, + { select: '', expect: { throws: true } }, + { select: '', expect: { throws: true } }, + { select: '', expect: { throws: true } }, + // ----------------------------------------------------------------------------- // Valid selectors: querySelectorAll / querySelector // ----------------------------------------------------------------------------- diff --git a/test_new/browser/xml.test.ts b/test_new/browser/xml.test.ts index e6f6a32..0187dc1 100644 --- a/test_new/browser/xml.test.ts +++ b/test_new/browser/xml.test.ts @@ -11,8 +11,8 @@ runScenarios('xml', 'normal', [ htmlMode: 'document', cases: [ { select: '[*|href]', expect: { count: 1 } }, - { select: '[xlink:href=foo]', expect: { count: 1 } }, - { select: '[xlink\\:href=foo]', expect: { count: 1 }, status: 'fixme' }, // maybe not interesting? + { select: '[xlink:href=foo]', expect: { count: 1 }, status: 'fixme' }, + { select: '[xlink\\:href=foo]', expect: { count: 1 }, status: 'fixme' }, ], }, @@ -65,7 +65,7 @@ runScenarios('xml', 'normal', [ { select: 'foo', ref: { by: 'id', id: 'upper-box' }, expect: { count: 0 }, status: 'fixme' }, { select: 'bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 1 } }, { select: 'foo bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 0 }, status: 'fixme' }, - { select: 'Foo bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 1 }, status: 'fixme' }, + { select: 'Foo bar', ref: { by: 'id', id: 'upper-box' }, expect: { count: 1 } }, // { select: 'bar', ref: { by: 'id', id: 'lower-box' }, expect: { count: 1 } }, diff --git a/test_new/utils/type.ts b/test_new/utils/type.ts index 42f34aa..257a837 100644 --- a/test_new/utils/type.ts +++ b/test_new/utils/type.ts @@ -3,3 +3,7 @@ export type DistributiveOmit = T extends unknown ? Omi export function assertNever(x: never): never { throw new Error(`Unexpected key: ${x}`); } + +export type Permutations = + [T] extends [never] ? [] : + T extends K ? [T, ...Permutations>] : never; From 06d04c33adb31f554560c2c02a61f07754ede7c6 Mon Sep 17 00:00:00 2001 From: koal44 Date: Tue, 14 Apr 2026 20:25:52 -0700 Subject: [PATCH 15/19] add missing deduped jquery selector coverage from w3c and slick --- test_new/browser/harness/browser.ts | 10 +- test_new/browser/harness/scenarios.ts | 73 +++++--- test_new/browser/jquery.test.ts | 259 +++++++++++++++++--------- 3 files changed, 230 insertions(+), 112 deletions(-) diff --git a/test_new/browser/harness/browser.ts b/test_new/browser/harness/browser.ts index 0ff5ff9..f325328 100644 --- a/test_new/browser/harness/browser.ts +++ b/test_new/browser/harness/browser.ts @@ -49,11 +49,11 @@ export function installBrowserHelpers(): void { return typeof x === 'object' && x !== null && 'nodeType' in x && x.nodeType === 11; } -function isRehomed(ref?: ContextRef): boolean { - if (!ref) return false; - if ('within' in ref && isRehomed(ref.within)) return true; - return 'home' in ref && !!ref.home && ref.home !== 'document'; -} + function isRehomed(ref?: ContextRef): boolean { + if (!ref) return false; + if ('within' in ref && isRehomed(ref.within)) return true; + return 'home' in ref && !!ref.home && ref.home !== 'document'; + } // Source - https://stackoverflow.com/a/65443215 function stringify(obj: unknown): string { diff --git a/test_new/browser/harness/scenarios.ts b/test_new/browser/harness/scenarios.ts index d7a5745..b01786b 100644 --- a/test_new/browser/harness/scenarios.ts +++ b/test_new/browser/harness/scenarios.ts @@ -34,6 +34,7 @@ export type Scenario = { html: string; htmlMode?: 'body' | 'document'; browsers?: BrowserName[]; + engines?: Engine[]; steps?: ScenarioStep[]; // ergonomic sugar for simple scenarios that don't require multiple steps @@ -208,6 +209,7 @@ async function runCase(page: Page, caseInfo: CaseInfo): Promise { return; } if (c.browsers && !c.browsers.includes(caseInfo.browser)) return; + c.engines = c.engines ?? s.engines; const result = await evalCase(page, c); const expectation = c.expect ?? {}; @@ -370,79 +372,106 @@ function runEngineChecks( }) .map(([engine]) => engine); - const label = ` · engines=${enginesWithSameOutcome.join('+')}${baseMsg}`; - check(r, label); + const engineLabel = ` · Engines=${enginesWithSameOutcome.join('+')}${baseMsg}`; + check(r, engineLabel); } } function checkResult(result: EvalResult, expectation: Expectation, caseInfo: CaseInfo): void { const { stepIndex, caseIndex, browser, scenario: s } = caseInfo; - const header = `${s.name}\nStep #${stepIndex + 1}, Case #${caseIndex + 1} · browser=${browser}`; + const header = `${s.name}\nStep #${stepIndex + 1}, Case #${caseIndex + 1} · Browser=${browser}`; const msg = `\nQuery: ${result.info}` + - `${result.mismatchMsg ? `\n${result.mismatchMsg}` : ''}`; + `\nContext: ${formatContextRef(caseInfo.case.ref)}` + + `${result.mismatchMsg ? `\n\n${result.mismatchMsg}` : ''}`; - runEngineChecks(result, msg, 'threw', (r, label) => { - expect(r.threw, `${header}${label}`).toBe(expectation.throws ?? false); + runEngineChecks(result, msg, 'threw', (r, nglabel) => { + const errLabel = `Expected ${expectation.throws ? 'a throw' : 'no throw'}, got ${r.threw ? 'a throw' : 'no throw'}.`; + expect(r.threw, `${errLabel}\n\n${header}${nglabel}`).toBe(expectation.throws ?? false); }); if (expectation.throws) return; if (expectation.count !== undefined) { - runEngineChecks(result, msg, 'count', (r, label) => { - expect(r.count, `${header}${label}`).toEqual(expectation.count); + runEngineChecks(result, msg, 'count', (r, ngLabel) => { + const errLabel = `Expected count ${expectation.count}, got ${r.count}.`; + expect(r.count, `${errLabel}\n\n${header}${ngLabel}`).toEqual(expectation.count); }); } if (expectation.ids) { - runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, `${header}${label}`).toEqual(expectation.ids); + runEngineChecks(result, msg, 'ids', (r, ngLabel) => { + const errLabel = `Expected ids ${JSON.stringify(expectation.ids)}, got ${JSON.stringify(r.ids)}.`; + expect(r.ids, `${errLabel}\n\n${header}${ngLabel}`).toEqual(expectation.ids); }); } if (expectation.includesIds) { for (const id of expectation.includesIds) { - runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, `${header}${label}`).toContain(id); + runEngineChecks(result, msg, 'ids', (r, ngLabel) => { + const errLabel = `Expected ids to include ${JSON.stringify(id)}, got ${JSON.stringify(r.ids)}.`; + expect(r.ids, `${errLabel}\n\n${header}${ngLabel}`).toContain(id); }); } } if (expectation.excludesIds) { for (const id of expectation.excludesIds) { - runEngineChecks(result, msg, 'ids', (r, label) => { - expect(r.ids, `${header}${label}`).not.toContain(id); + runEngineChecks(result, msg, 'ids', (r, ngLabel) => { + const errLabel = `Expected ids not to include ${JSON.stringify(id)}, got ${JSON.stringify(r.ids)}.`; + expect(r.ids, `${errLabel}\n\n${header}${ngLabel}`).not.toContain(id); }); } } if (expectation.classes) { - runEngineChecks(result, msg, 'classes', (r, label) => { - expect(r.classes, `${header}${label}`).toEqual(expectation.classes); + runEngineChecks(result, msg, 'classes', (r, ngLabel) => { + const errLabel = `Expected classes ${JSON.stringify(expectation.classes)}, got ${JSON.stringify(r.classes)}.`; + expect(r.classes, `${errLabel}\n\n${header}${ngLabel}`).toEqual(expectation.classes); }); } if (expectation.includesClasses) { for (const cls of expectation.includesClasses) { - runEngineChecks(result, msg, 'classes', (r, label) => { + runEngineChecks(result, msg, 'classes', (r, ngLabel) => { + const errLabel = `Expected classes to include ${JSON.stringify(cls)}, got ${JSON.stringify(r.classes)}.`; const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, `${header}${label}`).toContain(cls); + expect(classTokens, `${errLabel}\n\n${header}${ngLabel}`).toContain(cls); }); } } if (expectation.excludesClasses) { for (const cls of expectation.excludesClasses) { - runEngineChecks(result, msg, 'classes', (r, label) => { + runEngineChecks(result, msg, 'classes', (r, ngLabel) => { + const errLabel = `Expected classes not to include ${JSON.stringify(cls)}, got ${JSON.stringify(r.classes)}.`; const classTokens = r.classes.flatMap(s => s.trim() ? s.trim().split(/\s+/) : []); - expect(classTokens, `${header}${label}`).not.toContain(cls); + expect(classTokens, `${errLabel}\n\n${header}${ngLabel}`).not.toContain(cls); }); } } - expect(!!result.mismatchMsg, `${header}${msg}`).toBe(false); + expect(!!result.mismatchMsg, `Expected engine agreement, but they differed.\n\n${header}${msg}`).toBe(false); if (expectation.equivalentCase) { + const errLabel = `Expected this case to match its equivalent case, but it did not.`; const equivMismatch = !!result.equivMismatchMsg; - expect(equivMismatch, `${header}${msg}\n${result.equivMismatchMsg}`).toBe(false); + expect(equivMismatch, `${errLabel}\n\n${header}${msg}\n${result.equivMismatchMsg}`).toBe(false); + } +} + +function formatContextRef(ref?: ContextRef): string { + if (!ref) return 'document'; + let base: string; + switch (ref.by) { + case 'document': base = 'document'; break; + case 'id': base = `id(${ref.id})`; break; + case 'first': base = `first(${ref.selector})`; break; + case 'documentElement': base = 'documentElement'; break; + case 'iframe': base = `iframe(${ref.id})`; break; + case 'template': base = `template(${ref.id})`; break; + default: assertNever(ref); } + if ('home' in ref && ref.home) base += `:${ref.home}`; + if ('within' in ref && ref.within) base = `${formatContextRef(ref.within)} > ${base}`; + return base; } diff --git a/test_new/browser/jquery.test.ts b/test_new/browser/jquery.test.ts index 87b2a1b..b514177 100644 --- a/test_new/browser/jquery.test.ts +++ b/test_new/browser/jquery.test.ts @@ -1,4 +1,4 @@ -import { expect } from "@playwright/test"; +import { expect, type Page } from "@playwright/test"; import { runScenarios } from "./harness/scenarios"; const html = ` @@ -16,6 +16,7 @@ const html = `

      jQuery Test Suite

      +

      @@ -263,7 +264,7 @@ const html = ` runScenarios('jquery', 'normal', [ { - name: 'element', + name: 'element selectors', html: html, htmlMode: 'document', cases: [ @@ -287,11 +288,13 @@ runScenarios('jquery', 'normal', [ { select: '#lengthtest input', expect: { count: 2 } }, // Duplicate / sort-order checks from the original suite, expressed as counts. - { select: '*', expect: { count: 186 } }, - { select: '*, *', expect: { count: 186 } }, + { select: '*', expect: { count: 187 } }, + { select: '*, *', expect: { count: 187 } }, + { select: '*', expect: { equivalentCase: { select: '*, *' } } }, { select: 'p', expect: { count: 6 } }, { select: 'p, div p', expect: { count: 6 } }, + { select: 'p', expect: { equivalentCase: { select: 'div p' } } }, { select: 'h2, h1', expect: { ids: ['header', 'banner', 'userAgent'] } }, { select: 'p, p a', expect: { ids: ['firstp', 'simon1', 'ap', 'google', 'groups', 'anchor1', 'mark', 'sndp', 'en', 'yahoo', 'sap', 'anchor2', 'simon', 'first'] } }, @@ -311,6 +314,7 @@ runScenarios('jquery', 'normal', [ { select: '{', expect: { throws: true } }, { select: '<', expect: { throws: true } }, { select: '()', expect: { throws: true } }, + { select: '{}', expect: { throws: true } }, { select: '<>', expect: { throws: true } }, { select: ':nth-child(2n+-0)', expect: { throws: true } }, @@ -355,6 +359,7 @@ runScenarios('jquery', 'normal', [ { select: '#firstUL > *', expect: { ids: [] } }, { select: '#lengthtest', expect: { ids: ['lengthtest'] } }, + { select: '#tName2', expect: { ids: [] } }, { select: '#asdfasdf #foobar', expect: { ids: [] } }, // bug #986 { select: 'body div#form', expect: { ids: [] } }, @@ -404,10 +409,6 @@ runScenarios('jquery', 'normal', [ { select: 'a.blog', expect: { ids: ['mark', 'simon'] } }, { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - // Repeated as in the original test - { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, - { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, { select: 'p .blog', expect: { ids: ['mark', 'simon'] } }, { select: '.台北Táiběi', expect: { ids: ['utf8class1'] } }, @@ -483,6 +484,7 @@ runScenarios('jquery', 'normal', [ { select: '#form input[name=action]', expect: { ids: ['text1'] } }, { select: "#form input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: "#form[action='formaction']", expect: { ids: ['form'] } }, { select: '[name=tName1]', expect: { ids: ['tName1ID'] } }, { select: '[name=tName2]', expect: { ids: ['tName2ID'] } }, @@ -498,6 +500,11 @@ runScenarios('jquery', 'normal', [ { select: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, { select: 'h2 , p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, { select: 'h2,p', expect: { ids: ['banner', 'userAgent', 'firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + + { select: 'a.blog, p', expect: { ids: ['firstp', 'ap', 'mark', 'sndp', 'en', 'sap', 'simon', 'first'] } }, + { select: 'a.blog , p', expect: { ids: ['firstp', 'ap', 'mark', 'sndp', 'en', 'sap', 'simon', 'first'] } }, + { select: 'a.blog ,p', expect: { ids: ['firstp', 'ap', 'mark', 'sndp', 'en', 'sap', 'simon', 'first'] } }, + { select: 'a.blog,p', expect: { ids: ['firstp', 'ap', 'mark', 'sndp', 'en', 'sap', 'simon', 'first'] } }, ], }, @@ -600,6 +607,15 @@ runScenarios('jquery', 'normal', [ { select: '#form select:first-of-type option:nth-child(3n-3)', expect: { ids: ['option1c'] } }, { select: '#form select:first-of-type option:nth-child(3n+0)', expect: { ids: ['option1c'] } }, { select: '#form select:first-of-type option:nth-child(-n+3)', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, + + { select: '#form #select1 option:nth-child( 3n - 3)', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child(3n - 3 )', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child(\t 3n - 3)', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child(3n - 3 \t)', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child( \t 3n - 3)', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child(3n - 3 \t )', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child( \t\t\t 3n - 3)', expect: { ids: ['option1c'] } }, + { select: '#form #select1 option:nth-child(3n - 3 \t\t\t )', expect: { ids: ['option1c'] } }, ], }, @@ -635,10 +651,10 @@ runScenarios('jquery', 'normal', [ { select: 'form label[for]', expect: { ids: ['label-for'] } }, { select: '#form [for=action]', expect: { ids: ['label-for'] } }, - // Disabled tests - expandos don't work in all browsers - // { selector: 'form input[test]', expect: { ids: ['text1', 'text2'] } }, - // { selector: 'form input[test=0]', expect: { ids: ['text1'] } }, - // { selector: 'form input[test=1]', expect: { ids: ['text2'] } }, + // Disabled tests - expandos don't work in browsers + // { select: 'form input[test]', expect: { ids: ['text1', 'text2'] } }, + // { select: 'form input[test=0]', expect: { ids: ['text1'] } }, + // { select: 'form input[test=1]', expect: { ids: ['text2'] } }, { select: "input[name^='foo[']", expect: { ids: ['hidden2'] } }, { select: "input[name^='foo[bar]']", expect: { ids: ['hidden2'] } }, @@ -662,12 +678,26 @@ runScenarios('jquery', 'normal', [ { select: "#select1 option[value='']", expect: { ids: ['option1a'] } }, { select: "#select1 option:not([value=''])", expect: { ids: ['option1b', 'option1c', 'option1d'] } }, + { select: '#select1 option[selected]', expect: { ids: [] } }, + { select: '#select2 option[selected]', expect: { ids: ['option2d'] } }, + { select: '#select3 option[selected]', expect: { ids: ['option3b', 'option3c'] } }, + { select: '#select1 option:checked', expect: { ids: ['option1a'] } }, { select: '#select2 option:checked', expect: { ids: ['option2d'] } }, { select: '#select3 option:checked', expect: { ids: ['option3b', 'option3c'] } }, { select: "input[name='foo[bar]']", expect: { ids: ['hidden2'] } }, + { select: ':matches(#form select[multiple])', expect: { ids: ['select3'] }, status: 'fixme' }, + { select: ':matches(#form select[name=select1], #form select[name=select2])', expect: { ids: ['select1', 'select2'] }, status: 'fixme' }, + { select: ':matches(#form select[name=select1], #form select[name=select2])', expect: { ids: ['select1', 'select2'] }, status: 'fixme' }, + { select: ':matches(#form select[multiple], :-test-invalid)', expect: { throws: true }, status: 'fixme' }, + + { select: ':is(#form select[multiple])', expect: { ids: ['select3'] } }, + { select: ':is(#form select[name=select1], #form select[name=select2])', expect: { ids: ['select1', 'select2'] } }, + { select: ':is(#form select[name=select1], #form select[name=select2])', expect: { ids: ['select1', 'select2'] } }, + { select: ':is(#form select[multiple], :-test-invalid)', expect: { ids: ['select3'] }, status: 'fixme' }, + { select: '#form select:not([multiple])', expect: { ids: ['select1', 'select2'] } }, { select: '#form select:not([name=select1])', expect: { ids: ['select2', 'select3'] } }, { select: "#form select:not([name='select1'])", expect: { ids: ['select2', 'select3'] } }, @@ -691,14 +721,14 @@ runScenarios('jquery', 'normal', [ { select: '#form option:checked', expect: { ids: ['option1a', 'option2d', 'option3b', 'option3c'] } }, // jQuery-only text pseudos; not valid in the native-parity harness - // { selector: "a:contains('Google')", expect: { ids: ['google', 'groups'] } }, - // { selector: "a:contains('Google Groups')", expect: { ids: ['groups'] } }, - // { selector: "a:contains('Google Groups (Link)')", expect: { ids: ['groups'] } }, - // { selector: "a:contains('(Link)')", expect: { ids: ['groups'] } }, + // { select: "a:contains('Google')", expect: { ids: ['google', 'groups'] } }, + // { select: "a:contains('Google Groups')", expect: { ids: ['groups'] } }, + // { select: "a:contains('Google Groups (Link)')", expect: { ids: ['groups'] } }, + // { select: "a:contains('(Link)')", expect: { ids: ['groups'] } }, { select: 'p ~ div', expect: { ids: ['foo', 'moretests', 'tabindex-tests', 'liveHandlerOrder'] } }, { select: 'a.blog:not(.link)', expect: { ids: ['mark'] } }, - // { selector: "#form option:not(:contains('Nothing'),#option1b,:checked)", expect: { ids: ['option1c', 'option1d', 'option2b', 'option2c', 'option3d', 'option3e'] } }, + // { select: "#form option:not(:contains('Nothing'),#option1b,:checked)", expect: { ids: ['option1c', 'option1d', 'option2b', 'option2c', 'option3d', 'option3e'] } }, { select: "#form option:not([id^='opt']:nth-child(-n+3))", expect: { ids: ['option1d', 'option2d', 'option3d', 'option3e'] } }, { select: "#form option:not(:not(:checked))[id^='option3']", expect: { ids: ['option3b', 'option3c'] } }, { select: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, @@ -709,8 +739,11 @@ runScenarios('jquery', 'normal', [ { select: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, { select: 'p:not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, { select: 'p:not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(div):not(.foo)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, // invalid compound selector inside :not() pseudo-class + { select: 'p:not(div):not(#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + { select: 'p:not(p):not(#blargh)', expect: { ids: [] } }, { select: 'p:not(div#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, { select: 'p:not(p#blargh)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, { select: 'p:not(div)', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, @@ -723,23 +756,23 @@ runScenarios('jquery', 'normal', [ { select: 'p:not(a,p)', expect: { ids: [] } }, { select: 'p:not(p,a)', expect: { ids: [] } }, { select: 'p:not(a,p,b)', expect: { ids: [] } }, - // { selector: ':input:not(:image,:input,:submit)', expect: { ids: [] } }, + // { select: ':input:not(:image,:input,:submit)', expect: { ids: [] } }, // jQuery-only positional pseudos; keep parked for now - // { selector: 'p:nth(1)', expect: { ids: ['ap'] } }, - // { selector: 'p:first', expect: { ids: ['firstp'] } }, - // { selector: 'p:last', expect: { ids: ['first'] } }, - // { selector: 'p:even', expect: { ids: ['firstp', 'sndp', 'sap'] } }, - // { selector: 'p:odd', expect: { ids: ['ap', 'en', 'first'] } }, - // { selector: 'p:eq(1)', expect: { ids: ['ap'] } }, - // { selector: 'p:gt(0)', expect: { ids: ['ap', 'sndp', 'en', 'sap', 'first'] } }, - // { selector: 'p:lt(3)', expect: { ids: ['firstp', 'ap', 'sndp'] } }, - // { selector: 'p:parent', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, - // { selector: '#form input:visible', expect: { ids: [] } }, - // { selector: 'div:visible:not(.testrunner-toolbar):lt(2)', expect: { ids: ['nothiddendiv', 'nothiddendivchild'] } }, - // { selector: '#form input:hidden', expect: { ids: ['text1', 'text2', 'radio1', 'radio2', 'check1', 'check2', 'hidden1', 'hidden2', 'name', 'search'] } }, - // { selector: '#main:hidden', expect: { ids: ['main'] } }, - // { selector: '#dl:hidden', expect: { ids: ['dl'] } }, + // { select: 'p:nth(1)', expect: { ids: ['ap'] } }, + // { select: 'p:first', expect: { ids: ['firstp'] } }, + // { select: 'p:last', expect: { ids: ['first'] } }, + // { select: 'p:even', expect: { ids: ['firstp', 'sndp', 'sap'] } }, + // { select: 'p:odd', expect: { ids: ['ap', 'en', 'first'] } }, + // { select: 'p:eq(1)', expect: { ids: ['ap'] } }, + // { select: 'p:gt(0)', expect: { ids: ['ap', 'sndp', 'en', 'sap', 'first'] } }, + // { select: 'p:lt(3)', expect: { ids: ['firstp', 'ap', 'sndp'] } }, + // { select: 'p:parent', expect: { ids: ['firstp', 'ap', 'sndp', 'en', 'sap', 'first'] } }, + // { select: '#form input:visible', expect: { ids: [] } }, + // { select: 'div:visible:not(#testrunner-toolbar):lt(2)', expect: { ids: ['nothiddendiv', 'nothiddendivchild'] } }, + // { select: '#form input:hidden', expect: { ids: ['text1', 'text2', 'radio1', 'radio2', 'check1', 'check2', 'hidden1', 'hidden2', 'name', 'search'] } }, + // { select: '#main:hidden', expect: { ids: ['main'] } }, + // { select: '#dl:hidden', expect: { ids: ['dl'] } }, ], }, @@ -748,60 +781,64 @@ runScenarios('jquery', 'normal', [ { name: 'jquery visibility and position pseudos (legacy)', status: 'skip', - html: html, + html, htmlMode: 'document', - setupPage: async (page) => { - await page.evaluate(() => { - const div = NW.Dom.select('#nothiddendivchild')[0] as HTMLElement | undefined; - if (!div) throw new Error('#nothiddendivchild not found'); - - div.style.fontSize = '0'; - div.style.lineHeight = '0'; - div.style.width = '0'; - div.style.height = '0'; - expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); - expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual([]); - - div.style.width = '1px'; - div.style.height = '0'; - expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); - expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual([]); - - div.style.width = '0'; - div.style.height = '1px'; - expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); - expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual([]); - - div.style.width = '1px'; - div.style.height = '1px'; - expect(NW.Dom.select('#nothiddendivchild:visible').map(el => el.getAttribute('id'))).toEqual(['nothiddendivchild']); - expect(NW.Dom.select('#nothiddendivchild:hidden').map(el => el.getAttribute('id'))).toEqual([]); - - div.style.width = ''; - div.style.height = ''; - div.style.fontSize = ''; - div.style.lineHeight = ''; - }); - }, - cases: [ - { select: 'div#nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, - { select: 'div#nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, - { select: 'div#nothiddendiv:not(:gt(0))', expect: { ids: ['nothiddendiv'] } }, - { select: '#foo > :not(:first)', expect: { ids: ['en', 'sap'] } }, - { select: 'select > :not(:gt(2))', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, - { select: 'select:lt(2) :not(:first)', expect: { ids: ['option1b', 'option1c', 'option1d', 'option2a', 'option2b', 'option2c', 'option2d'] } }, - { select: 'div.nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, - { select: 'div.nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, - { select: 'div.nothiddendiv:not(:lt(0))', expect: { ids: ['nothiddendiv'] } }, - - { select: 'div div:eq(0)', expect: { ids: ['nothiddendivchild'] } }, - { select: 'div div:eq(5)', expect: { ids: ['t2037'] } }, - { select: 'div div:eq(27)', expect: { ids: ['hide'] } }, - { select: 'div div:first', expect: { ids: ['nothiddendivchild'] } }, - { select: 'div > div:first', expect: { ids: ['nothiddendivchild'] } }, - { select: '#dl div:first div:first', expect: { ids: ['foo'] } }, - { select: '#dl div:first > div:first', expect: { ids: ['foo'] } }, - { select: 'div#nothiddendiv:first > div:first', expect: { ids: ['nothiddendivchild'] } }, + steps: [ + { + // 0x0 with zero font metrics + setupPage: async page => { await setElementProps(page, '#nothiddendivchild', { fontSize: '0', lineHeight: '0', width: '0', height: '0' }); }, + cases: [ + { select: '#nothiddendivchild:hidden', expect: { ids: ['nothiddendivchild'] } }, + { select: '#nothiddendivchild:visible', expect: { ids: [] } }, + ], + }, + { + // 1x0 with zero font metrics + setupPage: async page => { await setElementProps(page, '#nothiddendivchild', { fontSize: '0', lineHeight: '0', width: '1px', height: '0' }); }, + cases: [ + { select: '#nothiddendivchild:visible', expect: { ids: ['nothiddendivchild'] } }, + { select: '#nothiddendivchild:hidden', expect: { ids: [] } }, + ], + }, + { + // 0x1 with zero font metrics + setupPage: async page => { await setElementProps(page, '#nothiddendivchild', { fontSize: '0', lineHeight: '0', width: '0', height: '1px' }); }, + cases: [ + { select: '#nothiddendivchild:visible', expect: { ids: ['nothiddendivchild'] } }, + { select: '#nothiddendivchild:hidden', expect: { ids: [] } }, + ], + }, + { + // 1x1 with zero font metrics + setupPage: async page => { await setElementProps(page, '#nothiddendivchild', { fontSize: '0', lineHeight: '0', width: '1px', height: '1px' }); }, + cases: [ + { select: '#nothiddendivchild:visible', expect: { ids: ['nothiddendivchild'] } }, + { select: '#nothiddendivchild:hidden', expect: { ids: [] } }, + ], + }, + { + setupPage: async page => { await setElementProps(page, '#nothiddendivchild', { width: '', height: '', fontSize: '', lineHeight: '' }); }, + cases: [ + { select: 'div#nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, + { select: 'div#nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, + { select: 'div#nothiddendiv:not(:gt(0))', expect: { ids: ['nothiddendiv'] } }, + { select: '#foo > :not(:first)', expect: { ids: ['en', 'sap'] } }, + { select: 'select > :not(:gt(2))', expect: { ids: ['option1a', 'option1b', 'option1c'] } }, + { select: 'select:lt(2) :not(:first)', expect: { ids: ['option1b', 'option1c', 'option1d', 'option2a', 'option2b', 'option2c', 'option2d'] } }, + { select: 'div.nothiddendiv:eq(0)', expect: { ids: ['nothiddendiv'] } }, + { select: 'div.nothiddendiv:last', expect: { ids: ['nothiddendiv'] } }, + { select: 'div.nothiddendiv:not(:lt(0))', expect: { ids: ['nothiddendiv'] } }, + + { select: 'div div:eq(0)', expect: { ids: ['nothiddendivchild'] } }, + { select: 'div div:eq(5)', expect: { ids: ['t2037'] } }, + { select: 'div div:eq(27)', expect: { ids: ['hide'] } }, + { select: 'div div:first', expect: { ids: ['nothiddendivchild'] } }, + { select: 'div > div:first', expect: { ids: ['nothiddendivchild'] } }, + { select: '#dl div:first div:first', expect: { ids: ['foo'] } }, + { select: '#dl div:first > div:first', expect: { ids: ['foo'] } }, + { select: 'div#nothiddendiv:first > div:first', expect: { ids: ['nothiddendivchild'] } }, + ], + } ], }, @@ -832,4 +869,56 @@ runScenarios('jquery', 'normal', [ ], }, + { + name: 'escaped attribute selector values', + html: ` +
      +
      hi there
      + +
      foo
      +
      foo
      +
      foo
      +
      foo
      +
      foo
      +
      foo
      +
      foo
      +
      foo
      +
      + `, + cases: [ + { select: `[data-test="foo]bar"]`, expect: { ids: ['escapedSelectorClosingBracket'] } }, + { select: `[data-test="foo\\"bar"]`, expect: { ids: ['escapedSelectorDoubleQuote'] } }, + { select: `[data-test='foo\\'bar']`, expect: { ids: ['escapedSelectorSingleQuote'] } }, + { select: `[data-test='foo\\\\bar']`, expect: { ids: ['escapedSelectorBackslash'] } }, + { select: `[data-test='foo\\a bar']`, expect: { ids: ['escapedSelectorLF'] } }, + { select: `[data-test='foo\\00000d bar']`, expect: { ids: ['escapedSelectorCR'] } }, + { select: `[data-test='foo\\24B62 bar']`, expect: { ids: ['escapedSupplementary'] } }, + { select: `[data-test='foo\\\\\\"\\'\\62 ar']`, expect: { ids: ['escapedCombined'] } }, + + { select: `[data-test=foo\\]bar]`, expect: { ids: ['escapedSelectorClosingBracket'] } }, + { select: `[data-test=foo\\"bar]`, expect: { ids: ['escapedSelectorDoubleQuote'] } }, + { select: `[data-test=foo\\'bar]`, expect: { ids: ['escapedSelectorSingleQuote'] } }, + { select: `[data-test=foo\\\\bar]`, expect: { ids: ['escapedSelectorBackslash'] } }, + { select: `[data-test=foo\\a bar]`, expect: { ids: ['escapedSelectorLF'] } }, + { select: `[data-test=foo\\00000d bar]`, expect: { ids: ['escapedSelectorCR'] } }, + { select: `[data-test=foo\\24B62 bar]`, expect: { ids: ['escapedSupplementary'] } }, + { select: `[data-test=foo\\\\\\"\\'\\62 ar]`, expect: { ids: ['escapedCombined'] } }, + ], + } + ]); + +const setElementProps = async ( + page: Page, + selector: string, + props: Partial> +) => { + await page.evaluate(({ selector, props }) => { + const el = NW.Dom.first(selector) as HTMLElement | null; + if (!el) throw new Error(`${selector} not found`); + if (props.width !== undefined) el.style.width = props.width; + if (props.height !== undefined) el.style.height = props.height; + if (props.fontSize !== undefined) el.style.fontSize = props.fontSize; + if (props.lineHeight !== undefined) el.style.lineHeight = props.lineHeight; + }, { selector, props }); +}; \ No newline at end of file From ac215fa3f1ef09a5a4d849d52d12ad77c72777a6 Mon Sep 17 00:00:00 2001 From: koal44 Date: Tue, 14 Apr 2026 22:15:08 -0700 Subject: [PATCH 16/19] add test readme --- test_new/README.md | 75 +++++++++++++++++++++++++++++++++ test_new/browser/issues.test.ts | 3 +- test_new/browser/scope.test.ts | 5 +-- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 test_new/README.md diff --git a/test_new/README.md b/test_new/README.md new file mode 100644 index 0000000..92e750f --- /dev/null +++ b/test_new/README.md @@ -0,0 +1,75 @@ +# Browser harness (Playwright) + +Browser-based tests for `nwsapi`, migrated from older selector suites and run under Playwright. + +The harness compares `NW.Dom` against native DOM behavior in Chromium, Firefox, and WebKit. Tests run inside the page via `page.evaluate()`, which makes it possible to measure native browser results directly and compare them against `nwsapi` under the same fixture and context. + +## Install + +Requires Node.js (which includes npm). + +```sh +npm install +npx playwright install +``` + +`npm install` pulls in the dev dependencies needed to run the scripts. `npx playwright install` installs the browser binaries used by the harness. + +## Run + +Run the full browser harness: + +```sh +npm run test:browser +``` + +Run only cases marked `fixme`: + +```sh +npm run test:browser:fixme +``` + +Run a subset by label, for example: + +```sh +npm run test:browser jquery +npm run test:browser:fixme w3c +``` + +For faster iteration, run a subset by label or mark a scenario/case `only`. + +`fixme` cases are open questions or known mismatches found during migration and need review. + +## Tests + +Tests are written as scenario collections and run through the shared harness. + +A typical test file looks like this: + +```ts +import { runScenarios } from './harness/scenarios'; + +runScenarios('jquery', 'normal', [ + { + name: 'id selectors', + html: `
      `, + cases: [ + { select: '#a', expect: { ids: ['a'] } }, + { select: '#b', expect: { ids: ['b'] } }, + ], + }, +]); +``` + +## Scenarios + +Scenarios can: + +- choose engines (`nw`, `native`) and browsers +- provide inline HTML fixtures +- use `setupPage` to run page-side JavaScript before assertions +- use steps to change page state between groups of cases +- express cases using `select`, `first`, `match`, `closest`, `byId`, `byTag`, and `byClass` +- attach expectations such as `count`, `ids`, `classes`, `throws`, and inclusion/exclusion checks +- use context refs when a query should run relative to a specific node, including rehomed contexts for detached nodes and fragments +- mark scenarios or cases as `skip`, `only`, `fail`, or `fixme` diff --git a/test_new/browser/issues.test.ts b/test_new/browser/issues.test.ts index 2289a55..8a9ed08 100644 --- a/test_new/browser/issues.test.ts +++ b/test_new/browser/issues.test.ts @@ -3,7 +3,6 @@ import { runScenarios } from "./harness/scenarios"; runScenarios('issues', 'normal', [ { name: 'issue 160 adjacent-descendant regression', - status: 'fixme', html: `
      @@ -16,7 +15,7 @@ runScenarios('issues', 'normal', [ `, cases: [ { select: '.neighbor + div .target', expect: { count: 1 } }, - { select: '.neighbor + * .target', expect: { count: 1 } }, + { select: '.neighbor + * .target', expect: { count: 1 }, status: 'fixme' }, ], }, ]); \ No newline at end of file diff --git a/test_new/browser/scope.test.ts b/test_new/browser/scope.test.ts index 67cf023..9fe7ed3 100644 --- a/test_new/browser/scope.test.ts +++ b/test_new/browser/scope.test.ts @@ -82,7 +82,6 @@ runScenarios('scope', 'normal', [ { name: 'scope-04a', - status: 'fixme', htmlMode: 'document', html: ` @@ -99,13 +98,12 @@ runScenarios('scope', 'normal', [ `, cases: [ - { select: ':scope > [data-test="foo"]', ref: { by: 'first', selector: 'body' }, expect: { count: 1 } }, + { select: ':scope > [data-test="foo"]', ref: { by: 'first', selector: 'body' }, expect: { count: 1 }, status: 'fixme' }, ], }, { name: 'scope-04b', - status: 'fixme', html: `
      @@ -121,6 +119,7 @@ runScenarios('scope', 'normal', [ expect: { classes: ['outer', 'other-outer'] }, + status: 'fixme', }, ], }, From b7d9ece591c0c7abfad0199cb999c6386cc7281a Mon Sep 17 00:00:00 2001 From: koal44 Date: Thu, 16 Apr 2026 15:38:38 -0700 Subject: [PATCH 17/19] add xml-document mode to browser harness --- test_new/browser/css3-compat.test.ts | 56 +++---- test_new/browser/css3-escape.test.ts | 24 +-- test_new/browser/harness.test.ts | 33 ++++- test_new/browser/harness/browser.ts | 12 +- test_new/browser/harness/scenarios.ts | 91 +++++++++--- test_new/browser/html5.test.ts | 4 +- test_new/browser/issues.test.ts | 2 +- test_new/browser/jquery.test.ts | 64 ++++---- test_new/browser/jsvm.test.ts | 2 +- test_new/browser/prototype.test.ts | 14 +- test_new/browser/quirks.test.ts | 8 +- test_new/browser/scope.test.ts | 16 +- test_new/browser/scotch.test.ts | 42 +++--- test_new/browser/slick.test.ts | 0 test_new/browser/speed.test.ts | 40 ++--- test_new/browser/w3c-selector.test.ts | 2 +- test_new/browser/w3c.test.ts | 122 +++++++-------- test_new/browser/xml.test.ts | 204 ++++++++++---------------- test_new/global.d.ts | 2 + 19 files changed, 378 insertions(+), 360 deletions(-) create mode 100644 test_new/browser/slick.test.ts diff --git a/test_new/browser/css3-compat.test.ts b/test_new/browser/css3-compat.test.ts index 292fa5a..2a5452a 100644 --- a/test_new/browser/css3-compat.test.ts +++ b/test_new/browser/css3-compat.test.ts @@ -3,7 +3,7 @@ import { runScenarios } from "./harness/scenarios"; runScenarios('css3 compat', 'normal', [ { name: 'test 0 basic selectors', - html: ` + markup: `

      CSS 3 Selectors tests

      Original CSS work by Daniel Glazman (c) Disruptive Innovations 2008

      @@ -49,8 +49,8 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 1 childhood selector', - htmlMode: 'document', - html: ` + markupMode: 'html-document', + markup: ` @@ -68,7 +68,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 2 attribute existence selector', - html: ` + markup: `
      @@ -90,7 +90,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 3 attribute value selector', - html: ` + markup: `
      @@ -108,7 +108,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 4 space-separated attribute selector', - html: ` + markup: `
      @@ -127,7 +127,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 5 attribute starts-with selector', - html: ` + markup: `
      @@ -147,7 +147,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 6 attribute ends-with selector', - html: ` + markup: `
      @@ -167,7 +167,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'test 7 attribute contains selector', - html: ` + markup: `
      @@ -187,7 +187,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'first-child selector', - html: ` + markup: `
      @@ -204,7 +204,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'root selector', - html: '', + markup: '', cases: [ /* :root tests */ { select: 'html', expect: { equivalentCase: { select: ':root' } } }, @@ -212,7 +212,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':nth-child(n) and :nth-of-type tests', - html: ` + markup: `
      @@ -297,7 +297,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: 'not pseudo-class selector', - html: ` + markup: `
      @@ -316,7 +316,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':only-of-type tests', - html: ` + markup: `

      @@ -333,7 +333,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':last-child tests', - html: ` + markup: `

        @@ -348,7 +348,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':first-of-type tests', - html: ` + markup: `

      @@ -367,7 +367,7 @@ runScenarios('css3 compat', 'normal', [ { name: ':last-of-type tests', - html: ` + markup: `

      @@ -384,7 +384,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':only-child tests', - html: ` + markup: `
      @@ -400,7 +400,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':only-of-type tests 2', - html: ` + markup: `

      @@ -418,7 +418,7 @@ runScenarios('css3 compat', 'normal', [ { name: ':empty tests', - html: ` + markup: `
      @@ -438,7 +438,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: ':lang() tests', - html: ` + markup: `
      @@ -462,7 +462,7 @@ runScenarios('css3 compat', 'normal', [ }, { name: '[|=] tests', - html: ` + markup: `
      @@ -489,7 +489,7 @@ runScenarios('css3 compat', 'normal', [ { name: 'UI tests', - html: ` + markup: `