@jesscss/core 2.0.0-alpha.4 → 2.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +20159 -0
- package/lib/index.d.cts +5993 -0
- package/lib/index.d.cts.map +1 -0
- package/lib/index.d.ts +5992 -21
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +19926 -22
- package/lib/index.js.map +1 -1
- package/package.json +15 -14
- package/src/__tests__/define-function-record.test.ts +58 -0
- package/src/__tests__/define-function-simple.test.ts +55 -0
- package/src/__tests__/define-function-split-sequence.test.ts +547 -0
- package/src/__tests__/define-function-type-parity.test.ts +9 -0
- package/src/__tests__/define-function.test.ts +763 -0
- package/src/__tests__/num-operations.test.ts +91 -0
- package/src/__tests__/safe-parse.test.ts +374 -0
- package/src/context.ts +896 -0
- package/src/conversions.ts +282 -0
- package/src/debug-log.ts +29 -0
- package/src/define-function.ts +1006 -0
- package/src/deprecation.ts +67 -0
- package/src/globals.d.ts +26 -0
- package/src/index.ts +31 -0
- package/src/jess-error.ts +773 -0
- package/src/logger/deprecation-processing.ts +109 -0
- package/src/logger.ts +31 -0
- package/src/plugin.ts +292 -0
- package/src/tree/LOOKUP_CHAINS.md +35 -0
- package/src/tree/README.md +18 -0
- package/src/tree/__tests__/__snapshots__/extend-eval-integration.test.ts.snap +1455 -0
- package/src/tree/__tests__/ampersand.test.ts +382 -0
- package/src/tree/__tests__/at-rule.test.ts +2047 -0
- package/src/tree/__tests__/basic-render.test.ts +212 -0
- package/src/tree/__tests__/block.test.ts +40 -0
- package/src/tree/__tests__/call.test.ts +346 -0
- package/src/tree/__tests__/color.test.ts +537 -0
- package/src/tree/__tests__/condition.test.ts +186 -0
- package/src/tree/__tests__/control.test.ts +564 -0
- package/src/tree/__tests__/declaration.test.ts +253 -0
- package/src/tree/__tests__/dependency-graph.test.ts +177 -0
- package/src/tree/__tests__/detached-rulesets.test.ts +213 -0
- package/src/tree/__tests__/dimension.test.ts +236 -0
- package/src/tree/__tests__/expression.test.ts +73 -0
- package/src/tree/__tests__/ext-node.test.ts +31 -0
- package/src/tree/__tests__/extend-eval-integration.test.ts +1033 -0
- package/src/tree/__tests__/extend-import-style.test.ts +929 -0
- package/src/tree/__tests__/extend-less-fixtures.test.ts +851 -0
- package/src/tree/__tests__/extend-list.test.ts +31 -0
- package/src/tree/__tests__/extend-roots.test.ts +1045 -0
- package/src/tree/__tests__/extend-rules.test.ts +740 -0
- package/src/tree/__tests__/func.test.ts +171 -0
- package/src/tree/__tests__/import-js.test.ts +33 -0
- package/src/tree/__tests__/import-style-test-helpers.ts +56 -0
- package/src/tree/__tests__/import-style.test.ts +1967 -0
- package/src/tree/__tests__/interpolated-reference.test.ts +44 -0
- package/src/tree/__tests__/interpolated.test.ts +41 -0
- package/src/tree/__tests__/list.test.ts +177 -0
- package/src/tree/__tests__/log.test.ts +83 -0
- package/src/tree/__tests__/mixin-recursion.test.ts +639 -0
- package/src/tree/__tests__/mixin.test.ts +2171 -0
- package/src/tree/__tests__/negative.test.ts +45 -0
- package/src/tree/__tests__/nesting-collapse.test.ts +519 -0
- package/src/tree/__tests__/node-flags-perf.test.ts +195 -0
- package/src/tree/__tests__/node-flags.test.ts +410 -0
- package/src/tree/__tests__/node-graph.test.ts +598 -0
- package/src/tree/__tests__/node-mutation.test.ts +182 -0
- package/src/tree/__tests__/operation.test.ts +18 -0
- package/src/tree/__tests__/paren.test.ts +90 -0
- package/src/tree/__tests__/preserve-mode-output.test.ts +50 -0
- package/src/tree/__tests__/quoted.test.ts +72 -0
- package/src/tree/__tests__/range.test.ts +59 -0
- package/src/tree/__tests__/reference.test.ts +743 -0
- package/src/tree/__tests__/rest.test.ts +29 -0
- package/src/tree/__tests__/rules-raw.test.ts +14 -0
- package/src/tree/__tests__/rules.test.ts +1271 -0
- package/src/tree/__tests__/ruleset.test.ts +597 -0
- package/src/tree/__tests__/selector-attr.test.ts +50 -0
- package/src/tree/__tests__/selector-basic.test.ts +44 -0
- package/src/tree/__tests__/selector-capture.test.ts +22 -0
- package/src/tree/__tests__/selector-complex.test.ts +120 -0
- package/src/tree/__tests__/selector-compound.test.ts +74 -0
- package/src/tree/__tests__/selector-interpolated.test.ts +50 -0
- package/src/tree/__tests__/selector-list.test.ts +59 -0
- package/src/tree/__tests__/selector-pseudo.test.ts +23 -0
- package/src/tree/__tests__/selector.test.ts +182 -0
- package/src/tree/__tests__/sequence.test.ts +226 -0
- package/src/tree/__tests__/serialize-types.test.ts +529 -0
- package/src/tree/__tests__/spaced.test.ts +8 -0
- package/src/tree/__tests__/url.test.ts +72 -0
- package/src/tree/__tests__/var-declaration.test.ts +90 -0
- package/src/tree/ampersand.ts +538 -0
- package/src/tree/any.ts +169 -0
- package/src/tree/at-rule.ts +760 -0
- package/src/tree/block.ts +72 -0
- package/src/tree/bool.ts +46 -0
- package/src/tree/call.ts +593 -0
- package/src/tree/collection.ts +52 -0
- package/src/tree/color.ts +629 -0
- package/src/tree/combinator.ts +30 -0
- package/src/tree/comment.ts +36 -0
- package/src/tree/condition.ts +194 -0
- package/src/tree/control.ts +452 -0
- package/src/tree/declaration-custom.ts +56 -0
- package/src/tree/declaration-var.ts +87 -0
- package/src/tree/declaration.ts +742 -0
- package/src/tree/default-guard.ts +35 -0
- package/src/tree/dimension.ts +392 -0
- package/src/tree/expression.ts +97 -0
- package/src/tree/extend-list.ts +51 -0
- package/src/tree/extend.ts +391 -0
- package/src/tree/function.ts +254 -0
- package/src/tree/import-js.ts +130 -0
- package/src/tree/import-style.ts +875 -0
- package/{lib/tree/index.js → src/tree/index.ts} +49 -22
- package/src/tree/interpolated.ts +346 -0
- package/src/tree/js-array.ts +21 -0
- package/src/tree/js-expr.ts +50 -0
- package/src/tree/js-function.ts +31 -0
- package/src/tree/js-object.ts +22 -0
- package/src/tree/list.ts +415 -0
- package/src/tree/log.ts +89 -0
- package/src/tree/mixin.ts +331 -0
- package/src/tree/negative.ts +58 -0
- package/src/tree/nil.ts +57 -0
- package/src/tree/node-base.ts +1716 -0
- package/src/tree/node-type.ts +122 -0
- package/src/tree/node.ts +118 -0
- package/src/tree/number.ts +54 -0
- package/src/tree/operation.ts +187 -0
- package/src/tree/paren.ts +132 -0
- package/src/tree/query-condition.ts +47 -0
- package/src/tree/quoted.ts +119 -0
- package/src/tree/range.ts +101 -0
- package/src/tree/reference.ts +1099 -0
- package/src/tree/rest.ts +55 -0
- package/src/tree/rules-raw.ts +52 -0
- package/src/tree/rules.ts +2896 -0
- package/src/tree/ruleset.ts +1217 -0
- package/src/tree/selector-attr.ts +172 -0
- package/src/tree/selector-basic.ts +75 -0
- package/src/tree/selector-capture.ts +85 -0
- package/src/tree/selector-complex.ts +189 -0
- package/src/tree/selector-compound.ts +205 -0
- package/src/tree/selector-interpolated.ts +95 -0
- package/src/tree/selector-list.ts +245 -0
- package/src/tree/selector-pseudo.ts +173 -0
- package/src/tree/selector-simple.ts +10 -0
- package/src/tree/selector.ts +152 -0
- package/src/tree/sequence.ts +463 -0
- package/src/tree/tree.ts +130 -0
- package/src/tree/url.ts +95 -0
- package/src/tree/util/EXTEND_ARCHITECTURE_ANALYSIS.md +215 -0
- package/src/tree/util/EXTEND_AUDIT.md +233 -0
- package/src/tree/util/EXTEND_BASELINE.md +64 -0
- package/src/tree/util/EXTEND_CALL_GRAPH_ANALYSIS.md +244 -0
- package/src/tree/util/EXTEND_DOCS.md +24 -0
- package/src/tree/util/EXTEND_FINAL_SUMMARY.md +95 -0
- package/src/tree/util/EXTEND_FUNCTION_AUDIT.md +1433 -0
- package/src/tree/util/EXTEND_OPTIMIZATION_PLAN.md +114 -0
- package/src/tree/util/EXTEND_REFACTORING_SUMMARY.md +152 -0
- package/src/tree/util/EXTEND_RULES.md +74 -0
- package/src/tree/util/EXTEND_UNUSED_FUNCTIONS.md +127 -0
- package/src/tree/util/EXTEND_UNUSED_FUNCTIONS_ANALYSIS.md +227 -0
- package/src/tree/util/NODE_COPY_REDUCTION_PLAN.md +12 -0
- package/src/tree/util/__tests__/EXTEND_TEST_INDEX.md +59 -0
- package/src/tree/util/__tests__/OPTIMIZATION-ANALYSIS.md +130 -0
- package/src/tree/util/__tests__/WALK_AND_CONSUME_DESIGN.md +138 -0
- package/src/tree/util/__tests__/_archive/2026-02-09__OPTIMIZATION-ANALYSIS.md +9 -0
- package/src/tree/util/__tests__/_archive/README.md +4 -0
- package/src/tree/util/__tests__/bitset.test.ts +142 -0
- package/src/tree/util/__tests__/debug-log.ts +50 -0
- package/src/tree/util/__tests__/extend-comment-handling.test.ts +187 -0
- package/src/tree/util/__tests__/extend-core-unit.test.ts +941 -0
- package/src/tree/util/__tests__/extend-pipeline-bench.test.ts +154 -0
- package/src/tree/util/__tests__/extend-pipeline-bench.ts +190 -0
- package/src/tree/util/__tests__/fast-reject.test.ts +377 -0
- package/src/tree/util/__tests__/is-node.test.ts +63 -0
- package/src/tree/util/__tests__/list-like.test.ts +63 -0
- package/src/tree/util/__tests__/outputwriter.test.ts +523 -0
- package/src/tree/util/__tests__/print.test.ts +183 -0
- package/src/tree/util/__tests__/process-extends.test.ts +226 -0
- package/src/tree/util/__tests__/process-leading-is.test.ts +205 -0
- package/src/tree/util/__tests__/recursion-helper.test.ts +184 -0
- package/src/tree/util/__tests__/selector-match-unit.test.ts +1427 -0
- package/src/tree/util/__tests__/sourcemap.test.ts +117 -0
- package/src/tree/util/ampersand-template.ts +9 -0
- package/src/tree/util/bitset.ts +194 -0
- package/src/tree/util/calculate.ts +11 -0
- package/src/tree/util/cast.ts +89 -0
- package/src/tree/util/cloning.ts +8 -0
- package/src/tree/util/collections.ts +299 -0
- package/src/tree/util/compare.ts +90 -0
- package/src/tree/util/cursor.ts +171 -0
- package/src/tree/util/extend-core.ts +2139 -0
- package/src/tree/util/extend-roots.ts +1108 -0
- package/src/tree/util/field-helpers.ts +354 -0
- package/src/tree/util/is-node.ts +43 -0
- package/src/tree/util/list-like.ts +93 -0
- package/src/tree/util/mixin-instance-primitives.ts +2020 -0
- package/src/tree/util/print.ts +303 -0
- package/src/tree/util/process-leading-is.ts +421 -0
- package/src/tree/util/recursion-helper.ts +54 -0
- package/src/tree/util/regex.ts +2 -0
- package/src/tree/util/registry-utils.ts +1953 -0
- package/src/tree/util/ruleset-trace.ts +17 -0
- package/src/tree/util/scoped-body-eval.ts +320 -0
- package/src/tree/util/selector-match-core.ts +2005 -0
- package/src/tree/util/selector-utils.ts +757 -0
- package/src/tree/util/serialize-helper.ts +535 -0
- package/src/tree/util/serialize-types.ts +318 -0
- package/src/tree/util/should-operate.ts +78 -0
- package/src/tree/util/sourcemap.ts +37 -0
- package/src/types/config.ts +247 -0
- package/src/types/index.ts +12 -0
- package/{lib/types/modes.d.ts → src/types/modes.ts} +2 -1
- package/src/types.d.ts +9 -0
- package/src/types.ts +68 -0
- package/src/use-webpack-resolver.ts +56 -0
- package/src/visitor/__tests__/visitor.test.ts +136 -0
- package/src/visitor/index.ts +263 -0
- package/{lib/visitor/less-visitor.js → src/visitor/less-visitor.ts} +3 -2
- package/lib/context.d.ts +0 -352
- package/lib/context.d.ts.map +0 -1
- package/lib/context.js +0 -636
- package/lib/context.js.map +0 -1
- package/lib/conversions.d.ts +0 -73
- package/lib/conversions.d.ts.map +0 -1
- package/lib/conversions.js +0 -253
- package/lib/conversions.js.map +0 -1
- package/lib/debug-log.d.ts +0 -2
- package/lib/debug-log.d.ts.map +0 -1
- package/lib/debug-log.js +0 -27
- package/lib/debug-log.js.map +0 -1
- package/lib/define-function.d.ts +0 -587
- package/lib/define-function.d.ts.map +0 -1
- package/lib/define-function.js +0 -726
- package/lib/define-function.js.map +0 -1
- package/lib/deprecation.d.ts +0 -34
- package/lib/deprecation.d.ts.map +0 -1
- package/lib/deprecation.js +0 -57
- package/lib/deprecation.js.map +0 -1
- package/lib/jess-error.d.ts +0 -343
- package/lib/jess-error.d.ts.map +0 -1
- package/lib/jess-error.js +0 -508
- package/lib/jess-error.js.map +0 -1
- package/lib/logger/deprecation-processing.d.ts +0 -41
- package/lib/logger/deprecation-processing.d.ts.map +0 -1
- package/lib/logger/deprecation-processing.js +0 -81
- package/lib/logger/deprecation-processing.js.map +0 -1
- package/lib/logger.d.ts +0 -10
- package/lib/logger.d.ts.map +0 -1
- package/lib/logger.js +0 -20
- package/lib/logger.js.map +0 -1
- package/lib/plugin.d.ts +0 -94
- package/lib/plugin.d.ts.map +0 -1
- package/lib/plugin.js +0 -174
- package/lib/plugin.js.map +0 -1
- package/lib/tree/ampersand.d.ts +0 -94
- package/lib/tree/ampersand.d.ts.map +0 -1
- package/lib/tree/ampersand.js +0 -269
- package/lib/tree/ampersand.js.map +0 -1
- package/lib/tree/any.d.ts +0 -58
- package/lib/tree/any.d.ts.map +0 -1
- package/lib/tree/any.js +0 -104
- package/lib/tree/any.js.map +0 -1
- package/lib/tree/at-rule.d.ts +0 -53
- package/lib/tree/at-rule.d.ts.map +0 -1
- package/lib/tree/at-rule.js +0 -503
- package/lib/tree/at-rule.js.map +0 -1
- package/lib/tree/block.d.ts +0 -22
- package/lib/tree/block.d.ts.map +0 -1
- package/lib/tree/block.js +0 -24
- package/lib/tree/block.js.map +0 -1
- package/lib/tree/bool.d.ts +0 -18
- package/lib/tree/bool.d.ts.map +0 -1
- package/lib/tree/bool.js +0 -28
- package/lib/tree/bool.js.map +0 -1
- package/lib/tree/call.d.ts +0 -66
- package/lib/tree/call.d.ts.map +0 -1
- package/lib/tree/call.js +0 -306
- package/lib/tree/call.js.map +0 -1
- package/lib/tree/collection.d.ts +0 -30
- package/lib/tree/collection.d.ts.map +0 -1
- package/lib/tree/collection.js +0 -37
- package/lib/tree/collection.js.map +0 -1
- package/lib/tree/color.d.ts +0 -101
- package/lib/tree/color.d.ts.map +0 -1
- package/lib/tree/color.js +0 -513
- package/lib/tree/color.js.map +0 -1
- package/lib/tree/combinator.d.ts +0 -13
- package/lib/tree/combinator.d.ts.map +0 -1
- package/lib/tree/combinator.js +0 -12
- package/lib/tree/combinator.js.map +0 -1
- package/lib/tree/comment.d.ts +0 -20
- package/lib/tree/comment.d.ts.map +0 -1
- package/lib/tree/comment.js +0 -19
- package/lib/tree/comment.js.map +0 -1
- package/lib/tree/condition.d.ts +0 -31
- package/lib/tree/condition.d.ts.map +0 -1
- package/lib/tree/condition.js +0 -103
- package/lib/tree/condition.js.map +0 -1
- package/lib/tree/control.d.ts +0 -104
- package/lib/tree/control.d.ts.map +0 -1
- package/lib/tree/control.js +0 -430
- package/lib/tree/control.js.map +0 -1
- package/lib/tree/declaration-custom.d.ts +0 -18
- package/lib/tree/declaration-custom.d.ts.map +0 -1
- package/lib/tree/declaration-custom.js +0 -24
- package/lib/tree/declaration-custom.js.map +0 -1
- package/lib/tree/declaration-var.d.ts +0 -35
- package/lib/tree/declaration-var.d.ts.map +0 -1
- package/lib/tree/declaration-var.js +0 -63
- package/lib/tree/declaration-var.js.map +0 -1
- package/lib/tree/declaration.d.ts +0 -78
- package/lib/tree/declaration.d.ts.map +0 -1
- package/lib/tree/declaration.js +0 -286
- package/lib/tree/declaration.js.map +0 -1
- package/lib/tree/default-guard.d.ts +0 -15
- package/lib/tree/default-guard.d.ts.map +0 -1
- package/lib/tree/default-guard.js +0 -19
- package/lib/tree/default-guard.js.map +0 -1
- package/lib/tree/dimension.d.ts +0 -34
- package/lib/tree/dimension.d.ts.map +0 -1
- package/lib/tree/dimension.js +0 -294
- package/lib/tree/dimension.js.map +0 -1
- package/lib/tree/expression.d.ts +0 -25
- package/lib/tree/expression.d.ts.map +0 -1
- package/lib/tree/expression.js +0 -32
- package/lib/tree/expression.js.map +0 -1
- package/lib/tree/extend-list.d.ts +0 -23
- package/lib/tree/extend-list.d.ts.map +0 -1
- package/lib/tree/extend-list.js +0 -23
- package/lib/tree/extend-list.js.map +0 -1
- package/lib/tree/extend.d.ts +0 -47
- package/lib/tree/extend.d.ts.map +0 -1
- package/lib/tree/extend.js +0 -296
- package/lib/tree/extend.js.map +0 -1
- package/lib/tree/function.d.ts +0 -48
- package/lib/tree/function.d.ts.map +0 -1
- package/lib/tree/function.js +0 -74
- package/lib/tree/function.js.map +0 -1
- package/lib/tree/import-js.d.ts +0 -35
- package/lib/tree/import-js.d.ts.map +0 -1
- package/lib/tree/import-js.js +0 -45
- package/lib/tree/import-js.js.map +0 -1
- package/lib/tree/import-style.d.ts +0 -156
- package/lib/tree/import-style.d.ts.map +0 -1
- package/lib/tree/import-style.js +0 -566
- package/lib/tree/import-style.js.map +0 -1
- package/lib/tree/index.d.ts +0 -71
- package/lib/tree/index.d.ts.map +0 -1
- package/lib/tree/index.js.map +0 -1
- package/lib/tree/interpolated-reference.d.ts +0 -24
- package/lib/tree/interpolated-reference.d.ts.map +0 -1
- package/lib/tree/interpolated-reference.js +0 -37
- package/lib/tree/interpolated-reference.js.map +0 -1
- package/lib/tree/interpolated.d.ts +0 -62
- package/lib/tree/interpolated.d.ts.map +0 -1
- package/lib/tree/interpolated.js +0 -204
- package/lib/tree/interpolated.js.map +0 -1
- package/lib/tree/js-array.d.ts +0 -10
- package/lib/tree/js-array.d.ts.map +0 -1
- package/lib/tree/js-array.js +0 -10
- package/lib/tree/js-array.js.map +0 -1
- package/lib/tree/js-expr.d.ts +0 -23
- package/lib/tree/js-expr.d.ts.map +0 -1
- package/lib/tree/js-expr.js +0 -28
- package/lib/tree/js-expr.js.map +0 -1
- package/lib/tree/js-function.d.ts +0 -20
- package/lib/tree/js-function.d.ts.map +0 -1
- package/lib/tree/js-function.js +0 -16
- package/lib/tree/js-function.js.map +0 -1
- package/lib/tree/js-object.d.ts +0 -10
- package/lib/tree/js-object.d.ts.map +0 -1
- package/lib/tree/js-object.js +0 -10
- package/lib/tree/js-object.js.map +0 -1
- package/lib/tree/list.d.ts +0 -38
- package/lib/tree/list.d.ts.map +0 -1
- package/lib/tree/list.js +0 -83
- package/lib/tree/list.js.map +0 -1
- package/lib/tree/log.d.ts +0 -29
- package/lib/tree/log.d.ts.map +0 -1
- package/lib/tree/log.js +0 -56
- package/lib/tree/log.js.map +0 -1
- package/lib/tree/mixin.d.ts +0 -87
- package/lib/tree/mixin.d.ts.map +0 -1
- package/lib/tree/mixin.js +0 -112
- package/lib/tree/mixin.js.map +0 -1
- package/lib/tree/negative.d.ts +0 -17
- package/lib/tree/negative.d.ts.map +0 -1
- package/lib/tree/negative.js +0 -22
- package/lib/tree/negative.js.map +0 -1
- package/lib/tree/nil.d.ts +0 -30
- package/lib/tree/nil.d.ts.map +0 -1
- package/lib/tree/nil.js +0 -35
- package/lib/tree/nil.js.map +0 -1
- package/lib/tree/node-base.d.ts +0 -361
- package/lib/tree/node-base.d.ts.map +0 -1
- package/lib/tree/node-base.js +0 -930
- package/lib/tree/node-base.js.map +0 -1
- package/lib/tree/node.d.ts +0 -10
- package/lib/tree/node.d.ts.map +0 -1
- package/lib/tree/node.js +0 -45
- package/lib/tree/node.js.map +0 -1
- package/lib/tree/number.d.ts +0 -21
- package/lib/tree/number.d.ts.map +0 -1
- package/lib/tree/number.js +0 -27
- package/lib/tree/number.js.map +0 -1
- package/lib/tree/operation.d.ts +0 -26
- package/lib/tree/operation.d.ts.map +0 -1
- package/lib/tree/operation.js +0 -103
- package/lib/tree/operation.js.map +0 -1
- package/lib/tree/paren.d.ts +0 -19
- package/lib/tree/paren.d.ts.map +0 -1
- package/lib/tree/paren.js +0 -92
- package/lib/tree/paren.js.map +0 -1
- package/lib/tree/query-condition.d.ts +0 -17
- package/lib/tree/query-condition.d.ts.map +0 -1
- package/lib/tree/query-condition.js +0 -39
- package/lib/tree/query-condition.js.map +0 -1
- package/lib/tree/quoted.d.ts +0 -28
- package/lib/tree/quoted.d.ts.map +0 -1
- package/lib/tree/quoted.js +0 -75
- package/lib/tree/quoted.js.map +0 -1
- package/lib/tree/range.d.ts +0 -33
- package/lib/tree/range.d.ts.map +0 -1
- package/lib/tree/range.js +0 -47
- package/lib/tree/range.js.map +0 -1
- package/lib/tree/reference.d.ts +0 -76
- package/lib/tree/reference.d.ts.map +0 -1
- package/lib/tree/reference.js +0 -521
- package/lib/tree/reference.js.map +0 -1
- package/lib/tree/rest.d.ts +0 -15
- package/lib/tree/rest.d.ts.map +0 -1
- package/lib/tree/rest.js +0 -32
- package/lib/tree/rest.js.map +0 -1
- package/lib/tree/rules-raw.d.ts +0 -17
- package/lib/tree/rules-raw.d.ts.map +0 -1
- package/lib/tree/rules-raw.js +0 -37
- package/lib/tree/rules-raw.js.map +0 -1
- package/lib/tree/rules.d.ts +0 -262
- package/lib/tree/rules.d.ts.map +0 -1
- package/lib/tree/rules.js +0 -2359
- package/lib/tree/rules.js.map +0 -1
- package/lib/tree/ruleset.d.ts +0 -92
- package/lib/tree/ruleset.d.ts.map +0 -1
- package/lib/tree/ruleset.js +0 -528
- package/lib/tree/ruleset.js.map +0 -1
- package/lib/tree/selector-attr.d.ts +0 -31
- package/lib/tree/selector-attr.d.ts.map +0 -1
- package/lib/tree/selector-attr.js +0 -99
- package/lib/tree/selector-attr.js.map +0 -1
- package/lib/tree/selector-basic.d.ts +0 -24
- package/lib/tree/selector-basic.d.ts.map +0 -1
- package/lib/tree/selector-basic.js +0 -38
- package/lib/tree/selector-basic.js.map +0 -1
- package/lib/tree/selector-capture.d.ts +0 -23
- package/lib/tree/selector-capture.d.ts.map +0 -1
- package/lib/tree/selector-capture.js +0 -34
- package/lib/tree/selector-capture.js.map +0 -1
- package/lib/tree/selector-complex.d.ts +0 -40
- package/lib/tree/selector-complex.d.ts.map +0 -1
- package/lib/tree/selector-complex.js +0 -143
- package/lib/tree/selector-complex.js.map +0 -1
- package/lib/tree/selector-compound.d.ts +0 -16
- package/lib/tree/selector-compound.d.ts.map +0 -1
- package/lib/tree/selector-compound.js +0 -114
- package/lib/tree/selector-compound.js.map +0 -1
- package/lib/tree/selector-interpolated.d.ts +0 -23
- package/lib/tree/selector-interpolated.d.ts.map +0 -1
- package/lib/tree/selector-interpolated.js +0 -27
- package/lib/tree/selector-interpolated.js.map +0 -1
- package/lib/tree/selector-list.d.ts +0 -17
- package/lib/tree/selector-list.d.ts.map +0 -1
- package/lib/tree/selector-list.js +0 -174
- package/lib/tree/selector-list.js.map +0 -1
- package/lib/tree/selector-pseudo.d.ts +0 -42
- package/lib/tree/selector-pseudo.d.ts.map +0 -1
- package/lib/tree/selector-pseudo.js +0 -204
- package/lib/tree/selector-pseudo.js.map +0 -1
- package/lib/tree/selector-simple.d.ts +0 -5
- package/lib/tree/selector-simple.d.ts.map +0 -1
- package/lib/tree/selector-simple.js +0 -6
- package/lib/tree/selector-simple.js.map +0 -1
- package/lib/tree/selector.d.ts +0 -43
- package/lib/tree/selector.d.ts.map +0 -1
- package/lib/tree/selector.js +0 -56
- package/lib/tree/selector.js.map +0 -1
- package/lib/tree/sequence.d.ts +0 -43
- package/lib/tree/sequence.d.ts.map +0 -1
- package/lib/tree/sequence.js +0 -151
- package/lib/tree/sequence.js.map +0 -1
- package/lib/tree/tree.d.ts +0 -87
- package/lib/tree/tree.d.ts.map +0 -1
- package/lib/tree/tree.js +0 -2
- package/lib/tree/tree.js.map +0 -1
- package/lib/tree/url.d.ts +0 -18
- package/lib/tree/url.d.ts.map +0 -1
- package/lib/tree/url.js +0 -35
- package/lib/tree/url.js.map +0 -1
- package/lib/tree/util/__tests__/debug-log.d.ts +0 -1
- package/lib/tree/util/__tests__/debug-log.d.ts.map +0 -1
- package/lib/tree/util/__tests__/debug-log.js +0 -36
- package/lib/tree/util/__tests__/debug-log.js.map +0 -1
- package/lib/tree/util/calculate.d.ts +0 -3
- package/lib/tree/util/calculate.d.ts.map +0 -1
- package/lib/tree/util/calculate.js +0 -10
- package/lib/tree/util/calculate.js.map +0 -1
- package/lib/tree/util/cast.d.ts +0 -10
- package/lib/tree/util/cast.d.ts.map +0 -1
- package/lib/tree/util/cast.js +0 -87
- package/lib/tree/util/cast.js.map +0 -1
- package/lib/tree/util/cloning.d.ts +0 -4
- package/lib/tree/util/cloning.d.ts.map +0 -1
- package/lib/tree/util/cloning.js +0 -8
- package/lib/tree/util/cloning.js.map +0 -1
- package/lib/tree/util/collections.d.ts +0 -57
- package/lib/tree/util/collections.d.ts.map +0 -1
- package/lib/tree/util/collections.js +0 -136
- package/lib/tree/util/collections.js.map +0 -1
- package/lib/tree/util/compare.d.ts +0 -11
- package/lib/tree/util/compare.d.ts.map +0 -1
- package/lib/tree/util/compare.js +0 -89
- package/lib/tree/util/compare.js.map +0 -1
- package/lib/tree/util/extend-helpers.d.ts +0 -2
- package/lib/tree/util/extend-helpers.d.ts.map +0 -1
- package/lib/tree/util/extend-helpers.js +0 -2
- package/lib/tree/util/extend-helpers.js.map +0 -1
- package/lib/tree/util/extend-roots.d.ts +0 -37
- package/lib/tree/util/extend-roots.d.ts.map +0 -1
- package/lib/tree/util/extend-roots.js +0 -700
- package/lib/tree/util/extend-roots.js.map +0 -1
- package/lib/tree/util/extend-roots.old.d.ts +0 -132
- package/lib/tree/util/extend-roots.old.d.ts.map +0 -1
- package/lib/tree/util/extend-roots.old.js +0 -2272
- package/lib/tree/util/extend-roots.old.js.map +0 -1
- package/lib/tree/util/extend-trace-debug.d.ts +0 -13
- package/lib/tree/util/extend-trace-debug.d.ts.map +0 -1
- package/lib/tree/util/extend-trace-debug.js +0 -34
- package/lib/tree/util/extend-trace-debug.js.map +0 -1
- package/lib/tree/util/extend-walk.d.ts +0 -53
- package/lib/tree/util/extend-walk.d.ts.map +0 -1
- package/lib/tree/util/extend-walk.js +0 -881
- package/lib/tree/util/extend-walk.js.map +0 -1
- package/lib/tree/util/extend.d.ts +0 -218
- package/lib/tree/util/extend.d.ts.map +0 -1
- package/lib/tree/util/extend.js +0 -3182
- package/lib/tree/util/extend.js.map +0 -1
- package/lib/tree/util/find-extendable-locations.d.ts +0 -2
- package/lib/tree/util/find-extendable-locations.d.ts.map +0 -1
- package/lib/tree/util/find-extendable-locations.js +0 -2
- package/lib/tree/util/find-extendable-locations.js.map +0 -1
- package/lib/tree/util/format.d.ts +0 -20
- package/lib/tree/util/format.d.ts.map +0 -1
- package/lib/tree/util/format.js +0 -67
- package/lib/tree/util/format.js.map +0 -1
- package/lib/tree/util/is-node.d.ts +0 -13
- package/lib/tree/util/is-node.d.ts.map +0 -1
- package/lib/tree/util/is-node.js +0 -43
- package/lib/tree/util/is-node.js.map +0 -1
- package/lib/tree/util/print.d.ts +0 -80
- package/lib/tree/util/print.d.ts.map +0 -1
- package/lib/tree/util/print.js +0 -205
- package/lib/tree/util/print.js.map +0 -1
- package/lib/tree/util/process-leading-is.d.ts +0 -25
- package/lib/tree/util/process-leading-is.d.ts.map +0 -1
- package/lib/tree/util/process-leading-is.js +0 -364
- package/lib/tree/util/process-leading-is.js.map +0 -1
- package/lib/tree/util/recursion-helper.d.ts +0 -15
- package/lib/tree/util/recursion-helper.d.ts.map +0 -1
- package/lib/tree/util/recursion-helper.js +0 -43
- package/lib/tree/util/recursion-helper.js.map +0 -1
- package/lib/tree/util/regex.d.ts +0 -4
- package/lib/tree/util/regex.d.ts.map +0 -1
- package/lib/tree/util/regex.js +0 -4
- package/lib/tree/util/regex.js.map +0 -1
- package/lib/tree/util/registry-utils.d.ts +0 -192
- package/lib/tree/util/registry-utils.d.ts.map +0 -1
- package/lib/tree/util/registry-utils.js +0 -1214
- package/lib/tree/util/registry-utils.js.map +0 -1
- package/lib/tree/util/ruleset-trace.d.ts +0 -4
- package/lib/tree/util/ruleset-trace.d.ts.map +0 -1
- package/lib/tree/util/ruleset-trace.js +0 -14
- package/lib/tree/util/ruleset-trace.js.map +0 -1
- package/lib/tree/util/selector-compare.d.ts +0 -2
- package/lib/tree/util/selector-compare.d.ts.map +0 -1
- package/lib/tree/util/selector-compare.js +0 -2
- package/lib/tree/util/selector-compare.js.map +0 -1
- package/lib/tree/util/selector-match-core.d.ts +0 -184
- package/lib/tree/util/selector-match-core.d.ts.map +0 -1
- package/lib/tree/util/selector-match-core.js +0 -1603
- package/lib/tree/util/selector-match-core.js.map +0 -1
- package/lib/tree/util/selector-utils.d.ts +0 -30
- package/lib/tree/util/selector-utils.d.ts.map +0 -1
- package/lib/tree/util/selector-utils.js +0 -100
- package/lib/tree/util/selector-utils.js.map +0 -1
- package/lib/tree/util/serialize-helper.d.ts +0 -13
- package/lib/tree/util/serialize-helper.d.ts.map +0 -1
- package/lib/tree/util/serialize-helper.js +0 -387
- package/lib/tree/util/serialize-helper.js.map +0 -1
- package/lib/tree/util/serialize-types.d.ts +0 -9
- package/lib/tree/util/serialize-types.d.ts.map +0 -1
- package/lib/tree/util/serialize-types.js +0 -216
- package/lib/tree/util/serialize-types.js.map +0 -1
- package/lib/tree/util/should-operate.d.ts +0 -23
- package/lib/tree/util/should-operate.d.ts.map +0 -1
- package/lib/tree/util/should-operate.js +0 -46
- package/lib/tree/util/should-operate.js.map +0 -1
- package/lib/tree/util/sourcemap.d.ts +0 -7
- package/lib/tree/util/sourcemap.d.ts.map +0 -1
- package/lib/tree/util/sourcemap.js +0 -25
- package/lib/tree/util/sourcemap.js.map +0 -1
- package/lib/types/config.d.ts +0 -205
- package/lib/types/config.d.ts.map +0 -1
- package/lib/types/config.js +0 -2
- package/lib/types/config.js.map +0 -1
- package/lib/types/index.d.ts +0 -15
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/index.js +0 -3
- package/lib/types/index.js.map +0 -1
- package/lib/types/modes.d.ts.map +0 -1
- package/lib/types/modes.js +0 -2
- package/lib/types/modes.js.map +0 -1
- package/lib/types.d.ts +0 -61
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/lib/use-webpack-resolver.d.ts +0 -9
- package/lib/use-webpack-resolver.d.ts.map +0 -1
- package/lib/use-webpack-resolver.js +0 -41
- package/lib/use-webpack-resolver.js.map +0 -1
- package/lib/visitor/index.d.ts +0 -136
- package/lib/visitor/index.d.ts.map +0 -1
- package/lib/visitor/index.js +0 -135
- package/lib/visitor/index.js.map +0 -1
- package/lib/visitor/less-visitor.d.ts +0 -7
- package/lib/visitor/less-visitor.d.ts.map +0 -1
- package/lib/visitor/less-visitor.js.map +0 -1
|
@@ -0,0 +1,1427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for every exported function in selector-match-core.ts.
|
|
3
|
+
*
|
|
4
|
+
* These tests pin the CURRENT behaviour so we can safely replace
|
|
5
|
+
* internals with a walk-and-consume algorithm. If any test breaks
|
|
6
|
+
* after a refactor, the refactor changed observable semantics.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
el, sel, sellist, compound, is, co, pseudo, amp, rules, ruleset
|
|
11
|
+
} from '../../../index.js';
|
|
12
|
+
import { Context } from '../../../context.js';
|
|
13
|
+
import {
|
|
14
|
+
selectorMatch
|
|
15
|
+
} from '../selector-match-core.js';
|
|
16
|
+
|
|
17
|
+
let context: Context;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
context = new Context();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('basic selectors', () => {
|
|
23
|
+
it('matches identical simple selectors', () => {
|
|
24
|
+
let sel1 = el('.a');
|
|
25
|
+
let sel2 = el('.a');
|
|
26
|
+
sel1.eval(context);
|
|
27
|
+
sel2.eval(context);
|
|
28
|
+
let result = selectorMatch(sel1, sel2);
|
|
29
|
+
expect(result.fullMatch).toBe(true);
|
|
30
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
31
|
+
expect(result.matches[0]!.consumedTarget).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('rejects different simple selectors', async () => {
|
|
35
|
+
let sel1 = el('.a');
|
|
36
|
+
let sel2 = el('.b');
|
|
37
|
+
sel1.eval(context);
|
|
38
|
+
sel2.eval(context);
|
|
39
|
+
expect(selectorMatch(sel1, sel2).fullMatch).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('compound selectors', () => {
|
|
44
|
+
it('matches identical compound selectors', async () => {
|
|
45
|
+
let sel1 = compound([el('.a'), el('.b')]);
|
|
46
|
+
let sel2 = compound([el('.a'), el('.b')]);
|
|
47
|
+
await sel1.eval(context);
|
|
48
|
+
await sel2.eval(context);
|
|
49
|
+
expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('matches rearranged compound selectors', async () => {
|
|
53
|
+
let sel1 = compound([el('.a'), el('.b')]);
|
|
54
|
+
let sel2 = compound([el('.b'), el('.a')]);
|
|
55
|
+
await sel1.eval(context);
|
|
56
|
+
await sel2.eval(context);
|
|
57
|
+
expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('returns a partial match when a compound has extra members inside the matched span', async () => {
|
|
61
|
+
let sel1 = compound([el('.b'), el('.a'), pseudo({ name: ':hover' })]);
|
|
62
|
+
let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
|
|
63
|
+
await sel1.eval(context);
|
|
64
|
+
await sel2.eval(context);
|
|
65
|
+
let result = selectorMatch(sel2, sel1);
|
|
66
|
+
expect(result.fullMatch).toBe(false);
|
|
67
|
+
expect(result.partialMatch).toBe(true);
|
|
68
|
+
expect(result.matches).toHaveLength(1);
|
|
69
|
+
expect(result.matches[0]!.startIndex).toBe(0);
|
|
70
|
+
expect(result.matches[0]!.endIndex).toBe(2);
|
|
71
|
+
expect(result.matches[0]!.matchedIndices).toEqual([0, 2]);
|
|
72
|
+
expect(result.matches[0]!.consumedTarget).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('returns a partial match for .b:hover within .b.a:hover', async () => {
|
|
76
|
+
let sel1 = compound([el('.b'), el('.a'), pseudo({ name: ':hover' })]);
|
|
77
|
+
let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
|
|
78
|
+
await sel1.eval(context);
|
|
79
|
+
await sel2.eval(context);
|
|
80
|
+
let result = selectorMatch(sel2, sel1);
|
|
81
|
+
expect(result.fullMatch).toBe(false);
|
|
82
|
+
expect(result.partialMatch).toBe(true);
|
|
83
|
+
expect(result.matches).toHaveLength(1);
|
|
84
|
+
expect(result.matches[0]!.startIndex).toBe(0);
|
|
85
|
+
expect(result.matches[0]!.endIndex).toBe(2);
|
|
86
|
+
expect(result.matches[0]!.matchedIndices).toEqual([0, 2]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('returns a partial match for .b.x within .b.a.x', async () => {
|
|
90
|
+
let sel1 = compound([el('.b'), el('.a'), el('.x')]);
|
|
91
|
+
let sel2 = compound([el('.b'), el('.x')]);
|
|
92
|
+
await sel1.eval(context);
|
|
93
|
+
await sel2.eval(context);
|
|
94
|
+
let result = selectorMatch(sel2, sel1);
|
|
95
|
+
expect(result.fullMatch).toBe(false);
|
|
96
|
+
expect(result.partialMatch).toBe(true);
|
|
97
|
+
expect(result.matches).toHaveLength(1);
|
|
98
|
+
expect(result.matches[0]!.startIndex).toBe(0);
|
|
99
|
+
expect(result.matches[0]!.endIndex).toBe(2);
|
|
100
|
+
expect(result.matches[0]!.matchedIndices).toEqual([0, 2]);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('pseudo-selectors', () => {
|
|
105
|
+
describe(':is()', () => {
|
|
106
|
+
it('matches identical :is() pseudo-selectors', async () => {
|
|
107
|
+
let sel1 = pseudo({ name: ':is', arg: el('.a') });
|
|
108
|
+
let sel2 = pseudo({ name: ':is', arg: el('.a') });
|
|
109
|
+
await sel1.eval(context);
|
|
110
|
+
await sel2.eval(context);
|
|
111
|
+
expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('normalizes simple selector in :is()', async () => {
|
|
115
|
+
let sel1 = pseudo({ name: ':is', arg: el('.a') });
|
|
116
|
+
let sel2 = el('.a');
|
|
117
|
+
await sel1.eval(context);
|
|
118
|
+
await sel2.eval(context);
|
|
119
|
+
expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('normalizes compound selector in :is()', async () => {
|
|
123
|
+
let sel1 = pseudo({ name: ':is', arg: compound([el('.a'), el('.b')]) });
|
|
124
|
+
let sel2 = compound([el('.b'), el('.a')]);
|
|
125
|
+
await sel1.eval(context);
|
|
126
|
+
await sel2.eval(context);
|
|
127
|
+
let result = selectorMatch(sel1, sel2);
|
|
128
|
+
expect(result.fullMatch).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('matches a selector in :is()', async () => {
|
|
132
|
+
let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: compound([el('.a'), el('.b')]) })]);
|
|
133
|
+
let sel2 = compound([el('.b'), el('.a')]);
|
|
134
|
+
await sel1.eval(context);
|
|
135
|
+
await sel2.eval(context);
|
|
136
|
+
let result = selectorMatch(sel1, sel2);
|
|
137
|
+
expect(result.fullMatch).toBe(false);
|
|
138
|
+
expect(result.partialMatch).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('matches an :is() as a full match #1', async () => {
|
|
142
|
+
let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: compound([el('.a'), el('.b')]) })]);
|
|
143
|
+
let sel2 = sel([el('foo'), co('>'), compound([el('.a'), is(el('.b'))])]);
|
|
144
|
+
await sel1.eval(context);
|
|
145
|
+
await sel2.eval(context);
|
|
146
|
+
let result = selectorMatch(sel1, sel2);
|
|
147
|
+
expect(result.fullMatch).toBe(true);
|
|
148
|
+
expect(result.partialMatch).toBe(true);
|
|
149
|
+
expect(result.matches).toHaveLength(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('matches an :is() as a full match #2', async () => {
|
|
153
|
+
let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([compound([el('.a'), el('.b')]), co('>'), el('.b')]) })]);
|
|
154
|
+
let sel2 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([compound([el('.b'), el('.a')]), co('>'), el('.b')]) })]);
|
|
155
|
+
await sel1.eval(context);
|
|
156
|
+
await sel2.eval(context);
|
|
157
|
+
let result = selectorMatch(sel1, sel2);
|
|
158
|
+
expect(result.fullMatch).toBe(true);
|
|
159
|
+
expect(result.partialMatch).toBe(true);
|
|
160
|
+
expect(result.matches).toHaveLength(1);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('matches an :is() as a full match #3', async () => {
|
|
164
|
+
let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sellist([sel([el('q'), co('>'), el('x')]), sel([compound([el('.a'), el('.b')]), co('>'), el('.b')])]) })]);
|
|
165
|
+
let sel2 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([compound([el('.b'), el('.a')]), co('>'), el('.b')]) })]);
|
|
166
|
+
await sel1.eval(context);
|
|
167
|
+
await sel2.eval(context);
|
|
168
|
+
let result = selectorMatch(sel1, sel2);
|
|
169
|
+
expect(result.fullMatch).toBe(true);
|
|
170
|
+
expect(result.partialMatch).toBe(true);
|
|
171
|
+
expect(result.matches).toHaveLength(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('matches an :is() as a full match #4', async () => {
|
|
175
|
+
let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sellist([sel([el('q'), co('>'), el('x')]), sel([compound([el('.a'), el('.b')]), co('>'), el('.b')])]) })]);
|
|
176
|
+
let sel2 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([el('q'), co('>'), el('x')]) })]);
|
|
177
|
+
await sel1.eval(context);
|
|
178
|
+
await sel2.eval(context);
|
|
179
|
+
let result = selectorMatch(sel1, sel2);
|
|
180
|
+
expect(result.fullMatch).toBe(true);
|
|
181
|
+
expect(result.partialMatch).toBe(true);
|
|
182
|
+
expect(result.matches).toHaveLength(1);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('matches through deeply nested :is() alternates', async () => {
|
|
186
|
+
let sel1 = pseudo({
|
|
187
|
+
name: ':is',
|
|
188
|
+
arg: sellist([
|
|
189
|
+
pseudo({ name: ':is', arg: sellist([el('.a'), el('.b')]) }),
|
|
190
|
+
el('.c')
|
|
191
|
+
])
|
|
192
|
+
});
|
|
193
|
+
let sel2 = el('.b');
|
|
194
|
+
await sel1.eval(context);
|
|
195
|
+
await sel2.eval(context);
|
|
196
|
+
let result = selectorMatch(sel2, sel1);
|
|
197
|
+
expect(result.fullMatch).toBe(true);
|
|
198
|
+
expect(result.partialMatch).toBe(true);
|
|
199
|
+
expect(result.matches).toHaveLength(1);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('doesn\'t match branched :is() as full match #1', async () => {
|
|
203
|
+
let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.b')]) })]);
|
|
204
|
+
let sel2 = sel([el('.a'), co('>'), el('.b')]);
|
|
205
|
+
await sel1.eval(context);
|
|
206
|
+
await sel2.eval(context);
|
|
207
|
+
let result = selectorMatch(sel1, sel2);
|
|
208
|
+
expect(result.fullMatch).toBe(false);
|
|
209
|
+
expect(result.partialMatch).toBe(true);
|
|
210
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
211
|
+
expect(result.matches).toHaveLength(0);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('doesn\'t match branched :is() as full match #2', async () => {
|
|
215
|
+
let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
|
|
216
|
+
let sel2 = sel([el('.a'), co('>'), el('.b')]);
|
|
217
|
+
await sel1.eval(context);
|
|
218
|
+
await sel2.eval(context);
|
|
219
|
+
let result = selectorMatch(sel1, sel2);
|
|
220
|
+
expect(result.fullMatch).toBe(false);
|
|
221
|
+
expect(result.partialMatch).toBe(false);
|
|
222
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
223
|
+
expect(result.matches).toHaveLength(0);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('doesn\'t match branched :is() as full match #3', async () => {
|
|
227
|
+
let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
|
|
228
|
+
let sel2 = sel([el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
|
|
229
|
+
await sel1.eval(context);
|
|
230
|
+
await sel2.eval(context);
|
|
231
|
+
let result = selectorMatch(sel1, sel2);
|
|
232
|
+
expect(result.fullMatch).toBe(false);
|
|
233
|
+
expect(result.partialMatch).toBe(false);
|
|
234
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
235
|
+
expect(result.matches).toHaveLength(0);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('does match branched :is() as partial match #1', async () => {
|
|
239
|
+
let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
|
|
240
|
+
let sel2 = sel([el('.a'), co('>'), el('.c')]);
|
|
241
|
+
await sel1.eval(context);
|
|
242
|
+
await sel2.eval(context);
|
|
243
|
+
let result = selectorMatch(sel1, sel2);
|
|
244
|
+
expect(result.fullMatch).toBe(false);
|
|
245
|
+
expect(result.partialMatch).toBe(true);
|
|
246
|
+
expect(result.matches).toHaveLength(1);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('does match branched :is() as partial match #2', async () => {
|
|
250
|
+
let sel1 = sel([el('.a'), co('>'), compound([el('.x'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })])]);
|
|
251
|
+
let sel2 = sel([el('.a'), co('>'), compound([el('.c'), el('.x')])]);
|
|
252
|
+
await sel1.eval(context);
|
|
253
|
+
await sel2.eval(context);
|
|
254
|
+
let result = selectorMatch(sel1, sel2);
|
|
255
|
+
expect(result.fullMatch).toBe(false);
|
|
256
|
+
expect(result.partialMatch).toBe(true);
|
|
257
|
+
expect(result.matches).toHaveLength(1);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('does match branched :is() as partial match #3', async () => {
|
|
261
|
+
let sel1 = sel([el('.a'), co('>'), compound([pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) }), el('.x')])]);
|
|
262
|
+
let sel2 = sel([el('.a'), co('>'), compound([el('.c'), el('.x')])]);
|
|
263
|
+
await sel1.eval(context);
|
|
264
|
+
await sel2.eval(context);
|
|
265
|
+
let result = selectorMatch(sel1, sel2);
|
|
266
|
+
expect(result.fullMatch).toBe(false);
|
|
267
|
+
expect(result.partialMatch).toBe(true);
|
|
268
|
+
expect(result.matches).toHaveLength(1);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('does match branched :is() as partial match #4', async () => {
|
|
272
|
+
let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
|
|
273
|
+
let sel2 = sel([el('.b'), co('>'), el('.c')]);
|
|
274
|
+
await sel1.eval(context);
|
|
275
|
+
await sel2.eval(context);
|
|
276
|
+
let result = selectorMatch(sel1, sel2);
|
|
277
|
+
expect(result.fullMatch).toBe(false);
|
|
278
|
+
expect(result.partialMatch).toBe(true);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('other pseudos', () => {
|
|
283
|
+
it('returns a partial match when only the inner selector matches', async () => {
|
|
284
|
+
let sel0 = compound([el('.a'), el('.b')]);
|
|
285
|
+
let sel1 = pseudo({ name: ':where', arg: sel0 });
|
|
286
|
+
let sel2 = compound([el('.b'), el('.a')]);
|
|
287
|
+
await sel1.eval(context);
|
|
288
|
+
await sel2.eval(context);
|
|
289
|
+
let result = selectorMatch(sel1, sel2);
|
|
290
|
+
expect(result.fullMatch).toBe(false);
|
|
291
|
+
expect(result.partialMatch).toBe(true);
|
|
292
|
+
expect(result.matches[0]!.exact).toBe(false);
|
|
293
|
+
expect(result.matches[0]!.containingNode).toBe(sel0);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('does not continue a match across a nested pseudo boundary', async () => {
|
|
297
|
+
let sel1 = compound([el('.a'), pseudo({ name: ':where', arg: el('.b') })]);
|
|
298
|
+
let sel2 = compound([el('.a'), el('.b')]);
|
|
299
|
+
await sel1.eval(context);
|
|
300
|
+
await sel2.eval(context);
|
|
301
|
+
let result = selectorMatch(sel1, sel2);
|
|
302
|
+
expect(result.fullMatch).toBe(false);
|
|
303
|
+
expect(result.partialMatch).toBe(false);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('does not continue a match across a leading pseudo boundary', async () => {
|
|
307
|
+
let sel1 = compound([pseudo({ name: ':where', arg: el('.a') }), el('.b')]);
|
|
308
|
+
let sel2 = compound([el('.a'), el('.b')]);
|
|
309
|
+
await sel1.eval(context);
|
|
310
|
+
await sel2.eval(context);
|
|
311
|
+
let result = selectorMatch(sel1, sel2);
|
|
312
|
+
expect(result.fullMatch).toBe(false);
|
|
313
|
+
expect(result.partialMatch).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('does not match non-matching pseudo names', async () => {
|
|
317
|
+
let sel1 = pseudo({ name: ':where', arg: el('.a') });
|
|
318
|
+
let sel2 = pseudo({ name: ':not', arg: el('.a') });
|
|
319
|
+
await sel1.eval(context);
|
|
320
|
+
await sel2.eval(context);
|
|
321
|
+
let result = selectorMatch(sel1, sel2);
|
|
322
|
+
expect(result.fullMatch).toBe(false);
|
|
323
|
+
expect(result.partialMatch).toBe(false);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('matches equivalent pseudo selectors', async () => {
|
|
327
|
+
let sel1 = pseudo({ name: ':not', arg: compound([el('.a'), el('.b')]) });
|
|
328
|
+
let sel2 = pseudo({ name: ':not', arg: compound([el('.b'), el('.a')]) });
|
|
329
|
+
await sel1.eval(context);
|
|
330
|
+
await sel2.eval(context);
|
|
331
|
+
let result = selectorMatch(sel1, sel2);
|
|
332
|
+
expect(result.fullMatch).toBe(true);
|
|
333
|
+
expect(result.partialMatch).toBe(true);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('matches equivalent pseudo selectors with selector-list alternates', async () => {
|
|
337
|
+
let sel1 = pseudo({ name: ':not', arg: compound([el('.a'), is(sellist([el('.b'), el('.x')]))]) });
|
|
338
|
+
let sel2 = pseudo({ name: ':not', arg: compound([el('.b'), el('.a')]) });
|
|
339
|
+
await sel1.eval(context);
|
|
340
|
+
await sel2.eval(context);
|
|
341
|
+
let result = selectorMatch(sel1, sel2);
|
|
342
|
+
expect(result.fullMatch).toBe(true);
|
|
343
|
+
expect(result.partialMatch).toBe(true);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('matches equivalent pseudo selectors with complex selector args', async () => {
|
|
347
|
+
let sel1 = compound([
|
|
348
|
+
pseudo({ name: ':where', arg: sel([el('.a'), co('>'), el('.b')]) }),
|
|
349
|
+
el('.c')
|
|
350
|
+
]);
|
|
351
|
+
let sel2 = compound([
|
|
352
|
+
el('.c'),
|
|
353
|
+
pseudo({ name: ':where', arg: sel([el('.a'), co('>'), el('.b')]) })
|
|
354
|
+
]);
|
|
355
|
+
await sel1.eval(context);
|
|
356
|
+
await sel2.eval(context);
|
|
357
|
+
let result = selectorMatch(sel1, sel2);
|
|
358
|
+
expect(result.fullMatch).toBe(true);
|
|
359
|
+
expect(result.partialMatch).toBe(true);
|
|
360
|
+
expect(result.matches).toHaveLength(1);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('matches equivalent mixed nested pseudo selectors', async () => {
|
|
364
|
+
let sel1 = pseudo({
|
|
365
|
+
name: ':not',
|
|
366
|
+
arg: compound([
|
|
367
|
+
pseudo({ name: ':is', arg: sellist([el('.a'), el('.c')]) }),
|
|
368
|
+
el('.d')
|
|
369
|
+
])
|
|
370
|
+
});
|
|
371
|
+
let sel2 = pseudo({
|
|
372
|
+
name: ':not',
|
|
373
|
+
arg: compound([
|
|
374
|
+
el('.d'),
|
|
375
|
+
pseudo({ name: ':is', arg: sellist([el('.c'), el('.a')]) })
|
|
376
|
+
])
|
|
377
|
+
});
|
|
378
|
+
await sel1.eval(context);
|
|
379
|
+
await sel2.eval(context);
|
|
380
|
+
let result = selectorMatch(sel1, sel2);
|
|
381
|
+
expect(result.fullMatch).toBe(true);
|
|
382
|
+
expect(result.partialMatch).toBe(true);
|
|
383
|
+
expect(result.matches).toHaveLength(1);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('recursively searches through multiple nested pseudo boundaries', async () => {
|
|
387
|
+
let sel1 = pseudo({
|
|
388
|
+
name: ':not',
|
|
389
|
+
arg: pseudo({
|
|
390
|
+
name: ':where',
|
|
391
|
+
arg: pseudo({
|
|
392
|
+
name: ':is',
|
|
393
|
+
arg: compound([el('.a'), el('.b')])
|
|
394
|
+
})
|
|
395
|
+
})
|
|
396
|
+
});
|
|
397
|
+
let sel2 = el('.a');
|
|
398
|
+
await sel1.eval(context);
|
|
399
|
+
await sel2.eval(context);
|
|
400
|
+
let result = selectorMatch(sel2, sel1);
|
|
401
|
+
expect(result.fullMatch).toBe(false);
|
|
402
|
+
expect(result.partialMatch).toBe(true);
|
|
403
|
+
expect(result.matches.length).toBeGreaterThan(0);
|
|
404
|
+
expect(result.matches.every(match => !match.exact)).toBe(true);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe('selector lists and branching', () => {
|
|
410
|
+
describe('direct matches', () => {
|
|
411
|
+
it('finds a selector in a selector list #1', async () => {
|
|
412
|
+
let sel1 = sellist([el('.a'), el('.b')]);
|
|
413
|
+
let sel2 = el('.b');
|
|
414
|
+
await sel1.eval(context);
|
|
415
|
+
await sel2.eval(context);
|
|
416
|
+
expect(selectorMatch(sel2, sel1).fullMatch).toBe(true);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('finds a selector in a selector list #2', async () => {
|
|
420
|
+
let sel1 = sellist([el('.b'), el('.x'), el('.y')]);
|
|
421
|
+
let sel2 = el('.b');
|
|
422
|
+
await sel1.eval(context);
|
|
423
|
+
await sel2.eval(context);
|
|
424
|
+
expect(selectorMatch(sel2, sel1).fullMatch).toBe(true);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('finds a compound selector in a selector list', async () => {
|
|
428
|
+
let sel1 = sellist([compound([el('.b'), el('.x')]), el('.y')]);
|
|
429
|
+
let sel2 = compound([el('.x'), el('.b')]);
|
|
430
|
+
await sel1.eval(context);
|
|
431
|
+
await sel2.eval(context);
|
|
432
|
+
expect(selectorMatch(sel2, sel1).fullMatch).toBe(true);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('matches a compound to a compound with an :is() in it', async () => {
|
|
436
|
+
let sel1 = compound([el('.a'), is(sellist([el('.x'), el('.c')])), el('.d')]);
|
|
437
|
+
let sel2 = compound([el('.d'), el('.a'), el('.c')]);
|
|
438
|
+
await sel1.eval(context);
|
|
439
|
+
await sel2.eval(context);
|
|
440
|
+
let result = selectorMatch(sel2, sel1);
|
|
441
|
+
expect(result.fullMatch).toBe(true);
|
|
442
|
+
expect(result.partialMatch).toBe(true);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('reports a single selector-list item match against the list container', async () => {
|
|
446
|
+
let sel1 = sellist([el('a'), el('b'), el('c')]);
|
|
447
|
+
let sel2 = el('a');
|
|
448
|
+
await sel1.eval(context);
|
|
449
|
+
await sel2.eval(context);
|
|
450
|
+
let result = selectorMatch(sel2, sel1);
|
|
451
|
+
expect(result.fullMatch).toBe(true);
|
|
452
|
+
expect(result.partialMatch).toBe(true);
|
|
453
|
+
expect(result.matches).toHaveLength(1);
|
|
454
|
+
expect(result.matches[0]!.containingNode).toBe(sel1);
|
|
455
|
+
expect(result.matches[0]!.startIndex).toBe(0);
|
|
456
|
+
expect(result.matches[0]!.endIndex).toBe(0);
|
|
457
|
+
expect(result.matches[0]!.matchedIndices).toEqual([0]);
|
|
458
|
+
expect(result.matches[0]!.consumedTarget).toBe(false);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('reports a complex selector-list item match against the list container', async () => {
|
|
462
|
+
let sel1 = sellist([sel([el('.a'), co('>'), el('.b')]), el('.c')]);
|
|
463
|
+
let sel2 = sel([el('.a'), co('>'), el('.b')]);
|
|
464
|
+
await sel1.eval(context);
|
|
465
|
+
await sel2.eval(context);
|
|
466
|
+
let result = selectorMatch(sel2, sel1);
|
|
467
|
+
expect(result.fullMatch).toBe(true);
|
|
468
|
+
expect(result.partialMatch).toBe(true);
|
|
469
|
+
expect(result.matches).toHaveLength(1);
|
|
470
|
+
expect(result.matches[0]!.containingNode).toBe(sel1);
|
|
471
|
+
expect(result.matches[0]!.startIndex).toBe(0);
|
|
472
|
+
expect(result.matches[0]!.endIndex).toBe(0);
|
|
473
|
+
expect(result.matches[0]!.matchedIndices).toEqual([0]);
|
|
474
|
+
expect(result.matches[0]!.consumedTarget).toBe(false);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('reports a pseudo-wrapped selector-list item match against the list container', async () => {
|
|
478
|
+
let sel1 = sellist([pseudo({ name: ':where', arg: compound([el('.a'), el('.b')]) }), el('.c')]);
|
|
479
|
+
let sel2 = compound([el('.b'), el('.a')]);
|
|
480
|
+
await sel1.eval(context);
|
|
481
|
+
await sel2.eval(context);
|
|
482
|
+
let result = selectorMatch(sel2, sel1);
|
|
483
|
+
expect(result.fullMatch).toBe(false);
|
|
484
|
+
expect(result.partialMatch).toBe(true);
|
|
485
|
+
expect(result.matches).toHaveLength(1);
|
|
486
|
+
expect(result.matches[0]!.containingNode).toBe(sel1);
|
|
487
|
+
expect(result.matches[0]!.startIndex).toBe(0);
|
|
488
|
+
expect(result.matches[0]!.endIndex).toBe(0);
|
|
489
|
+
expect(result.matches[0]!.matchedIndices).toEqual([0]);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('treats a find-side selector list as alternates', async () => {
|
|
493
|
+
let sel1 = el('a');
|
|
494
|
+
let sel2 = sellist([el('a'), el('b'), el('c')]);
|
|
495
|
+
await sel1.eval(context);
|
|
496
|
+
await sel2.eval(context);
|
|
497
|
+
let result = selectorMatch(sel2, sel1);
|
|
498
|
+
expect(result.fullMatch).toBe(true);
|
|
499
|
+
expect(result.partialMatch).toBe(true);
|
|
500
|
+
expect(result.matches).toHaveLength(1);
|
|
501
|
+
expect(result.matches[0]!.containingNode).toBe(sel1);
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
describe('partial matches', () => {
|
|
506
|
+
it('finds (partially) a compound selector in a selector list', async () => {
|
|
507
|
+
let sel1 = sellist([sel([compound([el('.b'), el('.x')]), co('>'), el('.y')]), el('.z')]);
|
|
508
|
+
let sel2 = compound([el('.x'), el('.b')]);
|
|
509
|
+
await sel1.eval(context);
|
|
510
|
+
await sel2.eval(context);
|
|
511
|
+
expect(selectorMatch(sel2, sel1).fullMatch).toBe(false);
|
|
512
|
+
expect(selectorMatch(sel2, sel1).partialMatch).toBe(true);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('returns a partial match for partial match in a compound selector', async () => {
|
|
516
|
+
let sel1 = compound([el('.a'), is(sellist([el('.x'), el('.c')])), el('.d')]);
|
|
517
|
+
let sel2 = compound([el('.d'), el('.c')]);
|
|
518
|
+
await sel1.eval(context);
|
|
519
|
+
await sel2.eval(context);
|
|
520
|
+
let result = selectorMatch(sel2, sel1);
|
|
521
|
+
expect(result.fullMatch).toBe(false);
|
|
522
|
+
expect(result.partialMatch).toBe(true);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('returns a partial match for partial match in a complex selector', async () => {
|
|
526
|
+
let sel1 = sel([el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
|
|
527
|
+
let sel2 = sel([el('.a'), co('>'), el('.b')]);
|
|
528
|
+
await sel1.eval(context);
|
|
529
|
+
await sel2.eval(context);
|
|
530
|
+
let result = selectorMatch(sel2, sel1);
|
|
531
|
+
expect(result.fullMatch).toBe(false);
|
|
532
|
+
expect(result.partialMatch).toBe(true);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it('returns a partial match for partial compound match in a complex selector', async () => {
|
|
536
|
+
let sel1 = sel([el('.a'), co('>'), compound([el('.b'), el('.x')]), co('>'), el('.c')]);
|
|
537
|
+
let sel2 = sel([el('.a'), co('>'), el('.b')]);
|
|
538
|
+
await sel1.eval(context);
|
|
539
|
+
await sel2.eval(context);
|
|
540
|
+
let result = selectorMatch(sel2, sel1);
|
|
541
|
+
expect(result.fullMatch).toBe(false);
|
|
542
|
+
expect(result.partialMatch).toBe(true);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('does not give up finding a match too soon', async () => {
|
|
546
|
+
let sel1 = sel([el('.a'), co('>'), el('.c'), co('>'), el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
|
|
547
|
+
let sel2 = sel([el('.a'), co('>'), el('.c')]);
|
|
548
|
+
await sel1.eval(context);
|
|
549
|
+
await sel2.eval(context);
|
|
550
|
+
let result = selectorMatch(sel2, sel1);
|
|
551
|
+
expect(result.fullMatch).toBe(false);
|
|
552
|
+
expect(result.partialMatch).toBe(true);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe('misses', () => {
|
|
557
|
+
it('does not find a match #1', async () => {
|
|
558
|
+
let sel1 = sel([el('.a'), co('>'), compound([el('.b'), el('.x')]), co('>'), el('.c')]);
|
|
559
|
+
let sel2 = sel([el('.a'), co('>'), el('.c')]);
|
|
560
|
+
await sel1.eval(context);
|
|
561
|
+
await sel2.eval(context);
|
|
562
|
+
let result = selectorMatch(sel2, sel1);
|
|
563
|
+
expect(result.fullMatch).toBe(false);
|
|
564
|
+
expect(result.partialMatch).toBe(false);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('does not find a match #2', async () => {
|
|
568
|
+
let sel1 = sel([el('.a'), co('>'), compound([el('.b'), el('.x')]), co('>'), el('.c')]);
|
|
569
|
+
let sel2 = sel([el('.q'), co('>'), el('.r')]);
|
|
570
|
+
await sel1.eval(context);
|
|
571
|
+
await sel2.eval(context);
|
|
572
|
+
let result = selectorMatch(sel2, sel1);
|
|
573
|
+
expect(result.fullMatch).toBe(false);
|
|
574
|
+
expect(result.partialMatch).toBe(false);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('does not find a match #3', async () => {
|
|
578
|
+
let sel1 = sel([el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
|
|
579
|
+
let sel2 = sel([el('.a'), co('>'), el('.c')]);
|
|
580
|
+
await sel1.eval(context);
|
|
581
|
+
await sel2.eval(context);
|
|
582
|
+
let result = selectorMatch(sel2, sel1);
|
|
583
|
+
expect(result.fullMatch).toBe(false);
|
|
584
|
+
expect(result.partialMatch).toBe(false);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
describe('multiple matches', () => {
|
|
589
|
+
it('can find multiple matches #1', async () => {
|
|
590
|
+
let sel1 = sel([el('.a'), co('>'), el('.c'), co('>'), el('.a'), co('>'), el('.c')]);
|
|
591
|
+
let sel2 = sel([el('.a'), co('>'), el('.c')]);
|
|
592
|
+
await sel1.eval(context);
|
|
593
|
+
await sel2.eval(context);
|
|
594
|
+
let result = selectorMatch(sel2, sel1);
|
|
595
|
+
let fullMatches = result.matches.filter(match => match.exact);
|
|
596
|
+
let partialMatches = result.matches.filter(match => !match.exact);
|
|
597
|
+
expect(result.fullMatch).toBe(false);
|
|
598
|
+
expect(result.partialMatch).toBe(true);
|
|
599
|
+
expect(fullMatches).toHaveLength(0);
|
|
600
|
+
expect(partialMatches).toHaveLength(2);
|
|
601
|
+
expect(partialMatches[0]!.startIndex).toBe(0);
|
|
602
|
+
expect(partialMatches[0]!.endIndex).toBe(2);
|
|
603
|
+
expect(partialMatches[0]!.containingNode).toBe(sel1);
|
|
604
|
+
expect(partialMatches[1]!.startIndex).toBe(4);
|
|
605
|
+
expect(partialMatches[1]!.endIndex).toBe(6);
|
|
606
|
+
expect(partialMatches[1]!.containingNode).toBe(sel1);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('can find multiple matches #2', async () => {
|
|
610
|
+
let sel1 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
|
|
611
|
+
let sel2 = compound([el('.a'), el('.b')]);
|
|
612
|
+
await sel1.eval(context);
|
|
613
|
+
await sel2.eval(context);
|
|
614
|
+
let result = selectorMatch(sel2, sel1);
|
|
615
|
+
let fullMatches = result.matches.filter(match => match.exact);
|
|
616
|
+
let partialMatches = result.matches.filter(match => !match.exact);
|
|
617
|
+
expect(result.fullMatch).toBe(false);
|
|
618
|
+
expect(result.partialMatch).toBe(true);
|
|
619
|
+
expect(fullMatches).toHaveLength(0);
|
|
620
|
+
expect(partialMatches).toHaveLength(2);
|
|
621
|
+
expect(partialMatches[0]!.startIndex).toBe(0);
|
|
622
|
+
expect(partialMatches[0]!.endIndex).toBe(1);
|
|
623
|
+
expect(partialMatches[0]!.containingNode).toBe(sel1);
|
|
624
|
+
expect(partialMatches[1]!.startIndex).toBe(3);
|
|
625
|
+
expect(partialMatches[1]!.endIndex).toBe(4);
|
|
626
|
+
expect(partialMatches[1]!.containingNode).toBe(sel1);
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('can find multiple matches #3', async () => {
|
|
630
|
+
let sel1 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
|
|
631
|
+
let sel2 = el('.a');
|
|
632
|
+
await sel1.eval(context);
|
|
633
|
+
await sel2.eval(context);
|
|
634
|
+
let result = selectorMatch(sel2, sel1);
|
|
635
|
+
let fullMatches = result.matches.filter(match => match.exact);
|
|
636
|
+
let partialMatches = result.matches.filter(match => !match.exact);
|
|
637
|
+
expect(result.fullMatch).toBe(false);
|
|
638
|
+
expect(result.partialMatch).toBe(true);
|
|
639
|
+
expect(fullMatches).toHaveLength(0);
|
|
640
|
+
expect(partialMatches).toHaveLength(2);
|
|
641
|
+
expect(partialMatches[0]!.startIndex).toBe(0);
|
|
642
|
+
expect(partialMatches[0]!.endIndex).toBe(0);
|
|
643
|
+
expect(partialMatches[0]!.containingNode).toBe(sel1);
|
|
644
|
+
expect(partialMatches[1]!.startIndex).toBe(3);
|
|
645
|
+
expect(partialMatches[1]!.endIndex).toBe(3);
|
|
646
|
+
expect(partialMatches[1]!.containingNode).toBe(sel1);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('can find multiple matches #4', async () => {
|
|
650
|
+
let sel0 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
|
|
651
|
+
let sel1 = sel([sel0, co('>'), el('.d')]);
|
|
652
|
+
let sel2 = el('.a');
|
|
653
|
+
await sel1.eval(context);
|
|
654
|
+
await sel2.eval(context);
|
|
655
|
+
let result = selectorMatch(sel2, sel1);
|
|
656
|
+
let fullMatches = result.matches.filter(match => match.exact);
|
|
657
|
+
let partialMatches = result.matches.filter(match => !match.exact);
|
|
658
|
+
expect(result.fullMatch).toBe(false);
|
|
659
|
+
expect(result.partialMatch).toBe(true);
|
|
660
|
+
expect(fullMatches).toHaveLength(0);
|
|
661
|
+
expect(partialMatches).toHaveLength(2);
|
|
662
|
+
expect(partialMatches[0]!.startIndex).toBe(0);
|
|
663
|
+
expect(partialMatches[0]!.endIndex).toBe(0);
|
|
664
|
+
expect((partialMatches[0]!.containingNode.sourceNode ?? partialMatches[0]!.containingNode)).toBe(sel0);
|
|
665
|
+
expect(partialMatches[1]!.startIndex).toBe(3);
|
|
666
|
+
expect(partialMatches[1]!.endIndex).toBe(3);
|
|
667
|
+
expect((partialMatches[1]!.containingNode.sourceNode ?? partialMatches[1]!.containingNode)).toBe(sel0);
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
it('can find multiple matches #5', async () => {
|
|
671
|
+
let sel0 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
|
|
672
|
+
let sel1 = sel([sel0, co('>'), el('.d')]);
|
|
673
|
+
let sel2 = compound([el('.b'), el('.a')]);
|
|
674
|
+
await sel1.eval(context);
|
|
675
|
+
await sel2.eval(context);
|
|
676
|
+
let result = selectorMatch(sel2, sel1);
|
|
677
|
+
let fullMatches = result.matches.filter(match => match.exact);
|
|
678
|
+
let partialMatches = result.matches.filter(match => !match.exact);
|
|
679
|
+
expect(result.fullMatch).toBe(false);
|
|
680
|
+
expect(result.partialMatch).toBe(true);
|
|
681
|
+
expect(fullMatches).toHaveLength(0);
|
|
682
|
+
expect(partialMatches).toHaveLength(2);
|
|
683
|
+
expect(partialMatches[0]!.startIndex).toBe(0);
|
|
684
|
+
expect(partialMatches[0]!.endIndex).toBe(1);
|
|
685
|
+
expect((partialMatches[0]!.containingNode.sourceNode ?? partialMatches[0]!.containingNode)).toBe(sel0);
|
|
686
|
+
expect(partialMatches[1]!.startIndex).toBe(3);
|
|
687
|
+
expect(partialMatches[1]!.endIndex).toBe(4);
|
|
688
|
+
expect((partialMatches[1]!.containingNode.sourceNode ?? partialMatches[1]!.containingNode)).toBe(sel0);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
describe('miscellaneous', () => {
|
|
693
|
+
it('can continue the search into an ampersand', async () => {
|
|
694
|
+
let sel1 = compound([amp({ selectorContainer: { selector: el('a') } }), pseudo({ name: ':hover' })]);
|
|
695
|
+
let sel2 = compound([el('a'), pseudo({ name: ':hover' })]);
|
|
696
|
+
let evald1 = await sel1.eval(context);
|
|
697
|
+
let evald2 = await sel2.eval(context);
|
|
698
|
+
let result = selectorMatch(sel2, sel1);
|
|
699
|
+
expect(`${evald1}`).toBe('&:hover');
|
|
700
|
+
expect(`${evald2}`).toBe('a:hover');
|
|
701
|
+
expect(result.fullMatch).toBe(true);
|
|
702
|
+
expect(result.partialMatch).toBe(true);
|
|
703
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
704
|
+
expect(result.matches).toHaveLength(1);
|
|
705
|
+
expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
|
|
706
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.ampersandNode).toBe(sel1.get('value')[0]);
|
|
707
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.targetSegment.containingNode).toBe(sel1);
|
|
708
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.parentSegment!.containingNode.valueOf()).toBe('a');
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('matches near an ampersand but doesn\'t cross it #1', async () => {
|
|
712
|
+
let inner = el('a').eval(context);
|
|
713
|
+
let sel1 = compound([amp({ selectorContainer: { selector: inner } }), el('.b'), pseudo({ name: ':hover' })]);
|
|
714
|
+
let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
|
|
715
|
+
let evald1 = await sel1.eval(context);
|
|
716
|
+
let evald2 = await sel2.eval(context);
|
|
717
|
+
let result = selectorMatch(sel2, sel1);
|
|
718
|
+
expect(`${evald1}`).toBe('&.b:hover');
|
|
719
|
+
expect(`${evald2}`).toBe('.b:hover');
|
|
720
|
+
expect(result.fullMatch).toBe(false);
|
|
721
|
+
expect(result.partialMatch).toBe(true);
|
|
722
|
+
expect(result.matches).toHaveLength(1);
|
|
723
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it('matches near an ampersand but doesn\'t cross it #2', async () => {
|
|
727
|
+
let inner = el('.c').eval(context);
|
|
728
|
+
let sel1 = compound([el('.b'), pseudo({ name: ':hover' }), amp({ selectorContainer: { selector: inner } }), el('.b')]);
|
|
729
|
+
let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
|
|
730
|
+
let evald1 = await sel1.eval(context);
|
|
731
|
+
let evald2 = await sel2.eval(context);
|
|
732
|
+
let result = selectorMatch(sel2, sel1);
|
|
733
|
+
expect(`${evald1}`).toBe('.b:hover&.b');
|
|
734
|
+
expect(`${evald2}`).toBe('.b:hover');
|
|
735
|
+
expect(result.fullMatch).toBe(false);
|
|
736
|
+
expect(result.partialMatch).toBe(true);
|
|
737
|
+
expect(result.matches).toHaveLength(1);
|
|
738
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
739
|
+
expect(result.matches[0]!.ampersandCrossings).toBeUndefined();
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
it('matches repeated ampersand compounds in one complex selector', async () => {
|
|
743
|
+
let parentSelector = compound([el('.a'), el('.b')]).eval(context);
|
|
744
|
+
let sel1 = sel([
|
|
745
|
+
compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')]),
|
|
746
|
+
co(' '),
|
|
747
|
+
compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')])
|
|
748
|
+
]);
|
|
749
|
+
let sel2 = compound([el('.b'), el('.x')]);
|
|
750
|
+
await sel1.eval(context);
|
|
751
|
+
await sel2.eval(context);
|
|
752
|
+
let result = selectorMatch(sel2, sel1);
|
|
753
|
+
expect(result.fullMatch).toBe(false);
|
|
754
|
+
expect(result.partialMatch).toBe(true);
|
|
755
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
756
|
+
expect(result.matches).toHaveLength(2);
|
|
757
|
+
expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
|
|
758
|
+
expect(result.matches[1]!.ampersandCrossings).toHaveLength(1);
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it('matches one repeated ampersand compound against a fully resolved compound find', async () => {
|
|
762
|
+
let parentSelector = compound([el('.a'), el('.b')]).eval(context);
|
|
763
|
+
let sel1 = sel([
|
|
764
|
+
compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')]),
|
|
765
|
+
co(' '),
|
|
766
|
+
compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')])
|
|
767
|
+
]);
|
|
768
|
+
let sel2 = compound([el('.a'), el('.b'), el('.x')]);
|
|
769
|
+
await sel1.eval(context);
|
|
770
|
+
await sel2.eval(context);
|
|
771
|
+
let result = selectorMatch(sel2, sel1, parentSelector);
|
|
772
|
+
expect(result.fullMatch).toBe(false);
|
|
773
|
+
expect(result.partialMatch).toBe(true);
|
|
774
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
775
|
+
expect(result.matches).toHaveLength(2);
|
|
776
|
+
expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
|
|
777
|
+
expect(result.matches[1]!.ampersandCrossings).toHaveLength(1);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it('matches repeated :is() compounds in one complex selector', async () => {
|
|
781
|
+
let sel1 = sel([
|
|
782
|
+
compound([is(compound([el('.a'), el('.b')])), el('.x')]),
|
|
783
|
+
co(' '),
|
|
784
|
+
compound([is(compound([el('.a'), el('.b')])), el('.x')])
|
|
785
|
+
]);
|
|
786
|
+
let sel2 = compound([el('.b'), el('.x')]);
|
|
787
|
+
await sel1.eval(context);
|
|
788
|
+
await sel2.eval(context);
|
|
789
|
+
let result = selectorMatch(sel2, sel1);
|
|
790
|
+
expect(result.fullMatch).toBe(false);
|
|
791
|
+
expect(result.partialMatch).toBe(true);
|
|
792
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
793
|
+
expect(result.matches).toHaveLength(2);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
it('can continue through a non-leading ampersand with a complex resolved parent', async () => {
|
|
797
|
+
let parentSelector = sel([el('.grand'), co('>'), el('.parent')]).eval(context);
|
|
798
|
+
let sel1 = sel([
|
|
799
|
+
amp({ selectorContainer: { selector: parentSelector } }),
|
|
800
|
+
co(' '),
|
|
801
|
+
el('.prefix'),
|
|
802
|
+
co(' '),
|
|
803
|
+
amp({ selectorContainer: { selector: parentSelector } }),
|
|
804
|
+
co(' '),
|
|
805
|
+
el('.child')
|
|
806
|
+
]);
|
|
807
|
+
let sel2 = sel([el('.grand'), co('>'), el('.parent'), co(' '), el('.child')]);
|
|
808
|
+
await sel1.eval(context);
|
|
809
|
+
await sel2.eval(context);
|
|
810
|
+
let result = selectorMatch(sel2, sel1, parentSelector);
|
|
811
|
+
expect(result.fullMatch).toBe(false);
|
|
812
|
+
expect(result.partialMatch).toBe(true);
|
|
813
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
814
|
+
expect(result.matches).toHaveLength(1);
|
|
815
|
+
expect(result.matches[0]!.startIndex).toBe(4);
|
|
816
|
+
expect(result.matches[0]!.endIndex).toBe(6);
|
|
817
|
+
expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
|
|
818
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.ampersandNode).toBe(sel1.get('value')[4]);
|
|
819
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.targetSegment.containingNode).toBe(sel1);
|
|
820
|
+
expect(
|
|
821
|
+
result.matches[0]!.ampersandCrossings![0]!.parentSegment!.containingNode.valueOf()
|
|
822
|
+
).toBe(parentSelector.valueOf());
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
describe('parent selector context', () => {
|
|
826
|
+
it('uses the parent context for a plain target selector', async () => {
|
|
827
|
+
let parent = sellist([el('div'), el('span')]);
|
|
828
|
+
let sel1 = el('.a');
|
|
829
|
+
let sel2 = sel([el('span'), co(' '), el('.a')]);
|
|
830
|
+
await parent.eval(context);
|
|
831
|
+
await sel1.eval(context);
|
|
832
|
+
await sel2.eval(context);
|
|
833
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
834
|
+
expect(result.fullMatch).toBe(true);
|
|
835
|
+
expect(result.partialMatch).toBe(true);
|
|
836
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
837
|
+
expect(result.matches).toHaveLength(1);
|
|
838
|
+
expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
|
|
839
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.targetSegment.containingNode).toBe(sel1);
|
|
840
|
+
expect(result.matches[0]!.ampersandCrossings![0]!.parentSegment!.containingNode.valueOf()).toBe('span');
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
it('uses a parent selector list as alternates across a leading ampersand', async () => {
|
|
844
|
+
let parent = sellist([el('div'), el('span')]);
|
|
845
|
+
let sel1 = sel([amp(), co(' '), el('.a')]);
|
|
846
|
+
let sel2 = sel([el('span'), co(' '), el('.a')]);
|
|
847
|
+
await parent.eval(context);
|
|
848
|
+
await sel1.eval(context);
|
|
849
|
+
await sel2.eval(context);
|
|
850
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
851
|
+
expect(result.fullMatch).toBe(true);
|
|
852
|
+
expect(result.partialMatch).toBe(true);
|
|
853
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
854
|
+
expect(result.matches).toHaveLength(1);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
it('does not search the parent when the target already fully matched before the leading ampersand', async () => {
|
|
858
|
+
let parent = sellist([el('div'), el('span')]);
|
|
859
|
+
let sel1 = sel([amp(), co(' '), el('.a')]);
|
|
860
|
+
let sel2 = el('.a');
|
|
861
|
+
await parent.eval(context);
|
|
862
|
+
await sel1.eval(context);
|
|
863
|
+
await sel2.eval(context);
|
|
864
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
865
|
+
expect(result.fullMatch).toBe(false);
|
|
866
|
+
expect(result.partialMatch).toBe(true);
|
|
867
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
868
|
+
expect(result.matches).toHaveLength(1);
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it('does not search the parent when nothing matched before the leading ampersand', async () => {
|
|
872
|
+
let parent = sellist([el('div'), el('span')]);
|
|
873
|
+
let sel1 = sel([amp(), co(' '), el('.a')]);
|
|
874
|
+
let sel2 = el('span');
|
|
875
|
+
await parent.eval(context);
|
|
876
|
+
await sel1.eval(context);
|
|
877
|
+
await sel2.eval(context);
|
|
878
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
879
|
+
expect(result.fullMatch).toBe(false);
|
|
880
|
+
expect(result.partialMatch).toBe(false);
|
|
881
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
882
|
+
expect(result.matches).toHaveLength(0);
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it('does not add matches that exist only inside an explicit ampersand selector', async () => {
|
|
886
|
+
let inner = el('a').eval(context);
|
|
887
|
+
let sel1 = compound([amp({ selectorContainer: { selector: inner } }), pseudo({ name: ':hover' })]);
|
|
888
|
+
let sel2 = el('a');
|
|
889
|
+
await sel1.eval(context);
|
|
890
|
+
await sel2.eval(context);
|
|
891
|
+
let result = selectorMatch(sel2, sel1);
|
|
892
|
+
expect(result.fullMatch).toBe(false);
|
|
893
|
+
expect(result.partialMatch).toBe(false);
|
|
894
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
895
|
+
expect(result.matches).toHaveLength(0);
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
it('does not match an implicit space combinator #1', async () => {
|
|
899
|
+
let parent = sellist([el('div'), el('span')]);
|
|
900
|
+
let sel1 = el('.a');
|
|
901
|
+
let sel2 = compound([el('span'), el('.a')]);
|
|
902
|
+
await parent.eval(context);
|
|
903
|
+
await sel1.eval(context);
|
|
904
|
+
await sel2.eval(context);
|
|
905
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
906
|
+
expect(result.fullMatch).toBe(false);
|
|
907
|
+
expect(result.partialMatch).toBe(false);
|
|
908
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
909
|
+
expect(result.matches).toHaveLength(0);
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it('does not match an implicit space combinator #2', async () => {
|
|
913
|
+
let parent = el('a');
|
|
914
|
+
let sel1 = el(':hover');
|
|
915
|
+
let sel2 = compound([el('a'), el(':hover')]);
|
|
916
|
+
await parent.eval(context);
|
|
917
|
+
await sel1.eval(context);
|
|
918
|
+
await sel2.eval(context);
|
|
919
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
920
|
+
expect(result.fullMatch).toBe(false);
|
|
921
|
+
expect(result.partialMatch).toBe(false);
|
|
922
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
923
|
+
expect(result.matches).toHaveLength(0);
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
it('does match an implicit space combinator #1', async () => {
|
|
927
|
+
let parent = el('a');
|
|
928
|
+
let sel1 = el(':hover');
|
|
929
|
+
let sel2 = sel([el('a'), co(' '), el(':hover')]);
|
|
930
|
+
await parent.eval(context);
|
|
931
|
+
await sel1.eval(context);
|
|
932
|
+
await sel2.eval(context);
|
|
933
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
934
|
+
expect(result.fullMatch).toBe(true);
|
|
935
|
+
expect(result.partialMatch).toBe(true);
|
|
936
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
937
|
+
expect(result.matches).toHaveLength(1);
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
it('does match an implicit space combinator #2', async () => {
|
|
941
|
+
let parent = sellist([el('div'), el('span')]);
|
|
942
|
+
let sel1 = el(':hover');
|
|
943
|
+
let sel2 = sel([el('div'), co(' '), el(':hover')]);
|
|
944
|
+
await parent.eval(context);
|
|
945
|
+
await sel1.eval(context);
|
|
946
|
+
await sel2.eval(context);
|
|
947
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
948
|
+
expect(result.fullMatch).toBe(true);
|
|
949
|
+
expect(result.partialMatch).toBe(true);
|
|
950
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
951
|
+
expect(result.matches).toHaveLength(1);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it('does full-match a local nested selector against a simple parent context', async () => {
|
|
955
|
+
const parent = el('.aa');
|
|
956
|
+
const target = el('.dd');
|
|
957
|
+
const find = el('.dd');
|
|
958
|
+
await parent.eval(context);
|
|
959
|
+
await target.eval(context);
|
|
960
|
+
await find.eval(context);
|
|
961
|
+
const result = selectorMatch(find, target, parent as any);
|
|
962
|
+
expect(result.fullMatch).toBe(true);
|
|
963
|
+
expect(result.partialMatch).toBe(true);
|
|
964
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
it('does full-match a nested selector across a parent selector list for an exact route', async () => {
|
|
968
|
+
const parent = sellist([
|
|
969
|
+
compound([el('.replace'), el('.replace')]),
|
|
970
|
+
sel([compound([el('.c'), el('.replace')]), co('+'), el('.replace')])
|
|
971
|
+
]);
|
|
972
|
+
const target = sellist([el('.replace'), el('.c')]);
|
|
973
|
+
const find = sel([compound([el('.replace'), el('.replace')]), co(' '), el('.replace')]);
|
|
974
|
+
await parent.eval(context);
|
|
975
|
+
await target.eval(context);
|
|
976
|
+
await find.eval(context);
|
|
977
|
+
const result = selectorMatch(find, target, parent as any);
|
|
978
|
+
expect(result.fullMatch).toBe(true);
|
|
979
|
+
expect(result.partialMatch).toBe(true);
|
|
980
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
it('does full-match a repeated implicit ampersand compound against a simple parent', async () => {
|
|
984
|
+
const parent = el('.e');
|
|
985
|
+
const target = compound([amp(), amp()]);
|
|
986
|
+
const find = compound([el('.e'), el('.e')]);
|
|
987
|
+
await parent.eval(context);
|
|
988
|
+
await target.eval(context);
|
|
989
|
+
await find.eval(context);
|
|
990
|
+
const result = selectorMatch(find, target, parent as any);
|
|
991
|
+
expect(result.fullMatch).toBe(true);
|
|
992
|
+
expect(result.partialMatch).toBe(true);
|
|
993
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
it('does not continue parent matching through a non-:is() pseudo boundary', async () => {
|
|
997
|
+
let parent = el('div');
|
|
998
|
+
let sel1 = sel([amp(), co(' '), pseudo({ name: ':where', arg: el('.a') })]);
|
|
999
|
+
let sel2 = sel([el('div'), co(' '), el('.a')]);
|
|
1000
|
+
await parent.eval(context);
|
|
1001
|
+
await sel1.eval(context);
|
|
1002
|
+
await sel2.eval(context);
|
|
1003
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
1004
|
+
expect(result.fullMatch).toBe(false);
|
|
1005
|
+
expect(result.partialMatch).toBe(false);
|
|
1006
|
+
expect(result.crossesAmpersand).toBe(false);
|
|
1007
|
+
expect(result.matches).toHaveLength(0);
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it('does continue parent matching through an :is() boundary', async () => {
|
|
1011
|
+
let parent = el('div');
|
|
1012
|
+
let sel1 = sel([amp(), co(' '), pseudo({ name: ':is', arg: el('.a') })]);
|
|
1013
|
+
let sel2 = sel([el('div'), co(' '), el('.a')]);
|
|
1014
|
+
await parent.eval(context);
|
|
1015
|
+
await sel1.eval(context);
|
|
1016
|
+
await sel2.eval(context);
|
|
1017
|
+
let result = selectorMatch(sel2, sel1, parent as any);
|
|
1018
|
+
expect(result.fullMatch).toBe(true);
|
|
1019
|
+
expect(result.partialMatch).toBe(true);
|
|
1020
|
+
expect(result.crossesAmpersand).toBe(true);
|
|
1021
|
+
expect(result.matches).toHaveLength(1);
|
|
1022
|
+
});
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
describe('implicit parent boundary with SelectorList', () => {
|
|
1027
|
+
it('matches .replace.replace .replace against SelectorList inner with SelectorList outer parent', () => {
|
|
1028
|
+
// Models the nested structure:
|
|
1029
|
+
// .replace.replace, .c.replace + .replace { .replace, .c { ... } }
|
|
1030
|
+
// find: .replace.replace .replace
|
|
1031
|
+
// target: inner SelectorList .replace, .c
|
|
1032
|
+
// parent: outer SelectorList .replace.replace, .c.replace + .replace
|
|
1033
|
+
const parent = sellist([
|
|
1034
|
+
compound([el('.replace'), el('.replace')]),
|
|
1035
|
+
sel([compound([el('.c'), el('.replace')]), co('+'), compound([el('.replace')])])
|
|
1036
|
+
]);
|
|
1037
|
+
const target = sellist([el('.replace'), el('.c')]);
|
|
1038
|
+
const find = sel([compound([el('.replace'), el('.replace')]), co(' '), el('.replace')]);
|
|
1039
|
+
|
|
1040
|
+
const result = selectorMatch(find, target, parent);
|
|
1041
|
+
expect(result.fullMatch).toBe(true);
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// // ─────────────────────────────────────────────────
|
|
1047
|
+
// // arePseudoSelectorsEquivalent
|
|
1048
|
+
// // ─────────────────────────────────────────────────
|
|
1049
|
+
// describe('arePseudoSelectorsEquivalent', () => {
|
|
1050
|
+
// it('returns false for non-pseudo-selectors', () => {
|
|
1051
|
+
// expect(arePseudoSelectorsEquivalent(el('.a'), el('.a'))).toBe(false);
|
|
1052
|
+
// });
|
|
1053
|
+
|
|
1054
|
+
// it('matches pseudo-selectors with same name and no args', () => {
|
|
1055
|
+
// const a = pseudo({ name: ':hover' });
|
|
1056
|
+
// const b = pseudo({ name: ':hover' });
|
|
1057
|
+
// expect(arePseudoSelectorsEquivalent(a, b)).toBe(true);
|
|
1058
|
+
// });
|
|
1059
|
+
|
|
1060
|
+
// it('rejects pseudo-selectors with different names', () => {
|
|
1061
|
+
// const a = pseudo({ name: ':hover' });
|
|
1062
|
+
// const b = pseudo({ name: ':focus' });
|
|
1063
|
+
// expect(arePseudoSelectorsEquivalent(a, b)).toBe(false);
|
|
1064
|
+
// });
|
|
1065
|
+
|
|
1066
|
+
// it('matches :where() with same args', () => {
|
|
1067
|
+
// const a = pseudo({ name: ':where', arg: el('.x') });
|
|
1068
|
+
// const b = pseudo({ name: ':where', arg: el('.x') });
|
|
1069
|
+
// expect(arePseudoSelectorsEquivalent(a, b)).toBe(true);
|
|
1070
|
+
// });
|
|
1071
|
+
|
|
1072
|
+
// it('rejects when one has arg and other does not', () => {
|
|
1073
|
+
// const a = pseudo({ name: ':where', arg: el('.x') });
|
|
1074
|
+
// const b = pseudo({ name: ':where' });
|
|
1075
|
+
// expect(arePseudoSelectorsEquivalent(a, b)).toBe(false);
|
|
1076
|
+
// });
|
|
1077
|
+
|
|
1078
|
+
// it('matches :not() with equivalent compound args in any order', () => {
|
|
1079
|
+
// const a = pseudo({ name: ':not', arg: compound([el('.x'), el('.y')]) });
|
|
1080
|
+
// const b = pseudo({ name: ':not', arg: compound([el('.y'), el('.x')]) });
|
|
1081
|
+
// expect(arePseudoSelectorsEquivalent(a, b)).toBe(true);
|
|
1082
|
+
// });
|
|
1083
|
+
// });
|
|
1084
|
+
|
|
1085
|
+
// // ─────────────────────────────────────────────────
|
|
1086
|
+
// // areSelectorArgumentsEquivalent
|
|
1087
|
+
// // ─────────────────────────────────────────────────
|
|
1088
|
+
// describe('areSelectorArgumentsEquivalent', () => {
|
|
1089
|
+
// it('matches identical simple selectors', () => {
|
|
1090
|
+
// expect(areSelectorArgumentsEquivalent(el('.a'), el('.a'))).toBe(true);
|
|
1091
|
+
// });
|
|
1092
|
+
|
|
1093
|
+
// it('matches selector lists in any order', () => {
|
|
1094
|
+
// const a = sellist([el('.x'), el('.y')]) as unknown as Selector;
|
|
1095
|
+
// const b = sellist([el('.y'), el('.x')]) as unknown as Selector;
|
|
1096
|
+
// expect(areSelectorArgumentsEquivalent(a, b)).toBe(true);
|
|
1097
|
+
// });
|
|
1098
|
+
|
|
1099
|
+
// it('rejects selector lists of different length', () => {
|
|
1100
|
+
// const a = sellist([el('.x'), el('.y')]) as unknown as Selector;
|
|
1101
|
+
// const b = sellist([el('.x')]) as unknown as Selector;
|
|
1102
|
+
// expect(areSelectorArgumentsEquivalent(a, b)).toBe(false);
|
|
1103
|
+
// });
|
|
1104
|
+
|
|
1105
|
+
// it('matches compound selectors', () => {
|
|
1106
|
+
// const a = compound([el('.a'), el('.b')]);
|
|
1107
|
+
// const b = compound([el('.b'), el('.a')]);
|
|
1108
|
+
// expect(areSelectorArgumentsEquivalent(a, b)).toBe(true);
|
|
1109
|
+
// });
|
|
1110
|
+
// });
|
|
1111
|
+
|
|
1112
|
+
// // ─────────────────────────────────────────────────
|
|
1113
|
+
// // areCompoundSelectorsEquivalent
|
|
1114
|
+
// // ─────────────────────────────────────────────────
|
|
1115
|
+
// describe('areCompoundSelectorsEquivalent', () => {
|
|
1116
|
+
// it('matches identical compounds', () => {
|
|
1117
|
+
// const a = compound([el('.a'), el('.b')]);
|
|
1118
|
+
// const b = compound([el('.a'), el('.b')]);
|
|
1119
|
+
// expect(areCompoundSelectorsEquivalent(a, b)).toBe(true);
|
|
1120
|
+
// });
|
|
1121
|
+
|
|
1122
|
+
// it('matches compounds in different order', () => {
|
|
1123
|
+
// const a = compound([el('.a'), el('.b')]);
|
|
1124
|
+
// const b = compound([el('.b'), el('.a')]);
|
|
1125
|
+
// expect(areCompoundSelectorsEquivalent(a, b)).toBe(true);
|
|
1126
|
+
// });
|
|
1127
|
+
|
|
1128
|
+
// it('rejects compounds of different length', () => {
|
|
1129
|
+
// const a = compound([el('.a'), el('.b')]);
|
|
1130
|
+
// const b = compound([el('.a')]);
|
|
1131
|
+
// expect(areCompoundSelectorsEquivalent(a, b)).toBe(false);
|
|
1132
|
+
// });
|
|
1133
|
+
|
|
1134
|
+
// it('matches compounds with :is() components via compoundComponentMatches', () => {
|
|
1135
|
+
// // :is(.a).b should match .a.b
|
|
1136
|
+
// const a = compound([is(sellist([el('.a')])), el('.b')]);
|
|
1137
|
+
// const b = compound([el('.a'), el('.b')]);
|
|
1138
|
+
// expect(areCompoundSelectorsEquivalent(a, b)).toBe(true);
|
|
1139
|
+
// });
|
|
1140
|
+
// });
|
|
1141
|
+
|
|
1142
|
+
// // ─────────────────────────────────────────────────
|
|
1143
|
+
// // expandCompoundWithPseudoSelectors
|
|
1144
|
+
// // ─────────────────────────────────────────────────
|
|
1145
|
+
// describe('expandCompoundWithPseudoSelectors', () => {
|
|
1146
|
+
// it('returns original for compound without :is()', () => {
|
|
1147
|
+
// const c = compound([el('.a'), el('.b')]);
|
|
1148
|
+
// const result = expandCompoundWithPseudoSelectors(c);
|
|
1149
|
+
// expect(result).toHaveLength(1);
|
|
1150
|
+
// });
|
|
1151
|
+
|
|
1152
|
+
// it('expands compound with one :is(2 alts) into 3 compounds', () => {
|
|
1153
|
+
// // .a:is(.x,.y) → [.a:is(.x,.y), .a.x, .a.y]
|
|
1154
|
+
// const c = compound([el('.a'), is(sellist([el('.x'), el('.y')]))]);
|
|
1155
|
+
// const result = expandCompoundWithPseudoSelectors(c);
|
|
1156
|
+
// // Original + 2 expanded = at least 3
|
|
1157
|
+
// expect(result.length).toBeGreaterThanOrEqual(3);
|
|
1158
|
+
// });
|
|
1159
|
+
|
|
1160
|
+
// it('expansion is combinatorial for multiple :is()', () => {
|
|
1161
|
+
// // .a:is(.x,.y):is(.p,.q) → (1+2)*(1+2) = 9
|
|
1162
|
+
// const c = compound([
|
|
1163
|
+
// el('.a'),
|
|
1164
|
+
// is(sellist([el('.x'), el('.y')])),
|
|
1165
|
+
// is(sellist([el('.p'), el('.q')]))
|
|
1166
|
+
// ]);
|
|
1167
|
+
// const result = expandCompoundWithPseudoSelectors(c);
|
|
1168
|
+
// expect(result.length).toBe(9);
|
|
1169
|
+
// });
|
|
1170
|
+
// });
|
|
1171
|
+
|
|
1172
|
+
// // ─────────────────────────────────────────────────
|
|
1173
|
+
// // expandComplexSelectorWithIs
|
|
1174
|
+
// // ─────────────────────────────────────────────────
|
|
1175
|
+
// describe('expandComplexSelectorWithIs', () => {
|
|
1176
|
+
// it('returns original for complex without :is()', () => {
|
|
1177
|
+
// const s = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
|
|
1178
|
+
// const result = expandComplexSelectorWithIs(s);
|
|
1179
|
+
// expect(result).toHaveLength(1);
|
|
1180
|
+
// });
|
|
1181
|
+
|
|
1182
|
+
// it('expands :is(.x,.y) .a into [.x .a, .y .a]', () => {
|
|
1183
|
+
// const s = sel([is(sellist([el('.x'), el('.y')])), co(' '), el('.a')]) as ComplexSelector;
|
|
1184
|
+
// const result = expandComplexSelectorWithIs(s);
|
|
1185
|
+
// expect(result).toHaveLength(2);
|
|
1186
|
+
// const values = result.map(r => r.valueOf());
|
|
1187
|
+
// expect(values).toContain('.x .a');
|
|
1188
|
+
// expect(values).toContain('.y .a');
|
|
1189
|
+
// });
|
|
1190
|
+
// });
|
|
1191
|
+
|
|
1192
|
+
// // ─────────────────────────────────────────────────
|
|
1193
|
+
// // expandSelectorWithIs
|
|
1194
|
+
// // ─────────────────────────────────────────────────
|
|
1195
|
+
// describe('expandSelectorWithIs', () => {
|
|
1196
|
+
// it('passes through simple selectors unchanged', () => {
|
|
1197
|
+
// const s = el('.a');
|
|
1198
|
+
// expect(expandSelectorWithIs(s)).toHaveLength(1);
|
|
1199
|
+
// });
|
|
1200
|
+
|
|
1201
|
+
// it('delegates to expandComplexSelectorWithIs for complex selectors', () => {
|
|
1202
|
+
// const s = sel([is(sellist([el('.x'), el('.y')])), co(' '), el('.a')]) as ComplexSelector;
|
|
1203
|
+
// const result = expandSelectorWithIs(s as any);
|
|
1204
|
+
// expect(result).toHaveLength(2);
|
|
1205
|
+
// });
|
|
1206
|
+
|
|
1207
|
+
// it('delegates to expandCompoundWithPseudoSelectors for compound selectors', () => {
|
|
1208
|
+
// const c = compound([el('.a'), is(sellist([el('.x'), el('.y')]))]);
|
|
1209
|
+
// const result = expandSelectorWithIs(c);
|
|
1210
|
+
// expect(result.length).toBeGreaterThanOrEqual(3);
|
|
1211
|
+
// });
|
|
1212
|
+
// });
|
|
1213
|
+
|
|
1214
|
+
// // ─────────────────────────────────────────────────
|
|
1215
|
+
// // areComplexSelectorsEquivalent
|
|
1216
|
+
// // ─────────────────────────────────────────────────
|
|
1217
|
+
// describe('areComplexSelectorsEquivalent', () => {
|
|
1218
|
+
// it('matches identical complex selectors', () => {
|
|
1219
|
+
// const a = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
|
|
1220
|
+
// const b = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
|
|
1221
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
|
|
1222
|
+
// });
|
|
1223
|
+
|
|
1224
|
+
// it('rejects complex selectors of different length', () => {
|
|
1225
|
+
// const a = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
|
|
1226
|
+
// const b = sel([el('.a')]) as ComplexSelector;
|
|
1227
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(false);
|
|
1228
|
+
// });
|
|
1229
|
+
|
|
1230
|
+
// it('rejects complex selectors with different combinators', () => {
|
|
1231
|
+
// const a = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
|
|
1232
|
+
// const b = sel([el('.a'), co('+'), el('.b')]) as ComplexSelector;
|
|
1233
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(false);
|
|
1234
|
+
// });
|
|
1235
|
+
|
|
1236
|
+
// it('matches complex selectors with compound components in any order', () => {
|
|
1237
|
+
// const a = sel([compound([el('.x'), el('.y')]), co('>'), el('.b')]) as ComplexSelector;
|
|
1238
|
+
// const b = sel([compound([el('.y'), el('.x')]), co('>'), el('.b')]) as ComplexSelector;
|
|
1239
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
|
|
1240
|
+
// });
|
|
1241
|
+
|
|
1242
|
+
// it('matches when one component is :is(.a) and other is .a', () => {
|
|
1243
|
+
// const a = sel([is(el('.a')), co(' '), el('.b')]) as ComplexSelector;
|
|
1244
|
+
// const b = sel([el('.a'), co(' '), el('.b')]) as ComplexSelector;
|
|
1245
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
|
|
1246
|
+
// });
|
|
1247
|
+
|
|
1248
|
+
// it('matches :is(.a,.b) against .a (picks one alternative)', () => {
|
|
1249
|
+
// const a = sel([is(sellist([el('.a'), el('.b')])), co(' '), el('.c')]) as ComplexSelector;
|
|
1250
|
+
// const b = sel([el('.a'), co(' '), el('.c')]) as ComplexSelector;
|
|
1251
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
|
|
1252
|
+
// });
|
|
1253
|
+
|
|
1254
|
+
// it('rejects :is(.a,.b) against .c (no alternative matches)', () => {
|
|
1255
|
+
// const a = sel([is(sellist([el('.a'), el('.b')])), co(' '), el('.c')]) as ComplexSelector;
|
|
1256
|
+
// const b = sel([el('.c'), co(' '), el('.c')]) as ComplexSelector;
|
|
1257
|
+
// expect(areComplexSelectorsEquivalent(a, b)).toBe(false);
|
|
1258
|
+
// });
|
|
1259
|
+
// });
|
|
1260
|
+
|
|
1261
|
+
// // ─────────────────────────────────────────────────
|
|
1262
|
+
// // isStructurallyEqual
|
|
1263
|
+
// // ─────────────────────────────────────────────────
|
|
1264
|
+
// describe('isStructurallyEqual', () => {
|
|
1265
|
+
// it('matches identical simple selectors', () => {
|
|
1266
|
+
// expect(isStructurallyEqual(el('.a'), el('.a'))).toBe(true);
|
|
1267
|
+
// });
|
|
1268
|
+
|
|
1269
|
+
// it('rejects different simple selectors', () => {
|
|
1270
|
+
// expect(isStructurallyEqual(el('.a'), el('.b'))).toBe(false);
|
|
1271
|
+
// });
|
|
1272
|
+
|
|
1273
|
+
// it('matches equivalent pseudo-selectors', () => {
|
|
1274
|
+
// const a = pseudo({ name: ':hover' });
|
|
1275
|
+
// const b = pseudo({ name: ':hover' });
|
|
1276
|
+
// expect(isStructurallyEqual(a, b)).toBe(true);
|
|
1277
|
+
// });
|
|
1278
|
+
|
|
1279
|
+
// it('rejects pseudo-selectors with different names', () => {
|
|
1280
|
+
// const a = pseudo({ name: ':hover' });
|
|
1281
|
+
// const b = pseudo({ name: ':focus' });
|
|
1282
|
+
// expect(isStructurallyEqual(a, b)).toBe(false);
|
|
1283
|
+
// });
|
|
1284
|
+
|
|
1285
|
+
// it('matches complex selectors component-by-component', () => {
|
|
1286
|
+
// const a = sel([el('.a'), co('>'), el('.b')]);
|
|
1287
|
+
// const b = sel([el('.a'), co('>'), el('.b')]);
|
|
1288
|
+
// expect(isStructurallyEqual(a as any, b as any)).toBe(true);
|
|
1289
|
+
// });
|
|
1290
|
+
|
|
1291
|
+
// it('rejects complex selectors with different components', () => {
|
|
1292
|
+
// const a = sel([el('.a'), co('>'), el('.b')]);
|
|
1293
|
+
// const b = sel([el('.a'), co('>'), el('.c')]);
|
|
1294
|
+
// expect(isStructurallyEqual(a as any, b as any)).toBe(false);
|
|
1295
|
+
// });
|
|
1296
|
+
|
|
1297
|
+
// it('matches compound selectors with same components in same order', () => {
|
|
1298
|
+
// const a = compound([el('.x'), el('.y')]);
|
|
1299
|
+
// const b = compound([el('.x'), el('.y')]);
|
|
1300
|
+
// expect(isStructurallyEqual(a, b)).toBe(true);
|
|
1301
|
+
// });
|
|
1302
|
+
// });
|
|
1303
|
+
|
|
1304
|
+
// // ─────────────────────────────────────────────────
|
|
1305
|
+
// // selectorMatchesExtendTarget
|
|
1306
|
+
// // ─────────────────────────────────────────────────
|
|
1307
|
+
// describe('selectorMatchesExtendTarget', () => {
|
|
1308
|
+
// it('matches identical simple selectors (non-partial)', () => {
|
|
1309
|
+
// expect(selectorMatchesExtendTarget(el('.a'), el('.a'), false)).toBe(true);
|
|
1310
|
+
// });
|
|
1311
|
+
|
|
1312
|
+
// it('rejects different simple selectors (non-partial)', () => {
|
|
1313
|
+
// expect(selectorMatchesExtendTarget(el('.a'), el('.b'), false)).toBe(false);
|
|
1314
|
+
// });
|
|
1315
|
+
|
|
1316
|
+
// it('matches when target is inside a SelectorList (non-partial)', () => {
|
|
1317
|
+
// const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
|
|
1318
|
+
// expect(selectorMatchesExtendTarget(target, el('.a'), false)).toBe(true);
|
|
1319
|
+
// });
|
|
1320
|
+
|
|
1321
|
+
// it('matches complex selectors (non-partial)', () => {
|
|
1322
|
+
// const a = sel([el('.a'), co('>'), el('.b')]);
|
|
1323
|
+
// const b = sel([el('.a'), co('>'), el('.b')]);
|
|
1324
|
+
// expect(selectorMatchesExtendTarget(a as any, b as any, false)).toBe(true);
|
|
1325
|
+
// });
|
|
1326
|
+
|
|
1327
|
+
// it('rejects when find is not in target (non-partial)', () => {
|
|
1328
|
+
// const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
|
|
1329
|
+
// expect(selectorMatchesExtendTarget(target, el('.c'), false)).toBe(false);
|
|
1330
|
+
// });
|
|
1331
|
+
// });
|
|
1332
|
+
|
|
1333
|
+
// // ─────────────────────────────────────────────────
|
|
1334
|
+
// // normalizeSelectorForExtend
|
|
1335
|
+
// // ─────────────────────────────────────────────────
|
|
1336
|
+
// describe('normalizeSelectorForExtend', () => {
|
|
1337
|
+
// it('passes through simple selectors unchanged', () => {
|
|
1338
|
+
// const s = el('.a');
|
|
1339
|
+
// const result = normalizeSelectorForExtend(s);
|
|
1340
|
+
// expect(result.valueOf()).toBe('.a');
|
|
1341
|
+
// });
|
|
1342
|
+
|
|
1343
|
+
// it('normalizes selector for extend matching', () => {
|
|
1344
|
+
// // Basic sanity — should not crash and should return a selector
|
|
1345
|
+
// const s = sel([el('.a'), co(' '), el('.b')]);
|
|
1346
|
+
// const result = normalizeSelectorForExtend(s as any);
|
|
1347
|
+
// expect(result).toBeTruthy();
|
|
1348
|
+
// expect(typeof result.valueOf()).toBe('string');
|
|
1349
|
+
// });
|
|
1350
|
+
// });
|
|
1351
|
+
|
|
1352
|
+
// // ─────────────────────────────────────────────────
|
|
1353
|
+
// // selectorCompare — additional edge cases
|
|
1354
|
+
// // ─────────────────────────────────────────────────
|
|
1355
|
+
// describe('selectorCompare edge cases', () => {
|
|
1356
|
+
// it('reports whole match for identical simple selectors', () => {
|
|
1357
|
+
// const result = selectorCompare(el('.a'), el('.a'));
|
|
1358
|
+
// expect(result.isEquivalent).toBe(true);
|
|
1359
|
+
// expect(result.hasWholeMatch).toBe(true);
|
|
1360
|
+
// });
|
|
1361
|
+
|
|
1362
|
+
// it('reports no match for different simple selectors', () => {
|
|
1363
|
+
// const result = selectorCompare(el('.a'), el('.b'));
|
|
1364
|
+
// expect(result.isEquivalent).toBe(false);
|
|
1365
|
+
// expect(result.hasWholeMatch).toBe(false);
|
|
1366
|
+
// });
|
|
1367
|
+
|
|
1368
|
+
// it('reports match for SelectorList containing the find', () => {
|
|
1369
|
+
// const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
|
|
1370
|
+
// const result = selectorCompare(target, el('.a'));
|
|
1371
|
+
// // selectorCompare uses findExtendableLocations which detects presence
|
|
1372
|
+
// expect(result.hasWholeMatch || result.hasPartialMatch).toBe(true);
|
|
1373
|
+
// });
|
|
1374
|
+
|
|
1375
|
+
// it('uses Set-based O(N) comparison for two SelectorLists', () => {
|
|
1376
|
+
// const a = sellist([el('.x'), el('.y'), el('.z')]) as unknown as Selector;
|
|
1377
|
+
// const b = sellist([el('.z'), el('.x'), el('.y')]) as unknown as Selector;
|
|
1378
|
+
// const result = selectorCompare(a, b);
|
|
1379
|
+
// expect(result.isEquivalent).toBe(true);
|
|
1380
|
+
// });
|
|
1381
|
+
|
|
1382
|
+
// it('rejects SelectorLists with different items', () => {
|
|
1383
|
+
// const a = sellist([el('.x'), el('.y')]) as unknown as Selector;
|
|
1384
|
+
// const b = sellist([el('.x'), el('.z')]) as unknown as Selector;
|
|
1385
|
+
// const result = selectorCompare(a, b);
|
|
1386
|
+
// expect(result.isEquivalent).toBe(false);
|
|
1387
|
+
// });
|
|
1388
|
+
// });
|
|
1389
|
+
|
|
1390
|
+
// // ─────────────────────────────────────────────────
|
|
1391
|
+
// // findExtendableLocations — additional unit tests
|
|
1392
|
+
// // ─────────────────────────────────────────────────
|
|
1393
|
+
// describe('findExtendableLocations unit tests', () => {
|
|
1394
|
+
// it('finds simple selector in SelectorList', () => {
|
|
1395
|
+
// const target = sellist([el('.a'), el('.b'), el('.c')]) as unknown as Selector;
|
|
1396
|
+
// const result = findExtendableLocations(target, el('.b'));
|
|
1397
|
+
// expect(result.hasMatches).toBe(true);
|
|
1398
|
+
// });
|
|
1399
|
+
|
|
1400
|
+
// it('returns no match when target does not contain find', () => {
|
|
1401
|
+
// const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
|
|
1402
|
+
// const result = findExtendableLocations(target, el('.z'));
|
|
1403
|
+
// expect(result.hasMatches).toBe(false);
|
|
1404
|
+
// });
|
|
1405
|
+
|
|
1406
|
+
// it('finds selector inside :is() argument', () => {
|
|
1407
|
+
// const target = is(sellist([el('.a'), el('.b')]));
|
|
1408
|
+
// const result = findExtendableLocations(target, el('.a'));
|
|
1409
|
+
// expect(result.hasMatches).toBe(true);
|
|
1410
|
+
// });
|
|
1411
|
+
|
|
1412
|
+
// it('finds compound subsequence as partial match', () => {
|
|
1413
|
+
// // .a.b.c target, .a.c find → partial match
|
|
1414
|
+
// const target = compound([el('.a'), el('.b'), el('.c')]);
|
|
1415
|
+
// const result = findExtendableLocations(target, compound([el('.a'), el('.c')]));
|
|
1416
|
+
// expect(result.hasMatches).toBe(true);
|
|
1417
|
+
// });
|
|
1418
|
+
|
|
1419
|
+
// it('caches results for identical selector+find pairs', () => {
|
|
1420
|
+
// const target = el('.cached');
|
|
1421
|
+
// const find = el('.cached');
|
|
1422
|
+
// const r1 = findExtendableLocations(target, find);
|
|
1423
|
+
// const r2 = findExtendableLocations(target, find);
|
|
1424
|
+
// // Should return the same cached object
|
|
1425
|
+
// expect(r1).toBe(r2);
|
|
1426
|
+
// });
|
|
1427
|
+
// });
|