redo testing interface so it's actually readable by a human.

This commit is contained in:
Elm UI Automation 2020-05-02 14:38:03 -04:00
parent 510b32ae82
commit 639efbb5ab
9 changed files with 639 additions and 540 deletions

View File

@ -265,31 +265,29 @@ function renderEnvName(env) {
return `${env.platform}, ${env.browser} ${env.browserVersion}`;
}
function print_results(label, results) {
function print_results(label, tests) {
var total_passed = 0;
var total_failed = 0;
var i;
if (program.verbose) {
console.log(label);
}
for (i = 0; i < results.length; i++) {
for (i = 0; i < tests.length; i++) {
var passed = 0;
var failed = 0;
for (j = 0; j < results[i].results.length; j++) {
if (results[i].results[j][1] == null) {
for (j = 0; j < tests[i].results.length; j++) {
if (tests[i].results[j].passing) {
passed = passed + 1;
} else {
if (failed == 0) {
if (!program.verbose) {
console.log(label);
}
console.log(results[i].label);
console.log(tests[i].label);
}
failed = failed + 1;
console.log(
" " + chalk.red("fail") + " -> " + results[i].results[j][0]
);
console.log(" " + results[i].results[j][1].description);
console.log(" " + chalk.red("fail") + " ->");
console.log(" " + tests[i].results[j][1].description);
}
}
total_passed = total_passed + passed;
@ -300,7 +298,7 @@ function print_results(label, results) {
console.log(chalk.red(` ${failed} tests failed`));
console.log();
} else if (program.verbose) {
console.log(results[i].label + chalk.green(` ${passed} tests passed`));
console.log(tests[i].label + chalk.green(` ${passed} tests passed`));
}
}
if (total_failed == 0) {

View File

@ -1,105 +1,107 @@
<!DOCTYPE HTML>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Rendering Benchmark Viewer</title>
<script>
${compiled_elm_code}
</script>
</head>
<style>
</style>
<body id="root"></body>
<script type="text/javascript">
var app = Elm.Tests.Run.init();
var test_results = "waiting.."
app.ports.report.subscribe(function (results) {
test_results = results;
})
app.ports.analyze.subscribe(function (ids) {
// ids : List String
var idsLength = ids.length;
var results = [];
for (var i = 0; i < idsLength; i++) {
var id = ids[i];
var element = document.getElementById(id);
if (element == null) {
console.log("id " + id + " not found");
}
var style = getStyle(element);
var bbox = getBoundingBox(element);
var visible = isVisible(id, bbox);
var result = { "bbox": bbox, "style": style, "id": id, "isVisible": visible };
results.push(result);
}
app.ports.styles.send(results);
});
function isVisible(id, bbox) {
var current = document.getElementById(id);
var result = 0;
if (current == document.elementFromPoint(bbox['left'], bbox['top'])) {
result++;
}
if (current == document.elementFromPoint(bbox['left'], bbox['bottom'] - 1)) {
result++;
}
if (current == document.elementFromPoint(bbox['right'] - 1, bbox['top'])) {
result++;
}
if (current == document.elementFromPoint(bbox['right'] - 1, bbox['bottom'] - 1)) {
result++;
}
if (result == 4) {
return true;
} else {
return false
}
}
function getStyle(element) {
var props = []
var style = window.getComputedStyle(element);
for (var i = style.length; i--;) {
var name = style.item(i);
var value = style.getPropertyValue(name);
props.push([name, value]);
}
return props;
}
function getBoundingBox(element) {
var bbox = element.getBoundingClientRect();
var style = window.getComputedStyle(element, null);
var padding = {
'top': parseFloat(style.getPropertyValue('padding-top'))
, 'bottom': parseFloat(style.getPropertyValue('padding-bottom'))
, 'left': parseFloat(style.getPropertyValue('padding-left'))
, 'right': parseFloat(style.getPropertyValue('padding-right'))
};
return {
'top': bbox.top
, 'bottom': bbox.bottom
, 'left': bbox.left
, 'right': bbox.right
, 'width': bbox.width
, 'height': bbox.height
, 'padding': padding
}
}
<head>
<meta charset="UTF-8" />
<title>Rendering Benchmark Viewer</title>
<script>
${compiled_elm_code}
</script>
</html>
</head>
<style></style>
<body id="root"></body>
<script type="text/javascript">
var app = Elm.Tests.Run.init();
var test_results = "waiting..";
app.ports.report.subscribe(function (results) {
// these are extracted by the script that runs the browser automation.
test_results = results;
});
app.ports.analyze.subscribe(function (ids) {
// ids : List String
var idsLength = ids.length;
var results = [];
for (var i = 0; i < idsLength; i++) {
var id = ids[i];
var element = document.getElementById(id);
if (element == null) {
console.log("id " + id + " not found");
}
var style = getStyle(element);
var bbox = getBoundingBox(element);
var visible = isVisible(id, bbox);
var result = { bbox: bbox, style: style, id: id, isVisible: visible };
results.push(result);
}
app.ports.styles.send(results);
});
function isVisible(id, bbox) {
var current = document.getElementById(id);
var result = 0;
if (current == document.elementFromPoint(bbox["left"], bbox["top"])) {
result++;
}
if (
current == document.elementFromPoint(bbox["left"], bbox["bottom"] - 1)
) {
result++;
}
if (
current == document.elementFromPoint(bbox["right"] - 1, bbox["top"])
) {
result++;
}
if (
current ==
document.elementFromPoint(bbox["right"] - 1, bbox["bottom"] - 1)
) {
result++;
}
if (result == 4) {
return true;
} else {
return false;
}
}
function getStyle(element) {
var props = [];
var style = window.getComputedStyle(element);
for (var i = style.length; i--; ) {
var name = style.item(i);
var value = style.getPropertyValue(name);
props.push([name, value]);
}
return props;
}
function getBoundingBox(element) {
var bbox = element.getBoundingClientRect();
var style = window.getComputedStyle(element, null);
var padding = {
top: parseFloat(style.getPropertyValue("padding-top")),
bottom: parseFloat(style.getPropertyValue("padding-bottom")),
left: parseFloat(style.getPropertyValue("padding-left")),
right: parseFloat(style.getPropertyValue("padding-right")),
};
return {
top: bbox.top,
bottom: bbox.bottom,
left: bbox.left,
right: bbox.right,
width: bbox.width,
height: bbox.height,
padding: padding,
};
}
</script>
</html>

View File

@ -1,2 +1,2 @@
elm-live cases/open/InFrontSize.elm --open --dir=view -- --output=view/elm.js --debug
elm-live cases/open/WeirdCentering.elm --dir=view -- --output=view/elm.js --debug

View File

@ -4,40 +4,32 @@ module Testable exposing
, Element(..)
, Found
, LayoutContext(..)
, LayoutExpectation(..)
, LayoutTest
, Location(..)
, Style
, Surroundings
, addAttribute
, applyLabels
, compareFormattedColor
, createAttributeTest
, createTest
, equal
, formatColor
, formatColorWithAlpha
, getElementId
, getIds
, getSpacing
, idAttr
, levelToString
, render
, renderAttribute
, renderElement
, runTests
, toTest
, toElement
, toHtml
, todo
, true
)
{-| -}
import Dict exposing (Dict)
import Element exposing (Color)
import Expect
import Html exposing (Html)
import Html.Attributes
import Internal.Model as Internal
import Random
import Test exposing (Test)
import Test.Runner
import Test.Runner.Failure
type Element msg
@ -52,18 +44,20 @@ type Element msg
type Attr msg
= Attr (Element.Attribute msg)
| AttrTest (Surroundings -> Test)
| AttrTest
{ test : Surroundings -> List LayoutExpectation
, label : String
}
| Batch (List (Attr msg))
| Spacing Int
| Nearby
{ location : Location
, element : Element msg
, test : Surroundings -> () -> Expect.Expectation
, test : Surroundings -> List LayoutExpectation
, label : String
}
| Label String
| LabeledTest
{ test : Surroundings -> () -> Expect.Expectation
{ test : Surroundings -> List LayoutExpectation
, label : String
, attr : Element.Attribute msg
}
@ -125,6 +119,46 @@ type alias BoundingBox =
}
type LayoutExpectation
= Expect
{ description : String
, result : Bool
}
| Todo String
type alias LayoutTest =
{ elementDomId : String
, label : String
, expectations : List LayoutExpectation
}
{- Expectations -}
equal : a -> a -> LayoutExpectation
equal one two =
Expect
{ description = "Are equal"
, result = one == two
}
true : String -> Bool -> LayoutExpectation
true label passing =
Expect
{ description = label
, result = passing
}
todo : String -> LayoutExpectation
todo =
Todo
{- Retrieve Ids -}
@ -183,8 +217,13 @@ getElementId level el =
{- Render as Html -}
render : Element msg -> Html msg
render el =
toElement : Element msg -> Element.Element msg
toElement el =
renderElement [ 0, 0 ] el
toHtml : Element msg -> Html msg
toHtml el =
Element.layout [ idAttr "0" ] <|
renderElement [ 0, 0 ] el
@ -253,9 +292,6 @@ renderAttribute level attrIndex attr =
Spacing _ ->
[]
Label _ ->
[]
Nearby { location, element } ->
case location of
Above ->
@ -288,30 +324,35 @@ renderAttribute level attrIndex attr =
{- Convert to Test -}
toTest : String -> Dict String Found -> Element msg -> Test
toTest label harvested el =
runTests : Dict String Found -> Element msg -> List LayoutTest
runTests harvested el =
let
maybeFound =
Dict.get "se-0" harvested
in
case maybeFound of
Nothing ->
Test.describe label
[ Test.test "Find Root" <|
\_ -> Expect.fail "unable to find root"
]
[ { elementDomId = "se-0"
, label = "Finding root element"
, expectations =
[ Expect
{ description = "Locating root element for rendering tests"
, result = False
}
]
}
]
Just root ->
Test.describe label <|
createTest
{ siblings = []
, parent = root
, cache = harvested
, parentSpacing = 0
, level = [ 0, 0 ]
, element = el
, location = InEl
}
createTest
{ siblings = []
, parent = root
, cache = harvested
, parentSpacing = 0
, level = [ 0, 0 ]
, element = el
, location = InEl
}
levelToString : List Int -> String
@ -331,7 +372,7 @@ createTest :
, location : LayoutContext
, parentSpacing : Int
}
-> List Test
-> List LayoutTest
createTest { siblings, parent, cache, level, element, location, parentSpacing } =
let
spacing =
@ -341,7 +382,7 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
id =
levelToString level
testChildren : Found -> List (Element msg) -> List Test
testChildren : Found -> List (Element msg) -> List LayoutTest
testChildren found children =
let
childrenFound =
@ -369,13 +410,13 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
{ a
| index : Int
, previous : List Found
, tests : List Test
, tests : List LayoutTest
, upcoming : List Found
}
->
{ index : Int
, previous : List Found
, tests : List Test
, tests : List LayoutTest
, upcoming : List Found
}
applyChildTest found child childTest =
@ -439,7 +480,7 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
rest
}
tests : Found -> List (Attr msg) -> List (Element msg) -> List Test
tests : Found -> List (Attr msg) -> List (Element msg) -> List LayoutTest
tests self attributes children =
let
findBBox elem ( i, gathered ) =
@ -479,7 +520,6 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
attributeTests =
attributes
|> applyLabels
|> List.indexedMap
-- Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
(\i attr ->
@ -511,7 +551,16 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
[]
_ ->
[ Test.test ("Unable to find " ++ id) (always <| Expect.fail "failed id lookup") ]
[ { elementDomId = id
, label = "Finding element in DOM"
, expectations =
[ Expect
{ description = "Locating element for rendering test"
, result = False
}
]
}
]
Just self ->
case element of
@ -537,59 +586,31 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
[]
applyLabels : List (Attr msg) -> List (Attr msg)
applyLabels attrs =
let
toLabel attr =
case attr of
Label label ->
Just label
_ ->
Nothing
newLabels =
attrs
|> List.filterMap toLabel
|> String.join ", "
applyLabel newLabel attr =
case attr of
LabeledTest labeled ->
LabeledTest
{ labeled
| label =
if newLabel == "" then
labeled.label
else
newLabel ++ ", " ++ labeled.label
}
x ->
x
in
List.map (applyLabel newLabels) attrs
createAttributeTest : Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
createAttributeTest :
Found
-> Dict String Found
-> List Int
-> Int
-> Surroundings
-> Attr msg
-> List LayoutTest
createAttributeTest parent cache level attrIndex surroundings attr =
let
indexLabel =
levelToString (attrIndex :: level)
domId =
"#" ++ levelToString level
in
case attr of
Attr _ ->
[]
Label _ ->
[]
Spacing _ ->
[]
AttrTest test ->
[ test surroundings
AttrTest details ->
[ { elementDomId = domId
, label = details.label
, expectations = details.test surroundings
}
]
Nearby nearby ->
@ -600,7 +621,15 @@ createAttributeTest parent cache level attrIndex surroundings attr =
, parentSpacing = 0
, level = attrIndex :: -1 :: level
, location = IsNearby nearby.location
, element = addAttribute (AttrTest (\context -> Test.test (nearby.label ++ " #" ++ indexLabel) (nearby.test context))) nearby.element
, element =
addAttribute
(AttrTest
{ label = nearby.label
, test =
nearby.test
}
)
nearby.element
}
Batch batch ->
@ -609,7 +638,10 @@ createAttributeTest parent cache level attrIndex surroundings attr =
|> List.concat
LabeledTest { label, test } ->
[ Test.test (label ++ " #" ++ indexLabel) (test surroundings)
[ { elementDomId = domId
, label = label
, expectations = test surroundings
}
]
@ -638,46 +670,43 @@ addAttribute attr el =
Text str
runTests :
Random.Seed
-> Test
->
List
( String
, Maybe
{ given : Maybe String
, description : String
, reason : Test.Runner.Failure.Reason
}
)
runTests seed tests =
let
run runner =
let
ran =
List.map Test.Runner.getFailureReason (runner.run ())
in
List.map2 Tuple.pair runner.labels ran
results =
case Test.Runner.fromTest 100 seed tests of
Test.Runner.Plain rnrs ->
List.map run rnrs
Test.Runner.Only rnrs ->
List.map run rnrs
Test.Runner.Skipping rnrs ->
List.map run rnrs
Test.Runner.Invalid invalid ->
let
_ =
Debug.log "Invalid tests" invalid
in
[]
in
List.concat results
-- runTests :
-- Random.Seed
-- -> Test
-- ->
-- List
-- ( String
-- , Maybe
-- { given : Maybe String
-- , description : String
-- , reason : Test.Runner.Failure.Reason
-- }
-- )
-- runTests seed tests =
-- let
-- run runner =
-- let
-- ran =
-- List.map Test.Runner.getFailureReason (runner.run ())
-- in
-- List.map2 Tuple.pair runner.labels ran
-- results =
-- case Test.Runner.fromTest 100 seed tests of
-- Test.Runner.Plain rnrs ->
-- List.map run rnrs
-- Test.Runner.Only rnrs ->
-- List.map run rnrs
-- Test.Runner.Skipping rnrs ->
-- List.map run rnrs
-- Test.Runner.Invalid invalid ->
-- let
-- _ =
-- Debug.log "Invalid tests" invalid
-- in
-- []
-- in
-- List.concat results
compareFormattedColor : Color -> String -> Bool

View File

@ -23,6 +23,8 @@ module Testable.Element exposing
, maximum
, minimum
, moveDown
, moveLeft
, moveRight
, moveUp
, none
, onLeft
@ -104,6 +106,12 @@ textColumn =
Testable.TextColumn
{-| Old labeling mechanism that i removed to hastily
-}
label str =
Testable.Batch []
moveUp x =
Testable.Attr (Element.moveUp x)
@ -112,6 +120,14 @@ moveDown x =
Testable.Attr (Element.moveDown x)
moveRight x =
Testable.Attr (Element.moveRight x)
moveLeft x =
Testable.Attr (Element.moveLeft x)
type Length
= Px Int
| Fill Int
@ -156,11 +172,6 @@ fillPortion =
Fill
label : String -> Testable.Attr msg
label =
Testable.Label
transparent : Bool -> Testable.Attr msg
transparent on =
Testable.LabeledTest
@ -172,7 +183,7 @@ transparent on =
"opaque"
, attr = Element.transparent on
, test =
\context _ ->
\context ->
let
selfTransparency =
context.self.style
@ -186,7 +197,7 @@ transparent on =
else
"1"
in
Expect.equal value selfTransparency
[ Testable.equal value selfTransparency ]
}
@ -197,8 +208,8 @@ isVisible =
{ label = "is-visible"
, attr = Element.htmlAttribute (Html.Attributes.style "not" "applicable")
, test =
\context _ ->
Expect.equal context.self.isVisible True
\context ->
[ Testable.equal context.self.isVisible True ]
}
@ -209,14 +220,14 @@ alpha a =
{ label = "alpha-" ++ String.fromFloat a
, attr = Element.alpha a
, test =
\context _ ->
\context ->
let
selfTransparency =
context.self.style
|> Dict.get "opacity"
|> Maybe.withDefault "notfound"
in
Expect.equal (String.fromFloat a) selfTransparency
[ Testable.equal (String.fromFloat a) selfTransparency ]
}
@ -227,8 +238,8 @@ padding pad =
{ label = "padding " ++ String.fromInt pad
, attr = Element.padding pad
, test =
\found _ ->
Expect.true ("Padding " ++ String.fromInt pad ++ " is present")
\found ->
[ Testable.true ("Padding " ++ String.fromInt pad ++ " is present")
(List.all ((==) pad)
[ floor found.self.bbox.padding.left
, floor found.self.bbox.padding.right
@ -236,6 +247,7 @@ padding pad =
, floor found.self.bbox.padding.bottom
]
)
]
}
@ -246,8 +258,8 @@ paddingXY x y =
{ label = "paddingXY " ++ String.fromInt x ++ ", " ++ String.fromInt y
, attr = Element.paddingXY x y
, test =
\found _ ->
Expect.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
\found ->
[ Testable.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
(List.all ((==) x)
[ floor found.self.bbox.padding.left
, floor found.self.bbox.padding.right
@ -257,6 +269,7 @@ paddingXY x y =
, floor found.self.bbox.padding.bottom
]
)
]
}
@ -326,18 +339,20 @@ widthHelper maybeMin maybeMax len =
|> addMax
)
, test =
\found _ ->
Expect.all
[ \_ ->
Expect.true "exact width is exact" (floor found.self.bbox.width == val)
, \_ -> Expect.true "min/max is upheld" (minMaxTest (floor found.self.bbox.width))
]
()
\found ->
[ Testable.true "exact width is exact" (floor found.self.bbox.width == val)
, Testable.true "min/max is upheld" (minMaxTest (floor found.self.bbox.width))
]
}
Fill portion ->
Testable.LabeledTest
{ label = "width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
{ label =
if portion == 1 then
"width fill" ++ minLabel ++ maxLabel
else
"width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
, attr =
Element.width
(Element.fillPortion portion
@ -345,30 +360,30 @@ widthHelper maybeMin maybeMax len =
|> addMax
)
, test =
\context _ ->
\context ->
if List.member context.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
Expect.true "width fill doesn't apply to onright/onleft elements" True
[ Testable.true "width fill doesn't apply to onright/onleft elements" True ]
else
let
parentAvailableWidth =
context.parent.bbox.width - (context.self.bbox.padding.left + context.self.bbox.padding.right)
in
case context.location of
[ case context.location of
Testable.IsNearby _ ->
Expect.true "Nearby Element has fill width"
Testable.true "Nearby Element has fill width"
((floor context.parent.bbox.width == floor context.self.bbox.width)
|| minMaxTest (floor context.self.bbox.width)
)
Testable.InColumn ->
Expect.true "Element within column has fill width"
Testable.true "Element within column has fill width"
((floor parentAvailableWidth == floor context.self.bbox.width)
|| minMaxTest (floor context.self.bbox.width)
)
Testable.InEl ->
Expect.true "Element within element has fill width" <|
Testable.true "Element within element has fill width" <|
(floor parentAvailableWidth == floor context.self.bbox.width)
|| minMaxTest (floor context.self.bbox.width)
@ -377,9 +392,10 @@ widthHelper maybeMin maybeMax len =
spacePerPortion =
parentAvailableWidth / toFloat (List.length context.siblings + 1)
in
Expect.true "element has fill width" <|
Testable.true "element has fill width" <|
(floor spacePerPortion == floor context.self.bbox.width)
|| minMaxTest (floor context.self.bbox.width)
]
}
Shrink ->
@ -392,7 +408,7 @@ widthHelper maybeMin maybeMax len =
|> addMax
)
, test =
\context _ ->
\context ->
let
childWidth child =
-- TODO: add margin values to widths
@ -412,12 +428,12 @@ widthHelper maybeMin maybeMax len =
if totalChildren == 0 then
-- TODO: The issue is that we have a hard time measuring `text` elements
-- So if a element has a text child, then it's width isn't going to show up in the system.
expectRoundedEquality context.self.bbox.width context.self.bbox.width
[ expectRoundedEquality context.self.bbox.width context.self.bbox.width ]
else
-- This fails if this element is actually a column
-- So we need to capture what this element is in order to do this calculation.
expectRoundedEquality (totalChildren + horizontalPadding + spacingValue) context.self.bbox.width
[ expectRoundedEquality (totalChildren + horizontalPadding + spacingValue) context.self.bbox.width ]
}
@ -487,16 +503,12 @@ heightHelper maybeMin maybeMax len =
|> addMax
)
, test =
\found _ ->
Expect.all
[ \_ ->
Expect.true ("exact height is exact: " ++ String.fromInt (floor found.self.bbox.height) ++ "," ++ String.fromInt val)
(floor found.self.bbox.height == val)
, \_ ->
Expect.true "min/max holds true"
(minMaxTest (floor found.self.bbox.height))
]
()
\found ->
[ Testable.true ("exact height is exact: " ++ String.fromInt (floor found.self.bbox.height) ++ "," ++ String.fromInt val)
(floor found.self.bbox.height == val)
, Testable.true "min/max holds true"
(minMaxTest (floor found.self.bbox.height))
]
}
Fill portion ->
@ -509,30 +521,30 @@ heightHelper maybeMin maybeMax len =
|> addMax
)
, test =
\context _ ->
if List.member context.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
Expect.true "height fill doesn't apply to above/below elements" True
\context ->
[ if List.member context.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
Testable.true "height fill doesn't apply to above/below elements" True
else
else
let
parentAvailableHeight =
context.parent.bbox.height - (context.self.bbox.padding.top + context.self.bbox.padding.bottom)
in
case context.location of
Testable.IsNearby _ ->
Expect.true "Nearby Element has fill height"
Testable.true "Nearby Element has fill height"
((floor context.parent.bbox.height == floor context.self.bbox.height)
|| minMaxTest (floor context.self.bbox.height)
)
Testable.InColumn ->
Expect.true "Element within column has fill height"
Testable.true "Element within column has fill height"
((floor parentAvailableHeight == floor context.self.bbox.height)
|| minMaxTest (floor context.self.bbox.height)
)
Testable.InEl ->
Expect.true "Element within el has fill height" <|
Testable.true "Element within el has fill height" <|
(floor parentAvailableHeight == floor context.self.bbox.height)
|| minMaxTest (floor context.self.bbox.height)
@ -541,9 +553,10 @@ heightHelper maybeMin maybeMax len =
spacePerPortion =
parentAvailableHeight / toFloat (List.length context.siblings + 1)
in
Expect.true "el has fill height" <|
Testable.true "el has fill height" <|
(floor spacePerPortion == floor context.self.bbox.height)
|| minMaxTest (floor context.self.bbox.height)
]
}
Shrink ->
@ -556,7 +569,7 @@ heightHelper maybeMin maybeMax len =
|> addMax
)
, test =
\context _ ->
\context ->
let
childWidth child =
-- TODO: add margin values to heights
@ -576,12 +589,12 @@ heightHelper maybeMin maybeMax len =
if totalChildren == 0 then
-- TODO: The issue is that we have a hard time measuring `text` elements
-- So if a element has a text child, then it's height isn't going to show up in the system.
expectRoundedEquality context.self.bbox.height context.self.bbox.height
[ expectRoundedEquality context.self.bbox.height context.self.bbox.height ]
else
-- This fails if this element is actually a column
-- So we need to capture what this element is in order to do this calculation.
expectRoundedEquality (totalChildren + verticalPadding + spacingValue) context.self.bbox.height
[ expectRoundedEquality (totalChildren + verticalPadding + spacingValue) context.self.bbox.height ]
}
@ -603,7 +616,7 @@ spacing space =
{ label = "spacing: " ++ String.fromInt space
, attr = Element.spacing space
, test =
\found _ ->
\found ->
let
findDistance child total =
List.concatMap
@ -649,7 +662,7 @@ spacing space =
[]
distances
in
Expect.true
[ Testable.true
("All children are at least "
++ String.fromInt space
++ " pixels apart."
@ -657,6 +670,7 @@ spacing space =
++ " are not though"
)
(allAreSpaced == [])
]
}
]
@ -680,28 +694,28 @@ alignLeft =
{ label = "alignLeft"
, attr = Element.alignLeft
, test =
\found _ ->
if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
Expect.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
\found ->
[ if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
Testable.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
else if
else if
List.member found.location
[ Testable.IsNearby Testable.InFront
, Testable.IsNearby Testable.Behind
, Testable.IsNearby Testable.Above
, Testable.IsNearby Testable.Below
]
then
then
expectRoundedEquality
found.self.bbox.left
found.parent.bbox.left
else if List.length found.siblings == 0 then
else if List.length found.siblings == 0 then
expectRoundedEquality
found.self.bbox.left
(found.parent.bbox.left + found.parent.bbox.padding.left)
else
else
case found.location of
Testable.InRow ->
let
@ -724,6 +738,7 @@ alignLeft =
expectRoundedEquality
found.self.bbox.left
(found.parent.bbox.left + found.parent.bbox.padding.left)
]
}
@ -734,7 +749,7 @@ centerX =
{ label = "centerX"
, attr = Element.centerX
, test =
\found _ ->
\found ->
let
selfCenter : Float
selfCenter =
@ -745,12 +760,13 @@ centerX =
found.parent.bbox.left + (found.parent.bbox.width / 2)
in
if List.member found.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
Expect.true "centerX doesn't apply to elements that are onLeft or onRight" True
[ Testable.true "centerX doesn't apply to elements that are onLeft or onRight" True ]
else if List.length found.siblings == 0 then
expectRoundedEquality
[ expectRoundedEquality
selfCenter
parentCenter
]
else
case found.location of
@ -783,12 +799,13 @@ centerX =
/ 2
)
in
expectRoundedEquality
[ expectRoundedEquality
selfCenter
expectedCenter
]
_ ->
expectRoundedEquality selfCenter parentCenter
[ expectRoundedEquality selfCenter parentCenter ]
}
@ -799,9 +816,9 @@ alignRight =
{ label = "alignRight"
, attr = Element.alignRight
, test =
\found _ ->
\found ->
if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
Expect.true "alignRight doesn't apply to elements that are onLeft or onRight" True
[ Testable.true "alignRight doesn't apply to elements that are onLeft or onRight" True ]
else if
List.member found.location
@ -811,14 +828,16 @@ alignRight =
, Testable.IsNearby Testable.Below
]
then
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.right
found.parent.bbox.right
]
else if List.length found.siblings == 0 then
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.right
(found.parent.bbox.right + found.parent.bbox.padding.right)
]
else
case found.location of
@ -835,14 +854,16 @@ alignRight =
|> List.map (.width << .bbox)
|> List.sum
in
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.right
(found.parent.bbox.right - (found.parent.bbox.padding.right + widthsOnRight + spacings))
]
_ ->
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.right
(found.parent.bbox.right + found.parent.bbox.padding.right)
]
}
@ -853,9 +874,9 @@ alignTop =
{ label = "alignTop"
, attr = Element.alignTop
, test =
\found _ ->
\found ->
if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
Expect.true "alignTop doesn't apply to elements that are above or below" True
[ Testable.true "alignTop doesn't apply to elements that are above or below" True ]
else if
List.member found.location
@ -865,14 +886,16 @@ alignTop =
, Testable.IsNearby Testable.OnLeft
]
then
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.top
found.parent.bbox.top
]
else if List.length found.siblings == 0 then
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.top
(found.parent.bbox.top + found.parent.bbox.padding.top)
]
else
case found.location of
@ -889,14 +912,16 @@ alignTop =
|> List.map (.height << .bbox)
|> List.sum
in
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.top
(found.parent.bbox.top + (found.parent.bbox.padding.top + heightsAbove + spacings))
]
_ ->
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.top
found.parent.bbox.top
]
}
@ -907,9 +932,9 @@ alignBottom =
{ label = "alignBottom"
, attr = Element.alignBottom
, test =
\found _ ->
\found ->
if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
Expect.true "alignBottom doesn't apply to elements that are above or below" True
[ Testable.true "alignBottom doesn't apply to elements that are above or below" True ]
else if
List.member found.location
@ -919,14 +944,16 @@ alignBottom =
, Testable.IsNearby Testable.OnLeft
]
then
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.bottom
found.parent.bbox.bottom
]
else if List.length found.siblings == 0 then
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.bottom
(found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
]
else
case found.location of
@ -943,22 +970,30 @@ alignBottom =
|> List.map (.height << .bbox)
|> List.sum
in
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.bottom
(found.parent.bbox.bottom - (found.parent.bbox.padding.bottom + heightsBelow + spacings))
]
_ ->
expectRoundedEquality
[ expectRoundedEquality
found.self.bbox.bottom
(found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
]
}
expectRoundedEquality x y =
Expect.true ("within 1 of each other " ++ String.fromFloat x ++ ":" ++ String.fromFloat y)
Testable.true
("within 1 of each other " ++ floatToString x ++ ", " ++ floatToString y)
(abs (x - y) < 1)
floatToString : Float -> String
floatToString x =
String.fromFloat (toFloat (round (x * 100)) / 100)
{-| -}
centerY : Testable.Attr msg
centerY =
@ -966,7 +1001,7 @@ centerY =
{ label = "centerY"
, attr = Element.centerY
, test =
\found _ ->
\found ->
let
selfCenter =
found.self.bbox.top + (found.self.bbox.height / 2)
@ -975,12 +1010,13 @@ centerY =
found.parent.bbox.top + (found.parent.bbox.height / 2)
in
if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
Expect.true "centerY doesn't apply to elements that are above or below" True
[ Testable.true "centerY doesn't apply to elements that are above or below" True ]
else if List.length found.siblings == 0 then
expectRoundedEquality
[ expectRoundedEquality
selfCenter
parentCenter
]
else
case found.location of
@ -1013,12 +1049,13 @@ centerY =
/ 2
)
in
expectRoundedEquality
[ expectRoundedEquality
selfCenter
expectedCenter
]
_ ->
expectRoundedEquality selfCenter parentCenter
[ expectRoundedEquality selfCenter parentCenter ]
}
@ -1030,8 +1067,8 @@ above element =
, element = element
, label = "above"
, test =
\found _ ->
expectRoundedEquality found.self.bbox.bottom found.parent.bbox.top
\found ->
[ expectRoundedEquality found.self.bbox.bottom found.parent.bbox.top ]
}
@ -1043,8 +1080,8 @@ below element =
, element = element
, label = "below"
, test =
\found _ ->
expectRoundedEquality found.self.bbox.top found.parent.bbox.bottom
\found ->
[ expectRoundedEquality found.self.bbox.top found.parent.bbox.bottom ]
}
@ -1056,8 +1093,8 @@ onRight element =
, element = element
, label = "onRight"
, test =
\found _ ->
expectRoundedEquality found.self.bbox.left found.parent.bbox.right
\found ->
[ expectRoundedEquality found.self.bbox.left found.parent.bbox.right ]
}
@ -1069,8 +1106,8 @@ onLeft element =
, element = element
, label = "onLeft"
, test =
\found _ ->
expectRoundedEquality found.self.bbox.right found.parent.bbox.left
\found ->
[ expectRoundedEquality found.self.bbox.right found.parent.bbox.left ]
}
@ -1086,11 +1123,12 @@ inFront element =
, element = element
, label = "inFront"
, test =
withinHelper
\found ->
[ withinHelper found ]
}
withinHelper found _ =
withinHelper found =
let
horizontalCheck =
if found.self.bbox.width > found.parent.bbox.width then
@ -1114,7 +1152,7 @@ withinHelper found _ =
, compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
]
in
Expect.true "within the confines of the parent"
Testable.true "within the confines of the parent"
(List.all ((==) True)
(List.concat
[ horizontalCheck
@ -1132,5 +1170,6 @@ behindContent element =
, element = element
, label = "behindContent"
, test =
withinHelper
\found ->
[ withinHelper found ]
}

View File

@ -5,23 +5,23 @@ module Testable.Element.Background exposing (color)
import Dict
import Element exposing (Color)
import Element.Background as Background
import Expect
import Testable
color : Color -> Testable.Attr msg
color clr =
Testable.LabeledTest
{ label = "background color-" ++ Testable.formatColor clr
{ label = "background color"
, attr = Background.color clr
, test =
\context _ ->
\context ->
let
selfBackgroundColor =
context.self.style
|> Dict.get "background-color"
|> Maybe.withDefault "notfound"
in
Expect.true ("Expected color: " ++ (Testable.formatColor clr ++ " vs found:" ++ selfBackgroundColor))
[ Testable.true ("expected: " ++ (Testable.formatColor clr ++ ", found:" ++ selfBackgroundColor))
(Testable.compareFormattedColor clr selfBackgroundColor)
]
}

View File

@ -15,15 +15,16 @@ color clr =
{ label = "font color-" ++ Testable.formatColor clr
, attr = Font.color clr
, test =
\context _ ->
\context ->
let
selfFontColor =
context.self.style
|> Dict.get "color"
|> Maybe.withDefault "notfound"
in
Expect.true ("Color Match - " ++ (Testable.formatColor clr ++ " vs " ++ selfFontColor))
[ Testable.true ("Color Match - " ++ (Testable.formatColor clr ++ " vs " ++ selfFontColor))
(Testable.compareFormattedColor clr selfFontColor)
]
}
@ -33,7 +34,7 @@ size i =
{ label = "font size-" ++ String.fromInt i
, attr = Font.size i
, test =
\context _ ->
\context ->
let
selfFontSize =
context.self.style
@ -43,6 +44,7 @@ size i =
formattedInt =
String.fromInt i
in
Expect.true ("Size Match - " ++ (formattedInt ++ " vs " ++ selfFontSize))
[ Testable.true ("Size Match - " ++ (formattedInt ++ " vs " ++ selfFontSize))
(formattedInt == selfFontSize)
]
}

View File

@ -3,27 +3,23 @@ port module Testable.Runner exposing (Msg, TestableProgram, program, show)
{-| -}
import Browser
import Char
import Dict exposing (Dict)
import Element
import Element.Background as Background
import Element.Border as Border
import Element.Events as Events
import Element.Font as Font
import Html exposing (Html)
import Parser exposing ((|.), (|=))
import Html.Attributes
import Process
import Random
import Set
import Task
import Test.Runner
import Test.Runner.Failure as Failure
import Testable
import Time
show : Testable.Element msg -> Html msg
show =
Testable.render
Testable.toHtml
type alias TestableProgram =
@ -56,6 +52,7 @@ program tests =
( { current = current
, upcoming = upcoming
, finished = []
, highlightDomId = Nothing
}
, Task.perform (always Analyze)
(Process.sleep 32
@ -80,6 +77,7 @@ type alias Model msg =
{ current : Maybe ( String, Testable.Element msg )
, upcoming : List ( String, Testable.Element msg )
, finished : List (WithResults (Testable.Element msg))
, highlightDomId : Maybe String
}
@ -87,49 +85,44 @@ type alias WithResults thing =
{ element : thing
, label : String
, results :
List
( String
, Maybe
{ given : Maybe String
, description : String
, reason : Failure.Reason
}
)
List Testable.LayoutTest
}
prepareResults :
encodeForReport :
List (WithResults (Testable.Element msg))
->
List
{ label : String
, results :
List
( String
, Maybe
{ given : Maybe String
, description : String
}
)
{ description : String
, passing : Bool
, todo : Bool
}
}
prepareResults withResults =
encodeForReport withResults =
let
prepareNode ( x, maybeResult ) =
( x
, case maybeResult of
Nothing ->
Nothing
prepareExpectation layoutTest exp =
case exp of
Testable.Todo description ->
{ description = description
, passing = False
, todo = True
}
Just res ->
Just
{ given = res.given
, description = res.description
}
)
Testable.Expect details ->
{ description = details.description
, passing = details.result
, todo = False
}
prepareNode layoutTest =
List.map (prepareExpectation layoutTest) layoutTest.expectations
prepare { label, results } =
{ label = label
, results = List.map prepareNode results
, results = List.concatMap prepareNode results
}
in
List.map prepare withResults
@ -138,6 +131,7 @@ prepareResults withResults =
type Msg
= NoOp
| Analyze
| HighlightDomID (Maybe String)
| RefreshBoundingBox
(List
{ id : String
@ -151,14 +145,8 @@ type Msg
runTest : Dict String Testable.Found -> String -> Testable.Element msg -> WithResults (Testable.Element msg)
runTest boxes label element =
let
tests =
Testable.toTest label boxes element
seed =
Random.initialSeed 227852860
results =
Testable.runTests seed tests
Testable.runTests boxes element
in
{ element = element
, label = label
@ -172,6 +160,11 @@ update msg model =
NoOp ->
( model, Cmd.none )
HighlightDomID newId ->
( { model | highlightDomId = newId }
, Cmd.none
)
RefreshBoundingBox boxes ->
case model.current of
Nothing ->
@ -203,7 +196,7 @@ update msg model =
| current = Nothing
, finished = model.finished ++ [ currentResults ]
}
, report (prepareResults (currentResults :: model.finished))
, report (encodeForReport (currentResults :: model.finished))
)
newCurrent :: remaining ->
@ -246,23 +239,42 @@ view model =
[ Element.spacing 20
, Element.padding 20
, Element.width (Element.px 800)
-- , Background.color Color.grey
]
[ Element.none ]
finished :: remaining ->
if False then
viewResultsInline finished
else
Element.layout [] <|
Element.column
Element.layout
[ Font.size 16
, Element.inFront (Element.html (viewElementHighlight model))
]
<|
Element.row [ Element.width Element.fill ]
[ Element.el
[ Element.width Element.fill
]
(Element.el
[ Element.centerX
, Element.padding 100
, Border.dashed
, Border.width 2
, Border.color palette.lightGrey
, Element.inFront
(Element.el
[ Font.size 14
, Font.color palette.lightGrey
]
(Element.text "test case")
)
]
(Testable.toElement finished.element)
)
, Element.column
[ Element.spacing 20
, Element.padding 20
, Element.width (Element.px 800)
, Element.width Element.fill
]
(List.map viewResult (finished :: remaining))
]
]
}
@ -274,162 +286,181 @@ view model =
Just ( label, current ) ->
{ title = "running"
, body =
[ Testable.render current ]
[ Testable.toHtml current ]
}
viewResultsInline : WithResults (Testable.Element Msg) -> Html Msg
viewResultsInline testable =
Html.div
[]
[ viewResultsAnnotationStylesheet testable.results
, Testable.render testable.element
]
viewElementHighlight model =
case model.highlightDomId of
Nothing ->
Html.text ""
Just highlightDomId ->
let
elementHighlight =
highlightDomId ++ " { outline: solid; }"
{-| Our ID is part of our label. This could be fixed farther down the chain, but I think it'd be pretty involved.
testId =
highlightDomId
|> String.dropLeft 1
|> String.append "#tests-"
So, now we can just parse the id out of the label.
testHighlight =
testId ++ " { outline: dashed; }"
-}
parseId str =
str
|> Parser.run
(Parser.succeed identity
|. Parser.chompWhile (\c -> c /= '#')
|= Parser.variable
{ start = \c -> c == '#'
, inner = \c -> Char.isAlphaNum c || c == '-'
, reserved = Set.empty
}
)
|> Result.toMaybe
viewResultsAnnotationStylesheet results =
let
toStyleClass ( label, maybeFailure ) =
case maybeFailure of
Nothing ->
""
Just failure ->
case parseId label of
Nothing ->
Debug.log "NO ID FOUND" label
Just id ->
id ++ " { background-color:red; outline: dashed; };"
styleSheet =
results
|> List.map toStyleClass
|> String.join ""
in
Html.node "style"
[]
[ Html.text styleSheet
]
styleSheet =
String.join "\n"
[ elementHighlight
, testHighlight
]
in
Html.node "style"
[]
[ Html.text styleSheet
]
viewResult : WithResults (Testable.Element Msg) -> Element.Element Msg
viewResult testable =
let
isPassing result =
case Tuple.second result of
Nothing ->
isExpectationPassing result =
case result of
Testable.Todo label ->
True
Just _ ->
False
Testable.Expect details ->
details.result
isPassing layoutTest =
List.any isExpectationPassing layoutTest.expectations
( passing, failing ) =
List.partition isPassing testable.results
viewSingle result =
case result of
( label, Nothing ) ->
Element.el
[ Background.color palette.green
, Font.color palette.black
, Element.paddingXY 20 10
, Element.alignLeft
, Border.rounded 3
]
<|
Element.text ("Success! - " ++ label)
( label, Just ({ given, description } as reason) ) ->
Element.column
[ Background.color palette.red
, Font.color palette.black
, Element.paddingXY 20 10
, Element.alignLeft
, Element.width Element.shrink
-- , Element.spacing 25
, Border.rounded 3
]
[ Element.el [ Element.width Element.fill ] <| Element.text label
, Element.el [ Element.width Element.fill ] <| Element.text (viewReason reason)
]
in
Element.column
[ Border.width 1
, Border.color palette.lightGrey
, Element.padding 20
, Element.height Element.shrink
, Element.alignLeft
[ Element.alignLeft
, Element.spacing 16
]
[ Element.el [ Font.bold, Font.size 64 ] (Element.text testable.label)
[ Element.el [ Font.size 24 ] (Element.text testable.label)
, Element.column [ Element.alignLeft, Element.spacing 20 ]
(failing
|> List.map viewSingle
(List.map viewLayoutTest failing)
, Element.column
[ Element.alignLeft, Element.spacing 16 ]
(passing
|> groupBy .elementDomId
|> List.map viewLayoutTestGroup
)
, Element.el
[ Element.alignLeft
, Element.spacing 20
, Background.color palette.green
, Font.color palette.black
, Element.paddingXY 20 10
, Element.alignLeft
, Border.rounded 3
]
(Element.text (String.fromInt (List.length passing) ++ " tests passing!"))
]
viewReason { description, reason } =
case reason of
Failure.Custom ->
description
groupBy fn list =
groupWhile (\one two -> fn one == fn two) list
|> List.map
(\( fst, remaining ) ->
{ id = fn fst
, members = fst :: remaining
}
)
Failure.Equality one two ->
description ++ " " ++ one ++ " " ++ two
Failure.Comparison one two ->
description ++ " " ++ one ++ " " ++ two
groupWhile : (a -> a -> Bool) -> List a -> List ( a, List a )
groupWhile isSameGroup items =
List.foldr
(\x acc ->
case acc of
[] ->
[ ( x, [] ) ]
Failure.ListDiff expected actual ->
"expected\n"
++ String.join " \n" expected
++ "actual\n"
++ String.join " \n" actual
( y, restOfGroup ) :: groups ->
if isSameGroup x y then
( x, y :: restOfGroup ) :: groups
Failure.CollectionDiff { expected, actual, extra, missing } ->
String.join "\n"
[ formatKeyValue "expected" expected
, formatKeyValue "actual" actual
, formatKeyValue "extra" (String.join ", " extra)
, formatKeyValue "missing" (String.join ", " missing)
]
else
( x, [] ) :: acc
)
[]
items
Failure.TODO ->
description
Failure.Invalid _ ->
description
viewLayoutTestGroup group =
let
testId =
group.id
|> String.dropLeft 1
|> String.append "tests-"
in
Element.column
[ Element.spacing 8
, Element.htmlAttribute (Html.Attributes.id testId)
, Events.onMouseEnter (HighlightDomID (Just group.id))
, Events.onMouseLeave (HighlightDomID Nothing)
, Element.htmlAttribute (Html.Attributes.style "user-select" "none")
]
[ Element.el [ Font.color palette.lightGrey ] (Element.text group.id)
, Element.column
[ Element.spacing 8
, Element.paddingXY 32 0
]
(List.map viewLayoutTest group.members)
]
type alias Grouped thing =
{ id : String
, members : List thing
}
viewLayoutTest layoutTest =
Element.column
[ Element.spacing 8
]
[ Element.row [ Element.spacing 8 ]
[ Element.el [ Font.bold ] (Element.text layoutTest.label)
-- , Element.el [ Font.color palette.lightGrey ] (Element.text layoutTest.elementDomId)
]
, Element.column [ Element.spacing 8 ]
(List.map viewLayoutExpectation layoutTest.expectations)
]
viewLayoutExpectation expectation =
case expectation of
Testable.Todo label ->
Element.row [ Element.spacing 4 ]
[ todo, Element.text label ]
Testable.Expect details ->
if details.result then
Element.row [ Element.spacing 4 ]
[ pass, Element.text details.description ]
else
Element.row [ Element.spacing 4 ]
[ fail, Element.text details.description ]
badge color text =
Element.el
[ Background.color color
, Font.color palette.black
, Element.paddingXY 4 8
, Border.rounded 2
]
(Element.text text)
todo =
badge palette.lightGrey "todo"
pass =
badge palette.green "pass"
fail =
badge palette.red "fail"
formatKeyValue : String -> String -> String
@ -442,12 +473,10 @@ port report :
{ label : String
, results :
List
( String
, Maybe
{ given : Maybe String
, description : String
}
)
{ description : String
, passing : Bool
, todo : Bool
}
}
-> Cmd msg

View File

@ -9,7 +9,7 @@
<body id="root"></body>
<script type="text/javascript">
var app = Elm.InFrontSize.init();
var app = Elm.WeirdCentering.init();
var test_results = "waiting..";