/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test for autocomplete of CSS variables in the Rules view.
const IFRAME_URL = `https://example.org/document-builder.sjs?html=${encodeURIComponent(`
iframe
`)}`;
const TEST_URI = `https://example.org/document-builder.sjs?html=
Hello world
`;
add_task(async function () {
await pushPref("layout.css.properties-and-values.enabled", true);
await addTab(TEST_URI);
const { inspector, view } = await openRuleView();
await selectNode("h1", inspector);
info("Wait for @property panel to be displayed");
await waitFor(() =>
view.styleDocument.querySelector("#registered-properties-container")
);
const topLevelVariables = [
{ label: "--css", postLabel: "rgb(255, 0, 0)", hasColorSwatch: true },
{ label: "--js", postLabel: "gold", hasColorSwatch: true },
{ label: "--nested", postLabel: "rgb(255, 215, 0)", hasColorSwatch: true },
{
label: "--nested-with-function",
postLabel: "color-mix(in srgb, rgb(255, 0, 0) 50%, blue)",
hasColorSwatch: true,
},
{ label: "--not-registered", postLabel: "blue", hasColorSwatch: true },
];
await checkNewPropertyCssVariableAutocomplete(view, topLevelVariables);
await checkCssVariableAutocomplete(
view,
getTextProperty(view, 1, { color: "gold" }).editor.valueSpan,
topLevelVariables
);
info(
"Check that the list is correct when selecting a node from another document"
);
await selectNodeInFrames(["iframe", "h1"], inspector);
const iframeVariables = [
{ label: "--iframe" },
{
label: "--iframe-not-registered",
postLabel: "turquoise",
hasColorSwatch: true,
},
];
await checkNewPropertyCssVariableAutocomplete(view, iframeVariables);
await checkCssVariableAutocomplete(
view,
getTextProperty(view, 1, { color: "tomato" }).editor.valueSpan,
iframeVariables
);
});
async function checkNewPropertyCssVariableAutocomplete(
view,
expectedPopupItems
) {
const ruleEditor = getRuleViewRuleEditor(view, 1);
const editor = await focusNewRuleViewProperty(ruleEditor);
const onPopupOpen = editor.popup.once("popup-opened");
EventUtils.sendString("--");
await onPopupOpen;
assertEditorPopupItems(
editor,
// we don't display postLabel for the new property
expectedPopupItems.map(item => ({ label: item.label }))
);
info("Close the popup");
const onPopupClosed = once(editor.popup, "popup-closed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
await onPopupClosed;
info("Close the editor");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
}
async function checkCssVariableAutocomplete(
view,
inplaceEditorEl,
expectedPopupItems
) {
const editor = await focusEditableField(view, inplaceEditorEl);
await wait(500);
const onCloseParenthesisAppended = editor.once("after-suggest");
EventUtils.sendString("var(");
await onCloseParenthesisAppended;
let onRuleViewChanged = view.once("ruleview-changed");
EventUtils.sendString("--");
const onPopupOpen = editor.popup.once("popup-opened");
view.debounce.flush();
await onPopupOpen;
assertEditorPopupItems(editor, expectedPopupItems);
await onRuleViewChanged;
info("Close the popup");
const onPopupClosed = once(editor.popup, "popup-closed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
await onPopupClosed;
info("Cancel");
onRuleViewChanged = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
await onRuleViewChanged;
view.debounce.flush();
}
/**
* Check that the popup items are the expected ones.
*
* @param {InplaceEditor} editor
* @param {Array{Object}} expectedPopupItems
*/
function assertEditorPopupItems(editor, expectedPopupItems) {
const popupListItems = Array.from(editor.popup.list.querySelectorAll("li"));
is(
popupListItems.length,
expectedPopupItems.length,
"Popup has expected number of items"
);
popupListItems.forEach((li, i) => {
const expected = expectedPopupItems[i];
const value =
(li.querySelector(".initial-value")?.textContent ?? "") +
li.querySelector(".autocomplete-value").textContent;
is(value, expected.label, `Popup item #${i} as expected label`);
// Don't pollute test logs if we don't have the expected variable
if (value !== expected.label) {
return;
}
const postLabelEl = li.querySelector(".autocomplete-postlabel");
is(
li.querySelector(".autocomplete-postlabel")?.textContent,
expected.postLabel,
`${expected.label} has expected post label`
);
is(
!!postLabelEl?.querySelector(".autocomplete-swatch"),
!!expected.hasColorSwatch,
`${expected.label} ${
expected.hasColorSwatch ? "has" : "does not have"
} a post label color swatch`
);
});
}