mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-16 11:42:04 +08:00
Inital by-type split
Split type.class from CssTypeSelector to CssCompositeSelector, probably support type#id.class selectors Apply review comments, refactor css-selectors internally Applied refactoring, all tests pass, button does not notify changes Add tests for the css selectors parser. Added tests for css-selectors Added basic implementation of mayMatch and changeMap for css match state Implemented TKUnit.assertDeepEqual to check key and key/values in Map and Set Watch for property and pseudoClass changes Add one child group test Add typings for animations Added mechanism to enable/disable listeners for pseudo classes Count listeners instead of checking handlers, reverse subscription and unsubscription
This commit is contained in:
@ -227,6 +227,82 @@ export function assertEqual(actual: any, expected: any, message?: string) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Assert two json like objects are deep equal.
|
||||
*/
|
||||
export function assertDeepEqual(actual, expected, path: any[] = []): void {
|
||||
let typeofActual = typeof actual;
|
||||
let typeofExpected = typeof expected;
|
||||
if (typeofActual !== typeofExpected) {
|
||||
throw new Error("At /" + path.join("/") + " types of actual " + typeofActual + " and expected " + typeofExpected + " differ.");
|
||||
} else if (typeofActual === "object" || typeofActual === "array") {
|
||||
if (expected instanceof Map) {
|
||||
if (actual instanceof Map) {
|
||||
expected.forEach((value, key) => {
|
||||
if (actual.has(key)) {
|
||||
assertDeepEqual(actual.get(key), value, path.concat([key]));
|
||||
} else {
|
||||
throw new Error("At /" + path.join("/") + " expected Map has key '" + key + "' but actual does not.");
|
||||
}
|
||||
});
|
||||
actual.forEach((value, key) => {
|
||||
if (!expected.has(key)) {
|
||||
throw new Error("At /" + path.join("/") + " actual Map has key '" + key + "' but expected does not.");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error("At /" + path.join("/") + " expected is Map but actual is not.");
|
||||
}
|
||||
}
|
||||
if (expected instanceof Set) {
|
||||
if (actual instanceof Set) {
|
||||
expected.forEach(i => {
|
||||
if (!actual.has(i)) {
|
||||
throw new Error("At /" + path.join("/") + " expected Set has item '" + i + "' but actual does not.");
|
||||
}
|
||||
});
|
||||
actual.forEach(i => {
|
||||
if (!expected.has(i)) {
|
||||
throw new Error("At /" + path.join("/") + " actual Set has item '" + i + "' but expected does not.");
|
||||
}
|
||||
})
|
||||
} else {
|
||||
throw new Error("At /" + path.join("/") + " expected is Set but actual is not.");
|
||||
}
|
||||
}
|
||||
for (let key in actual) {
|
||||
if (!(key in expected)) {
|
||||
throw new Error("At /" + path.join("/") + " found unexpected key " + key + ".");
|
||||
}
|
||||
assertDeepEqual(actual[key], expected[key], path.concat([key]));
|
||||
}
|
||||
for (let key in expected) {
|
||||
if (!(key in actual)) {
|
||||
throw new Error("At /" + path.join("/") + " expected a key " + key + ".");
|
||||
}
|
||||
}
|
||||
} else if (actual !== expected) {
|
||||
throw new Error("At /" + path.join("/") + " actual: '" + actual + "' and expected: '" + expected + "' differ.");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertDeepSuperset(actual, expected, path: any[] = []): void {
|
||||
let typeofActual = typeof actual;
|
||||
let typeofExpected = typeof expected;
|
||||
if (typeofActual !== typeofExpected) {
|
||||
throw new Error("At /" + path.join("/") + " types of actual " + typeofActual + " and expected " + typeofExpected + " differ.");
|
||||
} else if (typeofActual === "object" || typeofActual === "array") {
|
||||
for (let key in expected) {
|
||||
if (!(key in actual)) {
|
||||
throw new Error("At /" + path.join("/") + " expected a key " + key + ".");
|
||||
}
|
||||
assertDeepSuperset(actual[key], expected[key], path.concat([key]));
|
||||
}
|
||||
} else if (actual !== expected) {
|
||||
throw new Error("At /" + path.join("/") + " actual: '" + actual + "' and expected: '" + expected + "' differ.");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNull(actual: any, message?: string) {
|
||||
if (actual !== null && actual !== undefined) {
|
||||
throw new Error(message + " Actual: " + actual + " is not null/undefined");
|
||||
|
@ -68,6 +68,8 @@ allTests["VIEW"] = require("./ui/view/view-tests");
|
||||
allTests["STYLE"] = require("./ui/styling/style-tests");
|
||||
allTests["VISUAL-STATE"] = require("./ui/styling/visual-state-tests");
|
||||
allTests["VALUE-SOURCE"] = require("./ui/styling/value-source-tests");
|
||||
allTests["CSS-SELECTOR-PARSER"] = require("./ui/styling/css-selector-parser");
|
||||
allTests["CSS-SELECTOR"] = require("./ui/styling/css-selector");
|
||||
allTests["BUTTON"] = require("./ui/button/button-tests");
|
||||
allTests["BORDER"] = require("./ui/border/border-tests");
|
||||
allTests["LABEL"] = require("./ui/label/label-tests");
|
||||
|
@ -6,7 +6,9 @@ import helper = require("../../ui/helper");
|
||||
import stackModule = require("ui/layouts/stack-layout");
|
||||
import labelModule = require("ui/label");
|
||||
import color = require("color");
|
||||
import selectorModule = require("ui/styling/css-selector");
|
||||
|
||||
import {SelectorCore} from "ui/styling/css-selector";
|
||||
|
||||
//import styling = require("ui/styling");
|
||||
|
||||
function createAnimationFromCSS(css: string, name: string): keyframeAnimation.KeyframeAnimationInfo {
|
||||
@ -15,21 +17,15 @@ function createAnimationFromCSS(css: string, name: string): keyframeAnimation.Ke
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, name);
|
||||
if (selector !== undefined) {
|
||||
let animation = selector.animations[0];
|
||||
let animation = scope.getAnimations(selector.ruleset)[0];
|
||||
return animation;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findSelectorInScope(scope: styleScope.StyleScope, name: string): selectorModule.CssSelector {
|
||||
let selector = undefined;
|
||||
for (let sel of (<any>scope)._mergedCssSelectors) {
|
||||
if (sel.expression === name) {
|
||||
selector = sel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return selector;
|
||||
function findSelectorInScope(scope: styleScope.StyleScope, cssClass: string): SelectorCore {
|
||||
let selectors = scope.query({cssClasses: new Set([cssClass])});
|
||||
return selectors[0];
|
||||
}
|
||||
|
||||
export function test_ReadAnimationProperties() {
|
||||
@ -108,7 +104,7 @@ export function test_ReadKeyframe() {
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "test");
|
||||
TKUnit.assert(selector !== undefined, "CSS selector was not created!");
|
||||
let animation = selector.animations[0];
|
||||
let animation = scope.getAnimations(selector.ruleset)[0];
|
||||
TKUnit.assertEqual(animation.name, "test", "Wrong animation name!");
|
||||
TKUnit.assertEqual(animation.keyframes.length, 2, "Keyframes not parsed correctly!");
|
||||
TKUnit.assertEqual(animation.keyframes[0].duration, 0, "First keyframe duration should be 0");
|
||||
@ -221,15 +217,15 @@ export function test_LoadTwoAnimationsWithTheSameName() {
|
||||
scope.css = "@keyframes a1 { from { opacity: 0; } to { opacity: 1; } } @keyframes a1 { from { opacity: 0; } to { opacity: 0.5; } } .a { animation-name: a1; }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "a");
|
||||
let animation = selector.animations[0];
|
||||
let animation = scope.getAnimations(selector.ruleset)[0];
|
||||
TKUnit.assertEqual(animation.keyframes.length, 2);
|
||||
TKUnit.assertEqual(animation.keyframes[1].declarations[0].value, 0.5);
|
||||
scope = new styleScope.StyleScope();
|
||||
scope.css = "@keyframes k { from { opacity: 0; } to { opacity: 1; } } .a { animation-name: k; animation-duration: 2; } .a { animation-name: k; animation-duration: 3; }";
|
||||
scope.ensureSelectors();
|
||||
selector = findSelectorInScope(scope, "a");
|
||||
TKUnit.assertEqual(selector.animations[0].keyframes.length, 2);
|
||||
TKUnit.assertEqual(selector.animations[0].keyframes.length, 2);
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset)[0].keyframes.length, 2);
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset)[0].keyframes.length, 2);
|
||||
}
|
||||
|
||||
export function test_LoadAnimationProgrammatically() {
|
||||
@ -295,11 +291,11 @@ export function test_ReadTwoAnimations() {
|
||||
scope.css = ".test { animation: one 0.2s ease-out 1 2, two 2s ease-in; }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "test");
|
||||
TKUnit.assertEqual(selector.animations.length, 2);
|
||||
TKUnit.assertEqual(selector.animations[0].curve, enums.AnimationCurve.easeOut);
|
||||
TKUnit.assertEqual(selector.animations[1].curve, enums.AnimationCurve.easeIn);
|
||||
TKUnit.assertEqual(selector.animations[1].name, "two");
|
||||
TKUnit.assertEqual(selector.animations[1].duration, 2000);
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset).length, 2);
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset)[0].curve, enums.AnimationCurve.easeOut);
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset)[1].curve, enums.AnimationCurve.easeIn);
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset)[1].name, "two");
|
||||
TKUnit.assertEqual(scope.getAnimations(selector.ruleset)[1].duration, 2000);
|
||||
}
|
||||
|
||||
export function test_AnimationCurveInKeyframes() {
|
||||
@ -307,7 +303,7 @@ export function test_AnimationCurveInKeyframes() {
|
||||
scope.css = "@keyframes an { from { animation-timing-function: linear; background-color: red; } 50% { background-color: green; } to { background-color: black; } } .test { animation-name: an; animation-timing-function: ease-in; }";
|
||||
scope.ensureSelectors();
|
||||
let selector = findSelectorInScope(scope, "test");
|
||||
let animation = selector.animations[0];
|
||||
let animation = scope.getAnimations(selector.ruleset)[0];
|
||||
TKUnit.assertEqual(animation.keyframes[0].curve, enums.AnimationCurve.linear);
|
||||
TKUnit.assertEqual(animation.keyframes[1].curve, undefined);
|
||||
TKUnit.assertEqual(animation.keyframes[1].curve, undefined);
|
||||
|
141
tests/app/ui/styling/css-selector-parser.ts
Normal file
141
tests/app/ui/styling/css-selector-parser.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import * as parser from "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" }
|
||||
]);
|
||||
}
|
235
tests/app/ui/styling/css-selector.ts
Normal file
235
tests/app/ui/styling/css-selector.ts
Normal file
@ -0,0 +1,235 @@
|
||||
import * as selector from "ui/styling/css-selector";
|
||||
import * as parser from "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<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<Node, selector.Changes>().set(prod, { attributes: new Set(["special"])} );
|
||||
TKUnit.assertDeepEqual(match.changeMap, expected);
|
||||
}
|
@ -12,7 +12,6 @@ import types = require("utils/types");
|
||||
import viewModule = require("ui/core/view");
|
||||
import styleModule = require("ui/styling/style");
|
||||
import dependencyObservableModule = require("ui/core/dependency-observable");
|
||||
import {StyleScope} from 'ui/styling/style-scope';
|
||||
|
||||
export function test_css_dataURI_is_applied_to_backgroundImageSource() {
|
||||
var stack = new stackModule.StackLayout();
|
||||
@ -358,10 +357,8 @@ export var test_composite_selector_type_and_class = function () {
|
||||
let testFunc = function (views: Array<viewModule.View>) {
|
||||
TKUnit.assert(btnWithClass.style.color, "Color property no applied correctly.");
|
||||
TKUnit.assert(btnWithClass.style.color.hex === "#FF0000", "Color property no applied correctly.");
|
||||
|
||||
TKUnit.assert(btnWithNoClass.style.color === undefined, "Color should not have a value");
|
||||
|
||||
TKUnit.assert(lblWithClass.style.color === undefined, "Color should not have a value");
|
||||
TKUnit.assert(btnWithNoClass.style.color === undefined, "btnWithNoClass color should not have a value");
|
||||
TKUnit.assert(lblWithClass.style.color === undefined, "lblWithClass color should not have a value");
|
||||
}
|
||||
|
||||
helper.buildUIAndRunTest(testStack, testFunc, testCss);
|
||||
@ -630,13 +627,6 @@ export function test_styling_properties_are_defined() {
|
||||
TKUnit.assert(types.isFunction(styling.properties.getPropertyByName), "properties.getPropertyByName function is not defined");
|
||||
}
|
||||
|
||||
export function test_styling_visualStates_are_defined() {
|
||||
TKUnit.assert(types.isDefined(styling.visualStates), "visualStates module is not defined");
|
||||
TKUnit.assert(types.isString(styling.visualStates.Hovered), "Hovered state is not defined");
|
||||
TKUnit.assert(types.isString(styling.visualStates.Normal), "Normal state is not defined");
|
||||
TKUnit.assert(types.isString(styling.visualStates.Pressed), "Pressed state is not defined");
|
||||
}
|
||||
|
||||
export function test_styling_stylers_are_defined() {
|
||||
TKUnit.assert(types.isFunction(styleModule.registerHandler), "registerHandler function is not defined");
|
||||
TKUnit.assert(types.isFunction(styleModule.StylePropertyChangedHandler), "StylePropertyChangedHandler class is not defined");
|
||||
@ -1445,16 +1435,6 @@ export function test_CascadingClassNamesAppliesAfterPageLoad() {
|
||||
});
|
||||
}
|
||||
|
||||
export function test_SortingOfCssSelectorsWithSameSpecificity() {
|
||||
let scope = new StyleScope();
|
||||
scope.css = ".button { border-color: #b2b2b2; background-color: hotpink; color: #444; margin: 5; padding: 7 2; border-width: 1; border-style: solid; border-radius: 2; text-align: center; font-size: 18; line-height: 42; } .button-small { background-color: salmon; } .button-large { font-size: 26; } .button-light { border-color: #ddd; background-color: #fff; color: #444; } .button-stable { border-color: #b2b2b2; background-color: #f8f8f8; color: #444; } .button-positive { border-color: #0c60ee; background-color: #387ef5; color: #fff; } .button-calm { border-color: #0a9dc7;background-color: #11c1f3; color: #fff; } .button-balanced { border-color: #28a54c; background-color: #33cd5f; color: #fff; } .button-energized { border-color: #e6b500; background-color: #ffc900; color: #fff; } .button-assertive { border-color: #e42112; background-color: #ef473a; color: #fff; } .button-royal { border-color: #6b46e5; background-color: #886aea; color: #fff; } .button-dark { border-color: #111; background-color: #444; color: #fff; }";
|
||||
scope.ensureSelectors();
|
||||
let expressions = [];
|
||||
(<any>scope)._mergedCssSelectors.forEach((v) => {
|
||||
expressions.push(v.expression);
|
||||
});
|
||||
TKUnit.assertTrue(expressions.indexOf('button') < expressions.indexOf('button-calm'), "button class selector should be before button-calm selector.");
|
||||
}
|
||||
// <snippet module="ui/styling" title="styling">
|
||||
// For information and example how to use style properties please refer to special [**Styling**](../../../styling.md) topic.
|
||||
// </snippet>
|
||||
|
@ -42,21 +42,3 @@ export function test_value_Local_stronger_than_Css() {
|
||||
btn.style.color = undefined;
|
||||
TKUnit.assertEqual(btn.style.color, undefined, "style.color should be undefined when set locally.");
|
||||
}
|
||||
|
||||
export var test_value_VisualState_stronger_than_Local = function () {
|
||||
let testPage = helper.getCurrentPage();
|
||||
|
||||
let testStack = new stack.StackLayout();
|
||||
testPage.content = testStack;
|
||||
|
||||
let btn = new button.Button();
|
||||
btn.style.color = new color.Color("#FF0000");
|
||||
testStack.addChild(btn);
|
||||
testPage.css = "button:pressed { color: #0000FF; }";
|
||||
|
||||
helper.assertViewColor(btn, "#FF0000");
|
||||
btn._goToVisualState("pressed");
|
||||
helper.assertViewColor(btn, "#0000FF");
|
||||
btn._goToVisualState("normal");
|
||||
helper.assertViewColor(btn, "#FF0000");
|
||||
}
|
@ -1,37 +1,17 @@
|
||||
import TKUnit = require("../../TKUnit");
|
||||
import view = require("ui/core/view");
|
||||
import page = require("ui/page");
|
||||
import vsConstants = require("ui/styling/visual-state-constants");
|
||||
import types = require("utils/types");
|
||||
import helper = require("../helper");
|
||||
|
||||
export var test_VisualStates_Parsed = function () {
|
||||
var test = function (views: Array<view.View>) {
|
||||
var page = <page.Page>views[0];
|
||||
page.css = "button:hovered { color: red; background-color: orange } button:pressed { color: white; background-color: black }";
|
||||
|
||||
var states = page._getStyleScope().getVisualStates(views[1]);
|
||||
TKUnit.assert(types.isDefined(states));
|
||||
|
||||
var counter = 0,
|
||||
hoveredFound = false,
|
||||
pressedFound = false;
|
||||
|
||||
for (var p in states) {
|
||||
counter++;
|
||||
if (p === vsConstants.Hovered) {
|
||||
hoveredFound = true;
|
||||
} else if (p === vsConstants.Pressed) {
|
||||
pressedFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
TKUnit.assert(counter === 2);
|
||||
TKUnit.assert(hoveredFound);
|
||||
TKUnit.assert(pressedFound);
|
||||
function assertInState(view: view.View, state: string, knownStates: string[]): void {
|
||||
let pseudo = view.cssPseudoClasses;
|
||||
if (state) {
|
||||
TKUnit.assert(pseudo.has(state), "Expected view " + view + " to have pseudo class " + state);
|
||||
}
|
||||
|
||||
helper.do_PageTest_WithButton(test);
|
||||
knownStates.filter(s => s !== state).forEach(s => {
|
||||
TKUnit.assert(!pseudo.has(s), "Expected view " + view + " not to have pseudo class " + s + (state ? " expected just " + state + "." : ""));
|
||||
});
|
||||
}
|
||||
|
||||
export var test_goToVisualState = function () {
|
||||
@ -40,15 +20,19 @@ export var test_goToVisualState = function () {
|
||||
|
||||
var btn = views[1];
|
||||
|
||||
assertInState(btn, null, ["hovered", "pressed"]);
|
||||
|
||||
btn._goToVisualState("hovered");
|
||||
|
||||
TKUnit.assert(btn.visualState === "hovered");
|
||||
assertInState(btn, "hovered", ["hovered", "pressed"]);
|
||||
|
||||
TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === "red");
|
||||
TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === "orange");
|
||||
|
||||
btn._goToVisualState("pressed");
|
||||
|
||||
TKUnit.assert(btn.visualState === "pressed");
|
||||
assertInState(btn, "pressed", ["hovered", "pressed"]);
|
||||
|
||||
TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === "white");
|
||||
TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === "black");
|
||||
}
|
||||
@ -61,17 +45,18 @@ export var test_goToVisualState_NoState_ShouldResetStyledProperties = function (
|
||||
(<page.Page>views[0]).css = "button:hovered { color: red; background-color: orange }";
|
||||
|
||||
var btn = views[1];
|
||||
assertInState(btn, null, ["hovered", "pressed"]);
|
||||
|
||||
btn._goToVisualState("hovered");
|
||||
|
||||
TKUnit.assert(btn.visualState === "hovered");
|
||||
assertInState(btn, "hovered", ["hovered", "pressed"]);
|
||||
TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === "red");
|
||||
TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === "orange");
|
||||
|
||||
btn._goToVisualState("pressed");
|
||||
|
||||
// since there are no modifiers for the "Pressed" state, the "Normal" state is returned.
|
||||
TKUnit.assert(btn.visualState === "normal");
|
||||
assertInState(btn, "pressed", ["hovered", "pressed"]);
|
||||
|
||||
// properties are reset (set to undefined)
|
||||
TKUnit.assert(types.isUndefined(btn.style.color));
|
||||
@ -87,16 +72,18 @@ export var test_goToVisualState_NoState_ShouldGoToNormal = function () {
|
||||
|
||||
var btn = views[1];
|
||||
|
||||
assertInState(btn, null, ["hovered", "pressed"]);
|
||||
|
||||
btn._goToVisualState("hovered");
|
||||
|
||||
TKUnit.assert(btn.visualState === "hovered");
|
||||
assertInState(btn, "hovered", ["hovered", "pressed"]);
|
||||
TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === "red");
|
||||
TKUnit.assert(types.isDefined(btn.style.backgroundColor) && btn.style.backgroundColor.name === "orange");
|
||||
|
||||
btn._goToVisualState("pressed");
|
||||
|
||||
// since there are no modifiers for the "Pressed" state, the "Normal" state is returned.
|
||||
TKUnit.assert(btn.visualState === "normal");
|
||||
assertInState(btn, "pressed", ["hovered", "pressed"]);
|
||||
|
||||
// the actual state is "normal" and properties are reverted to these settings (if any)
|
||||
TKUnit.assert(types.isDefined(btn.style.color) && btn.style.color.name === "orange");
|
||||
|
@ -455,7 +455,7 @@ export function test_parse_ShouldParseBindingToSpecialProperty() {
|
||||
p.bindingContext = obj;
|
||||
|
||||
TKUnit.assertEqual(p.content.className, classProp);
|
||||
TKUnit.assertEqual(p.content._cssClasses.length, 1);
|
||||
TKUnit.assertEqual(p.content.cssClasses.size, 1);
|
||||
};
|
||||
|
||||
export function test_parse_ShouldParseBindingsWithCommaInsideSingleQuote() {
|
||||
|
Reference in New Issue
Block a user