// Run tests simultaneously so we don't have to take up too much time.
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("untriaged"); var gTestsRunning = 0;
function TestStarted() { ++gTestsRunning; }
function TestFinished() { if (--gTestsRunning == 0) SimpleTest.finish(); }
// An array of arrays of functions to be called at the outer index number
// of seconds after the present. var gFutureCalls = [];
function add_future_call(index, func)
{
if (!(index in gFutureCalls)) {
gFutureCalls[index] = [];
}
gFutureCalls[index].push(func);
TestStarted();
} var gStartTime1, gStartTime2; var gCurrentTime; var gSetupComplete = false;
function process_future_calls(index)
{ var calls = gFutureCalls[index];
if (!calls)
return;
gCurrentTime = Date.now();
for (var i = 0; i < calls.length; ++i) {
calls[i]();
TestFinished();
}
}
var timingFunctions = {
// a map from the value of 'transition-timing-function' to an array of
// the portions this function yields at 0 (always 0), 1/4, 1/2, and
// 3/4 and all (always 1) of the way through the time of the
// transition. Each portion is represented as a value and an
// acceptable error tolerance (based on a time error of 1%) for that
// value.
// Set up all the elements on which we are going to initiate transitions.
// We have two reference elements to check the expected timing range.
// They both have 16s linear transitions from 0 to 1000px.
// This means they move through 62.5 pixels per second.
const REF_PX_PER_SEC = 62.5;
function make_reference_p() {
let p = document.createElement("p");
p.appendChild(document.createTextNode("reference"));
p.style.textIndent = "0px";
p.style.transition = "16s text-indent linear"; div.appendChild(p);
return p;
} var earlyref = make_reference_p(); var earlyrefcs = getComputedStyle(earlyref, "");
// Test all timing functions using a set of 8-second transitions, which
// we check at times 0, 2s, 4s, 6s, and 8s. var tftests = [];
for (let tf in timingFunctions) {
let p = document.createElement("p");
let t = document.createTextNode("transition-timing-function: " + tf);
p.appendChild(t);
p.style.textIndent = "0px";
p.style.transition = "8s text-indent linear";
p.style.transitionTimingFunction = tf; div.appendChild(p);
is(getComputedStyle(p, "").textIndent, "0px", "should be zero before changing value");
tftests.push([ p, tf ]);
}
// Check that the timing function continues even when we restyle in the
// middle. var interrupt_tests = [];
for (var restyleParent of [true, false]) {
for (let itime = 2; itime < 8; itime += 2) {
let p = document.createElement("p");
let t = document.createTextNode("interrupt on " +
(restyleParent ? "parent" : "node itself") + " at " + itime + "s");
p.appendChild(t);
p.style.textIndent = "0px";
p.style.transition = "8s text-indent cubic-bezier(0, 1, 1, 0)";
if (restyleParent) {
let d = document.createElement("div");
d.appendChild(p); div.appendChild(d);
} else { div.appendChild(p);
}
is(getComputedStyle(p, "").textIndent, "0px", "should be zero before changing value");
setTimeout("interrupt_tests[" + interrupt_tests.length + "]" + "[0]" + (restyleParent ? ".parentNode" : "") + ".style.color = 'blue';" + "check_interrupt_tests()", itime*1000);
interrupt_tests.push([ p, itime ]);
}
}
// Test transition-delay values of -4s through 4s on a 4s transition
// with 'ease-out' timing function. var delay_tests = {};
for (let d = -4; d <= 4; ++d) {
let p = document.createElement("p");
let delay = d + "s";
let t = document.createTextNode("transition-delay: " + delay);
p.appendChild(t);
p.style.marginLeft = "0px";
p.style.transition = "4s margin-left ease-out " + delay; div.appendChild(p);
is(getComputedStyle(p, "").marginLeft, "0px", "should be zero before changing value");
delay_tests[d] = p;
}
// Test transition-delay values of -4s through 4s on a 4s transition
// with duration of zero. var delay_zero_tests = {};
for (let d = -4; d <= 4; ++d) {
let p = document.createElement("p");
let delay = d + "s";
let t = document.createTextNode("transition-delay: " + delay);
p.appendChild(t);
p.style.marginLeft = "0px";
p.style.transition = "0s margin-left linear " + delay; div.appendChild(p);
is(getComputedStyle(p, "").marginLeft, "0px", "should be zero before changing value");
delay_zero_tests[d] = p;
}
// Test that changing the value on an already-running transition to the
// value it currently happens to have resets the transition.
function make_reset_test(transition, description)
{
let p = document.createElement("p");
let t = document.createTextNode(description);
p.appendChild(t);
p.style.marginLeft = "0px";
p.style.transition = transition; div.appendChild(p);
is(getComputedStyle(p, "").marginLeft, "0px", "should be zero before changing value");
return p;
} var reset_test = make_reset_test("4s margin-left ease-out 4s", "transition-delay reset to starting point"); var reset_test_reference = make_reset_test("4s margin-left linear -3s", "reference for previous test (reset test)");
// Test that transitions on descendants start correctly when the
// inherited value is itself transitioning. In other words, when
// ancestor and descendant both have a transition for the same property,
// and the descendant inherits the property from the ancestor, the
// descendant's transition starts as specified, based on the concepts of
// the before-change style, the after-change style, and the
// after-transition style. var descendant_tests = [
{ parent_transition: "",
child_transition: "4s text-indent" },
{ parent_transition: "4s text-indent",
child_transition: "" },
{ parent_transition: "4s text-indent",
child_transition: "16s text-indent" },
{ parent_transition: "4s text-indent",
child_transition: "1s text-indent" },
{ parent_transition: "8s letter-spacing",
child_transition: "4s text-indent" },
{ parent_transition: "4s text-indent",
child_transition: "8s letter-spacing" },
{ parent_transition: "4s text-indent",
child_transition: "8s all" },
{ parent_transition: "8s text-indent",
child_transition: "4s all" },
// examples with positive and negative delay
{ parent_transition: "4s text-indent 1s",
child_transition: "8s text-indent" },
{ parent_transition: "4s text-indent -1s",
child_transition: "8s text-indent" }
];
for (let i in descendant_tests) {
let test = descendant_tests[i];
test.parentNode = document.createElement("div");
test.childNode = document.createElement("p");
test.parentNode.appendChild(test.childNode);
test.childNode.appendChild(document.createTextNode( "parent with \"" + test.parent_transition + "\" and " + "child with \"" + test.child_transition + "\""));
test.parentNode.style.transition = test.parent_transition;
test.childNode.style.transition = test.child_transition;
test.parentNode.style.textIndent = "50px"; // transition from 50 to 150
test.parentNode.style.letterSpacing = "10px"; // transition from 10 to 5 div.appendChild(test.parentNode); var parentCS = getComputedStyle(test.parentNode, ""); var childCS = getComputedStyle(test.childNode, "");
is(parentCS.textIndent, "50px", "parent text-indent should be 50px before changing");
is(parentCS.letterSpacing, "10px", "parent letter-spacing should be 10px before changing");
is(childCS.textIndent, "50px", "child text-indent should be 50px before changing");
is(childCS.letterSpacing, "10px", "child letter-spacing should be 10px before changing");
test.childCS = childCS;
}
// For all of these transitions, the transition for margin-left should
// have a duration of 8s, and the default timing function (ease) and
// delay (0).
// This is because we're implementing the proposal in
// http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html var number_tests = [
{ style: "transition: 4s margin, 8s margin-left" },
{ style: "transition: 4s margin-left, 8s margin" },
{ style: "transition-property: margin-left; " + "transition-duration: 8s, 2s" },
{ style: "transition-property: margin-left, margin-left; " + "transition-duration: 2s, 8s" },
{ style: "transition-property: margin-left, margin-left, margin-left; " + "transition-duration: 8s, 2s" },
{ style: "transition-property: margin-left; " + "transition-duration: 8s, 16s" },
{ style: "transition-property: margin-left, margin-left; " + "transition-duration: 16s, 8s" },
{ style: "transition-property: margin-left, margin-left, margin-left; " + "transition-duration: 8s, 16s" },
{ style: "transition-property: text-indent,word-spacing,margin-left; " + "transition-duration: 8s; " + "transition-delay: 0, 8s" },
{ style: "transition-property: text-indent,word-spacing,margin-left; " + "transition-duration: 8s, 16s; " + "transition-delay: 8s, 8s, 0, 8s, 8s, 8s" },
];
for (let i in number_tests) {
let test = number_tests[i];
let p = document.createElement("p");
p.setAttribute("style", test.style);
let t = document.createTextNode(test.style);
p.appendChild(t);
p.style.marginLeft = "100px"; div.appendChild(p);
is(getComputedStyle(p, "").marginLeft, "100px", "should be 100px before changing value");
test.node = p;
}
// Test transitions that are also from-display:none, to-display:none, and
// display:none throughout. var from_none_test, to_none_test, always_none_test;
function make_display_test(initially_none, text)
{
let p = document.createElement("p");
p.appendChild(document.createTextNode(text));
p.style.textIndent = "0px";
p.style.transition = "8s text-indent ease-in-out";
if (initially_none)
p.style.display = "none"; div.appendChild(p);
return p;
}
from_none_test = make_display_test(true, "transition from display:none");
to_none_test = make_display_test(false, "transition to display:none");
always_none_test = make_display_test(true, "transition always display:none"); var display_tests = [ from_none_test, to_none_test, always_none_test ];
// Test transitions on pseudo-elements var before_test, after_test;
function make_pseudo_elem_test(pseudo)
{
let p = document.createElement("p");
p.className = pseudo; div.appendChild(p);
return {"pseudo": pseudo, element: p};
}
before_test = make_pseudo_elem_test("before");
after_test = make_pseudo_elem_test("after"); var pseudo_element_tests = [ before_test, after_test ];
// FIXME (Bug 522599): Test a transition that reverses partway through.
var lateref = make_reference_p(); var laterefcs = getComputedStyle(lateref, "");
// flush style changes var x = getComputedStyle(div, "").width;
// Start our timer as close as possible to when we start the first
// transition.
// Do not use setInterval because once it gets off in time, it stays off.
for (let i = 1; i <= 8; ++i) {
setTimeout(process_future_calls, i * 1000, i);
}
gStartTime1 = Date.now(); // set before any transitions have started
// Start all the transitions.
earlyref.style.textIndent = "1000px";
for (let test in tftests) {
let p = tftests[test][0];
p.style.textIndent = "100px";
}
for (let test in interrupt_tests) {
let p = interrupt_tests[test][0];
p.style.textIndent = "100px";
}
for (let d in delay_tests) {
let p = delay_tests[d];
p.style.marginLeft = "100px";
}
for (let d in delay_zero_tests) {
let p = delay_zero_tests[d];
p.style.marginLeft = "100px";
}
reset_test.style.marginLeft = "100px";
reset_test_reference.style.marginLeft = "100px";
for (let i in descendant_tests) {
let test = descendant_tests[i];
test.parentNode.style.textIndent = "150px";
test.parentNode.style.letterSpacing = "5px";
}
for (let i in number_tests) {
let test = number_tests[i];
test.node.style.marginLeft = "50px";
}
from_none_test.style.textIndent = "100px";
from_none_test.style.display = "";
to_none_test.style.textIndent = "100px";
to_none_test.style.display = "none";
always_none_test.style.textIndent = "100px";
for (let i in pseudo_element_tests) {
let test = pseudo_element_tests[i];
test.element.classList.add("started");
}
lateref.style.textIndent = "1000px";
// flush style changes
x = getComputedStyle(div, "").width;
gStartTime2 = Date.now(); // set after all transitions have started
gCurrentTime = gStartTime2;
/**
* Assert that a transition whose timing function yields the bezier
* |func|, running from |start_time| to |end_time| (both in seconds
* relative to when the transitions were started) should have produced
* computed value |cval| given that the transition was from
* |start_value| to |end_value| (both numbers in CSS pixels).
*/
function check_transition_value(func, start_time, end_time,
start_value, end_value, cval, desc,
xfail)
{
/**
* Compute the value at a given time |elapsed|, by normalizing the
* input to the timing function using start_time and end_time and
* then turning the output into a value using start_value and
* end_value.
*
* The |error_direction| argument should be either -1, 0, or 1,
* suggesting adding on a little bit of error, to allow for the
* cubic-bezier calculation being an approximation. The amount of
* error is proportional to the slope of the timing function, since
* the error is added to the *input* of the timing function (after
* normalization to 0-1 based on start_time and end_time).
*/
function value_at(elapsed, error_direction) { var time_portion = (elapsed - start_time) / (end_time - start_time);
if (time_portion < 0)
time_portion = 0;
else if (time_portion > 1)
time_portion = 1;
// Assume a small error since bezier computation can be off slightly.
// (This test's computation is probably more accurate than Mozilla's.) var value_portion = func(time_portion + error_direction * 0.0005);
if (value_portion < 0)
value_portion = 0;
else if (value_portion > 1)
value_portion = 1; var value = (1 - value_portion) * start_value + value_portion * end_value;
if (start_value > end_value)
error_direction = -error_direction;
// Computed values get rounded to 1/60th of a pixel.
return value + error_direction * 0.02;
}
var time_range; // in seconds var uns_range; // |range| before being sorted (so errors give it
// in the original order
if (!gSetupComplete) {
// No timers involved
time_range = [0, 0];
if (start_time < 0) {
uns_range = [ value_at(0, -1), value_at(0, 1) ];
} else { var val = value_at(0, 0);
uns_range = [val, val];
}
} else {
time_range = [ px_to_num(earlyrefcs.textIndent) / REF_PX_PER_SEC,
px_to_num(laterefcs.textIndent) / REF_PX_PER_SEC ];
// seconds
uns_range = [ value_at(time_range[0], -1),
value_at(time_range[1], 1) ];
} var range = uns_range.concat(). /* concat to clone array */
sort(function compareNumbers(a,b) { return a - b; }); var actual = px_to_num(cval);
var fn = ok;
if (xfail && xfail(range))
fn = todo;
fn(range[0] <= actual && actual <= range[1],
desc + ": computed value " + cval + " should be between " +
uns_range[0].toFixed(6) + "px and " + uns_range[1].toFixed(6) + "px at time between " + time_range[0] + "s and " + time_range[1] + "s.");
}
function check_ref_range()
{
// This is the only test where we compare the progress of the
// transitions to an actual time; we need considerable tolerance at
// the low end (we are using half a second). var expected_range = [ (gCurrentTime - gStartTime2 - 40) / 16,
(Date.now() - gStartTime1 + 20) / 16 ];
if (expected_range[0] > 1000) {
expected_range[0] = 1000;
}
if (expected_range[1] > 1000) {
expected_range[1] = 1000;
}
function check(desc, value) {
// The timing on the unit test VMs is not reliable, so make this
// test report PASS when it succeeds and TODO when it fails. var passed = expected_range[0] <= value && value <= expected_range[1];
(passed ? ok : todo)(passed,
desc + ": computed value " + value + "px should be between " +
expected_range[0].toFixed(6) + "px and " +
expected_range[1].toFixed(6) + "px at time between " +
expected_range[0]/REF_PX_PER_SEC + "s and " +
expected_range[1]/REF_PX_PER_SEC + "s.");
}
check("early reference", px_to_num(earlyrefcs.textIndent));
check("late reference", px_to_num(laterefcs.textIndent));
}
for (let i = 1; i <= 8; ++i) {
add_future_call(i, check_ref_range);
}
function check_tf_test()
{
for (var test in tftests) { var p = tftests[test][0]; var tf = tftests[test][1];
check_transition_value(timingFunctions[tf], 0, 8, 0, 100,
getComputedStyle(p, "").textIndent, "timing function test for timing function " + tf);
// check_interrupt_tests is called from check_tf_test and from
// where we reset the interrupts
function check_delay_test(time)
{
let tf = timingFunctions["ease-out"];
for (let d in delay_tests) {
let p = delay_tests[d];
check_transition_value(tf, Number(d), Number(d) + 4, 0, 100,
getComputedStyle(p, "").marginLeft, "delay test for delay " + d + "s");
}
}
check_delay_test(0);
for (let i = 1; i <= 8; ++i) {
add_future_call(i, check_delay_test);
}
function check_delay_zero_test(time)
{
for (let d in delay_zero_tests) {
let p = delay_zero_tests[d];
time_range = [ px_to_num(earlyrefcs.textIndent) / REF_PX_PER_SEC,
px_to_num(laterefcs.textIndent) / REF_PX_PER_SEC ]; var m = getComputedStyle(p, "").marginLeft; var desc = "delay_zero test for delay " + d + "s";
if (time_range[0] < d && time_range[1] < d) {
is(m, "0px", desc);
} else if ((time_range[0] > d && time_range[1] > d) ||
(d == 0 && time == 0)) {
is(m, "100px", desc);
}
}
}
check_delay_zero_test(0);
for (let i = 1; i <= 8; ++i) {
add_future_call(i, check_delay_zero_test);
}
function reset_reset_test(time)
{
reset_test.style.marginLeft = "0px";
}
function check_reset_test(time)
{
is(getComputedStyle(reset_test, "").marginLeft, "0px", "reset test value at time " + time + "s.");
}
check_reset_test(0);
// reset the reset test right now so we don't have to worry about clock skew
// To make sure that this is valid, check that a pretty-much-identical test is
// already transitioning.
is(getComputedStyle(reset_test_reference, "").marginLeft, "75px", "reset test reference value");
reset_reset_test();
check_reset_test(0);
for (let i = 1; i <= 8; ++i) {
(function(j) {
add_future_call(j, function() { check_reset_test(j); });
})(i);
}
for (let i in descendant_tests) {
let test = descendant_tests[i];
/* ti=text-indent, ls=letter-spacing */ var child_ti_duration = 0; var child_ls_duration = 0; var child_ti_delay = 0; var child_ls_delay = 0;
if (test.parent_transition != "") { var props = test.parent_transition.split(" "); var duration = parseInt(props[0]); var delay = (props.length > 2) ? parseInt(props[2]) : 0; var property = props[1];
if (property == "text-indent") {
child_ti_duration = duration;
child_ti_delay = delay;
} else if (property == "letter-spacing") {
child_ls_duration = duration;
child_ls_delay = delay;
} else {
ok(false, "fix this test (unexpected transition-property " +
property + " on parent)");
}
}
if (test.child_transition != "") { var props = test.child_transition.split(" "); var duration = parseInt(props[0]); var delay = (props.length > 2) ? parseInt(props[2]) : 0; var property = props[1];
if (property != "text-indent" && property != "letter-spacing" &&
property != "all") {
ok(false, "fix this test (unexpected transition-property " +
property + " on child)");
}
// Override the parent's transition with the child's as long
// as the child transition is still running.
if (property != "letter-spacing" && duration + delay > time) {
child_ti_duration = duration;
child_ti_delay = delay;
}
if (property != "text-indent" && duration + delay > time) {
child_ls_duration = duration;
child_ls_delay = delay;
}
}
function check_display_tests(time)
{
for (let i in display_tests) {
let p = display_tests[i];
// There is no transition if the old or new style is display:none, so
// the computed value is always the end value. var computedValue = getComputedStyle(p, "").textIndent;
is(computedValue, "100px", "display test for test with " + p.childNodes[0].data + ": computed value " + computedValue + " should be 100px.");
}
}
function check_pseudo_element_tests(time)
{
let tf = timingFunctions["ease-in-out"];
for (let i in pseudo_element_tests) {
let test = pseudo_element_tests[i];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.