git.iko.soy/typeable/elm-ui
on master help sign in
.github/ISSUE_TEMPLATE/bug-unexpected-behavior-report.md+1−1
since splitsnapshots
unifiedsplit
@@ -8,7 +8,7 @@ about: Capture any unexpected behavior or bugs you've found with this library
8
8
9If you can, please put together a [Short, Self Contained, Correct, Example](http://sscce.org/) of what you're encountering. It really helps if the example is as succinct as possible.
9If you can, please put together a [Short, Self Contained, Correct, Example](http://sscce.org/) of what you're encountering. It really helps if the example is as succinct as possible.
10
10
11−Here's a basic template in [ellie](https://ellie-app.com/3fhCyrxjLw3a1) to get you started!
11+Here's a basic template in [ellie](https://www.ellie-app.com/3fhCyrxjLw3a1) to get you started!
12
12
13**Expected behavior**
13**Expected behavior**
14A clear and concise description of what you expected to happen.
14A clear and concise description of what you expected to happen.
.github/workflows/docs-preview.ymlnew file+36
@@ -0,0 +1,36 @@
1+# This is a basic workflow to help you get started with Actions
2+
3+name: Documentation
4+
5+# Controls when the action will run. Triggers the workflow on push or pull request
6+# events but only for the master branch
7+on:
8+ push:
9+ branches: [ master ]
10+ pull_request:
11+ branches: [ master ]
12+
13+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
14+jobs:
15+ # This workflow contains a single job called "build"
16+ build:
17+ # The type of runner that the job will run on
18+ runs-on: ubuntu-latest
19+
20+
21+ # Steps represent a sequence of tasks that will be executed as part of the job
22+ steps:
23+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
24+ - uses: actions/checkout@v2
25+
26+ - name: Setup node
27+ uses: actions/setup-node@v1
28+ with:
29+ node-version: 10
30+
31+ - name: Install
32+ run: npm install
33+
34+ # Runs a single command using the runners shell
35+ - name: Run a one-line script
36+ run: npm run test-doc-preview
.github/workflows/label-issues-from-body.ymlnew file+14
@@ -0,0 +1,14 @@
1+name: "Label issue from body keyword"
2+on:
3+ issues:
4+ types: [opened, edited]
5+
6+jobs:
7+ test:
8+ runs-on: ubuntu-latest
9+ steps:
10+ - uses: Naturalclar/issue-action@v1.0.0
11+ with:
12+ keywords: '["https://ellie-app.com/"]'
13+ labels: '["has-ellie"]'
14+ github-token: "${{ secrets.GITHUB_TOKEN }}"
.github/workflows/label-issues-from-comments.ymlnew file+59
@@ -0,0 +1,59 @@
1+name: "Label issue from comment keyword"
2+on:
3+ issue_comment:
4+ types: [created, edited]
5+
6+jobs:
7+ test:
8+ runs-on: ubuntu-latest
9+ steps:
10+ - uses: Amwam/issue-comment-action@v1.3.1
11+ with:
12+ keywords: '["#bug"]'
13+ labels: '["bug"]'
14+ github-token: "${{ secrets.GITHUB_TOKEN }}"
15+ - uses: Amwam/issue-comment-action@v1.3.1
16+ with:
17+ keywords: '["#feature-suggestion"]'
18+ labels: '["feature-suggestion"]'
19+ github-token: "${{ secrets.GITHUB_TOKEN }}"
20+ - uses: Amwam/issue-comment-action@v1.3.1
21+ with:
22+ keywords: '["#has-ellie", "https://ellie-app.com/"]'
23+ labels: '["has-ellie"]'
24+ github-token: "${{ secrets.GITHUB_TOKEN }}"
25+ - uses: Amwam/issue-comment-action@v1.3.1
26+ with:
27+ keywords: '["#no-ellie"]'
28+ labels: '["no-ellie"]'
29+ github-token: "${{ secrets.GITHUB_TOKEN }}"
30+ - uses: Amwam/issue-comment-action@v1.3.1
31+ with:
32+ keywords: '["#has-test"]'
33+ labels: '["has-test"]'
34+ github-token: "${{ secrets.GITHUB_TOKEN }}"
35+ - uses: Amwam/issue-comment-action@v1.3.1
36+ with:
37+ keywords: '["#pain-point"]'
38+ labels: '["pain-point"]'
39+ github-token: "${{ secrets.GITHUB_TOKEN }}"
40+ - uses: Amwam/issue-comment-action@v1.3.1
41+ with:
42+ keywords: '["#test-incorrectly-passing"]'
43+ labels: '["test-incorrectly-passing"]'
44+ github-token: "${{ secrets.GITHUB_TOKEN }}"
45+ - uses: Amwam/issue-comment-action@v1.3.1
46+ with:
47+ keywords: '["#test-not-compiling"]'
48+ labels: '["test-not-compiling"]'
49+ github-token: "${{ secrets.GITHUB_TOKEN }}"
50+ - uses: Amwam/issue-comment-action@v1.3.1
51+ with:
52+ keywords: '["#test-failing"]'
53+ labels: '["test-failing"]'
54+ github-token: "${{ secrets.GITHUB_TOKEN }}"
55+ - uses: Amwam/issue-comment-action@v1.3.1
56+ with:
57+ keywords: '["#unexpected"]'
58+ labels: '["unexpected"]'
59+ github-token: "${{ secrets.GITHUB_TOKEN }}"
.github/workflows/tests.ymlnew file+43
@@ -0,0 +1,43 @@
1+name: Test Suite
2+
3+on:
4+ push:
5+ branches: [ master ]
6+ pull_request:
7+ branches: [ master ]
8+
9+
10+jobs:
11+ testing:
12+ runs-on: ubuntu-latest
13+
14+ steps:
15+ - uses: actions/checkout@v2
16+
17+ - name: Setup node
18+ uses: actions/setup-node@v1
19+ with:
20+ node-version: 10
21+
22+ - uses: actions/cache@v1
23+ with:
24+ path: ~/.npm
25+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
26+ restore-keys: |
27+ ${{ runner.os }}-node-
28+
29+ - name: Install
30+ run: npm ci
31+
32+ - name: Elm test suite
33+ run: npm run test
34+
35+ - uses: webfactory/ssh-agent@v0.2.0
36+ with:
37+ ssh-private-key: ${{ secrets.ELM_UI_TESTING_SSH_PRIVATE_KEY }}
38+
39+ - name: Rendering test suite on saucelabs
40+ run: npm run test-render-sauce-automated -- --build=2.0.0 --name=development
41+ env:
42+ SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
43+ SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
CSS-LOOKUP.md+1−1
@@ -15,4 +15,4 @@ CSS | Style Elements | Description
15`margin` | __N/A__ Instead, check out `padding` and `spacing` | `margin` in CSS was designed to fight with `padding`. This library was designed to minimize override logic and properties that fight with each other in order to create a layout language that is predictable and easy to think about. The result is that in style elements, there's generally only *one place* where an effect can happen.
15`margin` | __N/A__ Instead, check out `padding` and `spacing` | `margin` in CSS was designed to fight with `padding`. This library was designed to minimize override logic and properties that fight with each other in order to create a layout language that is predictable and easy to think about. The result is that in style elements, there's generally only *one place* where an effect can happen.
16`:hover`, `:focus`, `:active` | `mouseOver`, `focused`, `mouseDown` | Only certain styles are allowed to be in a pseudo state. They have the type `Attr decorative msg`, which means they can be either an `Attribute` or a `Decoration`.
16`:hover`, `:focus`, `:active` | `mouseOver`, `focused`, `mouseDown` | Only certain styles are allowed to be in a pseudo state. They have the type `Attr decorative msg`, which means they can be either an `Attribute` or a `Decoration`.
17`<form>` | __N/a__ | __Elm__ already has a mechanism for submiting data to a server, namely the `Http` package. There has been some mention that the `form` element might be beneficial accessibility-wise, which I'm definitely open to hearing about!
17`<form>` | __N/a__ | __Elm__ already has a mechanism for submiting data to a server, namely the `Http` package. There has been some mention that the `form` element might be beneficial accessibility-wise, which I'm definitely open to hearing about!
18−`onSubmit` | __N/A__ | Similar to `<form>`, there is no `onSubmit` behavior. Likely if you're attempting to capture some of the keybaord related behavior of `onSubmit`, you're likely better just crafting a keyboard even handler in the first place!
18+`onSubmit` | __N/A__ | Similar to `<form>`, there is no `onSubmit` behavior. Likely if you're attempting to capture some of the keybaord related behavior of `onSubmit`, you're likely better just crafting a keyboard event handler in the first place!
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2018, Matthew Griffith
1+Copyright (c) 2020, Matthew Griffith
2All rights reserved.
2All rights reserved.
3
3
4Redistribution and use in source and binary forms, with or without
4Redistribution and use in source and binary forms, with or without
README.md+10−3
@@ -1,8 +1,7 @@
1−# Join the Elm UI Slack!
1+![](https://github.com/mdgriffith/elm-ui/workflows/Test%20Suite/badge.svg)
2
2
3−First, if you have a question about how to do something with the library, join `#elm-ui` on the [Elm Slack](https://elmlang.herokuapp.com/)! There are usually a number of people who are willing to help out, myself included.
4
3
5−Also, check out the talk that goes with the library, [Building a Better Design Toolkit](https://www.youtube.com/watch?v=Ie-gqwSHQr0&t=2s)
4+Check out the talk that goes with the library, [Building a Better Design Toolkit](https://www.youtube.com/watch?v=Ie-gqwSHQr0&t=2s)
6
5
7# A New Language for Layout and Interface
6# A New Language for Layout and Interface
8
7
@@ -55,6 +54,10 @@ myElement =
55 (text "stylish!")
54 (text "stylish!")
56```
55```
57
56
57+# Join the Elm UI Slack!
58+
59+First, if you have a question about how to do something with the library, join `#elm-ui` on the [Elm Slack](https://elmlang.herokuapp.com/)! There are usually a number of people who are willing to help out, myself included.
60+
58## History
61## History
59
62
60The work is based off of a rewrite of the [Style Elements](https://github.com/mdgriffith/style-elements) library. A lot of that work was originally released under the [Stylish Elephants](https://github.com/mdgriffith/stylish-elephants) project.
63The work is based off of a rewrite of the [Style Elements](https://github.com/mdgriffith/style-elements) library. A lot of that work was originally released under the [Stylish Elephants](https://github.com/mdgriffith/stylish-elephants) project.
@@ -63,6 +66,10 @@ The work is based off of a rewrite of the [Style Elements](https://github.com/md
63
66
64The community around `elm-ui` is maintaining a collection of examples called the [elm-ui-cookbook](https://github.com/rofrol/elm-ui-cookbook). If you are just starting out with `elm-ui`, or get stuck on specific things, this can be a great resource.
67The community around `elm-ui` is maintaining a collection of examples called the [elm-ui-cookbook](https://github.com/rofrol/elm-ui-cookbook). If you are just starting out with `elm-ui`, or get stuck on specific things, this can be a great resource.
65
68
69+## Notes
70+
71+Make sure to have `<!DOCTYPE html>` on top of your application; otherwise, things might get messed up in some browsers.
72+
66## Contributing
73## Contributing
67
74
68Want to help out fixing bugs or reporting issues?
75Want to help out fixing bugs or reporting issues?
benchmarks/elm.json+1−1
@@ -4,7 +4,7 @@
4 "src",
4 "src",
5 "../src"
5 "../src"
6 ],
6 ],
7− "elm-version": "0.19.0",
7+ "elm-version": "0.19.1",
8 "dependencies": {
8 "dependencies": {
9 "direct": {
9 "direct": {
10 "avh4/elm-color": "1.0.0",
10 "avh4/elm-color": "1.0.0",
benchmarks/src/View/Results.elm+1−1
@@ -142,7 +142,7 @@ viewLinks data =
142viewSingleLink result =
142viewSingleLink result =
143 Html.a
143 Html.a
144 [ Attr.href result.link
144 [ Attr.href result.link
145− , Html.style "padding-right" "30px"
145+ , Attr.style "padding-right" "30px"
146 ]
146 ]
147 [ Html.text result.name ]
147 [ Html.text result.name ]
148
148
cases/elm.jsondeleted−26
@@ -1,26 +0,0 @@
1−{
2− "type": "application",
3− "source-directories": [
4− "open",
5− "../src/"
6− ],
7− "elm-version": "0.19.0",
8− "dependencies": {
9− "direct": {
10− "elm/browser": "1.0.0",
11− "elm/core": "1.0.0",
12− "elm/html": "1.0.0",
13− "elm/json": "1.0.0",
14− "elm/virtual-dom": "1.0.0",
15− "ohanhi/lorem": "1.0.2"
16− },
17− "indirect": {
18− "elm/time": "1.0.0",
19− "elm/url": "1.0.0"
20− }
21− },
22− "test-dependencies": {
23− "direct": {},
24− "indirect": {}
25− }
26−}
cases/open/WeirdCentering.elmdeleted−31
@@ -1,31 +0,0 @@
1−module Main exposing (main)
2−
3−import Browser
4−import Element
5−import Element.Background as Background
6−import Element.Input as Input
7−import Html exposing (Html, button, div, text)
8−import Html.Events exposing (onClick)
9−
10−
11−main : Html msg
12−main =
13− Element.layout [] <|
14− Element.column []
15− [ Element.text "Example with centerX:"
16− , Element.row
17− [ Element.width Element.fill ]
18− [ Element.row [ Element.centerX, Background.color <| Element.rgb255 0 200 0 ]
19− [ Element.paragraph [ Element.width Element.shrink ] [ Element.text "Hello world" ]
20− , Element.paragraph [ Element.width Element.shrink ] [ Element.text "Hello world" ]
21− ]
22− ]
23− , Element.text "Example without centerX:"
24− , Element.row
25− [ Element.width Element.fill ]
26− [ Element.row [ Background.color <| Element.rgb255 0 200 0 ]
27− [ Element.paragraph [ Element.width Element.shrink ] [ Element.text "Hello world" ]
28− , Element.paragraph [ Element.width Element.shrink ] [ Element.text "Hello world" ]
29− ]
30− ]
31− ]
elm.json+1−1
@@ -3,7 +3,7 @@
3 "name": "mdgriffith/elm-ui",
3 "name": "mdgriffith/elm-ui",
4 "summary": "Layout and style that's easy to refactor, all without thinking about CSS.",
4 "summary": "Layout and style that's easy to refactor, all without thinking about CSS.",
5 "license": "BSD-3-Clause",
5 "license": "BSD-3-Clause",
6− "version": "1.1.5",
6+ "version": "1.1.8",
7 "exposed-modules": [
7 "exposed-modules": [
8 "Element",
8 "Element",
9 "Element.Input",
9 "Element.Input",
examples/Basic.elm+1−1
@@ -1,4 +1,4 @@
1−module Main exposing (..)
1+module Basic exposing (..)
2
2
3{-| -}
3{-| -}
4
4
examples/Form.elm+12−4
@@ -1,4 +1,4 @@
1−module Main exposing (..)
1+module Form exposing (..)
2
2
3{-| -}
3{-| -}
4
4
@@ -80,7 +80,14 @@ view model =
80 [ Font.size 20
80 [ Font.size 20
81 ]
81 ]
82 <|
82 <|
83− Element.column [ width (px 800), height shrink, centerY, centerX, spacing 36, padding 10, explain Debug.todo ]
83+ Element.column
84+ [ width (px 800)
85+ , height shrink
86+ , centerY
87+ , centerX
88+ , spacing 36
89+ , padding 10
90+ ]
84 [ el
91 [ el
85 [ Region.heading 1
92 [ Region.heading 1
86 , alignLeft
93 , alignLeft
@@ -117,7 +124,7 @@ view model =
117 , onChange = \new -> Update { model | username = new }
124 , onChange = \new -> Update { model | username = new }
118 , label = Input.labelAbove [ Font.size 14 ] (text "Username")
125 , label = Input.labelAbove [ Font.size 14 ] (text "Username")
119 }
126 }
120− , Input.currentPassword [ spacing 12, width shrink ]
127+ , Input.currentPassword [ spacing 12 ]
121 { text = model.password
128 { text = model.password
122 , placeholder = Nothing
129 , placeholder = Nothing
123 , onChange = \new -> Update { model | password = new }
130 , onChange = \new -> Update { model | password = new }
@@ -193,7 +200,8 @@ view model =
193 , Border.color darkBlue
200 , Border.color darkBlue
194 , paddingXY 32 16
201 , paddingXY 32 16
195 , Border.rounded 3
202 , Border.rounded 3
203+
196− , width fill
204+ -- , width fill
197 ]
205 ]
198 { onPress = Nothing
206 { onPress = Nothing
199 , label = Element.text "Place your lunch order!"
207 , label = Element.text "Place your lunch order!"
examples/Table.elm+1−1
@@ -1,4 +1,4 @@
1−module Main exposing (..)
1+module Table exposing (..)
2
2
3{-| -}
3{-| -}
4
4
examples/elm.json+19−22
@@ -1,25 +1,22 @@
1{
1{
2− "type": "application",
2+ "type": "application",
3− "source-directories": [
3+ "source-directories": [".", "../src/"],
4− ".",
5− "../src/"
6− ],
7− "elm-version": "0.19.0",
4+ "elm-version": "0.19.1",
8− "dependencies": {
5+ "dependencies": {
9− "direct": {
6+ "direct": {
10− "elm/browser": "1.0.0",
7+ "elm/browser": "1.0.0",
11− "elm/core": "1.0.0",
8+ "elm/core": "1.0.0",
12− "elm/html": "1.0.0",
9+ "elm/html": "1.0.0",
13− "elm/json": "1.0.0",
10+ "elm/json": "1.0.0",
14− "elm/virtual-dom": "1.0.0"
11+ "elm/virtual-dom": "1.0.0"
15− },
16− "indirect": {
17− "elm/time": "1.0.0",
18− "elm/url": "1.0.0"
19− }
20 },
12 },
21− "test-dependencies": {
22− "direct": {},
23− "indirect": {}
13+ "indirect": {
14+ "elm/time": "1.0.0",
15+ "elm/url": "1.0.0"
24 }
16 }
17+ },
18+ "test-dependencies": {
19+ "direct": {},
20+ "indirect": {}
21+ }
25−}
22+}
notes/RUNNING_TESTS.md+4−4
@@ -23,8 +23,8 @@ So, the tests in `elm-ui/tests-rendering/src/Tests` will render output, then har
23Run this locally via:
23Run this locally via:
24
24
25```bash
25```bash
26−yarn install # or npm install
26+npm install
27−yarn test-render # or npm run test-render
27+npm run test-render
28```
28```
29
29
30**Note** if you run this, it runs by default in headless chrome, so you won't see a browser actually open. Ideally you'll just see something like this:
30**Note** if you run this, it runs by default in headless chrome, so you won't see a browser actually open. Ideally you'll just see something like this:
@@ -36,7 +36,7 @@ Running locally on Chrome...
36Local Chrome -> All 18029 tests passed
36Local Chrome -> All 18029 tests passed
37```
37```
38
38
39−That's right. We have 18,029 tests. Whew.
39+That's right. We have 18,029 tests. Woohoo!
40
40
41# Running on Sauce Labs
41# Running on Sauce Labs
42
42
@@ -52,7 +52,7 @@ export SAUCE_USERNAME={your username}
52You can then run.
52You can then run.
53
53
54```bash
54```bash
55−yarn test-render-sauce
55+npm run test-render-sauce
56```
56```
57
57
58**Note**: The compiled `elm-ui` test needs to be made public somewhere in order for this to work. At the moment it's at my github.io account, though something more permanenet might be set up.
58**Note**: The compiled `elm-ui` test needs to be made public somewhere in order for this to work. At the moment it's at my github.io account, though something more permanenet might be set up.
notes/classifying-issues.mdnew file+29
@@ -0,0 +1,29 @@
1+# Classifying Issues
2+
3+
4+One of the first things we want to do is to keep our issues categorized.
5+
6+If you add a comment to an issue that references a label, that label will be assigned to that issue! (e.g. `#has-ellie` will add the `has-ellie` label.)
7+
8+[Here's one that I categorized](https://github.com/mdgriffith/elm-ui/issues/201)
9+
10+[Labels are described here](https://github.com/mdgriffith/elm-ui/labels)
11+
12+- **Feature Suggestions** - `#feature-suggestion` - This issue describes a feature suggestion. Tagging it with `#feature-suggestion` is all that needs to happen.
13+
14+- **Pain Point** - `#pain-point` - This is an issue that descrbes a challenge someone is facing, but makes not direct suggestion about how it should be solved. Here's a good example, [unable to applly text overflow ellipses](https://github.com/mdgriffith/elm-ui/issues/112).
15+
16+- **Bug** - `#bug` - The issue that's being described is potentially a bug. It may not be totally obvious what the fix is.
17+ - **Has Ellie** - `#has-ellie` - Every bug described should have an ellie showing the problem, even if it's sorta trivial.
18+ - **No Ellie** - If it's a bug, but has been checked for ellies and has no ellie, flag it as `#no-ellie`.
19+ - **Has Test** - `#has-test` - Once we have an ellie for the problem, we want a version of that code in the codebase so I can easily take a look.
20+ - The thing to do is to open a PR that copies that ellie into the [`tests-rendering/cases/open`](https://github.com/mdgriffith/elm-ui/tree/master/tests-rendering/cases/open) directory. Give it a succinct, human name.
21+ - Rename the `Element.*` imports as `Testable.Element.*`
22+ - Add a link to the issue, the title of the issue, and the body of the issue to the module comment. [Here's an example](https://github.com/mdgriffith/elm-ui/tree/master/tests-rendering/cases/open/InFrontSize.elm)
23+ - Test is compiling - Optionally you can see if the test is compiling by first `cd tests-rendering` and then running `elm-live cases/open/{The file}.elm --open --dir=view -- --output=view/elm.js --debug`
24+ - If it does not compile, flag the issue as `#test-not-compiling`. Don't worry about fixing it unless it's really obvious what needs to happen.
25+ - Check if the test passes. Do that by opening `tests-rendering/cases/view/view.html` after running the above compilation. If it says all passes, likely you should flag the issue as `#test-incorrectly-passing`.
26+ - If it's failing, you can flag as `#test-failing`
27+
28+
29+
notes/discussion/ColorWithoutAlpha.API.mdnew file+135
@@ -0,0 +1,135 @@
1+# API sketch for separating out alpha from Color
2+
3+**Motivation**
4+
5+If we separate out alpha from the core color type, we can
6+
7+1. Have a unified `Color` type that works in all contexts, including in webGL situations where transparency may be handled differently, or not at all.
8+
9+2. Encourage people to use actual color mixing functions where possible and to enumerate their colors concretely.
10+ - The point here is that color mixing is situation dependent. Why not just mix a color explicitly in a way where you know it looks good?
11+
12+3. With a totally opaque color type, we can calculate contrast for accessibility evaluation easily. An alpha channel means we'd have to emulate browser mixing to do that, which I'm not sure is consistent.
13+
14+Ultimately I want the position of `elm-ui` to be something like the following:
15+
16+1. `rgb`(both value based and hex) is more of an interop format than a color space you'd actually want to modify things in. Think, when have you ever thought "oh, just bump the red channel down a bit and you'll be good"?
17+
18+ - As an interop format, it means it's what you're going to be copying from tools or receiving from a designer.
19+ - These tools generally have the ability to mix things properly.
20+ - By mixing things concretely, designers have more control over their ultimate design.
21+
22+2. Transparency in most of these cases is still necessary or at the least convenient. It's not too difficult to treat that separately than the identity of a color value.
23+
24+3. Ultimately we want to encourage people to use a colorspace like [HSLuv](https://package.elm-lang.org/packages/kuon/elm-hsluv/latest/). Basically any of the colorspaces that are based in [CIEXYZ colorspace](https://en.wikipedia.org/wiki/CIE_1931_color_space). They're much easier to use. - May not really seem related to alpha channels, but the idea is that doing things like `lightening` a color is much easier in these other colorspaces.
25+
26+## Element.Font
27+
28+ Font.color : Color -> Attribute msg
29+
30+ -- Tempted to drop this one.
31+ -- People could still have transparent text using `Element.opacity`
32+ -- if they really want it
33+ Font.opacity : Float -> Attribute msg
34+
35+ Font.shadow :
36+ { offset : (Float, Float)
37+ , blur : Float
38+ , color : Color
39+ , opacity : Float
40+ } -> Attribute msg
41+
42+## Element.Background
43+
44+ Background.color : Color -> Attribute
45+
46+ -- background opacity is commonly used when we have some text
47+ -- over an image background.
48+ Background.opacity : Float -> Attribute
49+
50+For gradients, we can have opacity as a separate set of steps.
51+This actually makes it _easier_ to do something like a rainbow that linearly fades away. We don't have to figure out the exact opacity that the 5th color has.
52+
53+ Background.gradient :
54+ { angle : Float
55+ , colors : List Color
56+ , opacity : List Float
57+ }
58+
59+Do you ever have a gradient with more than two alpha steps?
60+Maybe you'd actually like to adjust the falloff curve instead of have it be linear?
61+Maybe there's an even better way to represent this?
62+
63+ Will eventually want to cover radial gradients, but a similar approach to the above is likely easy.
64+
65+Took a look at [background blend modes](https://developer.mozilla.org/en-US/docs/Web/CSS/background-blend-mode), which can be used to dynamically color images, like the [header image here](https://professional-rentals.com/).
66+
67+The blend modes are influenced by background or element opacity.
68+
69+In that way, we could have something like
70+
71+ Background.blend 0.5 color
72+
73+Which would set a color mode and set the background opacity. I'd need to play around with it, but seems kinda cool!
74+
75+## Element.Border
76+
77+Sometimes a transparent border is used as a placeholder, and then we change a color on `mouseOver`. This is to avoid layout movement in the resulting animation.
78+
79+ Border.color : Color -> Attribute
80+
81+ Border.opacity : Float -> Attribute
82+
83+ Border.shadow :
84+ { offset : (Float, Float)
85+ , size : Float
86+ , blur : Float
87+ , color : Color
88+ , opacity : Float
89+ }
90+
91+ Border.manyShadows :
92+ List
93+ { offset : (Float, Float)
94+ , blur : Float
95+ , color : Color
96+ , opacity : Float
97+ } -> Attribute msg
98+
99+ Border.innerShadow
100+ { offset : ( Float, Float )
101+ , size : Float
102+ , blur : Float
103+ , color : Color
104+ , opacity : Float
105+ }
106+
107+# Discussion and Usecases
108+
109+The main question about binding alpha with color is:
110+
111+ Is the identity of this color an identity about the color and it's ability to mix. Knowing that it's ability to mix in this case is not great.
112+
113+Or would you want to set these things explicitly and independently of each other?
114+
115+## Tinting and shading (i.e. lightening and darkening)
116+
117+_Note_ - not sure what package would house these functions.
118+
119+We usually get in the habit of using alpha for an ad-hoc lightener/darkener.
120+This can easily be replicated with functions that actually operate on colors in elm.
121+
122+ Color.lighten 0.3 color
123+
124+With the idea that this function should use the luminance from CIELUV instead of lightness from hsl.
125+
126+## Mixing two colors
127+
128+We also use alpha as an ad hoc way to mix two colors (i.e. [turn a font subtley green on a green background](https://medium.com/refactoring-ui/7-practical-tips-for-cheating-at-design-40c736799886#9cdf).
129+
130+ el
131+ [ Background.color bg
132+ -- explicitly mix the two!
133+ , Font.color (Color.mix 0.2 white bg)
134+ ]
135+ (text "Howdy")
notes/discussion/ColorWithoutAlpha.mdnew file+67
@@ -0,0 +1,67 @@
1+# A Design for Color without Alpha
2+
3+Color is hard!
4+
5+I've spent a decade and a half in web development and only now feel like I have some sort of handle on it.
6+
7+What I've learned basically comes down to the fact that the two color spaces we're used to dealing with (`rgb` and `hsl`), are not actually that great for thinking about color. Which is unfortunate.
8+
9+So, what can we use instead?
10+
11+Research on color perception in the 1920's led to the development of the [CIEXYZ colorspace](https://en.wikipedia.org/wiki/CIE_1931_color_space), which describes color in a way that is much closer to how humans perceive color. Essentially they were able to model constants in their equations that literally represent some of the built-in values we have as humans to perceive color.
12+
13+How crazy is that?
14+
15+(Skipping over the fact that there is variation in how people perceive color.)
16+
17+There are a number of color spaces that are based on this initial research, including CIELAB(generally recommended for print), and CIELUV(generally recommended for light). They are essentailly different ways to talk about the same color space. Sorta like polar coordinates(r,θ) and cartesian coordiantes (x,y) can both represent a point.
18+
19+The `L` in the above acronyms represents `Luminance`.
20+
21+This is one of the really cool parts. Two colors that have the same `Luminance`, will be equally "bright" to the human eye.
22+
23+You might be thinking "well, what about the `lightness` channel in `HSL`, huh?". Turns out it's not really based on anything!
24+
25+Here are two hsl colors with the same lightness:
26+![](http://lea.verou.me/wp-content/uploads/2020/04/image-4.png)
27+
28+Do they look like the same "brightness"?
29+
30+Unfortunately the L in HSL stands for Lies and we've been misled all along.
31+
32+[This whole article on color spaces is worth a read if you got the time](http://lea.verou.me/2020/04/lch-colors-in-css-what-why-and-how/)
33+
34+
35+Ok, so, luminance is really cool.
36+
37+With a real `Luminance` factor you can
38+ 1. Generate palettes of colors that work together in a consistent manner.
39+ 2. Automatically know the difference in luminance between two colors. Guess what that is. It's a contrast calculation! Like for accessibility! How nice would that be if it was X - Y as opposed to some crazy math?
40+ 3. Get an intuition for how colors will interact with each other.
41+ 4. Generate gradients that aren't unexpectedly broken. (I have some examples of this, I'll dig them up later)
42+ 5. Animate colors consistently. Animating in rgb or hsl will cause color animation to look awful in different ways.
43+
44+It really is a better world.
45+
46+Ok, so how do we get there and what does this mean for elm-ui?
47+
48+
49+# Elm UI stuff
50+
51+The above knowledge is hard won. Many people are not aware that there are better colorspaces than rgb and hsl.
52+
53+Color is hard. In many cases, it will remain hard until people get a nice introduction to something based on CIEXYZ.
54+
55+In elm, [we have a package for HSLuv](
56+https://package.elm-lang.org/packages/kuon/elm-hsluv/latest/), which you can use right now. It's great.
57+
58+However! We have one confouding factor, which is that we're used to bundling an `alpha` value with our colors.
59+
60+The same math that makes rgb and hsl less than great is used to blend colors via alpha.
61+
62+By bundling alpha with color, we're encouranging people to blend colors in an ad-hoc way when we could be providing them avenues to easily do it the right way. We have a whole programming language, we're not confined to only CSS anymore.
63+
64+However! Frontend development also involves talking to designers. Designers usually provide rgb, hsl, or a hex version of rgb.
65+
66+I now sorta view these as *interop formats*.
67+
package-lock.jsonnew file+1235
@@ -0,0 +1,1235 @@
1+{
2+ "name": "elm-ui-testing",
3+ "version": "1.0.0",
4+ "lockfileVersion": 1,
5+ "requires": true,
6+ "dependencies": {
7+ "ajv": {
8+ "version": "6.12.0",
9+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
10+ "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
11+ "requires": {
12+ "fast-deep-equal": "^3.1.1",
13+ "fast-json-stable-stringify": "^2.0.0",
14+ "json-schema-traverse": "^0.4.1",
15+ "uri-js": "^4.2.2"
16+ }
17+ },
18+ "ansi-styles": {
19+ "version": "3.2.1",
20+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
21+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
22+ "requires": {
23+ "color-convert": "^1.9.0"
24+ }
25+ },
26+ "anymatch": {
27+ "version": "3.1.1",
28+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
29+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
30+ "requires": {
31+ "normalize-path": "^3.0.0",
32+ "picomatch": "^2.0.4"
33+ }
34+ },
35+ "asn1": {
36+ "version": "0.2.4",
37+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
38+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
39+ "requires": {
40+ "safer-buffer": "~2.1.0"
41+ }
42+ },
43+ "assert-plus": {
44+ "version": "1.0.0",
45+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
46+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
47+ },
48+ "asynckit": {
49+ "version": "0.4.0",
50+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
51+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
52+ },
53+ "aws-sign2": {
54+ "version": "0.7.0",
55+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
56+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
57+ },
58+ "aws4": {
59+ "version": "1.9.1",
60+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
61+ "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
62+ },
63+ "balanced-match": {
64+ "version": "1.0.0",
65+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
66+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
67+ },
68+ "bcrypt-pbkdf": {
69+ "version": "1.0.2",
70+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
71+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
72+ "requires": {
73+ "tweetnacl": "^0.14.3"
74+ }
75+ },
76+ "binary": {
77+ "version": "0.3.0",
78+ "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
79+ "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
80+ "requires": {
81+ "buffers": "~0.1.1",
82+ "chainsaw": "~0.1.0"
83+ }
84+ },
85+ "binary-extensions": {
86+ "version": "2.0.0",
87+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
88+ "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow=="
89+ },
90+ "binwrap": {
91+ "version": "0.2.2",
92+ "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.2.2.tgz",
93+ "integrity": "sha512-Y+Wvypk3JhH5GPZAvlwJAWOVH/OsOhQMSj37vySuWHwQivoALplPxfBA8b973rFJI7OS+O+1YmmYXIiEXVMAcw==",
94+ "requires": {
95+ "mustache": "^3.0.1",
96+ "request": "^2.88.0",
97+ "request-promise": "^4.2.4",
98+ "tar": "^4.4.10",
99+ "unzip-stream": "^0.3.0"
100+ }
101+ },
102+ "bluebird": {
103+ "version": "3.7.2",
104+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
105+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
106+ },
107+ "brace-expansion": {
108+ "version": "1.1.11",
109+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
110+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
111+ "requires": {
112+ "balanced-match": "^1.0.0",
113+ "concat-map": "0.0.1"
114+ }
115+ },
116+ "braces": {
117+ "version": "3.0.2",
118+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
119+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
120+ "requires": {
121+ "fill-range": "^7.0.1"
122+ }
123+ },
124+ "buffers": {
125+ "version": "0.1.1",
126+ "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
127+ "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
128+ },
129+ "caseless": {
130+ "version": "0.12.0",
131+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
132+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
133+ },
134+ "chainsaw": {
135+ "version": "0.1.0",
136+ "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
137+ "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
138+ "requires": {
139+ "traverse": ">=0.3.0 <0.4"
140+ }
141+ },
142+ "chalk": {
143+ "version": "2.4.2",
144+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
145+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
146+ "requires": {
147+ "ansi-styles": "^3.2.1",
148+ "escape-string-regexp": "^1.0.5",
149+ "supports-color": "^5.3.0"
150+ },
151+ "dependencies": {
152+ "has-flag": {
153+ "version": "3.0.0",
154+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
155+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
156+ },
157+ "supports-color": {
158+ "version": "5.5.0",
159+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
160+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
161+ "requires": {
162+ "has-flag": "^3.0.0"
163+ }
164+ }
165+ }
166+ },
167+ "chokidar": {
168+ "version": "3.3.0",
169+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
170+ "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
171+ "requires": {
172+ "anymatch": "~3.1.1",
173+ "braces": "~3.0.2",
174+ "fsevents": "~2.1.1",
175+ "glob-parent": "~5.1.0",
176+ "is-binary-path": "~2.1.0",
177+ "is-glob": "~4.0.1",
178+ "normalize-path": "~3.0.0",
179+ "readdirp": "~3.2.0"
180+ }
181+ },
182+ "chownr": {
183+ "version": "1.1.4",
184+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
185+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
186+ },
187+ "color-convert": {
188+ "version": "1.9.3",
189+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
190+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
191+ "requires": {
192+ "color-name": "1.1.3"
193+ }
194+ },
195+ "color-name": {
196+ "version": "1.1.3",
197+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
198+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
199+ },
200+ "combined-stream": {
201+ "version": "1.0.8",
202+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
203+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
204+ "requires": {
205+ "delayed-stream": "~1.0.0"
206+ }
207+ },
208+ "commander": {
209+ "version": "2.20.0",
210+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
211+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
212+ },
213+ "concat-map": {
214+ "version": "0.0.1",
215+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
216+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
217+ },
218+ "core-util-is": {
219+ "version": "1.0.2",
220+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
221+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
222+ },
223+ "cross-spawn": {
224+ "version": "6.0.5",
225+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
226+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
227+ "requires": {
228+ "nice-try": "^1.0.4",
229+ "path-key": "^2.0.1",
230+ "semver": "^5.5.0",
231+ "shebang-command": "^1.2.0",
232+ "which": "^1.2.9"
233+ }
234+ },
235+ "dashdash": {
236+ "version": "1.14.1",
237+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
238+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
239+ "requires": {
240+ "assert-plus": "^1.0.0"
241+ }
242+ },
243+ "delayed-stream": {
244+ "version": "1.0.0",
245+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
246+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
247+ },
248+ "ecc-jsbn": {
249+ "version": "0.1.2",
250+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
251+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
252+ "requires": {
253+ "jsbn": "~0.1.0",
254+ "safer-buffer": "^2.1.0"
255+ }
256+ },
257+ "elm": {
258+ "version": "0.19.1-3",
259+ "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-3.tgz",
260+ "integrity": "sha512-6y36ewCcVmTOx8lj7cKJs3bhI5qMfoVEigePZ9PhEUNKpwjjML/pU2u2YSpHVAznuCcojoF6KIsrS1Ci7GtVaQ==",
261+ "requires": {
262+ "request": "^2.88.0"
263+ }
264+ },
265+ "elm-test": {
266+ "version": "0.19.1-revision2",
267+ "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision2.tgz",
268+ "integrity": "sha512-zVs2mVeyIE+K9y7/8b333h5xRMDWAoqbBDm7ThLDhyTi7ICxeL3t5uOS4KZCrRk9+4sP6+voSbcBlgr46Q+GiQ==",
269+ "requires": {
270+ "chalk": "3.0.0",
271+ "chokidar": "3.3.0",
272+ "cross-spawn": "7.0.1",
273+ "elmi-to-json": "1.3.0",
274+ "find-parent-dir": "^0.3.0",
275+ "firstline": "2.0.2",
276+ "fs-extra": "8.1.0",
277+ "glob": "7.1.6",
278+ "lodash": "4.17.15",
279+ "minimist": "^1.2.0",
280+ "murmur-hash-js": "1.0.0",
281+ "node-elm-compiler": "5.0.4",
282+ "split": "1.0.1",
283+ "supports-color": "7.1.0",
284+ "temp": "0.9.1",
285+ "which": "2.0.1",
286+ "xmlbuilder": "^13.0.2"
287+ },
288+ "dependencies": {
289+ "ansi-styles": {
290+ "version": "4.2.1",
291+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
292+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
293+ "requires": {
294+ "@types/color-name": "^1.1.1",
295+ "color-convert": "^2.0.1"
296+ },
297+ "dependencies": {
298+ "@types/color-name": {
299+ "version": "1.1.1",
300+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
301+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
302+ }
303+ }
304+ },
305+ "chalk": {
306+ "version": "3.0.0",
307+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
308+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
309+ "requires": {
310+ "ansi-styles": "^4.1.0",
311+ "supports-color": "^7.1.0"
312+ }
313+ },
314+ "color-convert": {
315+ "version": "2.0.1",
316+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
317+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
318+ "requires": {
319+ "color-name": "~1.1.4"
320+ }
321+ },
322+ "color-name": {
323+ "version": "1.1.4",
324+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
325+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
326+ },
327+ "cross-spawn": {
328+ "version": "7.0.1",
329+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
330+ "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
331+ "requires": {
332+ "path-key": "^3.1.0",
333+ "shebang-command": "^2.0.0",
334+ "which": "^2.0.1"
335+ },
336+ "dependencies": {
337+ "path-key": {
338+ "version": "3.1.1",
339+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
340+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
341+ },
342+ "shebang-command": {
343+ "version": "2.0.0",
344+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
345+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
346+ "requires": {
347+ "shebang-regex": "^3.0.0"
348+ }
349+ }
350+ }
351+ },
352+ "find-elm-dependencies": {
353+ "version": "2.0.2",
354+ "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.2.tgz",
355+ "integrity": "sha512-nM5UCbccD1G8CGK2GsM7ykG3ksOAl9E+34jiDfl07CAl2OPnLpBVWY2hlxEmIkSBfdJjSopEowWHrO0cI8RhxQ==",
356+ "requires": {
357+ "firstline": "1.2.0",
358+ "lodash": "4.17.15"
359+ },
360+ "dependencies": {
361+ "firstline": {
362+ "version": "1.2.0",
363+ "resolved": "https://registry.npmjs.org/firstline/-/firstline-1.2.0.tgz",
364+ "integrity": "sha1-yfSIbn9/vwr8EtcZQdzgaxkq6gU="
365+ }
366+ }
367+ },
368+ "firstline": {
369+ "version": "2.0.2",
370+ "resolved": "https://registry.npmjs.org/firstline/-/firstline-2.0.2.tgz",
371+ "integrity": "sha512-8KcmfI0jgSECnzdhucm0i7vrwef3BWwgjimW2YkRC5eSFwjb5DibVoA0YvgkYwwxuJi9c+7M7X3b3lX8o9B6wg=="
372+ },
373+ "lodash": {
374+ "version": "4.17.15",
375+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
376+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
377+ },
378+ "node-elm-compiler": {
379+ "version": "5.0.4",
380+ "resolved": "https://registry.npmjs.org/node-elm-compiler/-/node-elm-compiler-5.0.4.tgz",
381+ "integrity": "sha512-VQsT8QSierYGkHzRed+b4MnccQVF1+qPHunE8jBoU7jD6YpuRqCDPzEoC2zfyEJS80qVnlMZrqobLnyjzX9lJg==",
382+ "requires": {
383+ "cross-spawn": "6.0.5",
384+ "find-elm-dependencies": "2.0.2",
385+ "lodash": "4.17.15",
386+ "temp": "^0.9.0"
387+ },
388+ "dependencies": {
389+ "cross-spawn": {
390+ "version": "6.0.5",
391+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
392+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
393+ "requires": {
394+ "nice-try": "^1.0.4",
395+ "path-key": "^2.0.1",
396+ "semver": "^5.5.0",
397+ "shebang-command": "^1.2.0",
398+ "which": "^1.2.9"
399+ }
400+ },
401+ "nice-try": {
402+ "version": "1.0.5",
403+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
404+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
405+ },
406+ "path-key": {
407+ "version": "2.0.1",
408+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
409+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
410+ },
411+ "semver": {
412+ "version": "5.7.1",
413+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
414+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
415+ },
416+ "shebang-command": {
417+ "version": "1.2.0",
418+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
419+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
420+ "requires": {
421+ "shebang-regex": "^1.0.0"
422+ }
423+ },
424+ "shebang-regex": {
425+ "version": "1.0.0",
426+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
427+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
428+ },
429+ "which": {
430+ "version": "1.3.1",
431+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
432+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
433+ "requires": {
434+ "isexe": "^2.0.0"
435+ }
436+ }
437+ }
438+ },
439+ "supports-color": {
440+ "version": "7.1.0",
441+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
442+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
443+ "requires": {
444+ "has-flag": "^4.0.0"
445+ }
446+ },
447+ "temp": {
448+ "version": "0.9.1",
449+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz",
450+ "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==",
451+ "requires": {
452+ "rimraf": "~2.6.2"
453+ }
454+ },
455+ "which": {
456+ "version": "2.0.1",
457+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz",
458+ "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==",
459+ "requires": {
460+ "isexe": "^2.0.0"
461+ }
462+ }
463+ }
464+ },
465+ "elmi-to-json": {
466+ "version": "1.3.0",
467+ "resolved": "https://registry.npmjs.org/elmi-to-json/-/elmi-to-json-1.3.0.tgz",
468+ "integrity": "sha512-6m1D5/Pb5pUrSOOBgRG3fE2mK19nhmLgZ16jj2KWTVIhT+0GIBuDI1iV0Fee27CZH790J7uMcdGWJ7fnVvpsKg==",
469+ "requires": {
470+ "binwrap": "0.2.2"
471+ }
472+ },
473+ "escape-string-regexp": {
474+ "version": "1.0.5",
475+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
476+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
477+ },
478+ "extend": {
479+ "version": "3.0.2",
480+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
481+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
482+ },
483+ "extsprintf": {
484+ "version": "1.3.0",
485+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
486+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
487+ },
488+ "fast-deep-equal": {
489+ "version": "3.1.1",
490+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
491+ "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
492+ },
493+ "fast-json-stable-stringify": {
494+ "version": "2.1.0",
495+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
496+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
497+ },
498+ "fill-range": {
499+ "version": "7.0.1",
500+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
501+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
502+ "requires": {
503+ "to-regex-range": "^5.0.1"
504+ }
505+ },
506+ "find-elm-dependencies": {
507+ "version": "2.0.2",
508+ "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.2.tgz",
509+ "integrity": "sha512-nM5UCbccD1G8CGK2GsM7ykG3ksOAl9E+34jiDfl07CAl2OPnLpBVWY2hlxEmIkSBfdJjSopEowWHrO0cI8RhxQ==",
510+ "requires": {
511+ "firstline": "1.2.0",
512+ "lodash": "4.17.15"
513+ }
514+ },
515+ "find-parent-dir": {
516+ "version": "0.3.0",
517+ "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
518+ "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ="
519+ },
520+ "firstline": {
521+ "version": "1.2.0",
522+ "resolved": "https://registry.npmjs.org/firstline/-/firstline-1.2.0.tgz",
523+ "integrity": "sha1-yfSIbn9/vwr8EtcZQdzgaxkq6gU="
524+ },
525+ "forever-agent": {
526+ "version": "0.6.1",
527+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
528+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
529+ },
530+ "form-data": {
531+ "version": "2.3.3",
532+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
533+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
534+ "requires": {
535+ "asynckit": "^0.4.0",
536+ "combined-stream": "^1.0.6",
537+ "mime-types": "^2.1.12"
538+ }
539+ },
540+ "fs-extra": {
541+ "version": "8.1.0",
542+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
543+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
544+ "requires": {
545+ "graceful-fs": "^4.2.0",
546+ "jsonfile": "^4.0.0",
547+ "universalify": "^0.1.0"
548+ }
549+ },
550+ "fs-minipass": {
551+ "version": "1.2.7",
552+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
553+ "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
554+ "requires": {
555+ "minipass": "^2.6.0"
556+ }
557+ },
558+ "fs.realpath": {
559+ "version": "1.0.0",
560+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
561+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
562+ },
563+ "fsevents": {
564+ "version": "2.1.2",
565+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
566+ "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
567+ "optional": true
568+ },
569+ "getpass": {
570+ "version": "0.1.7",
571+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
572+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
573+ "requires": {
574+ "assert-plus": "^1.0.0"
575+ }
576+ },
577+ "glob": {
578+ "version": "7.1.6",
579+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
580+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
581+ "requires": {
582+ "fs.realpath": "^1.0.0",
583+ "inflight": "^1.0.4",
584+ "inherits": "2",
585+ "minimatch": "^3.0.4",
586+ "once": "^1.3.0",
587+ "path-is-absolute": "^1.0.0"
588+ }
589+ },
590+ "glob-parent": {
591+ "version": "5.1.0",
592+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
593+ "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
594+ "requires": {
595+ "is-glob": "^4.0.1"
596+ }
597+ },
598+ "graceful-fs": {
599+ "version": "4.2.3",
600+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
601+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
602+ },
603+ "har-schema": {
604+ "version": "2.0.0",
605+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
606+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
607+ },
608+ "har-validator": {
609+ "version": "5.1.3",
610+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
611+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
612+ "requires": {
613+ "ajv": "^6.5.5",
614+ "har-schema": "^2.0.0"
615+ }
616+ },
617+ "has-flag": {
618+ "version": "4.0.0",
619+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
620+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
621+ },
622+ "http-signature": {
623+ "version": "1.2.0",
624+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
625+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
626+ "requires": {
627+ "assert-plus": "^1.0.0",
628+ "jsprim": "^1.2.2",
629+ "sshpk": "^1.7.0"
630+ }
631+ },
632+ "immediate": {
633+ "version": "3.0.6",
634+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
635+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
636+ },
637+ "inflight": {
638+ "version": "1.0.6",
639+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
640+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
641+ "requires": {
642+ "once": "^1.3.0",
643+ "wrappy": "1"
644+ }
645+ },
646+ "inherits": {
647+ "version": "2.0.4",
648+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
649+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
650+ },
651+ "is-binary-path": {
652+ "version": "2.1.0",
653+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
654+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
655+ "requires": {
656+ "binary-extensions": "^2.0.0"
657+ }
658+ },
659+ "is-extglob": {
660+ "version": "2.1.1",
661+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
662+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
663+ },
664+ "is-glob": {
665+ "version": "4.0.1",
666+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
667+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
668+ "requires": {
669+ "is-extglob": "^2.1.1"
670+ }
671+ },
672+ "is-number": {
673+ "version": "7.0.0",
674+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
675+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
676+ },
677+ "is-typedarray": {
678+ "version": "1.0.0",
679+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
680+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
681+ },
682+ "isarray": {
683+ "version": "1.0.0",
684+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
685+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
686+ },
687+ "isexe": {
688+ "version": "2.0.0",
689+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
690+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
691+ },
692+ "isstream": {
693+ "version": "0.1.2",
694+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
695+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
696+ },
697+ "jsbn": {
698+ "version": "0.1.1",
699+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
700+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
701+ },
702+ "json-schema": {
703+ "version": "0.2.3",
704+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
705+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
706+ },
707+ "json-schema-traverse": {
708+ "version": "0.4.1",
709+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
710+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
711+ },
712+ "json-stringify-safe": {
713+ "version": "5.0.1",
714+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
715+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
716+ },
717+ "jsonfile": {
718+ "version": "4.0.0",
719+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
720+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
721+ "requires": {
722+ "graceful-fs": "^4.1.6"
723+ }
724+ },
725+ "jsprim": {
726+ "version": "1.4.1",
727+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
728+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
729+ "requires": {
730+ "assert-plus": "1.0.0",
731+ "extsprintf": "1.3.0",
732+ "json-schema": "0.2.3",
733+ "verror": "1.10.0"
734+ }
735+ },
736+ "jszip": {
737+ "version": "3.4.0",
738+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.4.0.tgz",
739+ "integrity": "sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==",
740+ "requires": {
741+ "lie": "~3.3.0",
742+ "pako": "~1.0.2",
743+ "readable-stream": "~2.3.6",
744+ "set-immediate-shim": "~1.0.1"
745+ }
746+ },
747+ "lie": {
748+ "version": "3.3.0",
749+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
750+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
751+ "requires": {
752+ "immediate": "~3.0.5"
753+ }
754+ },
755+ "lodash": {
756+ "version": "4.17.15",
757+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
758+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
759+ },
760+ "mime-db": {
761+ "version": "1.43.0",
762+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
763+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
764+ },
765+ "mime-types": {
766+ "version": "2.1.26",
767+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
768+ "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
769+ "requires": {
770+ "mime-db": "1.43.0"
771+ }
772+ },
773+ "minimatch": {
774+ "version": "3.0.4",
775+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
776+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
777+ "requires": {
778+ "brace-expansion": "^1.1.7"
779+ }
780+ },
781+ "minimist": {
782+ "version": "1.2.4",
783+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.4.tgz",
784+ "integrity": "sha512-wTiNDqe4D2rbTJGZk1qcdZgFtY0/r+iuE6GDT7V0/+Gu5MLpIDm4+CssDECR79OJs/OxLPXMzdxy153b5Qy3hg=="
785+ },
786+ "minipass": {
787+ "version": "2.9.0",
788+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
789+ "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
790+ "requires": {
791+ "safe-buffer": "^5.1.2",
792+ "yallist": "^3.0.0"
793+ }
794+ },
795+ "minizlib": {
796+ "version": "1.3.3",
797+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
798+ "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
799+ "requires": {
800+ "minipass": "^2.9.0"
801+ }
802+ },
803+ "mkdirp": {
804+ "version": "0.5.5",
805+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
806+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
807+ "requires": {
808+ "minimist": "^1.2.5"
809+ },
810+ "dependencies": {
811+ "minimist": {
812+ "version": "1.2.5",
813+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
814+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
815+ }
816+ }
817+ },
818+ "murmur-hash-js": {
819+ "version": "1.0.0",
820+ "resolved": "https://registry.npmjs.org/murmur-hash-js/-/murmur-hash-js-1.0.0.tgz",
821+ "integrity": "sha1-UEEEkmnJZjPIZjhpYLL0KJ515bA="
822+ },
823+ "mustache": {
824+ "version": "3.2.1",
825+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz",
826+ "integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA=="
827+ },
828+ "nice-try": {
829+ "version": "1.0.5",
830+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
831+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
832+ },
833+ "node-elm-compiler": {
834+ "version": "5.0.4",
835+ "resolved": "https://registry.npmjs.org/node-elm-compiler/-/node-elm-compiler-5.0.4.tgz",
836+ "integrity": "sha512-VQsT8QSierYGkHzRed+b4MnccQVF1+qPHunE8jBoU7jD6YpuRqCDPzEoC2zfyEJS80qVnlMZrqobLnyjzX9lJg==",
837+ "requires": {
838+ "cross-spawn": "6.0.5",
839+ "find-elm-dependencies": "2.0.2",
840+ "lodash": "4.17.15",
841+ "temp": "^0.9.0"
842+ }
843+ },
844+ "normalize-path": {
845+ "version": "3.0.0",
846+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
847+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
848+ },
849+ "oauth-sign": {
850+ "version": "0.9.0",
851+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
852+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
853+ },
854+ "once": {
855+ "version": "1.4.0",
856+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
857+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
858+ "requires": {
859+ "wrappy": "1"
860+ }
861+ },
862+ "os-tmpdir": {
863+ "version": "1.0.2",
864+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
865+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
866+ },
867+ "pako": {
868+ "version": "1.0.11",
869+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
870+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
871+ },
872+ "path-is-absolute": {
873+ "version": "1.0.1",
874+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
875+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
876+ },
877+ "path-key": {
878+ "version": "2.0.1",
879+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
880+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
881+ },
882+ "performance-now": {
883+ "version": "2.1.0",
884+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
885+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
886+ },
887+ "picomatch": {
888+ "version": "2.2.1",
889+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
890+ "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA=="
891+ },
892+ "process-nextick-args": {
893+ "version": "2.0.1",
894+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
895+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
896+ },
897+ "psl": {
898+ "version": "1.7.0",
899+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
900+ "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
901+ },
902+ "punycode": {
903+ "version": "2.1.1",
904+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
905+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
906+ },
907+ "qs": {
908+ "version": "6.5.2",
909+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
910+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
911+ },
912+ "readable-stream": {
913+ "version": "2.3.7",
914+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
915+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
916+ "requires": {
917+ "core-util-is": "~1.0.0",
918+ "inherits": "~2.0.3",
919+ "isarray": "~1.0.0",
920+ "process-nextick-args": "~2.0.0",
921+ "safe-buffer": "~5.1.1",
922+ "string_decoder": "~1.1.1",
923+ "util-deprecate": "~1.0.1"
924+ },
925+ "dependencies": {
926+ "safe-buffer": {
927+ "version": "5.1.2",
928+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
929+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
930+ }
931+ }
932+ },
933+ "readdirp": {
934+ "version": "3.2.0",
935+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
936+ "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
937+ "requires": {
938+ "picomatch": "^2.0.4"
939+ }
940+ },
941+ "request": {
942+ "version": "2.88.2",
943+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
944+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
945+ "requires": {
946+ "aws-sign2": "~0.7.0",
947+ "aws4": "^1.8.0",
948+ "caseless": "~0.12.0",
949+ "combined-stream": "~1.0.6",
950+ "extend": "~3.0.2",
951+ "forever-agent": "~0.6.1",
952+ "form-data": "~2.3.2",
953+ "har-validator": "~5.1.3",
954+ "http-signature": "~1.2.0",
955+ "is-typedarray": "~1.0.0",
956+ "isstream": "~0.1.2",
957+ "json-stringify-safe": "~5.0.1",
958+ "mime-types": "~2.1.19",
959+ "oauth-sign": "~0.9.0",
960+ "performance-now": "^2.1.0",
961+ "qs": "~6.5.2",
962+ "safe-buffer": "^5.1.2",
963+ "tough-cookie": "~2.5.0",
964+ "tunnel-agent": "^0.6.0",
965+ "uuid": "^3.3.2"
966+ }
967+ },
968+ "request-promise": {
969+ "version": "4.2.5",
970+ "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz",
971+ "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==",
972+ "requires": {
973+ "bluebird": "^3.5.0",
974+ "request-promise-core": "1.1.3",
975+ "stealthy-require": "^1.1.1",
976+ "tough-cookie": "^2.3.3"
977+ }
978+ },
979+ "request-promise-core": {
980+ "version": "1.1.3",
981+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
982+ "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
983+ "requires": {
984+ "lodash": "^4.17.15"
985+ }
986+ },
987+ "rimraf": {
988+ "version": "2.6.3",
989+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
990+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
991+ "requires": {
992+ "glob": "^7.1.3"
993+ }
994+ },
995+ "safe-buffer": {
996+ "version": "5.2.0",
997+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
998+ "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
999+ },
1000+ "safer-buffer": {
1001+ "version": "2.1.2",
1002+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1003+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1004+ },
1005+ "selenium-webdriver": {
1006+ "version": "4.0.0-alpha.7",
1007+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.7.tgz",
1008+ "integrity": "sha512-D4qnTsyTr91jT8f7MfN+OwY0IlU5+5FmlO5xlgRUV6hDEV8JyYx2NerdTEqDDkNq7RZDYc4VoPALk8l578RBHw==",
1009+ "requires": {
1010+ "jszip": "^3.2.2",
1011+ "rimraf": "^2.7.1",
1012+ "tmp": "0.0.30"
1013+ },
1014+ "dependencies": {
1015+ "rimraf": {
1016+ "version": "2.7.1",
1017+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
1018+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
1019+ "requires": {
1020+ "glob": "^7.1.3"
1021+ }
1022+ }
1023+ }
1024+ },
1025+ "semver": {
1026+ "version": "5.7.1",
1027+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1028+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
1029+ },
1030+ "set-immediate-shim": {
1031+ "version": "1.0.1",
1032+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
1033+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
1034+ },
1035+ "shebang-command": {
1036+ "version": "1.2.0",
1037+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
1038+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
1039+ "requires": {
1040+ "shebang-regex": "^1.0.0"
1041+ },
1042+ "dependencies": {
1043+ "shebang-regex": {
1044+ "version": "1.0.0",
1045+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
1046+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
1047+ }
1048+ }
1049+ },
1050+ "shebang-regex": {
1051+ "version": "3.0.0",
1052+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1053+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
1054+ },
1055+ "split": {
1056+ "version": "1.0.1",
1057+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
1058+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
1059+ "requires": {
1060+ "through": "2"
1061+ }
1062+ },
1063+ "sshpk": {
1064+ "version": "1.16.1",
1065+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
1066+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
1067+ "requires": {
1068+ "asn1": "~0.2.3",
1069+ "assert-plus": "^1.0.0",
1070+ "bcrypt-pbkdf": "^1.0.0",
1071+ "dashdash": "^1.12.0",
1072+ "ecc-jsbn": "~0.1.1",
1073+ "getpass": "^0.1.1",
1074+ "jsbn": "~0.1.0",
1075+ "safer-buffer": "^2.0.2",
1076+ "tweetnacl": "~0.14.0"
1077+ }
1078+ },
1079+ "stealthy-require": {
1080+ "version": "1.1.1",
1081+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
1082+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
1083+ },
1084+ "string_decoder": {
1085+ "version": "1.1.1",
1086+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
1087+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
1088+ "requires": {
1089+ "safe-buffer": "~5.1.0"
1090+ },
1091+ "dependencies": {
1092+ "safe-buffer": {
1093+ "version": "5.1.2",
1094+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
1095+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
1096+ }
1097+ }
1098+ },
1099+ "tar": {
1100+ "version": "4.4.13",
1101+ "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
1102+ "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
1103+ "requires": {
1104+ "chownr": "^1.1.1",
1105+ "fs-minipass": "^1.2.5",
1106+ "minipass": "^2.8.6",
1107+ "minizlib": "^1.2.1",
1108+ "mkdirp": "^0.5.0",
1109+ "safe-buffer": "^5.1.2",
1110+ "yallist": "^3.0.3"
1111+ }
1112+ },
1113+ "temp": {
1114+ "version": "0.9.1",
1115+ "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.1.tgz",
1116+ "integrity": "sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==",
1117+ "requires": {
1118+ "rimraf": "~2.6.2"
1119+ }
1120+ },
1121+ "through": {
1122+ "version": "2.3.8",
1123+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
1124+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
1125+ },
1126+ "tmp": {
1127+ "version": "0.0.30",
1128+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
1129+ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
1130+ "requires": {
1131+ "os-tmpdir": "~1.0.1"
1132+ }
1133+ },
1134+ "to-regex-range": {
1135+ "version": "5.0.1",
1136+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1137+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1138+ "requires": {
1139+ "is-number": "^7.0.0"
1140+ }
1141+ },
1142+ "tough-cookie": {
1143+ "version": "2.5.0",
1144+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
1145+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
1146+ "requires": {
1147+ "psl": "^1.1.28",
1148+ "punycode": "^2.1.1"
1149+ }
1150+ },
1151+ "traverse": {
1152+ "version": "0.3.9",
1153+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
1154+ "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
1155+ },
1156+ "tunnel-agent": {
1157+ "version": "0.6.0",
1158+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
1159+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
1160+ "requires": {
1161+ "safe-buffer": "^5.0.1"
1162+ }
1163+ },
1164+ "tweetnacl": {
1165+ "version": "0.14.5",
1166+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
1167+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
1168+ },
1169+ "universalify": {
1170+ "version": "0.1.2",
1171+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
1172+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
1173+ },
1174+ "unzip-stream": {
1175+ "version": "0.3.0",
1176+ "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.0.tgz",
1177+ "integrity": "sha512-NG1h/MdGIX3HzyqMjyj1laBCmlPYhcO4xEy7gEqqzGiSLw7XqDQCnY4nYSn5XSaH8mQ6TFkaujrO8d/PIZN85A==",
1178+ "requires": {
1179+ "binary": "^0.3.0",
1180+ "mkdirp": "^0.5.1"
1181+ }
1182+ },
1183+ "uri-js": {
1184+ "version": "4.2.2",
1185+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
1186+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
1187+ "requires": {
1188+ "punycode": "^2.1.0"
1189+ }
1190+ },
1191+ "util-deprecate": {
1192+ "version": "1.0.2",
1193+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1194+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
1195+ },
1196+ "uuid": {
1197+ "version": "3.4.0",
1198+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
1199+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
1200+ },
1201+ "verror": {
1202+ "version": "1.10.0",
1203+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
1204+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
1205+ "requires": {
1206+ "assert-plus": "^1.0.0",
1207+ "core-util-is": "1.0.2",
1208+ "extsprintf": "^1.2.0"
1209+ }
1210+ },
1211+ "which": {
1212+ "version": "1.3.1",
1213+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
1214+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
1215+ "requires": {
1216+ "isexe": "^2.0.0"
1217+ }
1218+ },
1219+ "wrappy": {
1220+ "version": "1.0.2",
1221+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1222+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
1223+ },
1224+ "xmlbuilder": {
1225+ "version": "13.0.2",
1226+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz",
1227+ "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ=="
1228+ },
1229+ "yallist": {
1230+ "version": "3.1.1",
1231+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1232+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
1233+ }
1234+ }
1235+}
package.json+33−29
@@ -1,30 +1,34 @@
1{
1{
2− "name": "elm-ui-testing",
2+ "name": "elm-ui-testing",
3− "version": "1.0.0",
3+ "version": "1.0.0",
4− "description": "Run the Elm UI benchmarks and render tests",
4+ "description": "Run the Elm UI benchmarks and render tests",
5− "main": "index.js",
5+ "main": "index.js",
6− "scripts": {
6+ "scripts": {
7− "test": "elm-test",
7+ "test": "elm-test",
8+ "view-test": "node tests-rendering/automation/view.js",
8− "test-render": "node tests-rendering/automation/run.js --chrome",
9+ "test-render": "node tests-rendering/automation/run.js --chrome",
9− "test-render-sauce": "source sauce.env; node tests-rendering/automation/run.js --sauce",
10+ "test-render-sauce": "source sauce.env; node tests-rendering/automation/run.js --sauce --headless",
11+ "test-render-sauce-automated": "node tests-rendering/automation/run.js --sauce --headless",
12+ "test-doc-preview": "bash tests-rendering/automation/doc-preview.sh",
10− "bench": "node benchmarks/runtime/index.js"
13+ "bench": "node benchmarks/runtime/index.js",
14+ "list-issues": "hub issue -f \"%i,%U,%t,'%L'%n\" > issues.csv"
11− },
15+ },
12− "repository": {
16+ "repository": {
13− "type": "git",
17+ "type": "git",
14− "url": "git+https://github.com/mdgriffith/elm-ui.git"
18+ "url": "git+https://github.com/mdgriffith/elm-ui.git"
15− },
19+ },
16− "author": "Matthew Griffith <mdg.griffith@gmail.com> (http://mechanical-elephant.com/)",
20+ "author": "Matthew Griffith <mdg.griffith@gmail.com> (http://mechanical-elephant.com/)",
17− "license": "ISC",
21+ "license": "ISC",
18− "bugs": {
22+ "bugs": {
19− "url": "https://github.com/mdgriffith/elm-ui/issues"
23+ "url": "https://github.com/mdgriffith/elm-ui/issues"
20− },
24+ },
21− "homepage": "https://github.com/mdgriffith/elm-ui#readme",
25+ "homepage": "https://github.com/mdgriffith/elm-ui#readme",
22− "dependencies": {
26+ "dependencies": {
23− "chalk": "^2.4.2",
27+ "chalk": "^2.4.2",
24− "commander": "^2.20.0",
28+ "commander": "^2.20.0",
29+ "elm": "^0.19.1-3",
25− "elm-test": "^0.19.1-revision2",
30+ "elm-test": "^0.19.1-revision2",
26− "node-elm-compiler": "^5.0.3",
31+ "node-elm-compiler": "^5.0.4",
27− "puppeteer": "^1.18.0",
28− "selenium-webdriver": "^4.0.0-alpha.4"
32+ "selenium-webdriver": "^4.0.0-alpha.7"
29− }
33+ }
30−}
34+}
src/Element.elm+79−37
@@ -645,14 +645,34 @@ wrappedRow attrs children =
645 newPadding =
645 newPadding =
646 case padded of
646 case padded of
647 Just (Internal.Padding name t r b l) ->
647 Just (Internal.Padding name t r b l) ->
648− if r >= (x // 2) && b >= (y // 2) then
648+ if r >= (toFloat x / 2) && b >= (toFloat y / 2) then
649+ let
650+ newTop =
651+ t - (toFloat y / 2)
652+
653+ newRight =
654+ r - (toFloat x / 2)
655+
656+ newBottom =
657+ b - (toFloat y / 2)
658+
659+ newLeft =
660+ l - (toFloat x / 2)
661+ in
649 Just <|
662 Just <|
650− paddingEach
663+ Internal.StyleClass Flag.padding
651− { top = t - (y // 2)
664+ (Internal.PaddingStyle
652− , right = r - (x // 2)
665+ (Internal.paddingNameFloat
653− , bottom = b - (y // 2)
666+ newTop
654− , left = l - (x // 2)
667+ newRight
655− }
668+ newBottom
669+ newLeft
670+ )
671+ newTop
672+ newRight
673+ newBottom
674+ newLeft
675+ )
656
676
657 else
677 else
658 Nothing
678 Nothing
@@ -988,14 +1008,15 @@ tableHelper attrs config =
988
1008
989{-| A paragraph will layout all children as wrapped, inline elements.
1009{-| A paragraph will layout all children as wrapped, inline elements.
990
1010
991− import Element
1011+ import Element exposing (el, paragraph, text)
992 import Element.Font as Font
1012 import Element.Font as Font
993
1013
1014+ view =
994− Element.paragraph []
1015+ paragraph []
995− [ text "lots of text ...."
1016+ [ text "lots of text ...."
996− , el [ Font.bold ] (text "this is bold")
1017+ , el [ Font.bold ] (text "this is bold")
997− , text "lots of text ...."
1018+ , text "lots of text ...."
998− ]
1019+ ]
999
1020
1000This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
1021This is really useful when you want to markup text by having some parts be bold, or some be links, or whatever you so desire.
1001
1022
@@ -1003,17 +1024,18 @@ Also, if a child element has `alignLeft` or `alignRight`, then it will be moved
1003
1024
1004This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial).
1025This makes it particularly easy to do something like a [dropped capital](https://en.wikipedia.org/wiki/Initial).
1005
1026
1006− import Element
1027+ import Element exposing (alignLeft, el, padding, paragraph, text)
1007 import Element.Font as Font
1028 import Element.Font as Font
1008
1029
1009− Element.paragraph []
1030+ view =
1031+ paragraph []
1010− [ el
1032+ [ el
1011− [ alignLeft
1033+ [ alignLeft
1012− , padding 5
1034+ , padding 5
1035+ ]
1036+ (text "S")
1037+ , text "o much text ...."
1013 ]
1038 ]
1014− (text "S")
1015− , text "o much text ...."
1016− ]
1017
1039
1018Which will look something like
1040Which will look something like
1019
1041
@@ -1181,11 +1203,6 @@ newTabLink attrs { url, label } =
1181
1203
1182
1204
1183{-| A link to download a file.
1205{-| A link to download a file.
1184−
1185−**Note** If you're using `Browser.application`, then this won't be enough to actually trigger a file download due to how `Browser.Navigation` works.
1186−
1187−[Here's a description of what needs to happen](https://github.com/elm/html/issues/175).
1188−
1189-}
1206-}
1190download :
1207download :
1191 List (Attribute msg)
1208 List (Attribute msg)
@@ -1341,7 +1358,11 @@ moveLeft x =
1341{-| -}
1358{-| -}
1342padding : Int -> Attribute msg
1359padding : Int -> Attribute msg
1343padding x =
1360padding x =
1361+ let
1362+ f =
1363+ toFloat x
1364+ in
1344− Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1365+ Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) f f f f)
1345
1366
1346
1367
1347{-| Set horizontal and vertical padding.
1368{-| Set horizontal and vertical padding.
@@ -1349,16 +1370,27 @@ padding x =
1349paddingXY : Int -> Int -> Attribute msg
1370paddingXY : Int -> Int -> Attribute msg
1350paddingXY x y =
1371paddingXY x y =
1351 if x == y then
1372 if x == y then
1373+ let
1374+ f =
1375+ toFloat x
1376+ in
1352− Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) x x x x)
1377+ Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt x) f f f f)
1353
1378
1354 else
1379 else
1380+ let
1381+ xFloat =
1382+ toFloat x
1383+
1384+ yFloat =
1385+ toFloat y
1386+ in
1355 Internal.StyleClass Flag.padding
1387 Internal.StyleClass Flag.padding
1356 (Internal.PaddingStyle
1388 (Internal.PaddingStyle
1357 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1389 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1358− y
1390+ yFloat
1359− x
1391+ xFloat
1360− y
1392+ yFloat
1361− x
1393+ xFloat
1362 )
1394 )
1363
1395
1364
1396
@@ -1379,16 +1411,26 @@ And then just do
1379paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1411paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1380paddingEach { top, right, bottom, left } =
1412paddingEach { top, right, bottom, left } =
1381 if top == right && top == bottom && top == left then
1413 if top == right && top == bottom && top == left then
1414+ let
1415+ topFloat =
1416+ toFloat top
1417+ in
1418+ Internal.StyleClass Flag.padding
1382− Internal.StyleClass Flag.padding (Internal.PaddingStyle ("p-" ++ String.fromInt top) top top top top)
1419+ (Internal.PaddingStyle ("p-" ++ String.fromInt top)
1420+ topFloat
1421+ topFloat
1422+ topFloat
1423+ topFloat
1424+ )
1383
1425
1384 else
1426 else
1385 Internal.StyleClass Flag.padding
1427 Internal.StyleClass Flag.padding
1386 (Internal.PaddingStyle
1428 (Internal.PaddingStyle
1387 (Internal.paddingName top right bottom left)
1429 (Internal.paddingName top right bottom left)
1388− top
1430+ (toFloat top)
1389− right
1431+ (toFloat right)
1390− bottom
1432+ (toFloat bottom)
1391− left
1433+ (toFloat left)
1392 )
1434 )
1393
1435
1394
1436
src/Element/Input.elm+78−40
@@ -133,6 +133,11 @@ Instead of implicit submission behavior, [try making an `onEnter` event handler
133And no one has to look up obtuse html documentation to understand the behavior of their code :).
133And no one has to look up obtuse html documentation to understand the behavior of their code :).
134
134
135
135
136+# File Inputs
137+
138+Presently, elm-ui does not expose a replacement for `<input type="file">`; in the meantime, an `Input.button` and `elm/file`'s `File.Select` may meet your needs.
139+
140+
136# Disabling Inputs
141# Disabling Inputs
137
142
138You also might be wondering how to disable an input.
143You also might be wondering how to disable an input.
@@ -325,6 +330,8 @@ The `onPress` handler will be fired either `onClick` or when the element is focu
325 , label = text "My Button"
330 , label = text "My Button"
326 }
331 }
327
332
333+**Note** If you have an icon button but want it to be accessible, consider adding a [`Region.description`](Element-Region#description), which will describe the button to screen readers.
334+
328-}
335-}
329button :
336button :
330 List (Attribute msg)
337 List (Attribute msg)
@@ -362,7 +369,17 @@ button attrs { onPress, label } =
362
369
363 Just msg ->
370 Just msg ->
364 Events.onClick msg
371 Events.onClick msg
365− :: onEnter msg
372+ :: onKeyLookup
373+ (\code ->
374+ if code == enter then
375+ Just msg
376+
377+ else if code == space then
378+ Just msg
379+
380+ else
381+ Nothing
382+ )
366 :: attrs
383 :: attrs
367 )
384 )
368 )
385 )
@@ -440,22 +457,24 @@ checkbox attrs { label, icon, checked, onChange } =
440 ]
457 ]
441 ++ attrs
458 ++ attrs
442 in
459 in
443− applyLabel attributes
460+ applyLabel
444− label
445− (Internal.element
446− Internal.asEl
447− Internal.div
448− [ Internal.Attr <|
449− Html.Attributes.attribute "role" "checkbox"
461+ (Internal.Attr (Html.Attributes.attribute "role" "checkbox")
450− , Internal.Attr <|
462+ :: Internal.Attr
451− Html.Attributes.attribute "aria-checked" <|
463+ (Html.Attributes.attribute "aria-checked" <|
452 if checked then
464 if checked then
453 "true"
465 "true"
454
466
455 else
467 else
456 "false"
468 "false"
469+ )
457− , hiddenLabelAttribute label
470+ :: hiddenLabelAttribute label
471+ :: attributes
472+ )
473+ label
474+ (Internal.element
475+ Internal.asEl
476+ Internal.div
458− , Element.centerY
477+ [ Element.centerY
459 , Element.height Element.fill
478 , Element.height Element.fill
460 , Element.width Element.shrink
479 , Element.width Element.shrink
461 ]
480 ]
@@ -694,7 +713,7 @@ slider attributes input =
694 ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
713 ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
695 thumbShadowStyle
714 thumbShadowStyle
696 )
715 )
697− , Internal.Attr (Html.Attributes.class (className ++ " focusable-parent"))
716+ , Internal.Attr (Html.Attributes.class (className ++ " ui-slide-bar focusable-parent"))
698 , Internal.Attr
717 , Internal.Attr
699 (Html.Events.onInput
718 (Html.Events.onInput
700 (\str ->
719 (\str ->
@@ -760,10 +779,10 @@ slider attributes input =
760 -- This is after `attributes` because the thumb should be in front of everything.
779 -- This is after `attributes` because the thumb should be in front of everything.
761 ++ [ Element.behindContent <|
780 ++ [ Element.behindContent <|
762 if vertical then
781 if vertical then
763− viewVerticalThumb factor thumbAttributes trackWidth
782+ viewVerticalThumb factor (Internal.htmlClass "focusable-thumb" :: thumbAttributes) trackWidth
764
783
765 else
784 else
766− viewHorizontalThumb factor thumbAttributes trackHeight
785+ viewHorizontalThumb factor (Internal.htmlClass "focusable-thumb" :: thumbAttributes) trackHeight
767 ]
786 ]
768 )
787 )
769 Element.none
788 Element.none
@@ -863,10 +882,10 @@ textHelper textInput attrs textOptions =
863 -- when things are off by a pixel or two.
882 -- when things are off by a pixel or two.
864 -- (or at least when the browser *thinks* it's off by a pixel or two)
883 -- (or at least when the browser *thinks* it's off by a pixel or two)
865 Just
884 Just
866− { top = max 0 (floor (toFloat t - 3))
885+ { top = max 0 (floor (t - 3))
867− , right = max 0 (floor (toFloat r - 3))
886+ , right = max 0 (floor (r - 3))
868− , bottom = max 0 (floor (toFloat b - 3))
887+ , bottom = max 0 (floor (b - 3))
869− , left = max 0 (floor (toFloat l - 3))
888+ , left = max 0 (floor (l - 3))
870 }
889 }
871
890
872 _ ->
891 _ ->
@@ -1309,23 +1328,36 @@ redistributeOver isMultiline stacked attr els =
1309 Element.htmlAttribute
1328 Element.htmlAttribute
1310 (Html.Attributes.style
1329 (Html.Attributes.style
1311 "height"
1330 "height"
1312− ("calc(1.0em + " ++ String.fromInt (2 * min t b) ++ "px)")
1331+ ("calc(1.0em + " ++ String.fromFloat (2 * min t b) ++ "px)")
1313 )
1332 )
1314
1333
1315 newLineHeight =
1334 newLineHeight =
1316 Element.htmlAttribute
1335 Element.htmlAttribute
1317 (Html.Attributes.style
1336 (Html.Attributes.style
1318 "line-height"
1337 "line-height"
1319− ("calc(1.0em + " ++ String.fromInt (2 * min t b) ++ "px)")
1338+ ("calc(1.0em + " ++ String.fromFloat (2 * min t b) ++ "px)")
1320 )
1339 )
1321
1340
1341+ newTop =
1342+ t - min t b
1343+
1344+ newBottom =
1345+ b - min t b
1346+
1322 reducedVerticalPadding =
1347 reducedVerticalPadding =
1323− Element.paddingEach
1348+ Internal.StyleClass Flag.padding
1324− { top = t - min t b
1349+ (Internal.PaddingStyle
1325− , right = r
1350+ (Internal.paddingNameFloat
1326− , bottom = b - min t b
1351+ newTop
1327− , left = l
1352+ r
1328− }
1353+ newBottom
1354+ l
1355+ )
1356+ newTop
1357+ r
1358+ newBottom
1359+ l
1360+ )
1329 in
1361 in
1330 { els
1362 { els
1331 | parent = reducedVerticalPadding :: els.parent
1363 | parent = reducedVerticalPadding :: els.parent
@@ -1533,6 +1565,8 @@ email =
1533
1565
1534By default it will have a minimum height of one line and resize based on it's contents.
1566By default it will have a minimum height of one line and resize based on it's contents.
1535
1567
1568+Use `Element.spacing` to change its line-height.
1569+
1536-}
1570-}
1537multiline :
1571multiline :
1538 List (Attribute msg)
1572 List (Attribute msg)
@@ -1594,28 +1628,28 @@ applyLabel attrs label input =
1594 Internal.element
1628 Internal.element
1595 Internal.asColumn
1629 Internal.asColumn
1596 (Internal.NodeName "label")
1630 (Internal.NodeName "label")
1597− attrs
1631+ (Internal.htmlClass classes.inputLabel :: attrs)
1598 (Internal.Unkeyed [ labelElement, input ])
1632 (Internal.Unkeyed [ labelElement, input ])
1599
1633
1600 Below ->
1634 Below ->
1601 Internal.element
1635 Internal.element
1602 Internal.asColumn
1636 Internal.asColumn
1603 (Internal.NodeName "label")
1637 (Internal.NodeName "label")
1604− attrs
1638+ (Internal.htmlClass classes.inputLabel :: attrs)
1605 (Internal.Unkeyed [ input, labelElement ])
1639 (Internal.Unkeyed [ input, labelElement ])
1606
1640
1607 OnRight ->
1641 OnRight ->
1608 Internal.element
1642 Internal.element
1609 Internal.asRow
1643 Internal.asRow
1610 (Internal.NodeName "label")
1644 (Internal.NodeName "label")
1611− attrs
1645+ (Internal.htmlClass classes.inputLabel :: attrs)
1612 (Internal.Unkeyed [ input, labelElement ])
1646 (Internal.Unkeyed [ input, labelElement ])
1613
1647
1614 OnLeft ->
1648 OnLeft ->
1615 Internal.element
1649 Internal.element
1616 Internal.asRow
1650 Internal.asRow
1617 (Internal.NodeName "label")
1651 (Internal.NodeName "label")
1618− attrs
1652+ (Internal.htmlClass classes.inputLabel :: attrs)
1619 (Internal.Unkeyed [ labelElement, input ])
1653 (Internal.Unkeyed [ labelElement, input ])
1620
1654
1621
1655
@@ -2071,7 +2105,11 @@ onKeyLookup lookup =
2071 Json.field "key" Json.string
2105 Json.field "key" Json.string
2072 |> Json.andThen decode
2106 |> Json.andThen decode
2073 in
2107 in
2108+ -- We generally want these attached to the keydown event becaues it allows us to prevent default on things like spacebar scrolling the page.
2109+ -- https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role
2074− Internal.Attr <| Html.Events.on "keyup" isKey
2110+ Internal.Attr <|
2111+ Html.Events.preventDefaultOn "keydown"
2112+ (Json.map (\fired -> ( fired, True )) isKey)
2075
2113
2076
2114
2077{-| -}
2115{-| -}
@@ -2167,7 +2205,8 @@ defaultCheckbox : Bool -> Element msg
2167defaultCheckbox checked =
2205defaultCheckbox checked =
2168 Element.el
2206 Element.el
2169 [ Internal.htmlClass "focusable"
2207 [ Internal.htmlClass "focusable"
2170− , Element.width (Element.px 14)
2208+ , Element.width
2209+ (Element.px 14)
2171 , Element.height (Element.px 14)
2210 , Element.height (Element.px 14)
2172 , Font.color white
2211 , Font.color white
2173 , Element.centerY
2212 , Element.centerY
@@ -2203,9 +2242,8 @@ defaultCheckbox checked =
2203
2242
2204 else
2243 else
2205 1
2244 1
2206− ]
2245+ , Element.inFront
2207− (if checked then
2208− Element.el
2246+ (Element.el
2209 [ Border.color white
2247 [ Border.color white
2210 , Element.height (Element.px 6)
2248 , Element.height (Element.px 6)
2211 , Element.width (Element.px 9)
2249 , Element.width (Element.px 9)
@@ -2213,6 +2251,7 @@ defaultCheckbox checked =
2213 , Element.centerX
2251 , Element.centerX
2214 , Element.centerY
2252 , Element.centerY
2215 , Element.moveUp 1
2253 , Element.moveUp 1
2254+ , Element.transparent (not checked)
2216 , Border.widthEach
2255 , Border.widthEach
2217 { top = 0
2256 { top = 0
2218 , left = 2
2257 , left = 2
@@ -2221,7 +2260,6 @@ defaultCheckbox checked =
2221 }
2260 }
2222 ]
2261 ]
2223 Element.none
2262 Element.none
2224−
2263+ )
2225− else
2264+ ]
2226− Element.none
2265+ Element.none
2227− )
src/Element/Region.elm+5−1
@@ -101,7 +101,11 @@ announce =
101 Internal.Describe LivePolite
101 Internal.Describe LivePolite
102
102
103
103
104+{-| Adds an `aria-label`, which is used by accessibility software to identity otherwise unlabeled elements.
105+
106+A common use for this would be to label buttons that only have an icon.
107+
104−{-| -}
108+-}
105description : String -> Attribute msg
109description : String -> Attribute msg
106description =
110description =
107 Internal.Describe << Internal.Label
111 Internal.Describe << Internal.Label
src/Internal/Model.elm+30−32
@@ -79,6 +79,7 @@ module Internal.Model exposing
79 , onlyStyles
79 , onlyStyles
80 , optionsToRecord
80 , optionsToRecord
81 , paddingName
81 , paddingName
82+ , paddingNameFloat
82 , pageClass
83 , pageClass
83 , paragraphClass
84 , paragraphClass
84 , reduceRecursive
85 , reduceRecursive
@@ -173,7 +174,7 @@ type Style
173 | Colored String String Color
174 | Colored String String Color
174 | SpacingStyle String Int Int
175 | SpacingStyle String Int Int
175 | BorderWidth String Int Int Int Int
176 | BorderWidth String Int Int Int Int
176− | PaddingStyle String Int Int Int Int
177+ | PaddingStyle String Float Float Float Float
177 | GridTemplateStyle
178 | GridTemplateStyle
178 { spacing : ( Length, Length )
179 { spacing : ( Length, Length )
179 , columns : List Length
180 , columns : List Length
@@ -1477,7 +1478,9 @@ renderHeight h =
1477 Single
1478 Single
1478 cls
1479 cls
1479 "min-height"
1480 "min-height"
1481+ -- This needs to be !important because we're using `min-height: min-content`
1482+ -- to correct for safari's incorrect implementation of flexbox.
1480− (String.fromInt minSize ++ "px")
1483+ (String.fromInt minSize ++ "px !important")
1481
1484
1482 ( newFlag, newAttrs, newStyle ) =
1485 ( newFlag, newAttrs, newStyle ) =
1483 renderHeight len
1486 renderHeight len
@@ -1945,7 +1948,7 @@ type Spacing
1945
1948
1946
1949
1947type Padding
1950type Padding
1948− = Padding String Int Int Int Int
1951+ = Padding String Float Float Float Float
1949
1952
1950
1953
1951extractSpacingAndPadding : List (Attribute aligned msg) -> ( Maybe Padding, Maybe Spacing )
1954extractSpacingAndPadding : List (Attribute aligned msg) -> ( Maybe Padding, Maybe Spacing )
@@ -2241,7 +2244,11 @@ renderFocusStyle focus =
2241 , Just <| Property "outline" "none"
2244 , Just <| Property "outline" "none"
2242 ]
2245 ]
2243 )
2246 )
2247+ , Style
2244− , Style (Internal.Style.dot classes.any ++ ":focus .focusable, " ++ Internal.Style.dot classes.any ++ ".focusable:focus")
2248+ ((Internal.Style.dot classes.any ++ ":focus .focusable, ")
2249+ ++ (Internal.Style.dot classes.any ++ ".focusable:focus, ")
2250+ ++ (".ui-slide-bar:focus + " ++ Internal.Style.dot classes.any ++ " .focusable-thumb")
2251+ )
2245 (List.filterMap identity
2252 (List.filterMap identity
2246 [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
2253 [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
2247 , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
2254 , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
@@ -2642,34 +2649,14 @@ renderStyle options maybePseudo selector props =
2642 renderedProps =
2649 renderedProps =
2643 List.foldl (renderProps False) "" props
2650 List.foldl (renderProps False) "" props
2644 in
2651 in
2645− [ selector
2646− ++ "-fs:focus {"
2647− ++ renderedProps
2652+ [ selector ++ "-fs:focus {" ++ renderedProps ++ "\n}"
2648− ++ "\n}"
2649− , "."
2650− ++ classes.any
2653+ , ("." ++ classes.any ++ ":focus " ++ selector ++ "-fs {")
2651− ++ ":focus ~ "
2652− ++ selector
2653− ++ "-fs:not(.focus) {"
2654 ++ renderedProps
2654 ++ renderedProps
2655 ++ "\n}"
2655 ++ "\n}"
2656− , "."
2656+ , (selector ++ "-fs:focus-within {")
2657− ++ classes.any
2658− ++ ":focus "
2659− ++ selector
2660− ++ "-fs {"
2661 ++ renderedProps
2657 ++ renderedProps
2662 ++ "\n}"
2658 ++ "\n}"
2663− , selector
2659+ , (".ui-slide-bar:focus + " ++ Internal.Style.dot classes.any ++ " .focusable-thumb" ++ selector ++ "-fs {")
2664− ++ "-fs:focus-within {"
2665− ++ renderedProps
2666− ++ "\n}"
2667− , ".focusable-parent:focus ~ "
2668− ++ "."
2669− ++ classes.any
2670− ++ " "
2671− ++ selector
2672− ++ "-fs {"
2673 ++ renderedProps
2660 ++ renderedProps
2674 ++ "\n}"
2661 ++ "\n}"
2675 ]
2662 ]
@@ -2876,13 +2863,13 @@ renderStyleRule options rule maybePseudo =
2876 maybePseudo
2863 maybePseudo
2877 class
2864 class
2878 [ Property "padding"
2865 [ Property "padding"
2879− (String.fromInt top
2866+ (String.fromFloat top
2880 ++ "px "
2867 ++ "px "
2881− ++ String.fromInt right
2868+ ++ String.fromFloat right
2882 ++ "px "
2869 ++ "px "
2883− ++ String.fromInt bottom
2870+ ++ String.fromFloat bottom
2884 ++ "px "
2871 ++ "px "
2885− ++ String.fromInt left
2872+ ++ String.fromFloat left
2886 ++ "px"
2873 ++ "px"
2887 )
2874 )
2888 ]
2875 ]
@@ -3212,6 +3199,17 @@ paddingName top right bottom left =
3212 ++ String.fromInt left
3199 ++ String.fromInt left
3213
3200
3214
3201
3202+paddingNameFloat top right bottom left =
3203+ "pad-"
3204+ ++ floatClass top
3205+ ++ "-"
3206+ ++ floatClass right
3207+ ++ "-"
3208+ ++ floatClass bottom
3209+ ++ "-"
3210+ ++ floatClass left
3211+
3212+
3215getStyleName : Style -> String
3213getStyleName : Style -> String
3216getStyleName style =
3214getStyleName style =
3217 case style of
3215 case style of
src/Internal/Style.elm+63−16
@@ -10,6 +10,7 @@ type Class
10type Rule
10type Rule
11 = Prop String String
11 = Prop String String
12 | Child String (List Rule)
12 | Child String (List Rule)
13+ | AllChildren String (List Rule)
13 | Supports ( String, String ) (List ( String, String ))
14 | Supports ( String, String ) (List ( String, String ))
14 | Descriptor String (List Rule)
15 | Descriptor String (List Rule)
15 | Adjacent String (List Rule)
16 | Adjacent String (List Rule)
@@ -256,6 +257,7 @@ classes =
256 , inputMultilineParent = "imlp"
257 , inputMultilineParent = "imlp"
257 , inputMultilineFiller = "imlf"
258 , inputMultilineFiller = "imlf"
258 , inputMultilineWrapper = "implw"
259 , inputMultilineWrapper = "implw"
260+ , inputLabel = "lbl"
259
261
260 -- link
262 -- link
261 , link = "lnk"
263 , link = "lnk"
@@ -560,6 +562,15 @@ renderRules (Intermediate parent) rulesToRender =
560 :: rendered.others
562 :: rendered.others
561 }
563 }
562
564
565+ AllChildren child childRules ->
566+ { rendered
567+ | others =
568+ renderRules
569+ (emptyIntermediate (parent.selector ++ " " ++ child) "")
570+ childRules
571+ :: rendered.others
572+ }
573+
563 Descriptor descriptor descriptorRules ->
574 Descriptor descriptor descriptorRules ->
564 { rendered
575 { rendered
565 | others =
576 | others =
@@ -950,6 +961,18 @@ baseSheet =
950 ]
961 ]
951 , Class (dot classes.any ++ dot classes.single ++ dot classes.imageContainer)
962 , Class (dot classes.any ++ dot classes.single ++ dot classes.imageContainer)
952 [ Prop "display" "block"
963 [ Prop "display" "block"
964+ , Descriptor (dot classes.heightFill)
965+ [ Child "img"
966+ [ Prop "max-height" "100%"
967+ , Prop "object-fit" "cover"
968+ ]
969+ ]
970+ , Descriptor (dot classes.widthFill)
971+ [ Child "img"
972+ [ Prop "max-width" "100%"
973+ , Prop "object-fit" "cover"
974+ ]
975+ ]
953 ]
976 ]
954 , Class (dot classes.any ++ ":focus")
977 , Class (dot classes.any ++ ":focus")
955 [ Prop "outline" "none"
978 [ Prop "outline" "none"
@@ -972,6 +995,7 @@ baseSheet =
972 , Child (dot classes.inFront)
995 , Child (dot classes.inFront)
973 [ Descriptor (dot classes.nearby)
996 [ Descriptor (dot classes.nearby)
974 [ Prop "position" "fixed"
997 [ Prop "position" "fixed"
998+ , Prop "z-index" "20"
975 ]
999 ]
976 ]
1000 ]
977 ]
1001 ]
@@ -1220,6 +1244,7 @@ baseSheet =
1220 -- If it's 1, it bumps up to something like 1.2
1244 -- If it's 1, it bumps up to something like 1.2
1221 [ Prop "line-height" "1.05"
1245 [ Prop "line-height" "1.05"
1222 , Prop "background" "transparent"
1246 , Prop "background" "transparent"
1247+ , Prop "text-align" "inherit"
1223 ]
1248 ]
1224 , Descriptor (dot classes.single)
1249 , Descriptor (dot classes.single)
1225 elDescription
1250 elDescription
@@ -1346,18 +1371,28 @@ baseSheet =
1346 , Descriptor (dot classes.spaceEvenly)
1371 , Descriptor (dot classes.spaceEvenly)
1347 [ Prop "justify-content" "space-between"
1372 [ Prop "justify-content" "space-between"
1348 ]
1373 ]
1374+ , Descriptor (dot classes.inputLabel)
1375+ [ Prop "align-items" "baseline"
1376+ ]
1349 ]
1377 ]
1350 , Descriptor (dot classes.column)
1378 , Descriptor (dot classes.column)
1351 [ Prop "display" "flex"
1379 [ Prop "display" "flex"
1352 , Prop "flex-direction" "column"
1380 , Prop "flex-direction" "column"
1353 , Child (dot classes.any)
1381 , Child (dot classes.any)
1382+ -- *Note* - While rows have flex-basis 0%,
1383+ -- which allows for the children of a row to default to their content size
1384+ -- This apparently is a different story for columns.
1385+ -- Safari has an issue if this is flex-basis: 0%, as it goes entirely to 0,
1386+ -- instead of the expected content size.
1387+ -- So we add `min-height: min-content`, which isn't supported by IE, but works for all other browsers!
1388+ -- Separately, 0% is different than 0px, but only for columns
1389+ -- In columns, 0% will actually be calculated as `auto` for columns
1390+ -- So, 0px is the one we want.
1354− [ Prop "flex-basis" "0%"
1391+ [ Prop "flex-basis" "0px"
1392+ , Prop "min-height" "min-content"
1355 , Descriptor (dot classes.heightExact)
1393 , Descriptor (dot classes.heightExact)
1356 [ Prop "flex-basis" "auto"
1394 [ Prop "flex-basis" "auto"
1357 ]
1395 ]
1358− , Descriptor (dot classes.column)
1359− [ Prop "flex-basis" "auto"
1360− ]
1361 ]
1396 ]
1362 , Child (dot classes.heightFill)
1397 , Child (dot classes.heightFill)
1363 [ Prop "flex-grow" "100000"
1398 [ Prop "flex-grow" "100000"
@@ -1561,7 +1596,7 @@ baseSheet =
1561 )
1596 )
1562 ]
1597 ]
1563 , Descriptor (dot classes.inputMultiline)
1598 , Descriptor (dot classes.inputMultiline)
1564− [ Prop "white-space" "pre-wrap"
1599+ [ Prop "white-space" "pre-wrap !important"
1565 , Prop "height" "100%"
1600 , Prop "height" "100%"
1566 , Prop "width" "100%"
1601 , Prop "width" "100%"
1567 , Prop "background-color" "transparent"
1602 , Prop "background-color" "transparent"
@@ -1581,29 +1616,47 @@ baseSheet =
1581 [ Prop "flex-basis" "auto" ]
1616 [ Prop "flex-basis" "auto" ]
1582 ]
1617 ]
1583 , Descriptor (dot classes.inputMultilineParent)
1618 , Descriptor (dot classes.inputMultilineParent)
1584− [ Prop "white-space" "pre-wrap"
1619+ [ Prop "white-space" "pre-wrap !important"
1585 , Prop "cursor" "text"
1620 , Prop "cursor" "text"
1586 , Child (dot classes.inputMultilineFiller)
1621 , Child (dot classes.inputMultilineFiller)
1587− [ Prop "white-space" "pre-wrap"
1622+ [ Prop "white-space" "pre-wrap !important"
1588 , Prop "color" "transparent"
1623 , Prop "color" "transparent"
1589 ]
1624 ]
1590 ]
1625 ]
1591 , Descriptor (dot classes.paragraph)
1626 , Descriptor (dot classes.paragraph)
1592 [ Prop "display" "block"
1627 [ Prop "display" "block"
1593 , Prop "white-space" "normal"
1628 , Prop "white-space" "normal"
1629+ , Prop "overflow-wrap" "break-word"
1594 , Descriptor (dot classes.hasBehind)
1630 , Descriptor (dot classes.hasBehind)
1595 [ Prop "z-index" "0"
1631 [ Prop "z-index" "0"
1596 , Child (dot classes.behind)
1632 , Child (dot classes.behind)
1597 [ Prop "z-index" "-1"
1633 [ Prop "z-index" "-1"
1598 ]
1634 ]
1599 ]
1635 ]
1600− , Child (dot classes.text)
1636+ , AllChildren (dot classes.text)
1601 [ Prop "display" "inline"
1637 [ Prop "display" "inline"
1602 , Prop "white-space" "normal"
1638 , Prop "white-space" "normal"
1603 ]
1639 ]
1640+ , AllChildren (dot classes.paragraph)
1641+ [ Prop "display" "inline"
1642+ , Descriptor "::after"
1643+ [ Prop "content" "none"
1644+ ]
1645+ , Descriptor "::before"
1646+ [ Prop "content" "none"
1647+ ]
1648+ ]
1604− , Child (dot classes.single)
1649+ , AllChildren (dot classes.single)
1605 [ Prop "display" "inline"
1650 [ Prop "display" "inline"
1606 , Prop "white-space" "normal"
1651 , Prop "white-space" "normal"
1652+
1653+ -- Inline block allows the width of the item to be set
1654+ -- but DOES NOT like wrapping text in a standard, normal, sane way.
1655+ -- We're sorta counting that if an exact width has been set,
1656+ -- people aren't expecting proper text wrapping for this element
1657+ , Descriptor (dot classes.widthExact)
1658+ [ Prop "display" "inline-block"
1659+ ]
1607 , Descriptor (dot classes.inFront)
1660 , Descriptor (dot classes.inFront)
1608 [ Prop "display" "flex"
1661 [ Prop "display" "flex"
1609 ]
1662 ]
@@ -1626,15 +1679,9 @@ baseSheet =
1626 [ Prop "display" "inline"
1679 [ Prop "display" "inline"
1627 , Prop "white-space" "normal"
1680 , Prop "white-space" "normal"
1628 ]
1681 ]
1629− , Child (dot classes.single)
1630− [ Child (dot classes.text)
1631− [ Prop "display" "inline"
1632− , Prop "white-space" "normal"
1633− ]
1634− ]
1635 ]
1682 ]
1636 , Child (dot classes.row)
1683 , Child (dot classes.row)
1637− [ Prop "display" "inline-flex"
1684+ [ Prop "display" "inline"
1638 ]
1685 ]
1639 , Child (dot classes.column)
1686 , Child (dot classes.column)
1640 [ Prop "display" "inline-flex"
1687 [ Prop "display" "inline-flex"
tests-rendering/automation/build.jsnew file+90
@@ -0,0 +1,90 @@
1+const fs = require("fs");
2+const path = require("path");
3+const compileToString = require("node-elm-compiler").compileToString;
4+
5+// {
6+// template: "./tests-rendering/automation/templates/gather-styles.html",
7+// target: "./tmp/test.html",
8+// elm: "src/Tests/Run.elm",
9+// replace:
10+// elmOptions: { cwd: "./tests-rendering" },
11+// }
12+
13+async function compile_and_embed(config) {
14+ const elm_entry_module = fs.readFileSync(
15+ path.join(config.elmOptions.cwd, config.elm),
16+ "utf8"
17+ );
18+
19+ const module_name = elm_entry_module
20+ .split(/[\r\n]+/)[0]
21+ .match(/module ([A-Za-z0-9\.]+)/i)[1];
22+
23+ // we embed the compiled js to avoid having to start a server to read the app.
24+ await compileToString([config.elm], config.elmOptions).then(function (
25+ compiled_elm_code
26+ ) {
27+ // console.log(compiled_elm_code);
28+ const compiled = fs
29+ .readFileSync(config.template, "utf8")
30+ .replace(/~~_compiled_elm_code_~~/, () => compiled_elm_code)
31+ .replace(/~~_module_name_~~/, () => module_name);
32+
33+ fs.writeFileSync(config.target, compiled);
34+ });
35+}
36+
37+async function compile_to_string(config) {
38+ const elm_entry_module = fs.readFileSync(
39+ path.join(config.elmOptions.cwd, config.elm),
40+ "utf8"
41+ );
42+
43+ const module_name = elm_entry_module
44+ .split(/[\r\n]+/)[0]
45+ .match(/module ([A-Za-z0-9\.]+)/i)[1];
46+
47+ const compiled = await compileToString([config.elm], config.elmOptions).then(
48+ function (compiled_elm_code) {
49+ return fs
50+ .readFileSync(config.template, "utf8")
51+ .replace(/~~_module_name_~~/g, () => module_name)
52+ .replace(/~~_compiled_elm_code_~~/g, () => compiled_elm_code);
53+ }
54+ );
55+ return compiled;
56+}
57+
58+async function get_module_name(config) {
59+ const elm_entry_module = fs.readFileSync(
60+ path.join(config.cwd, config.elm),
61+ "utf8"
62+ );
63+
64+ const module_name = elm_entry_module
65+ .split(/[\r\n]+/)[0]
66+ .match(/module ([A-Za-z0-9\.]+)/i)[1];
67+ return module_name;
68+}
69+
70+async function create_runner(config) {
71+ const elm_entry_module = fs.readFileSync(config.elm, "utf8");
72+
73+ let module_name = elm_entry_module
74+ .split(/[\r\n]+/)[0]
75+ .match(/module ([A-Za-z0-9\.]+)/i)[1];
76+
77+ const module_name_sentence = module_name.replace(/([A-Z])/g, " $1");
78+
79+ const runner = fs
80+ .readFileSync(config.template, "utf8")
81+ .replace(/~~_module_name_~~/g, () => module_name)
82+ .replace(/~~_module_name_sentence_~~/g, () => module_name_sentence);
83+ fs.writeFileSync(config.target, runner);
84+}
85+
86+exports.compile_and_embed = compile_and_embed;
87+
88+exports.create_runner = create_runner;
89+
90+exports.compile_to_string = compile_to_string;
tests-rendering/automation/doc-preview.shnew file+1
@@ -0,0 +1 @@
1+elm make --docs docs.json
tests-rendering/automation/publish-file.sh+2
@@ -19,6 +19,8 @@ if [ -z "$(git status --porcelain)" ]; then
19else
19else
20 cd "$DIRECTORY"
20 cd "$DIRECTORY"
21 # Uncommitted changes
21 # Uncommitted changes
22+ git config --global user.email "$GIT_EMAIL"
23+ git config --global user.name "$GIT_NAME"
22 git add .
24 git add .
23 git commit -m "Elm UI test for $NAME on $BUILD"
25 git commit -m "Elm UI test for $NAME on $BUILD"
24 git push origin master
26 git push origin master
tests-rendering/automation/run.js+263−255
@@ -1,296 +1,304 @@
1−const compileToString = require("node-elm-compiler").compileToString;
2−const { Builder, By, Key, until } = require('selenium-webdriver');
1+const { Builder, By, Key, until } = require("selenium-webdriver");
3−const chrome = require('selenium-webdriver/chrome');
2+const chrome = require("selenium-webdriver/chrome");
4−const firefox = require('selenium-webdriver/firefox');
3+const firefox = require("selenium-webdriver/firefox");
5−const fs = require('fs');
4+const fs = require("fs");
6−const program = require('commander');
5+const program = require("commander");
7const path = require("path");
6const path = require("path");
8−const chalk = require('chalk');
7+const chalk = require("chalk");
9−const childProcess = require('child_process')
8+const childProcess = require("child_process");
10−
9+const compile_and_embed = require("./build.js").compile_and_embed;
10+
11+program
11−program.option('-s, --sauce', 'run tests on sauce labs')
12+ .option("-s, --sauce", "run tests on sauce labs")
12− .option('--chrome', 'run chrome tests (default)')
13+ .option("--chrome", "run chrome tests (default)")
13− .option('--firefox', 'run firefox tests')
14+ .option("--firefox", "run firefox tests")
14− .option('--ie', 'run ie tests')
15+ .option("--ie", "run ie tests")
15− .option('--edge', 'run edge tests')
16+ .option("--edge", "run edge tests")
16− .option('--build [value]', 'Set build number for sauce labs')
17+ .option("--build [value]", "Set build number for sauce labs")
17− .option('--name [value]', 'Set run name for sauce labs')
18+ .option("--name [value]", "Set run name for sauce labs")
18− .option('--verbose', 'Print out all test results')
19+ .option("--verbose", "Print out all test results")
20+ .option("--headless", "run tests without showing the browser")
19− .parse(process.argv)
21+ .parse(process.argv);
20−
21
22
22// 'Windows 10'
23// 'Windows 10'
23
24
24−
25const windows = {
25const windows = {
26− chrome: {
26+ chrome: {
27− platform: "Windows 10"
27+ platform: "Windows 10",
28− , browser: "chrome"
28+ browser: "chrome",
29− , browserVersion: "latest"
29+ browserVersion: "latest",
30− },
30+ },
31− firefox: {
31+ firefox: {
32− platform: "Windows 10"
32+ platform: "Windows 10",
33− , browser: "firefox"
33+ browser: "firefox",
34− , browserVersion: "latest"
34+ browserVersion: "latest",
35− },
35+ },
36− edge: {
36+ edge: {
37− platform: "Windows 10"
37+ platform: "Windows 10",
38− , browser: "MicrosoftEdge"
38+ browser: "MicrosoftEdge",
39− , browserVersion: "latest"
39+ browserVersion: "latest",
40− },
40+ },
41− ie: {
41+ ie: {
42− platform: "Windows 10"
42+ platform: "Windows 10",
43− , browser: "internet explorer"
43+ browser: "internet explorer",
44− , browserVersion: "latest"
44+ browserVersion: "latest",
45− }
45+ },
46−}
46+};
47
47
48const all_windows = [
48const all_windows = [
49− windows.chrome
49+ windows.chrome,
50− , windows.firefox
50+ windows.firefox,
51− , windows.edge
51+ windows.edge,
52− // i.e. takes forever and seems to make selenium timeout :/
52+ // i.e. takes forever and seems to make selenium timeout :/
53− // not really sure how to extend the timeout.
53+ // not really sure how to extend the timeout.
54− // , windows.ie
54+ // , windows.ie
55−]
55+];
56
56
57// "macOS 10.14"
57// "macOS 10.14"
58const osx = {
58const osx = {
59− chrome: {
59+ chrome: {
60− platform: "macOS 10.14"
60+ platform: "macOS 10.14",
61− , browser: "chrome"
61+ browser: "chrome",
62− , browserVersion: "latest"
62+ browserVersion: "latest",
63− },
63+ },
64− firefox: {
64+ firefox: {
65− platform: "macOS 10.14"
65+ platform: "macOS 10.14",
66− , browser: "firefox"
66+ browser: "firefox",
67− , browserVersion: "latest"
67+ browserVersion: "latest",
68− },
68+ },
69− safari: {
69+ safari: {
70− platform: "macOS 10.14"
70+ platform: "macOS 10.14",
71− , browser: "safari"
71+ browser: "safari",
72− , browserVersion: "latest"
72+ browserVersion: "latest",
73− }
73+ },
74−}
74+};
75−
75+
76−const all_osx = [
76+const all_osx = [osx.safari];
77− osx.chrome
77+
78− , osx.firefox
79− , osx.safari
80−]
81−
82−const all_browsers =
83− all_windows.concat(all_osx)
78+const all_browsers = all_windows.concat(all_osx);
84
79
85function prepare_all_envs(config) {
80function prepare_all_envs(config) {
86− var envs = []
81+ var envs = [];
87−
82+
88− for (i = 0; i < all_windows.length; i++) {
83+ for (i = 0; i < all_browsers.length; i++) {
89− envs.push({
84+ envs.push({
90− browser: all_windows[i].browser,
85+ browser: all_browsers[i].browser,
91− browserVersion: all_windows[i].browserVersion,
86+ browserVersion: all_browsers[i].browserVersion,
92− platform: all_windows[i].platform,
87+ platform: all_browsers[i].platform,
93− build: config.build,
88+ build: config.build,
94− name: config.name
89+ name: config.name,
95− })
96− }
97−
98− // for (i = 0; i < osx.length; i++) {
99− // envs.push({
100− // browser: osx[i].browser,
101− // browserVersion: osx[i].browserVersion,
102− // platform: "macOS 10.14",
103− // build: config.build,
104− // name: config.name
105− // })
106− // }
107−
108− return envs
109−}
110−
111−async function compile_and_embed(config) {
112− var template = fs.readFileSync(config.template)
113− // we embed the compiled js to avoid having to start a server to read the app.
114− await compileToString(config.elm, config.elmOptions).then(function (compiled_elm_code) {
115− const compiled = eval(`\`${template}\``)
116− fs.writeFileSync(config.target, compiled)
117 });
90 });
91+ }
92+
93+ // for (i = 0; i < osx.length; i++) {
94+ // envs.push({
95+ // browser: osx[i].browser,
96+ // browserVersion: osx[i].browserVersion,
97+ // platform: "macOS 10.14",
98+ // build: config.build,
99+ // name: config.name,
100+ // });
101+ // }
102+
103+ return envs;
118}
104}
119
105
120−
121−
122−
123function prepare_sauce_driver(env) {
106function prepare_sauce_driver(env) {
124− const username = process.env.SAUCE_USERNAME;
107+ const username = process.env.SAUCE_USERNAME;
125− const accessKey = process.env.SAUCE_ACCESS_KEY;
108+ const accessKey = process.env.SAUCE_ACCESS_KEY;
126−
109+
127− driver = new Builder().withCapabilities({
110+ driver = new Builder()
111+ .withCapabilities({
128− 'browserName': env.browser,
112+ browserName: env.browser,
129− 'platformName': env.platform,
113+ platformName: env.platform,
130− 'browserVersion': env.browserVersion,
114+ browserVersion: env.browserVersion,
131− /* Pass Sauce User Name and Access Key */
115+ /* Pass Sauce User Name and Access Key */
132− 'sauce:options': {
116+ "sauce:options": {
133− 'username': username,
117+ username: username,
134− 'accessKey': accessKey,
118+ accessKey: accessKey,
135− 'build': env.build,
119+ build: env.build,
136− 'name': env.name
120+ name: env.name,
137− }
121+ },
122+ })
138− }).usingServer("https://@ondemand.saucelabs.com:443/wd/hub").build();
123+ .usingServer("https://@ondemand.saucelabs.com:443/wd/hub")
124+ .build();
139
125
140− return driver;
126+ return driver;
141}
127}
142
128
143async function prepare_local_driver(env) {
129async function prepare_local_driver(env) {
144− const firefoxOptions = new firefox.Options().headless()
130+ const firefoxOptions = new firefox.Options();
145− const chromeOptions = new chrome.Options().headless()
131+ const chromeOptions = new chrome.Options();
146
132
147− let driver = await new Builder()
133+ if (program.headless) {
148− .forBrowser(env.browser)
134+ firefoxOptions.headless();
149− .setChromeOptions(chromeOptions)
135+ chromeOptions.headless();
150− .setFirefoxOptions(firefoxOptions)
136+ }
151− .build();
152
137
153− // Allow waits up to 5 minutes
138+ let driver = await new Builder()
154− await driver.manage().timeouts().implicitlyWait(300, TimeUnit.SECONDS);
139+ .forBrowser(env.browser)
140+ .setChromeOptions(chromeOptions)
141+ .setFirefoxOptions(firefoxOptions)
142+ .build();
155
143
156− return driver;
144+ return driver;
157}
145}
158
146
159async function run_test(driver, url) {
147async function run_test(driver, url) {
160− var results = null
148+ var results = null;
161− try {
149+ try {
150+ await driver
151+ .manage()
152+ .window()
162− await driver.manage().window().setRect({ width: 1200, height: 800, x: 0, y: 0 });
153+ .setRect({ width: 1200, height: 800, x: 0, y: 0 });
163− await driver.get(url);
154+ await driver.get(url);
164− await driver.wait(until.titleIs('tests finished'), 240 * 1000);
155+ await driver.wait(until.titleIs("tests finished"), 240 * 1000);
165− results = await driver.executeScript("return test_results")
156+ results = await driver.executeScript("return test_results");
166− } finally {
157+ } finally {
167− await driver.quit();
158+ await driver.quit();
168− }
159+ }
169− return results
160+ return results;
170}
161}
171
162
172−
173−
174(async () => {
163(async () => {
175− var dir = "tmp"
164+ var dir = "tmp";
176− if (!fs.existsSync(dir)) {
165+ if (!fs.existsSync(dir)) {
177− fs.mkdirSync(dir);
166+ fs.mkdirSync(dir);
167+ }
168+ console.log("Compiling tests");
169+ await compile_and_embed({
170+ template: "./tests-rendering/automation/templates/gather-styles.html",
171+ target: "./tmp/test.html",
172+ elm: "src/Tests/All.elm",
173+ elmOptions: { cwd: "./tests-rendering" },
174+ });
175+ console.log("Done compiling");
176+
177+ if (program.sauce) {
178+ const build = program.build;
179+ const name = program.name;
180+
181+ if (build == null || name == null) {
182+ throw "A Build and a Name must be specified.";
178 }
183 }
179− console.log("Compiling tests")
180− await compile_and_embed({
181− template: "./tests-rendering/automation/templates/gather-styles.html",
182− target: "./tmp/test.html",
183− elm: ["src/Tests/Run.elm"],
184− elmOptions: { cwd: "./tests-rendering" }
185− })
186− console.log("Done compiling")
187
184
188− if (program.sauce) {
189− const build = program.build
190− const name = program.name
191−
192− if (build == null || name == null) {
193− throw ("A Build and a Name must be specified.");
194− }
195−
196− var results = []
185+ var results = [];
197−
186+
198− // Publish to netlify
187+ // Publish to netlify
199− childProcess.execSync("sh tests-rendering/automation/publish-file.sh", {
188+ childProcess.execSync("sh tests-rendering/automation/publish-file.sh", {
200− env: {
189+ env: {
201− FILE: "test.html",
190+ FILE: "test.html",
202− BUILD: program.build,
191+ BUILD: program.build,
203− NAME: program.name
192+ NAME: program.name,
204− }
193+ GIT_EMAIL: "automation@elm-ui.com",
205− })
194+ GIT_NAME: "Elm UI Automation",
206− var url = `http://elm-ui-testing.netlify.com/tests/${program.build}/${program.name}/`
195+ HOME: process.env.HOME,
207− console.log(`Running sauce labs test:`)
196+ SSH_AUTH_SOCK: process.env.SSH_AUTH_SOCK,
208− console.log(` ${url}`)
197+ SSH_AGENT_PID: process.env.SSH_AGENT_PID,
209−
198+ },
210− const envs = prepare_all_envs({ build: build, name: name })
199+ });
211− var test_runs = []
212− var test_labels = []
213− for (i = 0; i < envs.length; i++) {
214− var driver = prepare_sauce_driver(envs[i])
215− test_runs.push(run_test(driver, url))
216− test_labels.push(envs[i])
217−
218− }
219− var results = await Promise.all(test_runs)
220− for (i = 0; i < results.length; i++) {
221− print_results(renderEnvName(test_labels[i]), results[i]);
222− }
223
200
201+ var url = `http://elm-ui-testing.netlify.com/tests/${program.build}/${program.name}/`;
202+ console.log(`Running sauce labs test:`);
203+ console.log(` ${url}`);
204+
205+ const envs = prepare_all_envs({ build: build, name: name });
206+ console.log(envs);
207+ var test_runs = [];
208+ var test_labels = [];
209+ for (i = 0; i < envs.length; i++) {
210+ var driver = prepare_sauce_driver(envs[i]);
211+ test_runs.push(run_test(driver, url));
212+ test_labels.push(envs[i]);
213+ }
214+ var results = await Promise.all(test_runs);
215+ for (i = 0; i < results.length; i++) {
216+ print_results(renderEnvName(test_labels[i]), results[i]);
217+ }
224− } else {
218+ } else {
225− // Run chrome if nothing else is selected
219+ // Run chrome if nothing else is selected
226− if (program.chrome || (!program.chrome && !program.firefox)) {
220+ if (program.chrome || (!program.chrome && !program.firefox)) {
227− console.log("Running locally on Chrome...")
221+ console.log("Running locally on Chrome...");
228− const driver = await prepare_local_driver(osx.chrome)
222+ const driver = await prepare_local_driver(osx.chrome);
223+ var results = await run_test(
224+ driver,
229− var results = await run_test(driver, "file://" + path.resolve('./tmp/test.html'))
225+ "file://" + path.resolve("./tmp/test.html")
226+ );
230− print_results("Local Chrome", results);
227+ print_results("Local Chrome", results);
231− }
228+ }
232− if (program.firefox) {
229+ if (program.firefox) {
233− console.log("Running locally on Firefox...")
230+ console.log("Running locally on Firefox...");
234− const driver = await prepare_local_driver(osx.firefox)
231+ const driver = await prepare_local_driver(osx.firefox);
232+ var results = await run_test(
233+ driver,
235− var results = await run_test(driver, "file://" + path.resolve('./tmp/test.html'))
234+ "file://" + path.resolve("./tmp/test.html")
235+ );
236− print_results("Local Firefox", results);
236+ print_results("Local Firefox", results);
237− }
237+ }
238− if (program.ie) {
238+ if (program.ie) {
239− console.log("Running locally on IE...")
239+ console.log("Running locally on IE...");
240− const driver = await prepare_local_driver(windows.ie)
240+ const driver = await prepare_local_driver(windows.ie);
241− var results = await run_test(driver, "file://" + path.resolve('./tmp/test.html'))
241+ var results = await run_test(
242− print_results("Local IE", results);
242+ driver,
243− }
244− if (program.edge) {
245− console.log("Running locally on Edge...")
246− const driver = await prepare_local_driver(windows.edge)
247− var results = await run_test(driver, "file://" + path.resolve('./tmp/test.html'))
243+ "file://" + path.resolve("./tmp/test.html")
244+ );
248− print_results("Local Edge", results);
245+ print_results("Local IE", results);
249− }
250 }
246 }
247+ if (program.edge) {
248+ console.log("Running locally on Edge...");
249+ const driver = await prepare_local_driver(windows.edge);
250+ var results = await run_test(
251+ driver,
252+ "file://" + path.resolve("./tmp/test.html")
253+ );
254+ print_results("Local Edge", results);
255+ }
256+ }
251})();
257})();
252
258
253function renderEnvName(env) {
259function renderEnvName(env) {
254− return `${env.platform}, ${env.browser} ${env.browserVersion}`
260+ return `${env.platform}, ${env.browser} ${env.browserVersion}`;
255}
261}
256
262
257−function print_results(label, results) {
263+function print_results(label, tests) {
258− var total_passed = 0
264+ var total_passed = 0;
259− var total_failed = 0
265+ var total_failed = 0;
260− var i;
266+ var i;
261− if (program.verbose) {
267+ if (program.verbose) {
262− console.log(label)
268+ console.log(label);
263− }
269+ }
264− for (i = 0; i < results.length; i++) {
270+ for (i = 0; i < tests.length; i++) {
265− var passed = 0
271+ var passed = 0;
266− var failed = 0
272+ var failed = 0;
267− for (j = 0; j < results[i].results.length; j++) {
273+ for (j = 0; j < tests[i].results.length; j++) {
268− if (results[i].results[j][1] == null) {
274+ if (tests[i].results[j].passing) {
269− passed = passed + 1
275+ passed = passed + 1;
270− } else {
276+ } else {
271− if (failed == 0) {
277+ if (failed == 0) {
272− if (!program.verbose) {
278+ if (!program.verbose) {
273− console.log(label)
274− }
275− console.log(results[i].label)
279+ console.log(label);
276− }
280+ }
277− failed = failed + 1
278− console.log(" " + chalk.red("fail") + " -> " + results[i].results[j][0])
279− console.log(" " + results[i].results[j][1].description)
280− }
281− }
282− total_passed = total_passed + passed
283− total_failed = total_failed + failed
284−
285− if (failed != 0) {
286− console.log(chalk.green(` ${passed} tests passed`))
287− console.log(chalk.red(` ${failed} tests failed`))
281+ console.log(tests[i].label);
288− console.log()
289− } else if (program.verbose) {
290− console.log(results[i].label + chalk.green(` ${passed} tests passed`))
291 }
282 }
283+ failed = failed + 1;
284+ console.log(" " + chalk.red("fail") + " ->");
285+ console.log(" " + tests[i].results[j].description);
286+ }
292 }
287 }
293− if (total_failed == 0) {
288+ total_passed = total_passed + passed;
289+ total_failed = total_failed + failed;
290+
291+ if (failed != 0) {
292+ console.log(chalk.green(` ${passed} tests passed`));
293+ console.log(chalk.red(` ${failed} tests failed`));
294+ console.log();
295+ } else if (program.verbose) {
294− console.log(`${label} -> ` + chalk.green(`All ${total_passed} tests passed`))
296+ console.log(tests[i].label + chalk.green(` ${passed} tests passed`));
295 }
297 }
298+ }
299+ if (total_failed == 0) {
300+ console.log(
301+ `${label} -> ` + chalk.green(`All ${total_passed} tests passed`)
302+ );
303+ }
296−}
304+}
tests-rendering/automation/templates/Run.elmnew file+14
@@ -0,0 +1,14 @@
1+module Tests.Run exposing (main)
2+
3+{-| _NOTE_ this is auto-generated! Notouchy!
4+-}
5+
6+import ~~_module_name_~~
7+import Testable.Runner
8+
9+
10+main : Testable.Runner.TestableProgram
11+main =
12+ Testable.Runner.program
13+ [ Testable.Runner.rename "~~_module_name_sentence_~~" ~~_module_name_~~.view
14+ ]
tests-rendering/automation/templates/gather-styles.html+179−99
@@ -1,105 +1,185 @@
1−<!DOCTYPE HTML>
1+<!DOCTYPE html>
2<html>
2<html>
3− <head>
3+ <head>
4− <meta charset="UTF-8">
4+ <meta charset="UTF-8" />
5− <title>Rendering Benchmark Viewer</title>
5+ <title>Elm UI Rendering Test</title>
6− <script>
6+ <script>
7− ${compiled_elm_code}
7+ ~~_compiled_elm_code_~~
8− </script>
8+ </script>
9− </head>
9+ <script>
10− <style>
10+ // Helpers
11− </style>
11+ // Calculations for canvas
12− <body id="root"></body>
12+ var PIXEL_RATIO = (function () {
13−
13+ var ctx = document.createElement("canvas").getContext("2d"),
14− <script type="text/javascript">
14+ dpr = window.devicePixelRatio || 1,
15−
15+ bsr =
16− var app = Elm.Tests.Run.init();
16+ ctx.webkitBackingStorePixelRatio ||
17−
17+ ctx.mozBackingStorePixelRatio ||
18− var test_results = "waiting.."
18+ ctx.msBackingStorePixelRatio ||
19−
19+ ctx.oBackingStorePixelRatio ||
20− app.ports.report.subscribe(function (results) {
20+ ctx.backingStorePixelRatio ||
21− test_results = results;
21+ 1;
22+
23+ return dpr / bsr;
22− })
24+ })();
23−
25+
24− app.ports.analyze.subscribe(function (ids) {
26+ function createHiDPICanvas(w, h, id, ratio) {
25− // ids : List String
27+ if (!ratio) {
26− var idsLength = ids.length;
28+ ratio = PIXEL_RATIO;
27− var results = [];
28− for (var i = 0; i < idsLength; i++) {
29− var id = ids[i];
30− var element = document.getElementById(id);
31− if (element == null) {
32− console.log("id " + id + " not found");
33− }
34− var style = getStyle(element);
35− var bbox = getBoundingBox(element);
36−
37− var visible = isVisible(id, bbox);
38−
39−
40− var result = { "bbox": bbox, "style": style, "id": id, "isVisible": visible };
41− results.push(result);
42−
43− }
44− app.ports.styles.send(results);
45− });
46−
47− function isVisible(id, bbox) {
48− var current = document.getElementById(id);
49− var result = 0;
50− if (current == document.elementFromPoint(bbox['left'], bbox['top'])) {
51− result++;
52− }
53− if (current == document.elementFromPoint(bbox['left'], bbox['bottom'] - 1)) {
54− result++;
55− }
56− if (current == document.elementFromPoint(bbox['right'] - 1, bbox['top'])) {
57− result++;
58− }
59− if (current == document.elementFromPoint(bbox['right'] - 1, bbox['bottom'] - 1)) {
60− result++;
61− }
62−
63−
64− if (result == 4) {
65− return true;
66− } else {
67− return false
68− }
69 }
29 }
70−
30+ let can = document.getElementById(id);
71− function getStyle(element) {
31+ can.width = w * ratio;
72− var props = []
32+ can.height = h * ratio;
73− var style = window.getComputedStyle(element);
33+ can.style.width = w + "px";
74− for (var i = style.length; i--;) {
34+ can.style.height = h + "px";
75− var name = style.item(i);
35+ can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
76− var value = style.getPropertyValue(name);
77− props.push([name, value]);
78− }
79− return props;
36+ return can;
37+ }
38+
39+ function isVisible(id, bbox) {
40+ var current = document.getElementById(id);
41+ var result = 0;
42+ if (current == document.elementFromPoint(bbox["left"], bbox["top"])) {
43+ result++;
80 }
44 }
81−
45+ if (
82− function getBoundingBox(element) {
46+ current == document.elementFromPoint(bbox["left"], bbox["bottom"] - 1)
83− var bbox = element.getBoundingClientRect();
47+ ) {
84− var style = window.getComputedStyle(element, null);
48+ result++;
85−
49+ }
86− var padding = {
50+ if (
87− 'top': parseFloat(style.getPropertyValue('padding-top'))
51+ current == document.elementFromPoint(bbox["right"] - 1, bbox["top"])
88− , 'bottom': parseFloat(style.getPropertyValue('padding-bottom'))
52+ ) {
89− , 'left': parseFloat(style.getPropertyValue('padding-left'))
53+ result++;
90− , 'right': parseFloat(style.getPropertyValue('padding-right'))
54+ }
91− };
55+ if (
92−
56+ current ==
93− return {
57+ document.elementFromPoint(bbox["right"] - 1, bbox["bottom"] - 1)
94− 'top': bbox.top
58+ ) {
95− , 'bottom': bbox.bottom
59+ result++;
96− , 'left': bbox.left
97− , 'right': bbox.right
98− , 'width': bbox.width
99− , 'height': bbox.height
100− , 'padding': padding
101− }
102 }
60 }
103
61
62+ if (result == 4) {
63+ return true;
64+ } else {
65+ return false;
66+ }
67+ }
68+
69+ function getStyle(element) {
70+ var props = [];
71+ var style = window.getComputedStyle(element);
72+ for (var i = style.length; i--; ) {
73+ var name = style.item(i);
74+ var value = style.getPropertyValue(name);
75+ props.push([name, value]);
76+ }
77+ return props;
78+ }
79+
80+ function getBoundingBox(element) {
81+ var bbox = element.getBoundingClientRect();
82+ var style = window.getComputedStyle(element, null);
83+
84+ var padding = {
85+ top: parseFloat(style.getPropertyValue("padding-top")),
86+ bottom: parseFloat(style.getPropertyValue("padding-bottom")),
87+ left: parseFloat(style.getPropertyValue("padding-left")),
88+ right: parseFloat(style.getPropertyValue("padding-right")),
89+ };
90+
91+ return {
92+ top: bbox.top,
93+ bottom: bbox.bottom,
94+ left: bbox.left,
95+ right: bbox.right,
96+ width: bbox.width,
97+ height: bbox.height,
98+ padding: padding,
99+ };
100+ }
101+ function getTextMetrics(element) {
102+ // Given our element, we want to return the text metrics on the text within.
103+ // If it is an element we want to measure:
104+ // we want to get the font family
105+ // and the font size.
106+ // We're going to presume that the font is loaded.
107+ let childrenTextMetrics = [];
108+ for (i = 0; i < element.children.length; i++) {
109+ if (element.children[i].classList.contains("t")) {
110+ let style = window.getComputedStyle(element, null);
111+ let fontSize = style.getPropertyValue("font-size");
112+ let fontFamily = style.getPropertyValue("font-family");
113+ let ctx = canvas.getContext("2d");
114+
115+ ctx.font = fontSize + " " + fontFamily; //"16px Roboto";
116+ let text = ctx.measureText(element.children[i].innerText);
117+
118+ childrenTextMetrics.push({
119+ actualBoundingBoxAscent: text.actualBoundingBoxAscent,
120+ actualBoundingBoxDescent: text.actualBoundingBoxDescent,
121+ actualBoundingBoxLeft: text.actualBoundingBoxLeft,
122+ actualBoundingBoxRight: text.actualBoundingBoxRight,
123+ width: text.width,
124+ });
125+ }
126+ }
127+ return childrenTextMetrics;
128+ }
104 </script>
129 </script>
130+ </head>
131+ <style></style>
132+ <body id="root">
133+ <body id="root">
134+ <div id="elm-home"></div>
135+ <canvas id="canvas"></canvas>
136+ </body>
137+ </body>
138+
139+ <script type="text/javascript">
140+ // Starting Elm App,
141+ var app = Elm.~~_module_name_~~.init({
142+ node: document.getElementById("elm-home"),
143+ });
144+
145+ // create a canvas for text measurements
146+ let canvas = createHiDPICanvas(200, 100, "canvas");
147+
148+ var test_results = "waiting..";
149+
150+ app.ports.report.subscribe(function (results) {
151+ test_results = results;
152+ // NOTE - this is the signal to the automation that it is cool to retrieve results now
153+ // So don't remove this like you did before :D
154+ document.title = "tests finished";
155+ });
156+
157+ app.ports.analyze.subscribe(function (ids) {
158+ // ids : List String
159+ var idsLength = ids.length;
160+ var results = [];
161+ for (var i = 0; i < idsLength; i++) {
162+ var id = ids[i];
163+ var element = document.getElementById(id);
164+ if (element == null) {
165+ console.log("id " + id + " not found");
166+ }
167+ var style = getStyle(element);
168+ var bbox = getBoundingBox(element);
169+ var metrics = getTextMetrics(element);
170+
171+ var visible = isVisible(id, bbox);
172+
173+ var result = {
174+ textMetrics: metrics,
175+ bbox: bbox,
176+ style: style,
177+ id: id,
178+ isVisible: visible,
179+ };
180+ results.push(result);
181+ }
182+ app.ports.styles.send(results);
183+ });
184+ </script>
105−</html>
185+</html>
tests-rendering/automation/view.jsnew file+73
@@ -0,0 +1,73 @@
1+const fs = require("fs");
2+const program = require("commander");
3+const path = require("path");
4+const chalk = require("chalk");
5+const childProcess = require("child_process");
6+const build = require("./build.js");
7+const chokidar = require("chokidar");
8+const http = require("http");
9+
10+var filepath = null;
11+var test_filepath = null;
12+program
13+ .option("--all", "run all the tests")
14+ .option("--debug", "run with debug on")
15+ .arguments("<filepath>")
16+ .action(function (p) {
17+ test_filepath = path.join("./tests-rendering/cases/open", p);
18+ })
19+ .parse(process.argv);
20+
21+(async () => {
22+ if (test_filepath && test_filepath == null && !program.run) {
23+ console.log("Open Cases");
24+ console.log("");
25+ fs.readdirSync("./tests-rendering/cases/open").forEach((file) => {
26+ if (file.endsWith("elm")) {
27+ console.log(" " + file);
28+ }
29+ });
30+ console.log("");
31+ return;
32+ }
33+
34+ if (program.all) {
35+ filepath = "./src/Tests/All.elm";
36+ } else {
37+ filepath = "src/Tests/Run.elm";
38+ build.create_runner({
39+ elm: test_filepath,
40+ template: "./tests-rendering/automation/templates/Run.elm",
41+ target: "./tests-rendering/src/Tests/Run.elm",
42+ });
43+ }
44+
45+ //
46+ // Compile and serve
47+
48+ let content = await build.compile_to_string({
49+ template: "./tests-rendering/automation/templates/gather-styles.html",
50+ elm: filepath,
51+ elmOptions: { cwd: "./tests-rendering", debug: program.debug },
52+ });
53+
54+ console.log("Finished compiling");
55+ // console.log(content);
56+
57+ chokidar.watch("./tests-rendering/*.elm").on("all", (event, path) => {
58+ console.log("Changes detected, refresh!");
59+ content = build.compile_to_string({
60+ template: "./tests-rendering/automation/templates/gather-styles.html",
61+ elm: filepath,
62+ elmOptions: { cwd: "./tests-rendering", debug: program.debug },
63+ });
64+ });
65+ console.log("Serving on http://localhost:8080");
66+ http
67+ .createServer(function (req, res) {
68+ res.writeHead(200, { "Content-Type": "text/html" });
69+ res.write(content);
70+ res.end();
71+ })
72+ .listen(8080);
73+})();
cases/Manual/AlignedForm.elm → tests-rendering/cases/Manual/AlignedForm.elmrenamed

The content is the same.

cases/Manual/All.elm → tests-rendering/cases/Manual/All.elmrenamed

The content is the same.

cases/Manual/LazyPerformance.elm → tests-rendering/cases/Manual/LazyPerformance.elmrenamed

The content is the same.

cases/Manual/OtherScrollbars.elm → tests-rendering/cases/Manual/OtherScrollbars.elmrenamed

The content is the same.

cases/Manual/Overflow.elm → tests-rendering/cases/Manual/Overflow.elmrenamed

The content is the same.

cases/Manual/Scrollbars.elm → tests-rendering/cases/Manual/Scrollbars.elmrenamed

The content is the same.

cases/Manual/ScrollbarsPls.elm → tests-rendering/cases/Manual/ScrollbarsPls.elmrenamed

The content is the same.

cases/Manual/TextLayout.elm → tests-rendering/cases/Manual/TextLayout.elmrenamed

The content is the same.

tests-rendering/cases/elm.jsonnew file+23
@@ -0,0 +1,23 @@
1+{
2+ "type": "application",
3+ "source-directories": ["open", "../src/"],
4+ "elm-version": "0.19.1",
5+ "dependencies": {
6+ "direct": {
7+ "elm/browser": "1.0.0",
8+ "elm/core": "1.0.0",
9+ "elm/html": "1.0.0",
10+ "elm/json": "1.0.0",
11+ "elm/virtual-dom": "1.0.0",
12+ "ohanhi/lorem": "1.0.2"
13+ },
14+ "indirect": {
15+ "elm/time": "1.0.0",
16+ "elm/url": "1.0.0"
17+ }
18+ },
19+ "test-dependencies": {
20+ "direct": {},
21+ "indirect": {}
22+ }
23+}
tests-rendering/cases/open/InFrontSize.elmnew file+43
@@ -0,0 +1,43 @@
1+module InFrontSize exposing (view)
2+
3+{-|
4+
5+
6+# inFront elements with width fill are contained within the parent's border
7+
8+<https://github.com/mdgriffith/elm-ui/issues/201>
9+
10+Though they're expected to be the actual size of the parent.
11+
12+-}
13+
14+import Testable.Element exposing (..)
15+import Testable.Element.Background as Background
16+import Testable.Element.Border as Border
17+
18+
19+view =
20+ layout []
21+ (el
22+ [ width (px 100)
23+ , height (px 100)
24+ , centerX
25+ , centerY
26+ , Background.color (rgb 0.1 0.1 0.1)
27+ , Border.width 20
28+ , Border.color (rgb 0.5 1 1)
29+
30+ -- the following element should cover the parent's border
31+ , inFront
32+ (el
33+ [ width fill
34+ , height fill
35+ , Background.color (rgb 0.1 0.5 0.9)
36+
37+ -- , moveUp 40
38+ ]
39+ none
40+ )
41+ ]
42+ none
43+ )
tests-rendering/cases/open/LabelledInputAlignRight.elmnew file+28
@@ -0,0 +1,28 @@
1+module LabelledInputAlignRight exposing (view)
2+
3+import Testable.Element exposing (..)
4+import Testable.Element.Input as Input
5+
6+
7+view =
8+ layout [] <|
9+ column [ width fill ]
10+ [ Input.text
11+ [ width (px 200)
12+ , alignRight
13+ ]
14+ { onChange = always False
15+ , text = ""
16+ , placeholder = Nothing
17+ , label = Input.labelLeft [] (text "label")
18+ }
19+ , Input.text
20+ [ width (px 200)
21+ , alignRight
22+ ]
23+ { onChange = always False
24+ , text = ""
25+ , placeholder = Nothing
26+ , label = Input.labelHidden "label"
27+ }
28+ ]
tests-rendering/cases/open/NearbyElementBordersOverlap.elmnew file+46
@@ -0,0 +1,46 @@
1+module NearbyElementBordersOverlap exposing (view)
2+
3+{-| Elements placed with onLeft, onRight, above & below overlap main element's border
4+
5+
6+# inFront elements with width fill are contained within the parent's border
7+
8+<https://github.com/mdgriffith/elm-ui/issues/223>
9+
10+In addition to the overlap, it affects layout in unexpected ways: eg if the elements
11+are the same height, onLeft and onRight elements will be vertically offset
12+from the main element.
13+
14+-}
15+
16+import Testable.Element exposing (..)
17+import Testable.Element.Border as Border
18+
19+
20+blue =
21+ rgb 0 0 1
22+
23+
24+green =
25+ rgb 0 1 0
26+
27+
28+grey =
29+ rgb 0.5 0.5 0.5
30+
31+
32+view =
33+ layout [ width fill, height fill ] <|
34+ el
35+ [ centerX
36+ , centerY
37+ , Border.width 10
38+ , Border.color green
39+ , padding 50
40+ , above <| el [ Border.color grey, Border.width 10 ] <| text "above"
41+ , below <| el [ Border.color grey, Border.width 10 ] <| text "below"
42+ , onRight <| el [ Border.color blue, Border.width 10 ] <| text "onRight"
43+ , onLeft <| el [ Border.color blue, Border.width 10 ] <| text "onLeft"
44+ ]
45+ <|
46+ text "Centre"
cases/open/Scrollbar2.elm → tests-rendering/cases/open/Scrollbar2.elmrenamed+1−1
@@ -1,4 +1,4 @@
1−module Main exposing (main, myElement, sidebar)
1+module Scrollbar2 exposing (main, myElement, sidebar)
2
2
3import Element exposing (Element, alignRight, centerY, column, el, fill, height, padding, px, rgb255, row, spacing, text, width)
3import Element exposing (Element, alignRight, centerY, column, el, fill, height, padding, px, rgb255, row, spacing, text, width)
4import Element.Background as Background
4import Element.Background as Background
cases/open/Scrollbars.elm → tests-rendering/cases/open/Scrollbars.elmrenamed+1−1
@@ -1,4 +1,4 @@
1−module Main exposing (main)
1+module Scrollbars exposing (main)
2
2
3import Element exposing (..)
3import Element exposing (..)
4import Element.Background as Background
4import Element.Background as Background
tests-rendering/cases/open/StackedScrollingColumnsHeight.elmnew file+42
@@ -0,0 +1,42 @@
1+module StackedScrollingColumnsHeight exposing (..)
2+
3+{-|
4+
5+
6+# Column height can be set incorrectly for two stacked columns with scrollbarY
7+
8+<https://github.com/mdgriffith/elm-ui/issues/197>
9+
10+Both red and green columns should always have equal height.
11+
12+Currently, the height is set incorrectly if only one column has enough content to scroll.
13+
14+Both columns are correctly set to the same height only if neither has enough content
15+to scroll or if both have enough content to scroll.
16+
17+-}
18+
19+import Testable.Element exposing (..)
20+import Testable.Element.Background as Background
21+
22+
23+view =
24+ layout [ height fill ] <|
25+ column [ height fill ]
26+ [ column
27+ [ height fill
28+ , scrollbarY
29+ , Background.color (rgb 0 255 0)
30+ ]
31+ <|
32+ List.map (\_ -> text "Hello") <|
33+ List.range 1 20
34+ , column
35+ [ height fill
36+ , scrollbarY
37+ , Background.color (rgb 255 0 0)
38+ ]
39+ <|
40+ List.map (\_ -> text "Hello") <|
41+ List.range 1 3
42+ ]
tests-rendering/cases/open/WeirdCentering.elmnew file+35
@@ -0,0 +1,35 @@
1+module WeirdCentering exposing (view)
2+
3+import Testable.Element exposing (..)
4+import Testable.Element.Background as Background
5+
6+
7+view =
8+ layout [] <|
9+ column []
10+ [ text "Example with centerX:"
11+ , row
12+ [ width fill ]
13+ [ row
14+ [ centerX
15+ , Background.color <| rgb 0 1 0
16+ ]
17+ [ paragraph
18+ [ width shrink
19+ ]
20+ [ text "Hello world" ]
21+ , paragraph
22+ [ width shrink
23+ ]
24+ [ text "Hello world" ]
25+ ]
26+ ]
27+ , text "Example without centerX:"
28+ , row
29+ [ width fill ]
30+ [ row [ Background.color <| rgb 0 1 0 ]
31+ [ paragraph [ width shrink ] [ text "Hello world" ]
32+ , paragraph [ width shrink ] [ text "Hello world" ]
33+ ]
34+ ]
35+ ]
cases/resolved/Adjusted.elm → tests-rendering/cases/resolved/Adjusted.elmrenamed

The content is the same.

cases/resolved/BehindContent.elm → tests-rendering/cases/resolved/BehindContent.elmrenamed

The content is the same.

tests-rendering/cases/resolved/ClippedElInFixedWidthColumn.elmnew file+31
@@ -0,0 +1,31 @@
1+module ClippedElInFixedWidthColumn exposing (..)
2+
3+{-| el [ clip ] inside a fixed-width column rendered with 0px height in Chrome & Firefox
4+
5+<https://github.com/mdgriffith/elm-ui/issues/191>
6+
7+If an el [ clip ] is placed inside a fixed-width column which is inside a layout
8+with [ width fill, height fill ], then the element is rendered with 0px height in Chrome:
9+
10+layout [ width fill, height fill ] <|
11+column [ width <| px 300 ][ el [ clip ] <| text "The quick brown fox" ]
12+
13+In Firefox, the element is initially rendered correctly, but is resized to 0px height
14+on viewport resize. In Safari, the element is displayed correctly.
15+
16+The above seems to be the minimal reproduction:
17+
18+Removing attributes from layout or from column or from el makes the issue disappear.
19+Replacing column [ width <| px 300 ] with column [ width fill ] makes the issue disappear.
20+Replacing column with el also makes the issue disappear.
21+Adding htmlAttribute <| Attr.style "flex-basis" "auto" to clipped el is a workaround.
22+
23+-}
24+
25+import Testable.Element exposing (..)
26+
27+
28+view =
29+ layout [ width fill, height fill ] <|
30+ column [ width <| px 300 ]
31+ [ el [ clip ] <| text "The quick brown fox" ]
cases/resolved/ColumnGrowth.elm → tests-rendering/cases/resolved/ColumnGrowth.elmrenamed

The content is the same.

tests-rendering/cases/resolved/ElInFixedHeightColumn.elmnew file+41
@@ -0,0 +1,41 @@
1+module ElInFixedHeightColumn exposing (view)
2+
3+{-| el inside a fixed height column is rendered with zero height in Safari
4+
5+<https://github.com/mdgriffith/elm-ui/issues/190>
6+
7+If I put an el into a column of fixed height, the el is rendered with zero height
8+in Safari:
9+
10+ layout [] <|
11+ column
12+ [ width fill
13+ , height <| px 200
14+ ]
15+ [ el [ Border.width 3, Border.color <| rgb255 0 0 0 ] <| text "an element" ]
16+
17+Adding htmlAttribute <| Attr.style "flex-basis" "auto" to the el is a workaround
18+that fixes the problem in this instance.
19+
20+Expected behavior
21+
22+Element should be rendered the same as in Firefox & Chrome, with the border going
23+around the text.
24+
25+-}
26+
27+import Testable.Element exposing (..)
28+import Testable.Element.Border as Border
29+
30+
31+view =
32+ layout [] <|
33+ column
34+ [ width fill
35+ , height <| px 200
36+ ]
37+ [ el
38+ []
39+ <|
40+ text "an element"
41+ ]
cases/resolved/FocusStyles.elm → tests-rendering/cases/resolved/FocusStyles.elmrenamed

The content is the same.

cases/resolved/HugePlaceholder.elm → tests-rendering/cases/resolved/HugePlaceholder.elmrenamed

The content is the same.

tests-rendering/cases/resolved/Images/ImageRegression.elmnew file+63
@@ -0,0 +1,63 @@
1+module ImageRegression exposing (main)
2+
3+{-| <https://github.com/mdgriffith/elm-ui/issues/232>
4+-}
5+
6+import Browser
7+import Element exposing (Element, centerX, clipX, el, image, px, rgb255, row, shrink, width)
8+import Element.Background as Background
9+import Html exposing (Html, button, div, text)
10+import Html.Events exposing (onClick)
11+
12+
13+type alias Model =
14+ Int
15+
16+
17+type Msg
18+ = NoOp
19+
20+
21+update : Msg -> Model -> Model
22+update msg model =
23+ model
24+
25+
26+view : Model -> Html Msg
27+view model =
28+ Element.layout [] <|
29+ row
30+ [ centerX
31+ , width shrink
32+ , Background.color <| rgb255 255 255 255
33+ ]
34+ [ viewCard "qs" True
35+ , viewCard "ah" False
36+ ]
37+
38+
39+main : Program () Model Msg
40+main =
41+ Browser.sandbox
42+ { init = 0
43+ , view = view
44+ , update = update
45+ }
46+
47+
48+viewCard : String -> Bool -> Element Msg
49+viewCard card bool =
50+ let
51+ source =
52+ "https://hearts-bawolk.herokuapp.com/static/cards/" ++ card ++ ".png"
53+
54+ overlapStyles =
55+ if bool then
56+ [ width (px 50)
57+ , clipX
58+ ]
59+
60+ else
61+ []
62+ in
63+ el overlapStyles (image [] { src = source, description = "" })
tests-rendering/cases/resolved/Images/ImageRegression2.elmnew file+19
@@ -0,0 +1,19 @@
1+module ImageRegression2 exposing (main)
2+
3+{-| <https://github.com/mdgriffith/elm-ui/issues/228>
4+-}
5+
6+import Element exposing (..)
7+
8+
9+main =
10+ Element.layout []
11+ (Element.row [ width fill, height fill, explain Debug.todo ]
12+ [ text "BEFORE"
13+ , image []
14+ { src = "https://placekitten.com/300/200"
15+ , description = "kitten"
16+ }
17+ , text "AFTER"
18+ ]
19+ )
tests-rendering/cases/resolved/Images/ImageSizing.elmnew file+24
@@ -0,0 +1,24 @@
1+module ImageSizing exposing (main)
2+
3+{-| <https://github.com/mdgriffith/elm-ui/issues/62>
4+-}
5+
6+import Element as E exposing (column, el, px, row, text)
7+import Element.Border as Border
8+import Element.Font as Font
9+import Html exposing (Html)
10+
11+
12+main : Html ()
13+main =
14+ E.layout [ E.height E.fill, Border.width 5, E.explain Debug.todo ] <|
15+ E.image
16+ [ -- E.height (E.px 500)
17+ -- ,
18+ -- E.width E.fill
19+ E.height E.fill
20+ ]
21+ -- <| px 500 ]
22+ { src = "http://bburdette.github.io/music-reader.png"
23+ , description = "music-reader screenshot"
24+ }
tests-rendering/cases/resolved/Input/FocusLinks.elmnew file+37
@@ -0,0 +1,37 @@
1+module FocusLinks exposing (main)
2+
3+import Element exposing (Color, Element, el, focused, layout, link, row, spacing, text)
4+import Element.Border as Border
5+import Element.Region as Region
6+import Html exposing (Html)
7+
8+
9+main : Html msg
10+main =
11+ layout [] links
12+
13+
14+links : Element msg
15+links =
16+ row [ spacing 10 ]
17+ [ siteLink "Elm website" "https://elm-lang.org/"
18+ , siteLink "Elm-ui" "https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/Element"
19+ ]
20+
21+
22+siteLink : String -> String -> Element msg
23+siteLink label url =
24+ link [ focused [ Border.glow blue 2 ] ]
25+ { url = url
26+ , label = text label
27+ }
28+
29+
30+blue : Color
31+blue =
32+ Element.fromRgb255
33+ { red = 100
34+ , green = 180
35+ , blue = 250
36+ , alpha = 1
37+ }
tests-rendering/cases/resolved/Input/FocusStylePrecedingElements.elmnew file+38
@@ -0,0 +1,38 @@
1+module FocusStylePrecedingElements exposing (main)
2+
3+{-|
4+
5+
6+# `focused` style applied to an element when any element with a lower tab index is focused
7+
8+<https://github.com/mdgriffith/elm-ui/issues/198>
9+
10+When I add a focused attribute, the styles therein are applied not only when
11+the element is focused, but also when any element with a lower tab index is focused.
12+This behaviour is not browser-specific.
13+
14+To reproduce, tab through to the buttons in the Ellie below. As soon as the first
15+button has focus, the third button (which is the only one with a focused attribute)
16+is highlighted as well.
17+
18+I narrowed down the reason for this behaviour to this CSS selector:
19+.s:focus ~ .fc-250-50-50-255-fs:not(.focus)
20+
21+Disabling it produces the correct focus behaviour.
22+
23+-}
24+
25+import Element exposing (..)
26+import Element.Font as Font
27+import Element.Input as Input
28+
29+
30+main =
31+ layout [] <|
32+ column []
33+ [ Input.button [] { onPress = Nothing, label = text "Button" }
34+ , Input.button [] { onPress = Nothing, label = text "Button" }
35+ , Input.button [ focused [ Font.color <| rgb255 250 50 50 ] ] { onPress = Nothing, label = text "Button" }
36+ , Input.button [] { onPress = Nothing, label = text "Button" }
37+ , Input.button [] { onPress = Nothing, label = text "Button" }
38+ ]
tests-rendering/cases/resolved/Input/InputAlignment.elmnew file+44
@@ -0,0 +1,44 @@
1+module InputAlignment exposing (main)
2+
3+import Browser
4+import Element exposing (fill, padding, width)
5+import Element.Border as Border
6+import Element.Font as Font
7+import Element.Input as Input
8+import Html exposing (Html)
9+
10+
11+view : () -> Html ()
12+view () =
13+ Element.layout [ Font.alignRight ] <|
14+ Element.column [ padding 10 ]
15+ [ Element.el [ Border.width 1, padding 5, width fill ] <| Element.text "Element.el"
16+ , Input.text
17+ [ Border.width 1
18+ , padding 5
19+ , width fill
20+ , Border.rounded 0
21+ , Font.color (Element.rgb 0 0.5 0.5)
22+ ]
23+ { onChange = always ()
24+ , text = "Input.text"
25+ , placeholder = Nothing
26+ , label = Input.labelHidden "Input.text"
27+ }
28+ , Element.paragraph []
29+ [ Element.text "Helooooo!" ]
30+ , Element.row []
31+ [ Element.text "one!"
32+ , Element.text "two!"
33+ , Element.text "three!"
34+ ]
35+ ]
36+
37+
38+main : Program () () ()
39+main =
40+ Browser.sandbox
41+ { init = ()
42+ , view = view
43+ , update = \_ _ -> ()
44+ }
tests-rendering/cases/resolved/Input/LabelAlignBaseline.elmnew file+34
@@ -0,0 +1,34 @@
1+module LabelAlignBaseline exposing (..)
2+
3+import Element exposing (Element, alignRight, centerY, el, fill, padding, rgb255, row, spacing, text, width)
4+import Element.Background as Background
5+import Element.Border as Border
6+import Element.Font as Font
7+import Element.Input as Input
8+
9+
10+type Msg
11+ = Msg
12+
13+
14+main =
15+ Element.layout [] <|
16+ Element.column []
17+ [ Element.row []
18+ [ Element.text "dissociated label"
19+ , Input.text []
20+ { text = "input"
21+ , label = Input.labelHidden "hidden"
22+ , onChange = \_ -> Msg
23+ , placeholder = Nothing
24+ }
25+ ]
26+ , Element.row []
27+ [ Input.text []
28+ { text = "input"
29+ , label = Input.labelLeft [] <| Element.text "associated label"
30+ , onChange = \_ -> Msg
31+ , placeholder = Nothing
32+ }
33+ ]
34+ ]
tests-rendering/cases/resolved/Input/Placeholder.elmnew file+59
@@ -0,0 +1,59 @@
1+module Placeholder exposing (main)
2+
3+import Element exposing (..)
4+import Element.Background as Background
5+import Element.Input as Input
6+import Element.Keyed as Keyed
7+import Html.Attributes exposing (style)
8+
9+
10+grey =
11+ rgb 0.8 0.8 0.8
12+
13+
14+darkGrey =
15+ rgb 0.7 0.7 0.7
16+
17+
18+type Msg
19+ = NoMsg String
20+
21+
22+main =
23+ Element.layout [ width fill, height fill ] <|
24+ column
25+ [ width fill
26+ , inFront
27+ (el
28+ [ width fill
29+ , Background.color grey
30+ , padding 20
31+ , below <|
32+ el
33+ [ height (px 300)
34+ , width (px 300)
35+ , Background.color darkGrey
36+ , htmlAttribute <| style "z-index" "1"
37+ , moveRight 50
38+ , padding 50
39+ , alpha 0.5
40+ ]
41+ (el [ alignBottom ] (text "I'm a dropdown menu."))
42+ ]
43+ (text "I'm a toolbar.")
44+ )
45+ ]
46+ [ Input.text []
47+ { onChange = NoMsg
48+ , text = ""
49+ , placeholder = Just (Input.placeholder [] (text "I'm a text input."))
50+ , label = Input.labelAbove [] none
51+ }
52+ , Input.multiline []
53+ { onChange = NoMsg
54+ , text = ""
55+ , placeholder = Just (Input.placeholder [] (text "I'm a multiline input."))
56+ , label = Input.labelAbove [] none
57+ , spellcheck = False
58+ }
59+ ]
tests-rendering/cases/resolved/Input/Slider.elmnew file+74
@@ -0,0 +1,74 @@
1+module Slider exposing (main)
2+
3+import Browser
4+import Element as Element exposing (Element)
5+import Element.Background as Background
6+import Element.Input as Input
7+import Html exposing (Html, button, div, text)
8+import Html.Events exposing (onClick)
9+
10+
11+type alias Model =
12+ { rangeValue : Float }
13+
14+
15+initialModel : Model
16+initialModel =
17+ { rangeValue = 160.0 }
18+
19+
20+type Msg
21+ = UpdateRange Float
22+
23+
24+update : Msg -> Model -> Model
25+update msg model =
26+ case msg of
27+ UpdateRange value ->
28+ { model | rangeValue = value }
29+
30+
31+viewModal =
32+ Element.el
33+ [ Element.width Element.fill
34+ , Element.height Element.fill
35+ , Element.paddingEach { top = 100, left = 0, bottom = 0, right = 0 }
36+ , Background.color <| Element.rgba 0 0 0 0.8
37+ ]
38+ (Element.el [] (Element.text "Hello Modal"))
39+
40+
41+view : Model -> Html Msg
42+view model =
43+ Element.layout
44+ [--Element.inFront viewModal
45+ ]
46+ <|
47+ Input.slider
48+ [ Element.width Element.fill
49+ , Element.behindContent <|
50+ Element.el
51+ [ Element.width Element.fill
52+ , Element.height (Element.px 2)
53+ , Element.centerY
54+ , Background.color (Element.rgb 0.5 0.5 0.5)
55+ ]
56+ Element.none
57+ ]
58+ { onChange = UpdateRange
59+ , label = Input.labelHidden ""
60+ , min = 60.0
61+ , max = 280.0
62+ , value = model.rangeValue
63+ , thumb = Input.defaultThumb
64+ , step = Just 1.0
65+ }
66+
67+
68+main : Program () Model Msg
69+main =
70+ Browser.sandbox
71+ { init = initialModel
72+ , view = view
73+ , update = update
74+ }
tests-rendering/cases/resolved/Layout/BreaksWithinParagraph.elmnew file+17
@@ -0,0 +1,17 @@
1+module BreaksWithinParagraph exposing (main)
2+
3+import Browser
4+import Element exposing (..)
5+import Element.Background as Background
6+import Element.Border as Border
7+import Element.Font as Font
8+
9+
10+main =
11+ layout [ width fill, height fill ] <|
12+ paragraph [ Border.width 1, width (px 50) ]
13+ [ row []
14+ [ text "Thequick"
15+ ]
16+ , text "redfox"
17+ ]
tests-rendering/cases/resolved/Layout/ColumnLayouts.elmnew file+103
@@ -0,0 +1,103 @@
1+module ColumnLayouts exposing (main)
2+
3+{-| -}
4+
5+import Element exposing (..)
6+import Element.Background as Background
7+import Element.Border as Border
8+import Element.Font as Font
9+
10+
11+main =
12+ layout [] <|
13+ column [ spacing 48 ]
14+ [ column
15+ [ width fill
16+ , height <| px 200
17+ , spacing 24
18+ , Border.width 1
19+ , Border.color pink
20+ ]
21+ [ elem
22+ , elem
23+ , elem
24+ ]
25+ , column
26+ [ width fill
27+ , spacing 24
28+ , Border.width 1
29+ , Border.color pink
30+ ]
31+ [ elem
32+ , elem
33+ , elem
34+ ]
35+ , el [ height <| px 400 ] <|
36+ column [ height fill ]
37+ [ column
38+ [ width fill
39+ , height <| px 200
40+ , spacing 24
41+ , Border.width 1
42+ , Border.color pink
43+ ]
44+ [ elem
45+ , elem
46+ , el [ alignBottom ] elem
47+ ]
48+ , el [ alignBottom ] elem
49+ ]
50+ , column
51+ [ width fill
52+ , height <| px 200
53+ , spacing 24
54+ , Border.width 1
55+ , Border.color pink
56+ ]
57+ [ elem
58+ , el [] box
59+ , el [ alignBottom ] elem
60+ ]
61+ , column
62+ [ width fill
63+ , height <| px 200
64+ , spacing 24
65+ , Border.width 1
66+ , Border.color pink
67+ ]
68+ [ elem
69+ , el [] box
70+ , filled
71+ , el [ alignBottom ] elem
72+ ]
73+ ]
74+
75+
76+filled =
77+ el
78+ [ width fill
79+ , height fill
80+ , Background.color pink
81+ ]
82+ (text "an element")
83+
84+
85+elem =
86+ el
87+ [ Background.color pink
88+ ]
89+ (text "an element")
90+
91+
92+box =
93+ el
94+ [ width (px 50)
95+ , height (px 50)
96+ , Background.color (rgb (240 / 255) 0 (245 / 255))
97+ , Font.color (rgb 1 1 1)
98+ ]
99+ none
100+
101+
102+pink =
103+ rgb (240 / 255) 0 (245 / 255)
tests-rendering/cases/resolved/Layout/Columns/DistributeHeight.elmnew file+46
@@ -0,0 +1,46 @@
1+module DistributeHeight exposing (main)
2+
3+import Browser
4+import Element exposing (column, el, fill, fillPortion, height, minimum, none, px, rgb, row, text, width)
5+import Element.Background as Bg
6+import Html as Html exposing (Html)
7+
8+
9+type alias Model =
10+ ()
11+
12+
13+update : msg -> Model -> Model
14+update _ model =
15+ model
16+
17+
18+view : model -> Html msg
19+view _ =
20+ Element.layout [ height fill, width fill ] <|
21+ column [ height fill, width fill ]
22+ [ row
23+ [ height
24+ fill
25+ , width fill
26+ , Bg.color <| rgb 1 0 0
27+ ]
28+ [ el
29+ [ height <| px 200
30+ , width fill
31+ , Bg.color <| rgb 1 0 1
32+ ]
33+ (text "Helloooo!")
34+ ]
35+ , row [ height fill, width fill, Bg.color <| rgb 0 1 0 ]
36+ [ none ]
37+ ]
38+
39+
40+main : Program () Model msg
41+main =
42+ Browser.sandbox
43+ { init = ()
44+ , view = view
45+ , update = update
46+ }
tests-rendering/cases/resolved/Layout/Columns/DistributeHeightCorrectly2.elmnew file+86
@@ -0,0 +1,86 @@
1+module Other exposing (main)
2+
3+import Browser
4+import Element exposing (..)
5+import Element.Border as Border
6+import Element.Events as Events
7+import Html exposing (Html)
8+import Html.Attributes
9+
10+
11+type alias Model =
12+ { count : Int }
13+
14+
15+initialModel : Model
16+initialModel =
17+ { count = 0 }
18+
19+
20+type Msg
21+ = Increment
22+ | Decrement
23+
24+
25+update : Msg -> Model -> Model
26+update msg model =
27+ case msg of
28+ Increment ->
29+ { model | count = model.count + 1 }
30+
31+ Decrement ->
32+ { model | count = model.count - 1 }
33+
34+
35+view : Model -> Html Msg
36+view model =
37+ viewElements model
38+ |> layout []
39+
40+
41+viewElements : Model -> Element Msg
42+viewElements model =
43+ el [ height fill ] <|
44+ column [ height fill ]
45+ [ el
46+ [ height (fillPortion 1)
47+ ]
48+ <|
49+ viewCounter model
50+ , el
51+ [ height (fillPortion 2)
52+ ]
53+ <|
54+ viewCounters model
55+ ]
56+
57+
58+flexBasisZero : Element.Attribute msg
59+flexBasisZero =
60+ htmlAttribute (Html.Attributes.style "flex-basis" "0")
61+
62+
63+viewCounters : Model -> Element Msg
64+viewCounters model =
65+ column [ height fill ]
66+ (List.repeat model.count model
67+ |> List.map viewCounter
68+ )
69+
70+
71+viewCounter : Model -> Element Msg
72+viewCounter model =
73+ column [ width fill, height fill, Border.width 1, Border.dotted ]
74+ [ el [ Events.onClick Increment, Border.width 1, Border.rounded 3 ] <| text "+1"
75+ , el [] <| text <| String.fromInt model.count
76+ , el [ Events.onClick Decrement, Border.width 1, Border.rounded 3 ] <| text "-1"
77+ ]
78+
79+
80+main : Program () Model Msg
81+main =
82+ Browser.sandbox
83+ { init = initialModel
84+ , view = view
85+ , update = update
86+ }
tests-rendering/cases/resolved/Layout/Columns/DoNotCollapseBelow0.elmnew file+48
@@ -0,0 +1,48 @@
1+module DoNotCollapseBelow0 exposing (main)
2+
3+{-|
4+
5+
6+# Safari bug for `el` heigh calculation in combination with `height fill`
7+
8+The problem appears to be that the height of the first el is 0, and therefore the second
9+text is rendered on the first.
10+
11+Versions
12+
13+OS: macOS Mojave 10.14.5
14+Browser: Safari
15+Browser Version: 12.1.1 (14607.2.6.1.1)
16+Elm Version: 0.19
17+Elm UI Version: 1.1.5
18+
19+<https://github.com/mdgriffith/elm-ui/issues/147>
20+
21+I was able to pinpoint the issue using git bisect to this fd08f1a commit, which lines up.
22+
23+<https://github.com/mdgriffith/elm-ui/commit/fd08f1a953484ba96f2d05037d4f208eab351514>
24+
25+-}
26+
27+import Browser
28+import Element exposing (column, el, fill, height, layout, scrollbarY, text)
29+import Html exposing (Html)
30+
31+
32+view : () -> Html ()
33+view () =
34+ layout [ height fill ] <|
35+ column
36+ [ height fill ]
37+ [ el [] <| text "Element that Safari gives height 0, if inside an el and not just text."
38+ , text "Text below the el above"
39+ ]
40+
41+
42+main : Program () () ()
43+main =
44+ Browser.sandbox
45+ { init = ()
46+ , view = view
47+ , update = \() () -> ()
48+ }
tests-rendering/cases/resolved/Layout/Columns/SafariTwo.elmnew file+40
@@ -0,0 +1,40 @@
1+module SafariTwo exposing (main)
2+
3+{-| el inside a fixed height column is rendered with zero height in Safari
4+
5+<https://github.com/mdgriffith/elm-ui/issues/190>
6+
7+If I put an el into a column of fixed height, the el is rendered with zero height
8+in Safari:
9+
10+ layout [] <|
11+ column
12+ [ width fill
13+ , height <| px 200
14+ ]
15+ [ el [ Border.width 3, Border.color <| rgb255 0 0 0 ] <| text "an element" ]
16+
17+Adding htmlAttribute <| Attr.style "flex-basis" "auto" to the el is a workaround
18+that fixes the problem in this instance.
19+
20+Expected behavior
21+
22+Element should be rendered the same as in Firefox & Chrome, with the border going
23+around the text.
24+
25+-}
26+
27+import Element exposing (..)
28+
29+
30+main =
31+ layout [] <|
32+ column
33+ [ width fill
34+ , height <| px 200
35+ ]
36+ [ el
37+ []
38+ <|
39+ text "an element"
40+ ]
tests-rendering/cases/resolved/Layout/NestedParagraphs.elmnew file+165
@@ -0,0 +1,165 @@
1+module Layout.NestedParagraphs exposing (view)
2+
3+{-| Paragraph with children more than one level deep breaks flow
4+
5+<https://github.com/mdgriffith/elm-ui/issues/173>
6+
7+First of all, thanks for such a pleasant library to use! It's been awesome not having to use CSS.
8+
9+The problem I ran into is that paragraph only seems correctly flow text in children a single level deep. As an example, I made an Ellie demonstrating this with an el within a link within a paragraph:
10+
11+<https://ellie-app.com/7JcgLRNprF7a1>
12+
13+This might be a regression, as the resolved #50 and #103 seem to refer to the same problem.
14+
15+Expected behavior
16+Text should flow with no breaks in the paragraph.
17+
18+Versions
19+
20+ OS: macOS
21+ Browser: Firefox
22+ Browser Version: 71.0
23+ Elm Version: 0.19.1
24+ Elm UI Version: 1.1.5
25+
26+-}
27+
28+import Testable.Element as Element exposing (..)
29+import Testable.Element.Font as Font
30+import Testable.Generator
31+
32+
33+view =
34+ layout [] <|
35+ column [ spacing 48 ]
36+ [ paragraph []
37+ [ text "This is a paragraph with a "
38+ , el [ Font.bold ] <| text "child"
39+ , text " of depth one."
40+ ]
41+ , paragraph []
42+ [ text "This is a paragraph with a "
43+ , link []
44+ { label =
45+ el [ Font.bold ]
46+ (text Testable.Generator.lorem)
47+ , url = "http://example.com"
48+ }
49+ , text " of depth two."
50+ ]
51+ , el [ Font.size 32 ] (text "Many nested els")
52+ , paragraph []
53+ [ text "This is a paragraph with a "
54+ , link []
55+ { label =
56+ el [ Font.bold ]
57+ (text Testable.Generator.lorem)
58+ , url = "http://example.com"
59+ }
60+ , text " of depth two."
61+ , el [] (el [] (el [] (text Testable.Generator.lorem)))
62+ , el [] (el [] (el [] (el [] (text Testable.Generator.lorem))))
63+ ]
64+ , el [ Font.size 32 ] (text "NestedParas in els")
65+ , paragraph []
66+ [ text "This is a paragraph with a "
67+ , link []
68+ { label =
69+ el [ Font.bold ]
70+ (text Testable.Generator.lorem)
71+ , url = "http://example.com"
72+ }
73+ , text " of depth two."
74+ , el [] (el [] (el [] (text Testable.Generator.lorem)))
75+ , el []
76+ (paragraph []
77+ [ el []
78+ (el [] (text Testable.Generator.lorem))
79+ ]
80+ )
81+ ]
82+ , el [ Font.size 32 ] (text "Nested with link")
83+ , paragraph []
84+ [ text "This is a paragraph with a "
85+ , link []
86+ { label =
87+ el [ Font.bold ] <|
88+ text "child"
89+ , url = "http://example.com"
90+ }
91+ , text " of depth two."
92+ ]
93+ , el [ Font.size 32 ] (text "Para with boxes")
94+ , paragraph []
95+ [ text Testable.Generator.short
96+ , Testable.Generator.box
97+ , text Testable.Generator.short
98+
99+ -- , text Testable.Generator.lorem
100+ , Testable.Generator.box
101+ ]
102+ , el [ Font.size 32 ] (text "Row with boxes")
103+ , paragraph []
104+ [ text Testable.Generator.short
105+ , row [ spacing 25 ]
106+ [ Testable.Generator.box
107+ , text Testable.Generator.short
108+ , Testable.Generator.box
109+
110+ -- , text Testable.Generator.lorem
111+ ]
112+ , text Testable.Generator.short
113+
114+ -- , text Testable.Generator.lorem
115+ , Testable.Generator.box
116+ , paragraph []
117+ [ text "This is a paragraph with a "
118+ , el [ Font.bold ] <| text "child"
119+ , text " of depth one."
120+ ]
121+ ]
122+ , el [ Font.size 32 ] (text "Row with boxes and lots of content")
123+ , paragraph []
124+ [ column [ alignLeft ]
125+ [ Testable.Generator.box, Testable.Generator.box, Testable.Generator.box ]
126+ , text Testable.Generator.short
127+ , row [ spacing 25 ]
128+ [ Testable.Generator.box
129+ , text Testable.Generator.lorem
130+ , Testable.Generator.box
131+
132+ -- , text Testable.Generator.lorem
133+ ]
134+ , text Testable.Generator.short
135+
136+ -- , text Testable.Generator.lorem
137+ , Testable.Generator.box
138+ , paragraph []
139+ [ text "This is a paragraph with a "
140+ , el [ Font.bold ] <| text "child"
141+ , text " of depth one."
142+ ]
143+ ]
144+ , el [ Font.size 32 ] (text "Row with boxes")
145+ , paragraph []
146+ [ text Testable.Generator.short
147+ , row []
148+ [ Testable.Generator.box
149+
150+ -- , text Testable.Generator.lorem
151+ , Testable.Generator.box
152+
153+ -- , text Testable.Generator.lorem
154+ ]
155+ , text Testable.Generator.short
156+
157+ -- , text Testable.Generator.lorem
158+ , Testable.Generator.box
159+ , paragraph []
160+ [ text "This is a paragraph with a "
161+ , el [ Font.bold ] <| text "child"
162+ , text " of depth one."
163+ ]
164+ ]
165+ ]
tests-rendering/cases/resolved/Layout/Rounded.elmnew file+54
@@ -0,0 +1,54 @@
1+module Rounded exposing (main)
2+
3+import Element exposing (..)
4+import Element.Background as Background
5+import Html exposing (Html)
6+
7+
8+sizePadding =
9+ 10
10+
11+
12+sizeSpacing =
13+ 11
14+
15+
16+nbElement =
17+ 10
18+
19+
20+sizeElement =
21+ 50
22+
23+
24+totalSizeElement =
25+ nbElement * sizeElement
26+
27+
28+totalSpacing =
29+ (nbElement - 1) * sizeSpacing
30+
31+
32+totalPadding =
33+ 2 * sizePadding
34+
35+
36+sizeRow =
37+ totalSizeElement + totalSpacing + totalPadding
38+
39+
40+main : Html msg
41+main =
42+ layout [ width fill, height fill ] <|
43+ -- The row should have 4.5px of padding to get
44+ -- from the 5.5px of margin of the elements on the edge
45+ -- up to 10px of padding specified on the wrappedRow.
46+ -- Instead the row has 5px of padding.
47+ wrappedRow [ width (px sizeRow), padding sizePadding, spacing sizeSpacing, Background.color (rgb 0 0 1) ]
48+ <|
49+ List.repeat nbElement <|
50+ -- Each element has a margin of 5.5px (11px of spacing / 2)
51+ el [ width (px sizeElement), height (px sizeElement), Background.color (rgb 1 0 0) ]
52+ <|
53+ el [ centerX, centerY ] <|
54+ text "card"
tests-rendering/cases/resolved/Layout/SafariBugIssue147.elmnew file+37
@@ -0,0 +1,37 @@
1+module Layout.SafariBugIssue147 exposing (view)
2+
3+{-|
4+
5+
6+# Safari bug for `el` heigh calculation in combination with `height fill`
7+
8+The problem appears to be that the height of the first el is 0, and therefore the second
9+text is rendered on the first.
10+
11+Versions
12+
13+OS: macOS Mojave 10.14.5
14+Browser: Safari
15+Browser Version: 12.1.1 (14607.2.6.1.1)
16+Elm Version: 0.19
17+Elm UI Version: 1.1.5
18+
19+<https://github.com/mdgriffith/elm-ui/issues/147>
20+
21+I was able to pinpoint the issue using git bisect to this fd08f1a commit, which lines up.
22+
23+<https://github.com/mdgriffith/elm-ui/commit/fd08f1a953484ba96f2d05037d4f208eab351514>
24+
25+-}
26+
27+import Testable.Element exposing (..)
28+
29+
30+view =
31+ layout [ height fill ] <|
32+ el [ height fill ] <|
33+ column
34+ [ height fill ]
35+ [ el [] <| text "Element that Safari gives height 0, if inside an el and not just text."
36+ , text "Text below the el above"
37+ ]
tests-rendering/cases/resolved/Layout/WrapRow.elmnew file+13
@@ -0,0 +1,13 @@
1+module WrapRow exposing (main)
2+
3+import Browser
4+import Element exposing (..)
5+
6+
7+main =
8+ layout [] <|
9+ wrappedRow
10+ [ padding 10
11+ , spacingXY 6 5
12+ ]
13+ [ text "Higher by 1 px" ]
tests-rendering/cases/resolved/Misc/Scroll.elmnew file+48
@@ -0,0 +1,48 @@
1+module Scroll exposing (main)
2+
3+import Browser
4+import Element exposing (Element, alignTop, centerX, centerY, column, el, fill, fillPortion, height, inFront, layoutWith, none, padding, px, row, scrollbarY, spacing, text, width, wrappedRow)
5+import Element.Background as Background
6+import Element.Font as Font
7+import Element.Input as Input exposing (button)
8+import Html exposing (Html)
9+import Html.Attributes
10+
11+
12+type alias Model =
13+ ()
14+
15+
16+type alias Msg =
17+ ()
18+
19+
20+field i =
21+ text <| "Scrolling text " ++ String.fromInt i
22+
23+
24+view : Model -> Html Msg
25+view model =
26+ layoutWith { options = [] } [ height fill ] <|
27+ row
28+ [ height fill ]
29+ [ column
30+ [ height fill
31+ , scrollbarY
32+
33+ -- The width fill is required, when using scrollbarY above
34+ -- else the width is set to 0.
35+ --, width fill
36+ ]
37+ (List.map field (List.range 1 100))
38+ , text "Non scrolling text"
39+ ]
40+
41+
42+main : Program () Model Msg
43+main =
44+ Browser.sandbox
45+ { init = ()
46+ , view = view
47+ , update = \msg model -> model
48+ }
cases/resolved/MismatchTextInputText.elm → tests-rendering/cases/resolved/MismatchTextInputText.elmrenamed

The content is the same.

cases/resolved/MultilineCollapsed.elm → tests-rendering/cases/resolved/MultilineCollapsed.elmrenamed

The content is the same.

cases/resolved/MultilineInput.elm → tests-rendering/cases/resolved/MultilineInput.elmrenamed

The content is the same.

cases/resolved/MultilineNewline.elm → tests-rendering/cases/resolved/MultilineNewline.elmrenamed

The content is the same.

tests-rendering/cases/resolved/Nearby/InFrontIssue.elmnew file+64
@@ -0,0 +1,64 @@
1+module Nearby.InFrontIssue exposing (view)
2+
3+import Browser
4+import Element as Ui exposing (..)
5+import Element.Background as Background
6+import Element.Font as Font
7+import Html exposing (Html)
8+
9+
10+view : Html Msg
11+view =
12+ let
13+ a =
14+ el
15+ [ width fill
16+ , height <| px 30
17+ , Background.color <| rgb 0 1 0
18+ ]
19+ <|
20+ text "A"
21+
22+ b =
23+ column
24+ [ width fill
25+ , height <| px 5000
26+ , Background.color <| rgb 1 0 1
27+ , spacing 60
28+ ]
29+ [ text "B"
30+ , c
31+ ]
32+
33+ c =
34+ el
35+ [ width <| px 100
36+ , height <| px 100
37+ , Background.color <| rgb 0 0 0
38+ , Font.color <| rgb 1 1 1
39+ , paddingXY 0 30
40+ , centerX
41+ , above d
42+ , below d
43+ , onLeft d
44+ , onRight d
45+ ]
46+ <|
47+ text "C"
48+
49+ d =
50+ el
51+ [ width <| px 100
52+ , height <| px 100
53+ , centerX
54+ , Background.color <| rgb 1 1 1
55+ , Font.color <| rgb 0 0 0
56+ ]
57+ <|
58+ text "D"
59+ in
60+ layout
61+ [ inFront a
62+ , paddingXY 0 30
63+ ]
64+ b
tests-rendering/cases/resolved/Nearby/Issue22ZIndex.elmnew file+87
@@ -0,0 +1,87 @@
1+module Nearby.Issue22ZIndex exposing (main)
2+
3+import Browser
4+import Element as Ui exposing (..)
5+import Element.Background as Background
6+import Element.Font as Font
7+import Html exposing (Html)
8+
9+
10+type alias Model =
11+ {}
12+
13+
14+initialModel : Model
15+initialModel =
16+ {}
17+
18+
19+type Msg
20+ = NoOp
21+
22+
23+update : Msg -> Model -> Model
24+update msg model =
25+ model
26+
27+
28+view : Model -> Html Msg
29+view model =
30+ let
31+ a =
32+ el
33+ [ width fill
34+ , height <| px 30
35+ , Background.color <| rgb 0 1 0
36+ ]
37+ <|
38+ text "A"
39+
40+ b =
41+ column
42+ [ width fill
43+ , height <| px 5000
44+ , Background.color <| rgb 1 0 1
45+ , spacing 60
46+ ]
47+ [ text "B"
48+ , c
49+ ]
50+
51+ c =
52+ el
53+ [ width fill
54+ , height <| px 100
55+ , Background.color <| rgb 0 0 0
56+ , Font.color <| rgb 1 1 1
57+ , paddingXY 0 30
58+ , inFront d
59+ ]
60+ <|
61+ text "C"
62+
63+ d =
64+ el
65+ [ width <| px 100
66+ , height <| px 100
67+ , centerX
68+ , Background.color <| rgb 1 1 1
69+ , Font.color <| rgb 0 0 0
70+ ]
71+ <|
72+ text "D"
73+ in
74+ layout
75+ [ inFront a
76+ , paddingXY 0 30
77+ ]
78+ b
79+
80+
81+main : Program () Model Msg
82+main =
83+ Browser.sandbox
84+ { init = initialModel
85+ , view = view
86+ , update = update
87+ }
tests-rendering/cases/resolved/Nearby/OtherInFrontIssue.elmnew file+55
@@ -0,0 +1,55 @@
1+module Nearby.OtherInFrontIssue exposing (main)
2+
3+import Browser
4+import Element exposing (Element)
5+import Element.Background as Background
6+import Html exposing (Html, button, div, text)
7+import Html.Attributes
8+import Html.Events exposing (onClick)
9+
10+
11+
12+-- helping hack
13+
14+
15+translateLeft : Element.Attribute msg
16+translateLeft =
17+ Html.Attributes.style "transform" "translate(-100%)"
18+ |> Element.htmlAttribute
19+
20+
21+main =
22+ Element.layout
23+ [ Element.inFront <|
24+ Element.el
25+ [ Element.alignLeft
26+ , Background.color <| Element.rgb 1 0.5 0.5
27+ , Element.height Element.fill
28+ ]
29+ <|
30+ Element.text "Side Menu .........."
31+ ]
32+ contentPane
33+
34+
35+contentPane =
36+ Element.column
37+ [ Element.width Element.fill
38+ , Element.height Element.fill
39+ , Background.color <| Element.rgb 1 0 0
40+ ]
41+ [ Element.el
42+ [ Element.centerX
43+ , Element.centerY
44+ , Element.onLeft <| Element.text " [ onLeft. onLeft onLeft onLeft onLeft onLeft onLeft ] "
45+ ]
46+ (Element.text " [ Content: This will remain behind the Side Menu. ]")
47+ , Element.el
48+ [ Element.centerX
49+ , Element.centerY
50+ , Element.behindContent <|
51+ Element.el [ translateLeft ] <|
52+ Element.text " [ behindContent + translateLeft. behindContent + translateLeft ] "
53+ ]
54+ (Element.text " [ Content: This will remain behind the Side Menu. ]")
55+ ]
tests-rendering/cases/resolved/Nearby/ZIndexRegression.elmnew file+43
@@ -0,0 +1,43 @@
1+module Nearby.ZIndexRegression exposing (main)
2+
3+import Element exposing (..)
4+import Element.Background as Background
5+import Html exposing (Html)
6+
7+
8+main : Html msg
9+main =
10+ layout [] <|
11+ column [ width fill, height fill ]
12+ [ el
13+ [ width fill
14+ , height (px 100)
15+ , Background.color (rgb 0 0 1)
16+ , inFront menu
17+ ]
18+ none
19+ , el
20+ [ width fill
21+ , height fill
22+ , inFront menu
23+ ]
24+ none
25+ ]
26+
27+
28+menu : Element msg
29+menu =
30+ el
31+ [ transparent True
32+ , mouseOver [ transparent False ]
33+ , width fill
34+ , height fill
35+ , below <|
36+ el
37+ [ width fill
38+ , height (px 500)
39+ , Background.color (rgb 1 0 0)
40+ ]
41+ none
42+ ]
43+ none
cases/resolved/NestedColumns.elm → tests-rendering/cases/resolved/NestedColumns.elmrenamed

The content is the same.

cases/resolved/OtherOtherZindex.elm → tests-rendering/cases/resolved/OtherOtherZindex.elmrenamed

The content is the same.

cases/resolved/OtherZIndex.elm → tests-rendering/cases/resolved/OtherZIndex.elmrenamed

The content is the same.

cases/resolved/OverflowingInput.elm → tests-rendering/cases/resolved/OverflowingInput.elmrenamed

The content is the same.

cases/resolved/OverflowingInput2.elm → tests-rendering/cases/resolved/OverflowingInput2.elmrenamed

The content is the same.

cases/resolved/OverflowingInput3.elm → tests-rendering/cases/resolved/OverflowingInput3.elmrenamed

The content is the same.

cases/resolved/ReversedTableOrder.elm → tests-rendering/cases/resolved/ReversedTableOrder.elmrenamed

The content is the same.

cases/resolved/TextAreaMax.elm → tests-rendering/cases/resolved/TextAreaMax.elmrenamed

The content is the same.

cases/resolved/WeirdWrapping.elm → tests-rendering/cases/resolved/WeirdWrapping.elmrenamed

The content is the same.

cases/resolved/ZIndex.elm → tests-rendering/cases/resolved/ZIndex.elmrenamed

The content is the same.

cases/resolved/elm.json → tests-rendering/cases/resolved/elm.jsonrenamed

The content is the same.

tests-rendering/elm.json+22−25
@@ -1,28 +1,25 @@
1{
1{
2− "type": "application",
2+ "type": "application",
3− "source-directories": [
3+ "source-directories": ["src", "../src", "cases/open", "cases/resolved"],
4− "src",
5− "../src"
6− ],
7− "elm-version": "0.19.1",
4+ "elm-version": "0.19.1",
8− "dependencies": {
5+ "dependencies": {
9− "direct": {
6+ "direct": {
10− "elm/browser": "1.0.0",
7+ "elm/browser": "1.0.2",
11− "elm/core": "1.0.0",
8+ "elm/core": "1.0.0",
12− "elm/html": "1.0.0",
9+ "elm/html": "1.0.0",
13− "elm/json": "1.0.0",
10+ "elm/json": "1.0.0",
14− "elm/parser": "1.1.0",
11+ "elm/parser": "1.1.0",
15− "elm/random": "1.0.0",
12+ "elm/random": "1.0.0",
16− "elm/time": "1.0.0",
13+ "elm/time": "1.0.0",
17− "elm/virtual-dom": "1.0.0",
14+ "elm/virtual-dom": "1.0.2",
18− "elm-explorations/test": "1.1.0"
15+ "elm-explorations/test": "1.1.0"
19− },
20− "indirect": {
21− "elm/url": "1.0.0"
22− }
23 },
16 },
24− "test-dependencies": {
25− "direct": {},
26− "indirect": {}
17+ "indirect": {
18+ "elm/url": "1.0.0"
27 }
19 }
20+ },
21+ "test-dependencies": {
22+ "direct": {},
23+ "indirect": {}
24+ }
28−}
25+}
tests-rendering/src/Testable.elm+321−141
@@ -1,47 +1,48 @@
1module Testable exposing
1module Testable exposing
2 ( Attr(..)
2 ( Attr(..)
3+ , AttributeId(..)
3 , BoundingBox
4 , BoundingBox
4 , Element(..)
5 , Element(..)
6+ , ElementType(..)
5 , Found
7 , Found
6 , LayoutContext(..)
8 , LayoutContext(..)
9+ , LayoutExpectation(..)
10+ , LayoutTest
7 , Location(..)
11 , Location(..)
8 , Style
12 , Style
9 , Surroundings
13 , Surroundings
10− , addAttribute
14+ , TextMetrics
11− , applyLabels
12 , compareFormattedColor
15 , compareFormattedColor
13− , createAttributeTest
16+ , elementTypeToString
14− , createTest
17+ , equal
15 , formatColor
18 , formatColor
16 , formatColorWithAlpha
19 , formatColorWithAlpha
17− , getElementId
18 , getIds
20 , getIds
19 , getSpacing
21 , getSpacing
20− , idAttr
22+ , getSpacingFromAttributes
21− , levelToString
23+ , lessThanOrEqual
22− , render
24+ , rounded
23− , renderAttribute
24− , renderElement
25 , runTests
25 , runTests
26− , toTest
26+ , textHeight
27+ , toElement
28+ , toHtml
29+ , todo
30+ , true
27 )
31 )
28
32
29{-| -}
33{-| -}
30
34
31import Dict exposing (Dict)
35import Dict exposing (Dict)
32import Element exposing (Color)
36import Element exposing (Color)
33−import Expect
34import Html exposing (Html)
37import Html exposing (Html)
35import Html.Attributes
38import Html.Attributes
36import Internal.Model as Internal
39import Internal.Model as Internal
37−import Random
38import Test exposing (Test)
40import Test exposing (Test)
39−import Test.Runner
40−import Test.Runner.Failure
41
41
42
42
43type Element msg
43type Element msg
44 = El (List (Attr msg)) (Element msg)
44 = El (List (Attr msg)) (Element msg)
45+ | Link String (List (Attr msg)) (Element msg)
45 | Row (List (Attr msg)) (List (Element msg))
46 | Row (List (Attr msg)) (List (Element msg))
46 | Column (List (Attr msg)) (List (Element msg))
47 | Column (List (Attr msg)) (List (Element msg))
47 | TextColumn (List (Attr msg)) (List (Element msg))
48 | TextColumn (List (Attr msg)) (List (Element msg))
@@ -50,22 +51,104 @@ type Element msg
50 | Empty
51 | Empty
51
52
52
53
54+type ElementType
55+ = ElType
56+ | RowType
57+ | ColumnType
58+ | TextColumnType
59+ | ParagraphType
60+ | TextType
61+ | EmptyType
62+
63+
64+elementTypeToString : ElementType -> String
65+elementTypeToString elem =
66+ case elem of
67+ ElType ->
68+ "El"
69+
70+ RowType ->
71+ "Row"
72+
73+ ColumnType ->
74+ "Column"
75+
76+ TextColumnType ->
77+ "TextColumn"
78+
79+ ParagraphType ->
80+ "Paragraph"
81+
82+ TextType ->
83+ "Text"
84+
85+ EmptyType ->
86+ "Empty"
87+
88+
89+toElementType : Element msg -> ElementType
90+toElementType elem =
91+ case elem of
92+ El _ _ ->
93+ ElType
94+
95+ Link _ _ _ ->
96+ ElType
97+
98+ Row _ _ ->
99+ RowType
100+
101+ Column _ _ ->
102+ ColumnType
103+
104+ TextColumn _ _ ->
105+ TextColumnType
106+
107+ Paragraph _ _ ->
108+ ParagraphType
109+
110+ Text _ ->
111+ TextType
112+
113+ Empty ->
114+ EmptyType
115+
116+
117+{-| We have an attribute id in order to remove tests when an overriding attribtue is assigned.
118+
119+Basically, we want to implictly test for height/width shrink.
120+
121+But we want to skip this test if width/height is already set.
122+
123+This allows us to do that by comparing identities.
124+
125+-}
126+type AttributeId
127+ = NoId
128+ | IsWidth
129+ | IsHeight
130+
131+
53type Attr msg
132type Attr msg
54 = Attr (Element.Attribute msg)
133 = Attr (Element.Attribute msg)
55− | AttrTest (Surroundings -> Test)
134+ | AttrTest
135+ { test : Surroundings msg -> List LayoutExpectation
136+ , label : String
137+ , id : AttributeId
138+ }
56 | Batch (List (Attr msg))
139 | Batch (List (Attr msg))
57 | Spacing Int
140 | Spacing Int
58 | Nearby
141 | Nearby
59 { location : Location
142 { location : Location
60 , element : Element msg
143 , element : Element msg
61− , test : Surroundings -> () -> Expect.Expectation
144+ , test : Surroundings msg -> List LayoutExpectation
62 , label : String
145 , label : String
63 }
146 }
64− | Label String
65 | LabeledTest
147 | LabeledTest
66− { test : Surroundings -> () -> Expect.Expectation
148+ { test : Surroundings msg -> List LayoutExpectation
67 , label : String
149 , label : String
68 , attr : Element.Attribute msg
150 , attr : Element.Attribute msg
151+ , id : AttributeId
69 }
152 }
70
153
71
154
@@ -83,16 +166,20 @@ type LayoutContext
83 | InRow
166 | InRow
84 | InEl
167 | InEl
85 | InColumn
168 | InColumn
169+ | InParagraph
170+ | InTextCol
171+ | AtRoot
86
172
87
173
88−type alias Surroundings =
174+type alias Surroundings msg =
89 { siblings : List Found
175 { siblings : List Found
90 , parent : Found
176 , parent : Found
91 , children : List Found
177 , children : List Found
92 , self : Found
178 , self : Found
179+ , selfElement : Element msg
93
180
94 -- These values are needed to perform some types of tests.
181 -- These values are needed to perform some types of tests.
95− , location : LayoutContext
182+ , parentLayout : LayoutContext
96 , parentSpacing : Int
183 , parentSpacing : Int
97 }
184 }
98
185
@@ -101,9 +188,36 @@ type alias Found =
101 { bbox : BoundingBox
188 { bbox : BoundingBox
102 , style : Style
189 , style : Style
103 , isVisible : Bool
190 , isVisible : Bool
191+ , textMetrics : List TextMetrics
104 }
192 }
105
193
106
194
195+type alias TextMetrics =
196+ { actualBoundingBoxAscent : Float
197+ , actualBoundingBoxDescent : Float
198+ , actualBoundingBoxLeft : Float
199+ , actualBoundingBoxRight : Float
200+ , width : Float
201+ }
202+
203+
204+{-| The font metrics we currently have are `actual`, meaning for the text actually rendered, not the font as a whole.
205+
206+We also know the font size is 20, so we're just going to return 20.
207+
208+TODO: improve font metric collection using something like opentype.js
209+
210+-}
211+textHeight metrics =
212+ let
213+ actualHeight =
214+ metrics.actualBoundingBoxAscent
215+ + metrics.actualBoundingBoxDescent
216+ in
217+ -- actualHeight / 20
218+ 20
219+
220+
107{-| -}
221{-| -}
108type alias Style =
222type alias Style =
109 Dict String String
223 Dict String String
@@ -125,6 +239,67 @@ type alias BoundingBox =
125 }
239 }
126
240
127
241
242+type LayoutExpectation
243+ = Expect
244+ { description : String
245+ , result : Bool
246+ }
247+ | Todo String
248+
249+
250+type alias LayoutTest =
251+ { elementDomId : String
252+ , elementType : ElementType
253+ , label : String
254+ , expectations : List LayoutExpectation
255+ }
256+
257+
258+
259+{- Expectations -}
260+
261+
262+floatToString : Float -> String
263+floatToString x =
264+ String.fromFloat (toFloat (round (x * 100)) / 100)
265+
266+
267+lessThanOrEqual : String -> Float -> Float -> LayoutExpectation
268+lessThanOrEqual label one two =
269+ Expect
270+ { description = label ++ " " ++ floatToString one ++ " <= " ++ floatToString two
271+ , result = one <= two
272+ }
273+
274+
275+rounded : String -> { expected : Float, found : Float } -> LayoutExpectation
276+rounded label { expected, found } =
277+ true
278+ (label ++ "| expected " ++ floatToString expected ++ ", found " ++ floatToString found)
279+ (abs (expected - found) < 1)
280+
281+
282+equal : a -> a -> LayoutExpectation
283+equal one two =
284+ Expect
285+ { description = "Are equal"
286+ , result = one == two
287+ }
288+
289+
290+true : String -> Bool -> LayoutExpectation
291+true label passing =
292+ Expect
293+ { description = label
294+ , result = passing
295+ }
296+
297+
298+todo : String -> LayoutExpectation
299+todo =
300+ Todo
301+
302+
128
303
129{- Retrieve Ids -}
304{- Retrieve Ids -}
130
305
@@ -160,6 +335,9 @@ getElementId level el =
160 El attrs child ->
335 El attrs child ->
161 id :: getElementId (0 :: level) child ++ attributeIDs attrs
336 id :: getElementId (0 :: level) child ++ attributeIDs attrs
162
337
338+ Link _ attrs child ->
339+ id :: getElementId (0 :: level) child ++ attributeIDs attrs
340+
163 Row attrs children ->
341 Row attrs children ->
164 id :: childrenIDs children ++ attributeIDs attrs
342 id :: childrenIDs children ++ attributeIDs attrs
165
343
@@ -183,9 +361,19 @@ getElementId level el =
183{- Render as Html -}
361{- Render as Html -}
184
362
185
363
364+toElement : Element msg -> Element.Element msg
365+toElement el =
366+ renderElement [ 0, 0 ] el
367+
368+
186−render : Element msg -> Html msg
369+toHtml : Element msg -> Html msg
187−render el =
370+toHtml el =
188− Element.layout [ idAttr "0" ] <|
371+ Element.layout
372+ [ idAttr "0"
373+ , Element.width (Element.px 1000)
374+ , Element.height (Element.px 1000)
375+ ]
376+ <|
189 renderElement [ 0, 0 ] el
377 renderElement [ 0, 0 ] el
190
378
191
379
@@ -214,6 +402,13 @@ renderElement level el =
214 (id :: makeAttributes attrs)
402 (id :: makeAttributes attrs)
215 (renderElement (0 :: level) child)
403 (renderElement (0 :: level) child)
216
404
405+ Link url attrs child ->
406+ Element.link
407+ (id :: makeAttributes attrs)
408+ { url = url
409+ , label = renderElement (0 :: level) child
410+ }
411+
217 Row attrs children ->
412 Row attrs children ->
218 Element.row
413 Element.row
219 (id :: makeAttributes attrs)
414 (id :: makeAttributes attrs)
@@ -253,9 +448,6 @@ renderAttribute level attrIndex attr =
253 Spacing _ ->
448 Spacing _ ->
254 []
449 []
255
450
256− Label _ ->
257− []
258−
259 Nearby { location, element } ->
451 Nearby { location, element } ->
260 case location of
452 case location of
261 Above ->
453 Above ->
@@ -288,30 +480,36 @@ renderAttribute level attrIndex attr =
288{- Convert to Test -}
480{- Convert to Test -}
289
481
290
482
291−toTest : String -> Dict String Found -> Element msg -> Test
483+runTests : Dict String Found -> Element msg -> List LayoutTest
292−toTest label harvested el =
484+runTests harvested el =
293 let
485 let
294 maybeFound =
486 maybeFound =
295 Dict.get "se-0" harvested
487 Dict.get "se-0" harvested
296 in
488 in
297 case maybeFound of
489 case maybeFound of
298 Nothing ->
490 Nothing ->
299− Test.describe label
491+ [ { elementDomId = "se-0"
300− [ Test.test "Find Root" <|
492+ , elementType = EmptyType
301− \_ -> Expect.fail "unable to find root"
493+ , label = "Finding root element"
494+ , expectations =
495+ [ Expect
496+ { description = "Locating root element for rendering tests"
497+ , result = False
498+ }
499+ ]
500+ }
302− ]
501+ ]
303
502
304 Just root ->
503 Just root ->
305− Test.describe label <|
306− createTest
504+ createTest
307− { siblings = []
505+ { siblings = []
308− , parent = root
506+ , parent = root
309− , cache = harvested
507+ , cache = harvested
310− , parentSpacing = 0
508+ , parentSpacing = 0
311− , level = [ 0, 0 ]
509+ , level = [ 0, 0 ]
312− , element = el
510+ , element = el
313− , location = InEl
511+ , location = AtRoot
314− }
512+ }
315
513
316
514
317levelToString : List Int -> String
515levelToString : List Int -> String
@@ -331,7 +529,7 @@ createTest :
331 , location : LayoutContext
529 , location : LayoutContext
332 , parentSpacing : Int
530 , parentSpacing : Int
333 }
531 }
334− -> List Test
532+ -> List LayoutTest
335createTest { siblings, parent, cache, level, element, location, parentSpacing } =
533createTest { siblings, parent, cache, level, element, location, parentSpacing } =
336 let
534 let
337 spacing =
535 spacing =
@@ -341,7 +539,7 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
341 id =
539 id =
342 levelToString level
540 levelToString level
343
541
344− testChildren : Found -> List (Element msg) -> List Test
542+ testChildren : Found -> List (Element msg) -> List LayoutTest
345 testChildren found children =
543 testChildren found children =
346 let
544 let
347 childrenFound =
545 childrenFound =
@@ -369,13 +567,13 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
369 { a
567 { a
370 | index : Int
568 | index : Int
371 , previous : List Found
569 , previous : List Found
372− , tests : List Test
570+ , tests : List LayoutTest
373 , upcoming : List Found
571 , upcoming : List Found
374 }
572 }
375 ->
573 ->
376 { index : Int
574 { index : Int
377 , previous : List Found
575 , previous : List Found
378− , tests : List Test
576+ , tests : List LayoutTest
379 , upcoming : List Found
577 , upcoming : List Found
380 }
578 }
381 applyChildTest found child childTest =
579 applyChildTest found child childTest =
@@ -402,6 +600,9 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
402 El _ _ ->
600 El _ _ ->
403 InEl
601 InEl
404
602
603+ Link _ _ _ ->
604+ InEl
605+
405 Row _ _ ->
606 Row _ _ ->
406 InRow
607 InRow
407
608
@@ -409,10 +610,10 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
409 InColumn
610 InColumn
410
611
411 TextColumn _ _ ->
612 TextColumn _ _ ->
412− InColumn
613+ InTextCol
413
614
414 Paragraph _ _ ->
615 Paragraph _ _ ->
415− InRow
616+ InParagraph
416
617
417 Text _ ->
618 Text _ ->
418 InEl
619 InEl
@@ -439,7 +640,7 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
439 rest
640 rest
440 }
641 }
441
642
442− tests : Found -> List (Attr msg) -> List (Element msg) -> List Test
643+ tests : Found -> List (Attr msg) -> List (Element msg) -> List LayoutTest
443 tests self attributes children =
644 tests self attributes children =
444 let
645 let
445 findBBox elem ( i, gathered ) =
646 findBBox elem ( i, gathered ) =
@@ -479,7 +680,6 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
479
680
480 attributeTests =
681 attributeTests =
481 attributes
682 attributes
482− |> applyLabels
483 |> List.indexedMap
683 |> List.indexedMap
484 -- Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
684 -- Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
485 (\i attr ->
685 (\i attr ->
@@ -490,8 +690,9 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
490 { siblings = siblings
690 { siblings = siblings
491 , parent = parent
691 , parent = parent
492 , self = self
692 , self = self
693+ , selfElement = element
493 , children = childrenFoundData
694 , children = childrenFoundData
494− , location = location
695+ , parentLayout = location
495 , parentSpacing = parentSpacing
696 , parentSpacing = parentSpacing
496 }
697 }
497 attr
698 attr
@@ -511,13 +712,26 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
511 []
712 []
512
713
513 _ ->
714 _ ->
514− [ Test.test ("Unable to find " ++ id) (always <| Expect.fail "failed id lookup") ]
715+ [ { elementDomId = id
716+ , elementType = EmptyType
717+ , label = "Finding element in DOM"
718+ , expectations =
719+ [ Expect
720+ { description = "Locating element for rendering test"
721+ , result = False
722+ }
723+ ]
724+ }
725+ ]
515
726
516 Just self ->
727 Just self ->
517 case element of
728 case element of
518 El attrs child ->
729 El attrs child ->
519 tests self attrs [ child ]
730 tests self attrs [ child ]
520
731
732+ Link _ attrs child ->
733+ tests self attrs [ child ]
734+
521 Row attrs children ->
735 Row attrs children ->
522 tests self attrs children
736 tests self attrs children
523
737
@@ -537,59 +751,33 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
537 []
751 []
538
752
539
753
540−applyLabels : List (Attr msg) -> List (Attr msg)
541−applyLabels attrs =
542− let
543− toLabel attr =
544− case attr of
545− Label label ->
546− Just label
547−
548− _ ->
549− Nothing
550−
551− newLabels =
552− attrs
553− |> List.filterMap toLabel
554− |> String.join ", "
555−
556− applyLabel newLabel attr =
557− case attr of
558− LabeledTest labeled ->
559− LabeledTest
560− { labeled
561− | label =
562− if newLabel == "" then
563− labeled.label
564−
565− else
566− newLabel ++ ", " ++ labeled.label
567− }
568−
569− x ->
570− x
571− in
572− List.map (applyLabel newLabels) attrs
573−
574−
575−createAttributeTest : Found -> Dict String Found -> List Int -> Int -> Surroundings -> Attr msg -> List Test
754+createAttributeTest :
755+ Found
756+ -> Dict String Found
757+ -> List Int
758+ -> Int
759+ -> Surroundings msg
760+ -> Attr msg
761+ -> List LayoutTest
576createAttributeTest parent cache level attrIndex surroundings attr =
762createAttributeTest parent cache level attrIndex surroundings attr =
577 let
763 let
578− indexLabel =
764+ domId =
579− levelToString (attrIndex :: level)
765+ "#" ++ levelToString level
580 in
766 in
581 case attr of
767 case attr of
582 Attr _ ->
768 Attr _ ->
583 []
769 []
584
770
585− Label _ ->
586− []
587−
588 Spacing _ ->
771 Spacing _ ->
589 []
772 []
590
773
591− AttrTest test ->
774+ AttrTest details ->
775+ [ { elementDomId = domId
776+ , elementType =
777+ toElementType surroundings.selfElement
778+ , label = details.label
592− [ test surroundings
779+ , expectations = details.test surroundings
780+ }
593 ]
781 ]
594
782
595 Nearby nearby ->
783 Nearby nearby ->
@@ -600,16 +788,29 @@ createAttributeTest parent cache level attrIndex surroundings attr =
600 , parentSpacing = 0
788 , parentSpacing = 0
601 , level = attrIndex :: -1 :: level
789 , level = attrIndex :: -1 :: level
602 , location = IsNearby nearby.location
790 , location = IsNearby nearby.location
603− , element = addAttribute (AttrTest (\context -> Test.test (nearby.label ++ " #" ++ indexLabel) (nearby.test context))) nearby.element
791+ , element =
792+ addAttribute
793+ (AttrTest
794+ { label = nearby.label
795+ , test =
796+ nearby.test
797+ , id = NoId
798+ }
799+ )
800+ nearby.element
604 }
801 }
605
802
606 Batch batch ->
803 Batch batch ->
607 batch
804 batch
608− |> List.indexedMap (\i attribute -> createAttributeTest parent cache (attrIndex :: level) i surroundings attribute)
805+ |> List.indexedMap (\i attribute -> createAttributeTest parent cache level i surroundings attribute)
609 |> List.concat
806 |> List.concat
610
807
611 LabeledTest { label, test } ->
808 LabeledTest { label, test } ->
809+ [ { elementDomId = domId
810+ , elementType = toElementType surroundings.selfElement
811+ , label = label
612− [ Test.test (label ++ " #" ++ indexLabel) (test surroundings)
812+ , expectations = test surroundings
813+ }
613 ]
814 ]
614
815
615
816
@@ -619,6 +820,9 @@ addAttribute attr el =
619 El attrs child ->
820 El attrs child ->
620 El (attr :: attrs) child
821 El (attr :: attrs) child
621
822
823+ Link url attrs child ->
824+ Link url (attr :: attrs) child
825+
622 Row attrs children ->
826 Row attrs children ->
623 Row (attr :: attrs) children
827 Row (attr :: attrs) children
624
828
@@ -638,48 +842,6 @@ addAttribute attr el =
638 Text str
842 Text str
639
843
640
844
641−runTests :
642− Random.Seed
643− -> Test
644− ->
645− List
646− ( String
647− , Maybe
648− { given : Maybe String
649− , description : String
650− , reason : Test.Runner.Failure.Reason
651− }
652− )
653−runTests seed tests =
654− let
655− run runner =
656− let
657− ran =
658− List.map Test.Runner.getFailureReason (runner.run ())
659− in
660− List.map2 Tuple.pair runner.labels ran
661−
662− results =
663− case Test.Runner.fromTest 100 seed tests of
664− Test.Runner.Plain rnrs ->
665− List.map run rnrs
666−
667− Test.Runner.Only rnrs ->
668− List.map run rnrs
669−
670− Test.Runner.Skipping rnrs ->
671− List.map run rnrs
672−
673− Test.Runner.Invalid invalid ->
674− let
675− _ =
676− Debug.log "Invalid tests" invalid
677− in
678− []
679− in
680− List.concat results
681−
682−
683compareFormattedColor : Color -> String -> Bool
845compareFormattedColor : Color -> String -> Bool
684compareFormattedColor color expected =
846compareFormattedColor color expected =
685 formatColor color == expected || formatColorWithAlpha color == expected
847 formatColor color == expected || formatColorWithAlpha color == expected
@@ -716,6 +878,21 @@ formatColor (Internal.Rgba red green blue alpha) =
716 ++ ")"
878 ++ ")"
717
879
718
880
881+getSpacingFromAttributes attrs =
882+ case attrs of
883+ [] ->
884+ 0
885+
886+ (Spacing i) :: remain ->
887+ i
888+
889+ (Batch batched) :: remain ->
890+ getSpacingFromAttributes (batched ++ remain)
891+
892+ _ :: remain ->
893+ getSpacingFromAttributes remain
894+
895+
719getSpacing : Element msg -> Maybe Int
896getSpacing : Element msg -> Maybe Int
720getSpacing el =
897getSpacing el =
721 let
898 let
@@ -741,6 +918,9 @@ getSpacing el =
741 El attrs _ ->
918 El attrs _ ->
742 filterAttrs attrs
919 filterAttrs attrs
743
920
921+ Link _ attrs _ ->
922+ filterAttrs attrs
923+
744 Row attrs _ ->
924 Row attrs _ ->
745 filterAttrs attrs
925 filterAttrs attrs
746
926
tests-rendering/src/Testable/Element.elm+857−241
@@ -10,6 +10,7 @@ module Testable.Element exposing
10 , below
10 , below
11 , centerX
11 , centerX
12 , centerY
12 , centerY
13+ , clip
13 , column
14 , column
14 , el
15 , el
15 , expectRoundedEquality
16 , expectRoundedEquality
@@ -19,8 +20,14 @@ module Testable.Element exposing
19 , inFront
20 , inFront
20 , isVisible
21 , isVisible
21 , label
22 , label
23+ , layout
24+ , link
22 , maximum
25 , maximum
23 , minimum
26 , minimum
27+ , moveDown
28+ , moveLeft
29+ , moveRight
30+ , moveUp
24 , none
31 , none
25 , onLeft
32 , onLeft
26 , onRight
33 , onRight
@@ -28,11 +35,17 @@ module Testable.Element exposing
28 , paddingXY
35 , paddingXY
29 , paragraph
36 , paragraph
30 , px
37 , px
38+ , rgb
39+ , rgba
31 , row
40 , row
41+ , scrollbarX
42+ , scrollbarY
43+ , scrollbars
32 , shrink
44 , shrink
33 , spacing
45 , spacing
34 , text
46 , text
35 , textColumn
47 , textColumn
48+ , toProgram
36 , transparent
49 , transparent
37 , width
50 , width
38 )
51 )
@@ -51,9 +64,21 @@ In order to run the test:
51
64
52import Dict
65import Dict
53import Element
66import Element
54−import Expect
55import Html.Attributes
67import Html.Attributes
56import Testable
68import Testable
69+import Testable.Runner
70+
71+
72+{-| This is used when we want to run all tests in a test suite instead of one at a time.
73+-}
74+toProgram : Testable.Runner.Testable -> Testable.Runner.TestableProgram
75+toProgram testable =
76+ Testable.Runner.program [ testable ]
77+
78+
79+layout : List (Testable.Attr msg) -> Testable.Element Testable.Runner.Msg -> Testable.Runner.Testable
80+layout attrs elem =
81+ Testable.Runner.testable "Test" elem
57
82
58
83
59text : String -> Testable.Element msg
84text : String -> Testable.Element msg
@@ -62,18 +87,18 @@ text =
62
87
63
88
64el : List (Testable.Attr msg) -> Testable.Element msg -> Testable.Element msg
89el : List (Testable.Attr msg) -> Testable.Element msg -> Testable.Element msg
65−el =
90+el attrs =
66− Testable.El
91+ Testable.El (implicitWidthHeightShrink attrs)
67
92
68
93
69row : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
94row : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
70−row =
95+row attrs =
71− Testable.Row
96+ Testable.Row (implicitWidthHeightShrink attrs)
72
97
73
98
74column : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
99column : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
75−column =
100+column attrs =
76− Testable.Column
101+ Testable.Column (implicitWidthHeightShrink attrs)
77
102
78
103
79none : Testable.Element msg
104none : Testable.Element msg
@@ -82,13 +107,82 @@ none =
82
107
83
108
84paragraph : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
109paragraph : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
85−paragraph =
110+paragraph attrs =
111+ let
112+ withImplicits =
113+ implicitTest (widthHelper Nothing Nothing (Fill 1))
114+ :: implicitTest (heightHelper Nothing Nothing Shrink)
115+ :: attrs
116+ in
86− Testable.Paragraph
117+ Testable.Paragraph (skipOverridden withImplicits)
87
118
88
119
89textColumn : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
120textColumn : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
90−textColumn =
121+textColumn attrs =
122+ let
123+ withImplicits =
124+ implicitTest (widthHelper (Just 500) (Just 750) (Fill 1))
125+ :: implicitTest (heightHelper Nothing Nothing Shrink)
126+ :: attrs
127+ in
91− Testable.TextColumn
128+ Testable.TextColumn (skipOverridden withImplicits)
129+
130+
131+link :
132+ List (Testable.Attr msg)
133+ ->
134+ { url : String
135+ , label : Testable.Element msg
136+ }
137+ -> Testable.Element msg
138+link attrs details =
139+ Testable.Link details.url
140+ (implicitWidthHeightShrink attrs)
141+ details.label
142+
143+
144+clip =
145+ Testable.Attr Element.clip
146+
147+
148+{-| -}
149+scrollbars : Testable.Attr msg
150+scrollbars =
151+ Testable.Attr Element.scrollbars
152+
153+
154+{-| -}
155+scrollbarY : Testable.Attr msg
156+scrollbarY =
157+ Testable.Attr Element.scrollbarY
158+
159+
160+{-| -}
161+scrollbarX : Testable.Attr msg
162+scrollbarX =
163+ Testable.Attr Element.scrollbarX
164+
165+
166+{-| Old labeling mechanism that i removed to hastily
167+-}
168+label str =
169+ Testable.Batch []
170+
171+
172+moveUp x =
173+ Testable.Attr (Element.moveUp x)
174+
175+
176+moveDown x =
177+ Testable.Attr (Element.moveDown x)
178+
179+
180+moveRight x =
181+ Testable.Attr (Element.moveRight x)
182+
183+
184+moveLeft x =
185+ Testable.Attr (Element.moveLeft x)
92
186
93
187
94type Length
188type Length
@@ -107,6 +201,14 @@ maximum =
107 Maximum
201 Maximum
108
202
109
203
204+rgb =
205+ Element.rgb
206+
207+
208+rgba =
209+ Element.rgba
210+
211+
110px : Int -> Length
212px : Int -> Length
111px =
213px =
112 Px
214 Px
@@ -127,11 +229,6 @@ fillPortion =
127 Fill
229 Fill
128
230
129
231
130−label : String -> Testable.Attr msg
131−label =
132− Testable.Label
133−
134−
135transparent : Bool -> Testable.Attr msg
232transparent : Bool -> Testable.Attr msg
136transparent on =
233transparent on =
137 Testable.LabeledTest
234 Testable.LabeledTest
@@ -142,8 +239,9 @@ transparent on =
142 else
239 else
143 "opaque"
240 "opaque"
144 , attr = Element.transparent on
241 , attr = Element.transparent on
242+ , id = Testable.NoId
145 , test =
243 , test =
146− \context _ ->
244+ \context ->
147 let
245 let
148 selfTransparency =
246 selfTransparency =
149 context.self.style
247 context.self.style
@@ -157,19 +255,19 @@ transparent on =
157 else
255 else
158 "1"
256 "1"
159 in
257 in
160− Expect.equal value selfTransparency
258+ [ Testable.equal value selfTransparency ]
161 }
259 }
162
260
163
261
164{-| -}
262{-| -}
165isVisible : Testable.Attr msg
263isVisible : Testable.Attr msg
166isVisible =
264isVisible =
167− Testable.LabeledTest
265+ Testable.AttrTest
168 { label = "is-visible"
266 { label = "is-visible"
169− , attr = Element.htmlAttribute (Html.Attributes.style "not" "applicable")
267+ , id = Testable.NoId
170 , test =
268 , test =
171− \context _ ->
269+ \context ->
172− Expect.equal context.self.isVisible True
270+ [ Testable.equal context.self.isVisible True ]
173 }
271 }
174
272
175
273
@@ -179,15 +277,16 @@ alpha a =
179 Testable.LabeledTest
277 Testable.LabeledTest
180 { label = "alpha-" ++ String.fromFloat a
278 { label = "alpha-" ++ String.fromFloat a
181 , attr = Element.alpha a
279 , attr = Element.alpha a
280+ , id = Testable.NoId
182 , test =
281 , test =
183− \context _ ->
282+ \context ->
184 let
283 let
185 selfTransparency =
284 selfTransparency =
186 context.self.style
285 context.self.style
187 |> Dict.get "opacity"
286 |> Dict.get "opacity"
188 |> Maybe.withDefault "notfound"
287 |> Maybe.withDefault "notfound"
189 in
288 in
190− Expect.equal (String.fromFloat a) selfTransparency
289+ [ Testable.equal (String.fromFloat a) selfTransparency ]
191 }
290 }
192
291
193
292
@@ -197,9 +296,10 @@ padding pad =
197 Testable.LabeledTest
296 Testable.LabeledTest
198 { label = "padding " ++ String.fromInt pad
297 { label = "padding " ++ String.fromInt pad
199 , attr = Element.padding pad
298 , attr = Element.padding pad
299+ , id = Testable.NoId
200 , test =
300 , test =
201− \found _ ->
301+ \found ->
202− Expect.true ("Padding " ++ String.fromInt pad ++ " is present")
302+ [ Testable.true ("Padding " ++ String.fromInt pad ++ " is present")
203 (List.all ((==) pad)
303 (List.all ((==) pad)
204 [ floor found.self.bbox.padding.left
304 [ floor found.self.bbox.padding.left
205 , floor found.self.bbox.padding.right
305 , floor found.self.bbox.padding.right
@@ -207,6 +307,7 @@ padding pad =
207 , floor found.self.bbox.padding.bottom
307 , floor found.self.bbox.padding.bottom
208 ]
308 ]
209 )
309 )
310+ ]
210 }
311 }
211
312
212
313
@@ -216,9 +317,10 @@ paddingXY x y =
216 Testable.LabeledTest
317 Testable.LabeledTest
217 { label = "paddingXY " ++ String.fromInt x ++ ", " ++ String.fromInt y
318 { label = "paddingXY " ++ String.fromInt x ++ ", " ++ String.fromInt y
218 , attr = Element.paddingXY x y
319 , attr = Element.paddingXY x y
320+ , id = Testable.NoId
219 , test =
321 , test =
220− \found _ ->
322+ \found ->
221− Expect.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
323+ [ Testable.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
222 (List.all ((==) x)
324 (List.all ((==) x)
223 [ floor found.self.bbox.padding.left
325 [ floor found.self.bbox.padding.left
224 , floor found.self.bbox.padding.right
326 , floor found.self.bbox.padding.right
@@ -228,9 +330,15 @@ paddingXY x y =
228 , floor found.self.bbox.padding.bottom
330 , floor found.self.bbox.padding.bottom
229 ]
331 ]
230 )
332 )
333+ ]
231 }
334 }
232
335
233
336
337+listIf ls =
338+ List.filter Tuple.first ls
339+ |> List.map Tuple.second
340+
341+
234widthHelper maybeMin maybeMax len =
342widthHelper maybeMin maybeMax len =
235 let
343 let
236 addMin l =
344 addMin l =
@@ -290,6 +398,7 @@ widthHelper maybeMin maybeMax len =
290 -- Pixel values should ignore min and max?
398 -- Pixel values should ignore min and max?
291 Testable.LabeledTest
399 Testable.LabeledTest
292 { label = "width " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
400 { label = "width " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
401+ , id = Testable.IsWidth
293 , attr =
402 , attr =
294 Element.width
403 Element.width
295 (Element.px val
404 (Element.px val
@@ -297,18 +406,24 @@ widthHelper maybeMin maybeMax len =
297 |> addMax
406 |> addMax
298 )
407 )
299 , test =
408 , test =
300− \found _ ->
409+ \found ->
301− Expect.all
410+ [ expectRoundedEquality
302− [ \_ ->
411+ { expected = toFloat val
303− Expect.true "exact width is exact" (floor found.self.bbox.width == val)
412+ , found = found.self.bbox.width
413+ }
304− , \_ -> Expect.true "min/max is upheld" (minMaxTest (floor found.self.bbox.width))
414+ , Testable.true "min/max is upheld" (minMaxTest (floor found.self.bbox.width))
305− ]
415+ ]
306− ()
307 }
416 }
308
417
309 Fill portion ->
418 Fill portion ->
310 Testable.LabeledTest
419 Testable.LabeledTest
420+ { label =
421+ if portion == 1 then
422+ "width fill" ++ minLabel ++ maxLabel
423+
424+ else
311− { label = "width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
425+ "width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
426+ , id = Testable.IsWidth
312 , attr =
427 , attr =
313 Element.width
428 Element.width
314 (Element.fillPortion portion
429 (Element.fillPortion portion
@@ -316,41 +431,102 @@ widthHelper maybeMin maybeMax len =
316 |> addMax
431 |> addMax
317 )
432 )
318 , test =
433 , test =
319− \context _ ->
434+ \context ->
320− if List.member context.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
435+ if List.member context.parentLayout [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
321− Expect.true "width fill doesn't apply to onright/onleft elements" True
436+ [ Testable.true "width fill doesn't apply to onright/onleft elements" True ]
322
437
323 else
438 else
324 let
439 let
325 parentAvailableWidth =
440 parentAvailableWidth =
326 context.parent.bbox.width - (context.self.bbox.padding.left + context.self.bbox.padding.right)
441 context.parent.bbox.width - (context.self.bbox.padding.left + context.self.bbox.padding.right)
327 in
442 in
443+ List.concat
328− case context.location of
444+ [ case context.parentLayout of
329− Testable.IsNearby _ ->
445+ Testable.IsNearby _ ->
330− Expect.true "Nearby Element has fill width"
446+ [ Testable.true "Nearby Element has fill width"
331− ((floor context.parent.bbox.width == floor context.self.bbox.width)
447+ ((floor context.parent.bbox.width == floor context.self.bbox.width)
332− || minMaxTest (floor context.self.bbox.width)
448+ || minMaxTest (floor context.self.bbox.width)
333− )
449+ )
334−
450+ ]
451+
335− Testable.InColumn ->
452+ Testable.InColumn ->
336− Expect.true "Element within column has fill width"
453+ [ Testable.true "Element within column has fill width"
337− ((floor parentAvailableWidth == floor context.self.bbox.width)
454+ ((floor parentAvailableWidth == floor context.self.bbox.width)
338− || minMaxTest (floor context.self.bbox.width)
455+ || minMaxTest (floor context.self.bbox.width)
339− )
456+ )
340−
457+ ]
458+
341− Testable.InEl ->
459+ Testable.InEl ->
342− Expect.true "Element within element has fill width" <|
460+ [ Testable.true "Element within element has fill width" <|
343− (floor parentAvailableWidth == floor context.self.bbox.width)
461+ (floor parentAvailableWidth == floor context.self.bbox.width)
344− || minMaxTest (floor context.self.bbox.width)
462+ || minMaxTest (floor context.self.bbox.width)
345−
463+ ]
464+
346− _ ->
465+ _ ->
347− let
466+ let
348− spacePerPortion =
467+ spacePerPortion =
349− parentAvailableWidth / toFloat (List.length context.siblings + 1)
468+ parentAvailableWidth / toFloat (List.length context.siblings + 1)
350− in
469+ in
351− Expect.true "element has fill width" <|
470+ [ Testable.true "element has fill width" <|
352− (floor spacePerPortion == floor context.self.bbox.width)
471+ (floor spacePerPortion == floor context.self.bbox.width)
353− || minMaxTest (floor context.self.bbox.width)
472+ || minMaxTest (floor context.self.bbox.width)
473+ ]
474+ , [ Testable.lessThanOrEqual "not larger than parent"
475+ context.self.bbox.width
476+ context.parent.bbox.width
477+ ]
478+ , case context.selfElement of
479+ Testable.Paragraph _ _ ->
480+ -- A paragraph with width fill
481+ -- we want to test for correct text wrapping here.
482+ --
483+ let
484+ childWidth child =
485+ child.bbox.width
486+
487+ totalChildren =
488+ context.children
489+ |> List.map childWidth
490+ |> List.append (List.map .width context.self.textMetrics)
491+ |> List.sum
492+
493+ horizontalPadding =
494+ context.self.bbox.padding.left + context.self.bbox.padding.right
495+
496+ totalCalculatedWidth =
497+ totalChildren + horizontalPadding
498+
499+ expectedLines =
500+ if boxChildrenHeight > 20 then
501+ ceiling (boxChildrenHeight / 20)
502+
503+ else
504+ ceiling (totalCalculatedWidth / context.self.bbox.width)
505+
506+ boxChildrenHeight =
507+ context.children
508+ |> List.map
509+ (\c ->
510+ c.bbox.height
511+ )
512+ |> List.sum
513+ in
514+ [ Testable.true "text wrapping, checking height"
515+ -- all we're doing here is testing if wrapping is occurring
516+ -- not the exact height, which is a little tricky to calculate perfectly without more information.
517+ (if expectedLines > 1 then
518+ context.self.bbox.height >= (20 * 2)
519+
520+ else
521+ -- then it's less than 2 ~ line heights
522+ -- there's some weird variance here which makes it hard to be exact.
523+ context.self.bbox.height < (20 * 2)
524+ )
525+ ]
526+
527+ _ ->
528+ []
529+ ]
354 }
530 }
355
531
356 Shrink ->
532 Shrink ->
@@ -362,33 +538,211 @@ widthHelper maybeMin maybeMax len =
362 |> addMin
538 |> addMin
363 |> addMax
539 |> addMax
364 )
540 )
541+ , id = Testable.IsWidth
365 , test =
542 , test =
366− \context _ ->
543+ \context ->
544+ case context.selfElement of
545+ Testable.El _ _ ->
367− let
546+ let
368− childWidth child =
547+ childWidth child =
369− -- TODO: add margin values to widths
370− child.bbox.width
548+ child.bbox.width
371−
549+
372− totalChildren =
550+ totalChildren =
373− context.children
551+ context.children
374− |> List.map childWidth
552+ |> List.map childWidth
553+ |> List.append (List.map .width context.self.textMetrics)
375− |> List.sum
554+ |> List.sum
376−
555+
377− horizontalPadding =
556+ horizontalPadding =
378− context.self.bbox.padding.left + context.self.bbox.padding.right
557+ context.self.bbox.padding.left + context.self.bbox.padding.right
379−
558+ in
380− spacingValue =
559+ listIf
381− toFloat context.parentSpacing * (toFloat (List.length context.children) - 1)
560+ [ ( (context.parentLayout /= Testable.InParagraph)
382− in
561+ && (context.parentLayout /= Testable.InTextCol)
383− if totalChildren == 0 then
562+ , Testable.rounded "equals children"
384− -- TODO: The issue is that we have a hard time measuring `text` elements
563+ { expected = totalChildren + horizontalPadding
385− -- So if a element has a text child, then it's width isn't going to show up in the system.
564+ , found = context.self.bbox.width
565+ }
566+ )
567+ , ( context.parentLayout /= Testable.AtRoot
568+ , Testable.lessThanOrEqual "not larger than parent"
386− expectRoundedEquality context.self.bbox.width context.self.bbox.width
569+ context.self.bbox.width
570+ context.parent.bbox.width
571+ )
572+ ]
387
573
388− else
574+ Testable.Link _ _ _ ->
389− -- This fails if this element is actually a column
575+ let
390− -- So we need to capture what this element is in order to do this calculation.
576+ childWidth child =
577+ child.bbox.width
578+
579+ totalChildren =
580+ context.children
581+ |> List.map childWidth
582+ |> List.append (List.map .width context.self.textMetrics)
583+ |> List.sum
584+
585+ horizontalPadding =
586+ context.self.bbox.padding.left + context.self.bbox.padding.right
587+ in
588+ listIf
589+ [ ( (context.parentLayout /= Testable.InParagraph)
590+ && (context.parentLayout /= Testable.InTextCol)
591+ , Testable.rounded "equals children"
592+ { expected = totalChildren + horizontalPadding
593+ , found = context.self.bbox.width
594+ }
595+ )
596+ , ( context.parentLayout /= Testable.AtRoot
597+ , Testable.lessThanOrEqual "not larger than parent"
598+ context.self.bbox.width
599+ context.parent.bbox.width
600+ )
601+ ]
602+
603+ Testable.Row rowAttrs _ ->
604+ -- width of row is the sum of all children widths
605+ -- both text elements and others.
606+ let
607+ childWidth child =
608+ child.bbox.width
609+
610+ totalChildren =
611+ context.children
612+ |> List.map childWidth
613+ |> List.append (List.map .width context.self.textMetrics)
614+ |> List.sum
615+
616+ horizontalPadding =
617+ context.self.bbox.padding.left + context.self.bbox.padding.right
618+
619+ spacingAmount =
620+ Testable.getSpacingFromAttributes rowAttrs
621+
622+ totalSpacing =
623+ toFloat spacingAmount * (toFloat (List.length context.children) - 1)
624+ in
625+ listIf
626+ [ ( True
627+ , expectRoundedEquality
628+ { expected = totalChildren + horizontalPadding + totalSpacing
629+ , found = context.self.bbox.width
630+ }
631+ )
632+ , ( context.parentLayout /= Testable.AtRoot
633+ , Testable.lessThanOrEqual "not larger than parent"
634+ context.self.bbox.width
635+ context.parent.bbox.width
636+ )
637+ ]
638+
639+ Testable.Column _ _ ->
640+ -- The width of the column is the width of the widest child.
641+ let
642+ childWidth child =
643+ child.bbox.width
644+ + context.self.bbox.padding.left
645+ + context.self.bbox.padding.right
646+
647+ textChildren =
648+ List.map
649+ (\txt ->
650+ txt.width
651+ + context.self.bbox.padding.left
652+ + context.self.bbox.padding.right
653+ )
654+ context.self.textMetrics
655+
656+ allChildren =
657+ context.children
658+ |> List.map childWidth
659+ |> List.append textChildren
660+ in
661+ listIf
662+ [ ( True
663+ , expectRoundedEquality
664+ { expected = Maybe.withDefault 0 (List.maximum allChildren)
665+ , found = context.self.bbox.width
666+ }
667+ )
668+ , ( context.parentLayout /= Testable.AtRoot
669+ , Testable.lessThanOrEqual "not larger than parent"
670+ context.self.bbox.width
671+ context.parent.bbox.width
672+ )
673+ ]
674+
675+ Testable.TextColumn _ _ ->
676+ -- The width of the column is the width of the widest child.
677+ let
678+ childWidth child =
679+ child.bbox.width
680+ + context.self.bbox.padding.left
681+ + context.self.bbox.padding.right
682+
683+ textChildren =
684+ List.map
685+ (\txt ->
686+ txt.width
687+ + context.self.bbox.padding.left
688+ + context.self.bbox.padding.right
689+ )
690+ context.self.textMetrics
691+
692+ allChildren =
693+ context.children
694+ |> List.map childWidth
695+ |> List.append textChildren
696+ in
697+ listIf
698+ [ ( True
699+ , expectRoundedEquality
700+ { expected = Maybe.withDefault 0 (List.maximum allChildren)
701+ , found = context.self.bbox.width
702+ }
703+ )
704+ , ( context.parentLayout /= Testable.AtRoot
705+ , Testable.lessThanOrEqual "not larger than parent"
706+ context.self.bbox.width
707+ context.parent.bbox.width
708+ )
709+ ]
710+
711+ Testable.Paragraph _ _ ->
712+ -- This should be the size it's text,
713+ -- unless it takes up all available space, in which case it should wrap.
714+ let
715+ childWidth child =
716+ child.bbox.width
717+
718+ totalChildren =
719+ context.children
720+ |> List.map childWidth
721+ |> List.append (List.map .width context.self.textMetrics)
722+ |> List.sum
723+
724+ horizontalPadding =
725+ context.self.bbox.padding.left + context.self.bbox.padding.right
726+ in
727+ listIf
728+ [ ( True
729+ , expectRoundedEquality
391− expectRoundedEquality (totalChildren + horizontalPadding + spacingValue) context.self.bbox.width
730+ { expected = totalChildren + horizontalPadding
731+ , found = context.self.bbox.width
732+ }
733+ )
734+ , ( context.parentLayout /= Testable.AtRoot
735+ , Testable.lessThanOrEqual "not larger than parent"
736+ context.self.bbox.width
737+ context.parent.bbox.width
738+ )
739+ ]
740+
741+ Testable.Text _ ->
742+ []
743+
744+ Testable.Empty ->
745+ []
392 }
746 }
393
747
394
748
@@ -457,17 +811,16 @@ heightHelper maybeMin maybeMax len =
457 |> addMin
811 |> addMin
458 |> addMax
812 |> addMax
459 )
813 )
814+ , id = Testable.IsHeight
460 , test =
815 , test =
461− \found _ ->
816+ \found ->
462− Expect.all
817+ [ expectRoundedEquality
463− [ \_ ->
818+ { expected = toFloat val
464− Expect.true ("exact height is exact: " ++ String.fromInt (floor found.self.bbox.height) ++ "," ++ String.fromInt val)
465− (floor found.self.bbox.height == val)
819+ , found = found.self.bbox.height
466− , \_ ->
820+ }
467− Expect.true "min/max holds true"
821+ , Testable.true "min/max holds true"
468− (minMaxTest (floor found.self.bbox.height))
822+ (minMaxTest (floor found.self.bbox.height))
469− ]
823+ ]
470− ()
471 }
824 }
472
825
473 Fill portion ->
826 Fill portion ->
@@ -479,31 +832,32 @@ heightHelper maybeMin maybeMax len =
479 |> addMin
832 |> addMin
480 |> addMax
833 |> addMax
481 )
834 )
835+ , id = Testable.IsHeight
482 , test =
836 , test =
483− \context _ ->
837+ \context ->
484− if List.member context.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
838+ [ if List.member context.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
485− Expect.true "height fill doesn't apply to above/below elements" True
839+ Testable.true "height fill doesn't apply to above/below elements" True
486
840
487− else
841+ else
488 let
842 let
489 parentAvailableHeight =
843 parentAvailableHeight =
490 context.parent.bbox.height - (context.self.bbox.padding.top + context.self.bbox.padding.bottom)
844 context.parent.bbox.height - (context.self.bbox.padding.top + context.self.bbox.padding.bottom)
491 in
845 in
492− case context.location of
846+ case context.parentLayout of
493 Testable.IsNearby _ ->
847 Testable.IsNearby _ ->
494− Expect.true "Nearby Element has fill height"
848+ Testable.true "Nearby Element has fill height"
495 ((floor context.parent.bbox.height == floor context.self.bbox.height)
849 ((floor context.parent.bbox.height == floor context.self.bbox.height)
496 || minMaxTest (floor context.self.bbox.height)
850 || minMaxTest (floor context.self.bbox.height)
497 )
851 )
498
852
499 Testable.InColumn ->
853 Testable.InColumn ->
500− Expect.true "Element within column has fill height"
854+ Testable.true "Element within column has fill height"
501 ((floor parentAvailableHeight == floor context.self.bbox.height)
855 ((floor parentAvailableHeight == floor context.self.bbox.height)
502 || minMaxTest (floor context.self.bbox.height)
856 || minMaxTest (floor context.self.bbox.height)
503 )
857 )
504
858
505 Testable.InEl ->
859 Testable.InEl ->
506− Expect.true "Element within el has fill height" <|
860+ Testable.true "Element within el has fill height" <|
507 (floor parentAvailableHeight == floor context.self.bbox.height)
861 (floor parentAvailableHeight == floor context.self.bbox.height)
508 || minMaxTest (floor context.self.bbox.height)
862 || minMaxTest (floor context.self.bbox.height)
509
863
@@ -512,9 +866,10 @@ heightHelper maybeMin maybeMax len =
512 spacePerPortion =
866 spacePerPortion =
513 parentAvailableHeight / toFloat (List.length context.siblings + 1)
867 parentAvailableHeight / toFloat (List.length context.siblings + 1)
514 in
868 in
515− Expect.true "el has fill height" <|
869+ Testable.true "el has fill height" <|
516 (floor spacePerPortion == floor context.self.bbox.height)
870 (floor spacePerPortion == floor context.self.bbox.height)
517 || minMaxTest (floor context.self.bbox.height)
871 || minMaxTest (floor context.self.bbox.height)
872+ ]
518 }
873 }
519
874
520 Shrink ->
875 Shrink ->
@@ -526,33 +881,134 @@ heightHelper maybeMin maybeMax len =
526 |> addMin
881 |> addMin
527 |> addMax
882 |> addMax
528 )
883 )
884+ , id = Testable.IsHeight
529 , test =
885 , test =
530− \context _ ->
886+ \context ->
887+ case context.selfElement of
888+ Testable.El _ _ ->
531− let
889+ let
532− childWidth child =
890+ childHeight child =
533− -- TODO: add margin values to heights
534− child.bbox.height
891+ child.bbox.height
535−
892+ in
536− totalChildren =
893+ if List.isEmpty context.children then
537− context.children
894+ -- context.self.textMetrics
538− |> List.map childWidth
895+ -- |> List.map Testable.textHeight
539− |> List.sum
896+ -- |> List.sum
540−
897+ -- TODO: apparently the font metrics we have are for the literal characters rendered
541− verticalPadding =
898+ -- not for the font itself.
542− context.self.bbox.padding.top + context.self.bbox.padding.bottom
899+ [ Testable.todo "calculate height from actual text metrics"
543−
900+ ]
544− spacingValue =
545− toFloat context.parentSpacing * (toFloat (List.length context.children) - 1)
546− in
547− if totalChildren == 0 then
548− -- TODO: The issue is that we have a hard time measuring `text` elements
549− -- So if a element has a text child, then it's height isn't going to show up in the system.
550− expectRoundedEquality context.self.bbox.height context.self.bbox.height
551
901
552− else
902+ else
553− -- This fails if this element is actually a column
903+ [ expectRoundedEquality
554− -- So we need to capture what this element is in order to do this calculation.
904+ { expected =
555− expectRoundedEquality (totalChildren + verticalPadding + spacingValue) context.self.bbox.height
905+ context.children
906+ |> List.map childHeight
907+ |> List.sum
908+ |> (\h -> h + context.self.bbox.padding.top + context.self.bbox.padding.bottom)
909+ , found = context.self.bbox.height
910+ }
911+ , Testable.lessThanOrEqual "not larger than parent"
912+ context.self.bbox.height
913+ context.parent.bbox.height
914+ ]
915+
916+ Testable.Link _ _ _ ->
917+ let
918+ childHeight child =
919+ child.bbox.height
920+ in
921+ if List.isEmpty context.children then
922+ -- context.self.textMetrics
923+ -- |> List.map Testable.textHeight
924+ -- |> List.sum
925+ -- TODO: apparently the font metrics we have are for the literal characters rendered
926+ -- not for the font itself.
927+ [ Testable.todo "calculate height from actual text metrics"
928+ ]
929+
930+ else
931+ [ expectRoundedEquality
932+ { expected =
933+ context.children
934+ |> List.map childHeight
935+ |> List.sum
936+ |> (\h -> h + context.self.bbox.padding.top + context.self.bbox.padding.bottom)
937+ , found = context.self.bbox.height
938+ }
939+ , Testable.lessThanOrEqual "not larger than parent"
940+ context.self.bbox.height
941+ context.parent.bbox.height
942+ ]
943+
944+ Testable.Row _ _ ->
945+ let
946+ childHeight child =
947+ child.bbox.height
948+ in
949+ if List.isEmpty context.children then
950+ -- context.self.textMetrics
951+ -- |> List.map Testable.textHeight
952+ -- |> List.sum
953+ -- TODO: apparently the font metrics we have are for the literal characters rendered
954+ -- not for the font itself.
955+ [ Testable.todo "calculate height from actual text metrics" ]
956+
957+ else
958+ [ expectRoundedEquality
959+ { expected =
960+ context.children
961+ |> List.map childHeight
962+ |> List.maximum
963+ |> Maybe.withDefault 0
964+ |> (\h -> h + context.self.bbox.padding.top + context.self.bbox.padding.bottom)
965+ , found = context.self.bbox.height
966+ }
967+ , Testable.lessThanOrEqual "not larger than parent"
968+ context.self.bbox.height
969+ context.parent.bbox.height
970+ ]
971+
972+ Testable.Column colAttrs _ ->
973+ let
974+ childHeight child =
975+ child.bbox.height
976+
977+ totalChildren =
978+ context.children
979+ |> List.map childHeight
980+ |> List.append (List.map Testable.textHeight context.self.textMetrics)
981+ |> List.sum
982+
983+ verticalPadding =
984+ context.self.bbox.padding.top + context.self.bbox.padding.bottom
985+
986+ spacingAmount =
987+ Testable.getSpacingFromAttributes colAttrs
988+
989+ totalSpacing =
990+ toFloat spacingAmount * (toFloat (List.length context.children) - 1)
991+ in
992+ [ expectRoundedEquality
993+ { expected = totalChildren + verticalPadding + totalSpacing
994+ , found = context.self.bbox.height
995+ }
996+ , Testable.lessThanOrEqual "not larger than parent"
997+ context.self.bbox.height
998+ context.parent.bbox.height
999+ ]
1000+
1001+ Testable.TextColumn _ _ ->
1002+ []
1003+
1004+ Testable.Paragraph _ _ ->
1005+ []
1006+
1007+ Testable.Text _ ->
1008+ []
1009+
1010+ Testable.Empty ->
1011+ []
556 }
1012 }
557
1013
558
1014
@@ -573,8 +1029,9 @@ spacing space =
573 , Testable.LabeledTest
1029 , Testable.LabeledTest
574 { label = "spacing: " ++ String.fromInt space
1030 { label = "spacing: " ++ String.fromInt space
575 , attr = Element.spacing space
1031 , attr = Element.spacing space
1032+ , id = Testable.NoId
576 , test =
1033 , test =
577− \found _ ->
1034+ \found ->
578 let
1035 let
579 findDistance child total =
1036 findDistance child total =
580 List.concatMap
1037 List.concatMap
@@ -620,7 +1077,7 @@ spacing space =
620 []
1077 []
621 distances
1078 distances
622 in
1079 in
623− Expect.true
1080+ [ Testable.true
624 ("All children are at least "
1081 ("All children are at least "
625 ++ String.fromInt space
1082 ++ String.fromInt space
626 ++ " pixels apart."
1083 ++ " pixels apart."
@@ -628,6 +1085,7 @@ spacing space =
628 ++ " are not though"
1085 ++ " are not though"
629 )
1086 )
630 (allAreSpaced == [])
1087 (allAreSpaced == [])
1088+ ]
631 }
1089 }
632 ]
1090 ]
633
1091
@@ -650,30 +1108,33 @@ alignLeft =
650 Testable.LabeledTest
1108 Testable.LabeledTest
651 { label = "alignLeft"
1109 { label = "alignLeft"
652 , attr = Element.alignLeft
1110 , attr = Element.alignLeft
1111+ , id = Testable.NoId
653 , test =
1112 , test =
654− \found _ ->
1113+ \found ->
655− if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
1114+ [ if List.member found.parentLayout [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
656− Expect.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
1115+ Testable.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
657
1116
658− else if
1117+ else if
659− List.member found.location
1118+ List.member found.parentLayout
660 [ Testable.IsNearby Testable.InFront
1119 [ Testable.IsNearby Testable.InFront
661 , Testable.IsNearby Testable.Behind
1120 , Testable.IsNearby Testable.Behind
662 , Testable.IsNearby Testable.Above
1121 , Testable.IsNearby Testable.Above
663 , Testable.IsNearby Testable.Below
1122 , Testable.IsNearby Testable.Below
664 ]
1123 ]
665− then
1124+ then
666 expectRoundedEquality
1125 expectRoundedEquality
667− found.self.bbox.left
1126+ { found = found.self.bbox.left
668− found.parent.bbox.left
1127+ , expected = found.parent.bbox.left
1128+ }
669
1129
670− else if List.length found.siblings == 0 then
1130+ else if List.length found.siblings == 0 then
671 expectRoundedEquality
1131 expectRoundedEquality
672− found.self.bbox.left
1132+ { found = found.self.bbox.left
673− (found.parent.bbox.left + found.parent.bbox.padding.left)
1133+ , expected = found.parent.bbox.left + found.parent.bbox.padding.left
1134+ }
674
1135
675− else
1136+ else
676− case found.location of
1137+ case found.parentLayout of
677 Testable.InRow ->
1138 Testable.InRow ->
678 let
1139 let
679 siblingsOnLeft =
1140 siblingsOnLeft =
@@ -688,13 +1149,16 @@ alignLeft =
688 |> List.sum
1149 |> List.sum
689 in
1150 in
690 expectRoundedEquality
1151 expectRoundedEquality
691− found.self.bbox.left
1152+ { found = found.self.bbox.left
692− (found.parent.bbox.left + (found.parent.bbox.padding.left + widthsOnLeft + spacings))
1153+ , expected = found.parent.bbox.left + (found.parent.bbox.padding.left + widthsOnLeft + spacings)
1154+ }
693
1155
694 _ ->
1156 _ ->
695 expectRoundedEquality
1157 expectRoundedEquality
696− found.self.bbox.left
1158+ { found = found.self.bbox.left
697− (found.parent.bbox.left + found.parent.bbox.padding.left)
1159+ , expected = found.parent.bbox.left + found.parent.bbox.padding.left
1160+ }
1161+ ]
698 }
1162 }
699
1163
700
1164
@@ -704,8 +1168,9 @@ centerX =
704 Testable.LabeledTest
1168 Testable.LabeledTest
705 { label = "centerX"
1169 { label = "centerX"
706 , attr = Element.centerX
1170 , attr = Element.centerX
1171+ , id = Testable.NoId
707 , test =
1172 , test =
708− \found _ ->
1173+ \found ->
709 let
1174 let
710 selfCenter : Float
1175 selfCenter : Float
711 selfCenter =
1176 selfCenter =
@@ -715,16 +1180,18 @@ centerX =
715 parentCenter =
1180 parentCenter =
716 found.parent.bbox.left + (found.parent.bbox.width / 2)
1181 found.parent.bbox.left + (found.parent.bbox.width / 2)
717 in
1182 in
718− if List.member found.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
1183+ if List.member found.parentLayout [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
719− Expect.true "centerX doesn't apply to elements that are onLeft or onRight" True
1184+ [ Testable.true "centerX doesn't apply to elements that are onLeft or onRight" True ]
720
1185
721 else if List.length found.siblings == 0 then
1186 else if List.length found.siblings == 0 then
722− expectRoundedEquality
1187+ [ expectRoundedEquality
723− selfCenter
1188+ { found = selfCenter
724− parentCenter
1189+ , expected = parentCenter
1190+ }
1191+ ]
725
1192
726 else
1193 else
727− case found.location of
1194+ case found.parentLayout of
728 Testable.InRow ->
1195 Testable.InRow ->
729 let
1196 let
730 siblingsOnLeft =
1197 siblingsOnLeft =
@@ -754,12 +1221,18 @@ centerX =
754 / 2
1221 / 2
755 )
1222 )
756 in
1223 in
757− expectRoundedEquality
1224+ [ expectRoundedEquality
758− selfCenter
1225+ { found = selfCenter
759− expectedCenter
1226+ , expected = expectedCenter
1227+ }
1228+ ]
760
1229
761 _ ->
1230 _ ->
762− expectRoundedEquality selfCenter parentCenter
1231+ [ expectRoundedEquality
1232+ { found = selfCenter
1233+ , expected = parentCenter
1234+ }
1235+ ]
763 }
1236 }
764
1237
765
1238
@@ -769,30 +1242,35 @@ alignRight =
769 Testable.LabeledTest
1242 Testable.LabeledTest
770 { label = "alignRight"
1243 { label = "alignRight"
771 , attr = Element.alignRight
1244 , attr = Element.alignRight
1245+ , id = Testable.NoId
772 , test =
1246 , test =
773− \found _ ->
1247+ \found ->
774− if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
1248+ if List.member found.parentLayout [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
775− Expect.true "alignRight doesn't apply to elements that are onLeft or onRight" True
1249+ [ Testable.true "alignRight doesn't apply to elements that are onLeft or onRight" True ]
776
1250
777 else if
1251 else if
778− List.member found.location
1252+ List.member found.parentLayout
779 [ Testable.IsNearby Testable.InFront
1253 [ Testable.IsNearby Testable.InFront
780 , Testable.IsNearby Testable.Behind
1254 , Testable.IsNearby Testable.Behind
781 , Testable.IsNearby Testable.Above
1255 , Testable.IsNearby Testable.Above
782 , Testable.IsNearby Testable.Below
1256 , Testable.IsNearby Testable.Below
783 ]
1257 ]
784 then
1258 then
785− expectRoundedEquality
1259+ [ expectRoundedEquality
786− found.self.bbox.right
1260+ { found = found.self.bbox.right
787− found.parent.bbox.right
1261+ , expected = found.parent.bbox.right
1262+ }
1263+ ]
788
1264
789 else if List.length found.siblings == 0 then
1265 else if List.length found.siblings == 0 then
790− expectRoundedEquality
1266+ [ expectRoundedEquality
791− found.self.bbox.right
1267+ { found = found.self.bbox.right
792− (found.parent.bbox.right + found.parent.bbox.padding.right)
1268+ , expected = found.parent.bbox.right + found.parent.bbox.padding.right
1269+ }
1270+ ]
793
1271
794 else
1272 else
795− case found.location of
1273+ case found.parentLayout of
796 Testable.InRow ->
1274 Testable.InRow ->
797 let
1275 let
798 siblingsOnRight =
1276 siblingsOnRight =
@@ -806,14 +1284,18 @@ alignRight =
806 |> List.map (.width << .bbox)
1284 |> List.map (.width << .bbox)
807 |> List.sum
1285 |> List.sum
808 in
1286 in
809− expectRoundedEquality
1287+ [ expectRoundedEquality
810− found.self.bbox.right
1288+ { found = found.self.bbox.right
811− (found.parent.bbox.right - (found.parent.bbox.padding.right + widthsOnRight + spacings))
1289+ , expected = found.parent.bbox.right - (found.parent.bbox.padding.right + widthsOnRight + spacings)
1290+ }
1291+ ]
812
1292
813 _ ->
1293 _ ->
814− expectRoundedEquality
1294+ [ expectRoundedEquality
815− found.self.bbox.right
1295+ { found = found.self.bbox.right
816− (found.parent.bbox.right + found.parent.bbox.padding.right)
1296+ , expected = found.parent.bbox.right + found.parent.bbox.padding.right
1297+ }
1298+ ]
817 }
1299 }
818
1300
819
1301
@@ -823,30 +1305,35 @@ alignTop =
823 Testable.LabeledTest
1305 Testable.LabeledTest
824 { label = "alignTop"
1306 { label = "alignTop"
825 , attr = Element.alignTop
1307 , attr = Element.alignTop
1308+ , id = Testable.NoId
826 , test =
1309 , test =
827− \found _ ->
1310+ \found ->
828− if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
1311+ if List.member found.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
829− Expect.true "alignTop doesn't apply to elements that are above or below" True
1312+ [ Testable.true "alignTop doesn't apply to elements that are above or below" True ]
830
1313
831 else if
1314 else if
832− List.member found.location
1315+ List.member found.parentLayout
833 [ Testable.IsNearby Testable.InFront
1316 [ Testable.IsNearby Testable.InFront
834 , Testable.IsNearby Testable.Behind
1317 , Testable.IsNearby Testable.Behind
835 , Testable.IsNearby Testable.OnRight
1318 , Testable.IsNearby Testable.OnRight
836 , Testable.IsNearby Testable.OnLeft
1319 , Testable.IsNearby Testable.OnLeft
837 ]
1320 ]
838 then
1321 then
839− expectRoundedEquality
1322+ [ expectRoundedEquality
840− found.self.bbox.top
1323+ { found = found.self.bbox.top
841− found.parent.bbox.top
1324+ , expected = found.parent.bbox.top
1325+ }
1326+ ]
842
1327
843 else if List.length found.siblings == 0 then
1328 else if List.length found.siblings == 0 then
844− expectRoundedEquality
1329+ [ expectRoundedEquality
845− found.self.bbox.top
1330+ { found = found.self.bbox.top
846− (found.parent.bbox.top + found.parent.bbox.padding.top)
1331+ , expected = found.parent.bbox.top + found.parent.bbox.padding.top
1332+ }
1333+ ]
847
1334
848 else
1335 else
849− case found.location of
1336+ case found.parentLayout of
850 Testable.InColumn ->
1337 Testable.InColumn ->
851 let
1338 let
852 siblingsAbove =
1339 siblingsAbove =
@@ -860,14 +1347,18 @@ alignTop =
860 |> List.map (.height << .bbox)
1347 |> List.map (.height << .bbox)
861 |> List.sum
1348 |> List.sum
862 in
1349 in
863− expectRoundedEquality
1350+ [ expectRoundedEquality
864− found.self.bbox.top
1351+ { found = found.self.bbox.top
865− (found.parent.bbox.top + (found.parent.bbox.padding.top + heightsAbove + spacings))
1352+ , expected = found.parent.bbox.top + (found.parent.bbox.padding.top + heightsAbove + spacings)
1353+ }
1354+ ]
866
1355
867 _ ->
1356 _ ->
868− expectRoundedEquality
1357+ [ expectRoundedEquality
869− found.self.bbox.top
1358+ { found = found.self.bbox.top
870− found.parent.bbox.top
1359+ , expected = found.parent.bbox.top
1360+ }
1361+ ]
871 }
1362 }
872
1363
873
1364
@@ -877,30 +1368,35 @@ alignBottom =
877 Testable.LabeledTest
1368 Testable.LabeledTest
878 { label = "alignBottom"
1369 { label = "alignBottom"
879 , attr = Element.alignBottom
1370 , attr = Element.alignBottom
1371+ , id = Testable.NoId
880 , test =
1372 , test =
881− \found _ ->
1373+ \found ->
882− if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
1374+ if List.member found.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
883− Expect.true "alignBottom doesn't apply to elements that are above or below" True
1375+ [ Testable.true "alignBottom doesn't apply to elements that are above or below" True ]
884
1376
885 else if
1377 else if
886− List.member found.location
1378+ List.member found.parentLayout
887 [ Testable.IsNearby Testable.InFront
1379 [ Testable.IsNearby Testable.InFront
888 , Testable.IsNearby Testable.Behind
1380 , Testable.IsNearby Testable.Behind
889 , Testable.IsNearby Testable.OnRight
1381 , Testable.IsNearby Testable.OnRight
890 , Testable.IsNearby Testable.OnLeft
1382 , Testable.IsNearby Testable.OnLeft
891 ]
1383 ]
892 then
1384 then
893− expectRoundedEquality
1385+ [ expectRoundedEquality
894− found.self.bbox.bottom
1386+ { found = found.self.bbox.bottom
895− found.parent.bbox.bottom
1387+ , expected = found.parent.bbox.bottom
1388+ }
1389+ ]
896
1390
897 else if List.length found.siblings == 0 then
1391 else if List.length found.siblings == 0 then
898− expectRoundedEquality
1392+ [ expectRoundedEquality
899− found.self.bbox.bottom
1393+ { found = found.self.bbox.bottom
900− (found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
1394+ , expected = found.parent.bbox.bottom + found.parent.bbox.padding.bottom
1395+ }
1396+ ]
901
1397
902 else
1398 else
903− case found.location of
1399+ case found.parentLayout of
904 Testable.InColumn ->
1400 Testable.InColumn ->
905 let
1401 let
906 siblingsBelow =
1402 siblingsBelow =
@@ -914,20 +1410,31 @@ alignBottom =
914 |> List.map (.height << .bbox)
1410 |> List.map (.height << .bbox)
915 |> List.sum
1411 |> List.sum
916 in
1412 in
917− expectRoundedEquality
1413+ [ expectRoundedEquality
918− found.self.bbox.bottom
1414+ { found = found.self.bbox.bottom
919− (found.parent.bbox.bottom - (found.parent.bbox.padding.bottom + heightsBelow + spacings))
1415+ , expected = found.parent.bbox.bottom - (found.parent.bbox.padding.bottom + heightsBelow + spacings)
1416+ }
1417+ ]
920
1418
921 _ ->
1419 _ ->
922− expectRoundedEquality
1420+ [ expectRoundedEquality
923− found.self.bbox.bottom
1421+ { found = found.self.bbox.bottom
924− (found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
1422+ , expected = found.parent.bbox.bottom + found.parent.bbox.padding.bottom
1423+ }
1424+ ]
925 }
1425 }
926
1426
927
1427
928−expectRoundedEquality x y =
1428+expectRoundedEquality : { expected : Float, found : Float } -> Testable.LayoutExpectation
929− Expect.true ("within 1 of each other " ++ String.fromFloat x ++ ":" ++ String.fromFloat y)
1429+expectRoundedEquality { expected, found } =
930− (abs (x - y) < 1)
1430+ Testable.true
1431+ ("expected " ++ floatToString expected ++ ", found " ++ floatToString found)
1432+ (abs (expected - found) < 1)
1433+
1434+
1435+floatToString : Float -> String
1436+floatToString x =
1437+ String.fromFloat (toFloat (round (x * 100)) / 100)
931
1438
932
1439
933{-| -}
1440{-| -}
@@ -936,8 +1443,9 @@ centerY =
936 Testable.LabeledTest
1443 Testable.LabeledTest
937 { label = "centerY"
1444 { label = "centerY"
938 , attr = Element.centerY
1445 , attr = Element.centerY
1446+ , id = Testable.NoId
939 , test =
1447 , test =
940− \found _ ->
1448+ \found ->
941 let
1449 let
942 selfCenter =
1450 selfCenter =
943 found.self.bbox.top + (found.self.bbox.height / 2)
1451 found.self.bbox.top + (found.self.bbox.height / 2)
@@ -945,16 +1453,18 @@ centerY =
945 parentCenter =
1453 parentCenter =
946 found.parent.bbox.top + (found.parent.bbox.height / 2)
1454 found.parent.bbox.top + (found.parent.bbox.height / 2)
947 in
1455 in
948− if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
1456+ if List.member found.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
949− Expect.true "centerY doesn't apply to elements that are above or below" True
1457+ [ Testable.true "centerY doesn't apply to elements that are above or below" True ]
950
1458
951 else if List.length found.siblings == 0 then
1459 else if List.length found.siblings == 0 then
952− expectRoundedEquality
1460+ [ expectRoundedEquality
953− selfCenter
1461+ { found = selfCenter
954− parentCenter
1462+ , expected = parentCenter
1463+ }
1464+ ]
955
1465
956 else
1466 else
957− case found.location of
1467+ case found.parentLayout of
958 Testable.InColumn ->
1468 Testable.InColumn ->
959 let
1469 let
960 siblingsOnTop =
1470 siblingsOnTop =
@@ -984,12 +1494,18 @@ centerY =
984 / 2
1494 / 2
985 )
1495 )
986 in
1496 in
987− expectRoundedEquality
1497+ [ expectRoundedEquality
988− selfCenter
1498+ { found = selfCenter
989− expectedCenter
1499+ , expected = expectedCenter
1500+ }
1501+ ]
990
1502
991 _ ->
1503 _ ->
992− expectRoundedEquality selfCenter parentCenter
1504+ [ expectRoundedEquality
1505+ { found = selfCenter
1506+ , expected = parentCenter
1507+ }
1508+ ]
993 }
1509 }
994
1510
995
1511
@@ -1001,8 +1517,12 @@ above element =
1001 , element = element
1517 , element = element
1002 , label = "above"
1518 , label = "above"
1003 , test =
1519 , test =
1004− \found _ ->
1520+ \found ->
1005− expectRoundedEquality found.self.bbox.bottom found.parent.bbox.top
1521+ [ expectRoundedEquality
1522+ { found = found.self.bbox.bottom
1523+ , expected = found.parent.bbox.top
1524+ }
1525+ ]
1006 }
1526 }
1007
1527
1008
1528
@@ -1014,8 +1534,12 @@ below element =
1014 , element = element
1534 , element = element
1015 , label = "below"
1535 , label = "below"
1016 , test =
1536 , test =
1017− \found _ ->
1537+ \found ->
1018− expectRoundedEquality found.self.bbox.top found.parent.bbox.bottom
1538+ [ expectRoundedEquality
1539+ { found = found.self.bbox.top
1540+ , expected = found.parent.bbox.bottom
1541+ }
1542+ ]
1019 }
1543 }
1020
1544
1021
1545
@@ -1027,8 +1551,12 @@ onRight element =
1027 , element = element
1551 , element = element
1028 , label = "onRight"
1552 , label = "onRight"
1029 , test =
1553 , test =
1030− \found _ ->
1554+ \found ->
1031− expectRoundedEquality found.self.bbox.left found.parent.bbox.right
1555+ [ expectRoundedEquality
1556+ { found = found.self.bbox.left
1557+ , expected = found.parent.bbox.right
1558+ }
1559+ ]
1032 }
1560 }
1033
1561
1034
1562
@@ -1040,8 +1568,12 @@ onLeft element =
1040 , element = element
1568 , element = element
1041 , label = "onLeft"
1569 , label = "onLeft"
1042 , test =
1570 , test =
1043− \found _ ->
1571+ \found ->
1044− expectRoundedEquality found.self.bbox.right found.parent.bbox.left
1572+ [ expectRoundedEquality
1573+ { found = found.self.bbox.right
1574+ , expected = found.parent.bbox.left
1575+ }
1576+ ]
1045 }
1577 }
1046
1578
1047
1579
@@ -1057,11 +1589,12 @@ inFront element =
1057 , element = element
1589 , element = element
1058 , label = "inFront"
1590 , label = "inFront"
1059 , test =
1591 , test =
1060− withinHelper
1592+ \found ->
1593+ [ withinHelper found ]
1061 }
1594 }
1062
1595
1063
1596
1064−withinHelper found _ =
1597+withinHelper found =
1065 let
1598 let
1066 horizontalCheck =
1599 horizontalCheck =
1067 if found.self.bbox.width > found.parent.bbox.width then
1600 if found.self.bbox.width > found.parent.bbox.width then
@@ -1085,7 +1618,7 @@ withinHelper found _ =
1085 , compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
1618 , compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
1086 ]
1619 ]
1087 in
1620 in
1088− Expect.true "within the confines of the parent"
1621+ Testable.true "within the confines of the parent"
1089 (List.all ((==) True)
1622 (List.all ((==) True)
1090 (List.concat
1623 (List.concat
1091 [ horizontalCheck
1624 [ horizontalCheck
@@ -1103,5 +1636,88 @@ behindContent element =
1103 , element = element
1636 , element = element
1104 , label = "behindContent"
1637 , label = "behindContent"
1105 , test =
1638 , test =
1106− withinHelper
1639+ \found ->
1640+ [ withinHelper found ]
1107 }
1641 }
1642+
1643+
1644+
1645+{- Implicit width/height calcualtions -}
1646+
1647+
1648+implicitWidthHeightShrink attrs =
1649+ let
1650+ withImplicits =
1651+ implicitTest (widthHelper Nothing Nothing Shrink)
1652+ :: implicitTest (heightHelper Nothing Nothing Shrink)
1653+ :: attrs
1654+ in
1655+ skipOverridden withImplicits
1656+
1657+
1658+attrId attr =
1659+ case attr of
1660+ Testable.LabeledTest details ->
1661+ details.id
1662+
1663+ Testable.AttrTest details ->
1664+ details.id
1665+
1666+ _ ->
1667+ Testable.NoId
1668+
1669+
1670+skipOverridden ls =
1671+ List.foldr
1672+ (\attr ( found, caught ) ->
1673+ let
1674+ ( skip, has ) =
1675+ case attrId attr of
1676+ Testable.NoId ->
1677+ ( False, found )
1678+
1679+ Testable.IsWidth ->
1680+ ( found.hasWidth
1681+ , { hasWidth = True
1682+ , hasHeight = found.hasHeight
1683+ }
1684+ )
1685+
1686+ Testable.IsHeight ->
1687+ ( found.hasHeight
1688+ , { hasWidth = found.hasWidth
1689+ , hasHeight = True
1690+ }
1691+ )
1692+ in
1693+ ( has
1694+ , if skip then
1695+ caught
1696+
1697+ else
1698+ attr :: caught
1699+ )
1700+ )
1701+ ( { hasWidth = False
1702+ , hasHeight = False
1703+ }
1704+ , []
1705+ )
1706+ ls
1707+ |> Tuple.second
1708+ |> List.reverse
1709+
1710+
1711+{-| This is taking a normal test and rmoving its ability to attach a real `Element` attribute.
1712+-}
1713+implicitTest attr =
1714+ case attr of
1715+ Testable.LabeledTest details ->
1716+ Testable.AttrTest
1717+ { test = details.test
1718+ , label = "Implicit -> " ++ details.label
1719+ , id = details.id
1720+ }
1721+
1722+ otherwise ->
1723+ otherwise
tests-rendering/src/Testable/Element/Background.elm+5−4
@@ -5,23 +5,24 @@ module Testable.Element.Background exposing (color)
5import Dict
5import Dict
6import Element exposing (Color)
6import Element exposing (Color)
7import Element.Background as Background
7import Element.Background as Background
8−import Expect
9import Testable
8import Testable
10
9
11
10
12color : Color -> Testable.Attr msg
11color : Color -> Testable.Attr msg
13color clr =
12color clr =
14 Testable.LabeledTest
13 Testable.LabeledTest
15− { label = "background color-" ++ Testable.formatColor clr
14+ { label = "background color"
16 , attr = Background.color clr
15 , attr = Background.color clr
16+ , id = Testable.NoId
17 , test =
17 , test =
18− \context _ ->
18+ \context ->
19 let
19 let
20 selfBackgroundColor =
20 selfBackgroundColor =
21 context.self.style
21 context.self.style
22 |> Dict.get "background-color"
22 |> Dict.get "background-color"
23 |> Maybe.withDefault "notfound"
23 |> Maybe.withDefault "notfound"
24 in
24 in
25− Expect.true ("Expected color: " ++ (Testable.formatColor clr ++ " vs found:" ++ selfBackgroundColor))
25+ [ Testable.true ("expected " ++ (Testable.formatColor clr ++ ", found " ++ selfBackgroundColor))
26 (Testable.compareFormattedColor clr selfBackgroundColor)
26 (Testable.compareFormattedColor clr selfBackgroundColor)
27+ ]
27 }
28 }
tests-rendering/src/Testable/Element/Border.elmnew file+20
@@ -0,0 +1,20 @@
1+module Testable.Element.Border exposing (..)
2+
3+{-| -}
4+
5+import Dict
6+import Element exposing (Color)
7+import Element.Border as Border
8+import Element.Font as Font
9+import Expect
10+import Testable
11+
12+
13+color : Color -> Testable.Attr msg
14+color clr =
15+ Testable.Attr (Border.color clr)
16+
17+
18+width : Int -> Testable.Attr msg
19+width i =
20+ Testable.Attr (Border.width i)
tests-rendering/src/Testable/Element/Font.elm+14−6
@@ -1,29 +1,35 @@
1−module Testable.Element.Font exposing (color)
1+module Testable.Element.Font exposing (bold, color, size)
2
2
3{-| -}
3{-| -}
4
4
5import Dict
5import Dict
6import Element exposing (Color)
6import Element exposing (Color)
7import Element.Font as Font
7import Element.Font as Font
8−import Expect
9import Testable
8import Testable
10
9
11
10
11+bold : Testable.Attr msg
12+bold =
13+ Testable.Attr Font.bold
14+
15+
12color : Color -> Testable.Attr msg
16color : Color -> Testable.Attr msg
13color clr =
17color clr =
14 Testable.LabeledTest
18 Testable.LabeledTest
15 { label = "font color-" ++ Testable.formatColor clr
19 { label = "font color-" ++ Testable.formatColor clr
16 , attr = Font.color clr
20 , attr = Font.color clr
21+ , id = Testable.NoId
17 , test =
22 , test =
18− \context _ ->
23+ \context ->
19 let
24 let
20 selfFontColor =
25 selfFontColor =
21 context.self.style
26 context.self.style
22 |> Dict.get "color"
27 |> Dict.get "color"
23 |> Maybe.withDefault "notfound"
28 |> Maybe.withDefault "notfound"
24 in
29 in
25− Expect.true ("Color Match - " ++ (Testable.formatColor clr ++ " vs " ++ selfFontColor))
30+ [ Testable.true ("Color Match - " ++ (Testable.formatColor clr ++ " vs " ++ selfFontColor))
26 (Testable.compareFormattedColor clr selfFontColor)
31 (Testable.compareFormattedColor clr selfFontColor)
32+ ]
27 }
33 }
28
34
29
35
@@ -32,8 +38,9 @@ size i =
32 Testable.LabeledTest
38 Testable.LabeledTest
33 { label = "font size-" ++ String.fromInt i
39 { label = "font size-" ++ String.fromInt i
34 , attr = Font.size i
40 , attr = Font.size i
41+ , id = Testable.NoId
35 , test =
42 , test =
36− \context _ ->
43+ \context ->
37 let
44 let
38 selfFontSize =
45 selfFontSize =
39 context.self.style
46 context.self.style
@@ -43,6 +50,7 @@ size i =
43 formattedInt =
50 formattedInt =
44 String.fromInt i
51 String.fromInt i
45 in
52 in
46− Expect.true ("Size Match - " ++ (formattedInt ++ " vs " ++ selfFontSize))
53+ [ Testable.true ("Size Match - " ++ (formattedInt ++ " vs " ++ selfFontSize))
47 (formattedInt == selfFontSize)
54 (formattedInt == selfFontSize)
55+ ]
48 }
56 }
tests-rendering/src/Testable/FuzzMain.elm.olddeleted−632
@@ -1,632 +0,0 @@
1−port module FuzzMain exposing (Model, Msg(..), PossibleAttributes, Stage(..), Testable, TestableElement(..), allIndividualTestables, allPossibilities, analyze, attr, availableAttributes, content, createContainer, createElement, generateTest, generateTests, heightFill, heightPx, heights, init, layout, main, possibleToList, runTest, styles, subscriptions, testableLayout, tests, update, view, viewResult, widthFill, widthPx, widths)
2−
3−{-| -}
4−
5−import AnimationFrame
6−import Color
7−import Dict exposing (Dict)
8−import Element
9−import Element.Background as Background
10−import Element.Border as Border
11−import Element.Font as Font
12−import Expect
13−import Fuzz
14−import Html exposing (Html)
15−import List.Extra
16−import Random
17−import Test
18−import Test.Runner
19−import Test.Runner.Failure
20−import Testable
21−import Tests exposing (Testable)
22−import Time exposing (Time)
23−
24−
25−init : ( Model Msg, Cmd Msg )
26−init =
27− ( { current = Nothing
28− , upcoming =
29− -- Tests.tests
30− []
31−
32− -- generateTests (Random.initialSeed 227852860) 1
33− , finished = []
34− , stage = BeginRendering
35− }
36− , Cmd.none
37− )
38−
39−
40−main : Program Never (Model Msg) Msg
41−main =
42− Html.program
43− { init = init
44− , view = view
45− , update = update
46− , subscriptions = subscriptions
47− }
48−
49−
50−type alias Model msg =
51− { current : Maybe (Testable msg)
52− , upcoming : List (Testable msg)
53− , finished : List (Testable msg)
54− , stage : Stage
55− }
56−
57−
58−type Msg
59− = NoOp
60− | Tick Time
61− | RefreshBoundingBox
62− (List
63− { id : String
64− , bbox : Testable.BoundingBox
65− , style : Testable.Style
66− }
67− )
68−
69−
70−type Stage
71− = Rendered
72− | BeginRendering
73− | GatherData
74− | Finished
75−
76−
77−runTest : Dict String Testable.Found -> Testable msg -> Testable msg
78−runTest boxes test =
79− let
80− tests =
81− Testable.toTest test.label boxes test.element
82−
83− seed =
84− Random.initialSeed 227852860
85−
86− results =
87− Testable.runTests seed (Debug.log "created Tests" tests)
88− |> Debug.log "run tests"
89− in
90− { test | results = Just results }
91−
92−
93−generateTests : Random.Seed -> Int -> List (Testable msg)
94−generateTests seed num =
95− if num <= 0 then
96− []
97−
98− else
99− let
100− ( testable, newSeed ) =
101− generateTest seed
102− in
103− testable :: generateTests newSeed (num - 1)
104−
105−
106−generateTest : Random.Seed -> ( Testable msg, Random.Seed )
107−generateTest seed =
108− testableLayout
109− |> Test.Runner.fuzz
110− |> flip Random.step seed
111− |> (\( ( val, shrinker ), newSeed ) -> ( val, seed ))
112−
113−
114−update : Msg -> Model Msg -> ( Model Msg, Cmd Msg )
115−update msg model =
116− case msg of
117− NoOp ->
118− ( model, Cmd.none )
119−
120− RefreshBoundingBox boxes ->
121− case model.current of
122− Nothing ->
123− ( { model | stage = Finished }
124− , Cmd.none
125− )
126−
127− Just current ->
128− let
129− toTuple box =
130− ( box.id, { style = box.style, bbox = box.bbox } )
131−
132− foundData =
133− boxes
134− |> List.map toTuple
135− |> Dict.fromList
136−
137− currentResults =
138− runTest foundData current
139− in
140− case model.upcoming of
141− [] ->
142− ( { model
143− | stage = Finished
144− , current = Nothing
145− , finished = currentResults :: model.finished
146− }
147− , Cmd.none
148− )
149−
150− newCurrent :: remaining ->
151− ( { model
152− | finished = currentResults :: model.finished
153− , stage = BeginRendering
154− }
155− , Cmd.none
156− )
157−
158− Tick time ->
159− case model.stage of
160− BeginRendering ->
161− case model.upcoming of
162− [] ->
163− ( { model | stage = Rendered }
164− , Cmd.none
165− )
166−
167− current :: remaining ->
168− ( { model
169− | stage = Rendered
170− , upcoming = remaining
171− , current = Just current
172− }
173− , Cmd.none
174− )
175−
176− Rendered ->
177− case model.current of
178− Nothing ->
179− ( { model | stage = Finished }
180− , Cmd.none
181− )
182−
183− Just test ->
184− ( { model | stage = GatherData }
185− , analyze (Testable.getIds test.element)
186− )
187−
188− _ ->
189− ( { model | stage = Rendered }
190− , Cmd.none
191− )
192−
193−
194−subscriptions : { a | stage : Stage } -> Sub Msg
195−subscriptions model =
196− Sub.batch
197− [ styles RefreshBoundingBox
198− , case model.stage of
199− BeginRendering ->
200− AnimationFrame.times Tick
201−
202− Rendered ->
203− AnimationFrame.times Tick
204−
205− _ ->
206− Sub.none
207− ]
208−
209−
210−port analyze : List String -> Cmd msg
211−
212−
213−port styles : (List { id : String, bbox : Testable.BoundingBox, style : Testable.Style } -> msg) -> Sub msg
214−
215−
216−view : Model Msg -> Html Msg
217−view model =
218− case model.current of
219− Nothing ->
220− if model.stage == Finished then
221− Element.layout [] <|
222− Element.column
223− [ Element.spacing 20
224− , Element.padding 20
225− , Element.width (Element.px 800)
226−
227− -- , Background.color Color.grey
228− ]
229− (List.map viewResult model.finished)
230−
231− else
232− Html.text "running?"
233−
234− Just current ->
235− Testable.render current.element
236−
237−
238−viewResult : Testable Msg -> Element.Element Msg
239−viewResult testable =
240− let
241− viewSingle result =
242− case result of
243− ( label, Nothing ) ->
244− Element.el
245− [ Background.color Color.green
246− , Font.color Color.white
247− , Element.paddingXY 20 10
248− , Element.alignLeft
249− , Border.rounded 3
250− ]
251− <|
252− Element.text (label ++ " - " ++ "Success!")
253−
254− ( label, Just { given, description, reason } ) ->
255− Element.row
256− [ Background.color Color.red
257− , Font.color Color.white
258− , Element.paddingXY 20 10
259− , Element.alignLeft
260− , Element.spacing 25
261− , Border.rounded 3
262− ]
263− [ Element.el [ Element.width Element.fill ] <| Element.text label
264−
265− -- , Element.el [ Element.width Element.fill ] <| Element.text description
266− , Element.el [ Element.width Element.fill ] <| Element.text (toString reason)
267− ]
268− in
269− Element.column
270− [ Border.width 1
271− , Border.color Color.lightGrey
272− , Element.padding 20
273− , Element.height Element.shrink
274− , Element.alignLeft
275− ]
276− [ Element.el [ Font.bold ] (Element.text testable.label)
277− , Element.column [ Element.alignLeft, Element.spacing 20 ]
278− (case testable.results of
279− Nothing ->
280− [ Element.text "no results" ]
281−
282− Just results ->
283− List.map viewSingle results
284− )
285− ]
286−
287−
288−{--}
289−type alias Testable msg =
290− { element : Testable.Element msg
291− , label : String
292− , results :
293− Maybe
294− (List
295− ( String
296− , Maybe
297− { given : Maybe String
298− , description : String
299− , reason : Test.Runner.Failure.Reason
300− }
301− )
302− )
303− }
304−
305−
306−attr tag label attr test =
307− Testable.Batch
308− [ Testable.Attr attr
309− , Testable.AttrTest
310− (\found ->
311− Test.test (tag ++ ": " ++ label) <|
312− \_ -> test found
313− )
314− ]
315−
316−
317−widthFill tag =
318− attr tag
319− "width fill"
320− (Element.width Element.fill)
321− (\context ->
322− let
323− spacePerPortion =
324− context.parent.bbox.width / toFloat (List.length context.siblings + 1)
325− in
326− Expect.equal spacePerPortion context.self.bbox.width
327− )
328−
329−
330−heightFill tag =
331− attr tag
332− "height fill"
333− (Element.height Element.fill)
334− (\context ->
335− let
336− spacePerPortion =
337− context.parent.bbox.height / toFloat (List.length context.siblings + 1)
338− in
339− Expect.equal spacePerPortion context.self.bbox.height
340− )
341−
342−
343−heightPx px tag =
344− attr tag
345− ("height at " ++ toString px ++ "px")
346− (Element.height (Element.px (round px)))
347− (\found ->
348− Expect.equal found.self.bbox.height px
349− )
350−
351−
352−widthPx px tag =
353− attr tag
354− ("width at " ++ toString px ++ "px")
355− (Element.width (Element.px (round px)))
356− (\found ->
357− Expect.equal found.self.bbox.width px
358− )
359−
360−
361−type alias PossibleAttributes msg =
362− { width : String -> Testable.Attr msg
363− , height : String -> Testable.Attr msg
364− }
365−
366−
367−possibleToList str { width, height } =
368− width str :: height str :: []
369−
370−
371−availableAttributes : { width : Float, height : Float } -> Fuzz.Fuzzer (PossibleAttributes msg)
372−availableAttributes bounds =
373− Fuzz.map2 PossibleAttributes
374− (Fuzz.oneOf
375− (List.map Fuzz.constant (widths bounds.width))
376− )
377− (Fuzz.oneOf
378− (List.map Fuzz.constant (heights bounds.height))
379− )
380−
381−
382−widths : Float -> List (String -> Testable.Attr msg)
383−widths width =
384− [ \tag ->
385− attr tag
386− (" with width at " ++ toString width ++ "px")
387− (Element.width (Element.px (round width)))
388− (\found ->
389− Expect.equal found.self.bbox.width width
390− )
391− , \tag ->
392− attr tag
393− " with width fill"
394− (Element.width Element.fill)
395− (\context ->
396− let
397− -- {-|
398− -- Calculation is
399− -- totalparentwidth - (paddingLeft ++ paddingRight)
400− -- minus widths of all the other children
401− -- -}
402− otherWidth =
403− -- Debug.log "width" <|
404− List.sum <|
405− -- Debug.log "widths" <|
406− List.map (\child -> child.bbox.width) context.siblings
407−
408− spacePerPortion =
409− context.parent.bbox.width - otherWidth
410− in
411− Expect.equal spacePerPortion context.self.bbox.width
412− )
413− ]
414−
415−
416−heights : Float -> List (String -> Testable.Attr msg)
417−heights height =
418− [ \tag ->
419− attr tag
420− (" with height at " ++ toString height ++ "px")
421− (Element.height (Element.px (round height)))
422− (\found ->
423− Expect.equal found.self.bbox.height height
424− )
425− , \tag ->
426− attr tag
427− " with height fill"
428− (Element.height Element.fill)
429− (\context ->
430− let
431− -- {-|
432− -- Calculation is
433− -- totalparentwidth - (paddingLeft ++ paddingRight)
434− -- minus widths of all the other children
435− -- -}
436− otherHeight =
437− List.sum <|
438− List.map (\child -> child.bbox.height) context.siblings
439−
440− spacePerPortion =
441− context.parent.bbox.height - otherHeight
442− in
443− Expect.equal spacePerPortion context.self.bbox.height
444− )
445− ]
446−
447−
448−content : Testable.Element msg
449−content =
450− Testable.El
451− [ Testable.Attr <| Element.width (Element.px 20)
452− , Testable.Attr <| Element.height (Element.px 20)
453− ]
454− Testable.Empty
455−
456−
457−tests : List (Testable msg)
458−tests =
459− [ { label = "Width/Height 200"
460− , results = Nothing
461− , element =
462− Testable.El
463− [ widthPx 200 "single element"
464− , heightPx 200 "single element"
465− ]
466− Testable.Empty
467− }
468− , { label = "Width Fill/Height Fill - 1"
469− , results = Nothing
470− , element =
471− Testable.El
472− [ widthFill "single element"
473− , heightFill "single element"
474− ]
475− <|
476− content
477− }
478− , { label = "Width Fill/Height Fill - 2"
479− , results = Nothing
480− , element =
481− Testable.El
482− [ widthFill "top element"
483− , heightFill "top element"
484− ]
485− <|
486− Testable.El
487− [ widthFill "embedded element"
488− , heightFill "embedded element"
489− ]
490− <|
491− content
492− }
493− , { label = "Row Width Fill/Height Fill"
494− , results = Nothing
495− , element =
496− Testable.Row
497− [ widthFill "on row"
498− , heightFill "on row"
499− ]
500− [ Testable.El
501− [ widthFill "row - 1"
502− , heightFill "row - 1"
503− ]
504− content
505− , Testable.El
506− [ widthFill "row - 2"
507− , heightFill "row - 2"
508− ]
509− content
510− , Testable.El
511− [ widthFill "row - 3"
512− , heightFill "row - 3"
513− ]
514− content
515− ]
516− }
517− ]
518−
519−
520−
521−{- Generating A Complete Test Suite
522−
523−-}
524−
525−
526−type TestableElement
527− = El
528− | Row
529− | Column
530−
531−
532−allIndividualTestables : List (Fuzz.Fuzzer TestableElement)
533−allIndividualTestables =
534− [ Fuzz.constant El
535− , Fuzz.constant Row
536− , Fuzz.constant Column
537− ]
538−
539−
540−testableLayout : Fuzz.Fuzzer (Testable msg)
541−testableLayout =
542− let
543− asTestable layout =
544− { element = layout
545− , label = "A Sweet Test"
546− , results = Nothing
547− }
548− in
549− Fuzz.map asTestable layout
550−
551−
552−layout : Fuzz.Fuzzer (Testable.Element msg)
553−layout =
554− Fuzz.map4 createContainer
555− (Fuzz.tuple3
556− ( availableAttributes { width = 20, height = 20 }
557− , availableAttributes { width = 20, height = 20 }
558− , availableAttributes { width = 20, height = 20 }
559− )
560− )
561− (availableAttributes { width = 200, height = 200 })
562− (Fuzz.oneOf allIndividualTestables)
563− (Fuzz.oneOf allIndividualTestables)
564−
565−
566−createElement : String -> TestableElement -> PossibleAttributes msg -> Testable.Element msg
567−createElement tag layout possibleAttrs =
568− let
569− attrs =
570− possibleToList tag possibleAttrs
571− in
572− case layout of
573− El ->
574− Testable.El attrs content
575−
576− Row ->
577− Testable.Row attrs
578− [ content
579− , content
580− , content
581− ]
582−
583− Column ->
584− Testable.Column attrs
585− [ content
586− , content
587− , content
588− ]
589−
590−
591−createContainer : ( PossibleAttributes msg, PossibleAttributes msg, PossibleAttributes msg ) -> PossibleAttributes msg -> TestableElement -> TestableElement -> Testable.Element msg
592−createContainer ( a1, a2, a3 ) possibleAttrs parent child =
593− let
594− attrs =
595− possibleToList "0" possibleAttrs
596− in
597− case parent of
598− El ->
599− Testable.El attrs (createElement "0-0" child a1)
600−
601− Row ->
602− Testable.Row attrs
603− [ createElement "0-0" child a1
604− , createElement "0-1" child a2
605− , createElement "0-2" child a3
606− ]
607−
608− Column ->
609− Testable.Column attrs
610− [ createElement "0-0" child a1
611− , createElement "0-1" child a2
612− , createElement "0-2" child a3
613− ]
614−
615−
616−{-| Given a list of a list of possibilities,
617−
618−choose once from each list.
619−
620− [ [width (px 100), width fill]
621− , [height (px 100), height fill]
622− ]
623− ->
624−
625− [ [width (px 100), height (px 100)]
626− , [width (px 100), height fill]
627− ]
628−
629−-}
630−allPossibilities : List (List a) -> List (List a)
631−allPossibilities =
632− List.Extra.cartesianProduct
tests-rendering/src/Testable/Generator.elmnew file+398
@@ -0,0 +1,398 @@
1+module Testable.Generator exposing (..)
2+
3+{-| -}
4+
5+import Testable
6+import Testable.Element exposing (..)
7+import Testable.Element.Background as Background
8+import Testable.Element.Font as Font
9+
10+
11+{-| Given a list of attributes, generate every context this list of attributes could be in.
12+
13+So, this means,
14+
15+ - every element type
16+ - in every element type
17+
18+-}
19+element : String -> List (Testable.Attr msg) -> List ( String, Testable.Element msg )
20+element label attrs =
21+ -- [ ( label, paragraph attrs [ short ] )
22+ -- ]
23+ mapEveryCombo
24+ (\( selfLabel, makeSelf ) ( childLabel, child ) ->
25+ ( label ++ " - " ++ selfLabel ++ " > " ++ childLabel
26+ , makeSelf attrs child
27+ )
28+ )
29+ layouts
30+ contents
31+
32+
33+{-| Given a list of attributes, generate every context this list of attributes could be in.
34+
35+So, this means,
36+
37+ - every element type
38+ - in every element type
39+
40+-}
41+elementInLayout : String -> List (Testable.Attr msg) -> List ( String, Testable.Element msg )
42+elementInLayout label attrs =
43+ mapEveryCombo3
44+ (\( layoutLabel, makeLayout ) ( selfLabel, makeSelf ) ( childLabel, child ) ->
45+ ( label ++ " - " ++ layoutLabel ++ " > " ++ selfLabel ++ " > " ++ childLabel
46+ , makeLayout [] (makeSelf attrs child)
47+ )
48+ )
49+ layouts
50+ layouts
51+ contents
52+
53+
54+{-| Sometimes we want to try a whole bunch of combinations of attributes
55+
56+This makes it a bit easier to construct
57+
58+-}
59+elementWith : String -> List ( String, List (Testable.Attr msg) ) -> List ( String, Testable.Element msg )
60+elementWith label labelAttrs =
61+ mapEveryCombo3
62+ (\( attrLabel, attrs ) ( selfLabel, makeSelf ) ( childLabel, child ) ->
63+ ( label ++ " - " ++ selfLabel ++ " with " ++ attrLabel ++ " > " ++ childLabel
64+ , makeSelf attrs child
65+ )
66+ )
67+ labelAttrs
68+ layouts
69+ contents
70+
71+
72+{-| This varies the layout element that an element is in.
73+-}
74+elementInLayoutWith :
75+ String
76+ ->
77+ List
78+ ( String
79+ , { self : List (Testable.Attr msg)
80+ , parent : List (Testable.Attr msg)
81+ }
82+ )
83+ -> List ( String, Testable.Element msg )
84+elementInLayoutWith label labelAttrs =
85+ mapEveryCombo4
86+ (\( attrLabel, attrs ) ( layoutLabel, makeLayout ) ( selfLabel, makeSelf ) ( childLabel, child ) ->
87+ ( label ++ " - " ++ layoutLabel ++ " > " ++ selfLabel ++ " with " ++ attrLabel ++ " > " ++ childLabel
88+ , makeLayout attrs.parent (makeSelf attrs.self child)
89+ )
90+ )
91+ labelAttrs
92+ allLayouts
93+ layouts
94+ contents
95+
96+
97+layouts =
98+ [ ( "el"
99+ , \attrs child ->
100+ el attrs child
101+ )
102+ , ( "row"
103+ , \attrs child ->
104+ row attrs [ child ]
105+ )
106+ , ( "col"
107+ , \attrs child ->
108+ column attrs [ child ]
109+ )
110+ , ( "para"
111+ , \attrs child ->
112+ paragraph attrs [ child ]
113+ )
114+ , ( "txtCol"
115+ , \attrs child ->
116+ textColumn attrs [ child ]
117+ )
118+ ]
119+
120+
121+allElements =
122+ [ ( "el"
123+ , \attrs child ->
124+ el attrs child
125+ )
126+ , ( "row"
127+ , \attrs child ->
128+ row attrs [ child ]
129+ )
130+ , ( "col"
131+ , \attrs child ->
132+ column attrs [ child ]
133+ )
134+ , ( "para"
135+ , \attrs child ->
136+ paragraph attrs [ child ]
137+ )
138+ , ( "txtCol"
139+ , \attrs child ->
140+ textColumn attrs [ child ]
141+ )
142+ ]
143+
144+
145+allLayouts =
146+ [ ( "el"
147+ , \attrs child ->
148+ el attrs child
149+ )
150+ , ( "row 3, self 1"
151+ , \attrs child ->
152+ row attrs [ child, box, box ]
153+ )
154+ , ( "row 3, self 2"
155+ , \attrs child ->
156+ row attrs [ box, child, box ]
157+ )
158+ , ( "row 3, self 3"
159+ , \attrs child ->
160+ row attrs [ box, box, child ]
161+ )
162+ , ( "col 3, self 1"
163+ , \attrs child ->
164+ column attrs [ child, box, box ]
165+ )
166+ , ( "col 3, self 2"
167+ , \attrs child ->
168+ column attrs [ box, child, box ]
169+ )
170+ , ( "col 3, self 3"
171+ , \attrs child ->
172+ column attrs [ box, box, child ]
173+ )
174+ , ( "para"
175+ , \attrs child ->
176+ paragraph attrs [ child ]
177+ )
178+ , ( "txtCol"
179+ , \attrs child ->
180+ textColumn attrs [ child ]
181+ )
182+ ]
183+
184+
185+nearbys =
186+ [ ( "inFront", inFront )
187+ , ( "above", above )
188+ , ( "onLeft", onLeft )
189+ , ( "onRight", onRight )
190+ , ( "below", below )
191+ , ( "behindContent", behindContent )
192+ ]
193+
194+
195+alignments =
196+ horizontal ++ vertical
197+
198+
199+horizontal =
200+ [ Tuple.pair "alignLeft" alignLeft
201+ , Tuple.pair "alignRight" alignRight
202+ , Tuple.pair "centerX" centerX
203+ ]
204+
205+
206+vertical =
207+ [ Tuple.pair "alignTop" alignTop
208+ , Tuple.pair "alignBottom" alignBottom
209+ , Tuple.pair "centerY" centerY
210+ ]
211+
212+
213+extractLabel maybe =
214+ maybe
215+
216+
217+allAlignments =
218+ mapEveryCombo
219+ (\maybeOne maybeTwo ->
220+ let
221+ labels =
222+ List.filterMap (Maybe.map Tuple.first)
223+ [ maybeOne
224+ , maybeTwo
225+ ]
226+
227+ attrs =
228+ List.filterMap (Maybe.map Tuple.second)
229+ [ maybeOne
230+ , maybeTwo
231+ ]
232+ in
233+ Tuple.pair (String.join "++" labels)
234+ attrs
235+ )
236+ (Nothing :: List.map Just horizontal)
237+ (Nothing :: List.map Just vertical)
238+
239+
240+contents =
241+ [ ( "none", none )
242+ , ( "short text", text short )
243+
244+ -- , ( "long text", text lorem )
245+ , ( "box"
246+ , box
247+ )
248+ ]
249+
250+
251+box =
252+ el
253+ [ width (px 50)
254+ , height (px 50)
255+ , Background.color (rgb (240 / 255) 0 (245 / 255))
256+ , Font.color (rgb 1 1 1)
257+ ]
258+ none
259+
260+
261+short =
262+ "short and small"
263+
264+
265+lorem =
266+ "There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc."
267+
268+
269+mapEveryCombo fn listOne listTwo =
270+ List.concatMap
271+ (\one ->
272+ List.map
273+ (\two ->
274+ fn one two
275+ )
276+ listTwo
277+ )
278+ listOne
279+
280+
281+mapEveryCombo3 fn listOne listTwo listThree =
282+ List.concatMap
283+ (\one ->
284+ List.concatMap
285+ (\two ->
286+ List.map
287+ (\three ->
288+ fn one two three
289+ )
290+ listThree
291+ )
292+ listTwo
293+ )
294+ listOne
295+
296+
297+mapEveryCombo4 fn listOne listTwo listThree listFour =
298+ List.concatMap
299+ (\one ->
300+ List.concatMap
301+ (\two ->
302+ List.concatMap
303+ (\three ->
304+ List.map
305+ (\four ->
306+ fn one two three four
307+ )
308+ listFour
309+ )
310+ listThree
311+ )
312+ listTwo
313+ )
314+ listOne
315+
316+
317+sizes render =
318+ List.concatMap
319+ (\( widthLen, heightLen ) ->
320+ [ text (Debug.toString ( widthLen, heightLen ))
321+ , render
322+ (\attrs children ->
323+ el
324+ (width widthLen
325+ :: height heightLen
326+ :: attrs
327+ )
328+ children
329+ )
330+ ]
331+ )
332+ allLengthPairs
333+
334+
335+allLengthPairs : List ( Length, Length )
336+allLengthPairs =
337+ let
338+ crossProduct len =
339+ List.map (Tuple.pair len) lengths
340+ in
341+ List.concatMap crossProduct lengths
342+
343+
344+lengths =
345+ [ px 50
346+ , fill
347+ , shrink
348+ , fill
349+ |> maximum 100
350+ , fill
351+ |> maximum 100
352+ |> minimum 50
353+ , shrink
354+ |> maximum 100
355+ , shrink
356+ |> maximum 100
357+ |> minimum 50
358+ ]
359+
360+
361+type Generated thing
362+ = Generated (List (Labeled thing))
363+
364+
365+type alias Labeled thing =
366+ ( String, thing )
367+
368+
369+with : List (Labeled a) -> List (Labeled (a -> b)) -> List (Labeled b)
370+with first funcs =
371+ List.concatMap
372+ (\( lbl, fst ) ->
373+ List.map
374+ (\( fnLbl, fn ) ->
375+ Tuple.pair (fnLbl ++ "+" ++ lbl) (fn fst)
376+ )
377+ funcs
378+ )
379+ first
380+
381+
382+generate : String -> generated -> List (Labeled generated)
383+generate lbl fn =
384+ [ Tuple.pair lbl fn
385+ ]
386+
387+
388+test : List (Labeled (Testable.Element msg))
389+test =
390+ generate "Alignments"
391+ (\align ->
392+ el align none
393+ )
394+ |> with allAlignments
395+
396+
397+
398+-- |>
tests-rendering/src/Testable/Runner.elm+358−221
@@ -1,35 +1,55 @@
1−port module Testable.Runner exposing (TestableProgram, program, show)
1+port module Testable.Runner exposing
2+ ( Msg
3+ , Testable
4+ , TestableProgram
5+ , program
6+ , rename
7+ , show
8+ , testable
9+ )
2
10
3{-| -}
11{-| -}
4
12
5import Browser
13import Browser
6−import Char
7import Dict exposing (Dict)
14import Dict exposing (Dict)
8import Element
15import Element
9import Element.Background as Background
16import Element.Background as Background
10import Element.Border as Border
17import Element.Border as Border
18+import Element.Events as Events
11import Element.Font as Font
19import Element.Font as Font
12import Html exposing (Html)
20import Html exposing (Html)
13−import Parser exposing ((|.), (|=))
21+import Html.Attributes
14import Process
22import Process
15−import Random
16−import Set
17import Task
23import Task
18−import Test.Runner
19−import Test.Runner.Failure as Failure
20import Testable
24import Testable
21import Time
25import Time
22
26
23
27
24show : Testable.Element msg -> Html msg
28show : Testable.Element msg -> Html msg
25show =
29show =
26− Testable.render
30+ Testable.toHtml
27
31
28
32
29type alias TestableProgram =
33type alias TestableProgram =
30 Program () (Model Msg) Msg
34 Program () (Model Msg) Msg
31
35
32
36
37+{-| This should be a real type, but I'm too busy atm.
38+-}
39+type alias Testable =
40+ ( String, Testable.Element Msg )
41+
42+
43+rename : String -> Testable -> Testable
44+rename newName ( _, el ) =
45+ ( newName, el )
46+
47+
48+testable : String -> Testable.Element Msg -> Testable
49+testable =
50+ Tuple.pair
51+
52+
33palette =
53palette =
34 { white = Element.rgb 1 1 1
54 { white = Element.rgb 1 1 1
35 , red = Element.rgb 1 0 0
55 , red = Element.rgb 1 0 0
@@ -39,7 +59,7 @@ palette =
39 }
59 }
40
60
41
61
42−program : List ( String, Testable.Element Msg ) -> TestableProgram
62+program : List Testable -> TestableProgram
43program tests =
63program tests =
44 let
64 let
45 ( current, upcoming ) =
65 ( current, upcoming ) =
@@ -50,15 +70,17 @@ program tests =
50 cur :: remaining ->
70 cur :: remaining ->
51 ( Just cur, remaining )
71 ( Just cur, remaining )
52 in
72 in
53− Browser.document
73+ Browser.element
54 { init =
74 { init =
55 always
75 always
56 ( { current = current
76 ( { current = current
57 , upcoming = upcoming
77 , upcoming = upcoming
58 , finished = []
78 , finished = []
79+ , selected = 0
80+ , highlightDomId = Nothing
59 }
81 }
60 , Task.perform (always Analyze)
82 , Task.perform (always Analyze)
61− (Process.sleep 32
83+ (Process.sleep 64
62 |> Task.andThen
84 |> Task.andThen
63 (always Time.now)
85 (always Time.now)
64 )
86 )
@@ -80,56 +102,54 @@ type alias Model msg =
80 { current : Maybe ( String, Testable.Element msg )
102 { current : Maybe ( String, Testable.Element msg )
81 , upcoming : List ( String, Testable.Element msg )
103 , upcoming : List ( String, Testable.Element msg )
82 , finished : List (WithResults (Testable.Element msg))
104 , finished : List (WithResults (Testable.Element msg))
105+ , selected : Int
106+ , highlightDomId : Maybe String
83 }
107 }
84
108
85
109
86type alias WithResults thing =
110type alias WithResults thing =
87− { element : thing
111+ { index : Int
112+ , element : thing
88 , label : String
113 , label : String
89 , results :
114 , results :
90− List
115+ List Testable.LayoutTest
91− ( String
92− , Maybe
93− { given : Maybe String
94− , description : String
95− , reason : Failure.Reason
96− }
97− )
98 }
116 }
99
117
100
118
101−prepareResults :
119+encodeForReport :
102 List (WithResults (Testable.Element msg))
120 List (WithResults (Testable.Element msg))
103 ->
121 ->
104 List
122 List
105 { label : String
123 { label : String
106 , results :
124 , results :
107 List
125 List
108− ( String
126+ { description : String
109− , Maybe
127+ , passing : Bool
110− { given : Maybe String
128+ , todo : Bool
111− , description : String
129+ }
112− }
113− )
114 }
130 }
115−prepareResults withResults =
131+encodeForReport withResults =
116 let
132 let
117− prepareNode ( x, maybeResult ) =
133+ prepareExpectation layoutTest exp =
118− ( x
134+ case exp of
119− , case maybeResult of
135+ Testable.Todo description ->
120− Nothing ->
136+ { description = description
121− Nothing
137+ , passing = False
138+ , todo = True
139+ }
122
140
123− Just res ->
141+ Testable.Expect details ->
124− Just
142+ { description = details.description
125− { given = res.given
143+ , passing = details.result
126− , description = res.description
144+ , todo = False
127− }
145+ }
128− )
146+
147+ prepareNode layoutTest =
148+ List.map (prepareExpectation layoutTest) layoutTest.expectations
129
149
130 prepare { label, results } =
150 prepare { label, results } =
131 { label = label
151 { label = label
132− , results = List.map prepareNode results
152+ , results = List.concatMap prepareNode results
133 }
153 }
134 in
154 in
135 List.map prepare withResults
155 List.map prepare withResults
@@ -138,29 +158,27 @@ prepareResults withResults =
138type Msg
158type Msg
139 = NoOp
159 = NoOp
140 | Analyze
160 | Analyze
161+ | Select Int
162+ | HighlightDomID (Maybe String)
141 | RefreshBoundingBox
163 | RefreshBoundingBox
142 (List
164 (List
143 { id : String
165 { id : String
144 , bbox : Testable.BoundingBox
166 , bbox : Testable.BoundingBox
145− , isVisible : Bool
146 , style : List ( String, String )
167 , style : List ( String, String )
168+ , isVisible : Bool
169+ , textMetrics : List Testable.TextMetrics
147 }
170 }
148 )
171 )
149
172
150
173
151−runTest : Dict String Testable.Found -> String -> Testable.Element msg -> WithResults (Testable.Element msg)
174+runTest : Int -> Dict String Testable.Found -> String -> Testable.Element msg -> WithResults (Testable.Element msg)
152−runTest boxes label element =
175+runTest index boxes label element =
153 let
176 let
154− tests =
155− Testable.toTest label boxes element
156−
157− seed =
158− Random.initialSeed 227852860
159−
160 results =
177 results =
161− Testable.runTests seed tests
178+ Testable.runTests boxes element
162 in
179 in
163− { element = element
180+ { index = index
181+ , element = element
164 , label = label
182 , label = label
165 , results = results
183 , results = results
166 }
184 }
@@ -172,6 +190,16 @@ update msg model =
172 NoOp ->
190 NoOp ->
173 ( model, Cmd.none )
191 ( model, Cmd.none )
174
192
193+ HighlightDomID newId ->
194+ ( { model | highlightDomId = newId }
195+ , Cmd.none
196+ )
197+
198+ Select index ->
199+ ( { model | selected = index }
200+ , Cmd.none
201+ )
202+
175 RefreshBoundingBox boxes ->
203 RefreshBoundingBox boxes ->
176 case model.current of
204 case model.current of
177 Nothing ->
205 Nothing ->
@@ -186,6 +214,7 @@ update msg model =
186 , { style = Dict.fromList box.style
214 , { style = Dict.fromList box.style
187 , bbox = box.bbox
215 , bbox = box.bbox
188 , isVisible = box.isVisible
216 , isVisible = box.isVisible
217+ , textMetrics = box.textMetrics
189 }
218 }
190 )
219 )
191
220
@@ -195,7 +224,7 @@ update msg model =
195 |> Dict.fromList
224 |> Dict.fromList
196
225
197 currentResults =
226 currentResults =
198− runTest foundData label current
227+ runTest (List.length model.finished) foundData label current
199 in
228 in
200 case model.upcoming of
229 case model.upcoming of
201 [] ->
230 [] ->
@@ -203,7 +232,7 @@ update msg model =
203 | current = Nothing
232 | current = Nothing
204 , finished = model.finished ++ [ currentResults ]
233 , finished = model.finished ++ [ currentResults ]
205 }
234 }
206− , report (prepareResults (currentResults :: model.finished))
235+ , report (encodeForReport (currentResults :: model.finished))
207 )
236 )
208
237
209 newCurrent :: remaining ->
238 newCurrent :: remaining ->
@@ -213,7 +242,7 @@ update msg model =
213 , upcoming = remaining
242 , upcoming = remaining
214 }
243 }
215 , Task.perform (always Analyze)
244 , Task.perform (always Analyze)
216− (Process.sleep 32
245+ (Process.sleep 64
217 |> Task.andThen
246 |> Task.andThen
218 (always Time.now)
247 (always Time.now)
219 )
248 )
@@ -232,204 +261,313 @@ update msg model =
232 )
261 )
233
262
234
263
235−view : Model Msg -> Browser.Document Msg
264+view : Model Msg -> Html.Html Msg
236view model =
265view model =
237 case model.current of
266 case model.current of
238 Nothing ->
267 Nothing ->
239 if List.isEmpty model.upcoming then
268 if List.isEmpty model.upcoming then
240− { title = "tests finished"
269+ let
241− , body =
270+ selected =
242− [ case model.finished of
271+ getByIndex model.selected model.finished
243− [] ->
272+ in
244− Element.layout [] <|
273+ Element.layout
245− Element.column
274+ [ Font.size 16
246− [ Element.spacing 20
275+ , Element.inFront (Element.html (viewElementHighlight model))
247− , Element.padding 20
276+ , Element.height Element.fill
248− , Element.width (Element.px 800)
249−
250− -- , Background.color Color.grey
251− ]
252− [ Element.none ]
253−
254− finished :: remaining ->
255− if False then
256− viewResultsInline finished
257−
258− else
259− Element.layout [] <|
260− Element.column
261− [ Element.spacing 20
262− , Element.padding 20
263− , Element.width (Element.px 800)
264− ]
265− (List.map viewResult (finished :: remaining))
266 ]
277 ]
267− }
278+ <|
279+ Element.row [ Element.width Element.fill, Element.height Element.fill ]
280+ [ Element.el
281+ [ Element.width
282+ (Element.fill
283+ |> Element.maximum 900
284+ )
285+ , Element.alignTop
286+ , Element.height Element.fill
287+ , Element.scrollbars
288+ ]
289+ (Element.el
290+ [ Element.centerX
291+ , Element.padding 100
292+ , Border.dashed
293+ , Border.width 2
294+ , Border.color palette.lightGrey
295+ , Font.size 20
296+ , Element.inFront
297+ (Element.el
298+ [ Font.size 14
299+ , Font.color palette.lightGrey
300+ ]
301+ (Element.text "test case")
302+ )
303+ ]
304+ (case selected of
305+ Nothing ->
306+ Element.text "nothing selected"
307+
308+ Just sel ->
309+ Testable.toElement sel.element
310+ )
311+ )
312+ , Element.column
313+ [ Element.spacing 20
314+ , Element.padding 20
315+ , Element.width Element.fill
316+ , Element.height Element.fill
317+ , Element.scrollbarY
318+ ]
319+ (model.finished
320+ |> List.sortBy hasFailure
321+ |> List.map (viewResult model.selected)
322+ )
323+ ]
268
324
269 else
325 else
270− { title = "tests finished"
326+ Html.text ""
271− , body = []
272− }
273
327
274 Just ( label, current ) ->
328 Just ( label, current ) ->
275− { title = "running"
329+ Testable.toHtml current
276− , body =
277− [ Testable.render current ]
278− }
279
330
280
331
281−viewResultsInline : WithResults (Testable.Element Msg) -> Html Msg
332+getByIndex i ls =
282−viewResultsInline testable =
333+ List.foldl
283− Html.div
334+ (\elem ( index, found ) ->
284− []
335+ if i == index then
285− [ viewResultsAnnotationStylesheet testable.results
336+ ( index + 1, Just elem )
286− , Testable.render testable.element
287− ]
288
337
338+ else
339+ ( index + 1, found )
340+ )
341+ ( 0, Nothing )
342+ ls
343+ |> Tuple.second
289
344
290−{-| Our ID is part of our label. This could be fixed farther down the chain, but I think it'd be pretty involved.
291
345
292−So, now we can just parse the id out of the label.
346+viewElementHighlight model =
347+ case model.highlightDomId of
348+ Nothing ->
349+ Html.text ""
293
350
294−-}
351+ Just highlightDomId ->
295−parseId str =
352+ let
296− str
353+ elementHighlight =
297− |> Parser.run
354+ highlightDomId ++ " { outline: solid black; }"
298− (Parser.succeed identity
299− |. Parser.chompWhile (\c -> c /= '#')
300− |= Parser.variable
301− { start = \c -> c == '#'
302− , inner = \c -> Char.isAlphaNum c || c == '-'
303− , reserved = Set.empty
304− }
305− )
306− |> Result.toMaybe
307
355
356+ testId =
357+ highlightDomId
358+ |> String.dropLeft 1
359+ |> String.append "#tests-"
308
360
309−viewResultsAnnotationStylesheet results =
361+ testHighlight =
310− let
362+ testId ++ " { outline: dashed black; }"
311− toStyleClass ( label, maybeFailure ) =
312− case maybeFailure of
313− Nothing ->
314− ""
315
363
316− Just failure ->
364+ styleSheet =
317− case parseId label of
365+ String.join "\n"
318− Nothing ->
366+ [ elementHighlight
319− Debug.log "NO ID FOUND" label
367+ , testHighlight
368+ ]
369+ in
370+ Html.node "style"
371+ []
372+ [ Html.text styleSheet
373+ ]
320
374
321− Just id ->
322− id ++ " { background-color:red; outline: dashed; };"
323
375
324− styleSheet =
376+hasFailure : WithResults (Testable.Element Msg) -> Int
325− results
377+hasFailure myTest =
326− |> List.map toStyleClass
378+ if
327− |> String.join ""
379+ List.any
328− in
380+ (not << isPassing)
329− Html.node "style"
381+ myTest.results
382+ then
383+ 0
384+
385+ else
386+ 1
387+
388+
389+isExpectationPassing result =
390+ case result of
391+ Testable.Todo label ->
392+ True
393+
394+ Testable.Expect details ->
395+ details.result
396+
397+
398+isPassing layoutTest =
399+ List.all isExpectationPassing layoutTest.expectations
400+
401+
402+viewResult : Int -> WithResults (Testable.Element Msg) -> Element.Element Msg
403+viewResult selectedIndex myTest =
404+ if myTest.index == selectedIndex then
405+ Element.column
406+ [ Element.alignLeft
407+ , Element.spacing 16
408+ ]
409+ [ Element.el [ Font.size 24 ] (Element.text myTest.label)
410+ , Element.column
411+ [ Element.alignLeft, Element.spacing 16 ]
412+ (myTest.results
413+ |> groupBy .elementDomId
414+ |> List.map (expandDetails >> viewLayoutTestGroup)
415+ )
416+ ]
417+
418+ else
419+ let
420+ ( passing, failing ) =
421+ List.partition isPassing myTest.results
422+ in
423+ Element.column
424+ [ Element.alignLeft
425+ , Element.spacing 16
426+ , Element.pointer
427+ , Events.onClick (Select myTest.index)
428+ ]
429+ [ Element.row [ Element.spacing 16 ]
430+ [ Element.el [ Font.size 16 ] (Element.text myTest.label)
431+ , Element.text (String.fromInt (List.length passing) ++ " passing")
432+ , let
433+ failingCount =
434+ List.length failing
435+ in
436+ if failingCount == 0 then
437+ Element.none
438+
439+ else
440+ Element.text (String.fromInt (List.length failing) ++ " failing")
441+ ]
442+ ]
443+
444+
445+expandDetails group =
446+ case group.members of
447+ [] ->
448+ { id = group.id
449+ , elementType = Testable.EmptyType
450+ , members = group.members
451+ }
452+
453+ top :: _ ->
454+ { id = group.id
455+ , elementType = top.elementType
456+ , members = group.members
457+ }
458+
459+
460+groupBy fn list =
461+ groupWhile (\one two -> fn one == fn two) list
462+ |> List.map
463+ (\( fst, remaining ) ->
464+ { id = fn fst
465+ , members = fst :: remaining
466+ }
467+ )
468+
469+
470+groupWhile : (a -> a -> Bool) -> List a -> List ( a, List a )
471+groupWhile isSameGroup items =
472+ List.foldr
473+ (\x acc ->
474+ case acc of
475+ [] ->
476+ [ ( x, [] ) ]
477+
478+ ( y, restOfGroup ) :: groups ->
479+ if isSameGroup x y then
480+ ( x, y :: restOfGroup ) :: groups
481+
482+ else
483+ ( x, [] ) :: acc
484+ )
330 []
485 []
331− [ Html.text styleSheet
486+ items
332− ]
333
487
334
488
335−viewResult : WithResults (Testable.Element Msg) -> Element.Element Msg
489+viewLayoutTestGroup group =
336−viewResult testable =
337 let
490 let
338− isPassing result =
491+ testId =
339− case Tuple.second result of
492+ group.id
340− Nothing ->
493+ |> String.dropLeft 1
341− True
494+ |> String.append "tests-"
342−
343− Just _ ->
344− False
345−
346− ( passing, failing ) =
347− List.partition isPassing testable.results
348−
349− viewSingle result =
350− case result of
351− ( label, Nothing ) ->
352− Element.el
353− [ Background.color palette.green
354− , Font.color palette.black
355− , Element.paddingXY 20 10
356− , Element.alignLeft
357− , Border.rounded 3
358− ]
359− <|
360− Element.text ("Success! - " ++ label)
361−
362− ( label, Just ({ given, description } as reason) ) ->
363− Element.column
364− [ Background.color palette.red
365− , Font.color palette.black
366− , Element.paddingXY 20 10
367− , Element.alignLeft
368− , Element.width Element.shrink
369−
370− -- , Element.spacing 25
371− , Border.rounded 3
372− ]
373− [ Element.el [ Element.width Element.fill ] <| Element.text label
374− , Element.el [ Element.width Element.fill ] <| Element.text (viewReason reason)
375− ]
376 in
495 in
377 Element.column
496 Element.column
378− [ Border.width 1
497+ [ Element.spacing 8
379− , Border.color palette.lightGrey
498+ , Element.htmlAttribute (Html.Attributes.id testId)
380− , Element.padding 20
499+ , Events.onMouseEnter (HighlightDomID (Just group.id))
381− , Element.height Element.shrink
500+ , Events.onMouseLeave (HighlightDomID Nothing)
382− , Element.alignLeft
501+ , Element.htmlAttribute (Html.Attributes.style "user-select" "none")
383− , Element.spacing 16
384 ]
502 ]
385− [ Element.el [ Font.bold, Font.size 64 ] (Element.text testable.label)
503+ [ Element.row [ Element.spacing 16 ]
386− , Element.column [ Element.alignLeft, Element.spacing 20 ]
504+ [ Element.el [] (Element.text (Testable.elementTypeToString group.elementType))
387− (failing
505+ , Element.el [ Font.color palette.lightGrey ] (Element.text group.id)
388− |> List.map viewSingle
506+ ]
389− )
507+ , Element.column
390− , Element.el
508+ [ Element.spacing 8
391− [ Element.alignLeft
509+ , Element.paddingXY 32 0
392− , Element.spacing 20
393− , Background.color palette.green
394− , Font.color palette.black
395− , Element.paddingXY 20 10
396− , Element.alignLeft
397− , Border.rounded 3
398 ]
510 ]
399− (Element.text (String.fromInt (List.length passing) ++ " tests passing!"))
511+ (List.map viewLayoutTest group.members)
400 ]
512 ]
401
513
402
514
403−viewReason { description, reason } =
515+type alias Grouped thing =
404− case reason of
516+ { id : String
405− Failure.Custom ->
517+ , members : List thing
406− description
518+ }
407
519
408− Failure.Equality one two ->
409− description ++ " " ++ one ++ " " ++ two
410
520
411− Failure.Comparison one two ->
521+viewLayoutTest layoutTest =
412− description ++ " " ++ one ++ " " ++ two
522+ Element.column
523+ [ Element.spacing 8
524+ ]
525+ [ Element.row [ Element.spacing 8 ]
526+ [ Element.el [ Font.bold ] (Element.text layoutTest.label)
413
527
414− Failure.ListDiff expected actual ->
528+ -- , Element.el [ Font.color palette.lightGrey ] (Element.text layoutTest.elementDomId)
415− "expected\n"
529+ ]
416− ++ String.join " \n" expected
530+ , Element.column [ Element.spacing 8 ]
417− ++ "actual\n"
531+ (List.map viewLayoutExpectation layoutTest.expectations)
418− ++ String.join " \n" actual
532+ ]
419
533
420− Failure.CollectionDiff { expected, actual, extra, missing } ->
421− String.join "\n"
422− [ formatKeyValue "expected" expected
423− , formatKeyValue "actual" actual
424− , formatKeyValue "extra" (String.join ", " extra)
425− , formatKeyValue "missing" (String.join ", " missing)
426− ]
427
534
428− Failure.TODO ->
535+viewLayoutExpectation expectation =
429− description
536+ case expectation of
537+ Testable.Todo label ->
538+ Element.row [ Element.spacing 4 ]
539+ [ todo, Element.text label ]
430
540
431− Failure.Invalid _ ->
541+ Testable.Expect details ->
432− description
542+ if details.result then
543+ Element.row [ Element.spacing 4 ]
544+ [ pass, Element.text details.description ]
545+
546+ else
547+ Element.row [ Element.spacing 4 ]
548+ [ fail, Element.text details.description ]
549+
550+
551+badge color text =
552+ Element.el
553+ [ Background.color color
554+ , Font.color palette.black
555+ , Element.paddingXY 4 8
556+ , Border.rounded 2
557+ ]
558+ (Element.text text)
559+
560+
561+todo =
562+ badge palette.lightGrey "todo"
563+
564+
565+pass =
566+ badge palette.green "pass"
567+
568+
569+fail =
570+ badge palette.red "fail"
433
571
434
572
435formatKeyValue : String -> String -> String
573formatKeyValue : String -> String -> String
@@ -442,12 +580,10 @@ port report :
442 { label : String
580 { label : String
443 , results :
581 , results :
444 List
582 List
445− ( String
583+ { description : String
446− , Maybe
584+ , passing : Bool
447− { given : Maybe String
585+ , todo : Bool
448− , description : String
586+ }
449− }
450− )
451 }
587 }
452 -> Cmd msg
588 -> Cmd msg
453
589
@@ -464,6 +600,7 @@ port styles :
464 , bbox : Testable.BoundingBox
600 , bbox : Testable.BoundingBox
465 , style : List ( String, String )
601 , style : List ( String, String )
466 , isVisible : Bool
602 , isVisible : Bool
603+ , textMetrics : List Testable.TextMetrics
467 }
604 }
468 -> msg
605 -> msg
469 )
606 )
tests-rendering/src/Tests/Alignment.elmnew file+49
@@ -0,0 +1,49 @@
1+module Tests.Alignment exposing (view)
2+
3+{-| Testing nearby elements such as those defined with `above`, `below`, etc.
4+-}
5+
6+import Testable
7+import Testable.Element exposing (..)
8+import Testable.Element.Background as Background
9+import Testable.Element.Font as Font
10+import Testable.Generator
11+import Tests.Palette as Palette exposing (..)
12+
13+
14+box attrs =
15+ el
16+ ([ width (px 50)
17+ , height (px 50)
18+ , Background.color blue
19+ ]
20+ ++ attrs
21+ )
22+ none
23+
24+
25+{-| -}
26+view : List ( String, Testable.Element msg )
27+view =
28+ List.concat
29+ [ Testable.Generator.generate "Alignments"
30+ (\layout element alignment ->
31+ layout
32+ [ width (px 800)
33+ , height (px 800)
34+ , Background.color lightGrey
35+ ]
36+ (element
37+ ([ width (px 200)
38+ , height (px 200)
39+ , Background.color red
40+ ]
41+ ++ alignment
42+ )
43+ none
44+ )
45+ )
46+ |> Testable.Generator.with Testable.Generator.allLayouts
47+ |> Testable.Generator.with Testable.Generator.allElements
48+ |> Testable.Generator.with Testable.Generator.allAlignments
49+ ]
tests-rendering/src/Tests/All.elmnew file+44
@@ -0,0 +1,44 @@
1+module Tests.All exposing (main)
2+
3+{-| -}
4+
5+import Layout.NestedParagraphs
6+import Layout.SafariBugIssue147
7+import Testable.Generator
8+import Testable.Runner
9+import Tests.Alignment
10+import Tests.Nearby
11+import Tests.Spacing
12+import Tests.TextWrapping
13+import Tests.Transparency
14+
15+
16+main : Testable.Runner.TestableProgram
17+main =
18+ (Testable.Runner.program << List.concat)
19+ [ Testable.Generator.elementInLayout "Basics" []
20+ , Tests.Nearby.view
21+ , Tests.Alignment.view
22+ , Tests.Spacing.view
23+
24+ -- [
25+ -- , Tuple.pair "Transparency" Tests.Transparency.view
26+ -- ]
27+ -- , [ Tuple.pair "Paragraph wrapping" Tests.TextWrapping.view
28+ -- ]
29+ , issues
30+ ]
31+
32+
33+issues =
34+ [ -- Testable.Runner.rename "Weird Centering" WeirdCentering.view
35+ -- , Testable.Runner.rename "Stacked scrolling columns height" StackedScrollingColumnsHeight.view
36+ -- ,
37+ Testable.Runner.rename "Safari bug issue147" Layout.SafariBugIssue147.view
38+ , Testable.Runner.rename "Nested paragraphs" Layout.NestedParagraphs.view
39+
40+ -- , Testable.Runner.rename "Issue 22, zIndex" Nearby.Issue22ZIndex.view
41+ -- , Testable.Runner.rename "inFrontIssue" Nearby.InFrontIssue.view
42+ -- , Testable.Runner.rename "El in fixed height column" ElInFixedHeightColumn.view
43+ -- , Testable.Runner.rename "Clipped el in fixed width column" ClippedElInFixedWidthColumn.view
44+ ]
tests-rendering/src/Tests/Basic.elmdeleted−54
@@ -1,54 +0,0 @@
1−module Tests.Basic exposing (view)
2−
3−{-| -}
4−
5−import Element as Actual
6−import Html
7−import Testable
8−import Testable.Element as Element exposing (..)
9−import Testable.Element.Background as Background
10−import Testable.Element.Font as Font
11−import Testable.Runner
12−import Tests.Palette as Palette
13−
14−
15−{-| -}
16−main : Html.Html msg
17−main =
18− Testable.Runner.show view
19−
20−
21−{-| -}
22−view : Testable.Element msg
23−view =
24− row [ spacing 50, alignTop ]
25− [ el
26− [ width (px 200)
27− , height (px 200)
28− , Background.color Palette.blue
29− , Font.color Palette.white
30− ]
31− (text "Hello!")
32− , el
33− [ width (px 200)
34− , height (px 200)
35− , Background.color Palette.blue
36− , Font.color Palette.white
37− ]
38− (text "Hello!")
39− , el
40− [ width (px 200)
41− , height (px 200)
42− , Background.color (Actual.rgba 0 0 1 1)
43− , Font.color Palette.white
44− , below
45− (el
46− [ Background.color Palette.grey
47− , width (px 50)
48− , height (px 50)
49− ]
50− none
51− )
52− ]
53− (text "Hello!")
54− ]
tests-rendering/src/Tests/ColumnAlignment.elmdeleted−176
@@ -1,176 +0,0 @@
1−module Tests.ColumnAlignment exposing (..)
2−
3−import Html
4−import Testable
5−import Testable.Element as Element exposing (..)
6−import Testable.Element.Background as Background
7−import Testable.Element.Font as Font
8−import Testable.Runner
9−import Tests.Palette as Palette exposing (..)
10−
11−
12−{-| -}
13−main : Html.Html msg
14−main =
15− Testable.Runner.show view
16−
17−
18−box attrs =
19− el
20− ([ width (px 50)
21− , height (px 50)
22− , Background.color blue
23− ]
24− ++ attrs
25− )
26− none
27−
28−
29−container =
30− el [ width (px 100), height (px 100) ]
31−
32−
33−view =
34− let
35− colContainer attrs children =
36− column ([ Background.color lightGrey, spacing 20, width (px 100), height (px 500) ] ++ attrs) children
37−
38− tallContainer attrs children =
39− column ([ Background.color lightGrey, spacing 20, width (px 100), height fill ] ++ attrs) children
40− in
41− column
42− [ width fill, spacing 20 ]
43− [ el [] (text "Alignment Within a Column")
44− , row [ spacing 20 ]
45− [ colContainer []
46− [ box [] ]
47− , colContainer []
48− [ box [ centerY ]
49− ]
50− , colContainer []
51− [ box [ alignBottom, label "first" ]
52− ]
53− ]
54− , row [ spacing 20 ]
55− [ colContainer []
56− [ box [] ]
57− , colContainer []
58− [ box []
59− , box []
60− , box []
61− ]
62− , colContainer []
63− [ box [ alignLeft, label "first" ]
64− , box [ centerX, label "second" ]
65− , box [ alignRight, label "third" ]
66− ]
67− ]
68− , row [ spacing 20 ]
69− [ colContainer []
70− [ box []
71− , box []
72− , box []
73− ]
74− , colContainer []
75− [ box []
76− , box []
77− , box [ alignBottom, label "third" ]
78− ]
79− , colContainer []
80− [ box []
81− , box [ alignBottom, label "second" ]
82− , box []
83− ]
84− , colContainer []
85− [ box [ alignBottom, label "first" ]
86− , box []
87− , box []
88− ]
89− ]
90− , text "centerY"
91− , row [ spacing 20 ]
92− [ colContainer [ height fill ]
93− [ box [ centerY, label "solo" ]
94− ]
95− , colContainer []
96− [ box []
97− , box [ centerY, label "middle" ]
98− , box []
99− ]
100− , colContainer []
101− [ box []
102− , box []
103− , box [ centerY, label "last" ]
104− ]
105− ]
106− , text "multiple centerY"
107− , row [ height (px 800), spacing 20 ]
108− [ tallContainer []
109− [ box []
110− , box [ centerY ]
111− , box [ centerY ]
112− , box [ centerY ]
113− , box [ centerY ]
114− , box [ alignBottom ]
115− ]
116− , tallContainer []
117− [ box []
118− , box [ centerY ]
119− , box []
120− ]
121− , tallContainer []
122− [ box []
123− , box []
124− , box [ centerY ]
125− ]
126− ]
127− , text "top, center, bottom"
128− , row [ spacing 20 ]
129− [ colContainer []
130− [ box [ alignTop, label "first" ]
131− , box []
132− , box [ alignBottom, label "last" ]
133− ]
134− , colContainer []
135− [ box [ alignTop ]
136− , box [ centerY ]
137− , box [ alignBottom ]
138− ]
139− , colContainer []
140− [ box [ alignLeft, alignTop ]
141− , box [ centerX, centerY ]
142− , box [ alignRight, alignBottom ]
143− ]
144− ]
145− , el [ width fill ] (text "Column in a Row")
146− , row [ width fill, spacing 20, label "row" ]
147− [ box [ alignLeft, alignTop, label "box" ]
148− , column
149− [ alignLeft
150− , alignTop
151− , spacing 20
152− , label "column"
153− , Background.color lightGrey
154− ]
155− [ box []
156− , box []
157− , box []
158− ]
159− , column
160− [ spacing 20
161− , width (px 100)
162− , alignLeft
163− , alignTop
164− , Background.color lightGrey
165− ]
166− [ box []
167− , box []
168− , box []
169− ]
170− , colContainer []
171− [ box [ alignRight ]
172− , box [ centerX ]
173− , box [ alignLeft ]
174− ]
175− ]
176− ]
tests-rendering/src/Tests/ColumnSpacing.elmdeleted−68
@@ -1,68 +0,0 @@
1−module Tests.ColumnSpacing exposing (..)
2−
3−import Html
4−import Testable
5−import Testable.Element as Element exposing (..)
6−import Testable.Element.Background as Background
7−import Testable.Element.Font as Font
8−import Testable.Runner
9−import Tests.Palette as Palette exposing (..)
10−
11−
12−{-| -}
13−main : Html.Html msg
14−main =
15− Testable.Runner.show view
16−
17−
18−box attrs =
19− el
20− ([ width (px 50)
21− , height (px 50)
22− , Background.color blue
23− ]
24− ++ attrs
25− )
26− none
27−
28−
29−tinyBox attrs =
30− el
31− ([ width (px 20)
32− , height (px 20)
33−
34− -- , centerY
35− , Background.color darkCharcoal
36− ]
37− ++ attrs
38− )
39− none
40−
41−
42−container =
43− el [ width (px 100), height (px 100) ]
44−
45−
46−view =
47− let
48− colContainer attrs children =
49− column ([ spacing 20, width (px 100), height (px 500) ] ++ attrs) children
50− in
51− column
52− []
53− [ el [] (text "Spacing within a column")
54− , row []
55− [ colContainer []
56− [ box [] ]
57− , colContainer []
58− [ box []
59− , box []
60− , box []
61− ]
62− , colContainer [ onRight (tinyBox []) ]
63− [ box []
64− , box []
65− , box []
66− ]
67− ]
68− ]
tests-rendering/src/Tests/ElementAlignment.elmdeleted−111
@@ -1,111 +0,0 @@
1−module Tests.ElementAlignment exposing (..)
2−
3−import Generator
4−import Html
5−import Testable
6−import Testable.Element as Element exposing (..)
7−import Testable.Element.Background as Background
8−import Testable.Element.Font as Font
9−import Testable.Runner
10−import Tests.Palette as Palette exposing (..)
11−
12−
13−{-| -}
14−main : Html.Html msg
15−main =
16− Testable.Runner.show view
17−
18−
19−box : List (Testable.Attr msg) -> Testable.Element msg
20−box attrs =
21− el
22− ([ width (px 50)
23− , height (px 50)
24− , Background.color blue
25− ]
26− ++ attrs
27− )
28− none
29−
30−
31−container : Testable.Element msg -> Testable.Element msg
32−container =
33− el
34− [ width (px 200)
35− , height (px 200)
36− , Background.color lightGrey
37− ]
38−
39−
40−view : Testable.Element msg
41−view =
42− column []
43− [ el [] (text "Alignment Within an El")
44− , container <|
45− box []
46− , text "alignLeft, centerX, alignRight"
47− , column [ spacing 20 ] <|
48− Generator.sizes
49− (\resizeable ->
50− row [ spacing 20 ]
51− [ container <|
52− resizeable [ Background.color blue, alignLeft ] none
53− , container <|
54− resizeable [ Background.color blue, centerX ] none
55− , container <|
56− resizeable [ Background.color blue, alignRight ] none
57− ]
58− )
59− , text "top, centerY, bottom"
60− , column [ spacing 20 ] <|
61− Generator.sizes
62− (\resizeable ->
63− row [ spacing 20 ]
64− [ container <|
65− resizeable [ alignTop ] none
66− , container <|
67− resizeable [ centerY ] none
68− , container <|
69− resizeable [ alignBottom ] none
70− ]
71− )
72− , text "align top ++ alignments"
73− , column [ spacing 20 ] <|
74− Generator.sizes
75− (\resizeable ->
76− row [ spacing 20 ]
77− [ container <|
78− resizeable [ alignTop, alignLeft ] none
79− , container <|
80− resizeable [ alignTop, centerX ] none
81− , container <|
82− resizeable [ alignTop, alignRight ] none
83− ]
84− )
85− , text "centerY ++ alignments"
86− , column [ spacing 20 ] <|
87− Generator.sizes
88− (\resizeable ->
89− row [ spacing 20 ]
90− [ container <|
91− resizeable [ centerY, alignLeft ] none
92− , container <|
93− resizeable [ centerY, centerX ] none
94− , container <|
95− resizeable [ centerY, alignRight ] none
96− ]
97− )
98− , text "alignBottom ++ alignments"
99− , column [ spacing 20 ] <|
100− Generator.sizes
101− (\resizeable ->
102− row [ spacing 20 ]
103− [ container <|
104− resizeable [ alignBottom, alignLeft ] none
105− , container <|
106− resizeable [ alignBottom, centerX ] none
107− , container <|
108− resizeable [ alignBottom, alignRight ] none
109− ]
110− )
111− ]
tests-rendering/src/Tests/Nearby.elm+97−356
@@ -8,6 +8,7 @@ import Testable
8import Testable.Element exposing (..)
8import Testable.Element exposing (..)
9import Testable.Element.Background as Background
9import Testable.Element.Background as Background
10import Testable.Element.Font as Font
10import Testable.Element.Font as Font
11+import Testable.Generator
11import Testable.Runner
12import Testable.Runner
12import Tests.Palette as Palette exposing (..)
13import Tests.Palette as Palette exposing (..)
13
14
@@ -57,365 +58,39 @@ p attrs =
57
58
58
59
59{-| -}
60{-| -}
60−main : Html.Html msg
61+view : List ( String, Testable.Element msg )
61−main =
62− Testable.Runner.show view
63−
64−
65−{-| -}
66−view : Testable.Element msg
67view =
62view =
68− let
63+ List.concat
69− transparentBox attrs =
64+ [ [ Tuple.pair "Parent inFront above child inFront" layeredVisibility
70− el
65+ , Tuple.pair "Sibling onLeft above sibling inFront" overlappingChildren
71− ([ Font.color white
66+ , Tuple.pair "el with all nearbys and alignments" master
72− , width (px 50)
67+ , Tuple.pair "paragraph with all nearbys and alignments" masterParagraph
73− , height (px 50)
68+ ]
74− , Background.color (rgba 52 101 164 0.8)
69+ , Testable.Generator.generate "Nearbys"
75− ]
70+ (\element nearby ->
76− ++ attrs
71+ element
77− )
72+ [ nearby (box [])
78− (text "hi")
73+ , width (px 200)
79−
74+ , height (px 200)
80− -- single location name box =
75+ , Background.color red
81− -- row [ height (px 100), width fill, spacing 50 ]
82− -- [ box
83− -- [ location
84− -- (el
85− -- [ width (px 20)
86− -- , height (px 20)
87− -- , Background.color darkCharcoal
88− -- ]
89− -- none
90− -- )
91− -- ]
92− -- , box
93− -- [ location
94− -- (el
95− -- [ width (px 20)
96− -- , height (px 20)
97− -- , alignLeft
98− -- , Background.color darkCharcoal
99− -- ]
100− -- none
101− -- )
102− -- ]
103− -- ]
104− little name attrs =
105− el
106− ([ label name
107− , width (px 5)
108− , height (px 5)
109− , Background.color darkCharcoal
110− ]
111− ++ attrs
112− )
113− none
114−
115− nearby location name render =
116− column [ spacing 32, label "column" ]
117− [ el [ padding 20, Background.color green, Font.color white ] (text name)
118− , row [ height (px 100), width fill, spacing 50 ]
119− [ render
120− [ location
121− (el
122− [ label name
123− , width (px 20)
124− , height (px 20)
125− , Background.color darkCharcoal
126− ]
127− none
128− )
129− ]
130− , render
131− [ location
132− (el
133− [ label name
134− , width (px 20)
135− , height (px 20)
136− , alignLeft
137− , Background.color darkCharcoal
138− ]
139− none
140− )
141− ]
142− , render
143− [ location
144− (el
145− [ label name
146− , width (px 20)
147− , height (px 20)
148− , centerX
149− , Background.color darkCharcoal
150− ]
151− none
152− )
153− ]
154− , render
155− [ location
156− (el
157− [ label name
158− , width (px 20)
159− , height (px 20)
160− , alignRight
161− , Background.color darkCharcoal
162− ]
163− none
164− )
165− ]
166− , render
167− [ location
168− (el
169− [ label name
170− , width (px 20)
171− , height (px 20)
172− , alignTop
173− , Background.color darkCharcoal
174− ]
175− none
176− )
177− ]
178− , render
179− [ location
180− (el
181− [ label name
182− , width (px 20)
183− , height (px 20)
184− , centerY
185− , Background.color darkCharcoal
186− ]
187− none
188− )
189− ]
190− , render
191− [ location
192− (el
193− [ label name
194− , width (px 20)
195− , height (px 20)
196− , alignBottom
197− , Background.color darkCharcoal
198− ]
199− none
200− )
201− ]
202− ]
203− , text "widths/heights"
204− , row [ height (px 100), width fill, spacing 50, label "Row" ]
205− [ render
206− [ location
207− (el
208− [ label name
209− , width fill
210− , height fill
211− , Background.color darkCharcoal
212− ]
213− none
214− )
215− ]
216− , render
217− [ location
218− (el
219− [ label name
220− , width (px 20)
221− , height fill
222− , Background.color darkCharcoal
223− ]
224− none
225− )
226− ]
227− , render
228− [ label "render"
229− , location
230− (el
231− [ label name
232− , width fill
233− , height (px 20)
234− , Background.color darkCharcoal
235− ]
236− none
237− )
238− ]
239− , render
240− [ location
241− (el
242− [ label name
243− , width (px 20)
244− , height shrink
245− , Background.color darkCharcoal
246− , Font.color white
247− ]
248− (text "h-shrink")
249− )
250− ]
251− , render
252− [ location
253− (el
254− [ label name
255− , width shrink
256− , height (px 20)
257− , Background.color darkCharcoal
258− , Font.color white
259− ]
260− (text "w-shrink")
261− )
262− ]
263 ]
76 ]
264− , text "on paragraph"
77+ none
265− , row [ width fill, spacing 50, label "Row" ]
78+ )
266− [ p
79+ |> Testable.Generator.with Testable.Generator.allElements
267− [ location
80+ |> Testable.Generator.with Testable.Generator.nearbys
268− (el
81+ , Testable.Generator.generate "Nearbys with alignment"
269− [ label name
82+ (\element nearby align ->
270− , width (px 20)
83+ element
271− , height (px 20)
84+ [ nearby (box [ align ])
272− , Background.color darkCharcoal
85+ , width (px 200)
273− ]
86+ , height (px 200)
274− none
87+ , Background.color red
275− )
276− ]
277− , p
278− [ location
279− (el
280− [ label name
281− , width (px 20)
282− , height (px 20)
283− , alignLeft
284− , Background.color darkCharcoal
285− ]
286− none
287− )
288− ]
289− , p
290− [ location
291− (el
292− [ label name
293− , width (px 20)
294− , height (px 20)
295− , centerX
296− , Background.color darkCharcoal
297− ]
298− none
299− )
300− ]
301− , p
302− [ location
303− (el
304− [ label name
305− , width (px 20)
306− , height (px 20)
307− , alignRight
308− , Background.color darkCharcoal
309− ]
310− none
311− )
312− ]
313− , p
314− [ location
315− (el
316− [ label name
317− , width (px 20)
318− , height (px 20)
319− , alignTop
320− , Background.color darkCharcoal
321− ]
322− none
323− )
324− ]
325− , p
326− [ location
327− (el
328− [ label name
329− , width (px 20)
330− , height (px 20)
331− , centerY
332− , Background.color darkCharcoal
333− ]
334− none
335− )
336− ]
337− , p
338− [ location
339− (el
340− [ label name
341− , width (px 20)
342− , height (px 20)
343− , alignBottom
344− , Background.color darkCharcoal
345− ]
346− none
347− )
348− ]
349 ]
88 ]
350− ]
89+ none
351−
90+ )
352− master =
91+ |> Testable.Generator.with Testable.Generator.allElements
353− el [ padding 20 ] <|
92+ |> Testable.Generator.with Testable.Generator.nearbys
354− box
93+ |> Testable.Generator.with Testable.Generator.alignments
355− [ above (little "above-left" [ alignLeft ])
356− , above (little "above-center" [ centerX ])
357− , above (little "above-right" [ alignRight ])
358− , below (little "below-left" [ alignLeft ])
359− , below (little "below-center" [ centerX ])
360− , below (little "below-right" [ alignRight ])
361− , onRight (little "onRight-left" [ alignTop ])
362− , onRight (little "onRight-center" [ centerY ])
363− , onRight (little "onRight-right" [ alignBottom ])
364− , onLeft (little "onLeft-left" [ alignTop ])
365− , onLeft (little "onLeft-center" [ centerY ])
366− , onLeft (little "onLeft-right" [ alignBottom ])
367− , inFront (little "infront-left-top" [ alignTop, alignLeft ])
368− , inFront (little "infront-center-top" [ alignTop, centerX ])
369− , inFront (little "infront-right-top" [ alignTop, alignRight ])
370− , inFront (little "infront-left-center" [ centerY, alignLeft ])
371− , inFront (little "infront-center-center" [ centerY, centerX ])
372− , inFront (little "infront-right-center" [ centerY, alignRight ])
373− , inFront (little "infront-left-bottom" [ alignBottom, alignLeft ])
374− , inFront (little "infront-center-bottom" [ alignBottom, centerX ])
375− , inFront (little "infront-right-bottom" [ alignBottom, alignRight ])
376− ]
377−
378− masterParagraph =
379− el [ padding 20 ] <|
380− p
381− [ above (little "above-left" [ alignLeft ])
382− , above (little "above-center" [ centerX ])
383− , above (little "above-right" [ alignRight ])
384− , below (little "below-left" [ alignLeft ])
385− , below (little "below-center" [ centerX ])
386− , below (little "below-right" [ alignRight ])
387− , onRight (little "onRight-left" [ alignTop ])
388− , onRight (little "onRight-center" [ centerY ])
389− , onRight (little "onRight-right" [ alignBottom ])
390− , onLeft (little "onLeft-left" [ alignTop ])
391− , onLeft (little "onLeft-center" [ centerY ])
392− , onLeft (little "onLeft-right" [ alignBottom ])
393− , inFront (little "infront-left-top" [ alignTop, alignLeft ])
394− , inFront (little "infront-center-top" [ alignTop, centerX ])
395− , inFront (little "infront-right-top" [ alignTop, alignRight ])
396− , inFront (little "infront-left-center" [ centerY, alignLeft ])
397− , inFront (little "infront-center-center" [ centerY, centerX ])
398− , inFront (little "infront-right-center" [ centerY, alignRight ])
399− , inFront (little "infront-left-bottom" [ alignBottom, alignLeft ])
400− , inFront (little "infront-center-bottom" [ alignBottom, centerX ])
401− , inFront (little "infront-right-bottom" [ alignBottom, alignRight ])
402− ]
403− in
404− column
405− [ centerX, label "Nearby Elements", spacing 100 ]
406− [ layeredVisibility
407− , overlappingChildren
408−
409− -- Note: visibility checks like the above
410− -- need to be at the top so they're in the viewport
411− , master
412− , masterParagraph
413− , nearby above "above" box
414− , nearby below "below" box
415− , nearby inFront "inFront" box
416− , nearby onRight "onRight" box
417− , nearby onLeft "onLeft" box
418− , nearby behindContent "behindContent" transparentBox
419 ]
94 ]
420
95
421
96
@@ -436,3 +111,69 @@ overlappingChildren =
436 , el [ onLeft (littleBox "overlapping" [ isVisible, Background.color red ]) ]
111 , el [ onLeft (littleBox "overlapping" [ isVisible, Background.color red ]) ]
437 (justBox 40 darkGrey [])
112 (justBox 40 darkGrey [])
438 ]
113 ]
114+
115+
116+little name attrs =
117+ el
118+ ([ label name
119+ , width (px 5)
120+ , height (px 5)
121+ , Background.color darkCharcoal
122+ ]
123+ ++ attrs
124+ )
125+ none
126+
127+
128+master =
129+ el [ padding 20 ] <|
130+ box
131+ [ above (little "above-left" [ alignLeft ])
132+ , above (little "above-center" [ centerX ])
133+ , above (little "above-right" [ alignRight ])
134+ , below (little "below-left" [ alignLeft ])
135+ , below (little "below-center" [ centerX ])
136+ , below (little "below-right" [ alignRight ])
137+ , onRight (little "onRight-left" [ alignTop ])
138+ , onRight (little "onRight-center" [ centerY ])
139+ , onRight (little "onRight-right" [ alignBottom ])
140+ , onLeft (little "onLeft-left" [ alignTop ])
141+ , onLeft (little "onLeft-center" [ centerY ])
142+ , onLeft (little "onLeft-right" [ alignBottom ])
143+ , inFront (little "infront-left-top" [ alignTop, alignLeft ])
144+ , inFront (little "infront-center-top" [ alignTop, centerX ])
145+ , inFront (little "infront-right-top" [ alignTop, alignRight ])
146+ , inFront (little "infront-left-center" [ centerY, alignLeft ])
147+ , inFront (little "infront-center-center" [ centerY, centerX ])
148+ , inFront (little "infront-right-center" [ centerY, alignRight ])
149+ , inFront (little "infront-left-bottom" [ alignBottom, alignLeft ])
150+ , inFront (little "infront-center-bottom" [ alignBottom, centerX ])
151+ , inFront (little "infront-right-bottom" [ alignBottom, alignRight ])
152+ ]
153+
154+
155+masterParagraph =
156+ el [ padding 20 ] <|
157+ p
158+ [ above (little "above-left" [ alignLeft ])
159+ , above (little "above-center" [ centerX ])
160+ , above (little "above-right" [ alignRight ])
161+ , below (little "below-left" [ alignLeft ])
162+ , below (little "below-center" [ centerX ])
163+ , below (little "below-right" [ alignRight ])
164+ , onRight (little "onRight-left" [ alignTop ])
165+ , onRight (little "onRight-center" [ centerY ])
166+ , onRight (little "onRight-right" [ alignBottom ])
167+ , onLeft (little "onLeft-left" [ alignTop ])
168+ , onLeft (little "onLeft-center" [ centerY ])
169+ , onLeft (little "onLeft-right" [ alignBottom ])
170+ , inFront (little "infront-left-top" [ alignTop, alignLeft ])
171+ , inFront (little "infront-center-top" [ alignTop, centerX ])
172+ , inFront (little "infront-right-top" [ alignTop, alignRight ])
173+ , inFront (little "infront-left-center" [ centerY, alignLeft ])
174+ , inFront (little "infront-center-center" [ centerY, centerX ])
175+ , inFront (little "infront-right-center" [ centerY, alignRight ])
176+ , inFront (little "infront-left-bottom" [ alignBottom, alignLeft ])
177+ , inFront (little "infront-center-bottom" [ alignBottom, centerX ])
178+ , inFront (little "infront-right-bottom" [ alignBottom, alignRight ])
179+ ]
tests-rendering/src/Tests/RowAlignment.elmdeleted−212
@@ -1,212 +0,0 @@
1−module Tests.RowAlignment exposing (..)
2−
3−import Generator
4−import Html
5−import Testable
6−import Testable.Element as Element exposing (..)
7−import Testable.Element.Background as Background
8−import Testable.Element.Font as Font
9−import Testable.Runner
10−import Tests.Palette as Palette exposing (..)
11−
12−
13−{-| -}
14−main : Html.Html msg
15−main =
16− Testable.Runner.show view
17−
18−
19−box attrs =
20− el
21− ([ width (px 50)
22− , height (px 50)
23− , Background.color blue
24− ]
25− ++ attrs
26− )
27− none
28−
29−
30−view =
31− let
32− rowContainer attrs children =
33− row
34− ([ spacing 20
35− , height (px 100)
36− , Background.color lightGrey
37− ]
38− ++ attrs
39− )
40− children
41− in
42− column [ width (px 500), spacing 20 ]
43− [ el [] (text "Alignment Within a Row")
44− , column [ spacing 20 ] <|
45− Generator.sizes <|
46− \resizeable ->
47− rowContainer
48− [ label "single child" ]
49− [ resizeable [] none ]
50− , column [ spacing 20 ] <|
51− Generator.sizes <|
52− \resizeable ->
53− rowContainer
54− [ label "single child" ]
55− [ resizeable [ centerX ] none ]
56− , column [ spacing 20 ] <|
57− Generator.sizes <|
58− \resizeable ->
59− rowContainer [ label "single child" ]
60− [ resizeable [ alignRight ] none ]
61− , column [ spacing 20 ] <|
62− Generator.sizes <|
63− \resizeable ->
64− rowContainer []
65− [ resizeable [] none
66− , resizeable [] none
67− , resizeable [] none
68− ]
69− , column [ spacing 20 ] <|
70− Generator.sizes <|
71− \resizeable ->
72− rowContainer []
73− [ resizeable [] none
74− , resizeable [] none
75− , resizeable [ alignRight, label "Right Child in Row" ] none
76− ]
77− , column [ spacing 20 ] <|
78− Generator.sizes <|
79− \resizeable ->
80− rowContainer
81− []
82− [ resizeable [] none
83− , resizeable [ alignRight, label "Middle Child in Row" ] none
84− , resizeable [] none
85− ]
86− , column [ spacing 20 ] <|
87− Generator.sizes <|
88− \resizeable ->
89− rowContainer []
90− [ resizeable [ alignRight, label "Left Child in Row" ] none
91− , resizeable [] none
92− , resizeable [] none
93− ]
94− , text "center X"
95− , column [ spacing 20 ] <|
96− Generator.sizes <|
97− \resizeable ->
98− rowContainer []
99− [ resizeable [ centerX, label "Left Child in Row" ] none
100− , resizeable [] none
101− , resizeable [] none
102− ]
103− , column [ spacing 20 ] <|
104− Generator.sizes <|
105− \resizeable ->
106− rowContainer []
107− [ resizeable [] none
108− , resizeable [ centerX, label "Middle Child in Row" ] none
109− , resizeable [] none
110− ]
111− , column [ spacing 20 ] <|
112− Generator.sizes <|
113− \resizeable ->
114− rowContainer []
115− [ resizeable [] none
116− , resizeable [] none
117− , resizeable [ centerX, label "Right Child in Row" ] none
118− ]
119− , column [ spacing 20 ] <|
120− Generator.sizes <|
121− \resizeable ->
122− rowContainer []
123− [ resizeable [] none
124− , resizeable [] none
125− , resizeable [ centerX, label "Middle-Right Child in Row" ] none
126− , resizeable [] none
127− ]
128− , column [ spacing 20 ] <|
129− Generator.sizes <|
130− \resizeable ->
131− rowContainer []
132− [ resizeable [] none
133− , resizeable [] none
134− , resizeable [ centerX, label "Middle-Right Child in Row" ] none
135− , resizeable [ centerX, label "Middle-Right Child in Row" ] none
136− , resizeable [] none
137− ]
138− , column [ spacing 20 ] <|
139− Generator.sizes <|
140− \resizeable ->
141− rowContainer []
142− [ resizeable [] none
143− , resizeable [] none
144− , resizeable [ centerX, label "Middle-Right Child in Row" ] none
145− , resizeable [ centerX, label "Middle-Right Child in Row" ] none
146− , resizeable [ centerX, label "Middle-Right Child in Row" ] none
147− , resizeable [] none
148− ]
149− , text "left x right"
150− , column [ spacing 20 ] <|
151− Generator.sizes <|
152− \resizeable ->
153− rowContainer []
154− [ resizeable [ alignLeft, label "Left Child in Row" ] none
155− , resizeable [] none
156− , resizeable [ alignRight, label "Right Child in Row" ] none
157− ]
158− , text "left center right"
159− , column [ spacing 20 ] <|
160− Generator.sizes <|
161− \resizeable ->
162− rowContainer []
163− [ resizeable [ alignLeft, label "Left Child in Row" ] none
164− , resizeable [ centerX, label "Middle Child in Row" ] none
165− , resizeable [ alignRight, label "Right Child in Row" ] none
166− ]
167− , text "vertical alignment"
168− , column [ spacing 20 ] <|
169− Generator.sizes <|
170− \resizeable ->
171− rowContainer []
172− [ resizeable [ alignTop, label "Left Child in Row" ] none
173− , resizeable [ centerY, label "Middle Child in Row" ] none
174− , resizeable [ alignBottom, label "Right Child in Row" ] none
175− ]
176− , text "x and y alignments"
177− , column [ spacing 20 ] <|
178− Generator.sizes <|
179− \resizeable ->
180− rowContainer []
181− [ resizeable [ alignLeft, alignTop, label "Left Child" ] none
182− , resizeable [ centerX, centerY, label "Middle Child" ] none
183− , resizeable [ alignRight, alignBottom, label "Right Child" ] none
184− ]
185− , text "align Top and X alignments "
186− , column [ spacing 20 ] <|
187− Generator.sizes <|
188− \resizeable ->
189− rowContainer []
190− [ resizeable [ alignLeft, alignTop, label "Left Child" ] none
191− , resizeable [ centerX, alignTop, label "Middle Child" ] none
192− , resizeable [ alignRight, alignTop, label "Right Child" ] none
193− ]
194− , text "align Bottom and X alignments "
195− , column [ spacing 20 ] <|
196− Generator.sizes <|
197− \resizeable ->
198− rowContainer []
199− [ resizeable [ alignLeft, alignBottom, label "Left Child" ] none
200− , resizeable [ centerX, alignBottom, label "Middle Child" ] none
201− , resizeable [ alignRight, alignBottom, label "Right Child" ] none
202− ]
203− , text "centerY and X alignments "
204− , column [ spacing 20 ] <|
205− Generator.sizes <|
206− \resizeable ->
207− rowContainer []
208− [ resizeable [ alignLeft, centerY, label "Left Child" ] none
209− , resizeable [ centerX, centerY, label "Middle Child" ] none
210− , resizeable [ alignRight, centerY, label "Right Child" ] none
211− ]
212− ]
tests-rendering/src/Tests/RowSpacing.elmdeleted−69
@@ -1,69 +0,0 @@
1−module Tests.RowSpacing exposing (..)
2−
3−import Html
4−import Testable
5−import Testable.Element as Element exposing (..)
6−import Testable.Element.Background as Background
7−import Testable.Element.Font as Font
8−import Testable.Runner
9−import Tests.Palette as Palette exposing (..)
10−
11−
12−{-| -}
13−main : Html.Html msg
14−main =
15− Testable.Runner.show view
16−
17−
18−box attrs =
19− el
20− ([ width (px 50)
21− , height (px 50)
22− , Background.color blue
23− ]
24− ++ attrs
25− )
26− none
27−
28−
29−tinyBox attrs =
30− el
31− ([ width (px 20)
32− , height (px 20)
33−
34− -- , centerY
35− , Background.color darkCharcoal
36− ]
37− ++ attrs
38− )
39− none
40−
41−
42−container =
43− el [ width (px 100), height (px 100) ]
44−
45−
46−view =
47− let
48− colContainer attrs children =
49− row ([ spacing 20, width (px 500), height (px 120) ] ++ attrs) children
50− in
51− column
52− []
53− [ el [] (text "Spacing within a row")
54− , column []
55− [ box []
56− , colContainer []
57− [ box [] ]
58− , colContainer []
59− [ box []
60− , box []
61− , box []
62− ]
63− , colContainer [ below (tinyBox []) ]
64− [ box []
65− , box []
66− , box []
67− ]
68− ]
69− ]
tests-rendering/src/Tests/Run.elm+5−20
@@ -1,29 +1,14 @@
1module Tests.Run exposing (main)
1module Tests.Run exposing (main)
2
2
3−{-| -}
3+{-| _NOTE_ this is auto-generated! Notouchy!
4+-}
4
5
6+import ElInFixedHeightColumn
5import Testable.Runner
7import Testable.Runner
6−import Tests.Basic
7−import Tests.ColumnAlignment
8−import Tests.ColumnSpacing
9−import Tests.ElementAlignment
10−import Tests.Nearby
11−import Tests.RowAlignment
12−import Tests.RowSpacing
13−import Tests.Transparency
14
8
15
9
16main : Testable.Runner.TestableProgram
10main : Testable.Runner.TestableProgram
17main =
11main =
18− Testable.Runner.program
12+ Testable.Runner.program
19− [ Tuple.pair "Basic Element" Tests.Basic.view
13+ [ Testable.Runner.rename " El In Fixed Height Column" ElInFixedHeightColumn.view
20− , Tuple.pair "Nearby" Tests.Nearby.view
21− , Tuple.pair "Element Alignment" Tests.ElementAlignment.view
22− , Tuple.pair "Transparency" Tests.Transparency.view
23− , Tuple.pair "Column Alignment" Tests.ColumnAlignment.view
24−
25− -- This has 12k cases, so it runs slow and sometimes crashes IE
26− , Tuple.pair "Row Alignment" Tests.RowAlignment.view
27− , Tuple.pair "Column Spacing" Tests.ColumnSpacing.view
28− , Tuple.pair "Row Spacing" Tests.RowSpacing.view
29 ]
14 ]
tests-rendering/src/Tests/Spacing.elmnew file+36
@@ -0,0 +1,36 @@
1+module Tests.Spacing exposing (view)
2+
3+{-| Test that spacing works
4+-}
5+
6+import Testable
7+import Testable.Element exposing (..)
8+import Testable.Element.Background as Background
9+import Testable.Element.Font as Font
10+import Testable.Generator
11+import Tests.Palette as Palette exposing (..)
12+
13+
14+{-| -}
15+view : List ( String, Testable.Element msg )
16+view =
17+ List.concat
18+ [ Testable.Generator.generate "Spacing"
19+ (\layout element ->
20+ layout
21+ [ width (px 800)
22+ , height (px 800)
23+ , Background.color lightGrey
24+ , spacing 20
25+ ]
26+ (element
27+ [ width (px 200)
28+ , height (px 200)
29+ , Background.color red
30+ ]
31+ none
32+ )
33+ )
34+ |> Testable.Generator.with Testable.Generator.allLayouts
35+ |> Testable.Generator.with Testable.Generator.allElements
36+ ]
tests-rendering/src/Tests/TextWrapping.elmnew file+51
@@ -0,0 +1,51 @@
1+module Tests.TextWrapping exposing (main, view)
2+
3+import Html
4+import Testable
5+import Testable.Element exposing (..)
6+import Testable.Element.Background as Background
7+import Testable.Element.Font as Font
8+import Testable.Generator
9+import Testable.Runner
10+import Tests.Palette as Palette exposing (..)
11+
12+
13+box attrs =
14+ el
15+ ([ width (px 50)
16+ , height (px 50)
17+ , Background.color blue
18+ ]
19+ ++ attrs
20+ )
21+ none
22+
23+
24+{-| -}
25+main : Html.Html msg
26+main =
27+ Testable.Runner.show view
28+
29+
30+view =
31+ el [] <|
32+ paragraph []
33+ [ Testable.Generator.box
34+ ]
35+
36+
37+
38+-- column [ spacing 16 ]
39+-- [ paragraph []
40+-- [ el [] (text Testable.Generator.short)
41+-- ]
42+-- -- , paragraph []
43+-- -- [ text Testable.Generator.lorem
44+-- -- , text Testable.Generator.lorem
45+-- -- ]
46+-- -- , paragraph []
47+-- -- [ text Testable.Generator.lorem
48+-- -- , paragraph []
49+-- -- [ text Testable.Generator.lorem ]
50+-- -- ]
51+-- ]