Skip to content

CSS refactoring #2465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions tests/app/TKUnit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions tests/app/testRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
38 changes: 17 additions & 21 deletions tests/app/ui/animation/css-animation-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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() {
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -295,19 +291,19 @@ 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() {
let scope = new styleScope.StyleScope();
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);
Expand Down
141 changes: 141 additions & 0 deletions tests/app/ui/styling/css-selector-parser.ts
Original file line number Diff line number Diff line change
@@ -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" }
]);
}
Loading