mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-09-09 04:54:33 +00:00
245 lines
7.5 KiB
HTML
245 lines
7.5 KiB
HTML
<!DOCTYPE HTML>
|
|
<html>
|
|
<!--
|
|
https://bugzilla.mozilla.org/show_bug.cgi?id=935506
|
|
-->
|
|
<head>
|
|
<title>Test key events for number control</title>
|
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
<meta charset="UTF-8">
|
|
</head>
|
|
<body>
|
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=935506">Mozilla Bug 935506</a>
|
|
<p id="display"></p>
|
|
<div id="content">
|
|
<input id="input" type="number">
|
|
</div>
|
|
<pre id="test">
|
|
<script type="application/javascript">
|
|
|
|
/**
|
|
* Test for Bug 935506
|
|
* This test checks how the value of <input type=number> changes in response to
|
|
* key events while it is in various states.
|
|
**/
|
|
SimpleTest.waitForExplicitFinish();
|
|
// Turn off Spatial Navigation because it hijacks arrow keydown events:
|
|
SimpleTest.waitForFocus(function() {
|
|
SpecialPowers.pushPrefEnv({"set":[["snav.enabled", false]]}, function() {
|
|
test();
|
|
SimpleTest.finish();
|
|
});
|
|
});
|
|
const defaultMinimum = "NaN";
|
|
const defaultMaximum = "NaN";
|
|
const defaultStep = 1;
|
|
|
|
// Helpers:
|
|
// For the sake of simplicity, we do not currently support fractional value,
|
|
// step, etc.
|
|
|
|
function getMinimum(element) {
|
|
return Number(element.min || defaultMinimum);
|
|
}
|
|
|
|
function getMaximum(element) {
|
|
return Number(element.max || defaultMaximum);
|
|
}
|
|
|
|
function getDefaultValue(element) {
|
|
return 0;
|
|
}
|
|
|
|
function getValue(element) {
|
|
return Number(element.value || getDefaultValue(element));
|
|
}
|
|
|
|
function getStep(element) {
|
|
if (element.step == "any") {
|
|
return "any";
|
|
}
|
|
var step = Number(element.step || defaultStep);
|
|
return step <= 0 ? defaultStep : step;
|
|
}
|
|
|
|
function getStepBase(element) {
|
|
return Number(element.getAttribute("min") || "NaN") ||
|
|
Number(element.getAttribute("value") || "NaN") || 0;
|
|
}
|
|
|
|
function hasStepMismatch(element) {
|
|
var value = element.value;
|
|
if (value == "") {
|
|
value = 0;
|
|
}
|
|
var step = getStep(element);
|
|
if (step == "any") {
|
|
return false;
|
|
}
|
|
return ((value - getStepBase(element)) % step) != 0;
|
|
}
|
|
|
|
function floorModulo(x, y) {
|
|
return (x - y * Math.floor(x / y));
|
|
}
|
|
|
|
function expectedValueAfterStepUpOrDown(stepFactor, element) {
|
|
var value = getValue(element);
|
|
if (isNaN(value)) {
|
|
value = 0;
|
|
}
|
|
var step = getStep(element);
|
|
if (step == "any") {
|
|
step = 1;
|
|
}
|
|
|
|
var minimum = getMinimum(element);
|
|
var maximum = getMaximum(element);
|
|
if (!isNaN(maximum)) {
|
|
// "max - (max - stepBase) % step" is the nearest valid value to max.
|
|
maximum = maximum - floorModulo(maximum - getStepBase(element), step);
|
|
}
|
|
|
|
// Cases where we are clearly going in the wrong way.
|
|
// We don't use ValidityState because we can be higher than the maximal
|
|
// allowed value and still not suffer from range overflow in the case of
|
|
// of the value specified in @max isn't in the step.
|
|
if ((value <= minimum && stepFactor < 0) ||
|
|
(value >= maximum && stepFactor > 0)) {
|
|
return value;
|
|
}
|
|
|
|
if (hasStepMismatch(element) &&
|
|
value != minimum && value != maximum) {
|
|
if (stepFactor > 0) {
|
|
value -= floorModulo(value - getStepBase(element), step);
|
|
} else if (stepFactor < 0) {
|
|
value -= floorModulo(value - getStepBase(element), step);
|
|
value += step;
|
|
}
|
|
}
|
|
|
|
value += step * stepFactor;
|
|
|
|
// When stepUp() is called and the value is below minimum, we should clamp on
|
|
// minimum unless stepUp() moves us higher than minimum.
|
|
if (element.validity.rangeUnderflow && stepFactor > 0 &&
|
|
value <= minimum) {
|
|
value = minimum;
|
|
} else if (element.validity.rangeOverflow && stepFactor < 0 &&
|
|
value >= maximum) {
|
|
value = maximum;
|
|
} else if (stepFactor < 0 && !isNaN(minimum)) {
|
|
value = Math.max(value, minimum);
|
|
} else if (stepFactor > 0 && !isNaN(maximum)) {
|
|
value = Math.min(value, maximum);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function expectedValAfterKeyEvent(key, element) {
|
|
return expectedValueAfterStepUpOrDown(key == "VK_UP" ? 1 : -1, element);
|
|
}
|
|
|
|
function test() {
|
|
var elem = document.getElementById("input");
|
|
elem.focus();
|
|
|
|
elem.min = -5;
|
|
elem.max = 5;
|
|
elem.step = 2;
|
|
var defaultValue = 0;
|
|
var oldVal, expectedVal;
|
|
|
|
for (key of ["VK_UP", "VK_DOWN"]) {
|
|
// Start at middle:
|
|
oldVal = elem.value = -1;
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test " + key + " for number control with value set between min/max (" + oldVal + ")");
|
|
|
|
// Same again:
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
|
|
|
|
// Start at maximum:
|
|
oldVal = elem.value = elem.max;
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the maximum (" + oldVal + ")");
|
|
|
|
// Same again:
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
|
|
|
|
// Start at minimum:
|
|
oldVal = elem.value = elem.min;
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the minimum (" + oldVal + ")");
|
|
|
|
// Same again:
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
|
|
|
|
// Test preventDefault():
|
|
elem.addEventListener("keypress", function(evt) {
|
|
evt.preventDefault();
|
|
elem.removeEventListener("keypress", arguments.callee, false);
|
|
}, false);
|
|
oldVal = elem.value = 0;
|
|
expectedVal = 0;
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test " + key + " for number control where scripted preventDefault() should prevent the value changing");
|
|
|
|
// Test step="any" behavior:
|
|
var oldStep = elem.step;
|
|
elem.step = "any";
|
|
oldVal = elem.value = 0;
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the midpoint and step='any' (" + oldVal + ")");
|
|
elem.step = oldStep; // restore
|
|
|
|
// Test that invalid input blocks UI initiated stepping:
|
|
oldVal = elem.value = "";
|
|
elem.select();
|
|
sendString("abc");
|
|
synthesizeKey(key, {});
|
|
is(elem.value, "", "Test " + key + " does nothing when the input is invalid");
|
|
|
|
// Test that no value does not block UI initiated stepping:
|
|
oldVal = elem.value = "";
|
|
elem.setAttribute("required", "required");
|
|
elem.select();
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the empty string and with the 'required' attribute set");
|
|
|
|
// Same again:
|
|
expectedVal = expectedValAfterKeyEvent(key, elem);
|
|
synthesizeKey(key, {});
|
|
is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
|
|
|
|
// Reset 'required' attribute:
|
|
elem.removeAttribute("required");
|
|
}
|
|
|
|
// Test that key events are correctly dispatched
|
|
elem.max = "";
|
|
elem.value = "";
|
|
sendString("7837281");
|
|
is(elem.value, "7837281", "Test keypress event dispatch for number control");
|
|
}
|
|
|
|
</script>
|
|
</pre>
|
|
</body>
|
|
</html>
|