forked from mirrors/gecko-dev
839 lines
24 KiB
JavaScript
839 lines
24 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const {
|
|
getClientCssProperties,
|
|
} = require("resource://devtools/client/fronts/css-properties.js");
|
|
|
|
add_task(async function () {
|
|
await pushPref("layout.css.backdrop-filter.enabled", true);
|
|
await pushPref("layout.css.individual-transform.enabled", true);
|
|
await pushPref("layout.css.color-mix.enabled", true);
|
|
await pushPref("layout.css.motion-path-basic-shapes.enabled", true);
|
|
await addTab("about:blank");
|
|
await performTest();
|
|
gBrowser.removeCurrentTab();
|
|
});
|
|
|
|
async function performTest() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["security.allow_unsafe_parent_loads", true]],
|
|
});
|
|
|
|
const OutputParser = require("resource://devtools/client/shared/output-parser.js");
|
|
|
|
const { host, doc } = await createHost(
|
|
"bottom",
|
|
"data:text/html," + "<h1>browser_outputParser.js</h1><div></div>"
|
|
);
|
|
|
|
const cssProperties = getClientCssProperties();
|
|
|
|
const parser = new OutputParser(doc, cssProperties);
|
|
testParseCssProperty(doc, parser);
|
|
testParseCssVar(doc, parser);
|
|
testParseURL(doc, parser);
|
|
testParseFilter(doc, parser);
|
|
testParseBackdropFilter(doc, parser);
|
|
testParseAngle(doc, parser);
|
|
testParseShape(doc, parser);
|
|
testParseVariable(doc, parser);
|
|
testParseColorVariable(doc, parser);
|
|
testParseFontFamily(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) {
|
|
const result = {
|
|
name,
|
|
value,
|
|
expected: "",
|
|
};
|
|
|
|
for (const segment of segments) {
|
|
if (typeof segment === "string") {
|
|
result.expected += segment;
|
|
} else {
|
|
const buttonAttributes = {
|
|
class: COLOR_TEST_CLASS,
|
|
style: `background-color:${segment.name}`,
|
|
tabindex: 0,
|
|
role: "button",
|
|
};
|
|
if (segment.colorFunction) {
|
|
buttonAttributes["data-color-function"] = segment.colorFunction;
|
|
}
|
|
const buttonAttrString = Object.entries(buttonAttributes)
|
|
.map(([attr, v]) => `${attr}="${v}"`)
|
|
.join(" ");
|
|
|
|
// prettier-ignore
|
|
result.expected +=
|
|
`<span data-color="${segment.name}">` +
|
|
`<span ${buttonAttrString}></span>`+
|
|
`<span>${segment.name}</span>` +
|
|
`</span>`;
|
|
}
|
|
}
|
|
|
|
result.desc = "Testing " + name + ": " + value;
|
|
|
|
return result;
|
|
}
|
|
|
|
function testParseCssProperty(doc, parser) {
|
|
const 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", colorFunction: "linear-gradient" },
|
|
" 10%, ",
|
|
{ name: "rgba(0,0,0,1)", colorFunction: "linear-gradient" },
|
|
")",
|
|
]
|
|
),
|
|
|
|
// In "arial black", "black" is a font, not a color.
|
|
// (The font-family parser creates a span)
|
|
makeColorTest("font-family", "arial black", ["<span>arial black</span>"]),
|
|
|
|
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)",
|
|
[
|
|
'<span data-filters="blur(1px) drop-shadow(0 0 0 blue) ',
|
|
'url(red.svg#blue)"><span>',
|
|
"blur(1px) drop-shadow(0 0 0 ",
|
|
{ name: "blue", colorFunction: "drop-shadow" },
|
|
") url(red.svg#blue)</span></span>",
|
|
]
|
|
),
|
|
|
|
makeColorTest("color", "currentColor", ["currentColor"]),
|
|
|
|
// Test a very long property.
|
|
makeColorTest(
|
|
"background-image",
|
|
"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%)",
|
|
[
|
|
"linear-gradient(to left, ",
|
|
{ name: "transparent", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "transparent", colorFunction: "linear-gradient" },
|
|
" 5%,",
|
|
{ name: "#F00", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#F00", colorFunction: "linear-gradient" },
|
|
" 10%,",
|
|
{ name: "#FF0", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#FF0", colorFunction: "linear-gradient" },
|
|
" 15%,",
|
|
{ name: "#0F0", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#0F0", colorFunction: "linear-gradient" },
|
|
" 20%,",
|
|
{ name: "#0FF", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#0FF", colorFunction: "linear-gradient" },
|
|
" 25%,",
|
|
{ name: "#00F", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#00F", colorFunction: "linear-gradient" },
|
|
" 30%,",
|
|
{ name: "#800", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#800", colorFunction: "linear-gradient" },
|
|
" 35%,",
|
|
{ name: "#880", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#880", colorFunction: "linear-gradient" },
|
|
" 40%,",
|
|
{ name: "#080", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#080", colorFunction: "linear-gradient" },
|
|
" 45%,",
|
|
{ name: "#088", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#088", colorFunction: "linear-gradient" },
|
|
" 50%,",
|
|
{ name: "#008", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#008", colorFunction: "linear-gradient" },
|
|
" 55%,",
|
|
{ name: "#FFF", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#FFF", colorFunction: "linear-gradient" },
|
|
" 60%,",
|
|
{ name: "#EEE", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#EEE", colorFunction: "linear-gradient" },
|
|
" 65%,",
|
|
{ name: "#CCC", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#CCC", colorFunction: "linear-gradient" },
|
|
" 70%,",
|
|
{ name: "#999", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#999", colorFunction: "linear-gradient" },
|
|
" 75%,",
|
|
{ name: "#666", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#666", colorFunction: "linear-gradient" },
|
|
" 80%,",
|
|
{ name: "#333", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#333", colorFunction: "linear-gradient" },
|
|
" 85%,",
|
|
{ name: "#111", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#111", colorFunction: "linear-gradient" },
|
|
" 90%,",
|
|
{ name: "#000", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "#000", colorFunction: "linear-gradient" },
|
|
" 95%,",
|
|
{ name: "transparent", colorFunction: "linear-gradient" },
|
|
" 0, ",
|
|
{ name: "transparent", colorFunction: "linear-gradient" },
|
|
" 100%)",
|
|
]
|
|
),
|
|
|
|
// Note the lack of a space before the color here.
|
|
makeColorTest("border", "1px dotted#f06", [
|
|
"1px dotted ",
|
|
{ name: "#f06" },
|
|
]),
|
|
|
|
makeColorTest("color", "color-mix(in srgb, red, blue)", [
|
|
"color-mix(in srgb, ",
|
|
{ name: "red", colorFunction: "color-mix" },
|
|
", ",
|
|
{ name: "blue", colorFunction: "color-mix" },
|
|
")",
|
|
]),
|
|
|
|
makeColorTest(
|
|
"background-image",
|
|
"linear-gradient(to top, color-mix(in srgb, #008000, rgba(255, 255, 0, 0.9)), blue)",
|
|
[
|
|
"linear-gradient(to top, ",
|
|
"color-mix(in srgb, ",
|
|
{ name: "#008000", colorFunction: "color-mix" },
|
|
", ",
|
|
{ name: "rgba(255, 255, 0, 0.9)", colorFunction: "color-mix" },
|
|
"), ",
|
|
{ name: "blue", colorFunction: "linear-gradient" },
|
|
")",
|
|
]
|
|
),
|
|
];
|
|
|
|
const target = doc.querySelector("div");
|
|
ok(target, "captain, we have the div");
|
|
|
|
for (const test of tests) {
|
|
info(test.desc);
|
|
|
|
const 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) {
|
|
const frag = parser.parseCssProperty("color", "var(--some-kind-of-green)", {
|
|
colorSwatchClass: "test-colorswatch",
|
|
});
|
|
|
|
const 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,<style></style>",
|
|
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 (const test of tests) {
|
|
const url = test.leader + "something.jpg" + test.trailer;
|
|
const frag = parser.parseCssProperty("background", url, {
|
|
urlClass: "test-urlclass",
|
|
baseURI: test.baseURI,
|
|
});
|
|
|
|
const target = doc.querySelector("div");
|
|
target.appendChild(frag);
|
|
|
|
const expectedTrailer = test.expectedTrailer || test.trailer;
|
|
|
|
const expected =
|
|
test.leader +
|
|
'<a target="_blank" class="test-urlclass" ' +
|
|
'href="something.jpg">something.jpg</a>' +
|
|
expectedTrailer;
|
|
|
|
is(target.innerHTML, expected, test.desc);
|
|
|
|
target.innerHTML = "";
|
|
}
|
|
}
|
|
|
|
function testParseFilter(doc, parser) {
|
|
const frag = parser.parseCssProperty("filter", "something invalid", {
|
|
filterSwatchClass: "test-filterswatch",
|
|
});
|
|
|
|
const swatchCount = frag.querySelectorAll(".test-filterswatch").length;
|
|
is(swatchCount, 1, "filter swatch was created");
|
|
}
|
|
|
|
function testParseBackdropFilter(doc, parser) {
|
|
const frag = parser.parseCssProperty("backdrop-filter", "something invalid", {
|
|
filterSwatchClass: "test-filterswatch",
|
|
});
|
|
|
|
const swatchCount = frag.querySelectorAll(".test-filterswatch").length;
|
|
is(swatchCount, 1, "filter swatch was created for backdrop-filter");
|
|
}
|
|
|
|
function testParseAngle(doc, parser) {
|
|
let frag = parser.parseCssProperty("rotate", "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");
|
|
}
|
|
|
|
function testParseShape(doc, parser) {
|
|
info("Test shape parsing");
|
|
|
|
const tests = [
|
|
{
|
|
desc: "Polygon shape",
|
|
definition:
|
|
"polygon(evenodd, 0px 0px, 10%200px,30%30% , calc(250px - 10px) 0 ,\n " +
|
|
"12em var(--variable), 100% 100%) margin-box",
|
|
spanCount: 18,
|
|
},
|
|
{
|
|
desc: "Invalid polygon shape",
|
|
definition: "polygon(0px 0px 100px 20px, 20% 20%)",
|
|
spanCount: 0,
|
|
},
|
|
{
|
|
desc: "Circle shape with all arguments",
|
|
definition: "circle(25% at\n 30% 200px) border-box",
|
|
spanCount: 4,
|
|
},
|
|
{
|
|
desc: "Circle shape with only one center",
|
|
definition: "circle(25em at 40%)",
|
|
spanCount: 3,
|
|
},
|
|
{
|
|
desc: "Circle shape with no radius",
|
|
definition: "circle(at 30% 40%)",
|
|
spanCount: 3,
|
|
},
|
|
{
|
|
desc: "Circle shape with no center",
|
|
definition: "circle(12em)",
|
|
spanCount: 1,
|
|
},
|
|
{
|
|
desc: "Circle shape with no arguments",
|
|
definition: "circle()",
|
|
spanCount: 0,
|
|
},
|
|
{
|
|
desc: "Circle shape with no space before at",
|
|
definition: "circle(25%at 30% 30%)",
|
|
spanCount: 4,
|
|
},
|
|
{
|
|
desc: "Invalid circle shape",
|
|
definition: "circle(25%at30%30%)",
|
|
spanCount: 0,
|
|
},
|
|
{
|
|
desc: "Ellipse shape with all arguments",
|
|
definition: "ellipse(200px 10em at 25% 120px) content-box",
|
|
spanCount: 5,
|
|
},
|
|
{
|
|
desc: "Ellipse shape with only one center",
|
|
definition: "ellipse(200px 10% at 120px)",
|
|
spanCount: 4,
|
|
},
|
|
{
|
|
desc: "Ellipse shape with no radius",
|
|
definition: "ellipse(at 25% 120px)",
|
|
spanCount: 3,
|
|
},
|
|
{
|
|
desc: "Ellipse shape with no center",
|
|
definition: "ellipse(200px\n10em)",
|
|
spanCount: 2,
|
|
},
|
|
{
|
|
desc: "Ellipse shape with no arguments",
|
|
definition: "ellipse()",
|
|
spanCount: 0,
|
|
},
|
|
{
|
|
desc: "Invalid ellipse shape",
|
|
definition: "ellipse(200px100px at 30$ 20%)",
|
|
spanCount: 0,
|
|
},
|
|
{
|
|
desc: "Inset shape with 4 arguments",
|
|
definition: "inset(200px 100px\n 30%15%)",
|
|
spanCount: 4,
|
|
},
|
|
{
|
|
desc: "Inset shape with 3 arguments",
|
|
definition: "inset(200px 100px 15%)",
|
|
spanCount: 3,
|
|
},
|
|
{
|
|
desc: "Inset shape with 2 arguments",
|
|
definition: "inset(200px 100px)",
|
|
spanCount: 2,
|
|
},
|
|
{
|
|
desc: "Inset shape with 1 argument",
|
|
definition: "inset(200px)",
|
|
spanCount: 1,
|
|
},
|
|
{
|
|
desc: "Inset shape with 0 arguments",
|
|
definition: "inset()",
|
|
spanCount: 0,
|
|
},
|
|
{
|
|
desc: "offset-path property with inset shape value",
|
|
property: "offset-path",
|
|
definition: "inset(200px)",
|
|
spanCount: 1,
|
|
},
|
|
];
|
|
|
|
for (const { desc, definition, property = "clip-path", spanCount } of tests) {
|
|
info(desc);
|
|
const frag = parser.parseCssProperty(property, definition, {
|
|
shapeClass: "ruleview-shape",
|
|
});
|
|
const spans = frag.querySelectorAll(".ruleview-shape-point");
|
|
is(spans.length, spanCount, desc + " span count");
|
|
is(frag.textContent, definition, desc + " text content");
|
|
}
|
|
}
|
|
|
|
function testParseVariable(doc, parser) {
|
|
const TESTS = [
|
|
{
|
|
text: "var(--seen)",
|
|
variables: { "--seen": "chartreuse" },
|
|
expected:
|
|
// prettier-ignore
|
|
'<span data-color="chartreuse">' +
|
|
"<span>var(" +
|
|
'<span data-variable="--seen = chartreuse">--seen</span>)' +
|
|
"</span>" +
|
|
"</span>",
|
|
},
|
|
{
|
|
text: "var(--not-seen)",
|
|
variables: {},
|
|
expected:
|
|
// prettier-ignore
|
|
"<span>var(" +
|
|
'<span class="unmatched-class" data-variable="--not-seen is not set">--not-seen</span>' +
|
|
")</span>",
|
|
},
|
|
{
|
|
text: "var(--seen, seagreen)",
|
|
variables: { "--seen": "chartreuse" },
|
|
expected:
|
|
// prettier-ignore
|
|
'<span data-color="chartreuse">' +
|
|
"<span>var(" +
|
|
'<span data-variable="--seen = chartreuse">--seen</span>,' +
|
|
'<span class="unmatched-class"> ' +
|
|
'<span data-color="seagreen">' +
|
|
"<span>seagreen</span>" +
|
|
"</span>" +
|
|
"</span>)" +
|
|
"</span>" +
|
|
"</span>",
|
|
},
|
|
{
|
|
text: "var(--not-seen, var(--seen))",
|
|
variables: { "--seen": "chartreuse" },
|
|
expected:
|
|
// prettier-ignore
|
|
"<span>var(" +
|
|
'<span class="unmatched-class" data-variable="--not-seen is not set">--not-seen</span>,' +
|
|
"<span> " +
|
|
'<span data-color="chartreuse">' +
|
|
"<span>var(" +
|
|
'<span data-variable="--seen = chartreuse">--seen</span>)' +
|
|
"</span>" +
|
|
"</span>" +
|
|
"</span>)" +
|
|
"</span>",
|
|
},
|
|
{
|
|
text: "color-mix(in sgrb, var(--x), purple)",
|
|
variables: { "--x": "yellow" },
|
|
expected:
|
|
// prettier-ignore
|
|
`color-mix(in sgrb, ` +
|
|
`<span data-color="yellow">` +
|
|
`<span class="test-class" style="background-color:yellow" tabindex="0" role="button" data-color-function="color-mix">` +
|
|
`</span>` +
|
|
`<span>var(<span data-variable="--x = yellow">--x</span>)</span>` +
|
|
`</span>` +
|
|
`, ` +
|
|
`<span data-color="purple">` +
|
|
`<span class="test-class" style="background-color:purple" tabindex="0" role="button" data-color-function="color-mix">` +
|
|
`</span>` +
|
|
`<span>purple</span>` +
|
|
`</span>` +
|
|
`)`,
|
|
parserExtraOptions: {
|
|
colorSwatchClass: COLOR_TEST_CLASS,
|
|
},
|
|
},
|
|
{
|
|
text: "1px solid var(--seen, seagreen)",
|
|
variables: { "--seen": "chartreuse" },
|
|
expected:
|
|
// prettier-ignore
|
|
'1px solid ' +
|
|
'<span data-color="chartreuse">' +
|
|
"<span>var(" +
|
|
'<span data-variable="--seen = chartreuse">--seen</span>,' +
|
|
'<span class="unmatched-class"> ' +
|
|
'<span data-color="seagreen">' +
|
|
"<span>seagreen</span>" +
|
|
"</span>" +
|
|
"</span>)" +
|
|
"</span>" +
|
|
"</span>",
|
|
},
|
|
{
|
|
text: "1px solid var(--not-seen, seagreen)",
|
|
variables: {},
|
|
expected:
|
|
// prettier-ignore
|
|
`1px solid ` +
|
|
`<span>var(` +
|
|
`<span class="unmatched-class" data-variable="--not-seen is not set">--not-seen</span>,` +
|
|
`<span> ` +
|
|
`<span data-color="seagreen">` +
|
|
`<span>seagreen</span>` +
|
|
`</span>` +
|
|
`</span>)` +
|
|
`</span>`,
|
|
},
|
|
];
|
|
|
|
for (const test of TESTS) {
|
|
const getValue = function (varName) {
|
|
return test.variables[varName];
|
|
};
|
|
|
|
const frag = parser.parseCssProperty("color", test.text, {
|
|
getVariableValue: getValue,
|
|
unmatchedVariableClass: "unmatched-class",
|
|
...(test.parserExtraOptions || {}),
|
|
});
|
|
|
|
const target = doc.querySelector("div");
|
|
target.appendChild(frag);
|
|
|
|
is(target.innerHTML, test.expected, test.text);
|
|
target.innerHTML = "";
|
|
}
|
|
}
|
|
|
|
function testParseColorVariable(doc, parser) {
|
|
const testCategories = [
|
|
{
|
|
desc: "Test for CSS variable defining color",
|
|
tests: [
|
|
makeColorTest("--test-var", "lime", [{ name: "lime" }]),
|
|
makeColorTest("--test-var", "#000", [{ name: "#000" }]),
|
|
],
|
|
},
|
|
{
|
|
desc: "Test for CSS variable not defining color",
|
|
tests: [
|
|
makeColorTest("--foo", "something", ["something"]),
|
|
makeColorTest("--bar", "Arial Black", ["Arial Black"]),
|
|
makeColorTest("--baz", "10vmin", ["10vmin"]),
|
|
],
|
|
},
|
|
{
|
|
desc: "Test for non CSS variable defining color",
|
|
tests: [
|
|
makeColorTest("non-css-variable", "lime", ["lime"]),
|
|
makeColorTest("-non-css-variable", "#000", ["#000"]),
|
|
],
|
|
},
|
|
];
|
|
|
|
for (const category of testCategories) {
|
|
info(category.desc);
|
|
|
|
for (const test of category.tests) {
|
|
info(test.desc);
|
|
const target = doc.querySelector("div");
|
|
|
|
const frag = parser.parseCssProperty(test.name, test.value, {
|
|
colorSwatchClass: COLOR_TEST_CLASS,
|
|
});
|
|
|
|
target.appendChild(frag);
|
|
|
|
is(
|
|
target.innerHTML,
|
|
test.expected,
|
|
`The parsed result for '${test.name}: ${test.value}' is correct`
|
|
);
|
|
|
|
target.innerHTML = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
function testParseFontFamily(doc, parser) {
|
|
info("Test font-family parsing");
|
|
const tests = [
|
|
{
|
|
desc: "No fonts",
|
|
definition: "",
|
|
families: [],
|
|
},
|
|
{
|
|
desc: "List of fonts",
|
|
definition: "Arial,Helvetica,sans-serif",
|
|
families: ["Arial", "Helvetica", "sans-serif"],
|
|
},
|
|
{
|
|
desc: "Fonts with spaces",
|
|
definition: "Open Sans",
|
|
families: ["Open Sans"],
|
|
},
|
|
{
|
|
desc: "Quoted fonts",
|
|
definition: "\"Arial\",'Open Sans'",
|
|
families: ["Arial", "Open Sans"],
|
|
},
|
|
{
|
|
desc: "Fonts with extra whitespace",
|
|
definition: " Open Sans ",
|
|
families: ["Open Sans"],
|
|
},
|
|
];
|
|
|
|
const textContentTests = [
|
|
{
|
|
desc: "No whitespace between fonts",
|
|
definition: "Arial,Helvetica,sans-serif",
|
|
output: "Arial,Helvetica,sans-serif",
|
|
},
|
|
{
|
|
desc: "Whitespace between fonts",
|
|
definition: "Arial , Helvetica, sans-serif",
|
|
output: "Arial , Helvetica, sans-serif",
|
|
},
|
|
{
|
|
desc: "Whitespace before first font trimmed",
|
|
definition: " Arial,Helvetica,sans-serif",
|
|
output: "Arial,Helvetica,sans-serif",
|
|
},
|
|
{
|
|
desc: "Whitespace after last font trimmed",
|
|
definition: "Arial,Helvetica,sans-serif ",
|
|
output: "Arial,Helvetica,sans-serif",
|
|
},
|
|
{
|
|
desc: "Whitespace between quoted fonts",
|
|
definition: "'Arial' , \"Helvetica\" ",
|
|
output: "'Arial' , \"Helvetica\"",
|
|
},
|
|
{
|
|
desc: "Whitespace within font preserved",
|
|
definition: "' Ari al '",
|
|
output: "' Ari al '",
|
|
},
|
|
];
|
|
|
|
for (const { desc, definition, families } of tests) {
|
|
info(desc);
|
|
const frag = parser.parseCssProperty("font-family", definition, {
|
|
fontFamilyClass: "ruleview-font-family",
|
|
});
|
|
const spans = frag.querySelectorAll(".ruleview-font-family");
|
|
|
|
is(spans.length, families.length, desc + " span count");
|
|
for (let i = 0; i < spans.length; i++) {
|
|
is(spans[i].textContent, families[i], desc + " span contents");
|
|
}
|
|
}
|
|
|
|
info("Test font-family text content");
|
|
for (const { desc, definition, output } of textContentTests) {
|
|
info(desc);
|
|
const frag = parser.parseCssProperty("font-family", definition, {});
|
|
is(frag.textContent, output, desc + " text content matches");
|
|
}
|
|
|
|
info("Test font-family with custom properties");
|
|
const frag = parser.parseCssProperty(
|
|
"font-family",
|
|
"var(--family, Georgia, serif)",
|
|
{
|
|
getVariableValue: () => {},
|
|
unmatchedVariableClass: "unmatched-class",
|
|
fontFamilyClass: "ruleview-font-family",
|
|
}
|
|
);
|
|
const target = doc.createElement("div");
|
|
target.appendChild(frag);
|
|
is(
|
|
target.innerHTML,
|
|
// prettier-ignore
|
|
`<span>var(` +
|
|
`<span class="unmatched-class" data-variable="--family is not set">` +
|
|
`--family` +
|
|
`</span>` +
|
|
`,` +
|
|
`<span> ` +
|
|
`<span class="ruleview-font-family">Georgia</span>` +
|
|
`, ` +
|
|
`<span class="ruleview-font-family">serif</span>` +
|
|
`</span>)` +
|
|
`</span>`,
|
|
"Got expected output for font-family with custom properties"
|
|
);
|
|
}
|