/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {OutputParser} = require("devtools/client/shared/output-parser");
const {initCssProperties, getCssProperties} = require("devtools/shared/fronts/css-properties");
add_task(function* () {
yield addTab("about:blank");
yield performTest();
gBrowser.removeCurrentTab();
});
function* performTest() {
let [host, , doc] = yield createHost("bottom", "data:text/html," +
"
browser_outputParser.js
");
// Mock the toolbox that initCssProperties expect so we get the fallback css properties.
let toolbox = {target: {client: {}, hasActor: () => false}};
yield initCssProperties(toolbox);
let cssProperties = getCssProperties(toolbox);
let parser = new OutputParser(doc, cssProperties);
testParseCssProperty(doc, parser);
testParseCssVar(doc, parser);
testParseURL(doc, parser);
testParseFilter(doc, parser);
testParseAngle(doc, parser);
host.destroy();
}
// Class name used in color swatch.
var COLOR_TEST_CLASS = "test-class";
// Create a new CSS color-parsing test. |name| is the name of the CSS
// property. |value| is the CSS text to use. |segments| is an array
// describing the expected result. If an element of |segments| is a
// string, it is simply appended to the expected string. Otherwise,
// it must be an object with a |name| property, which is the color
// name as it appears in the input.
//
// This approach is taken to reduce boilerplate and to make it simpler
// to modify the test when the parseCssProperty output changes.
function makeColorTest(name, value, segments) {
let result = {
name,
value,
expected: ""
};
for (let segment of segments) {
if (typeof (segment) === "string") {
result.expected += segment;
} else {
result.expected += "" +
"" +
segment.name + "";
}
}
result.desc = "Testing " + name + ": " + value;
return result;
}
function testParseCssProperty(doc, parser) {
let tests = [
makeColorTest("border", "1px solid red",
["1px solid ", {name: "red"}]),
makeColorTest("background-image",
"linear-gradient(to right, #F60 10%, rgba(0,0,0,1))",
["linear-gradient(to right, ", {name: "#F60"},
" 10%, ", {name: "rgba(0,0,0,1)"},
")"]),
// In "arial black", "black" is a font, not a color.
makeColorTest("font-family", "arial black", ["arial black"]),
makeColorTest("box-shadow", "0 0 1em red",
["0 0 1em ", {name: "red"}]),
makeColorTest("box-shadow",
"0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)",
["0 0 1em ", {name: "red"},
", 2px 2px 0 0 ",
{name: "rgba(0,0,0,.5)"}]),
makeColorTest("content", "\"red\"", ["\"red\""]),
// Invalid property names should not cause exceptions.
makeColorTest("hellothere", "'red'", ["'red'"]),
makeColorTest("filter",
"blur(1px) drop-shadow(0 0 0 blue) url(red.svg#blue)",
["",
"blur(1px) drop-shadow(0 0 0 ",
{name: "blue"},
") url(red.svg#blue)"]),
makeColorTest("color", "currentColor", ["currentColor"]),
// Test a very long property.
makeColorTest("background-image",
/* eslint-disable max-len */
"linear-gradient(to left, transparent 0, transparent 5%,#F00 0, #F00 10%,#FF0 0, #FF0 15%,#0F0 0, #0F0 20%,#0FF 0, #0FF 25%,#00F 0, #00F 30%,#800 0, #800 35%,#880 0, #880 40%,#080 0, #080 45%,#088 0, #088 50%,#008 0, #008 55%,#FFF 0, #FFF 60%,#EEE 0, #EEE 65%,#CCC 0, #CCC 70%,#999 0, #999 75%,#666 0, #666 80%,#333 0, #333 85%,#111 0, #111 90%,#000 0, #000 95%,transparent 0, transparent 100%)",
/* eslint-enable max-len */
["linear-gradient(to left, ", {name: "transparent"},
" 0, ", {name: "transparent"},
" 5%,", {name: "#F00"},
" 0, ", {name: "#F00"},
" 10%,", {name: "#FF0"},
" 0, ", {name: "#FF0"},
" 15%,", {name: "#0F0"},
" 0, ", {name: "#0F0"},
" 20%,", {name: "#0FF"},
" 0, ", {name: "#0FF"},
" 25%,", {name: "#00F"},
" 0, ", {name: "#00F"},
" 30%,", {name: "#800"},
" 0, ", {name: "#800"},
" 35%,", {name: "#880"},
" 0, ", {name: "#880"},
" 40%,", {name: "#080"},
" 0, ", {name: "#080"},
" 45%,", {name: "#088"},
" 0, ", {name: "#088"},
" 50%,", {name: "#008"},
" 0, ", {name: "#008"},
" 55%,", {name: "#FFF"},
" 0, ", {name: "#FFF"},
" 60%,", {name: "#EEE"},
" 0, ", {name: "#EEE"},
" 65%,", {name: "#CCC"},
" 0, ", {name: "#CCC"},
" 70%,", {name: "#999"},
" 0, ", {name: "#999"},
" 75%,", {name: "#666"},
" 0, ", {name: "#666"},
" 80%,", {name: "#333"},
" 0, ", {name: "#333"},
" 85%,", {name: "#111"},
" 0, ", {name: "#111"},
" 90%,", {name: "#000"},
" 0, ", {name: "#000"},
" 95%,", {name: "transparent"},
" 0, ", {name: "transparent"},
" 100%)"]),
];
let target = doc.querySelector("div");
ok(target, "captain, we have the div");
for (let test of tests) {
info(test.desc);
let frag = parser.parseCssProperty(test.name, test.value, {
colorSwatchClass: COLOR_TEST_CLASS
});
target.appendChild(frag);
is(target.innerHTML, test.expected,
"CSS property correctly parsed for " + test.name + ": " + test.value);
target.innerHTML = "";
}
}
function testParseCssVar(doc, parser) {
let frag = parser.parseCssProperty("color", "var(--some-kind-of-green)", {
colorSwatchClass: "test-colorswatch"
});
let target = doc.querySelector("div");
ok(target, "captain, we have the div");
target.appendChild(frag);
is(target.innerHTML, "var(--some-kind-of-green)",
"CSS property correctly parsed");
target.innerHTML = "";
}
function testParseURL(doc, parser) {
info("Test that URL parsing preserves quoting style");
const tests = [
{
desc: "simple test without quotes",
leader: "url(",
trailer: ")",
},
{
desc: "simple test with single quotes",
leader: "url('",
trailer: "')",
},
{
desc: "simple test with double quotes",
leader: "url(\"",
trailer: "\")",
},
{
desc: "test with single quotes and whitespace",
leader: "url( \t'",
trailer: "'\r\n\f)",
},
{
desc: "simple test with uppercase",
leader: "URL(",
trailer: ")",
},
{
desc: "bad url, missing paren",
leader: "url(",
trailer: "",
expectedTrailer: ")"
},
{
desc: "bad url, missing paren, with baseURI",
baseURI: "data:text/html,",
leader: "url(",
trailer: "",
expectedTrailer: ")"
},
{
desc: "bad url, double quote, missing paren",
leader: "url(\"",
trailer: "\"",
expectedTrailer: "\")",
},
{
desc: "bad url, single quote, missing paren and quote",
leader: "url('",
trailer: "",
expectedTrailer: "')"
}
];
for (let test of tests) {
let url = test.leader + "something.jpg" + test.trailer;
let frag = parser.parseCssProperty("background", url, {
urlClass: "test-urlclass",
baseURI: test.baseURI,
});
let target = doc.querySelector("div");
target.appendChild(frag);
let expectedTrailer = test.expectedTrailer || test.trailer;
let expected = test.leader +
"something.jpg" +
expectedTrailer;
is(target.innerHTML, expected, test.desc);
target.innerHTML = "";
}
}
function testParseFilter(doc, parser) {
let frag = parser.parseCssProperty("filter", "something invalid", {
filterSwatchClass: "test-filterswatch"
});
let swatchCount = frag.querySelectorAll(".test-filterswatch").length;
is(swatchCount, 1, "filter swatch was created");
}
function testParseAngle(doc, parser) {
let frag = parser.parseCssProperty("image-orientation", "90deg", {
angleSwatchClass: "test-angleswatch"
});
let swatchCount = frag.querySelectorAll(".test-angleswatch").length;
is(swatchCount, 1, "angle swatch was created");
frag = parser.parseCssProperty("background-image",
"linear-gradient(90deg, red, blue", {
angleSwatchClass: "test-angleswatch"
});
swatchCount = frag.querySelectorAll(".test-angleswatch").length;
is(swatchCount, 1, "angle swatch was created");
}