mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Housekeeping node tests, renamed to unit-tests (#4936)
Add parsers for the background css shorthand property, make ViewBase unit testable in node environment
Add background parser and linear-gradient parser
Use sticky regexes
Simplify some types, introduce generic Parsed<T> instead of & TokenRange
Apply each parser to return a { start, end, value } object
Move the css selector parser to the css/parser and unify types
Add the first steps toward building homegrown css parser
Add somewhat standards compliant tokenizer, add baseline, rework and shady css parsers
Enable all tests again, skip flaky perf test
Improve css parser tokenizer by converting some char token types to simple string
Implement 'parse a stylesheet'
Add gonzales css-parser
Add parseLib and css-tree perf
Add a thin parser layer that will convert CSS3 tokens to values, for now output is compatible with rework
Make root tsc green
Return the requires of tns-core-modules to use relative paths for webpack to work
Implement support for '@import 'url-string';
Fix function parser, function-token is no-longer neglected
Make the style-scope be able to load from "css" and "css-ast" modules
Add a loadAppCss event so theme can be added to snapshot separately from loaded
This commit is contained in:
committed by
Hristo Hristov
parent
2eba7c66e4
commit
f7a3a36b9c
@@ -148,12 +148,6 @@ allTests["VISUAL-STATE"] = visualStateTests;
|
||||
import * as valueSourceTests from "./ui/styling/value-source-tests";
|
||||
allTests["VALUE-SOURCE"] = valueSourceTests;
|
||||
|
||||
import * as cssSelectorParserTests from "./ui/styling/css-selector-parser";
|
||||
allTests["CSS-SELECTOR-PARSER"] = cssSelectorParserTests;
|
||||
|
||||
import * as cssSelectorTests from "./ui/styling/css-selector";
|
||||
allTests["CSS-SELECTOR"] = cssSelectorTests;
|
||||
|
||||
import * as buttonTests from "./ui/button/button-tests";
|
||||
allTests["BUTTON"] = buttonTests;
|
||||
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
import * as parser from "tns-core-modules/ui/styling/css-selector-parser";
|
||||
import * as TKUnit from "../../TKUnit";
|
||||
|
||||
function test(css: string, expected: {}): void {
|
||||
let result = parser.parse(css);
|
||||
TKUnit.assertDeepEqual(result, expected);
|
||||
}
|
||||
|
||||
export function test_fairly_complex_selector(): void {
|
||||
test(` listview#products.mark gridlayout:selected[row="2"] a> b > c >d>e *[src] `, [
|
||||
{ pos: 2, type: "", ident: "listview" },
|
||||
{ pos: 10, type: "#", ident: "products" },
|
||||
{ pos: 19, type: ".", ident: "mark", comb: " " },
|
||||
{ pos: 25, type: "", ident: "gridlayout" },
|
||||
{ pos: 35, type: ":", ident: "selected" },
|
||||
{ pos: 44, type: "[]", prop: "row", test: "=", value: "2", comb: " " },
|
||||
{ pos: 54, type: "", ident: "a", comb: ">" },
|
||||
{ pos: 57, type: "", ident: "b", comb: ">" },
|
||||
{ pos: 63, type: "", ident: "c", comb: ">" },
|
||||
{ pos: 66, type: "", ident: "d", comb: ">" },
|
||||
{ pos: 68, type: "", ident: "e", comb: " " },
|
||||
{ pos: 70, type: "*" },
|
||||
{ pos: 71, type: "[]", prop: "src" }
|
||||
]);
|
||||
}
|
||||
|
||||
export function test_typeguard_isUniversal(): void {
|
||||
let selector = parser.parse("*")[0];
|
||||
TKUnit.assertTrue(parser.isUniversal(selector));
|
||||
TKUnit.assertFalse(parser.isType(selector));
|
||||
TKUnit.assertFalse(parser.isClass(selector));
|
||||
TKUnit.assertFalse(parser.isId(selector));
|
||||
TKUnit.assertFalse(parser.isPseudo(selector));
|
||||
TKUnit.assertFalse(parser.isAttribute(selector));
|
||||
}
|
||||
export function test_typeguard_isType(): void {
|
||||
let selector = parser.parse("button")[0];
|
||||
TKUnit.assertFalse(parser.isUniversal(selector));
|
||||
TKUnit.assertTrue(parser.isType(selector));
|
||||
TKUnit.assertFalse(parser.isClass(selector));
|
||||
TKUnit.assertFalse(parser.isId(selector));
|
||||
TKUnit.assertFalse(parser.isPseudo(selector));
|
||||
TKUnit.assertFalse(parser.isAttribute(selector));
|
||||
}
|
||||
export function test_typeguard_isClass(): void {
|
||||
let selector = parser.parse(".login")[0];
|
||||
TKUnit.assertFalse(parser.isUniversal(selector));
|
||||
TKUnit.assertFalse(parser.isType(selector));
|
||||
TKUnit.assertTrue(parser.isClass(selector));
|
||||
TKUnit.assertFalse(parser.isId(selector));
|
||||
TKUnit.assertFalse(parser.isPseudo(selector));
|
||||
TKUnit.assertFalse(parser.isAttribute(selector));
|
||||
}
|
||||
export function test_typeguard_isId(): void {
|
||||
let selector = parser.parse("#login")[0];
|
||||
TKUnit.assertFalse(parser.isUniversal(selector));
|
||||
TKUnit.assertFalse(parser.isType(selector));
|
||||
TKUnit.assertFalse(parser.isClass(selector));
|
||||
TKUnit.assertTrue(parser.isId(selector));
|
||||
TKUnit.assertFalse(parser.isPseudo(selector));
|
||||
TKUnit.assertFalse(parser.isAttribute(selector));
|
||||
}
|
||||
export function test_typeguard_isPseudo(): void {
|
||||
let selector = parser.parse(":hover")[0];
|
||||
TKUnit.assertFalse(parser.isUniversal(selector));
|
||||
TKUnit.assertFalse(parser.isType(selector));
|
||||
TKUnit.assertFalse(parser.isClass(selector));
|
||||
TKUnit.assertFalse(parser.isId(selector));
|
||||
TKUnit.assertTrue(parser.isPseudo(selector));
|
||||
TKUnit.assertFalse(parser.isAttribute(selector));
|
||||
}
|
||||
export function test_typeguard_isAttribute(): void {
|
||||
let selector = parser.parse("[src]")[0];
|
||||
TKUnit.assertFalse(parser.isUniversal(selector));
|
||||
TKUnit.assertFalse(parser.isType(selector));
|
||||
TKUnit.assertFalse(parser.isClass(selector));
|
||||
TKUnit.assertFalse(parser.isId(selector));
|
||||
TKUnit.assertFalse(parser.isPseudo(selector));
|
||||
TKUnit.assertTrue(parser.isAttribute(selector));
|
||||
}
|
||||
|
||||
export function test_universal_selector(): void {
|
||||
test(`*`, [{ pos: 0, type: "*" }]);
|
||||
}
|
||||
export function test_type_selector(): void {
|
||||
test(`button`, [{ pos: 0, type: "", ident: "button" }]);
|
||||
}
|
||||
export function test_class_selector(): void {
|
||||
test(`.red`, [{ pos: 0, type: ".", ident: "red" }]);
|
||||
}
|
||||
export function test_id_selector(): void {
|
||||
test(`#login`, [{ pos: 0, type: "#", ident: "login" }]);
|
||||
}
|
||||
export function test_pseudoClass(): void {
|
||||
test(`:hover`, [{ pos: 0, type: ":", ident: "hover" }]);
|
||||
}
|
||||
export function test_attribute_no_value(): void {
|
||||
test(`[src]`, [{ pos: 0, type: "[]", prop: "src" }]);
|
||||
}
|
||||
export function test_attribute_equal(): void {
|
||||
test(`[src = "res://"]`, [{ pos: 0, type: "[]", prop: "src", test: "=", value: `res://` }]);
|
||||
}
|
||||
export function test_attribute_all_tests(): void {
|
||||
["=", "^=", "$=", "*=", "=", "~=", "|="].forEach(t => test(`[src ${t} "val"]`, [{ pos: 0, type: "[]", prop: "src", test: t, value: "val"}]));
|
||||
}
|
||||
export function test_direct_parent_comb(): void {
|
||||
test(`listview > .image`, [
|
||||
{ pos: 0, type: "", ident: "listview", comb: ">" },
|
||||
{ pos: 11, type: ".", ident: "image" }
|
||||
]);
|
||||
}
|
||||
export function test_ancestor_comb(): void {
|
||||
test(`listview .image`, [
|
||||
{ pos: 0, type: "", ident: "listview", comb: " " },
|
||||
{ pos: 10, type: ".", ident: "image" }
|
||||
]);
|
||||
}
|
||||
export function test_single_sequence(): void {
|
||||
test(`button:hover`, [
|
||||
{ pos: 0, type: "", ident: "button" },
|
||||
{ pos: 6, type: ":", ident: "hover" }
|
||||
]);
|
||||
}
|
||||
export function test_multiple_sequences(): void {
|
||||
test(`listview>:selected image.product`, [
|
||||
{ pos: 0, type: "", ident: "listview", comb: ">" },
|
||||
{ pos: 9, type: ":", ident: "selected", comb: " " },
|
||||
{ pos: 19, type: "", ident: "image" },
|
||||
{ pos: 24, type: ".", ident: "product" }
|
||||
]);
|
||||
}
|
||||
export function test_multiple_attribute_and_pseudo_classes(): void {
|
||||
test(`button#login[user][pass]:focused:hovered`, [
|
||||
{ pos: 0, type: "", ident: "button" },
|
||||
{ pos: 6, type: "#", ident: "login" },
|
||||
{ pos: 12, type: "[]", prop: "user" },
|
||||
{ pos: 18, type: "[]", prop: "pass" },
|
||||
{ pos: 24, type: ":", ident: "focused" },
|
||||
{ pos: 32, type: ":", ident: "hovered" }
|
||||
]);
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
import * as selector from "tns-core-modules/ui/styling/css-selector";
|
||||
import * as parser from "tns-core-modules/css";
|
||||
import * as TKUnit from "../../TKUnit";
|
||||
|
||||
function create(css: string, source: string = "css-selectors.ts@test"): { rules: selector.RuleSet[], map: selector.SelectorsMap } {
|
||||
let parse = parser.parse(css, { source });
|
||||
let rulesAst = parse.stylesheet.rules.filter(n => n.type === "rule");
|
||||
let rules = selector.fromAstNodes(rulesAst);
|
||||
let map = new selector.SelectorsMap(rules);
|
||||
return { rules, map };
|
||||
}
|
||||
|
||||
function createOne(css: string, source: string = "css-selectors.ts@test"): selector.RuleSet {
|
||||
let {rules} = create(css, source);
|
||||
TKUnit.assertEqual(rules.length, 1);
|
||||
return rules[0];
|
||||
}
|
||||
|
||||
export function test_single_selector() {
|
||||
let rule = createOne(`* { color: red; }`);
|
||||
TKUnit.assertTrue(rule.selectors[0].match({ cssType: "button" }));
|
||||
TKUnit.assertTrue(rule.selectors[0].match({ cssType: "image" }));
|
||||
}
|
||||
|
||||
export function test_two_selectors() {
|
||||
let rule = createOne(`button, image { color: red; }`);
|
||||
TKUnit.assertTrue(rule.selectors[0].match({ cssType: "button" }));
|
||||
TKUnit.assertTrue(rule.selectors[1].match({ cssType: "image" }));
|
||||
TKUnit.assertFalse(rule.selectors[0].match({ cssType: "stacklayout" }));
|
||||
TKUnit.assertFalse(rule.selectors[1].match({ cssType: "stacklayout" }));
|
||||
}
|
||||
|
||||
export function test_narrow_selection() {
|
||||
let {map} = create(`
|
||||
.login { color: blue; }
|
||||
button { color: red; }
|
||||
image { color: green; }
|
||||
`);
|
||||
|
||||
let buttonQuerry = map.query({ cssType: "button" }).selectors;
|
||||
TKUnit.assertEqual(buttonQuerry.length, 1);
|
||||
TKUnit.assertDeepSuperset(buttonQuerry[0].ruleset.declarations, [
|
||||
{ property: "color", value: "red" }
|
||||
]);
|
||||
|
||||
let imageQuerry = map.query({ cssType: "image", cssClasses: new Set(["login"]) }).selectors;
|
||||
TKUnit.assertEqual(imageQuerry.length, 2);
|
||||
// Note class before type
|
||||
TKUnit.assertDeepSuperset(imageQuerry[0].ruleset.declarations, [
|
||||
{ property: "color", value: "green" }
|
||||
]);
|
||||
TKUnit.assertDeepSuperset(imageQuerry[1].ruleset.declarations, [
|
||||
{ property: "color", value: "blue" }
|
||||
]);
|
||||
}
|
||||
|
||||
let positiveMatches = {
|
||||
"*": (view) => true,
|
||||
"type": (view) => view.cssType === "type",
|
||||
"#id": (view) => view.id === "id",
|
||||
".class": (view) => view.cssClasses.has("class"),
|
||||
":pseudo": (view) => view.cssPseudoClasses.has("pseudo"),
|
||||
"[src1]": (view) => "src1" in view,
|
||||
"[src2='src-value']": (view) => view['src2'] === 'src-value'
|
||||
}
|
||||
|
||||
let positivelyMatchingView = {
|
||||
cssType: "type",
|
||||
id: "id",
|
||||
cssClasses: new Set(["class"]),
|
||||
cssPseudoClasses: new Set(["pseudo"]),
|
||||
"src1": "src",
|
||||
"src2": "src-value"
|
||||
}
|
||||
|
||||
let negativelyMatchingView = {
|
||||
cssType: "nottype",
|
||||
id: "notid",
|
||||
cssClasses: new Set(["notclass"]),
|
||||
cssPseudoClasses: new Set(["notpseudo"]),
|
||||
// Has no "src1"
|
||||
"src2": "not-src-value"
|
||||
}
|
||||
|
||||
export function test_simple_selectors_match() {
|
||||
for (let sel in positiveMatches) {
|
||||
let css = sel + " { color: red; }";
|
||||
let rule = createOne(css);
|
||||
TKUnit.assertTrue(rule.selectors[0].match(positivelyMatchingView), "Expected successful match for: " + css);
|
||||
if (sel !== "*") {
|
||||
TKUnit.assertFalse(rule.selectors[0].match(negativelyMatchingView), "Expected match failure for: " + css);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function test_two_selector_sequence_positive_match() {
|
||||
for (let firstStr in positiveMatches) {
|
||||
for (let secondStr in positiveMatches) {
|
||||
if (secondStr !== firstStr && secondStr !== "*" && secondStr !== "type") {
|
||||
let css = firstStr + secondStr + " { color: red; }";
|
||||
let rule = createOne(css);
|
||||
TKUnit.assertTrue(rule.selectors[0].match(positivelyMatchingView), "Expected successful match for: " + css);
|
||||
if (firstStr !== "*") {
|
||||
TKUnit.assertFalse(rule.selectors[0].match(negativelyMatchingView), "Expected match failure for: " + css);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function test_direct_parent_combinator() {
|
||||
let rule = createOne(`listview > item:selected { color: red; }`);
|
||||
TKUnit.assertTrue(rule.selectors[0].match({
|
||||
cssType: "item",
|
||||
cssPseudoClasses: new Set(["selected"]),
|
||||
parent: {
|
||||
cssType: "listview"
|
||||
}
|
||||
}), "Item in list view expected to match");
|
||||
TKUnit.assertFalse(rule.selectors[0].match({
|
||||
cssType: "item",
|
||||
cssPseudoClasses: new Set(["selected"]),
|
||||
parent: {
|
||||
cssType: "stacklayout",
|
||||
parent: {
|
||||
cssType: "listview"
|
||||
}
|
||||
}
|
||||
}), "Item in stack in list view NOT expected to match.");
|
||||
}
|
||||
|
||||
export function test_ancestor_combinator() {
|
||||
let rule = createOne(`listview item:selected { color: red; }`);
|
||||
TKUnit.assertTrue(rule.selectors[0].match({
|
||||
cssType: "item",
|
||||
cssPseudoClasses: new Set(["selected"]),
|
||||
parent: {
|
||||
cssType: "listview"
|
||||
}
|
||||
}), "Item in list view expected to match");
|
||||
TKUnit.assertTrue(rule.selectors[0].match({
|
||||
cssType: "item",
|
||||
cssPseudoClasses: new Set(["selected"]),
|
||||
parent: {
|
||||
cssType: "stacklayout",
|
||||
parent: {
|
||||
cssType: "listview"
|
||||
}
|
||||
}
|
||||
}), "Item in stack in list view expected to match.");
|
||||
TKUnit.assertFalse(rule.selectors[0].match({
|
||||
cssType: "item",
|
||||
cssPseudoClasses: new Set(["selected"]),
|
||||
parent: {
|
||||
cssType: "stacklayout",
|
||||
parent: {
|
||||
cssType: "page"
|
||||
}
|
||||
}
|
||||
}), "Item in stack in page NOT expected to match.");
|
||||
}
|
||||
|
||||
export function test_backtracking_css_selector() {
|
||||
let sel = createOne(`a>b c { color: red; }`).selectors[0];
|
||||
let child = {
|
||||
cssType: "c",
|
||||
parent: {
|
||||
cssType: "b",
|
||||
parent: {
|
||||
cssType: "fail",
|
||||
parent: {
|
||||
cssType: "b",
|
||||
parent: {
|
||||
cssType: "a"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TKUnit.assertTrue(sel.match(child));
|
||||
}
|
||||
|
||||
function toString() { return this.cssType; }
|
||||
|
||||
export function test_simple_query_match() {
|
||||
let {map} = create(`list grid[promotion] button:highlighted { color: red; }`);
|
||||
|
||||
let list, grid, button;
|
||||
|
||||
button = {
|
||||
cssType: "button",
|
||||
cssPseudoClasses: new Set<string>(["highlighted"]),
|
||||
toString,
|
||||
parent: grid = {
|
||||
cssType: "grid",
|
||||
promotion: true,
|
||||
toString,
|
||||
parent: list = {
|
||||
cssType: "list",
|
||||
toString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let match = map.query(button);
|
||||
TKUnit.assertEqual(match.selectors.length, 1, "Expected match to have one selector.");
|
||||
|
||||
let expected = new Map<selector.Node, selector.Changes>()
|
||||
.set(grid, { attributes: new Set(["promotion"]) })
|
||||
.set(button, { pseudoClasses: new Set(["highlighted"]) });
|
||||
|
||||
TKUnit.assertDeepEqual(match.changeMap, expected);
|
||||
}
|
||||
|
||||
export function test_query_match_one_child_group() {
|
||||
let {map} = create(`#prod[special] > gridlayout { color: red; }`);
|
||||
let gridlayout, prod;
|
||||
|
||||
gridlayout = {
|
||||
cssType: "gridlayout",
|
||||
toString,
|
||||
parent: prod = {
|
||||
id: "prod",
|
||||
cssType: "listview",
|
||||
toString
|
||||
}
|
||||
};
|
||||
|
||||
let match = map.query(gridlayout);
|
||||
TKUnit.assertEqual(match.selectors.length, 1, "Expected match to have one selector.");
|
||||
|
||||
let expected = new Map<selector.Node, selector.Changes>().set(prod, { attributes: new Set(["special"])} );
|
||||
TKUnit.assertDeepEqual(match.changeMap, expected);
|
||||
}
|
||||
|
||||
export function test_query_match_one_sibling_group() {
|
||||
let {map} = create(`list button:highlighted+button:disabled { color: red; }`);
|
||||
let list, button, disabledButton;
|
||||
|
||||
list = {
|
||||
cssType: "list",
|
||||
toString,
|
||||
getChildIndex: () => 1,
|
||||
getChildAt: () => button
|
||||
};
|
||||
|
||||
button = {
|
||||
cssType: "button",
|
||||
cssPseudoClasses: new Set<string>(["highlighted"]),
|
||||
toString,
|
||||
parent: list
|
||||
};
|
||||
|
||||
disabledButton = {
|
||||
cssType: "button",
|
||||
cssPseudoClasses: new Set<string>(["disabled"]),
|
||||
toString,
|
||||
parent: list
|
||||
};
|
||||
|
||||
let match = map.query(disabledButton);
|
||||
TKUnit.assertEqual(match.selectors.length, 1, "Expected match to have one selector.");
|
||||
|
||||
let expected = new Map<selector.Node, selector.Changes>()
|
||||
.set(button, { pseudoClasses: new Set(["highlighted"]) })
|
||||
.set(disabledButton, { pseudoClasses: new Set(["disabled"]) });
|
||||
|
||||
TKUnit.assertDeepEqual(match.changeMap, expected);
|
||||
}
|
||||
Reference in New Issue
Block a user