Bug 1409083 Part 5: Add tests of new Flex API. r=gl
authorBrad Werth <bwerth@mozilla.com>
Wed, 01 Nov 2017 15:53:41 -0700
changeset 395785 e06bb30b6f5fb324e83dde7815dde65681493966
parent 395784 3c2ef8c6a04f7bda8ab4d258f41c6fd1e154d142
child 395786 b6656f7bbc861f5879a3d61d007e599c2fbd6190
push id33054
push userrgurzau@mozilla.com
push dateFri, 08 Dec 2017 21:57:23 +0000
treeherdermozilla-central@6e2181b6137c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1409083
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1409083 Part 5: Add tests of new Flex API. r=gl MozReview-Commit-ID: KWzThXA9Jk5
.eslintignore
dom/flex/moz.build
dom/flex/test/chrome.ini
dom/flex/test/chrome/test_flex_items.html
dom/flex/test/chrome/test_flex_lines.html
--- a/.eslintignore
+++ b/.eslintignore
@@ -215,16 +215,17 @@ dom/console/**
 dom/crypto/**
 dom/devicestorage/**
 dom/encoding/**
 dom/events/**
 dom/fetch/**
 dom/file/**
 dom/filehandle/**
 dom/filesystem/**
+dom/flex/**
 dom/flyweb/**
 dom/gamepad/**
 dom/geolocation/**
 dom/grid/**
 dom/html/**
 dom/imptests/**
 dom/interfaces/**
 dom/ipc/**
--- a/dom/flex/moz.build
+++ b/dom/flex/moz.build
@@ -2,16 +2,18 @@
 # vim: set filetype=python:
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
 
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
+
 EXPORTS.mozilla.dom += [
     'Flex.h',
     'FlexItem.h',
     'FlexLine.h',
 ]
 
 UNIFIED_SOURCES += [
     'Flex.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/flex/test/chrome.ini
@@ -0,0 +1,2 @@
+[chrome/test_flex_items.html]
+[chrome/test_flex_lines.html]
new file mode 100644
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_items.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+  .container {
+    display: flex;
+    background-color: grey;
+    font: 14px sans-serif;
+    width: 800px;
+    height: 50px;
+  }
+
+  .base        { align-self: baseline; }
+  .lastbase    { align-self: last baseline; }
+
+  .offset      { margin-top: 10px;
+                 margin-bottom: 3px; }
+
+  .lime        { background: lime;   }
+  .yellow      { background: yellow; }
+  .orange      { background: orange; }
+  .pink        { background: pink;   }
+  .white       { background: white;  }
+
+  .crossMinMax { min-height: 40px;
+                 max-height: 120px; }
+
+  .mainMinMax  { min-width: 120px;
+                 max-width: 500px; }
+
+  .flexGrow    { flex-grow: 1; }
+
+  #second	     { width: 100px; }
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+const TEXT_NODE = Ci.nsIDOMNode.TEXT_NODE;
+
+function testItemMatchesExpectedValues(item, values, index) {
+  if (typeof(values.node) != "undefined") {
+    is(item.node, values.node, "Item index " + index + " has expected node.");
+  }
+
+  if (typeof(values.node_todo) != "undefined") {
+    todo_is(item.node, values.node_todo, "Item index " + index + " has expected node.");
+  }
+
+  if (typeof(values.mainBaseSize) != "undefined") {
+    is(item.mainBaseSize, values.mainBaseSize, "Item index " + index + " has expected mainBaseSize.");
+  }
+
+  if (typeof(values.mainDeltaSize) != "undefined") {
+    is(item.mainDeltaSize, values.mainDeltaSize, "Item index " + index + " has expected mainDeltaSize.");
+  }
+
+  if (typeof(values.mainMinSize) != "undefined") {
+    is(item.mainMinSize, values.mainMinSize, "Item index " + index + " has expected mainMinSize.");
+  }
+
+  if (typeof(values.mainMaxSize) != "undefined") {
+    is(item.mainMaxSize, values.mainMaxSize, "Item index " + index + " has expected mainMaxSize.");
+  }
+
+  if (typeof(values.crossMinSize) != "undefined") {
+    is(item.crossMinSize, values.crossMinSize, "Item index " + index + " has expected crossMinSize.");
+  }
+
+  if (typeof(values.crossMaxSize) != "undefined") {
+    is(item.crossMaxSize, values.crossMaxSize, "Item index " + index + " has expected crossMaxSize.");
+  }
+}
+
+function nearlyEqual(a, b) {
+  const ep = 1e-4;
+  let diff = a - b;
+  return (diff < ep && diff > -ep);
+}
+
+function runTests() {
+  let container = document.getElementById("wrapper");
+  let flex = container.getAsFlexContainer();
+  let lines = flex.getLines();
+  is(lines.length, 1, "Container has expected number of lines.");
+
+  let line = lines[0];
+  let containerHeight = container.getBoundingClientRect().height;
+  is(line.crossSize, containerHeight, "Line crossSize equals the height of the container.");
+
+  let first = document.getElementById("first");
+  let second = document.getElementById("second");
+  let third = document.getElementById("third");
+  let fourth = document.getElementById("fourth");
+  let fifth = document.getElementById("fifth");
+  let sixth = container.lastChild;
+  is(sixth.nodeType, TEXT_NODE, "Sixth child should be an anonymous text node.");
+
+  // We can't compare baselines precisely, so we'll just confirm that they appear
+  // somewhere within the elements that determine them.
+  let firstRect = first.getBoundingClientRect();
+  ok(line.firstBaselineOffset > firstRect.top &&
+     line.firstBaselineOffset < firstRect.bottom,
+     "Line firstBaselineOffset lands somewhere within the element that determines it.");
+
+  // For last baseline, it's measured from the bottom, so we have to compare against
+  // the element bounds subtracted from the container height.
+  let secondRect = second.getBoundingClientRect();
+  ok(line.lastBaselineOffset > containerHeight - secondRect.bottom &&
+     line.lastBaselineOffset < containerHeight - secondRect.top,
+     "Line lastBaselineOffset lands somewhere within the element that determines it.");
+
+  let items = line.getItems();
+  is(items.length, 6, "Line has expected number of items.");
+
+  let expectedValues = [
+    { node: first,
+      crossMinSize: 0 },
+    { node: second,
+      mainBaseSize: secondRect.width,
+      mainDeltaSize: 0 },
+    { node: third,
+      crossMinSize: 40,
+      crossMaxSize: 120,
+      mainDeltaSize: 0 },
+    { node: fourth,
+      mainMinSize: 120,
+      mainMaxSize: 500,
+      mainDeltaSize: 0 },
+    { node: fifth,
+      mainDeltaSize: 0 },
+    { node: sixth },
+  ];
+
+  for (let i = 0; i < items.length; ++i) {
+    let item = items[i];
+    let values = expectedValues[i];
+    testItemMatchesExpectedValues(item, values, i);
+  }
+
+  // Check that the delta size of the first item is nearly equal to the actual size minus the base size.
+  ok(nearlyEqual(items[0].mainDeltaSize, firstRect.width - items[0].mainBaseSize),
+     "flex-grow item has expected mainDeltaSize.");
+
+  SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+  <div id="wrapper" class="container">
+    <div id="first" class="lime base flexGrow">one line (first)</div>
+    <div id="second" class="yellow lastbase">one line (last)</div>
+    <div id="third" class="orange offset lastbase crossMinMax">two<br/>lines and offset (last)</div>
+    <div id="fourth" class="pink offset base mainMinMax">offset (first)</div>
+    <div style="display:contents">
+      <div id="fifth" class="white">replaced</div>
+    </div>
+    anonymous text node
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_lines.html
@@ -0,0 +1,281 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+  f {
+    display: flex;
+    background-color: grey;
+    font: 12px sans-serif;
+    width: 800px;
+    height: 42px;
+    margin-bottom: 5px;
+  }
+
+  .withZ::after {
+    background-color: pink;
+    content: "Z";
+    width: 100px;
+    height: 10px;
+  }
+
+  .wrap {
+    flex-wrap: wrap;
+  }
+
+  .wrapReverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  b {
+    background-color: gold;
+    min-width: 100px;
+    height: 20px;
+    flex-grow: 1;
+  }
+  b::after {
+    content: "B";
+  }
+
+  c {
+    background-color: yellow;
+    width: 200px;
+    height: 15px;
+  }
+  c::after {
+    content: "C";
+  }
+
+  d {
+    background-color: orange;
+    width: 300px;
+    height: 10px;
+  }
+  d::after {
+    content: "D";
+  }
+
+  e {
+    background-color: silver;
+    width: 300px;
+    height: 10px;
+    flex-shrink: 2;
+  }
+  e::after {
+    content: "E";
+  }
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function testLineMatchesExpectedValues(line, values, lineIndex, flexIndex) {
+  if (typeof(values.growthState) != "undefined") {
+    is(line.growthState, values.growthState, "Flex index " + flexIndex + " line index " + lineIndex + " has expected growthState.");
+  }
+
+  if (typeof(values.crossSize) != "undefined") {
+    is(line.crossSize, values.crossSize, "Flex index " + flexIndex + " line index " + lineIndex + " has expected crossSize.");
+  }
+
+  if (typeof(values.itemCount) != "undefined") {
+    is(line.getItems().length, values.itemCount, "Flex index " + flexIndex + " line index " + lineIndex + " has expected number of items.");
+  }
+}
+
+function runTests() {
+  let expectedValues = [
+    // items don't fill container, no grow, shrink, or min-width
+    [{ crossSize: 42,
+       itemCount: 2,
+       growthState: "unchanged" }],
+    [{ crossSize: 42,
+       itemCount: 3,
+       growthState: "unchanged" }],
+
+    // items don't fill container, no grow, shrink, or min-width, with wrap and align-content:center -->
+    [{ crossSize: 15,
+       itemCount: 2,
+       growthState: "unchanged" }],
+    [{ crossSize: 15,
+       itemCount: 3,
+       growthState: "unchanged" }],
+
+    // items don't fill container, with grow
+    [{ crossSize: 42,
+       itemCount: 3,
+       growthState: "growing" }],
+    [{ crossSize: 42,
+       itemCount: 4,
+       growthState: "growing" }],
+
+    // items overfill container, with min-width, and sometimes with wrap
+    [{ crossSize: 42,
+       itemCount: 5,
+       growthState: "shrinking" }],
+    [{ crossSize: 21,
+       itemCount: 3,
+       growthState: "growing" },
+     { crossSize: 21,
+       itemCount: 2,
+       growthState: "growing" }],
+    [{ crossSize: 42,
+       itemCount: 6,
+       growthState: "shrinking" }],
+    [{ crossSize: 21,
+       itemCount: 3,
+       growthState: "growing" },
+     { crossSize: 21,
+       itemCount: 3,
+       growthState: "growing" }],
+
+    // items overfill container, with shrink and sometimes with wrap
+    [{ crossSize: 42,
+       itemCount: 3,
+       growthState: "shrinking" }],
+    [{ crossSize: 21,
+       itemCount: 2,
+       growthState: "unchanged" },
+     { crossSize: 21,
+       itemCount: 1,
+       growthState: "unchanged" }],
+    [{ crossSize: 42,
+       itemCount: 4,
+       growthState: "shrinking" }],
+    [{ crossSize: 21,
+       itemCount: 2,
+       growthState: "unchanged" },
+     { crossSize: 21,
+       itemCount: 2,
+       growthState: "unchanged" }],
+
+    // items overfill container, with wrap and different types of align-content
+    [{ crossSize: 26,
+       itemCount: 3 },
+     { crossSize: 16,
+       itemCount: 1 }],
+    [{ crossSize: 20,
+       itemCount: 3 },
+     { crossSize: 10,
+       itemCount: 1 }],
+    [{ crossSize: 20,
+       itemCount: 3 },
+     { crossSize: 10,
+       itemCount: 1 }],
+    [{ crossSize: 20,
+       itemCount: 3 },
+     { crossSize: 10,
+       itemCount: 1 }],
+    [{ crossSize: 20,
+       itemCount: 3 },
+     { crossSize: 10,
+       itemCount: 1 }],
+    [{ crossSize: 20,
+       itemCount: 3 },
+     { crossSize: 10,
+       itemCount: 1 }],
+
+    // items overfill container, with wrap-reverse and different types of align-content
+    [{ crossSize: 16,
+       itemCount: 2 },
+     { crossSize: 26,
+       itemCount: 3 }],
+    [{ crossSize: 10,
+       itemCount: 2 },
+     { crossSize: 20,
+       itemCount: 3 }],
+    [{ crossSize: 10,
+       itemCount: 2 },
+     { crossSize: 20,
+       itemCount: 3 }],
+    [{ crossSize: 10,
+       itemCount: 2 },
+     { crossSize: 20,
+       itemCount: 3 }],
+    [{ crossSize: 10,
+       itemCount: 2 },
+     { crossSize: 20,
+       itemCount: 3 }],
+    [{ crossSize: 10,
+       itemCount: 2 },
+     { crossSize: 20,
+       itemCount: 3 }],
+
+	// other strange types of flex containers
+    [{ itemCount: 3 }],
+  ];
+
+  let children = document.body.children;
+  is(children.length, expectedValues.length, "Document has expected number of flex containers.");
+
+  for (let i = 0; i < children.length; ++i) {
+    let flex = children.item(i).getAsFlexContainer();
+	ok(flex, "Document child index " + i + " is a flex container.");
+	if (flex) {
+	  let values = expectedValues[i];
+
+	  let lines = flex.getLines();
+	  is(lines.length, values.length, "Flex index " + i + " has expected number of lines.");
+
+      for (let j = 0; j < lines.length; ++j) {
+	    testLineMatchesExpectedValues(lines[j], values[j], j, i);
+	  }
+	}
+  }
+
+  SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+
+<!-- items don't fill container, no grow, shrink, or min-width -->
+<f><c></c><d></d></f>
+<f class="withZ"><c></c><d></d></f>
+
+<!-- items don't fill container, no grow, shrink, or min-width, with wrap and align-content:center -->
+<f class="wrap" style="align-content:center"><c></c><d></d></f>
+<f class="withZ wrap" style="align-content:center"><c></c><d></d></f>
+
+<!-- items don't fill container, with grow -->
+<f><b></b><c></c><d></d></f>
+<f class="withZ"><b></b><c></c><d></d></f>
+
+<!-- items overfill container, with min-width, and sometimes with wrap -->
+<f><b></b><d></d><d></d><d></d><b></b></f>
+<f class="wrap"><b></b><d></d><d></d><d></d><b></b></f>
+<f class="withZ"><b></b><d></d><d></d><d></d><b></b></f>
+<f class="wrap withZ"><b></b><d></d><d></d><d></d><b></b></f>
+
+<!-- items overfill container, with shrink and sometimes with wrap -->
+<f><d></d><d></d><e></e></f>
+<f class="wrap"><d></d><d></d><e></e></f>
+<f class="withZ"><d></d><d></d><e></e></f>
+<f class="wrap withZ"><d></d><d></d><e></e></f>
+
+<!-- items overfill container, with wrap and different types of align-content -->
+<f class="wrap"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:flex-start"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:flex-end"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:center"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:space-between"><b></b><c></c><d></d><e></e></f>
+<f class="wrap" style="align-content:space-around"><b></b><c></c><d></d><e></e></f>
+
+<!-- items overfill container, with wrap-reverse and different types of align-content -->
+<f class="wrapReverse withZ"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:flex-start"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:flex-end"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:center"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:space-between"><b></b><c></c><d></d><e></e></f>
+<f class="wrapReverse withZ" style="align-content:space-around"><b></b><c></c><d></d><e></e></f>
+
+<!-- other strange types of flex containers -->
+<f style="overflow:scroll"><d></d><d></d><e></e></f>
+
+</body>
+</html>