/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const MONTH_YEAR = ".month-year",
BTN_MONTH_YEAR = "#month-year-label",
SPINNER_MONTH = "#spinner-month",
SPINNER_YEAR = "#spinner-year";
const DATE_FORMAT = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
timeZone: "UTC",
}).format;
/**
* Helper function to check if a DOM element has a specific attribute
*
* @param {DOMElement} el: DOM Element to be tested
* @param {String} attr: The name of the attribute to be tested
*/
function testAttribute(el, attr) {
Assert.ok(
el.hasAttribute(attr),
`The "${el}" element has a "${attr}" attribute`
);
}
/**
* Helper function to check for localization attributes of a DOM element
*
* @param {DOMElement} el: DOM Element to be tested
* @param {String} id: Value of the "data-l10n-id" attribute of the element
* @param {Object} args: Args provided by the l10n object of the element
*/
function testLocalization(el, id, args = null) {
const l10nAttrs = document.l10n.getAttributes(el);
Assert.deepEqual(
l10nAttrs,
{
id,
args,
},
`The "${id}" element is localizable`
);
}
/**
* Helper function to check for l10n of an element's attribute
*
* @param {DOMElement} el: DOM Element to be tested
* @param {String} attr: The name of the attribute to be tested
* @param {String} id: Value of the "data-l10n-id" attribute of the element
* @param {Object} args: Args provided by the l10n object of the element
*/
function testAttributeL10n(el, attr, id, args = null) {
testAttribute(el, attr);
testLocalization(el, id, args);
}
/**
* Helper function to check if a CSS property respects reduced motion mode
*
* @param {DOMElement} el: DOM Element to be tested
* @param {String} prop: The name of the CSS property to be tested
* @param {Object} valueNotReduced: Default value of the tested CSS property
* for "prefers-reduced-motion: no-preference"
* @param {String} valueReduced: Value of the tested CSS property
* for "prefers-reduced-motion: reduce"
*/
async function testReducedMotionProp(el, prop, valueNotReduced, valueReduced) {
info(`Test the panel's CSS ${prop} value depending on a reduced motion mode`);
// Set "prefers-reduced-motion" media to "no-preference"
await SpecialPowers.pushPrefEnv({
set: [["ui.prefersReducedMotion", 0]],
});
ok(
matchMedia("(prefers-reduced-motion: no-preference)").matches,
"The reduce motion mode is not active"
);
is(
getComputedStyle(el).getPropertyValue(prop),
valueNotReduced,
`Default ${prop} will be provided, when a reduce motion mode is not active`
);
// Set "prefers-reduced-motion" media to "reduce"
await SpecialPowers.pushPrefEnv({
set: [["ui.prefersReducedMotion", 1]],
});
ok(
matchMedia("(prefers-reduced-motion: reduce)").matches,
"The reduce motion mode is active"
);
is(
getComputedStyle(el).getPropertyValue(prop),
valueReduced,
`Reduced ${prop} will be provided, when a reduce motion mode is active`
);
}
let helper = new DateTimeTestHelper();
registerCleanupFunction(() => {
helper.cleanup();
});
/**
* Test that the Month spinner opens with an accessible markup
*/
add_task(async function test_spinner_month_markup() {
info("Test that the Month spinner opens with an accessible markup");
const inputValue = "2022-09-09";
await helper.openPicker(
`data:text/html, `
);
helper.click(helper.getElement(MONTH_YEAR));
const spinnerMonth = helper.getElement(SPINNER_MONTH);
const spinnerMonthPrev = spinnerMonth.children[0];
const spinnerMonthBtn = spinnerMonth.children[1];
const spinnerMonthNext = spinnerMonth.children[2];
Assert.equal(
spinnerMonthPrev.tagName,
"button",
"Spinner's Previous Month control is a button"
);
Assert.equal(
spinnerMonthBtn.getAttribute("role"),
"spinbutton",
"Spinner control is a spinbutton"
);
Assert.equal(
spinnerMonthBtn.getAttribute("tabindex"),
"0",
"Spinner control is included in the focus order"
);
Assert.equal(
spinnerMonthBtn.getAttribute("aria-valuemin"),
"0",
"Spinner control has a min value set"
);
Assert.equal(
spinnerMonthBtn.getAttribute("aria-valuemax"),
"11",
"Spinner control has a max value set"
);
// September 2022 as an example
Assert.equal(
spinnerMonthBtn.getAttribute("aria-valuenow"),
"8",
"Spinner control has a current value set"
);
Assert.equal(
spinnerMonthNext.tagName,
"button",
"Spinner's Next Month control is a button"
);
testAttribute(spinnerMonthBtn, "aria-valuetext");
let visibleEls = spinnerMonthBtn.querySelectorAll(
":scope > :not([aria-hidden])"
);
Assert.equal(
visibleEls.length,
0,
"There should be no children of the spinner without aria-hidden"
);
info("Test that the month spinner has localizable labels");
testAttributeL10n(
spinnerMonthPrev,
"aria-label",
"date-spinner-month-previous"
);
testAttributeL10n(spinnerMonthBtn, "aria-label", "date-spinner-month");
testAttributeL10n(spinnerMonthNext, "aria-label", "date-spinner-month-next");
await testReducedMotionProp(
spinnerMonthBtn,
"scroll-behavior",
"smooth",
"auto"
);
await helper.tearDown();
});
/**
* Test that the Year spinner opens with an accessible markup
*/
add_task(async function test_spinner_year_markup() {
info("Test that the year spinner opens with an accessible markup");
const inputValue = "2022-06-06";
const inputMin = "2020-06-01";
const inputMax = "2030-12-31";
await helper.openPicker(
`data:text/html, `
);
helper.click(helper.getElement(MONTH_YEAR));
const spinnerYear = helper.getElement(SPINNER_YEAR);
const spinnerYearPrev = spinnerYear.children[0];
const spinnerYearBtn = spinnerYear.children[1];
const spinnerYearNext = spinnerYear.children[2];
Assert.equal(
spinnerYearPrev.tagName,
"button",
"Spinner's Previous Year control is a button"
);
Assert.equal(
spinnerYearBtn.getAttribute("role"),
"spinbutton",
"Spinner control is a spinbutton"
);
Assert.equal(
spinnerYearBtn.getAttribute("tabindex"),
"0",
"Spinner control is included in the focus order"
);
Assert.equal(
spinnerYearBtn.getAttribute("aria-valuemin"),
"2020",
"Spinner control has a min value set, when the range is provided"
);
// 2020-2030 range is an example
Assert.equal(
spinnerYearBtn.getAttribute("aria-valuemax"),
"2030",
"Spinner control has a max value set, when the range is provided"
);
// June 2022 is an example
Assert.equal(
spinnerYearBtn.getAttribute("aria-valuenow"),
"2022",
"Spinner control has a current value set"
);
Assert.equal(
spinnerYearNext.tagName,
"button",
"Spinner's Next Year control is a button"
);
testAttribute(spinnerYearBtn, "aria-valuetext");
let visibleEls = spinnerYearBtn.querySelectorAll(
":scope > :not([aria-hidden])"
);
Assert.equal(
visibleEls.length,
0,
"There should be no children of the spinner without aria-hidden"
);
info("Test that the year spinner has localizable labels");
testAttributeL10n(
spinnerYearPrev,
"aria-label",
"date-spinner-year-previous"
);
testAttributeL10n(spinnerYearBtn, "aria-label", "date-spinner-year");
testAttributeL10n(spinnerYearNext, "aria-label", "date-spinner-year-next");
await testReducedMotionProp(
spinnerYearBtn,
"scroll-behavior",
"smooth",
"auto"
);
await helper.tearDown();
});