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 }}
.gitignore+5−1
@@ -21,4 +21,8 @@ sauce.env
21
21
22__pycache__
22__pycache__
23node_modules
23node_modules
24−benchmarks/tmp
24+benchmarks/tmp
25+benchmarks/results
26+tests/tmp
27+
28+tmp
CONTRIBUTING.md−5
@@ -23,8 +23,3 @@ Here's a basic template in [ellie](https://ellie-app.com/3fhCyrxjLw3a1) to get y
23One of the more challenging parts of the project like this is gathering information.
23One of the more challenging parts of the project like this is gathering information.
24
24
25If you know of good resources about accessibility, internationalization, design, or [any of the topics listed in the research folder](https://github.com/mdgriffith/elm-ui/tree/master/notes/research), send them my way either via PR to those documents or on slack.
25If you know of good resources about accessibility, internationalization, design, or [any of the topics listed in the research folder](https://github.com/mdgriffith/elm-ui/tree/master/notes/research), send them my way either via PR to those documents or on slack.
26−
27−
28−# Helpful Projects
29−
30−Check out the [projects](https://github.com/mdgriffith/elm-ui/tree/master/notes/projects) folder for project contributions that could be useful for `elm-ui`.
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+15−5
@@ -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
@@ -20,9 +19,11 @@ This means:
20* **Layout and style are explicit and easy to modify.** CSS and HTML as tools for a layout language are hard to modify because there's no central place that represents your layout. You're generally forced to bounce back and forth between multiple definitions in multiple files in order to adjust layout, even though it's probably the most common thing you'll do.
19* **Layout and style are explicit and easy to modify.** CSS and HTML as tools for a layout language are hard to modify because there's no central place that represents your layout. You're generally forced to bounce back and forth between multiple definitions in multiple files in order to adjust layout, even though it's probably the most common thing you'll do.
21
20
22
21
23−[Try this live example on Ellie!](https://ellie-app.com/3f2n4J5RnT3a1)
22+[Try this live example on Ellie!](https://ellie-app.com/7Cw4VCyr3RGa1)
24
23
25```elm
24```elm
25+module Main exposing (..)
26+
26import Element exposing (Element, el, text, row, alignRight, fill, width, rgb255, spacing, centerY, padding)
27import Element exposing (Element, el, text, row, alignRight, fill, width, rgb255, spacing, centerY, padding)
27import Element.Background as Background
28import Element.Background as Background
28import Element.Border as Border
29import Element.Border as Border
@@ -53,15 +54,24 @@ myElement =
53 (text "stylish!")
54 (text "stylish!")
54```
55```
55
56
57+# Join the Elm UI Slack!
56
58
57−
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.
58
60
59## History
61## History
60
62
61The 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.
62
64
65+## Community Cookbook
66+
67+The 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.
63
68
69+## Notes
64
70
71+Make sure to have `<!DOCTYPE html>` on top of your application; otherwise, things might get messed up in some browsers.
65
72
73+## Contributing
66
74
75+Want to help out fixing bugs or reporting issues?
67
76
77+Please add issues you find, and if you want to verify code you want to contribute, please read how to run the tests [here](https://github.com/mdgriffith/elm-ui/blob/master/notes/RUNNING_TESTS.md).
benchmarks/README.mddeleted−14
@@ -1,14 +0,0 @@
1−# Render Benchmark
2−
3−A CLI for capturing rendering performance.
4−
5−```bash
6−# cd to this directory
7−yarn install
8−yarn bench
9−```
10−
11−We're interested in overall rendering performance. Right now that means measuring:
12−
13−1. Speed for first paint.
14−2. FPS for extended animations.
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/micro/Suite.elm+234−2
@@ -3,6 +3,13 @@ module Suite exposing (main)
3import Benchmark exposing (Benchmark)
3import Benchmark exposing (Benchmark)
4import Benchmark.Runner exposing (BenchmarkProgram, program)
4import Benchmark.Runner exposing (BenchmarkProgram, program)
5import Dict
5import Dict
6+import Element exposing (..)
7+import Element.Background as Background
8+import Element.Font as Font
9+import Html
10+import Html.Attributes
11+import Internal.Flag as Flag
12+import Internal.Model as Internal
6import Json.Encode
13import Json.Encode
7import Set
14import Set
8
15
@@ -23,11 +30,150 @@ main : BenchmarkProgram
23main =
30main =
24 program <|
31 program <|
25 Benchmark.describe "sample"
32 Benchmark.describe "sample"
26− [ dict
33+ [ --dict
27− , deduplications
34+ -- , deduplications
35+ -- renderPipeline
36+ gatheringAttributes
37+
38+ -- flagOperations
28 ]
39 ]
29
40
30
41
42+flagOperations =
43+ Benchmark.describe "Flag Operations"
44+ [ Benchmark.benchmark "check if present" <|
45+ \_ ->
46+ Flag.present Flag.height Flag.none
47+ , Benchmark.benchmark "add to flagt" <|
48+ \_ ->
49+ Flag.add Flag.height Flag.none
50+ ]
51+
52+
53+gatheringAttributes =
54+ Benchmark.describe "Gathering Attributes"
55+ [ Benchmark.benchmark "existing - gatherAttrRecursive" <|
56+ \_ ->
57+ Internal.gatherAttrRecursive
58+ (Internal.contextClasses Internal.AsEl)
59+ Internal.Generic
60+ Flag.none
61+ Internal.Untransformed
62+ []
63+ []
64+ Internal.NoNearbyChildren
65+ [ Element.width Element.shrink
66+ , Element.height Element.shrink
67+ ]
68+ , Benchmark.benchmark "Simple list filterMap" <|
69+ \_ ->
70+ List.filterMap
71+ identity
72+ [ Just "thing"
73+ , Just "other width"
74+ ]
75+ , Benchmark.benchmark "Simple list map" <|
76+ \_ ->
77+ List.map
78+ identity
79+ [ Just "thing"
80+ , Just "other width"
81+ ]
82+ , Benchmark.benchmark "Foldl base speed" <|
83+ \_ ->
84+ List.foldl
85+ (::)
86+ [ Just "thing"
87+ , Just "other width"
88+ ]
89+ , Benchmark.benchmark "Recursive" <|
90+ \_ ->
91+ recurse
92+ (::)
93+ [ Just "thing"
94+ , Just "other width"
95+ ]
96+ , Benchmark.benchmark "foldl - new record each iteration" <|
97+ \_ ->
98+ List.foldl flipFoldl
99+ { attributes = "class"
100+ , styles = []
101+ , node = "Node"
102+ , children = Nothing
103+ , has = Flag.none
104+ }
105+ -- we reverse first because we have to for the attr api.
106+ (List.reverse
107+ [ Just "thing"
108+ , Just "other width"
109+ ]
110+ )
111+ , Benchmark.benchmark "foldl - update" <|
112+ \_ ->
113+ List.foldl flipFoldlUpdate
114+ { attributes = "class"
115+ , styles = []
116+ , node = "Node"
117+ , children = Nothing
118+ , has = Flag.none
119+ }
120+ -- we reverse first because we have to for the attr api.
121+ (List.reverse
122+ [ Just "thing"
123+ , Just "other width"
124+ ]
125+ )
126+ , Benchmark.benchmark "String Concatenation" <|
127+ \_ ->
128+ "This"
129+ ++ "this"
130+ ++ "this"
131+ ++ "this"
132+ ++ "this"
133+ ++ "this"
134+ ++ "this"
135+ ++ "this"
136+ , Benchmark.benchmark "List String Concatenation" <|
137+ \_ ->
138+ String.concat
139+ [ "This"
140+ , "this"
141+ , "this"
142+ , "this"
143+ , "this"
144+ , "this"
145+ , "this"
146+ , "this"
147+ ]
148+ ]
149+
150+
151+flipFoldl result found =
152+ { attributes = "class" ++ found.attributes
153+ , styles = Just 1 :: found.styles
154+ , node = found.node
155+ , children = Nothing
156+ , has = Flag.add Flag.height found.has
157+ }
158+
159+
160+flipFoldlUpdate result found =
161+ { found
162+ | attributes = "class" ++ found.attributes
163+ , styles = Just 1 :: found.styles
164+ , has = Flag.add Flag.height found.has
165+ }
166+
167+
168+recurse fn ls =
169+ case ls of
170+ [] ->
171+ []
172+
173+ fst :: remain ->
174+ fn fst (recurse fn remain)
175+
176+
31deduplications =
177deduplications =
32 Benchmark.describe "deduplications"
178 Benchmark.describe "deduplications"
33 [ Benchmark.benchmark "fold - dedup, String concat"
179 [ Benchmark.benchmark "fold - dedup, String concat"
@@ -96,3 +242,89 @@ largeListJson =
96 in
242 in
97 ( "elem-" ++ String.fromInt wrappingI, Json.Encode.string ("elem-" ++ String.fromInt wrappingI) )
243 ( "elem-" ++ String.fromInt wrappingI, Json.Encode.string ("elem-" ++ String.fromInt wrappingI) )
98 )
244 )
245+
246+
247+renderPipeline =
248+ [ Benchmark.benchmark "build 2000 elements"
249+ (\_ ->
250+ elements 0 twoThousand
251+ )
252+ , Benchmark.benchmark "build 2000 html"
253+ (\_ ->
254+ html 0 twoThousand
255+ )
256+ ]
257+
258+
259+twoThousand =
260+ List.range 0 2000
261+
262+
263+elements i count =
264+ Element.layout []
265+ (Element.column [ spacing 8, centerX ]
266+ (List.map (viewEl i) count)
267+ )
268+
269+
270+viewEl selectedIndex index =
271+ el
272+ [ Background.color
273+ (if selectedIndex == index then
274+ pink
275+
276+ else
277+ white
278+ )
279+ , Font.color
280+ (if selectedIndex /= index then
281+ pink
282+
283+ else
284+ white
285+ )
286+ , padding 24
287+ , width (px 500)
288+ , height (px 70)
289+ ]
290+ (if selectedIndex == index then
291+ text "selected"
292+
293+ else
294+ text "Hello!"
295+ )
296+
297+
298+white =
299+ rgb 1 1 1
300+
301+
302+pink =
303+ rgb255 240 0 245
304+
305+
306+html i count =
307+ Html.div []
308+ [ Html.div []
309+ (List.map (viewHtmlElement i) count)
310+ ]
311+
312+
313+viewHtmlElement selectedIndex index =
314+ Html.div
315+ [ Html.Attributes.class
316+ (if selectedIndex == index then
317+ "white"
318+
319+ else
320+ "pink"
321+ )
322+ ]
323+ [ Html.div []
324+ [ if selectedIndex == index then
325+ Html.text "selected"
326+
327+ else
328+ Html.text "Hello!"
329+ ]
330+ ]
benchmarks/micro/elm.json+3−2
@@ -1,7 +1,8 @@
1{
1{
2 "type": "application",
2 "type": "application",
3 "source-directories": [
3 "source-directories": [
4− "."
4+ ".",
5+ "../../src"
5 ],
6 ],
6 "elm-version": "0.19.0",
7 "elm-version": "0.19.0",
7 "dependencies": {
8 "dependencies": {
@@ -11,6 +12,7 @@
11 "elm/html": "1.0.0",
12 "elm/html": "1.0.0",
12 "elm/json": "1.1.3",
13 "elm/json": "1.1.3",
13 "elm/time": "1.0.0",
14 "elm/time": "1.0.0",
15+ "elm/virtual-dom": "1.0.2",
14 "elm-explorations/benchmark": "1.0.1"
16 "elm-explorations/benchmark": "1.0.1"
15 },
17 },
16 "indirect": {
18 "indirect": {
@@ -18,7 +20,6 @@
18 "Skinney/murmur3": "2.0.8",
20 "Skinney/murmur3": "2.0.8",
19 "elm/regex": "1.0.0",
21 "elm/regex": "1.0.0",
20 "elm/url": "1.0.0",
22 "elm/url": "1.0.0",
21− "elm/virtual-dom": "1.0.2",
22 "mdgriffith/style-elements": "5.0.1"
23 "mdgriffith/style-elements": "5.0.1"
23 }
24 }
24 },
25 },
benchmarks/package.jsondeleted−24
@@ -1,24 +0,0 @@
1−{
2− "name": "render-benchmark",
3− "version": "1.0.0",
4− "description": "A CLI for capturing rendering performance",
5− "main": "index.js",
6− "scripts": {
7− "test": "echo \"Error: no test specified\" && exit 1",
8− "bench": "node runtime/index.js"
9− },
10− "repository": {
11− "type": "git",
12− "url": "git+https://github.com/mdgriffith/render-benchmark.git"
13− },
14− "author": "Matthew Griffith <mdg.griffith@gmail.com> (http://mechanical-elephant.com/)",
15− "license": "ISC",
16− "bugs": {
17− "url": "https://github.com/mdgriffith/render-benchmark/issues"
18− },
19− "homepage": "https://github.com/mdgriffith/render-benchmark#readme",
20− "dependencies": {
21− "node-elm-compiler": "^5.0.3",
22− "puppeteer": "^1.18.0"
23− }
24−}
benchmarks/runtime/benchPage.js+54−6
@@ -1,24 +1,44 @@
1const path = require('path');
1const path = require('path');
2
2
3−async function benchPage(page) {
3+async function benchPage(page, file) {
4 const client = await page.target().createCDPSession();
4 const client = await page.target().createCDPSession();
5 await client.send('Performance.enable');
5 await client.send('Performance.enable');
6 // await page.tracing.start({ path: 'trace.json' });
6 // await page.tracing.start({ path: 'trace.json' });
7− await page.goto(`file:${path.join(__dirname, '/../tmp/index.html')}`)
7+ await page.goto('file://' + path.resolve(file))
8
8
9− await page.waitFor(1000);
9+ await page.waitFor(5000);
10 // await page.tracing.stop();
10 // await page.tracing.stop();
11 const metrics = await client.send('Performance.getMetrics');
11 const metrics = await client.send('Performance.getMetrics');
12+
13+ const startMetrics = await page.metrics()
14+ await page.evaluate(() => { window.elmRefresh() })
15+ await page.waitFor(1500);
16+ const endMetrics = await page.metrics()
17+
12 await page.evaluate(() => { window.elmStartAnim() })
18 await page.evaluate(() => { window.elmStartAnim() })
13− await sleep(10000)
19+ await sleep(5000)
14 await page.evaluate(() => { window.elmStopAnim() })
20 await page.evaluate(() => { window.elmStopAnim() })
21+ await page.waitFor(2000);
22+ const finalMetrics = await page.metrics()
15 const frames = await page.evaluate(x => {
23 const frames = await page.evaluate(x => {
16 return Promise.resolve(window.metrics);
24 return Promise.resolve(window.metrics);
17 }, {});
25 }, {});
18−
19 return {
26 return {
20 name: frames.name,
27 name: frames.name,
28+ link: 'file://' + path.resolve(file),
21 perf: extract(metrics),
29 perf: extract(metrics),
30+ afterRefresh: {
31+ RecalcStyleDuration: endMetrics.RecalcStyleDuration - startMetrics.RecalcStyleDuration,
32+ LayoutDuration: endMetrics.LayoutDuration - startMetrics.LayoutDuration,
33+ ScriptDuration: endMetrics.ScriptDuration - startMetrics.ScriptDuration,
34+ JSHeapUsedSize: endMetrics.JSHeapUsedSize - startMetrics.JSHeapUsedSize
35+ },
36+ afterAnimation: {
37+ RecalcStyleDuration: finalMetrics.RecalcStyleDuration - endMetrics.RecalcStyleDuration,
38+ LayoutDuration: finalMetrics.LayoutDuration - endMetrics.LayoutDuration,
39+ ScriptDuration: finalMetrics.ScriptDuration - endMetrics.ScriptDuration,
40+ JSHeapUsedSize: finalMetrics.JSHeapUsedSize - endMetrics.JSHeapUsedSize
41+ },
22 frames: summarize(frames.frames),
42 frames: summarize(frames.frames),
23 }
43 }
24}
44}
@@ -29,7 +49,9 @@ function summarize(frames) {
29 median: median(frames),
49 median: median(frames),
30 mode: mode(frames),
50 mode: mode(frames),
31 avg: average(frames),
51 avg: average(frames),
32− fps: 1000 / average(frames)
52+ fps: 1000 / median(frames),
53+ min: minimum(frames),
54+ max: maximum(frames)
33 }
55 }
34}
56}
35
57
@@ -66,6 +88,32 @@ function mode(array) {
66 return maxEl;
88 return maxEl;
67}
89}
68
90
91+function minimum(values) {
92+ var m = null
93+ for (var i = 0; i < values.length; i++) {
94+ if (m == null) {
95+ m = values[i]
96+ } else if (values[i] < m) {
97+ m = values[i]
98+ }
99+ }
100+ return m
101+}
102+
103+
104+function maximum(values) {
105+ var m = null
106+ for (var i = 0; i < values.length; i++) {
107+ if (m == null) {
108+ m = values[i]
109+ } else if (values[i] > m) {
110+ m = values[i]
111+ }
112+ }
113+ return m
114+}
115+
116+
69function average(values) {
117function average(values) {
70 var total = 0;
118 var total = 0;
71 for (var i = 0; i < values.length; i++) {
119 for (var i = 0; i < values.length; i++) {
benchmarks/runtime/index.js+82−33
@@ -1,9 +1,13 @@
1const puppeteer = require('puppeteer');
1const puppeteer = require('puppeteer');
2const benchPage = require('./benchPage');
2const benchPage = require('./benchPage');
3+const chalk = require('chalk');
3var compileToString = require("node-elm-compiler").compileToString;
4var compileToString = require("node-elm-compiler").compileToString;
4const fs = require('fs');
5const fs = require('fs');
5
6
6
7
8+
9+
10+
7function write_entrypoint(item) {
11function write_entrypoint(item) {
8
12
9 const entrypoint = `module Main exposing (main)
13 const entrypoint = `module Main exposing (main)
@@ -12,24 +16,51 @@ import Benchmark.Render
12
16
13main = Benchmark.Render.toProgram ${item.module}.${item.value}
17main = Benchmark.Render.toProgram ${item.module}.${item.value}
14`
18`
15− fs.writeFileSync('./tmp/Main.elm', entrypoint);
19+ fs.writeFileSync('./benchmarks/tmp/Main.elm', entrypoint);
20+}
21+
22+async function compile_and_embed(config) {
23+ var template = fs.readFileSync(config.template)
24+ // we embed the compiled js to avoid having to start a server to read the app.
25+ await compileToString(config.elm, config.elmOptions).then(function (compiled_elm_code) {
26+ const compiled = eval(`\`${template}\``)
27+ fs.writeFileSync(config.target, compiled)
28+ });
16}
29}
17
30
18
31
19−function regoupResults(results) {
32+function regroupResults(results) {
20 var regrouped = {}
33 var regrouped = {}
21
34
22 for (var i = 0; i < results.length; i++) {
35 for (var i = 0; i < results.length; i++) {
23 if (results[i].group) {
36 if (results[i].group) {
24
37
25 var reorged = {
38 var reorged = {
39+ name: results[i].name
40+ , link: results[i].link
26− count: results[i].count
41+ , count: results[i].count
27 , fps: results[i].frames.fps
42 , fps: results[i].frames.fps
28 , timeToFirstPaintMS: results[i].perf.TimeToFirstPaintMS
43 , timeToFirstPaintMS: results[i].perf.TimeToFirstPaintMS
29 , nodes: results[i].perf.Nodes
44 , nodes: results[i].perf.Nodes
45+ , coldRender: {
30− , layoutSeconds: results[i].perf.LayoutDuration
46+ layoutSeconds: results[i].perf.LayoutDuration
31− , recalcStyleSeconds: results[i].perf.RecalcStyleDuration
47+ , recalcStyleSeconds: results[i].perf.RecalcStyleDuration
32− , scriptDurationSeconds: results[i].perf.ScriptDuration
48+ , scriptDurationSeconds: results[i].perf.ScriptDuration
49+
50+ }
51+ , warmRender: {
52+ layoutSeconds: results[i].afterRefresh.LayoutDuration
53+ , recalcStyleSeconds: results[i].afterRefresh.RecalcStyleDuration
54+ , scriptDurationSeconds: results[i].afterRefresh.ScriptDuration
55+
56+ }
57+ , extendedRender: {
58+ layoutSeconds: results[i].afterAnimation.LayoutDuration
59+ , recalcStyleSeconds: results[i].afterAnimation.RecalcStyleDuration
60+ , scriptDurationSeconds: results[i].afterAnimation.ScriptDuration
61+
62+ }
63+
33 }
64 }
34 if (results[i].group in regrouped) {
65 if (results[i].group in regrouped) {
35 regrouped[results[i].group].results.push(reorged)
66 regrouped[results[i].group].results.push(reorged)
@@ -42,53 +73,53 @@ function regoupResults(results) {
42}
73}
43
74
44
75
76+var contain = [{ module: "Contain", group: "html", count: 1024, value: "viewHtml8192" }
77+ , { module: "Contain", group: "contain", count: 128, value: "viewHtmlContain8192" }]
78+
79+
45−async function write_results(allResults) {
80+async function writeResults(allResults, resultsDir, name) {
46− var resultsDir = "results"
81+
47 if (!fs.existsSync(resultsDir)) {
82 if (!fs.existsSync(resultsDir)) {
48 fs.mkdirSync(resultsDir);
83 fs.mkdirSync(resultsDir);
49 }
84 }
50 var results = JSON.stringify(allResults)
85 var results = JSON.stringify(allResults)
51− var template = fs.readFileSync("./runtime/template/viewResults.html")
86+ var template = fs.readFileSync("./benchmarks/runtime/template/viewResults.html")
52 // we embed the compiled js to avoid having to start a server to read the app.
87 // we embed the compiled js to avoid having to start a server to read the app.
53− await compileToString(["./src/View/Results.elm"], { optimize: true }).then(function (compiled_elm_code) {
88+ await compileToString(["./src/View/Results.elm"], { optimize: true, cwd: "./benchmarks" }).then(function (compiled_elm_code) {
54 const compiled = eval(`\`${template}\``)
89 const compiled = eval(`\`${template}\``)
55− fs.writeFileSync(`./${resultsDir}/1.1.1-cand.html`, compiled)
90+ fs.writeFileSync(`./${resultsDir}/${name}/index.html`, compiled)
56 });
91 });
57
92
58 // fs.writeFileSync(`./${resultsDir}/1.1.1-candidate-optimized.json`, results)
93 // fs.writeFileSync(`./${resultsDir}/1.1.1-candidate-optimized.json`, results)
59}
94}
60
95
96+
61(async () => {
97(async () => {
62 const browser = await puppeteer.launch();
98 const browser = await puppeteer.launch();
99+
100+
63− var instances = [{ module: "Baseline", group: null, value: "bench" }
101+ var instances = [
64− , { module: "ManyElements", group: "elmUI", count: 1024, value: "elmUI1024" }
102+ { module: "ManyElements", group: "elmUI", count: 1024, value: "elmUI1024" }
65 , { module: "ManyElements", group: "elmUI", count: 128, value: "elmUI128" }
103 , { module: "ManyElements", group: "elmUI", count: 128, value: "elmUI128" }
66 , { module: "ManyElements", group: "elmUI", count: 2048, value: "elmUI2048" }
104 , { module: "ManyElements", group: "elmUI", count: 2048, value: "elmUI2048" }
67 , { module: "ManyElements", group: "elmUI", count: 24, value: "elmUI24" }
105 , { module: "ManyElements", group: "elmUI", count: 24, value: "elmUI24" }
68− , { module: "ManyElements", group: "elmUI", count: 256, value: "elmUI256" }
69 , { module: "ManyElements", group: "elmUI", count: 4096, value: "elmUI4096" }
106 , { module: "ManyElements", group: "elmUI", count: 4096, value: "elmUI4096" }
70− , { module: "ManyElements", group: "elmUI", count: 512, value: "elmUI512" }
71− , { module: "ManyElements", group: "elmUI", count: 64, value: "elmUI64" }
72 , { module: "ManyElements", group: "elmUI", count: 8192, value: "elmUI8192" }
107 , { module: "ManyElements", group: "elmUI", count: 8192, value: "elmUI8192" }
108+ , { module: "ManyElements", group: "elmUIVCSS", count: 1024, value: "elmUIVCSS1024" }
109+ , { module: "ManyElements", group: "elmUIVCSS", count: 2048, value: "elmUIVCSS2048" }
110+ , { module: "ManyElements", group: "elmUIVCSS", count: 24, value: "elmUIVCSS24" }
111+ , { module: "ManyElements", group: "elmUIVCSS", count: 4096, value: "elmUIVCSS4096" }
112+ , { module: "ManyElements", group: "elmUIVCSS", count: 8192, value: "elmUIVCSS8192" }
73 , { module: "ManyElements", group: "viewHtml", count: 1024, value: "viewHtml1024" }
113 , { module: "ManyElements", group: "viewHtml", count: 1024, value: "viewHtml1024" }
74− , { module: "ManyElements", group: "viewHtml", count: 128, value: "viewHtml128" }
75 , { module: "ManyElements", group: "viewHtml", count: 2048, value: "viewHtml2048" }
114 , { module: "ManyElements", group: "viewHtml", count: 2048, value: "viewHtml2048" }
76 , { module: "ManyElements", group: "viewHtml", count: 24, value: "viewHtml24" }
115 , { module: "ManyElements", group: "viewHtml", count: 24, value: "viewHtml24" }
77− , { module: "ManyElements", group: "viewHtml", count: 256, value: "viewHtml256" }
78 , { module: "ManyElements", group: "viewHtml", count: 4096, value: "viewHtml4096" }
116 , { module: "ManyElements", group: "viewHtml", count: 4096, value: "viewHtml4096" }
79− , { module: "ManyElements", group: "viewHtml", count: 512, value: "viewHtml512" }
80− , { module: "ManyElements", group: "viewHtml", count: 64, value: "viewHtml64" }
81 , { module: "ManyElements", group: "viewHtml", count: 8192, value: "viewHtml8192" }
117 , { module: "ManyElements", group: "viewHtml", count: 8192, value: "viewHtml8192" }
82 , { module: "ManyElements", group: "viewInline", count: 1024, value: "viewInline1024" }
118 , { module: "ManyElements", group: "viewInline", count: 1024, value: "viewInline1024" }
83− , { module: "ManyElements", group: "viewInline", count: 128, value: "viewInline128" }
84 , { module: "ManyElements", group: "viewInline", count: 2048, value: "viewInline2048" }
119 , { module: "ManyElements", group: "viewInline", count: 2048, value: "viewInline2048" }
85 , { module: "ManyElements", group: "viewInline", count: 24, value: "viewInline24" }
120 , { module: "ManyElements", group: "viewInline", count: 24, value: "viewInline24" }
86− , { module: "ManyElements", group: "viewInline", count: 256, value: "viewInline256" }
87 , { module: "ManyElements", group: "viewInline", count: 4096, value: "viewInline4096" }
121 , { module: "ManyElements", group: "viewInline", count: 4096, value: "viewInline4096" }
88− , { module: "ManyElements", group: "viewInline", count: 512, value: "viewInline512" }
89− , { module: "ManyElements", group: "viewInline", count: 64, value: "viewInline64" }
90 , { module: "ManyElements", group: "viewInline", count: 8192, value: "viewInline8192" }
122 , { module: "ManyElements", group: "viewInline", count: 8192, value: "viewInline8192" }
91−
92 ]
123 ]
93
124
94
125
@@ -97,31 +128,49 @@ async function write_results(allResults) {
97 fs.mkdirSync(dir);
128 fs.mkdirSync(dir);
98 }
129 }
99
130
131+ var dir = "./benchmarks/tmp"
132+ if (!fs.existsSync(dir)) {
133+ fs.mkdirSync(dir);
134+ }
135+
100 var allResults = []
136 var allResults = []
137+ const resultsDir = "benchmarks/results"
138+ const resultName = "1.2-cand"
139+
140+ if (!fs.existsSync(`./${resultsDir}/${resultName}/instances/`)) {
141+ fs.mkdirSync(`./${resultsDir}/${resultName}/instances/`, { recursive: true });
142+ }
143+
144+ console.log(`Beginning benchmark for ${resultName} →`)
101
145
102 for (var i = 0; i < instances.length; i++) {
146 for (var i = 0; i < instances.length; i++) {
103 var item = instances[i]
147 var item = instances[i]
104 write_entrypoint(item)
148 write_entrypoint(item)
149+ const test_file = `${resultsDir}/${resultName}/instances/${item.value}.html`
105− var template = fs.readFileSync("./runtime/template/run.html")
150+ var template = fs.readFileSync("./benchmarks/runtime/template/run.html")
151+ var cssom = fs.readFileSync("./experiments/virtual-css/cssom.js")
106 // we embed the compiled js to avoid having to start a server to read the app.
152 // we embed the compiled js to avoid having to start a server to read the app.
107− await compileToString(["./tmp/Main.elm"], { optimize: true }).then(function (compiled_elm_code) {
153+ await compileToString(["tmp/Main.elm"], { optimize: true, cwd: "./benchmarks" }).then(function (compiled_elm_code) {
108 const compiled = eval(`\`${template}\``)
154 const compiled = eval(`\`${template}\``)
109− fs.writeFileSync("./tmp/run.html", compiled)
155+ fs.writeFileSync(test_file, compiled)
110 });
156 });
111
157
112 const page = await browser.newPage();
158 const page = await browser.newPage();
113− const results = await benchPage(page);
159+ const results = await benchPage(page, test_file);
114 results.group = item.group
160 results.group = item.group
115 results.count = item.count
161 results.count = item.count
116 await page.close()
162 await page.close()
117− console.log(results)
163+ // console.log(results)
164+ console.log(" Benchmark of " + chalk.green(`${item.module}.${item.value}`) + " complete")
118 allResults.push(results)
165 allResults.push(results)
119 }
166 }
120 await browser.close();
167 await browser.close();
121
168
169+ allResults = regroupResults(allResults)
122
170
123− allResults = regoupResults(allResults)
171+ writeResults(allResults, resultsDir, resultName)
124−
172+ console.log()
125− write_results(allResults)
173+ console.log("Benchmark complete")
174+ console.log(" → " + chalk.green(`${resultsDir}/${resultName}/index.html`))
126
175
127})();
176})();
benchmarks/runtime/template/run.html+19
@@ -4,8 +4,27 @@
4 <meta charset="UTF-8">
4 <meta charset="UTF-8">
5 <title>Main</title>
5 <title>Main</title>
6 <script>
6 <script>
7+ ${cssom}
8+ </script>
9+ <script>
7 ${compiled_elm_code}
10 ${compiled_elm_code}
8 </script>
11 </script>
12+ <style>
13+ .box {
14+ width: 400px;
15+ height: 50px;
16+ border: solid thick;
17+ }
18+ .pink {
19+ background-color: pink;
20+ }
21+ .white {
22+ background-color: white;
23+ }
24+ .contain {
25+ contain: strict;
26+ }
27+ </style>
9</head>
28</head>
10
29
11<body>
30<body>
benchmarks/src/Benchmark/Render.elm+13−1
@@ -6,6 +6,8 @@ port module Benchmark.Render exposing (Benchmark, toProgram)
6import Browser
6import Browser
7import Browser.Events
7import Browser.Events
8import Html exposing (Html)
8import Html exposing (Html)
9+import Html.Attributes as Attr
10+import Html.Events as Events
9import Json.Decode as Decode
11import Json.Decode as Decode
10import Json.Encode as Encode
12import Json.Encode as Encode
11import Time
13import Time
@@ -50,7 +52,17 @@ toProgram render =
50 , view =
52 , view =
51 \model ->
53 \model ->
52 { title = render.name
54 { title = render.name
55+ , body =
56+ [ Html.div
57+ [ Attr.style "display" "flex"
58+ , Attr.style "flex-direction" "row"
59+ ]
60+ [ Html.button [ Events.onClick Refresh ] [ Html.text "Refresh" ]
61+ , Html.button [ Events.onClick StartAnim ] [ Html.text "StartAnim" ]
62+ , Html.button [ Events.onClick StopAnim ] [ Html.text "StopAnim" ]
63+ ]
53− , body = [ render.view model.model ]
64+ , render.view model.model
65+ ]
54 }
66 }
55 , update =
67 , update =
56 update render
68 update render
benchmarks/src/Contain.elmnew file+159
@@ -0,0 +1,159 @@
1+module Contain exposing
2+ ( viewHtml8192
3+ , viewHtmlContain8192
4+ )
5+
6+{-| -}
7+
8+import Benchmark.Render
9+import Element exposing (..)
10+import Element.Background as Background
11+import Element.Font as Font
12+import Element.Keyed
13+import Html
14+import Html.Attributes
15+import Internal.Model as Internal
16+
17+
18+type alias Model =
19+ { index : Int
20+ , numberOfElements : Int
21+ , elements : List Int
22+ }
23+
24+
25+type Msg
26+ = Refresh
27+ | Tick Float
28+
29+
30+viewHtml8192 : Benchmark.Render.Benchmark Model Msg
31+viewHtml8192 =
32+ viewHtml "viewHtml8192" 8192
33+
34+
35+viewHtmlContain8192 : Benchmark.Render.Benchmark Model Msg
36+viewHtmlContain8192 =
37+ viewHtmlContain "viewHtmlContain8192" 8192
38+
39+
40+white =
41+ rgb 1 1 1
42+
43+
44+pink =
45+ rgb255 240 0 245
46+
47+
48+{-| -}
49+viewHtml : String -> Int -> Benchmark.Render.Benchmark Model Msg
50+viewHtml name count =
51+ { name = name
52+ , init =
53+ { index = 0
54+ , numberOfElements = count
55+ , elements = List.range 0 (count - 1)
56+ }
57+ , view =
58+ \model ->
59+ Html.div []
60+ [ Html.div []
61+ (List.map (viewHtmlElement model.index) model.elements)
62+ ]
63+ , update =
64+ \msg model ->
65+ case msg of
66+ Refresh ->
67+ if model.index > model.numberOfElements then
68+ { model | index = 0 }
69+
70+ else
71+ { model | index = model.index + 1 }
72+
73+ Tick i ->
74+ if model.index > model.numberOfElements then
75+ { model | index = 0 }
76+
77+ else
78+ { model | index = model.index + 1 }
79+ , tick = Tick
80+ , refresh = Refresh
81+ }
82+
83+
84+viewHtmlElement selectedIndex index =
85+ Html.div
86+ [ Html.Attributes.class "box"
87+ , Html.Attributes.class
88+ (if selectedIndex == index then
89+ "white"
90+
91+ else
92+ "pink"
93+ )
94+ ]
95+ [ Html.div []
96+ [ if selectedIndex == index then
97+ Html.text "selected"
98+
99+ else
100+ Html.text "Hello!"
101+ ]
102+ ]
103+
104+
105+{-| -}
106+viewHtmlContain : String -> Int -> Benchmark.Render.Benchmark Model Msg
107+viewHtmlContain name count =
108+ { name = name
109+ , init =
110+ { index = 0
111+ , numberOfElements = count
112+ , elements = List.range 0 (count - 1)
113+ }
114+ , view =
115+ \model ->
116+ Html.div []
117+ [ Html.div []
118+ (List.map (viewHtmlElementContain model.index) model.elements)
119+ ]
120+ , update =
121+ \msg model ->
122+ case msg of
123+ Refresh ->
124+ if model.index > model.numberOfElements then
125+ { model | index = 0 }
126+
127+ else
128+ { model | index = model.index + 1 }
129+
130+ Tick i ->
131+ if model.index > model.numberOfElements then
132+ { model | index = 0 }
133+
134+ else
135+ { model | index = model.index + 1 }
136+ , tick = Tick
137+ , refresh = Refresh
138+ }
139+
140+
141+viewHtmlElementContain selectedIndex index =
142+ Html.div
143+ [ Html.Attributes.class "box contain"
144+ , Html.Attributes.class
145+ (if selectedIndex == index then
146+ "white"
147+
148+ else
149+ "pink"
150+ )
151+ ]
152+ [ Html.div []
153+ [ if selectedIndex == index then
154+ Html.text "selected"
155+
156+ else
157+ Html.text "Hello!"
158+ ]
159+ ]
benchmarks/src/ManyElements.elm+134−13
@@ -8,6 +8,15 @@ module ManyElements exposing
8 , elmUI512
8 , elmUI512
9 , elmUI64
9 , elmUI64
10 , elmUI8192
10 , elmUI8192
11+ , elmUIVCSS1024
12+ , elmUIVCSS128
13+ , elmUIVCSS2048
14+ , elmUIVCSS24
15+ , elmUIVCSS256
16+ , elmUIVCSS4096
17+ , elmUIVCSS512
18+ , elmUIVCSS64
19+ , elmUIVCSS8192
11 , viewHtml1024
20 , viewHtml1024
12 , viewHtml128
21 , viewHtml128
13 , viewHtml2048
22 , viewHtml2048
@@ -37,6 +46,7 @@ import Element.Font as Font
37import Element.Keyed
46import Element.Keyed
38import Html
47import Html
39import Html.Attributes
48import Html.Attributes
49+import Internal.Model as Internal
40
50
41
51
42
52
@@ -89,6 +99,55 @@ elmUI8192 =
89
99
90
100
91
101
102+{- Elm UI VirtualCSS -}
103+
104+
105+elmUIVCSS24 : Benchmark.Render.Benchmark Model Msg
106+elmUIVCSS24 =
107+ elmUIVCSS "elmUIVCSS24" 24
108+
109+
110+elmUIVCSS64 : Benchmark.Render.Benchmark Model Msg
111+elmUIVCSS64 =
112+ elmUIVCSS "elmUIVCSS64" 64
113+
114+
115+elmUIVCSS128 : Benchmark.Render.Benchmark Model Msg
116+elmUIVCSS128 =
117+ elmUIVCSS "elmUIVCSS128" 128
118+
119+
120+elmUIVCSS256 : Benchmark.Render.Benchmark Model Msg
121+elmUIVCSS256 =
122+ elmUIVCSS "elmUIVCSS256" 256
123+
124+
125+elmUIVCSS512 : Benchmark.Render.Benchmark Model Msg
126+elmUIVCSS512 =
127+ elmUIVCSS "elmUIVCSS512" 512
128+
129+
130+elmUIVCSS1024 : Benchmark.Render.Benchmark Model Msg
131+elmUIVCSS1024 =
132+ elmUIVCSS "elmUIVCSS1024" 1024
133+
134+
135+elmUIVCSS2048 : Benchmark.Render.Benchmark Model Msg
136+elmUIVCSS2048 =
137+ elmUIVCSS "elmUIVCSS2048" 2048
138+
139+
140+elmUIVCSS4096 : Benchmark.Render.Benchmark Model Msg
141+elmUIVCSS4096 =
142+ elmUIVCSS "elmUIVCSS4096" 4096
143+
144+
145+elmUIVCSS8192 : Benchmark.Render.Benchmark Model Msg
146+elmUIVCSS8192 =
147+ elmUIVCSS "elmUIVCSS8192" 8192
148+
149+
150+
92{- Normal Html -}
151{- Normal Html -}
93
152
94
153
@@ -203,8 +262,8 @@ type Msg
203
262
204
263
205{-| -}
264{-| -}
206−elmUI : String -> Int -> Benchmark.Render.Benchmark Model Msg
265+elmUIVCSS : String -> Int -> Benchmark.Render.Benchmark Model Msg
207−elmUI name count =
266+elmUIVCSS name count =
208 { name = name
267 { name = name
209 , init =
268 , init =
210 { index = 0
269 { index = 0
@@ -212,7 +271,17 @@ elmUI name count =
212 , elements = List.range 0 (count - 1)
271 , elements = List.range 0 (count - 1)
213 }
272 }
214 , view =
273 , view =
215− view
274+ \model ->
275+ Element.layoutWith
276+ { options =
277+ [ Internal.RenderModeOption
278+ Internal.WithVirtualCss
279+ ]
280+ }
281+ []
282+ (Element.column [ spacing 8, centerX ]
283+ (List.map (viewEl model.index) model.elements)
284+ )
216 , update =
285 , update =
217 \msg model ->
286 \msg model ->
218 case msg of
287 case msg of
@@ -234,16 +303,44 @@ elmUI name count =
234 }
303 }
235
304
236
305
306+{-| -}
307+elmUI : String -> Int -> Benchmark.Render.Benchmark Model Msg
308+elmUI name count =
309+ { name = name
310+ , init =
311+ { index = 0
312+ , numberOfElements = count
313+ , elements = List.range 0 (count - 1)
314+ }
315+ , view =
237−view model =
316+ \model ->
238− Element.layout []
317+ Element.layout []
239− (Element.Keyed.column [ spacing 8, centerX ]
318+ (Element.column [ spacing 8, centerX ]
240− (List.map (viewEl model.index) model.elements)
319+ (List.map (viewEl model.index) model.elements)
241− )
320+ )
321+ , update =
322+ \msg model ->
323+ case msg of
324+ Refresh ->
325+ if model.index > model.numberOfElements then
326+ { model | index = 0 }
327+
328+ else
329+ { model | index = model.index + 1 }
330+
331+ Tick i ->
332+ if model.index > model.numberOfElements then
333+ { model | index = 0 }
334+
335+ else
336+ { model | index = model.index + 1 }
337+ , tick = Tick
338+ , refresh = Refresh
339+ }
242
340
243
341
244viewEl selectedIndex index =
342viewEl selectedIndex index =
245− ( String.fromInt index
246− , el
343+ el
247 [ Background.color
344 [ Background.color
248 (if selectedIndex == index then
345 (if selectedIndex == index then
249 pink
346 pink
@@ -268,7 +365,6 @@ viewEl selectedIndex index =
268 else
365 else
269 text "Hello!"
366 text "Hello!"
270 )
367 )
271− )
272
368
273
369
274white =
370white =
@@ -279,6 +375,21 @@ pink =
279 rgb255 240 0 245
375 rgb255 240 0 245
280
376
281
377
378+pinkish i =
379+ rgb255 (wrap255 (i + 240)) 0 245
380+
381+
382+wrap255 i =
383+ if i > 255 then
384+ 0
385+
386+ else if i < 0 then
387+ 0
388+
389+ else
390+ i
391+
392+
282{-| -}
393{-| -}
283viewHtml : String -> Int -> Benchmark.Render.Benchmark Model Msg
394viewHtml : String -> Int -> Benchmark.Render.Benchmark Model Msg
284viewHtml name count =
395viewHtml name count =
@@ -374,14 +485,14 @@ viewInline name count =
374viewInlineHtmlElement selectedIndex index =
485viewInlineHtmlElement selectedIndex index =
375 Html.div
486 Html.div
376 (if selectedIndex == index then
487 (if selectedIndex == index then
377− [ Html.Attributes.style "background-color" "rgb(240, 0, 245)"
488+ [ Html.Attributes.style "background-color" (renderColor (wrap255 (index + 240)) 0 245)
378 , Html.Attributes.style "color" "white"
489 , Html.Attributes.style "color" "white"
379 , Html.Attributes.style "padding" "24"
490 , Html.Attributes.style "padding" "24"
380 ]
491 ]
381
492
382 else
493 else
383 [ Html.Attributes.style "background-color" "white"
494 [ Html.Attributes.style "background-color" "white"
384− , Html.Attributes.style "color" "rgb(240, 0, 245)"
495+ , Html.Attributes.style "color" (renderColor (wrap255 (index + 240)) 0 245)
385 , Html.Attributes.style "padding" "24"
496 , Html.Attributes.style "padding" "24"
386 ]
497 ]
387 )
498 )
@@ -393,3 +504,13 @@ viewInlineHtmlElement selectedIndex index =
393 Html.text "Hello!"
504 Html.text "Hello!"
394 ]
505 ]
395 ]
506 ]
507+
508+
509+renderColor r g b =
510+ "rgb("
511+ ++ String.fromInt r
512+ ++ ","
513+ ++ String.fromInt g
514+ ++ ","
515+ ++ String.fromInt b
516+ ++ ")"
benchmarks/src/View/Results.elm+202−72
@@ -3,11 +3,15 @@ port module View.Results exposing (main)
3import Browser
3import Browser
4import Color
4import Color
5import Html
5import Html
6−import Html.Attributes exposing (class)
6+import Html.Attributes as Attr exposing (class)
7import LineChart as LineChart
7import LineChart as LineChart
8import LineChart.Area as Area
8import LineChart.Area as Area
9import LineChart.Axis as Axis
9import LineChart.Axis as Axis
10import LineChart.Axis.Intersection as Intersection
10import LineChart.Axis.Intersection as Intersection
11+import LineChart.Axis.Line as AxisLine
12+import LineChart.Axis.Range as Range
13+import LineChart.Axis.Ticks as Ticks
14+import LineChart.Axis.Title as Title
11import LineChart.Colors as Colors
15import LineChart.Colors as Colors
12import LineChart.Container as Container
16import LineChart.Container as Container
13import LineChart.Dots as Dots
17import LineChart.Dots as Dots
@@ -19,6 +23,22 @@ import LineChart.Legends as Legends
19import LineChart.Line as Line
23import LineChart.Line as Line
20
24
21
25
26+row children =
27+ Html.div
28+ [ Attr.style "display" "flex"
29+ , Attr.style "flex-direction" "row"
30+ ]
31+ children
32+
33+
34+column children =
35+ Html.div
36+ [ Attr.style "display" "flex"
37+ , Attr.style "flex-direction" "column"
38+ ]
39+ children
40+
41+
22main =
42main =
23 Browser.document
43 Browser.document
24 { init =
44 { init =
@@ -32,12 +52,60 @@ main =
32 , body =
52 , body =
33 [ Html.div
53 [ Html.div
34 [ class "container" ]
54 [ class "container" ]
55+ [ column
56+ [ Html.h2 [] [ Html.text "Benchmarked Pages" ]
57+ , row (List.map viewLinks model.data)
58+ ]
59+ , row
35− ([ viewNodes model.data
60+ [ -- viewNodes model.data
36− , viewFps model.data
61+ viewFps model.data
37− , viewTimeToPaint model.data
62+ , viewTimeToPaint model.data
38− ]
63+ ]
64+ , row
65+ [ column
66+ (List.map
67+ (\data ->
68+ viewRenderBreakdown
69+ { get = .coldRender
70+ , getCount = .count
71+ , name = data.name
72+ , flavor = "Cold render"
73+ , range = ( 0, 1.2 )
74+ }
75+ (List.sortBy .count data.results)
76+ )
77+ model.data
78+ )
79+ , column
80+ (List.map
81+ (\data ->
82+ viewRenderBreakdown
83+ { get = .warmRender
84+ , getCount = .count
85+ , name = data.name
86+ , flavor = "Warm render"
87+ , range = ( 0, 1.2 )
88+ }
89+ (List.sortBy .count data.results)
90+ )
91+ model.data
92+ )
93+ , column
94+ (List.map
95+ (\data ->
96+ viewRenderBreakdown
97+ { get = .extendedRender
98+ , getCount = .count
99+ , name = data.name
100+ , flavor = "Long Animation"
101+ , range = ( 0, 5 )
102+ }
103+ (List.sortBy .count data.results)
104+ )
39− ++ List.map viewTimeDistribution model.data
105+ model.data
40− )
106+ )
107+ ]
108+ ]
41 ]
109 ]
42 }
110 }
43 , update =
111 , update =
@@ -61,6 +129,24 @@ update msg model =
61 ( { data = data }, Cmd.none )
129 ( { data = data }, Cmd.none )
62
130
63
131
132+viewLinks data =
133+ column
134+ (Html.h1 [] [ Html.text data.name ]
135+ :: (data.results
136+ |> List.sortBy .link
137+ |> List.map viewSingleLink
138+ )
139+ )
140+
141+
142+viewSingleLink result =
143+ Html.a
144+ [ Attr.href result.link
145+ , Attr.style "padding-right" "30px"
146+ ]
147+ [ Html.text result.name ]
148+
149+
64
150
65-- DATA
151-- DATA
66
152
@@ -79,11 +165,23 @@ type alias Data =
79
165
80
166
81type alias Render =
167type alias Render =
168+ { name : String
82− { count : Float
169+ , count : Float
83 , fps : Float
170 , fps : Float
84 , timeToFirstPaintMS : Float
171 , timeToFirstPaintMS : Float
85 , nodes : Float
172 , nodes : Float
173+ , link : String
174+ , coldRender :
175+ Breakdown
176+ , warmRender :
177+ Breakdown
178+ , extendedRender :
179+ Breakdown
180+ }
181+
182+
183+type alias Breakdown =
86− , layoutSeconds : Float
184+ { layoutSeconds : Float
87 , recalcStyleSeconds : Float
185 , recalcStyleSeconds : Float
88 , scriptDurationSeconds : Float
186 , scriptDurationSeconds : Float
89 }
187 }
@@ -112,12 +210,27 @@ getShape i =
112 Dots.triangle
210 Dots.triangle
113
211
114
212
213+graphWidth =
214+ 800
215+
216+
217+graphHeight =
218+ 500
219+
220+
221+container =
222+ Container.styled "line-chart-1"
223+ [ ( "font-family", "monospace" )
224+ , ( "flex-shrink", "0" )
225+ ]
226+
227+
115viewFps : List Data -> Html.Html msg
228viewFps : List Data -> Html.Html msg
116viewFps datums =
229viewFps datums =
117 LineChart.viewCustom
230 LineChart.viewCustom
118− { y = Axis.default 800 "FPS" .fps
231+ { y = Axis.default graphHeight "FPS" .fps
119− , x = Axis.default 1400 "Base Count" .count
232+ , x = Axis.default graphWidth "Base Count" .count
120− , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
233+ , container = container
121 , interpolation =
234 , interpolation =
122 -- Try out these different configs!
235 -- Try out these different configs!
123 -- Interpolation.linear
236 -- Interpolation.linear
@@ -143,9 +256,9 @@ viewResult i data =
143viewTimeToPaint : List Data -> Html.Html msg
256viewTimeToPaint : List Data -> Html.Html msg
144viewTimeToPaint datums =
257viewTimeToPaint datums =
145 LineChart.viewCustom
258 LineChart.viewCustom
146− { y = Axis.default 800 "First Paint" (max 0 << .timeToFirstPaintMS)
259+ { y = Axis.default graphHeight "First Paint" (max 0 << .timeToFirstPaintMS)
147− , x = Axis.default 1400 "Base Count" .count
260+ , x = Axis.default graphWidth "Base Count" .count
148− , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
261+ , container = container
149 , interpolation =
262 , interpolation =
150 -- Try out these different configs!
263 -- Try out these different configs!
151 -- Interpolation.linear
264 -- Interpolation.linear
@@ -167,9 +280,9 @@ viewTimeToPaint datums =
167viewNodes : List Data -> Html.Html msg
280viewNodes : List Data -> Html.Html msg
168viewNodes datums =
281viewNodes datums =
169 LineChart.viewCustom
282 LineChart.viewCustom
170− { y = Axis.default 800 "Nodes" .nodes
283+ { y = Axis.default graphHeight "Nodes" .nodes
171− , x = Axis.default 1400 "Base Count" .count
284+ , x = Axis.default graphWidth "Base Count" .count
172− , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
285+ , container = container
173 , interpolation =
286 , interpolation =
174 -- Try out these different configs!
287 -- Try out these different configs!
175 -- Interpolation.linear
288 -- Interpolation.linear
@@ -188,62 +301,79 @@ viewNodes datums =
188 (List.indexedMap viewResult datums)
301 (List.indexedMap viewResult datums)
189
302
190
303
304+viewRenderBreakdown :
305+ { get : Render -> Breakdown
306+ , getCount : Render -> Float
307+ , name : String
308+ , flavor : String
309+ , range : ( Float, Float )
310+ }
311+ -> List Render
191−viewTimeDistribution : Data -> Html.Html msg
312+ -> Html.Html msg
192−viewTimeDistribution data =
313+viewRenderBreakdown { get, getCount, name, flavor, range } results =
193− let
314+ Html.div []
194− sorted =
315+ [ Html.h2 [] [ Html.text (name ++ ", " ++ flavor) ]
195− List.sortBy .count data.results
196− in
197− LineChart.viewCustom
316+ , LineChart.viewCustom
198− { y = Axis.default 800 (data.name ++ "(s)") .seconds
317+ { y =
199− , x = Axis.default 1400 "Base Count" .count
318+ Axis.custom
200− , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
319+ { title = Title.default (name ++ "(s)")
201− , interpolation =
320+ , variable = Just << .seconds
202− -- Try out these different configs!
321+ , pixels = graphHeight
203− -- Interpolation.linear
322+ , range = Range.window (Tuple.first range) (Tuple.second range)
204− Interpolation.monotone
323+ , axisLine = AxisLine.full Colors.black
205−
324+ , ticks = Ticks.default
206− -- Interpolation.stepped
207− , intersection = Intersection.default
208− , legends = Legends.default
209− , events = Events.default
210− , junk = Junk.default
211− , grid = Grid.default
212− , area = Area.stacked 0.5
213− , line = Line.default
214− , dots = Dots.default
215− }
216− [ LineChart.line (getColor 0)
217− (getShape 0)
218− "Layout"
219− (List.map
220− (\x ->
221− { seconds = x.layoutSeconds
222− , count = x.count
223 }
325 }
326+ , x = Axis.default graphWidth "Base Count" .count
327+ , container = container
328+ , interpolation =
329+ -- Try out these different configs!
330+ -- Interpolation.linear
331+ Interpolation.monotone
332+
333+ -- Interpolation.stepped
334+ , intersection = Intersection.default
335+ , legends = Legends.default
336+ , events = Events.default
337+ , junk = Junk.default
338+ , grid = Grid.default
339+ , area = Area.stacked 0.5
340+ , line = Line.default
341+ , dots = Dots.default
342+ }
343+ [ LineChart.line (getColor 0)
344+ (getShape 0)
345+ "Layout"
346+ (List.map
347+ (\x ->
348+ { seconds = .layoutSeconds (get x)
349+ , count = getCount x
350+ }
351+ )
352+ results
224 )
353 )
225− sorted
226− )
227− , LineChart.line (getColor 1)
354+ , LineChart.line (getColor 1)
228− (getShape 1)
355+ (getShape 1)
229− "RecalcStyle"
356+ "RecalcStyle"
230− (List.map
357+ (List.map
231− (\x ->
358+ (\x ->
359+ { seconds =
232− { seconds = x.recalcStyleSeconds
360+ .recalcStyleSeconds (get x)
233− , count = x.count
361+ , count = getCount x
234− }
362+ }
363+ )
364+ results
235 )
365 )
236− sorted
237− )
238− , LineChart.line (getColor 2)
366+ , LineChart.line (getColor 2)
239− (getShape 2)
367+ (getShape 2)
240− "Script"
368+ "Script"
241− (List.map
369+ (List.map
242− (\x ->
370+ (\x ->
371+ { seconds =
243− { seconds = x.scriptDurationSeconds
372+ .scriptDurationSeconds (get x)
244− , count = x.count
373+ , count = getCount x
245− }
374+ }
375+ )
376+ results
246 )
377 )
247− sorted
378+ ]
248− )
249 ]
379 ]
benchmarks/yarn.lockdeleted−371
@@ -1,371 +0,0 @@
1−# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2−# yarn lockfile v1
3−
4−
5−agent-base@^4.1.0:
6− version "4.3.0"
7− resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
8− integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
9− dependencies:
10− es6-promisify "^5.0.0"
11−
12−async-limiter@~1.0.0:
13− version "1.0.0"
14− resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
15− integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
16−
17−balanced-match@^1.0.0:
18− version "1.0.0"
19− resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
20− integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
21−
22−brace-expansion@^1.1.7:
23− version "1.1.11"
24− resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
25− integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
26− dependencies:
27− balanced-match "^1.0.0"
28− concat-map "0.0.1"
29−
30−buffer-from@^1.0.0:
31− version "1.1.1"
32− resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
33− integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
34−
35−concat-map@0.0.1:
36− version "0.0.1"
37− resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
38− integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
39−
40−concat-stream@1.6.2:
41− version "1.6.2"
42− resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
43− integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
44− dependencies:
45− buffer-from "^1.0.0"
46− inherits "^2.0.3"
47− readable-stream "^2.2.2"
48− typedarray "^0.0.6"
49−
50−core-util-is@~1.0.0:
51− version "1.0.2"
52− resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
53− integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
54−
55−cross-spawn@4.0.0:
56− version "4.0.0"
57− resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.0.tgz#8254774ab4786b8c5b3cf4dfba66ce563932c252"
58− integrity sha1-glR3SrR4a4xbPPTfumbOVjkywlI=
59− dependencies:
60− lru-cache "^4.0.1"
61− which "^1.2.9"
62−
63−debug@2.6.9:
64− version "2.6.9"
65− resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
66− integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
67− dependencies:
68− ms "2.0.0"
69−
70−debug@^3.1.0:
71− version "3.2.6"
72− resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
73− integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
74− dependencies:
75− ms "^2.1.1"
76−
77−debug@^4.1.0:
78− version "4.1.1"
79− resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
80− integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
81− dependencies:
82− ms "^2.1.1"
83−
84−es6-promise@^4.0.3:
85− version "4.2.8"
86− resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
87− integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
88−
89−es6-promisify@^5.0.0:
90− version "5.0.0"
91− resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
92− integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
93− dependencies:
94− es6-promise "^4.0.3"
95−
96−extract-zip@^1.6.6:
97− version "1.6.7"
98− resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
99− integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
100− dependencies:
101− concat-stream "1.6.2"
102− debug "2.6.9"
103− mkdirp "0.5.1"
104− yauzl "2.4.1"
105−
106−fd-slicer@~1.0.1:
107− version "1.0.1"
108− resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
109− integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=
110− dependencies:
111− pend "~1.2.0"
112−
113−find-elm-dependencies@2.0.1:
114− version "2.0.1"
115− resolved "https://registry.yarnpkg.com/find-elm-dependencies/-/find-elm-dependencies-2.0.1.tgz#802e4e58379ad7ee7f30e048f8695ab732cf8132"
116− integrity sha512-KBxPjc7J1CLw90jVateMKvMYMROZRTR7/QT2I3MxT+7I6KuUQUMNUFuS/eQXQnMnyElGKQ1iyPbe7GnEuYnFXw==
117− dependencies:
118− firstline "1.2.0"
119− lodash "4.17.11"
120−
121−firstline@1.2.0:
122− version "1.2.0"
123− resolved "https://registry.yarnpkg.com/firstline/-/firstline-1.2.0.tgz#c9f4886e7f7fbf0afc12d71941dce06b192aea05"
124− integrity sha1-yfSIbn9/vwr8EtcZQdzgaxkq6gU=
125−
126−fs.realpath@^1.0.0:
127− version "1.0.0"
128− resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
129− integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
130−
131−glob@^7.1.3:
132− version "7.1.4"
133− resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
134− integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
135− dependencies:
136− fs.realpath "^1.0.0"
137− inflight "^1.0.4"
138− inherits "2"
139− minimatch "^3.0.4"
140− once "^1.3.0"
141− path-is-absolute "^1.0.0"
142−
143−https-proxy-agent@^2.2.1:
144− version "2.2.1"
145− resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
146− integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
147− dependencies:
148− agent-base "^4.1.0"
149− debug "^3.1.0"
150−
151−inflight@^1.0.4:
152− version "1.0.6"
153− resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
154− integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
155− dependencies:
156− once "^1.3.0"
157− wrappy "1"
158−
159−inherits@2, inherits@^2.0.3, inherits@~2.0.3:
160− version "2.0.4"
161− resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
162− integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
163−
164−isarray@~1.0.0:
165− version "1.0.0"
166− resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
167− integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
168−
169−isexe@^2.0.0:
170− version "2.0.0"
171− resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
172− integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
173−
174−lodash@4.17.11:
175− version "4.17.11"
176− resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
177− integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
178−
179−lru-cache@^4.0.1:
180− version "4.1.5"
181− resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
182− integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
183− dependencies:
184− pseudomap "^1.0.2"
185− yallist "^2.1.2"
186−
187−mime@^2.0.3:
188− version "2.4.4"
189− resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
190− integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
191−
192−minimatch@^3.0.4:
193− version "3.0.4"
194− resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
195− integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
196− dependencies:
197− brace-expansion "^1.1.7"
198−
199−minimist@0.0.8:
200− version "0.0.8"
201− resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
202− integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
203−
204−mkdirp@0.5.1:
205− version "0.5.1"
206− resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
207− integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
208− dependencies:
209− minimist "0.0.8"
210−
211−ms@2.0.0:
212− version "2.0.0"
213− resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
214− integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
215−
216−ms@^2.1.1:
217− version "2.1.2"
218− resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
219− integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
220−
221−node-elm-compiler@^5.0.3:
222− version "5.0.3"
223− resolved "https://registry.yarnpkg.com/node-elm-compiler/-/node-elm-compiler-5.0.3.tgz#69ffe02c0db4948c4ad9a4f91f8e45b0840c7943"
224− integrity sha512-I3CWm/ExYYQ/a9bjB0OL9VsGa3Lmgbb8QOs4y2kEiB/DTkTqkcTaCr/lVyOYjRpgR25TsmOBATscsg6H6aC9Hg==
225− dependencies:
226− cross-spawn "4.0.0"
227− find-elm-dependencies "2.0.1"
228− lodash "4.17.11"
229− temp "^0.8.3"
230−
231−once@^1.3.0:
232− version "1.4.0"
233− resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
234− integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
235− dependencies:
236− wrappy "1"
237−
238−os-tmpdir@^1.0.0:
239− version "1.0.2"
240− resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
241− integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
242−
243−path-is-absolute@^1.0.0:
244− version "1.0.1"
245− resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
246− integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
247−
248−pend@~1.2.0:
249− version "1.2.0"
250− resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
251− integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
252−
253−process-nextick-args@~2.0.0:
254− version "2.0.1"
255− resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
256− integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
257−
258−progress@^2.0.1:
259− version "2.0.3"
260− resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
261− integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
262−
263−proxy-from-env@^1.0.0:
264− version "1.0.0"
265− resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
266− integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
267−
268−pseudomap@^1.0.2:
269− version "1.0.2"
270− resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
271− integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
272−
273−puppeteer@^1.18.0:
274− version "1.18.1"
275− resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.18.1.tgz#4a66f3bdab01115ededf70443ec904c99917a815"
276− integrity sha512-luUy0HPSuWPsPZ1wAp6NinE0zgetWtudf5zwZ6dHjMWfYpTQcmKveFRox7VBNhQ98OjNA9PQ9PzQyX8k/KrxTg==
277− dependencies:
278− debug "^4.1.0"
279− extract-zip "^1.6.6"
280− https-proxy-agent "^2.2.1"
281− mime "^2.0.3"
282− progress "^2.0.1"
283− proxy-from-env "^1.0.0"
284− rimraf "^2.6.1"
285− ws "^6.1.0"
286−
287−readable-stream@^2.2.2:
288− version "2.3.6"
289− resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
290− integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
291− dependencies:
292− core-util-is "~1.0.0"
293− inherits "~2.0.3"
294− isarray "~1.0.0"
295− process-nextick-args "~2.0.0"
296− safe-buffer "~5.1.1"
297− string_decoder "~1.1.1"
298− util-deprecate "~1.0.1"
299−
300−rimraf@^2.6.1:
301− version "2.6.3"
302− resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
303− integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
304− dependencies:
305− glob "^7.1.3"
306−
307−rimraf@~2.2.6:
308− version "2.2.8"
309− resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
310− integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=
311−
312−safe-buffer@~5.1.0, safe-buffer@~5.1.1:
313− version "5.1.2"
314− resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
315− integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
316−
317−string_decoder@~1.1.1:
318− version "1.1.1"
319− resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
320− integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
321− dependencies:
322− safe-buffer "~5.1.0"
323−
324−temp@^0.8.3:
325− version "0.8.3"
326− resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59"
327− integrity sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=
328− dependencies:
329− os-tmpdir "^1.0.0"
330− rimraf "~2.2.6"
331−
332−typedarray@^0.0.6:
333− version "0.0.6"
334− resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
335− integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
336−
337−util-deprecate@~1.0.1:
338− version "1.0.2"
339− resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
340− integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
341−
342−which@^1.2.9:
343− version "1.3.1"
344− resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
345− integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
346− dependencies:
347− isexe "^2.0.0"
348−
349−wrappy@1:
350− version "1.0.2"
351− resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
352− integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
353−
354−ws@^6.1.0:
355− version "6.2.1"
356− resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
357− integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
358− dependencies:
359− async-limiter "~1.0.0"
360−
361−yallist@^2.1.2:
362− version "2.1.2"
363− resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
364− integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
365−
366−yauzl@2.4.1:
367− version "2.4.1"
368− resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
369− integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
370− dependencies:
371− fd-slicer "~1.0.1"
cases/open/MultilineInput.elmdeleted−89
@@ -1,89 +0,0 @@
1−module Main exposing (main)
2−
3−import Browser
4−import Element exposing (..)
5−import Element.Input exposing (labelLeft, multiline)
6−import Html exposing (Html)
7−import Html.Events exposing (onClick)
8−
9−
10−type alias Model =
11− { count : Int }
12−
13−
14−initialModel : Model
15−initialModel =
16− { count = 0 }
17−
18−
19−type Msg
20− = Increment
21− | Decrement
22− | MLChanged String
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− MLChanged s ->
35− model
36−
37−
38−testtxt =
39− "orem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it?"
40−
41−
42−testtxtnewlines =
43− """orem Ipsum is simply dummy text of the printing
44−and typesetting industry. Lorem Ipsum has been the industry's
45−standard dummy text ever since the 1500s, when an unknown
46−printer took a galley of type and scrambled it to make a type specimen book.
47−It has survived not only five centuries, but also the leap into electronic
48−typesetting
49−, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it?"""
50−
51−
52−view : Model -> Html Msg
53−view model =
54− layout [] <|
55− column
56− [ width (px 500)
57− , spacing 50
58− ]
59− [ multiline []
60− { onChange = MLChanged
61− , text = testtxt
62− , placeholder = Nothing
63− , spellcheck = False
64− , label = labelLeft [ width (px 200) ] <| text "Default height"
65− }
66− , multiline [ height shrink ]
67− { onChange = MLChanged
68− , text = testtxt
69− , placeholder = Nothing
70− , spellcheck = False
71− , label = labelLeft [ width (px 200) ] <| text "Shrink - no nl"
72− }
73− , multiline [ height shrink ]
74− { onChange = MLChanged
75− , text = testtxtnewlines
76− , placeholder = Nothing
77− , spellcheck = False
78− , label = labelLeft [ width (px 200) ] <| text "Shrink - w/nl"
79− }
80− ]
81−
82−
83−main : Program () Model Msg
84−main =
85− Browser.sandbox
86− { init = initialModel
87− , view = view
88− , update = update
89− }
elm.json+5−3
@@ -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.0",
6+ "version": "1.1.8",
7 "exposed-modules": [
7 "exposed-modules": [
8 "Element",
8 "Element",
9 "Element.Input",
9 "Element.Input",
@@ -22,5 +22,7 @@
22 "elm/json": "1.0.0 <= v < 2.0.0",
22 "elm/json": "1.0.0 <= v < 2.0.0",
23 "elm/virtual-dom": "1.0.0 <= v < 2.0.0"
23 "elm/virtual-dom": "1.0.0 <= v < 2.0.0"
24 },
24 },
25− "test-dependencies": {}
25+ "test-dependencies": {
26+ "elm-explorations/test": "1.2.2 <= v < 2.0.0"
27+ }
26−}
28+}
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+}
experiments/virtual-css/Main.elmnew file+46
@@ -0,0 +1,46 @@
1+module Main exposing (main)
2+
3+import Browser
4+import Element exposing (..)
5+import Element.Background as Background
6+import Element.Events as Events
7+import Element.Font as Font
8+import Html
9+import Internal.Model as Internal
10+
11+
12+main =
13+ Browser.sandbox
14+ { init = 0
15+ , update = update
16+ , view = view
17+ }
18+
19+
20+update msg model =
21+ case msg of
22+ Click ->
23+ model + 1
24+
25+
26+type Msg
27+ = Click
28+
29+
30+view model =
31+ Element.layoutWith
32+ { options =
33+ [ Internal.RenderModeOption
34+ Internal.WithVirtualCss
35+ ]
36+ }
37+ [ Events.onClick Click ]
38+ (el
39+ [ padding 10
40+ , Background.color (rgb 0 0 0.8)
41+ , Font.color (rgb 1 1 1)
42+ , centerX
43+ , centerY
44+ ]
45+ (text ("hello!" ++ String.fromInt model))
46+ )
experiments/virtual-css/Makefilenew file+1
@@ -0,0 +1 @@
1+elm-live Main.elm --open --start-page=test.html -- --output=elm.js
experiments/virtual-css/cssom.js+42−11
@@ -1,16 +1,47 @@
1−class CssRulesNode extends HTMLParagraphElement {
1+(function () {
2− constructor() {
2+ var ruleCache = new Set()
3− var self = super();
3+
4−
4+ function createStyleNode() {
5− // Our css rule cache
5+ const node = document.createElement('style')
6− self.rules = {}
6+ // Without it, IE will have a broken source order specificity if we
7+ // insert rules after we insert the style tag.
8+ // It seems to kick-off the source order specificity algorithm.
9+ node.textContent = '\n'
10+ document.head.appendChild(node);
7− return self
11+ return node
12+ }
13+
14+ var staticSheet = createStyleNode();
15+ var sheet = createStyleNode().sheet;
16+
17+ function sync_static(newStyleString) {
18+ staticSheet.textContent = newStyleString
8− }
19+ }
9
20
21+ function sync(newStyles) {
22+ Object.keys(newStyles).forEach((key) => {
23+ if (!ruleCache.has(key)) {
24+ const newRule = newStyles[key]
25+ for (var i = 0; i < newRule.length; i++) {
26+ sheet.insertRule(newRule[i])
27+ }
28+ ruleCache.add(key)
29+ }
30+ });
31+ }
32+
33+ class CssRulesNode extends HTMLElement {
10 set rules(rules) {
34 set rules(rules) {
11− this._rules = rules;
35+ sync(rules);
12 }
36 }
13−}
37+ }
38+ customElements.define('elm-ui-rules', CssRulesNode);
14
39
40+ class CssStaticRulesNode extends HTMLElement {
41+ set rules(rules) {
42+ sync_static(rules);
43+ }
44+ }
45+ customElements.define('elm-ui-static-rules', CssStaticRulesNode);
15
46
16−customElements.define('elm-ui-css', CssRulesNode, { extends: 'div' });
47+}())
cases/elm.json → experiments/virtual-css/elm.jsonrenamed+2−2
@@ -1,8 +1,8 @@
1{
1{
2 "type": "application",
2 "type": "application",
3 "source-directories": [
3 "source-directories": [
4− "open",
4+ ".",
5− "../src/"
5+ "../../src/"
6 ],
6 ],
7 "elm-version": "0.19.0",
7 "elm-version": "0.19.0",
8 "dependencies": {
8 "dependencies": {
experiments/virtual-css/test.htmlnew file+18
@@ -0,0 +1,18 @@
1+<!DOCTYPE HTML>
2+<html>
3+<head>
4+ <meta charset="UTF-8">
5+ <title>Main</title>
6+ <script src="cssom.js"></script>
7+ <script src="elm.js"></script>
8+</head>
9+
10+<body>
11+ <div id="elm"></div>
12+ <script>
13+ var app = Elm.Main.init({
14+ node: document.getElementById('elm')
15+ });
16+ </script>
17+</body>
18+</html>
notes/BENCHMARKS.mdnew file+20
@@ -0,0 +1,20 @@
1+# Render Benchmark
2+
3+```bash
4+# In root directory
5+yarn install
6+yarn bench
7+```
8+
9+We're interested in overall rendering performance. Right now that means measuring:
10+
11+1. Speed for first paint.
12+2. Rerender speed.
13+3. FPS for extended animations.
14+
15+## Some notes about results.
16+
17+- For the extended animation graph, you see it tops out at a certain point. That's when the rendering pipeline is maxed out and it's now going below 60fps.
18+
19+- The html and inline style cases are **not really** directly comparable to the `elm-ui` stuff because they're not buying you the same thing (which is really write-able, maintainable layouts.) However they're nice to see because they're the current limit of elm's rendering.
20+
CHANGES-FROM-STYLE-ELEMENTS.md → notes/CHANGES-FROM-STYLE-ELEMENTS.mdrenamed+1−4
@@ -1,10 +1,7 @@
1# Compared to Style Elements
1# Compared to Style Elements
2
2
3−
4This was a MAJOR rewrite of Style Elements.
3This was a MAJOR rewrite of Style Elements.
5
4
6−
7−
8* **Major Performance improvement** - Style Elements v5 is much faster than v4 due to a better rendering strategy and generating very minimal html. The rewritten architecture also allows me to explore a few other optimizations, so things may get even faster than they are now.
5* **Major Performance improvement** - Style Elements v5 is much faster than v4 due to a better rendering strategy and generating very minimal html. The rewritten architecture also allows me to explore a few other optimizations, so things may get even faster than they are now.
9
6
10* **Lazy is here!** - It works with no weird caveats.
7* **Lazy is here!** - It works with no weird caveats.
@@ -119,7 +116,7 @@ Also of note, is that if something is `center`, then it will truly be in the cen
119
116
120- `Font.weight` has been removed in favor of `Font.extraBold`, `Font.regular`, `Font.light`, etc. All weights from 100 - 900 are represented.
117- `Font.weight` has been removed in favor of `Font.extraBold`, `Font.regular`, `Font.light`, etc. All weights from 100 - 900 are represented.
121- `Background.image` and `Background.fittedImage` will place a centered background image, instead of anchoring at the top left.
118- `Background.image` and `Background.fittedImage` will place a centered background image, instead of anchoring at the top left.
122−- `fillBetween { min : Maybe Int, max : Maybe Int}` is now present for `min/max height/width` behavior. It works like fill, but with an optional top and lower bound.
119+- `fill |> minimum 20 |> maximum 200` is now present for `minWidth`, `maxWidth`, `minHeight` and `maxHeight` behavior. It works like fill, but with an optional top and lower bound.
123- `transparent` - Set an element as transparent. It will take up space, but otherwise be transparent and unclickable.
120- `transparent` - Set an element as transparent. It will take up space, but otherwise be transparent and unclickable.
124- `alpha` can now be set for an element.
121- `alpha` can now be set for an element.
125- `attribute` has been renamed `htmlAttribute` to better convey what it's used for.
122- `attribute` has been renamed `htmlAttribute` to better convey what it's used for.
tests/README.md → notes/RUNNING_TESTS.mdrenamed+23−13
@@ -5,10 +5,12 @@ There are two types of tests for Elm UI. The normal kind, which are located in
5These can be run using [`elm-test`](https://github.com/elm-explorations/test) and can be run via
5These can be run using [`elm-test`](https://github.com/elm-explorations/test) and can be run via
6
6
7```bash
7```bash
8−elm-test
8+# in the root elm-ui directory
9+yarn install # or npm install
10+yarn run test # or npm run test
9```
11```
10
12
11−at the `elm-ui` root.
13+**Note** you need to be at the `elm-ui` root dir.
12
14
13# Layout Testing
15# Layout Testing
14
16
@@ -16,20 +18,25 @@ at the `elm-ui` root.
16
18
17In order to do this, we need a different testing environment.
19In order to do this, we need a different testing environment.
18
20
19−So, the tests in `elm-ui/tests/Tests` will render output, then harvest bounding boxes form the browser, and run the test on the resulting data.
21+So, the tests in `elm-ui/tests-rendering/src/Tests` will render output, then harvest bounding boxes form the browser, and run the test on the resulting data.
20
22
21−In order to run the test locally, compile
23+Run this locally via:
22
24
23−`elm make Tests/Run.elm --output elm.js`
25+```bash
24−
26+npm install
25−and then open `gather-styles.html`
27+npm run test-render
26−
28+```
27−Alternatively, you can run the tests locally using selenium by running:
28
29
29−`python3 automation/run.py --local`
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
31
31−from the `tests` directory.
32+```
33+Compiling tests
34+Done compiling
35+Running locally on Chrome...
36+Local Chrome -> All 18029 tests passed
37+```
32
38
39+That's right. We have 18,029 tests. Woohoo!
33
40
34# Running on Sauce Labs
41# Running on Sauce Labs
35
42
@@ -41,9 +48,12 @@ If you have an account, you'll need to create a `elm-ui/sauce.env` file, which h
41export SAUCE_ACCESS_KEY={your key}
48export SAUCE_ACCESS_KEY={your key}
42export SAUCE_USERNAME={your username}
49export SAUCE_USERNAME={your username}
43```
50```
51+
44You can then run.
52You can then run.
45
53
46−`python3 automation/run.py`
54+```bash
55+npm run test-render-sauce
56+```
47
57
48**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.
49
59
@@ -52,4 +62,4 @@ You can then run.
52
62
53https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/
63https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/
54https://wiki.saucelabs.com/display/DOCS/Sauce+Labs+Basics
64https://wiki.saucelabs.com/display/DOCS/Sauce+Labs+Basics
55−https://github.com/saucelabs-sample-test-frameworks/Python-Pytest-Selenium
65+https://github.com/saucelabs-sample-test-frameworks/Python-Pytest-Selenium
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.jsonnew file+34
@@ -0,0 +1,34 @@
1+{
2+ "name": "elm-ui-testing",
3+ "version": "1.0.0",
4+ "description": "Run the Elm UI benchmarks and render tests",
5+ "main": "index.js",
6+ "scripts": {
7+ "test": "elm-test",
8+ "view-test": "node tests-rendering/automation/view.js",
9+ "test-render": "node tests-rendering/automation/run.js --chrome",
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",
13+ "bench": "node benchmarks/runtime/index.js",
14+ "list-issues": "hub issue -f \"%i,%U,%t,'%L'%n\" > issues.csv"
15+ },
16+ "repository": {
17+ "type": "git",
18+ "url": "git+https://github.com/mdgriffith/elm-ui.git"
19+ },
20+ "author": "Matthew Griffith <mdg.griffith@gmail.com> (http://mechanical-elephant.com/)",
21+ "license": "ISC",
22+ "bugs": {
23+ "url": "https://github.com/mdgriffith/elm-ui/issues"
24+ },
25+ "homepage": "https://github.com/mdgriffith/elm-ui#readme",
26+ "dependencies": {
27+ "chalk": "^2.4.2",
28+ "commander": "^2.20.0",
29+ "elm": "^0.19.1-3",
30+ "elm-test": "^0.19.1-revision2",
31+ "node-elm-compiler": "^5.0.4",
32+ "selenium-webdriver": "^4.0.0-alpha.7"
33+ }
34+}
src/Element.elm+102−48
@@ -467,11 +467,11 @@ type alias Option =
467 Internal.Option
467 Internal.Option
468
468
469
469
470−{-| Elm UI embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elments being rendered.
470+{-| Elm UI embeds two StyleSheets, one that is constant, and one that changes dynamically based on styles collected from the elements being rendered.
471
471
472This option will stop the static/constant stylesheet from rendering.
472This option will stop the static/constant stylesheet from rendering.
473
473
474−Make sure to render the constant/static stylesheet at least once on your page!
474+If you're embedding multiple elm-ui `layout` elements, you need to guarantee that only one is rendering the static style sheet and that it's above all the others in the DOM tree.
475
475
476-}
476-}
477noStaticStyleSheet : Option
477noStaticStyleSheet : Option
@@ -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
@@ -1143,6 +1165,8 @@ link attrs { url, label } =
1143 (classes.contentCenterX
1165 (classes.contentCenterX
1144 ++ " "
1166 ++ " "
1145 ++ classes.contentCenterY
1167 ++ classes.contentCenterY
1168+ ++ " "
1169+ ++ classes.link
1146 )
1170 )
1147 :: attrs
1171 :: attrs
1148 )
1172 )
@@ -1166,18 +1190,19 @@ newTabLink attrs { url, label } =
1166 :: Internal.Attr (Html.Attributes.target "_blank")
1190 :: Internal.Attr (Html.Attributes.target "_blank")
1167 :: width shrink
1191 :: width shrink
1168 :: height shrink
1192 :: height shrink
1193+ :: Internal.htmlClass
1169− :: Internal.htmlClass (classes.contentCenterX ++ " " ++ classes.contentCenterY)
1194+ (classes.contentCenterX
1195+ ++ " "
1196+ ++ classes.contentCenterY
1197+ ++ " "
1198+ ++ classes.link
1199+ )
1170 :: attrs
1200 :: attrs
1171 )
1201 )
1172 (Internal.Unkeyed [ label ])
1202 (Internal.Unkeyed [ label ])
1173
1203
1174
1204
1175{-| A link to download a file.
1205{-| A link to download a file.
1176−
1177−**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.
1178−
1179−[Here's a description of what needs to happen](https://github.com/elm/html/issues/175).
1180−
1181-}
1206-}
1182download :
1207download :
1183 List (Attribute msg)
1208 List (Attribute msg)
@@ -1333,7 +1358,11 @@ moveLeft x =
1333{-| -}
1358{-| -}
1334padding : Int -> Attribute msg
1359padding : Int -> Attribute msg
1335padding x =
1360padding x =
1361+ let
1362+ f =
1363+ toFloat x
1364+ in
1336− 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)
1337
1366
1338
1367
1339{-| Set horizontal and vertical padding.
1368{-| Set horizontal and vertical padding.
@@ -1341,16 +1370,27 @@ padding x =
1341paddingXY : Int -> Int -> Attribute msg
1370paddingXY : Int -> Int -> Attribute msg
1342paddingXY x y =
1371paddingXY x y =
1343 if x == y then
1372 if x == y then
1373+ let
1374+ f =
1375+ toFloat x
1376+ in
1344− 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)
1345
1378
1346 else
1379 else
1380+ let
1381+ xFloat =
1382+ toFloat x
1383+
1384+ yFloat =
1385+ toFloat y
1386+ in
1347 Internal.StyleClass Flag.padding
1387 Internal.StyleClass Flag.padding
1348 (Internal.PaddingStyle
1388 (Internal.PaddingStyle
1349 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1389 ("p-" ++ String.fromInt x ++ "-" ++ String.fromInt y)
1350− y
1390+ yFloat
1351− x
1391+ xFloat
1352− y
1392+ yFloat
1353− x
1393+ xFloat
1354 )
1394 )
1355
1395
1356
1396
@@ -1371,16 +1411,26 @@ And then just do
1371paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1411paddingEach : { top : Int, right : Int, bottom : Int, left : Int } -> Attribute msg
1372paddingEach { top, right, bottom, left } =
1412paddingEach { top, right, bottom, left } =
1373 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
1374− 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+ )
1375
1425
1376 else
1426 else
1377 Internal.StyleClass Flag.padding
1427 Internal.StyleClass Flag.padding
1378 (Internal.PaddingStyle
1428 (Internal.PaddingStyle
1379 (Internal.paddingName top right bottom left)
1429 (Internal.paddingName top right bottom left)
1380− top
1430+ (toFloat top)
1381− right
1431+ (toFloat right)
1382− bottom
1432+ (toFloat bottom)
1383− left
1433+ (toFloat left)
1384 )
1434 )
1385
1435
1386
1436
@@ -1545,25 +1595,29 @@ type Orientation
1545
1595
1546
1596
1547{-| Takes in a Window.Size and returns a device profile which can be used for responsiveness.
1597{-| Takes in a Window.Size and returns a device profile which can be used for responsiveness.
1598+
1599+If you have more detailed concerns around responsiveness, it probably makes sense to copy this function into your codebase and modify as needed.
1600+
1548-}
1601-}
1549classifyDevice : { window | height : Int, width : Int } -> Device
1602classifyDevice : { window | height : Int, width : Int } -> Device
1550classifyDevice window =
1603classifyDevice window =
1604+ -- Tested in this ellie:
1605+ -- https://ellie-app.com/68QM7wLW8b9a1
1551 { class =
1606 { class =
1552 let
1607 let
1553− shortSide =
1608+ longSide =
1554− if window.width < window.height then
1609+ max window.width window.height
1555− window.width
1556
1610
1557− else
1611+ shortSide =
1558− window.height
1612+ min window.width window.height
1559 in
1613 in
1560− if shortSide <= 600 then
1614+ if shortSide < 600 then
1561 Phone
1615 Phone
1562
1616
1563− else if shortSide > 600 && shortSide <= 1200 then
1617+ else if longSide <= 1200 then
1564 Tablet
1618 Tablet
1565
1619
1566− else if shortSide > 1200 && shortSide <= 1800 then
1620+ else if longSide > 1200 && longSide <= 1920 then
1567 Desktop
1621 Desktop
1568
1622
1569 else
1623 else
src/Element/Font.elm+2−2
@@ -376,9 +376,9 @@ type alias Variant =
376{-| You can use this to set a single variant on an element itself such as:
376{-| You can use this to set a single variant on an element itself such as:
377
377
378 el
378 el
379− [ Font.variant Font.smallcaps
379+ [ Font.variant Font.smallCaps
380 ]
380 ]
381− (text "rendered with smallcaps")
381+ (text "rendered with smallCaps")
382
382
383**Note** These will **not** stack. If you want multiple variants, you should use `Font.variantList`.
383**Note** These will **not** stack. If you want multiple variants, you should use `Font.variantList`.
384
384
src/Element/Input.elm+267−101
@@ -20,7 +20,7 @@ We want all of our input elements to:
20
20
21While these three goals may seem pretty obvious, Html and CSS have made it surprisingly difficult to achieve!
21While these three goals may seem pretty obvious, Html and CSS have made it surprisingly difficult to achieve!
22
22
23−And incredibly difficult for developers to remember all the tricks necessary to make things work. If you've every tried to make a `<textarea>` be the height of it's content or restyle a radio button while maintaining accessibility, you may be familiar!
23+And incredibly difficult for developers to remember all the tricks necessary to make things work. If you've every tried to make a `<textarea>` be the height of it's content or restyle a radio button while maintaining accessibility, you may be familiar.
24
24
25This module is intended to be accessible by default. You shouldn't have to wade through docs, articles, and books to find out [exactly how accessible your html actually is](https://www.powermapper.com/tests/screen-readers/aria/index.html).
25This module is intended to be accessible by default. You shouldn't have to wade through docs, articles, and books to find out [exactly how accessible your html actually is](https://www.powermapper.com/tests/screen-readers/aria/index.html).
26
26
@@ -132,6 +132,56 @@ Instead of implicit submission behavior, [try making an `onEnter` event handler
132
132
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+
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+
141+# Disabling Inputs
142+
143+You also might be wondering how to disable an input.
144+
145+Disabled inputs can be a little problematic for user experience, and doubly so for accessibility. This is because it's now your priority to inform the user _why_ some field is disabled.
146+
147+If an input is truly disabled, meaning it's not focusable or doesn't send off a `Msg`, you actually lose your ability to help the user out! For those wary about accessibility [this is a big problem.](https://ux.stackexchange.com/questions/103239/should-disabled-elements-be-focusable-for-accessibility-purposes)
148+
149+Here are some alternatives to think about that don't involve explicitly disabling an input.
150+
151+**Disabled Buttons** - Change the `Msg` it fires, the text that is rendered, and optionally set a `Region.description` which will be available to screen readers.
152+
153+ import Element.Input as Input
154+ import Element.Region as Region
155+
156+ myButton ready =
157+ if ready then
158+ Input.button
159+ [ Background.color blue
160+ ]
161+ { onPress =
162+ Just SaveButtonPressed
163+ , label =
164+ text "Save blog post"
165+ }
166+
167+ else
168+ Input.button
169+ [ Background.color grey
170+ , Region.description
171+ "A publish date is required before saving a blogpost."
172+ ]
173+ { onPress =
174+ Just DisabledSaveButtonPressed
175+ , label =
176+ text "Save Blog "
177+ }
178+
179+Consider showing a hint if `DisabledSaveButtonPressed` is sent.
180+
181+For other inputs such as `Input.text`, consider simply rendering it in a normal `paragraph` or `el` if it's not editable.
182+
183+Alternatively, see if it's reasonable to _not_ display an input if you'd normally disable it. Is there an option where it's only visible when it's editable?
184+
135-}
185-}
136
186
137import Element exposing (Attribute, Color, Element)
187import Element exposing (Attribute, Color, Element)
@@ -276,11 +326,11 @@ The `onPress` handler will be fired either `onClick` or when the element is focu
276 , Element.focused
326 , Element.focused
277 [ Background.color purple ]
327 [ Background.color purple ]
278 ]
328 ]
279− { onPress = ClickMsg
329+ { onPress = Just ClickMsg
280 , label = text "My Button"
330 , label = text "My Button"
281 }
331 }
282
332
283−`onPress` takes a `Maybe msg`. If you provide the value `Nothing`, then the button will be disabled.
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.
284
334
285-}
335-}
286button :
336button :
@@ -319,7 +369,17 @@ button attrs { onPress, label } =
319
369
320 Just msg ->
370 Just msg ->
321 Events.onClick msg
371 Events.onClick msg
322− :: 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+ )
323 :: attrs
383 :: attrs
324 )
384 )
325 )
385 )
@@ -397,22 +457,24 @@ checkbox attrs { label, icon, checked, onChange } =
397 ]
457 ]
398 ++ attrs
458 ++ attrs
399 in
459 in
400− applyLabel attributes
460+ applyLabel
401− label
402− (Internal.element
403− Internal.asEl
404− Internal.div
405− [ Internal.Attr <|
406− Html.Attributes.attribute "role" "checkbox"
461+ (Internal.Attr (Html.Attributes.attribute "role" "checkbox")
407− , Internal.Attr <|
462+ :: Internal.Attr
408− Html.Attributes.attribute "aria-checked" <|
463+ (Html.Attributes.attribute "aria-checked" <|
409 if checked then
464 if checked then
410 "true"
465 "true"
411
466
412 else
467 else
413 "false"
468 "false"
469+ )
414− , hiddenLabelAttribute label
470+ :: hiddenLabelAttribute label
471+ :: attributes
472+ )
473+ label
474+ (Internal.element
475+ Internal.asEl
476+ Internal.div
415− , Element.centerY
477+ [ Element.centerY
416 , Element.height Element.fill
478 , Element.height Element.fill
417 , Element.width Element.shrink
479 , Element.width Element.shrink
418 ]
480 ]
@@ -651,7 +713,7 @@ slider attributes input =
651 ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
713 ("input[type=\"range\"]." ++ className ++ "::-ms-thumb")
652 thumbShadowStyle
714 thumbShadowStyle
653 )
715 )
654− , Internal.Attr (Html.Attributes.class (className ++ " focusable-parent"))
716+ , Internal.Attr (Html.Attributes.class (className ++ " ui-slide-bar focusable-parent"))
655 , Internal.Attr
717 , Internal.Attr
656 (Html.Events.onInput
718 (Html.Events.onInput
657 (\str ->
719 (\str ->
@@ -717,10 +779,10 @@ slider attributes input =
717 -- 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.
718 ++ [ Element.behindContent <|
780 ++ [ Element.behindContent <|
719 if vertical then
781 if vertical then
720− viewVerticalThumb factor thumbAttributes trackWidth
782+ viewVerticalThumb factor (Internal.htmlClass "focusable-thumb" :: thumbAttributes) trackWidth
721
783
722 else
784 else
723− viewHorizontalThumb factor thumbAttributes trackHeight
785+ viewHorizontalThumb factor (Internal.htmlClass "focusable-thumb" :: thumbAttributes) trackHeight
724 ]
786 ]
725 )
787 )
726 Element.none
788 Element.none
@@ -813,35 +875,46 @@ textHelper textInput attrs textOptions =
813 _ ->
875 _ ->
814 False
876 False
815
877
816− isHeightContent attr =
878+ getPadding attr =
817− case attr of
818− Internal.Height Internal.Content ->
819− True
820−
821− _ ->
822− False
823−
824− bottomPadding attr =
825 case attr of
879 case attr of
826 Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
880 Internal.StyleClass cls (Internal.PaddingStyle pad t r b l) ->
827− Just <|
881+ -- The - 3 is here to prevent accidental triggering of scrollbars
828− Element.paddingEach
882+ -- when things are off by a pixel or two.
883+ -- (or at least when the browser *thinks* it's off by a pixel or two)
884+ Just
829− { top = 0
885+ { top = max 0 (floor (t - 3))
830− , right = 0
886+ , right = max 0 (floor (r - 3))
831− , bottom = b
887+ , bottom = max 0 (floor (b - 3))
832− , left = 0
888+ , left = max 0 (floor (l - 3))
833− }
889+ }
834
890
835 _ ->
891 _ ->
836 Nothing
892 Nothing
837
893
838− heightContent =
894+ heightConstrained =
839 case textInput.type_ of
895 case textInput.type_ of
840 TextInputNode inputType ->
896 TextInputNode inputType ->
841 False
897 False
842
898
843 TextArea ->
899 TextArea ->
900+ withDefaults
901+ |> List.filterMap getHeight
902+ |> List.reverse
903+ |> List.head
904+ |> Maybe.map isConstrained
905+ |> Maybe.withDefault False
906+
907+ parentPadding =
844− List.any isHeightContent withDefaults
908+ withDefaults
909+ |> List.filterMap getPadding
910+ |> List.reverse
911+ |> List.head
912+ |> Maybe.withDefault
913+ { top = 0
914+ , right = 0
915+ , bottom = 0
916+ , left = 0
917+ }
845
918
846 inputElement =
919 inputElement =
847 Internal.element
920 Internal.element
@@ -853,26 +926,36 @@ textHelper textInput attrs textOptions =
853 TextArea ->
926 TextArea ->
854 Internal.NodeName "textarea"
927 Internal.NodeName "textarea"
855 )
928 )
856− ([ value textOptions.text
857− , Internal.Attr (Html.Events.onInput textOptions.onChange)
858− , hiddenLabelAttribute textOptions.label
859− , spellcheck textInput.spellchecked
860− , Maybe.map autofill textInput.autofill
861− |> Maybe.withDefault Internal.NoAttribute
862− ]
863− ++ (case textInput.type_ of
929+ ((case textInput.type_ of
864− TextInputNode inputType ->
930+ TextInputNode inputType ->
931+ -- Note: Due to a weird edgecase in...Edge...
932+ -- `type` needs to come _before_ `value`
933+ -- More reading: https://github.com/mdgriffith/elm-ui/pull/94/commits/4f493a27001ccc3cf1f2baa82e092c35d3811876
865− [ Internal.Attr (Html.Attributes.type_ inputType)
934+ [ Internal.Attr (Html.Attributes.type_ inputType)
866− , Internal.htmlClass classes.inputText
935+ , Internal.htmlClass classes.inputText
867− ]
936+ ]
868
937
869− TextArea ->
938+ TextArea ->
870− [ Element.clip
939+ [ Element.clip
871− , Element.height Element.fill
940+ , Element.height Element.fill
872− , Internal.htmlClass classes.inputMultiline
941+ , Internal.htmlClass classes.inputMultiline
873− , calcMoveToCompensateForPadding withDefaults
942+ , calcMoveToCompensateForPadding withDefaults
943+
944+ -- The only reason we do this padding trick is so that when the user clicks in the padding,
945+ -- that the cursor will reset correctly.
946+ -- This could probably be combined with the above `calcMoveToCompensateForPadding`
947+ , Element.paddingEach parentPadding
948+ , Internal.Attr (Html.Attributes.style "margin" (renderBox (negateBox parentPadding)))
949+ , Internal.Attr (Html.Attributes.style "box-sizing" "content-box")
874− ]
950+ ]
875− )
951+ )
952+ ++ [ value textOptions.text
953+ , Internal.Attr (Html.Events.onInput textOptions.onChange)
954+ , hiddenLabelAttribute textOptions.label
955+ , spellcheck textInput.spellchecked
956+ , Maybe.map autofill textInput.autofill
957+ |> Maybe.withDefault Internal.NoAttribute
958+ ]
876 ++ redistributed.input
959 ++ redistributed.input
877 )
960 )
878 (Internal.Unkeyed [])
961 (Internal.Unkeyed [])
@@ -881,15 +964,25 @@ textHelper textInput attrs textOptions =
881 case textInput.type_ of
964 case textInput.type_ of
882 TextArea ->
965 TextArea ->
883 -- textarea with height-content means that
966 -- textarea with height-content means that
884− -- the input element is rendered `inFront` and transparently.
967+ -- the input element is rendered `inFront` with a transparent background
885− -- Then the input text is rendered as the actual element.
968+ -- Then the input text is rendered as the space filling element.
886 Internal.element
969 Internal.element
887 Internal.asEl
970 Internal.asEl
888 Internal.div
971 Internal.div
972+ ((if heightConstrained then
973+ (::) Element.scrollbarY
974+
975+ else
976+ identity
977+ )
889− ([ Element.width Element.fill
978+ [ Element.width Element.fill
890− , Element.scrollbarY
979+ , if List.any hasFocusStyle withDefaults then
980+ Internal.NoAttribute
981+
982+ else
891− , Internal.htmlClass classes.focusedWithin
983+ Internal.htmlClass classes.focusedWithin
984+ , Internal.htmlClass classes.inputMultilineWrapper
892− ]
985+ ]
893 ++ redistributed.parent
986 ++ redistributed.parent
894 )
987 )
895 (Internal.Unkeyed
988 (Internal.Unkeyed
@@ -900,20 +993,16 @@ textHelper textInput attrs textOptions =
900 :: Element.height Element.fill
993 :: Element.height Element.fill
901 :: Element.inFront inputElement
994 :: Element.inFront inputElement
902 :: Internal.htmlClass classes.inputMultilineParent
995 :: Internal.htmlClass classes.inputMultilineParent
903− :: List.concat
904− [ if heightContent then
905− redistributed.wrapper
996+ :: redistributed.wrapper
906−
907− else
908− redistributed.wrapper
909− ++ List.filterMap bottomPadding redistributed.parent
910− ]
911 )
997 )
912 (Internal.Unkeyed
998 (Internal.Unkeyed
913 (if textOptions.text == "" then
999 (if textOptions.text == "" then
914 case textOptions.placeholder of
1000 case textOptions.placeholder of
915 Nothing ->
1001 Nothing ->
1002+ -- Without this, firefox will make the text area lose focus
1003+ -- if the input is empty and you mash the keyboard
1004+ [ Element.text "\u{00A0}"
916− []
1005+ ]
917
1006
918 Just place ->
1007 Just place ->
919 [ renderPlaceholder place
1008 [ renderPlaceholder place
@@ -922,7 +1011,13 @@ textHelper textInput attrs textOptions =
922 ]
1011 ]
923
1012
924 else
1013 else
925− [ Internal.unstyled (Html.text textOptions.text) ]
1014+ [ Internal.unstyled
1015+ (Html.span [ Html.Attributes.class classes.inputMultilineFiller ]
1016+ -- We append a non-breaking space to the end of the content so that newlines don't get chomped.
1017+ [ Html.text (textOptions.text ++ "\u{00A0}")
1018+ ]
1019+ )
1020+ ]
926 )
1021 )
927 )
1022 )
928 ]
1023 ]
@@ -933,6 +1028,12 @@ textHelper textInput attrs textOptions =
933 Internal.asEl
1028 Internal.asEl
934 Internal.div
1029 Internal.div
935 (Element.width Element.fill
1030 (Element.width Element.fill
1031+ :: (if List.any hasFocusStyle withDefaults then
1032+ Internal.NoAttribute
1033+
1034+ else
1035+ Internal.htmlClass classes.focusedWithin
1036+ )
936 :: List.concat
1037 :: List.concat
937 [ redistributed.parent
1038 [ redistributed.parent
938 , case textOptions.placeholder of
1039 , case textOptions.placeholder of
@@ -963,6 +1064,34 @@ textHelper textInput attrs textOptions =
963 wrappedInput
1064 wrappedInput
964
1065
965
1066
1067+getHeight attr =
1068+ case attr of
1069+ Internal.Height h ->
1070+ Just h
1071+
1072+ _ ->
1073+ Nothing
1074+
1075+
1076+negateBox box =
1077+ { top = negate box.top
1078+ , right = negate box.right
1079+ , bottom = negate box.bottom
1080+ , left = negate box.left
1081+ }
1082+
1083+
1084+renderBox { top, right, bottom, left } =
1085+ String.fromInt top
1086+ ++ "px "
1087+ ++ String.fromInt right
1088+ ++ "px "
1089+ ++ String.fromInt bottom
1090+ ++ "px "
1091+ ++ String.fromInt left
1092+ ++ "px"
1093+
1094+
966renderPlaceholder (Placeholder placeholderAttrs placeholderEl) forPlaceholder on =
1095renderPlaceholder (Placeholder placeholderAttrs placeholderEl) forPlaceholder on =
967 Element.el
1096 Element.el
968 (forPlaceholder
1097 (forPlaceholder
@@ -1010,7 +1139,7 @@ calcMoveToCompensateForPadding attrs =
1010 Internal.NoAttribute
1139 Internal.NoAttribute
1011
1140
1012 Just vSpace ->
1141 Just vSpace ->
1013− Element.moveUp (toFloat vSpace / 2)
1142+ Element.moveUp (toFloat (floor (toFloat vSpace / 2)))
1014
1143
1015
1144
1016{-| Given the list of attributes provided to `Input.multiline` or `Input.text`,
1145{-| Given the list of attributes provided to `Input.multiline` or `Input.text`,
@@ -1090,6 +1219,24 @@ isShrink len =
1090 isShrink l
1219 isShrink l
1091
1220
1092
1221
1222+isConstrained len =
1223+ case len of
1224+ Internal.Content ->
1225+ False
1226+
1227+ Internal.Px _ ->
1228+ True
1229+
1230+ Internal.Fill _ ->
1231+ True
1232+
1233+ Internal.Min _ l ->
1234+ isConstrained l
1235+
1236+ Internal.Max _ l ->
1237+ True
1238+
1239+
1093isPixel len =
1240isPixel len =
1094 case len of
1241 case len of
1095 Internal.Content ->
1242 Internal.Content ->
@@ -1116,17 +1263,17 @@ redistributeOver isMultiline stacked attr els =
1116 { els | parent = attr :: els.parent }
1263 { els | parent = attr :: els.parent }
1117
1264
1118 Internal.Width width ->
1265 Internal.Width width ->
1119− if stacked then
1120− { els | fullParent = attr :: els.fullParent }
1121−
1122− else if isFill width then
1266+ if isFill width then
1123 { els
1267 { els
1124 | fullParent = attr :: els.fullParent
1268 | fullParent = attr :: els.fullParent
1125 , parent = attr :: els.parent
1269 , parent = attr :: els.parent
1270+ , input = attr :: els.input
1126 }
1271 }
1127
1272
1128− else if isPixel width then
1273+ else if stacked then
1129− { els | parent = attr :: els.parent }
1274+ { els
1275+ | fullParent = attr :: els.fullParent
1276+ }
1130
1277
1131 else
1278 else
1132 { els
1279 { els
@@ -1181,23 +1328,36 @@ redistributeOver isMultiline stacked attr els =
1181 Element.htmlAttribute
1328 Element.htmlAttribute
1182 (Html.Attributes.style
1329 (Html.Attributes.style
1183 "height"
1330 "height"
1184− ("calc(1.0em + " ++ String.fromInt (2 * min t b) ++ "px)")
1331+ ("calc(1.0em + " ++ String.fromFloat (2 * min t b) ++ "px)")
1185 )
1332 )
1186
1333
1187 newLineHeight =
1334 newLineHeight =
1188 Element.htmlAttribute
1335 Element.htmlAttribute
1189 (Html.Attributes.style
1336 (Html.Attributes.style
1190 "line-height"
1337 "line-height"
1191− ("calc(1.0em + " ++ String.fromInt (2 * min t b) ++ "px)")
1338+ ("calc(1.0em + " ++ String.fromFloat (2 * min t b) ++ "px)")
1192 )
1339 )
1193
1340
1341+ newTop =
1342+ t - min t b
1343+
1344+ newBottom =
1345+ b - min t b
1346+
1194 reducedVerticalPadding =
1347 reducedVerticalPadding =
1195− Element.paddingEach
1348+ Internal.StyleClass Flag.padding
1196− { top = t - min t b
1349+ (Internal.PaddingStyle
1197− , right = r
1350+ (Internal.paddingNameFloat
1198− , bottom = b - min t b
1351+ newTop
1199− , left = l
1352+ r
1200− }
1353+ newBottom
1354+ l
1355+ )
1356+ newTop
1357+ r
1358+ newBottom
1359+ l
1360+ )
1201 in
1361 in
1202 { els
1362 { els
1203 | parent = reducedVerticalPadding :: els.parent
1363 | parent = reducedVerticalPadding :: els.parent
@@ -1223,7 +1383,7 @@ redistributeOver isMultiline stacked attr els =
1223 Internal.StyleClass _ (Internal.FontFamily _ _) ->
1383 Internal.StyleClass _ (Internal.FontFamily _ _) ->
1224 { els | fullParent = attr :: els.fullParent }
1384 { els | fullParent = attr :: els.fullParent }
1225
1385
1226− Internal.StyleClass _ _ ->
1386+ Internal.StyleClass flag cls ->
1227 { els | parent = attr :: els.parent }
1387 { els | parent = attr :: els.parent }
1228
1388
1229 Internal.NoAttribute ->
1389 Internal.NoAttribute ->
@@ -1405,6 +1565,8 @@ email =
1405
1565
1406By 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.
1407
1567
1568+Use `Element.spacing` to change its line-height.
1569+
1408-}
1570-}
1409multiline :
1571multiline :
1410 List (Attribute msg)
1572 List (Attribute msg)
@@ -1466,28 +1628,28 @@ applyLabel attrs label input =
1466 Internal.element
1628 Internal.element
1467 Internal.asColumn
1629 Internal.asColumn
1468 (Internal.NodeName "label")
1630 (Internal.NodeName "label")
1469− attrs
1631+ (Internal.htmlClass classes.inputLabel :: attrs)
1470 (Internal.Unkeyed [ labelElement, input ])
1632 (Internal.Unkeyed [ labelElement, input ])
1471
1633
1472 Below ->
1634 Below ->
1473 Internal.element
1635 Internal.element
1474 Internal.asColumn
1636 Internal.asColumn
1475 (Internal.NodeName "label")
1637 (Internal.NodeName "label")
1476− attrs
1638+ (Internal.htmlClass classes.inputLabel :: attrs)
1477 (Internal.Unkeyed [ input, labelElement ])
1639 (Internal.Unkeyed [ input, labelElement ])
1478
1640
1479 OnRight ->
1641 OnRight ->
1480 Internal.element
1642 Internal.element
1481 Internal.asRow
1643 Internal.asRow
1482 (Internal.NodeName "label")
1644 (Internal.NodeName "label")
1483− attrs
1645+ (Internal.htmlClass classes.inputLabel :: attrs)
1484 (Internal.Unkeyed [ input, labelElement ])
1646 (Internal.Unkeyed [ input, labelElement ])
1485
1647
1486 OnLeft ->
1648 OnLeft ->
1487 Internal.element
1649 Internal.element
1488 Internal.asRow
1650 Internal.asRow
1489 (Internal.NodeName "label")
1651 (Internal.NodeName "label")
1490− attrs
1652+ (Internal.htmlClass classes.inputLabel :: attrs)
1491 (Internal.Unkeyed [ labelElement, input ])
1653 (Internal.Unkeyed [ labelElement, input ])
1492
1654
1493
1655
@@ -1943,7 +2105,11 @@ onKeyLookup lookup =
1943 Json.field "key" Json.string
2105 Json.field "key" Json.string
1944 |> Json.andThen decode
2106 |> Json.andThen decode
1945 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
1946− Internal.Attr <| Html.Events.on "keyup" isKey
2110+ Internal.Attr <|
2111+ Html.Events.preventDefaultOn "keydown"
2112+ (Json.map (\fired -> ( fired, True )) isKey)
1947
2113
1948
2114
1949{-| -}
2115{-| -}
@@ -2019,7 +2185,7 @@ defaultTextBoxStyle =
2019 , Border.color darkGrey
2185 , Border.color darkGrey
2020 , Background.color white
2186 , Background.color white
2021 , Border.width 1
2187 , Border.width 1
2022− , Element.spacing 3
2188+ , Element.spacing 5
2023 , Element.width Element.fill
2189 , Element.width Element.fill
2024 , Element.height Element.shrink
2190 , Element.height Element.shrink
2025 ]
2191 ]
@@ -2039,7 +2205,8 @@ defaultCheckbox : Bool -> Element msg
2039defaultCheckbox checked =
2205defaultCheckbox checked =
2040 Element.el
2206 Element.el
2041 [ Internal.htmlClass "focusable"
2207 [ Internal.htmlClass "focusable"
2042− , Element.width (Element.px 14)
2208+ , Element.width
2209+ (Element.px 14)
2043 , Element.height (Element.px 14)
2210 , Element.height (Element.px 14)
2044 , Font.color white
2211 , Font.color white
2045 , Element.centerY
2212 , Element.centerY
@@ -2075,9 +2242,8 @@ defaultCheckbox checked =
2075
2242
2076 else
2243 else
2077 1
2244 1
2078− ]
2245+ , Element.inFront
2079− (if checked then
2080− Element.el
2246+ (Element.el
2081 [ Border.color white
2247 [ Border.color white
2082 , Element.height (Element.px 6)
2248 , Element.height (Element.px 6)
2083 , Element.width (Element.px 9)
2249 , Element.width (Element.px 9)
@@ -2085,6 +2251,7 @@ defaultCheckbox checked =
2085 , Element.centerX
2251 , Element.centerX
2086 , Element.centerY
2252 , Element.centerY
2087 , Element.moveUp 1
2253 , Element.moveUp 1
2254+ , Element.transparent (not checked)
2088 , Border.widthEach
2255 , Border.widthEach
2089 { top = 0
2256 { top = 0
2090 , left = 2
2257 , left = 2
@@ -2093,7 +2260,6 @@ defaultCheckbox checked =
2093 }
2260 }
2094 ]
2261 ]
2095 Element.none
2262 Element.none
2096−
2263+ )
2097− else
2264+ ]
2098− Element.none
2265+ Element.none
2099− )
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+574−539
@@ -16,6 +16,7 @@ module Internal.Model exposing
16 , LayoutContext(..)
16 , LayoutContext(..)
17 , Length(..)
17 , Length(..)
18 , Location(..)
18 , Location(..)
19+ , NearbyChildren(..)
19 , NodeName(..)
20 , NodeName(..)
20 , Option(..)
21 , Option(..)
21 , OptionRecord
22 , OptionRecord
@@ -78,14 +79,13 @@ module Internal.Model exposing
78 , onlyStyles
79 , onlyStyles
79 , optionsToRecord
80 , optionsToRecord
80 , paddingName
81 , paddingName
82+ , paddingNameFloat
81 , pageClass
83 , pageClass
82 , paragraphClass
84 , paragraphClass
83 , reduceRecursive
85 , reduceRecursive
84 , reduceStyles
86 , reduceStyles
85 , reduceStylesRecursive
87 , reduceStylesRecursive
86− , removeNever
88+ , removeNever
87− -- , renderFont
88−
89 , renderFontClassName
89 , renderFontClassName
90 , renderHeight
90 , renderHeight
91 , renderRoot
91 , renderRoot
@@ -95,7 +95,6 @@ module Internal.Model exposing
95 , rowClass
95 , rowClass
96 , singleClass
96 , singleClass
97 , spacingName
97 , spacingName
98− , staticRoot
99 , tag
98 , tag
100 , textShadowClass
99 , textShadowClass
101 , toHtml
100 , toHtml
@@ -113,7 +112,7 @@ import Html.Attributes
113import Html.Keyed
112import Html.Keyed
114import Internal.Flag as Flag exposing (Flag)
113import Internal.Flag as Flag exposing (Flag)
115import Internal.Style exposing (classes, dot)
114import Internal.Style exposing (classes, dot)
116−import Json.Encode as Json
115+import Json.Encode as Encode
117import Set exposing (Set)
116import Set exposing (Set)
118import VirtualDom
117import VirtualDom
119
118
@@ -175,7 +174,7 @@ type Style
175 | Colored String String Color
174 | Colored String String Color
176 | SpacingStyle String Int Int
175 | SpacingStyle String Int Int
177 | BorderWidth String Int Int Int Int
176 | BorderWidth String Int Int Int Int
178− | PaddingStyle String Int Int Int Int
177+ | PaddingStyle String Float Float Float Float
179 | GridTemplateStyle
178 | GridTemplateStyle
180 { spacing : ( Length, Length )
179 { spacing : ( Length, Length )
181 , columns : List Length
180 , columns : List Length
@@ -456,7 +455,10 @@ finalizeNode has node attributes children embedMode parentContext =
456 Embedded nodeName internal ->
455 Embedded nodeName internal ->
457 VirtualDom.node nodeName
456 VirtualDom.node nodeName
458 attributes
457 attributes
458+ [ createNode internal
459+ [ Html.Attributes.class
459− [ createNode internal [ Html.Attributes.class (classes.any ++ " " ++ classes.single) ]
460+ (classes.any ++ " " ++ classes.single)
461+ ]
460 ]
462 ]
461 in
463 in
462 case parentContext of
464 case parentContext of
@@ -533,49 +535,45 @@ finalizeNode has node attributes children embedMode parentContext =
533
535
534
536
535embedWith static opts styles children =
537embedWith static opts styles children =
538+ let
539+ dynamicStyleSheet =
540+ styles
541+ |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
542+ |> Tuple.second
543+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
544+ -- |> sortedReduce
545+ |> toStyleSheet opts
546+ in
536 if static then
547 if static then
537− staticRoot
548+ staticRoot opts
538− :: (styles
549+ :: dynamicStyleSheet
539− |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
540− |> Tuple.second
541− -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
542− -- |> sortedReduce
543− |> toStyleSheet opts
544− )
545 :: children
550 :: children
546
551
547 else
552 else
548− (styles
553+ dynamicStyleSheet
549− |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
550− |> Tuple.second
551− -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
552− -- |> sortedReduce
553− |> toStyleSheet opts
554− )
555 :: children
554 :: children
556
555
557
556
558embedKeyed static opts styles children =
557embedKeyed static opts styles children =
558+ let
559+ dynamicStyleSheet =
560+ styles
561+ |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
562+ |> Tuple.second
563+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
564+ -- |> sortedReduce
565+ |> toStyleSheet opts
566+ in
559 if static then
567 if static then
560− ( "static-stylesheet", staticRoot )
568+ ( "static-stylesheet", staticRoot opts )
561 :: ( "dynamic-stylesheet"
569 :: ( "dynamic-stylesheet"
562− , styles
570+ , dynamicStyleSheet
563− |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
564− |> Tuple.second
565− -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
566− -- |> sortedReduce
567− |> toStyleSheet opts
568 )
571 )
569 :: children
572 :: children
570
573
571 else
574 else
572 ( "dynamic-stylesheet"
575 ( "dynamic-stylesheet"
573− , styles
576+ , dynamicStyleSheet
574− |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
575− |> Tuple.second
576− -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
577− -- |> sortedReduce
578− |> toStyleSheet opts
579 )
577 )
580 :: children
578 :: children
581
579
@@ -1055,7 +1053,7 @@ gatherAttrRecursive classes node has transform styles attrs children elementAttr
1055 name =
1053 name =
1056 "height-px-" ++ val
1054 "height-px-" ++ val
1057 in
1055 in
1058− gatherAttrRecursive (name ++ " " ++ classes)
1056+ gatherAttrRecursive (Internal.Style.classes.heightExact ++ " " ++ name ++ " " ++ classes)
1059 node
1057 node
1060 (Flag.add Flag.height has)
1058 (Flag.add Flag.height has)
1061 transform
1059 transform
@@ -1144,20 +1142,15 @@ gatherAttrRecursive classes node has transform styles attrs children elementAttr
1144 gatherAttrRecursive classes (addNodeName "h6" node) has transform styles attrs children remaining
1142 gatherAttrRecursive classes (addNodeName "h6" node) has transform styles attrs children remaining
1145
1143
1146 Paragraph ->
1144 Paragraph ->
1147− let
1145+ -- previously we rendered a <p> tag, though apparently this invalidates the html if it has <div>s inside.
1148− newNode =
1146+ -- Since we can't guaranteee that there are no divs, we need another strategy.
1149− case node of
1147+ -- While it's not documented in many places, there apparently is a paragraph aria role
1150− Generic ->
1148+ -- https://github.com/w3c/aria/blob/11f85f41a5b621fdbe85fc9bcdcd270e653a48ba/common/script/roleInfo.js
1151− NodeName "p"
1149+ -- Though we'll need to wait till it gets released in an official wai-aria spec to use it.
1152−
1150+ -- If it's used at the moment, then Lighthouse complains (likely rightfully) that role paragraph is not recognized.
1153− NodeName name ->
1154− NodeName name
1155−
1156− Embedded x y ->
1157− Embedded x y
1158− in
1159− gatherAttrRecursive classes
1151+ gatherAttrRecursive
1160− newNode
1152+ classes
1153+ node
1161 has
1154 has
1162 transform
1155 transform
1163 styles
1156 styles
@@ -1443,7 +1436,7 @@ renderHeight h =
1443 "height-px-" ++ val
1436 "height-px-" ++ val
1444 in
1437 in
1445 ( Flag.none
1438 ( Flag.none
1446− , name
1439+ , Internal.Style.classes.heightExact ++ " " ++ name
1447 , [ Single name "height" (val ++ "px") ]
1440 , [ Single name "height" (val ++ "px") ]
1448 )
1441 )
1449
1442
@@ -1466,7 +1459,7 @@ renderHeight h =
1466 , [ Single
1459 , [ Single
1467 (Internal.Style.classes.any
1460 (Internal.Style.classes.any
1468 ++ "."
1461 ++ "."
1469− ++ Internal.Style.classes.row
1462+ ++ Internal.Style.classes.column
1470 ++ " > "
1463 ++ " > "
1471 ++ (Internal.Style.dot <| "height-fill-" ++ String.fromInt portion)
1464 ++ (Internal.Style.dot <| "height-fill-" ++ String.fromInt portion)
1472 )
1465 )
@@ -1485,7 +1478,9 @@ renderHeight h =
1485 Single
1478 Single
1486 cls
1479 cls
1487 "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.
1488− (String.fromInt minSize ++ "px")
1483+ (String.fromInt minSize ++ "px !important")
1489
1484
1490 ( newFlag, newAttrs, newStyle ) =
1485 ( newFlag, newAttrs, newStyle ) =
1491 renderHeight len
1486 renderHeight len
@@ -1822,8 +1817,20 @@ defaultOptions =
1822 }
1817 }
1823
1818
1824
1819
1825−staticRoot =
1820+staticRoot : OptionRecord -> VirtualDom.Node msg
1821+staticRoot opts =
1822+ case opts.mode of
1823+ Layout ->
1824+ -- wrap the style node in a div to prevent `Dark Reader` from blowin up the dom.
1825+ VirtualDom.node "div"
1826+ []
1826− VirtualDom.node "style" [] [ VirtualDom.text Internal.Style.rules ]
1827+ [ VirtualDom.node "style" [] [ VirtualDom.text Internal.Style.rules ] ]
1828+
1829+ NoStaticStyleSheet ->
1830+ VirtualDom.text ""
1831+
1832+ WithVirtualCss ->
1833+ VirtualDom.node "elm-ui-static-rules" [ VirtualDom.property "rules" (Encode.string Internal.Style.rules) ] []
1827
1834
1828
1835
1829addWhen ifThis x to =
1836addWhen ifThis x to =
@@ -1941,7 +1948,7 @@ type Spacing
1941
1948
1942
1949
1943type Padding
1950type Padding
1944− = Padding String Int Int Int Int
1951+ = Padding String Float Float Float Float
1945
1952
1946
1953
1947extractSpacingAndPadding : List (Attribute aligned msg) -> ( Maybe Padding, Maybe Spacing )
1954extractSpacingAndPadding : List (Attribute aligned msg) -> ( Maybe Padding, Maybe Spacing )
@@ -2037,34 +2044,44 @@ getHeight attrs =
2037 Nothing
2044 Nothing
2038
2045
2039
2046
2047+textElementClasses : String
2048+textElementClasses =
2049+ classes.any
2050+ ++ " "
2051+ ++ classes.text
2052+ ++ " "
2053+ ++ classes.widthContent
2054+ ++ " "
2055+ ++ classes.heightContent
2056+
2057+
2040textElement : String -> VirtualDom.Node msg
2058textElement : String -> VirtualDom.Node msg
2041textElement str =
2059textElement str =
2042 Html.div
2060 Html.div
2043 [ Html.Attributes.class
2061 [ Html.Attributes.class
2044− (String.join " "
2062+ textElementClasses
2045− [ classes.any
2046− , classes.text
2047− , classes.widthContent
2048− , classes.heightContent
2049− ]
2050− )
2051 ]
2063 ]
2052 [ Html.text str ]
2064 [ Html.text str ]
2053
2065
2054
2066
2067+textElementFillClasses : String
2068+textElementFillClasses =
2069+ classes.any
2070+ ++ " "
2071+ ++ classes.text
2072+ ++ " "
2073+ ++ classes.widthFill
2074+ ++ " "
2075+ ++ classes.heightFill
2076+
2077+
2055textElementFill : String -> VirtualDom.Node msg
2078textElementFill : String -> VirtualDom.Node msg
2056textElementFill str =
2079textElementFill str =
2057− VirtualDom.node "div"
2080+ Html.div
2058 [ Html.Attributes.class
2081 [ Html.Attributes.class
2059− (String.join " "
2082+ textElementFillClasses
2060− [ classes.any
2061− , classes.text
2062− , classes.widthFill
2063− , classes.heightFill
2064− ]
2065− )
2066 ]
2083 ]
2067− [ VirtualDom.text str ]
2084+ [ Html.text str ]
2068
2085
2069
2086
2070type Children x
2087type Children x
@@ -2072,10 +2089,6 @@ type Children x
2072 | Keyed (List ( String, x ))
2089 | Keyed (List ( String, x ))
2073
2090
2074
2091
2075−
2076−-- toHtml : OptionRecord -> Element msg -> VirtualDom.Node msg
2077−
2078−
2079toHtml mode el =
2092toHtml mode el =
2080 case el of
2093 case el of
2081 Unstyled html ->
2094 Unstyled html ->
@@ -2111,8 +2124,7 @@ renderRoot optionList attributes child =
2111
2124
2112
2125
2113type RenderMode
2126type RenderMode
2114− = Viewport
2115− | Layout
2127+ = Layout
2116 | NoStaticStyleSheet
2128 | NoStaticStyleSheet
2117 | WithVirtualCss
2129 | WithVirtualCss
2118
2130
@@ -2161,8 +2173,8 @@ rootStyle =
2161 , SansSerif
2173 , SansSerif
2162 ]
2174 ]
2163 in
2175 in
2164− [ StyleClass Flag.bgColor (Colored ("bg-color-" ++ formatColorClass (Rgba 1 1 1 0)) "background-color" (Rgba 1 1 1 0))
2176+ [ StyleClass Flag.bgColor (Colored ("bg-" ++ formatColorClass (Rgba 1 1 1 0)) "background-color" (Rgba 1 1 1 0))
2165− , StyleClass Flag.fontColor (Colored ("font-color-" ++ formatColorClass (Rgba 0 0 0 1)) "color" (Rgba 0 0 0 1))
2177+ , StyleClass Flag.fontColor (Colored ("fc-" ++ formatColorClass (Rgba 0 0 0 1)) "color" (Rgba 0 0 0 1))
2166 , StyleClass Flag.fontSize (FontSize 20)
2178 , StyleClass Flag.fontSize (FontSize 20)
2167 , StyleClass Flag.fontFamily <|
2179 , StyleClass Flag.fontFamily <|
2168 FontFamily (List.foldl renderFontClassName "font-" families)
2180 FontFamily (List.foldl renderFontClassName "font-" families)
@@ -2232,7 +2244,11 @@ renderFocusStyle focus =
2232 , Just <| Property "outline" "none"
2244 , Just <| Property "outline" "none"
2233 ]
2245 ]
2234 )
2246 )
2247+ , Style
2235− , 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+ )
2236 (List.filterMap identity
2252 (List.filterMap identity
2237 [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
2253 [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
2238 , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
2254 , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
@@ -2337,7 +2353,31 @@ optionsToRecord options =
2337
2353
2338toStyleSheet : OptionRecord -> List Style -> VirtualDom.Node msg
2354toStyleSheet : OptionRecord -> List Style -> VirtualDom.Node msg
2339toStyleSheet options styleSheet =
2355toStyleSheet options styleSheet =
2356+ case options.mode of
2357+ Layout ->
2358+ -- wrap the style node in a div to prevent `Dark Reader` from blowin up the dom.
2359+ VirtualDom.node "div"
2360+ []
2361+ [ VirtualDom.node "style"
2362+ []
2363+ [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2364+ ]
2365+
2366+ NoStaticStyleSheet ->
2367+ -- wrap the style node in a div to prevent `Dark Reader` from blowin up the dom.
2368+ VirtualDom.node "div"
2369+ []
2370+ [ VirtualDom.node "style"
2371+ []
2340− VirtualDom.node "style" [] [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2372+ [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2373+ ]
2374+
2375+ WithVirtualCss ->
2376+ VirtualDom.node "elm-ui-rules"
2377+ [ VirtualDom.property "rules"
2378+ (encodeStyles options styleSheet)
2379+ ]
2380+ []
2341
2381
2342
2382
2343renderTopLevelValues rules =
2383renderTopLevelValues rules =
@@ -2553,508 +2593,492 @@ renderProps force (Property key val) existing =
2553 existing ++ "\n " ++ key ++ ": " ++ val ++ ";"
2593 existing ++ "\n " ++ key ++ ": " ++ val ++ ";"
2554
2594
2555
2595
2596+encodeStyles options stylesheet =
2597+ stylesheet
2598+ |> List.map
2599+ (\style ->
2600+ let
2601+ styled =
2602+ renderStyleRule options style Nothing
2603+ in
2604+ ( getStyleName style, Encode.list Encode.string styled )
2605+ )
2606+ |> Encode.object
2607+
2608+
2556toStyleSheetString : OptionRecord -> List Style -> String
2609toStyleSheetString : OptionRecord -> List Style -> String
2557toStyleSheetString options stylesheet =
2610toStyleSheetString options stylesheet =
2558 let
2611 let
2559− renderStyle maybePseudo selector props =
2612+ combine style rendered =
2560− case maybePseudo of
2613+ { rules = rendered.rules ++ renderStyleRule options style Nothing
2561− Nothing ->
2614+ , topLevel =
2562− selector ++ "{" ++ List.foldl (renderProps False) "" props ++ "\n}"
2615+ case topLevelValue style of
2563−
2616+ Nothing ->
2564− Just pseudo ->
2617+ rendered.topLevel
2565− case pseudo of
2566− Hover ->
2567− case options.hover of
2568− NoHover ->
2569− ""
2570
2618
2571− ForceHover ->
2619+ Just topLevel ->
2572− selector ++ "-hv {" ++ List.foldl (renderProps True) "" props ++ "\n}"
2620+ topLevel :: rendered.topLevel
2621+ }
2622+ in
2623+ case List.foldl combine { topLevel = [], rules = [] } stylesheet of
2624+ { topLevel, rules } ->
2625+ renderTopLevelValues topLevel ++ String.concat rules
2573
2626
2574− AllowHover ->
2575− selector ++ "-hv:hover {" ++ List.foldl (renderProps False) "" props ++ "\n}"
2576
2627
2577− Focus ->
2628+renderStyle : OptionRecord -> Maybe PseudoClass -> String -> List Property -> List String
2578− let
2629+renderStyle options maybePseudo selector props =
2579− renderedProps =
2630+ case maybePseudo of
2631+ Nothing ->
2580− List.foldl (renderProps False) "" props
2632+ [ selector ++ "{" ++ List.foldl (renderProps False) "" props ++ "\n}" ]
2581− in
2582− String.join "\n"
2583− [ selector
2584− ++ "-fs:focus {"
2585− ++ renderedProps
2586− ++ "\n}"
2587− , "."
2588− ++ classes.any
2589− ++ ":focus ~ "
2590− ++ selector
2591− ++ "-fs:not(.focus) {"
2592− ++ renderedProps
2593− ++ "\n}"
2594− , "."
2595− ++ classes.any
2596− ++ ":focus "
2597− ++ selector
2598− ++ "-fs {"
2599− ++ renderedProps
2600− ++ "\n}"
2601− , ".focusable-parent:focus ~ "
2602− ++ "."
2603− ++ classes.any
2604− ++ " "
2605− ++ selector
2606− ++ "-fs {"
2607− ++ renderedProps
2608− ++ "\n}"
2609− ]
2610
2633
2611− Active ->
2634+ Just pseudo ->
2612− selector ++ "-act:active {" ++ List.foldl (renderProps False) "" props ++ "\n}"
2635+ case pseudo of
2636+ Hover ->
2637+ case options.hover of
2638+ NoHover ->
2639+ []
2613
2640
2614− renderStyleRule rule maybePseudo =
2641+ ForceHover ->
2615− case rule of
2642+ [ selector ++ "-hv {" ++ List.foldl (renderProps True) "" props ++ "\n}" ]
2616− Style selector props ->
2617− renderStyle maybePseudo selector props
2618
2643
2619− Shadows name prop ->
2644+ AllowHover ->
2620− renderStyle
2645+ [ selector ++ "-hv:hover {" ++ List.foldl (renderProps False) "" props ++ "\n}" ]
2621− maybePseudo
2622− ("." ++ name)
2623− [ Property "box-shadow" prop
2624− ]
2625
2646
2626− Transparency name transparency ->
2647+ Focus ->
2627 let
2648 let
2628− opacity =
2649+ renderedProps =
2629− (1 - transparency)
2650+ List.foldl (renderProps False) "" props
2630− |> min 1
2631− |> max 0
2632 in
2651 in
2633− renderStyle
2652+ [ selector ++ "-fs:focus {" ++ renderedProps ++ "\n}"
2634− maybePseudo
2653+ , ("." ++ classes.any ++ ":focus " ++ selector ++ "-fs {")
2635− ("." ++ name)
2654+ ++ renderedProps
2636− [ Property "opacity" (String.fromFloat opacity)
2655+ ++ "\n}"
2656+ , (selector ++ "-fs:focus-within {")
2657+ ++ renderedProps
2658+ ++ "\n}"
2659+ , (".ui-slide-bar:focus + " ++ Internal.Style.dot classes.any ++ " .focusable-thumb" ++ selector ++ "-fs {")
2660+ ++ renderedProps
2661+ ++ "\n}"
2637− ]
2662+ ]
2638
2663
2639− FontSize i ->
2664+ Active ->
2640− renderStyle
2665+ [ selector ++ "-act:active {" ++ List.foldl (renderProps False) "" props ++ "\n}" ]
2641− maybePseudo
2642− (".font-size-" ++ String.fromInt i)
2643− [ Property "font-size" (String.fromInt i ++ "px")
2644− ]
2645
2666
2646− FontFamily name typefaces ->
2647− let
2648− features =
2649− typefaces
2650− |> List.filterMap renderVariants
2651− |> String.join ", "
2652−
2653− families =
2654− [ Property "font-family"
2655− (typefaces
2656− |> List.map fontName
2657− |> String.join ", "
2658− )
2659− , Property "font-feature-settings" features
2660− , Property "font-variant"
2661− (if List.any hasSmallCaps typefaces then
2662− "small-caps"
2663
2667
2664− else
2668+renderStyleRule : OptionRecord -> Style -> Maybe PseudoClass -> List String
2665− "normal"
2669+renderStyleRule options rule maybePseudo =
2666− )
2670+ case rule of
2667− ]
2671+ Style selector props ->
2668− in
2672+ renderStyle options maybePseudo selector props
2669− String.join " "
2673+
2674+ Shadows name prop ->
2670− [ renderStyle
2675+ renderStyle options
2671− maybePseudo
2676+ maybePseudo
2672− ("." ++ name)
2677+ ("." ++ name)
2673− families
2678+ [ Property "box-shadow" prop
2674− ]
2679+ ]
2675
2680
2676− Single class prop val ->
2681+ Transparency name transparency ->
2682+ let
2683+ opacity =
2684+ (1 - transparency)
2685+ |> min 1
2686+ |> max 0
2687+ in
2677− renderStyle
2688+ renderStyle options
2678− maybePseudo
2689+ maybePseudo
2679− ("." ++ class)
2690+ ("." ++ name)
2680− [ Property prop val
2691+ [ Property "opacity" (String.fromFloat opacity)
2681− ]
2692+ ]
2682
2693
2683− Colored class prop color ->
2694+ FontSize i ->
2684− -- This is because the trick we're using to make a txtarea be height:content
2695+ renderStyle options
2685− -- stops the inheritance chain for font color.
2686− -- So we need to pack our bags, hike up our socks and jump over the break.
2687− if prop == "color" then
2688− String.concat
2689− [ renderStyle
2690− maybePseudo
2696+ maybePseudo
2691− ("." ++ class)
2697+ (".font-size-" ++ String.fromInt i)
2692− [ Property prop (formatColor color)
2698+ [ Property "font-size" (String.fromInt i ++ "px")
2693− ]
2694− , renderStyle
2695− maybePseudo
2696− ("." ++ class ++ " ." ++ classes.inputMultilineParent ++ " ." ++ classes.inputMultiline)
2697− [ Property prop (formatColor color)
2698− ]
2699− ]
2699+ ]
2700
2700
2701− else
2701+ FontFamily name typefaces ->
2702− renderStyle
2702+ let
2703− maybePseudo
2703+ features =
2704− ("." ++ class)
2704+ typefaces
2705− [ Property prop (formatColor color)
2705+ |> List.filterMap renderVariants
2706− ]
2706+ |> String.join ", "
2707+
2708+ families =
2709+ [ Property "font-family"
2710+ (typefaces
2711+ |> List.map fontName
2712+ |> String.join ", "
2713+ )
2714+ , Property "font-feature-settings" features
2715+ , Property "font-variant"
2716+ (if List.any hasSmallCaps typefaces then
2717+ "small-caps"
2707
2718
2708− SpacingStyle cls x y ->
2719+ else
2709− let
2720+ "normal"
2710− class =
2721+ )
2711− "." ++ cls
2722+ ]
2723+ in
2724+ renderStyle options
2725+ maybePseudo
2726+ ("." ++ name)
2727+ families
2712
2728
2713− halfX =
2729+ Single class prop val ->
2714− String.fromFloat (toFloat x / 2) ++ "px"
2730+ renderStyle options
2731+ maybePseudo
2732+ ("." ++ class)
2733+ [ Property prop val
2734+ ]
2715
2735
2716− halfY =
2736+ Colored class prop color ->
2717− String.fromFloat (toFloat y / 2) ++ "px"
2737+ renderStyle options
2738+ maybePseudo
2739+ ("." ++ class)
2740+ [ Property prop (formatColor color)
2741+ ]
2718
2742
2719− xPx =
2743+ SpacingStyle cls x y ->
2720− String.fromInt x ++ "px"
2744+ let
2745+ class =
2746+ "." ++ cls
2721
2747
2722− yPx =
2748+ halfX =
2723− String.fromInt y ++ "px"
2749+ String.fromFloat (toFloat x / 2) ++ "px"
2724
2750
2725− row =
2751+ halfY =
2726− "." ++ Internal.Style.classes.row
2752+ String.fromFloat (toFloat y / 2) ++ "px"
2727
2753
2728− wrappedRow =
2754+ xPx =
2729− "." ++ Internal.Style.classes.wrapped ++ row
2755+ String.fromInt x ++ "px"
2730
2756
2731− column =
2757+ yPx =
2732− "." ++ Internal.Style.classes.column
2758+ String.fromInt y ++ "px"
2733
2759
2734− page =
2760+ row =
2735− "." ++ Internal.Style.classes.page
2761+ "." ++ Internal.Style.classes.row
2736
2762
2737− paragraph =
2763+ wrappedRow =
2738− "." ++ Internal.Style.classes.paragraph
2764+ "." ++ Internal.Style.classes.wrapped ++ row
2739
2765
2740− left =
2766+ column =
2741− "." ++ Internal.Style.classes.alignLeft
2767+ "." ++ Internal.Style.classes.column
2742
2768
2743− right =
2769+ page =
2744− "." ++ Internal.Style.classes.alignRight
2770+ "." ++ Internal.Style.classes.page
2745
2771
2746− any =
2772+ paragraph =
2747− "." ++ Internal.Style.classes.any
2773+ "." ++ Internal.Style.classes.paragraph
2748
2774
2749− single =
2775+ left =
2750− "." ++ Internal.Style.classes.single
2776+ "." ++ Internal.Style.classes.alignLeft
2751− in
2752− String.concat
2753− [ renderStyle maybePseudo (class ++ row ++ " > " ++ any ++ " + " ++ any) [ Property "margin-left" xPx ]
2754−
2755− -- margins don't apply to last element of normal, unwrapped rows
2756− -- , renderStyle maybePseudo (class ++ row ++ " > " ++ any ++ ":first-child") [ Property "margin" "0" ]
2757− -- For wrapped rows, margins always apply because we handle "canceling out" the other margins manually in the element.
2758− , renderStyle maybePseudo
2759− (class ++ wrappedRow ++ " > " ++ any)
2760− [ Property "margin" (halfY ++ " " ++ halfX)
2761− ]
2762
2777
2763− -- , renderStyle maybePseudo
2764− -- (class ++ wrappedRow ++ " > " ++ any ++ ":last-child")
2765− -- [ Property "margin-right" "0"
2778+ right =
2766− -- ]
2779+ "." ++ Internal.Style.classes.alignRight
2767− -- columns
2768− , renderStyle maybePseudo (class ++ column ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2769− , renderStyle maybePseudo (class ++ page ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2770− , renderStyle maybePseudo (class ++ page ++ " > " ++ left) [ Property "margin-right" xPx ]
2771− , renderStyle maybePseudo (class ++ page ++ " > " ++ right) [ Property "margin-left" xPx ]
2772− , renderStyle
2773− maybePseudo
2774− (class ++ paragraph)
2775− [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2776− ]
2777− , renderStyle
2778− maybePseudo
2779− ("textarea" ++ any ++ class)
2780− [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2781− , Property "height" ("calc(100% + " ++ String.fromInt y ++ "px)")
2782− ]
2783
2780
2784− -- , renderStyle
2781+ any =
2785− -- maybePseudo
2782+ "." ++ Internal.Style.classes.any
2786− -- (class ++ paragraph ++ " > " ++ any)
2787− -- [ Property "margin-right" xPx
2788− -- , Property "margin-bottom" yPx
2789− -- ]
2790− , renderStyle
2791− maybePseudo
2792− (class ++ paragraph ++ " > " ++ left)
2793− [ Property "margin-right" xPx
2794− ]
2795− , renderStyle
2796− maybePseudo
2797− (class ++ paragraph ++ " > " ++ right)
2798− [ Property "margin-left" xPx
2799− ]
2800− , renderStyle
2801− maybePseudo
2802− (class ++ paragraph ++ "::after")
2803− [ Property "content" "''"
2804− , Property "display" "block"
2805− , Property "height" "0"
2806− , Property "width" "0"
2807− , Property "margin-top" (String.fromInt (-1 * (y // 2)) ++ "px")
2808− ]
2809− , renderStyle
2810− maybePseudo
2811− (class ++ paragraph ++ "::before")
2812− [ Property "content" "''"
2813− , Property "display" "block"
2814− , Property "height" "0"
2815− , Property "width" "0"
2816− , Property "margin-bottom" (String.fromInt (-1 * (y // 2)) ++ "px")
2817− ]
2818− ]
2819
2783
2820− PaddingStyle cls top right bottom left ->
2784+ single =
2821− let
2785+ "." ++ Internal.Style.classes.single
2822− class =
2823− "."
2824− ++ cls
2825− in
2786+ in
2826− renderStyle
2787+ List.concat
2827− maybePseudo
2788+ [ renderStyle options maybePseudo (class ++ row ++ " > " ++ any ++ " + " ++ any) [ Property "margin-left" xPx ]
2828− class
2789+
2829− [ Property "padding"
2790+ -- margins don't apply to last element of normal, unwrapped rows
2830− (String.fromInt top
2791+ -- , renderStyle options maybePseudo (class ++ row ++ " > " ++ any ++ ":first-child") [ Property "margin" "0" ]
2831− ++ "px "
2792+ -- For wrapped rows, margins always apply because we handle "canceling out" the other margins manually in the element.
2832− ++ String.fromInt right
2793+ , renderStyle options
2833− ++ "px "
2794+ maybePseudo
2834− ++ String.fromInt bottom
2795+ (class ++ wrappedRow ++ " > " ++ any)
2835− ++ "px "
2796+ [ Property "margin" (halfY ++ " " ++ halfX)
2836− ++ String.fromInt left
2837− ++ "px"
2838− )
2839− ]
2797+ ]
2840
2798
2841− BorderWidth cls top right bottom left ->
2799+ -- , renderStyle options maybePseudo
2842− let
2800+ -- (class ++ wrappedRow ++ " > " ++ any ++ ":last-child")
2843− class =
2801+ -- [ Property "margin-right" "0"
2844− "."
2802+ -- ]
2845− ++ cls
2803+ -- columns
2846− in
2804+ , renderStyle options maybePseudo (class ++ column ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2805+ , renderStyle options maybePseudo (class ++ page ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2806+ , renderStyle options maybePseudo (class ++ page ++ " > " ++ left) [ Property "margin-right" xPx ]
2807+ , renderStyle options maybePseudo (class ++ page ++ " > " ++ right) [ Property "margin-left" xPx ]
2847− renderStyle
2808+ , renderStyle options
2848− maybePseudo
2809+ maybePseudo
2849− class
2810+ (class ++ paragraph)
2850− [ Property "border-width"
2851− (String.fromInt top
2811+ [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2852− ++ "px "
2812+ ]
2853− ++ String.fromInt right
2813+ , renderStyle options
2854− ++ "px "
2814+ maybePseudo
2815+ ("textarea" ++ any ++ class)
2855− ++ String.fromInt bottom
2816+ [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2856− ++ "px "
2857− ++ String.fromInt left
2817+ , Property "height" ("calc(100% + " ++ String.fromInt y ++ "px)")
2858− ++ "px"
2859− )
2860− ]
2818+ ]
2861
2819
2862− GridTemplateStyle template ->
2820+ -- , renderStyle options
2863− let
2821+ -- maybePseudo
2864− class =
2822+ -- (class ++ paragraph ++ " > " ++ any)
2865− ".grid-rows-"
2823+ -- [ Property "margin-right" xPx
2866− ++ String.join "-" (List.map lengthClassName template.rows)
2824+ -- , Property "margin-bottom" yPx
2867− ++ "-cols-"
2825+ -- ]
2868− ++ String.join "-" (List.map lengthClassName template.columns)
2826+ , renderStyle options
2869− ++ "-space-x-"
2827+ maybePseudo
2870− ++ lengthClassName (Tuple.first template.spacing)
2828+ (class ++ paragraph ++ " > " ++ left)
2871− ++ "-space-y-"
2829+ [ Property "margin-right" xPx
2872− ++ lengthClassName (Tuple.second template.spacing)
2830+ ]
2873−
2831+ , renderStyle options
2874− ySpacing =
2832+ maybePseudo
2875− toGridLength (Tuple.second template.spacing)
2833+ (class ++ paragraph ++ " > " ++ right)
2876−
2834+ [ Property "margin-left" xPx
2877− xSpacing =
2835+ ]
2878− toGridLength (Tuple.first template.spacing)
2836+ , renderStyle options
2879−
2837+ maybePseudo
2880− toGridLength x =
2838+ (class ++ paragraph ++ "::after")
2881− toGridLengthHelper Nothing Nothing x
2839+ [ Property "content" "''"
2882−
2840+ , Property "display" "block"
2883− toGridLengthHelper minimum maximum x =
2841+ , Property "height" "0"
2884− case x of
2842+ , Property "width" "0"
2885− Px px ->
2843+ , Property "margin-top" (String.fromInt (-1 * (y // 2)) ++ "px")
2886− String.fromInt px ++ "px"
2844+ ]
2887−
2845+ , renderStyle options
2888− Content ->
2846+ maybePseudo
2889− case ( minimum, maximum ) of
2847+ (class ++ paragraph ++ "::before")
2890− ( Nothing, Nothing ) ->
2848+ [ Property "content" "''"
2891− "max-content"
2849+ , Property "display" "block"
2892−
2850+ , Property "height" "0"
2893− ( Just minSize, Nothing ) ->
2851+ , Property "width" "0"
2894− "minmax(" ++ String.fromInt minSize ++ "px, " ++ "max-content)"
2852+ , Property "margin-bottom" (String.fromInt (-1 * (y // 2)) ++ "px")
2895−
2853+ ]
2896− ( Nothing, Just maxSize ) ->
2854+ ]
2897− "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2898−
2899− ( Just minSize, Just maxSize ) ->
2900− "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2901−
2902− Fill i ->
2903− case ( minimum, maximum ) of
2904− ( Nothing, Nothing ) ->
2905− String.fromInt i ++ "fr"
2906−
2907− ( Just minSize, Nothing ) ->
2908− "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt i ++ "fr" ++ "fr)"
2909−
2910− ( Nothing, Just maxSize ) ->
2911− "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2912−
2913− ( Just minSize, Just maxSize ) ->
2914− "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2915−
2916− Min m len ->
2917− toGridLengthHelper (Just m) maximum len
2918−
2919− Max m len ->
2920− toGridLengthHelper minimum (Just m) len
2921−
2922− msColumns =
2923− template.columns
2924− |> List.map toGridLength
2925− |> String.join ySpacing
2926− |> (\x -> "-ms-grid-columns: " ++ x ++ ";")
2927−
2928− msRows =
2929− template.columns
2930− |> List.map toGridLength
2931− |> String.join ySpacing
2932− |> (\x -> "-ms-grid-rows: " ++ x ++ ";")
2933−
2934− base =
2935− class ++ "{" ++ msColumns ++ msRows ++ "}"
2936−
2937− columns =
2938− template.columns
2939− |> List.map toGridLength
2940− |> String.join " "
2941− |> (\x -> "grid-template-columns: " ++ x ++ ";")
2942−
2943− rows =
2944− template.rows
2945− |> List.map toGridLength
2946− |> String.join " "
2947− |> (\x -> "grid-template-rows: " ++ x ++ ";")
2948−
2949− gapX =
2950− "grid-column-gap:" ++ toGridLength (Tuple.first template.spacing) ++ ";"
2951−
2952− gapY =
2953− "grid-row-gap:" ++ toGridLength (Tuple.second template.spacing) ++ ";"
2954−
2955− modernGrid =
2956− class ++ "{" ++ columns ++ rows ++ gapX ++ gapY ++ "}"
2957−
2958− supports =
2959− "@supports (display:grid) {" ++ modernGrid ++ "}"
2960− in
2961− base ++ supports
2962
2855
2963− GridPosition position ->
2856+ PaddingStyle cls top right bottom left ->
2964− let
2857+ let
2965− class =
2858+ class =
2966− ".grid-pos-"
2859+ "."
2967− ++ String.fromInt position.row
2860+ ++ cls
2968− ++ "-"
2969− ++ String.fromInt position.col
2970− ++ "-"
2971− ++ String.fromInt position.width
2972− ++ "-"
2973− ++ String.fromInt position.height
2974−
2975− msPosition =
2976− String.join " "
2977− [ "-ms-grid-row: "
2978− ++ String.fromInt position.row
2979− ++ ";"
2980− , "-ms-grid-row-span: "
2981− ++ String.fromInt position.height
2982− ++ ";"
2983− , "-ms-grid-column: "
2984− ++ String.fromInt position.col
2985− ++ ";"
2986− , "-ms-grid-column-span: "
2987− ++ String.fromInt position.width
2988− ++ ";"
2989− ]
2990−
2991− base =
2992− class ++ "{" ++ msPosition ++ "}"
2993−
2994− modernPosition =
2995− String.join " "
2996− [ "grid-row: "
2997− ++ String.fromInt position.row
2998− ++ " / "
2999− ++ String.fromInt (position.row + position.height)
3000− ++ ";"
3001− , "grid-column: "
3002− ++ String.fromInt position.col
3003− ++ " / "
3004− ++ String.fromInt (position.col + position.width)
3005− ++ ";"
3006− ]
3007−
3008− modernGrid =
3009− class ++ "{" ++ modernPosition ++ "}"
3010−
3011− supports =
3012− "@supports (display:grid) {" ++ modernGrid ++ "}"
3013− in
2861+ in
3014− base ++ supports
2862+ renderStyle options
2863+ maybePseudo
2864+ class
2865+ [ Property "padding"
2866+ (String.fromFloat top
2867+ ++ "px "
2868+ ++ String.fromFloat right
2869+ ++ "px "
2870+ ++ String.fromFloat bottom
2871+ ++ "px "
2872+ ++ String.fromFloat left
2873+ ++ "px"
2874+ )
2875+ ]
3015
2876
3016− PseudoSelector class styles ->
2877+ BorderWidth cls top right bottom left ->
3017− let
2878+ let
3018− renderPseudoRule style =
2879+ class =
3019− renderStyleRule style (Just class)
2880+ "."
2881+ ++ cls
3020− in
2882+ in
3021− List.map renderPseudoRule styles
2883+ renderStyle options
2884+ maybePseudo
2885+ class
2886+ [ Property "border-width"
2887+ (String.fromInt top
2888+ ++ "px "
2889+ ++ String.fromInt right
2890+ ++ "px "
2891+ ++ String.fromInt bottom
2892+ ++ "px "
2893+ ++ String.fromInt left
2894+ ++ "px"
2895+ )
2896+ ]
2897+
2898+ GridTemplateStyle template ->
2899+ let
2900+ class =
2901+ ".grid-rows-"
2902+ ++ String.join "-" (List.map lengthClassName template.rows)
2903+ ++ "-cols-"
2904+ ++ String.join "-" (List.map lengthClassName template.columns)
2905+ ++ "-space-x-"
2906+ ++ lengthClassName (Tuple.first template.spacing)
2907+ ++ "-space-y-"
2908+ ++ lengthClassName (Tuple.second template.spacing)
2909+
2910+ ySpacing =
2911+ toGridLength (Tuple.second template.spacing)
2912+
2913+ xSpacing =
2914+ toGridLength (Tuple.first template.spacing)
2915+
2916+ toGridLength x =
2917+ toGridLengthHelper Nothing Nothing x
2918+
2919+ toGridLengthHelper minimum maximum x =
2920+ case x of
2921+ Px px ->
2922+ String.fromInt px ++ "px"
2923+
2924+ Content ->
2925+ case ( minimum, maximum ) of
2926+ ( Nothing, Nothing ) ->
2927+ "max-content"
2928+
2929+ ( Just minSize, Nothing ) ->
2930+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ "max-content)"
2931+
2932+ ( Nothing, Just maxSize ) ->
2933+ "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2934+
2935+ ( Just minSize, Just maxSize ) ->
2936+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2937+
2938+ Fill i ->
2939+ case ( minimum, maximum ) of
2940+ ( Nothing, Nothing ) ->
2941+ String.fromInt i ++ "fr"
2942+
2943+ ( Just minSize, Nothing ) ->
2944+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt i ++ "fr" ++ "fr)"
2945+
2946+ ( Nothing, Just maxSize ) ->
2947+ "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2948+
2949+ ( Just minSize, Just maxSize ) ->
2950+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2951+
2952+ Min m len ->
2953+ toGridLengthHelper (Just m) maximum len
2954+
2955+ Max m len ->
2956+ toGridLengthHelper minimum (Just m) len
2957+
2958+ msColumns =
2959+ template.columns
2960+ |> List.map toGridLength
2961+ |> String.join ySpacing
2962+ |> (\x -> "-ms-grid-columns: " ++ x ++ ";")
2963+
2964+ msRows =
2965+ template.columns
2966+ |> List.map toGridLength
2967+ |> String.join ySpacing
2968+ |> (\x -> "-ms-grid-rows: " ++ x ++ ";")
2969+
2970+ base =
2971+ class ++ "{" ++ msColumns ++ msRows ++ "}"
2972+
2973+ columns =
2974+ template.columns
2975+ |> List.map toGridLength
3022 |> String.join " "
2976 |> String.join " "
2977+ |> (\x -> "grid-template-columns: " ++ x ++ ";")
3023
2978
3024− Transform transform ->
2979+ rows =
3025− let
2980+ template.rows
3026− val =
2981+ |> List.map toGridLength
3027− transformValue transform
2982+ |> String.join " "
2983+ |> (\x -> "grid-template-rows: " ++ x ++ ";")
3028
2984
3029− class =
2985+ gapX =
3030− transformClass transform
2986+ "grid-column-gap:" ++ toGridLength (Tuple.first template.spacing) ++ ";"
3031− in
3032− case ( class, val ) of
3033− ( Just cls, Just v ) ->
3034− renderStyle
3035− maybePseudo
3036− ("." ++ cls)
3037− [ Property "transform"
3038− v
3039− ]
3040
2987
3041− _ ->
2988+ gapY =
3042− ""
2989+ "grid-row-gap:" ++ toGridLength (Tuple.second template.spacing) ++ ";"
3043
2990
3044− combine style rendered =
2991+ modernGrid =
3045− { rules = rendered.rules ++ renderStyleRule style Nothing
2992+ class ++ "{" ++ columns ++ rows ++ gapX ++ gapY ++ "}"
3046− , topLevel =
3047− case topLevelValue style of
3048− Nothing ->
3049− rendered.topLevel
3050
2993
3051− Just topLevel ->
2994+ supports =
3052− topLevel :: rendered.topLevel
2995+ "@supports (display:grid) {" ++ modernGrid ++ "}"
3053− }
2996+ in
2997+ [ base
2998+ , supports
2999+ ]
3000+
3001+ GridPosition position ->
3002+ let
3003+ class =
3004+ ".grid-pos-"
3005+ ++ String.fromInt position.row
3006+ ++ "-"
3007+ ++ String.fromInt position.col
3008+ ++ "-"
3009+ ++ String.fromInt position.width
3010+ ++ "-"
3011+ ++ String.fromInt position.height
3012+
3013+ msPosition =
3014+ String.join " "
3015+ [ "-ms-grid-row: "
3016+ ++ String.fromInt position.row
3017+ ++ ";"
3018+ , "-ms-grid-row-span: "
3019+ ++ String.fromInt position.height
3020+ ++ ";"
3021+ , "-ms-grid-column: "
3022+ ++ String.fromInt position.col
3023+ ++ ";"
3024+ , "-ms-grid-column-span: "
3025+ ++ String.fromInt position.width
3026+ ++ ";"
3027+ ]
3028+
3029+ base =
3030+ class ++ "{" ++ msPosition ++ "}"
3031+
3032+ modernPosition =
3033+ String.join " "
3034+ [ "grid-row: "
3035+ ++ String.fromInt position.row
3036+ ++ " / "
3037+ ++ String.fromInt (position.row + position.height)
3038+ ++ ";"
3039+ , "grid-column: "
3040+ ++ String.fromInt position.col
3041+ ++ " / "
3042+ ++ String.fromInt (position.col + position.width)
3043+ ++ ";"
3044+ ]
3045+
3046+ modernGrid =
3047+ class ++ "{" ++ modernPosition ++ "}"
3048+
3049+ supports =
3050+ "@supports (display:grid) {" ++ modernGrid ++ "}"
3054− in
3051+ in
3055− case List.foldl combine { topLevel = [], rules = "" } stylesheet of
3052+ [ base
3056− { topLevel, rules } ->
3053+ , supports
3057− renderTopLevelValues topLevel ++ rules
3054+ ]
3055+
3056+ PseudoSelector class styles ->
3057+ let
3058+ renderPseudoRule style =
3059+ renderStyleRule options style (Just class)
3060+ in
3061+ List.concatMap renderPseudoRule styles
3062+
3063+ Transform transform ->
3064+ let
3065+ val =
3066+ transformValue transform
3067+
3068+ class =
3069+ transformClass transform
3070+ in
3071+ case ( class, val ) of
3072+ ( Just cls, Just v ) ->
3073+ renderStyle options
3074+ maybePseudo
3075+ ("." ++ cls)
3076+ [ Property "transform"
3077+ v
3078+ ]
3079+
3080+ _ ->
3081+ []
3058
3082
3059
3083
3060lengthClassName : Length -> String
3084lengthClassName : Length -> String
@@ -3175,6 +3199,17 @@ paddingName top right bottom left =
3175 ++ String.fromInt left
3199 ++ String.fromInt left
3176
3200
3177
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+
3178getStyleName : Style -> String
3213getStyleName : Style -> String
3179getStyleName style =
3214getStyleName style =
3180 case style of
3215 case style of
src/Internal/Style.elm+98−18
@@ -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)
@@ -148,6 +149,7 @@ classes =
148 , widthFillPortion = "wfp"
149 , widthFillPortion = "wfp"
149 , heightFill = "hf"
150 , heightFill = "hf"
150 , heightContent = "hc"
151 , heightContent = "hc"
152+ , heightExact = "he"
151 , heightFillPortion = "hfp"
153 , heightFillPortion = "hfp"
152 , seButton = "sbt"
154 , seButton = "sbt"
153
155
@@ -253,6 +255,12 @@ classes =
253 , inputText = "it"
255 , inputText = "it"
254 , inputMultiline = "iml"
256 , inputMultiline = "iml"
255 , inputMultilineParent = "imlp"
257 , inputMultilineParent = "imlp"
258+ , inputMultilineFiller = "imlf"
259+ , inputMultilineWrapper = "implw"
260+ , inputLabel = "lbl"
261+
262+ -- link
263+ , link = "lnk"
256 }
264 }
257
265
258
266
@@ -554,6 +562,15 @@ renderRules (Intermediate parent) rulesToRender =
554 :: rendered.others
562 :: rendered.others
555 }
563 }
556
564
565+ AllChildren child childRules ->
566+ { rendered
567+ | others =
568+ renderRules
569+ (emptyIntermediate (parent.selector ++ " " ++ child) "")
570+ childRules
571+ :: rendered.others
572+ }
573+
557 Descriptor descriptor descriptorRules ->
574 Descriptor descriptor descriptorRules ->
558 { rendered
575 { rendered
559 | others =
576 | others =
@@ -876,6 +893,9 @@ elDescription =
876 -- Prop "align-self" "stretch !important"
893 -- Prop "align-self" "stretch !important"
877 Prop "width" "100%"
894 Prop "width" "100%"
878 ]
895 ]
896+ , Child (dot classes.widthFillPortion)
897+ [ Prop "width" "100%"
898+ ]
879 , Child (dot classes.widthContent)
899 , Child (dot classes.widthContent)
880 [ Prop "align-self" "flex-start"
900 [ Prop "align-self" "flex-start"
881 ]
901 ]
@@ -941,6 +961,18 @@ baseSheet =
941 ]
961 ]
942 , Class (dot classes.any ++ dot classes.single ++ dot classes.imageContainer)
962 , Class (dot classes.any ++ dot classes.single ++ dot classes.imageContainer)
943 [ 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+ ]
944 ]
976 ]
945 , Class (dot classes.any ++ ":focus")
977 , Class (dot classes.any ++ ":focus")
946 [ Prop "outline" "none"
978 [ Prop "outline" "none"
@@ -963,6 +995,7 @@ baseSheet =
963 , Child (dot classes.inFront)
995 , Child (dot classes.inFront)
964 [ Descriptor (dot classes.nearby)
996 [ Descriptor (dot classes.nearby)
965 [ Prop "position" "fixed"
997 [ Prop "position" "fixed"
998+ , Prop "z-index" "20"
966 ]
999 ]
967 ]
1000 ]
968 ]
1001 ]
@@ -1211,6 +1244,7 @@ baseSheet =
1211 -- 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
1212 [ Prop "line-height" "1.05"
1245 [ Prop "line-height" "1.05"
1213 , Prop "background" "transparent"
1246 , Prop "background" "transparent"
1247+ , Prop "text-align" "inherit"
1214 ]
1248 ]
1215 , Descriptor (dot classes.single)
1249 , Descriptor (dot classes.single)
1216 elDescription
1250 elDescription
@@ -1222,15 +1256,16 @@ baseSheet =
1222 , Descriptor (dot classes.widthExact)
1256 , Descriptor (dot classes.widthExact)
1223 [ Prop "flex-basis" "auto"
1257 [ Prop "flex-basis" "auto"
1224 ]
1258 ]
1259+ , Descriptor (dot classes.link)
1260+ [ Prop "flex-basis" "auto"
1261+ ]
1225 ]
1262 ]
1226 , Child (dot classes.heightFill)
1263 , Child (dot classes.heightFill)
1227− [ --Prop "height" "100%"
1228− -- alignTop, centerY, and alignBottom need to be disabled
1264+ [ -- alignTop, centerY, and alignBottom need to be disabled
1229 Prop "align-self" "stretch !important"
1265 Prop "align-self" "stretch !important"
1230 ]
1266 ]
1231 , Child (dot classes.heightFillPortion)
1267 , Child (dot classes.heightFillPortion)
1232− [ --Prop "height" "100%"
1233− -- alignTop, centerY, and alignBottom need to be disabled
1268+ [ -- alignTop, centerY, and alignBottom need to be disabled
1234 Prop "align-self" "stretch !important"
1269 Prop "align-self" "stretch !important"
1235 ]
1270 ]
1236
1271
@@ -1336,10 +1371,29 @@ baseSheet =
1336 , Descriptor (dot classes.spaceEvenly)
1371 , Descriptor (dot classes.spaceEvenly)
1337 [ Prop "justify-content" "space-between"
1372 [ Prop "justify-content" "space-between"
1338 ]
1373 ]
1374+ , Descriptor (dot classes.inputLabel)
1375+ [ Prop "align-items" "baseline"
1376+ ]
1339 ]
1377 ]
1340 , Descriptor (dot classes.column)
1378 , Descriptor (dot classes.column)
1341 [ Prop "display" "flex"
1379 [ Prop "display" "flex"
1342 , Prop "flex-direction" "column"
1380 , Prop "flex-direction" "column"
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.
1391+ [ Prop "flex-basis" "0px"
1392+ , Prop "min-height" "min-content"
1393+ , Descriptor (dot classes.heightExact)
1394+ [ Prop "flex-basis" "auto"
1395+ ]
1396+ ]
1343 , Child (dot classes.heightFill)
1397 , Child (dot classes.heightFill)
1344 [ Prop "flex-grow" "100000"
1398 [ Prop "flex-grow" "100000"
1345 ]
1399 ]
@@ -1354,7 +1408,7 @@ baseSheet =
1354 Prop "width" "100%"
1408 Prop "width" "100%"
1355 ]
1409 ]
1356
1410
1357− -- TODO:: THIs might be necessary, maybe it should move to widthFill?
1411+ -- TODO:: This might be necessary, maybe it should move to widthFill?
1358 -- , Child (dot classes.widthFill)
1412 -- , Child (dot classes.widthFill)
1359 -- [ Prop "align-self" "stretch"
1413 -- [ Prop "align-self" "stretch"
1360 -- , Descriptor (dot classes.alignedHorizontally)
1414 -- , Descriptor (dot classes.alignedHorizontally)
@@ -1542,35 +1596,67 @@ baseSheet =
1542 )
1596 )
1543 ]
1597 ]
1544 , Descriptor (dot classes.inputMultiline)
1598 , Descriptor (dot classes.inputMultiline)
1545− [ Prop "white-space" "pre-wrap"
1599+ [ Prop "white-space" "pre-wrap !important"
1546 , Prop "height" "100%"
1600 , Prop "height" "100%"
1547 , Prop "width" "100%"
1601 , Prop "width" "100%"
1548 , Prop "background-color" "transparent"
1602 , Prop "background-color" "transparent"
1549 ]
1603 ]
1604+ , Descriptor (dot classes.inputMultilineWrapper)
1605+ -- Get this.
1606+ -- This allows multiline input to anchor scrolling to the bottom of the node
1607+ -- when in a scrolling viewport, and the user is adding content.
1608+ -- however, it only works in chrome. In firefox, it prevents scrolling.
1609+ --
1610+ -- But how crazy is this solution?
1611+ -- [ Prop "display" "flex"
1612+ -- , Prop "flex-direction" "column-reverse"
1613+ -- ]
1614+ [ -- to increase specificity to beat another rule
1615+ Descriptor (dot classes.single)
1616+ [ Prop "flex-basis" "auto" ]
1617+ ]
1550 , Descriptor (dot classes.inputMultilineParent)
1618 , Descriptor (dot classes.inputMultilineParent)
1551− [ Prop "white-space" "pre-wrap"
1619+ [ Prop "white-space" "pre-wrap !important"
1552 , Prop "cursor" "text"
1620 , Prop "cursor" "text"
1553− , Descriptor (dot classes.paragraph)
1621+ , Child (dot classes.inputMultilineFiller)
1554− [ Prop "white-space" "pre-wrap"
1622+ [ Prop "white-space" "pre-wrap !important"
1555 , Prop "color" "transparent"
1623 , Prop "color" "transparent"
1556 ]
1624 ]
1557 ]
1625 ]
1558 , Descriptor (dot classes.paragraph)
1626 , Descriptor (dot classes.paragraph)
1559 [ Prop "display" "block"
1627 [ Prop "display" "block"
1560 , Prop "white-space" "normal"
1628 , Prop "white-space" "normal"
1629+ , Prop "overflow-wrap" "break-word"
1561 , Descriptor (dot classes.hasBehind)
1630 , Descriptor (dot classes.hasBehind)
1562 [ Prop "z-index" "0"
1631 [ Prop "z-index" "0"
1563 , Child (dot classes.behind)
1632 , Child (dot classes.behind)
1564 [ Prop "z-index" "-1"
1633 [ Prop "z-index" "-1"
1565 ]
1634 ]
1566 ]
1635 ]
1567− , Child (dot classes.text)
1636+ , AllChildren (dot classes.text)
1568 [ Prop "display" "inline"
1637 [ Prop "display" "inline"
1569 , Prop "white-space" "normal"
1638 , Prop "white-space" "normal"
1570 ]
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+ ]
1571− , Child (dot classes.single)
1649+ , AllChildren (dot classes.single)
1572 [ Prop "display" "inline"
1650 [ Prop "display" "inline"
1573 , 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+ ]
1574 , Descriptor (dot classes.inFront)
1660 , Descriptor (dot classes.inFront)
1575 [ Prop "display" "flex"
1661 [ Prop "display" "flex"
1576 ]
1662 ]
@@ -1593,15 +1679,9 @@ baseSheet =
1593 [ Prop "display" "inline"
1679 [ Prop "display" "inline"
1594 , Prop "white-space" "normal"
1680 , Prop "white-space" "normal"
1595 ]
1681 ]
1596− , Child (dot classes.single)
1597− [ Child (dot classes.text)
1598− [ Prop "display" "inline"
1599− , Prop "white-space" "normal"
1600− ]
1601− ]
1602 ]
1682 ]
1603 , Child (dot classes.row)
1683 , Child (dot classes.row)
1604− [ Prop "display" "inline-flex"
1684+ [ Prop "display" "inline"
1605 ]
1685 ]
1606 , Child (dot classes.column)
1686 , Child (dot classes.column)
1607 [ 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.shnew file+31
@@ -0,0 +1,31 @@
1+# These env variables need to be set before running this script
2+# FILE="test.html"
3+# BUILD="1.1.0"
4+# NAME="development"
5+DIRECTORY='elm-ui-testing'
6+
7+# Check to see if the repo is already cloned locally
8+cd tmp
9+if [ ! -d "$DIRECTORY" ]; then
10+ git clone git@github.com:mdgriffith/elm-ui-testing.git
11+else
12+ git pull
13+fi
14+mkdir -p "$DIRECTORY/public/tests/$BUILD/$NAME/"
15+cp "$FILE" "$DIRECTORY/public/tests/$BUILD/$NAME/index.html"
16+if [ -z "$(git status --porcelain)" ]; then
17+ # Working directory clean
18+ echo "No changes need for $BUILD -> $NAME"
19+else
20+ cd "$DIRECTORY"
21+ # Uncommitted changes
22+ git config --global user.email "$GIT_EMAIL"
23+ git config --global user.name "$GIT_NAME"
24+ git add .
25+ git commit -m "Elm UI test for $NAME on $BUILD"
26+ git push origin master
27+
28+ echo "Files published for for $BUILD -> $NAME"
29+fi
30+
31+
tests-rendering/automation/run.jsnew file+304
@@ -0,0 +1,304 @@
1+const { Builder, By, Key, until } = require("selenium-webdriver");
2+const chrome = require("selenium-webdriver/chrome");
3+const firefox = require("selenium-webdriver/firefox");
4+const fs = require("fs");
5+const program = require("commander");
6+const path = require("path");
7+const chalk = require("chalk");
8+const childProcess = require("child_process");
9+const compile_and_embed = require("./build.js").compile_and_embed;
10+
11+program
12+ .option("-s, --sauce", "run tests on sauce labs")
13+ .option("--chrome", "run chrome tests (default)")
14+ .option("--firefox", "run firefox tests")
15+ .option("--ie", "run ie tests")
16+ .option("--edge", "run edge tests")
17+ .option("--build [value]", "Set build number for sauce labs")
18+ .option("--name [value]", "Set run name for sauce labs")
19+ .option("--verbose", "Print out all test results")
20+ .option("--headless", "run tests without showing the browser")
21+ .parse(process.argv);
22+
23+// 'Windows 10'
24+
25+const windows = {
26+ chrome: {
27+ platform: "Windows 10",
28+ browser: "chrome",
29+ browserVersion: "latest",
30+ },
31+ firefox: {
32+ platform: "Windows 10",
33+ browser: "firefox",
34+ browserVersion: "latest",
35+ },
36+ edge: {
37+ platform: "Windows 10",
38+ browser: "MicrosoftEdge",
39+ browserVersion: "latest",
40+ },
41+ ie: {
42+ platform: "Windows 10",
43+ browser: "internet explorer",
44+ browserVersion: "latest",
45+ },
46+};
47+
48+const all_windows = [
49+ windows.chrome,
50+ windows.firefox,
51+ windows.edge,
52+ // i.e. takes forever and seems to make selenium timeout :/
53+ // not really sure how to extend the timeout.
54+ // , windows.ie
55+];
56+
57+// "macOS 10.14"
58+const osx = {
59+ chrome: {
60+ platform: "macOS 10.14",
61+ browser: "chrome",
62+ browserVersion: "latest",
63+ },
64+ firefox: {
65+ platform: "macOS 10.14",
66+ browser: "firefox",
67+ browserVersion: "latest",
68+ },
69+ safari: {
70+ platform: "macOS 10.14",
71+ browser: "safari",
72+ browserVersion: "latest",
73+ },
74+};
75+
76+const all_osx = [osx.safari];
77+
78+const all_browsers = all_windows.concat(all_osx);
79+
80+function prepare_all_envs(config) {
81+ var envs = [];
82+
83+ for (i = 0; i < all_browsers.length; i++) {
84+ envs.push({
85+ browser: all_browsers[i].browser,
86+ browserVersion: all_browsers[i].browserVersion,
87+ platform: all_browsers[i].platform,
88+ build: config.build,
89+ name: config.name,
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;
104+}
105+
106+function prepare_sauce_driver(env) {
107+ const username = process.env.SAUCE_USERNAME;
108+ const accessKey = process.env.SAUCE_ACCESS_KEY;
109+
110+ driver = new Builder()
111+ .withCapabilities({
112+ browserName: env.browser,
113+ platformName: env.platform,
114+ browserVersion: env.browserVersion,
115+ /* Pass Sauce User Name and Access Key */
116+ "sauce:options": {
117+ username: username,
118+ accessKey: accessKey,
119+ build: env.build,
120+ name: env.name,
121+ },
122+ })
123+ .usingServer("https://@ondemand.saucelabs.com:443/wd/hub")
124+ .build();
125+
126+ return driver;
127+}
128+
129+async function prepare_local_driver(env) {
130+ const firefoxOptions = new firefox.Options();
131+ const chromeOptions = new chrome.Options();
132+
133+ if (program.headless) {
134+ firefoxOptions.headless();
135+ chromeOptions.headless();
136+ }
137+
138+ let driver = await new Builder()
139+ .forBrowser(env.browser)
140+ .setChromeOptions(chromeOptions)
141+ .setFirefoxOptions(firefoxOptions)
142+ .build();
143+
144+ return driver;
145+}
146+
147+async function run_test(driver, url) {
148+ var results = null;
149+ try {
150+ await driver
151+ .manage()
152+ .window()
153+ .setRect({ width: 1200, height: 800, x: 0, y: 0 });
154+ await driver.get(url);
155+ await driver.wait(until.titleIs("tests finished"), 240 * 1000);
156+ results = await driver.executeScript("return test_results");
157+ } finally {
158+ await driver.quit();
159+ }
160+ return results;
161+}
162+
163+(async () => {
164+ var dir = "tmp";
165+ if (!fs.existsSync(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.";
183+ }
184+
185+ var results = [];
186+
187+ // Publish to netlify
188+ childProcess.execSync("sh tests-rendering/automation/publish-file.sh", {
189+ env: {
190+ FILE: "test.html",
191+ BUILD: program.build,
192+ NAME: program.name,
193+ GIT_EMAIL: "automation@elm-ui.com",
194+ GIT_NAME: "Elm UI Automation",
195+ HOME: process.env.HOME,
196+ SSH_AUTH_SOCK: process.env.SSH_AUTH_SOCK,
197+ SSH_AGENT_PID: process.env.SSH_AGENT_PID,
198+ },
199+ });
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+ }
218+ } else {
219+ // Run chrome if nothing else is selected
220+ if (program.chrome || (!program.chrome && !program.firefox)) {
221+ console.log("Running locally on Chrome...");
222+ const driver = await prepare_local_driver(osx.chrome);
223+ var results = await run_test(
224+ driver,
225+ "file://" + path.resolve("./tmp/test.html")
226+ );
227+ print_results("Local Chrome", results);
228+ }
229+ if (program.firefox) {
230+ console.log("Running locally on Firefox...");
231+ const driver = await prepare_local_driver(osx.firefox);
232+ var results = await run_test(
233+ driver,
234+ "file://" + path.resolve("./tmp/test.html")
235+ );
236+ print_results("Local Firefox", results);
237+ }
238+ if (program.ie) {
239+ console.log("Running locally on IE...");
240+ const driver = await prepare_local_driver(windows.ie);
241+ var results = await run_test(
242+ driver,
243+ "file://" + path.resolve("./tmp/test.html")
244+ );
245+ print_results("Local IE", results);
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+ }
257+})();
258+
259+function renderEnvName(env) {
260+ return `${env.platform}, ${env.browser} ${env.browserVersion}`;
261+}
262+
263+function print_results(label, tests) {
264+ var total_passed = 0;
265+ var total_failed = 0;
266+ var i;
267+ if (program.verbose) {
268+ console.log(label);
269+ }
270+ for (i = 0; i < tests.length; i++) {
271+ var passed = 0;
272+ var failed = 0;
273+ for (j = 0; j < tests[i].results.length; j++) {
274+ if (tests[i].results[j].passing) {
275+ passed = passed + 1;
276+ } else {
277+ if (failed == 0) {
278+ if (!program.verbose) {
279+ console.log(label);
280+ }
281+ console.log(tests[i].label);
282+ }
283+ failed = failed + 1;
284+ console.log(" " + chalk.red("fail") + " ->");
285+ console.log(" " + tests[i].results[j].description);
286+ }
287+ }
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) {
296+ console.log(tests[i].label + chalk.green(` ${passed} tests passed`));
297+ }
298+ }
299+ if (total_failed == 0) {
300+ console.log(
301+ `${label} -> ` + chalk.green(`All ${total_passed} tests passed`)
302+ );
303+ }
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.htmlnew file+185
@@ -0,0 +1,185 @@
1+<!DOCTYPE html>
2+<html>
3+ <head>
4+ <meta charset="UTF-8" />
5+ <title>Elm UI Rendering Test</title>
6+ <script>
7+ ~~_compiled_elm_code_~~
8+ </script>
9+ <script>
10+ // Helpers
11+ // Calculations for canvas
12+ var PIXEL_RATIO = (function () {
13+ var ctx = document.createElement("canvas").getContext("2d"),
14+ dpr = window.devicePixelRatio || 1,
15+ bsr =
16+ ctx.webkitBackingStorePixelRatio ||
17+ ctx.mozBackingStorePixelRatio ||
18+ ctx.msBackingStorePixelRatio ||
19+ ctx.oBackingStorePixelRatio ||
20+ ctx.backingStorePixelRatio ||
21+ 1;
22+
23+ return dpr / bsr;
24+ })();
25+
26+ function createHiDPICanvas(w, h, id, ratio) {
27+ if (!ratio) {
28+ ratio = PIXEL_RATIO;
29+ }
30+ let can = document.getElementById(id);
31+ can.width = w * ratio;
32+ can.height = h * ratio;
33+ can.style.width = w + "px";
34+ can.style.height = h + "px";
35+ can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
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++;
44+ }
45+ if (
46+ current == document.elementFromPoint(bbox["left"], bbox["bottom"] - 1)
47+ ) {
48+ result++;
49+ }
50+ if (
51+ current == document.elementFromPoint(bbox["right"] - 1, bbox["top"])
52+ ) {
53+ result++;
54+ }
55+ if (
56+ current ==
57+ document.elementFromPoint(bbox["right"] - 1, bbox["bottom"] - 1)
58+ ) {
59+ result++;
60+ }
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+ }
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>
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"
tests-rendering/cases/open/Scrollbar2.elmnew file+39
@@ -0,0 +1,39 @@
1+module Scrollbar2 exposing (main, myElement, sidebar)
2+
3+import Element exposing (Element, alignRight, centerY, column, el, fill, height, padding, px, rgb255, row, spacing, text, width)
4+import Element.Background as Background
5+import Element.Border as Border
6+import Element.Font as Font
7+
8+
9+main =
10+ Element.layout [ height fill ] <|
11+ row [ width fill, height fill ]
12+ [ sidebar
13+ ]
14+
15+
16+sidebar =
17+ column [ width fill, height fill, spacing 30 ]
18+ [ myElement
19+ , column [ width fill, height fill ]
20+ [ column [ width (px 350), height fill, Element.scrollbarY, Element.clip, Border.width 1 ]
21+ [ column [ width fill ] <|
22+ el [] (text "this should scroll indepedendently")
23+ :: List.map (always myElement) (List.range 0 100)
24+ ]
25+ , el [] (text "this should be at the bottom of the screen")
26+ ]
27+ ]
28+
29+
30+myElement : Element msg
31+myElement =
32+ el
33+ [ Background.color (rgb255 240 0 245)
34+ , Font.color (rgb255 255 255 255)
35+ , Border.rounded 3
36+ , padding 30
37+ , width fill
38+ ]
39+ (text "stylish!")
tests-rendering/cases/open/Scrollbars.elmnew file+50
@@ -0,0 +1,50 @@
1+module Scrollbars exposing (main)
2+
3+import Element exposing (..)
4+import Element.Background as Background
5+import Html.Attributes
6+import Lorem as Lorem
7+
8+
9+main =
10+ layout [ height fill ] <|
11+ column
12+ [ width fill
13+ , Background.color (rgba 0.8 0 0 0.5)
14+ , height fill
15+ ]
16+ [ row
17+ [ padding 15
18+ , width fill
19+ , Background.color (rgba 0.8 0.2 0 0.5)
20+ ]
21+ [ text "this row should always be visible" ]
22+ , row
23+ [ width fill
24+
25+ --, height (px 700)
26+ -- Don't know why it doesn't work with `height fill`
27+ , height fill
28+ , clip
29+
30+ --, htmlAttribute (Html.Attributes.style "flex-shrink" "1")
31+ , Element.explain Debug.todo
32+ ]
33+ [ column
34+ [ spacing 15
35+ , scrollbarY
36+ , height fill
37+ , width fill
38+ , padding 10
39+ ]
40+ (List.map (\p -> paragraph [] [ text p ]) <| Lorem.paragraphs 10)
41+ , column
42+ [ spacing 15
43+ , scrollbarY
44+ , width fill
45+ , height fill
46+ , padding 10
47+ ]
48+ (List.map (\p -> paragraph [] [ text p ]) <| Lorem.paragraphs 10)
49+ ]
50+ ]
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" ]
tests-rendering/cases/resolved/ColumnGrowth.elmnew file+40
@@ -0,0 +1,40 @@
1+module Main exposing (main)
2+
3+import Browser
4+import Element exposing (column, fill, fillPortion, height, minimum, paddingXY, px, rgb, row, shrink, spacing, width)
5+import Element.Background as Back
6+import Element.Border as Border
7+import Html exposing (Html)
8+
9+
10+main : Html m
11+main =
12+ let
13+ box =
14+ row
15+ [ Border.solid
16+ , Border.width 2
17+ , Back.color (rgb 1 1 1)
18+ , width (px 80)
19+ , height (fillPortion 1)
20+ ]
21+ []
22+
23+ grower =
24+ column
25+ [ width fill
26+
27+ -- fillPortion is not being applied here unless we set it to 1.
28+ -- It will also resume working if we delete the minimum bound.
29+ , height
30+ (fillPortion 2
31+ |> minimum 20
32+ )
33+ ]
34+ []
35+ in
36+ Element.layout
37+ [ Back.color (rgb 0.9 1 0.8)
38+ , paddingXY 20 20
39+ ]
40+ (column [ height fill ] [ box, grower, box ])
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+ ]
tests-rendering/cases/resolved/FocusStyles.elmnew file+34
@@ -0,0 +1,34 @@
1+module Main exposing (main)
2+
3+import Element
4+import Element.Background
5+import Element.Input
6+
7+
8+main =
9+ Element.layout []
10+ (Element.column [ Element.width (Element.px 200), Element.spacing 10 ]
11+ [ Element.Input.text [ Element.focused [] ]
12+ { label = Element.Input.labelHidden ""
13+ , onChange = \_ -> ()
14+ , placeholder = Nothing
15+ , text = ""
16+ }
17+ , Element.Input.multiline
18+ [ Element.focused [ Element.Background.color (Element.rgb 0 0 0) ] ]
19+ { label = Element.Input.labelHidden ""
20+ , onChange = \_ -> ()
21+ , placeholder = Nothing
22+ , spellcheck = False
23+ , text = ""
24+ }
25+ , Element.Input.button [ Element.focused [] ]
26+ { label = Element.text "Button"
27+ , onPress = Nothing
28+ }
29+ , Element.Input.button []
30+ { label = Element.text "Button"
31+ , onPress = Nothing
32+ }
33+ ]
34+ )
tests-rendering/cases/resolved/HugePlaceholder.elmnew file+25
@@ -0,0 +1,25 @@
1+module Main exposing (main)
2+
3+import Browser
4+import Element exposing (Element)
5+import Element.Background as Background
6+import Element.Border as Border
7+import Element.Input as Input
8+import Html exposing (Html, button, div, text)
9+import Html.Events exposing (onClick)
10+
11+
12+main =
13+ Element.layout [] sscce
14+
15+
16+sscce =
17+ Input.text
18+ [ Border.width 1
19+ , Element.width <| Element.px 50
20+ ]
21+ { onChange = always ()
22+ , text = ""
23+ , placeholder = Just <| Input.placeholder [] <| Element.text "this is a very long place holder ......................"
24+ , label = Input.labelHidden ""
25+ }
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+ }
tests-rendering/cases/resolved/MismatchTextInputText.elmnew file+82
@@ -0,0 +1,82 @@
1+module Main exposing (main)
2+
3+import Browser
4+import Element exposing (text)
5+import Element.Border as Border
6+import Element.Input as Input
7+import Html exposing (Html, button, div)
8+import Html.Events exposing (onClick)
9+
10+
11+black =
12+ Element.rgb 0 0 0
13+
14+
15+styles =
16+ [ Border.color black
17+ , Border.width 1
18+ , Element.padding 5
19+ , Border.rounded 0
20+ ]
21+
22+
23+view : () -> Html ()
24+view () =
25+ -- Element.layout []
26+ -- (Input.text [ Border.rounded 0 ]
27+ -- { onChange = always ()
28+ -- , text = "yes"
29+ -- , placeholder = Nothing
30+ -- , label = Input.labelAbove [] <| text ""
31+ -- }
32+ -- )
33+ Element.layout [] <|
34+ Element.row [ Element.padding 10 ]
35+ [ Element.el
36+ styles
37+ <|
38+ Element.text "Hello yes"
39+ , Input.text
40+ styles
41+ { onChange = always ()
42+ , text = "Hello yes"
43+ , placeholder = Nothing
44+ , label = Input.labelHidden "Hello"
45+ }
46+ , Element.el
47+ [ Element.padding 0
48+ , Border.color black
49+ , Border.width 1
50+ ]
51+ <|
52+ Element.text "Hello yes"
53+ , Input.search
54+ [ Element.padding 0
55+ , Border.color black
56+ , Border.width 1
57+ ]
58+ { onChange = always ()
59+ , text = "Hello yes"
60+ , placeholder = Nothing
61+ , label = Input.labelHidden "Hello"
62+ }
63+ , Input.text
64+ [ Element.padding 0
65+ , Border.color black
66+ , Border.width 1
67+ ]
68+ { onChange = always ()
69+ , text = "Hello yes"
70+ , placeholder = Nothing
71+ , label = Input.labelHidden "Hello"
72+ }
73+ ]
74+
75+
76+main : Program () () ()
77+main =
78+ Browser.sandbox
79+ { init = ()
80+ , view = view
81+ , update = \_ _ -> ()
82+ }
tests-rendering/cases/resolved/MultilineCollapsed.elmnew file+61
@@ -0,0 +1,61 @@
1+module Main 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.Input as Input
8+import Html exposing (Html)
9+import Html.Events exposing (onClick)
10+
11+
12+type alias Model =
13+ String
14+
15+
16+type Msg
17+ = Type String
18+
19+
20+update : Msg -> Model -> Model
21+update msg model =
22+ case msg of
23+ Type s ->
24+ s
25+
26+
27+view : Model -> Html Msg
28+view model =
29+ Element.layout [] <|
30+ column [ width fill, height fill ]
31+ [ Input.multiline
32+ [ height (maximum 200 shrink)
33+ ]
34+ { onChange = Type
35+ , text = model
36+ , placeholder = Nothing
37+ , label = Input.labelAbove [] <| text "only one line!"
38+ , spellcheck = False
39+ }
40+ , Input.multiline []
41+ { onChange = Type
42+ , text = model
43+ , placeholder = Nothing
44+ , label = Input.labelAbove [] <| text "expanded to multi lines."
45+ , spellcheck = False
46+ }
47+ ]
48+
49+
50+main : Program () Model Msg
51+main =
52+ Browser.sandbox
53+ { init = "yes\nno\nmaybe"
54+ , view = view
55+ , update = update
56+ }
57+
58+
59+sscce : Element msg
60+sscce =
61+ Element.text "sscce"
tests-rendering/cases/resolved/MultilineInput.elmnew file+377
@@ -0,0 +1,377 @@
1+module Main 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+import Element.Input as Input exposing (labelAbove, multiline)
9+import Html exposing (Html)
10+import Html.Events exposing (onClick)
11+
12+
13+type alias Model =
14+ { multiline : String
15+ , single : String
16+ }
17+
18+
19+initialModel : Model
20+initialModel =
21+ { multiline = testtxt
22+ , single = ""
23+ }
24+
25+
26+type Msg
27+ = MultilineChanged String
28+ | SingleChanged String
29+
30+
31+update : Msg -> Model -> Model
32+update msg model =
33+ case msg of
34+ MultilineChanged new ->
35+ { model | multiline = new }
36+
37+ SingleChanged new ->
38+ { model | single = new }
39+
40+
41+examplePlaceholder =
42+ Input.placeholder [ Font.color (rgb 0.2 0.2 0.8) ] (text "My placeholder...")
43+
44+
45+examplePlaceholderLong =
46+ Input.placeholder [ Font.color (rgb 0.2 0.2 0.8) ] (text "My placeholderMy placeholderMy placeholderMy placeholderMy placeholderMy placeholderMy placeholderMy placeholder...")
47+
48+
49+background =
50+ Background.color (rgb 0 1 0)
51+
52+
53+testtxt =
54+ """orem Ipsum is simply dummy text of the printing
55+and typesetting industry. Lorem Ipsum has been the industry's
56+standard dummy text ever since the 1500s, when an unknown
57+printer took a galley of type and scrambled it to make a type specimen book.
58+It has survived not only five centuries, but also the leap into electronic
59+typesetting
60+
61+, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it?"""
62+
63+
64+rowTest header els =
65+ column [ width fill, spacing 24 ]
66+ [ el [] (text header)
67+ , row
68+ [ width fill
69+ , height (px 800)
70+ , spacing 50
71+ , centerX
72+ , Border.width 1
73+ ]
74+ els
75+ ]
76+
77+
78+label str =
79+ labelAbove
80+ [ width (px 100)
81+ , Font.italic
82+ ]
83+ (text str)
84+
85+
86+view : Model -> Html Msg
87+view model =
88+ layout [] <|
89+ column []
90+ [ el [ Font.size 32 ] (text "Single Input")
91+ , viewSingle model
92+ , el [ Font.size 32 ] (text "Multiline")
93+ , viewMultiline model
94+ ]
95+
96+
97+viewMultiline model =
98+ column
99+ [ width (px 1500)
100+ , padding 100
101+ , spacing 500
102+ , centerX
103+ ]
104+ [ rowTest "Heights"
105+ [ multiline [ background ]
106+ { onChange = MultilineChanged
107+ , text = model.multiline
108+ , placeholder = Nothing
109+ , spellcheck = False
110+ , label =
111+ label "Default"
112+ }
113+ , multiline [ background, height shrink ]
114+ { onChange = MultilineChanged
115+ , text = model.multiline
116+ , placeholder = Nothing
117+ , spellcheck = False
118+ , label =
119+ label "Height shrink"
120+ }
121+ , multiline [ background, height fill ]
122+ { onChange = MultilineChanged
123+ , text = model.multiline
124+ , placeholder = Nothing
125+ , spellcheck = False
126+ , label =
127+ label "Height Fill"
128+ }
129+ , multiline [ background, height (px 200) ]
130+ { onChange = MultilineChanged
131+ , text = model.multiline
132+ , placeholder = Nothing
133+ , spellcheck = False
134+ , label =
135+ label "Height 200px"
136+ }
137+ ]
138+ , rowTest "Widths"
139+ [ multiline []
140+ { onChange = MultilineChanged
141+ , text = model.multiline
142+ , placeholder = Nothing
143+ , spellcheck = False
144+ , label =
145+ label "Default"
146+ }
147+ , multiline [ width fill ]
148+ { onChange = MultilineChanged
149+ , text = model.multiline
150+ , placeholder = Nothing
151+ , spellcheck = False
152+ , label =
153+ label "Width fill"
154+ }
155+ , multiline [ width shrink ]
156+ { onChange = MultilineChanged
157+ , text = model.multiline
158+ , placeholder = Nothing
159+ , spellcheck = False
160+ , label =
161+ label "Width Shrink"
162+ }
163+ , multiline [ width (px 300) ]
164+ { onChange = MultilineChanged
165+ , text = model.multiline
166+ , placeholder = Nothing
167+ , spellcheck = False
168+ , label = label "width 300"
169+ }
170+ ]
171+ , rowTest "Spacing"
172+ [ multiline []
173+ { onChange = MultilineChanged
174+ , text = model.multiline
175+ , placeholder = Nothing
176+ , spellcheck = False
177+ , label =
178+ label "Default"
179+ }
180+ , multiline [ spacing 30, padding 60 ]
181+ { onChange = MultilineChanged
182+ , text = model.multiline
183+ , placeholder = Nothing
184+ , spellcheck = False
185+ , label =
186+ label "Spacing 30"
187+ }
188+ , multiline [ spacing 0 ]
189+ { onChange = MultilineChanged
190+ , text = model.multiline
191+ , placeholder = Nothing
192+ , spellcheck = False
193+ , label = label "Spacing 0"
194+ }
195+ ]
196+ , rowTest "Scrollable viewport with different paddings "
197+ [ multiline [ height (px 200), padding 30 ]
198+ { onChange = MultilineChanged
199+ , text = model.multiline
200+ , placeholder = Nothing
201+ , spellcheck = False
202+ , label =
203+ label "Height 200, Padding 30"
204+ }
205+ , multiline [ height (px 200), padding 60 ]
206+ { onChange = MultilineChanged
207+ , text = model.multiline
208+ , placeholder = Nothing
209+ , spellcheck = False
210+ , label =
211+ label "Height 200, Padding 60"
212+ }
213+ , multiline [ height (px 200), padding 0 ]
214+ { onChange = MultilineChanged
215+ , text = model.multiline
216+ , placeholder = Nothing
217+ , spellcheck = False
218+ , label = label "Height 200, Padding 0"
219+ }
220+ ]
221+ , rowTest "Placeholders"
222+ [ multiline []
223+ { onChange = MultilineChanged
224+ , text = ""
225+ , placeholder = Just examplePlaceholder
226+ , spellcheck = False
227+ , label =
228+ label "Default"
229+ }
230+ , multiline [ height shrink ]
231+ { onChange = MultilineChanged
232+ , text = ""
233+ , placeholder = Just examplePlaceholder
234+ , spellcheck = False
235+ , label =
236+ label "Height shrink"
237+ }
238+ , multiline [ height fill ]
239+ { onChange = MultilineChanged
240+ , text = ""
241+ , placeholder = Just examplePlaceholder
242+ , spellcheck = False
243+ , label =
244+ label "Height Fill"
245+ }
246+ , multiline [ height (px 200) ]
247+ { onChange = MultilineChanged
248+ , text = ""
249+ , placeholder = Just examplePlaceholder
250+ , spellcheck = False
251+ , label =
252+ label "Height 200px"
253+ }
254+ ]
255+ , rowTest "Long Placeholders"
256+ [ multiline []
257+ { onChange = MultilineChanged
258+ , text = ""
259+ , placeholder = Just examplePlaceholderLong
260+ , spellcheck = False
261+ , label =
262+ label "Default"
263+ }
264+ , multiline [ height shrink ]
265+ { onChange = MultilineChanged
266+ , text = ""
267+ , placeholder = Just examplePlaceholderLong
268+ , spellcheck = False
269+ , label =
270+ label "Height shrink"
271+ }
272+ , multiline [ height fill ]
273+ { onChange = MultilineChanged
274+ , text = ""
275+ , placeholder = Just examplePlaceholderLong
276+ , spellcheck = False
277+ , label =
278+ label "Height Fill"
279+ }
280+ , multiline [ height (px 200) ]
281+ { onChange = MultilineChanged
282+ , text = ""
283+ , placeholder = Just examplePlaceholderLong
284+ , spellcheck = False
285+ , label =
286+ label "Height 200px"
287+ }
288+ ]
289+ ]
290+
291+
292+singleInput attrs labelTxt model =
293+ Input.text attrs
294+ { onChange = SingleChanged
295+ , text = model
296+ , label =
297+ labelAbove
298+ [ width (px 100)
299+ , Font.italic
300+ ]
301+ (text labelTxt)
302+ , placeholder =
303+ Just
304+ (Input.placeholder [ Font.color (rgb 0.8 0.8 0.8) ]
305+ (text "Here is my single input")
306+ )
307+ }
308+
309+
310+singleInputLongPlaceholder attrs labelTxt model =
311+ Input.text attrs
312+ { onChange = SingleChanged
313+ , text = model
314+ , label =
315+ labelAbove
316+ [ width (px 100)
317+ , Font.italic
318+ ]
319+ (text labelTxt)
320+ , placeholder =
321+ Just
322+ (Input.placeholder [ Font.color (rgb 0.8 0.8 0.8) ]
323+ (text "Here is my single input with a placeholder that is super long")
324+ )
325+ }
326+
327+
328+viewSingle model =
329+ column
330+ [ width (px 1500)
331+ , padding 100
332+ , spacing 500
333+ , centerX
334+ ]
335+ [ rowTest "Heights"
336+ [ singleInput [] "Default" model.single
337+ , singleInput [ height shrink ] "Height shrink" model.single
338+ , singleInput [ height fill ] "Height Fill" model.single
339+ , singleInput [ height (px 200) ] "Height 200px" model.single
340+ ]
341+ , rowTest "Widths"
342+ [ singleInput [] "Default" model.single
343+ , singleInput [ width fill ] "Width fill" model.single
344+ , singleInput [ width shrink ] "Width Shrink" model.single
345+ , singleInput [ width (px 200) ] "width 200" model.single
346+ ]
347+ , rowTest "Spacing (no effect)"
348+ [ singleInput [] "Default" model.single
349+ , singleInput [ spacing 60 ] "Spacing 30" model.single
350+ , singleInput [ spacing 0 ] "Spacing 0" model.single
351+ ]
352+ , rowTest "Padding"
353+ [ singleInput [] "Default" model.single
354+ , singleInput [ padding 60 ] "Padding 30" model.single
355+ , singleInput [ padding 0 ] "Padding 0" model.single
356+ ]
357+ , rowTest "Scrollable viewport with different paddings "
358+ [ singleInput [ height (px 200), padding 30 ] "Height 200, Padding 30" model.single
359+ , singleInput [ height (px 200), padding 60 ] "Height 200, Padding 60" model.single
360+ , singleInput [ height (px 200), padding 0 ] "Height 200, Padding 0" model.single
361+ ]
362+ , rowTest "Long Placeholders"
363+ [ singleInputLongPlaceholder [] "Default" model.single
364+ , singleInputLongPlaceholder [ height shrink ] "Height shrink" model.single
365+ , singleInputLongPlaceholder [ height fill ] "Height Fill" model.single
366+ , singleInputLongPlaceholder [ height (px 200) ] "Height 200px" model.single
367+ ]
368+ ]
369+
370+
371+main : Program () Model Msg
372+main =
373+ Browser.sandbox
374+ { init = initialModel
375+ , view = view
376+ , update = update
377+ }
tests-rendering/cases/resolved/MultilineNewline.elmnew file+61
@@ -0,0 +1,61 @@
1+module Main 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+import Element.Input as Input
9+import Html exposing (Html)
10+import Html.Events
11+
12+
13+type alias Model =
14+ { content : String }
15+
16+
17+initialModel : Model
18+initialModel =
19+ { content = "" }
20+
21+
22+type Msg
23+ = ContentChanged String
24+
25+
26+update : Msg -> Model -> Model
27+update msg model =
28+ case msg of
29+ ContentChanged str ->
30+ { model | content = str }
31+
32+
33+view : Model -> Html Msg
34+view model =
35+ Element.layout [ Font.color (rgb 1 0 0) ]
36+ (viewInput model)
37+
38+
39+main : Program () Model Msg
40+main =
41+ Browser.sandbox
42+ { init = initialModel
43+ , view = view
44+ , update = update
45+ }
46+
47+
48+viewInput model =
49+ Input.multiline
50+ [ padding 50
51+ , height (shrink |> maximum 300)
52+ , width fill
53+ , centerX
54+ , centerY
55+ ]
56+ { onChange = ContentChanged
57+ , text = model.content
58+ , placeholder = Just (Input.placeholder [] (text "Write your novel, dummy!"))
59+ , label = Input.labelAbove [] (text "My Input")
60+ , spellcheck = False
61+ }
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
tests-rendering/cases/resolved/NestedColumns.elmnew file+33
@@ -0,0 +1,33 @@
1+module Main exposing (main)
2+
3+import Browser
4+import Element
5+
6+
7+
8+{- Broken in chrome -}
9+
10+
11+main =
12+ Element.layout
13+ [ Element.inFront
14+ (Element.column []
15+ [ Element.column []
16+ [ Element.text "1"
17+ , Element.text "2"
18+ , Element.text "3"
19+ ]
20+ , Element.column []
21+ [ Element.text "4"
22+ , Element.text "5"
23+ , Element.text "6"
24+ ]
25+ , Element.column []
26+ [ Element.text "7"
27+ , Element.text "8"
28+ , Element.text "9"
29+ ]
30+ ]
31+ )
32+ ]
33+ Element.none
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.

tests-rendering/cases/resolved/OverflowingInput.elmnew file+48
@@ -0,0 +1,48 @@
1+module Main exposing (main)
2+
3+import Browser
4+import Element exposing (..)
5+import Element.Input as Input
6+import Html exposing (Html)
7+
8+
9+type alias Model =
10+ {}
11+
12+
13+type Msg
14+ = OnInput String
15+
16+
17+update : Msg -> Model -> Model
18+update msg model =
19+ model
20+
21+
22+view : Model -> Html Msg
23+view model =
24+ layout [] <|
25+ row
26+ [ width (px 200)
27+ , padding 8
28+ , spacing 16
29+ ]
30+ [ Input.text
31+ [ padding 4
32+ , width fill
33+ ]
34+ { onChange = OnInput
35+ , text = "text"
36+ , placeholder = Nothing
37+ , label = Input.labelHidden "label"
38+ }
39+ ]
40+
41+
42+main : Program () Model Msg
43+main =
44+ Browser.sandbox
45+ { init = {}
46+ , view = view
47+ , update = update
48+ }
tests-rendering/cases/resolved/OverflowingInput2.elmnew file+54
@@ -0,0 +1,54 @@
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)
8+
9+
10+type alias Model =
11+ {}
12+
13+
14+type Msg
15+ = TextUpdated String
16+
17+
18+update : Msg -> Model -> Model
19+update msg model =
20+ case msg of
21+ TextUpdated _ ->
22+ model
23+
24+
25+view : Model -> Html Msg
26+view model =
27+ Element.layout
28+ []
29+ (Element.row
30+ [ Element.width (Element.px 100)
31+ , Element.padding 10
32+ , Background.color (Element.rgb 1 0 0)
33+ ]
34+ [ Input.text
35+ [ Element.width (Element.fillPortion 2)
36+
37+ -- Element.width Element.fill
38+ ]
39+ { onChange = TextUpdated
40+ , text = ""
41+ , placeholder = Nothing
42+ , label = Input.labelHidden ""
43+ }
44+ ]
45+ )
46+
47+
48+main : Program () Model Msg
49+main =
50+ Browser.sandbox
51+ { init = {}
52+ , view = view
53+ , update = update
54+ }
tests-rendering/cases/resolved/OverflowingInput3.elmnew file+95
@@ -0,0 +1,95 @@
1+module Main 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+import Element.Input as Input
9+import Html exposing (Html)
10+
11+
12+type alias Model =
13+ { count : Int }
14+
15+
16+initialModel : Model
17+initialModel =
18+ { count = 0 }
19+
20+
21+type Msg
22+ = Increment
23+ | Decrement
24+
25+
26+update : Msg -> Model -> Model
27+update msg model =
28+ case msg of
29+ Increment ->
30+ { model | count = model.count + 1 }
31+
32+ Decrement ->
33+ { model | count = model.count - 1 }
34+
35+
36+blue =
37+ rgb 0 0 1
38+
39+
40+red =
41+ rgb 1 0 0
42+
43+
44+view : Model -> Html Msg
45+view model =
46+ layout [ width fill, padding 5 ] <|
47+ column [ width fill, spacing 10 ]
48+ [ row [ width fill, height <| px 100 ]
49+ [ link [ height fill, width <| fillPortion 1, Background.color blue ]
50+ { url = "https://github.com/mdgriffith/elm-ui/issues"
51+ , label = paragraph [ Font.center ] [ text "Hello!" ]
52+ }
53+ , el [ height fill, width <| fillPortion 2, Background.color red ] none
54+ ]
55+ , row [ width fill, height <| px 100 ]
56+ [ el [ height fill, width <| fillPortion 1, Background.color blue ] (text "Hello!")
57+ , el [ height fill, width <| fillPortion 2, Background.color red ] none
58+ ]
59+ ]
60+
61+
62+button : String -> Element Msg
63+button label =
64+ el
65+ [ Border.rounded 2
66+ , Border.width 1
67+ , Border.color black
68+ , Background.color white
69+ , Font.color black
70+ , padding 5
71+ , mouseOver
72+ [ Font.color white
73+ , Background.color black
74+ ]
75+ ]
76+ (text label)
77+
78+
79+black : Color
80+black =
81+ rgb 0 0 0
82+
83+
84+white : Color
85+white =
86+ rgb 255 255 255
87+
88+
89+main : Program () Model Msg
90+main =
91+ Browser.sandbox
92+ { init = initialModel
93+ , view = view
94+ , update = update
95+ }
cases/resolved/ReversedTableOrder.elm → tests-rendering/cases/resolved/ReversedTableOrder.elmrenamed

The content is the same.

cases/open/TextAreaMax.elm → tests-rendering/cases/resolved/TextAreaMax.elmrenamed+23−3
@@ -22,6 +22,10 @@ singleLine =
22 "Hello World! ->"
22 "Hello World! ->"
23
23
24
24
25+placeholder =
26+ "placeholder"
27+
28+
25main =
29main =
26 Element.layout [ width fill, height fill, Font.color (rgb 1 0 0) ] <|
30 Element.layout [ width fill, height fill, Font.color (rgb 1 0 0) ] <|
27 column [ width fill, height fill ]
31 column [ width fill, height fill ]
@@ -47,14 +51,30 @@ main =
47 none
51 none
48 , Input.text
52 , Input.text
49 [ centerX
53 [ centerX
54+ , spacing 5
50
55
51− -- , spacing 20
52 -- , height (px 100)
56 -- , height (px 100)
53− , width shrink
57+ -- , width shrink
58+ -- , height shrink
59+ , width (px 200)
54 ]
60 ]
55 { onChange = always ()
61 { onChange = always ()
56 , text = empty
62 , text = empty
57− , placeholder = Just (Input.placeholder [] (text "hello"))
63+ , placeholder = Just (Input.placeholder [] (text placeholder))
58 , label = Input.labelAbove [] (text "labeled")
64 , label = Input.labelAbove [] (text "labeled")
59 }
65 }
66+ , Input.text
67+ [ centerX
68+ , spacing 5
69+
70+ -- , height (px 100)
71+ -- , width shrink
72+ -- , height shrink
73+ , width (px 200)
74+ ]
75+ { onChange = always ()
76+ , text = "Filled Content"
77+ , placeholder = Just (Input.placeholder [] (text placeholder))
78+ , label = Input.labelBelow [] (text "labeled")
79+ }
60 ]
80 ]
tests-rendering/cases/resolved/WeirdWrapping.elmnew file+32
@@ -0,0 +1,32 @@
1+module Main exposing (main)
2+
3+import Element exposing (..)
4+import Element.Background as Background
5+import Element.Border as Border exposing (widthEach)
6+import Element.Font as Font
7+
8+
9+main =
10+ Element.layout
11+ [ height fill
12+ , width fill
13+ , padding 5
14+ ]
15+ <|
16+ row
17+ [ width fill ]
18+ [ newTabLink
19+ [ widthEach
20+ { bottom = 0
21+ , left = 2
22+ , right = 0
23+ , top = 0
24+ }
25+ , paddingXY 3 0
26+ ]
27+ { url = "url.example.com"
28+ , label =
29+ [ text "My news site super cool header" ]
30+ |> paragraph []
31+ }
32+ ]
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.jsonnew file+25
@@ -0,0 +1,25 @@
1+{
2+ "type": "application",
3+ "source-directories": ["src", "../src", "cases/open", "cases/resolved"],
4+ "elm-version": "0.19.1",
5+ "dependencies": {
6+ "direct": {
7+ "elm/browser": "1.0.2",
8+ "elm/core": "1.0.0",
9+ "elm/html": "1.0.0",
10+ "elm/json": "1.0.0",
11+ "elm/parser": "1.1.0",
12+ "elm/random": "1.0.0",
13+ "elm/time": "1.0.0",
14+ "elm/virtual-dom": "1.0.2",
15+ "elm-explorations/test": "1.1.0"
16+ },
17+ "indirect": {
18+ "elm/url": "1.0.0"
19+ }
20+ },
21+ "test-dependencies": {
22+ "direct": {},
23+ "indirect": {}
24+ }
25+}
tests/src/Generator.elm → tests-rendering/src/Generator.elmrenamed

The content is the same.

tests/src/Testable.elm → tests-rendering/src/Testable.elmrenamed+337−131
@@ -1,21 +1,48 @@
1−module Testable exposing (Attr(..), BoundingBox, Element(..), Found, LayoutContext(..), Location(..), Style, Surroundings, addAttribute, applyLabels, compareFormattedColor, createAttributeTest, createTest, formatColor, formatColorWithAlpha, getElementId, getIds, getSpacing, idAttr, levelToString, render, renderAttribute, renderElement, runTests, toTest)
1+module Testable exposing
2+ ( Attr(..)
3+ , AttributeId(..)
4+ , BoundingBox
5+ , Element(..)
6+ , ElementType(..)
7+ , Found
8+ , LayoutContext(..)
9+ , LayoutExpectation(..)
10+ , LayoutTest
11+ , Location(..)
12+ , Style
13+ , Surroundings
14+ , TextMetrics
15+ , compareFormattedColor
16+ , elementTypeToString
17+ , equal
18+ , formatColor
19+ , formatColorWithAlpha
20+ , getIds
21+ , getSpacing
22+ , getSpacingFromAttributes
23+ , lessThanOrEqual
24+ , rounded
25+ , runTests
26+ , textHeight
27+ , toElement
28+ , toHtml
29+ , todo
30+ , true
31+ )
2
32
3{-| -}
33{-| -}
4
34
5import Dict exposing (Dict)
35import Dict exposing (Dict)
6import Element exposing (Color)
36import Element exposing (Color)
7−import Expect
8import Html exposing (Html)
37import Html exposing (Html)
9import Html.Attributes
38import Html.Attributes
10import Internal.Model as Internal
39import Internal.Model as Internal
11−import Random
12import Test exposing (Test)
40import Test exposing (Test)
13−import Test.Runner
14−import Test.Runner.Failure
15
41
16
42
17type Element msg
43type Element msg
18 = El (List (Attr msg)) (Element msg)
44 = El (List (Attr msg)) (Element msg)
45+ | Link String (List (Attr msg)) (Element msg)
19 | Row (List (Attr msg)) (List (Element msg))
46 | Row (List (Attr msg)) (List (Element msg))
20 | Column (List (Attr msg)) (List (Element msg))
47 | Column (List (Attr msg)) (List (Element msg))
21 | TextColumn (List (Attr msg)) (List (Element msg))
48 | TextColumn (List (Attr msg)) (List (Element msg))
@@ -24,22 +51,104 @@ type Element msg
24 | Empty
51 | Empty
25
52
26
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+
27type Attr msg
132type Attr msg
28 = Attr (Element.Attribute msg)
133 = Attr (Element.Attribute msg)
29− | AttrTest (Surroundings -> Test)
134+ | AttrTest
135+ { test : Surroundings msg -> List LayoutExpectation
136+ , label : String
137+ , id : AttributeId
138+ }
30 | Batch (List (Attr msg))
139 | Batch (List (Attr msg))
31 | Spacing Int
140 | Spacing Int
32 | Nearby
141 | Nearby
33 { location : Location
142 { location : Location
34 , element : Element msg
143 , element : Element msg
35− , test : Surroundings -> () -> Expect.Expectation
144+ , test : Surroundings msg -> List LayoutExpectation
36 , label : String
145 , label : String
37 }
146 }
38− | Label String
39 | LabeledTest
147 | LabeledTest
40− { test : Surroundings -> () -> Expect.Expectation
148+ { test : Surroundings msg -> List LayoutExpectation
41 , label : String
149 , label : String
42 , attr : Element.Attribute msg
150 , attr : Element.Attribute msg
151+ , id : AttributeId
43 }
152 }
44
153
45
154
@@ -57,16 +166,20 @@ type LayoutContext
57 | InRow
166 | InRow
58 | InEl
167 | InEl
59 | InColumn
168 | InColumn
169+ | InParagraph
170+ | InTextCol
171+ | AtRoot
60
172
61
173
62−type alias Surroundings =
174+type alias Surroundings msg =
63 { siblings : List Found
175 { siblings : List Found
64 , parent : Found
176 , parent : Found
65 , children : List Found
177 , children : List Found
66 , self : Found
178 , self : Found
179+ , selfElement : Element msg
67
180
68 -- These values are needed to perform some types of tests.
181 -- These values are needed to perform some types of tests.
69− , location : LayoutContext
182+ , parentLayout : LayoutContext
70 , parentSpacing : Int
183 , parentSpacing : Int
71 }
184 }
72
185
@@ -75,9 +188,36 @@ type alias Found =
75 { bbox : BoundingBox
188 { bbox : BoundingBox
76 , style : Style
189 , style : Style
77 , isVisible : Bool
190 , isVisible : Bool
191+ , textMetrics : List TextMetrics
78 }
192 }
79
193
80
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+
81{-| -}
221{-| -}
82type alias Style =
222type alias Style =
83 Dict String String
223 Dict String String
@@ -99,6 +239,67 @@ type alias BoundingBox =
99 }
239 }
100
240
101
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+
102
303
103{- Retrieve Ids -}
304{- Retrieve Ids -}
104
305
@@ -134,6 +335,9 @@ getElementId level el =
134 El attrs child ->
335 El attrs child ->
135 id :: getElementId (0 :: level) child ++ attributeIDs attrs
336 id :: getElementId (0 :: level) child ++ attributeIDs attrs
136
337
338+ Link _ attrs child ->
339+ id :: getElementId (0 :: level) child ++ attributeIDs attrs
340+
137 Row attrs children ->
341 Row attrs children ->
138 id :: childrenIDs children ++ attributeIDs attrs
342 id :: childrenIDs children ++ attributeIDs attrs
139
343
@@ -157,9 +361,19 @@ getElementId level el =
157{- Render as Html -}
361{- Render as Html -}
158
362
159
363
364+toElement : Element msg -> Element.Element msg
365+toElement el =
366+ renderElement [ 0, 0 ] el
367+
368+
160−render : Element msg -> Html msg
369+toHtml : Element msg -> Html msg
161−render el =
370+toHtml el =
162− Element.layout [ idAttr "0" ] <|
371+ Element.layout
372+ [ idAttr "0"
373+ , Element.width (Element.px 1000)
374+ , Element.height (Element.px 1000)
375+ ]
376+ <|
163 renderElement [ 0, 0 ] el
377 renderElement [ 0, 0 ] el
164
378
165
379
@@ -188,6 +402,13 @@ renderElement level el =
188 (id :: makeAttributes attrs)
402 (id :: makeAttributes attrs)
189 (renderElement (0 :: level) child)
403 (renderElement (0 :: level) child)
190
404
405+ Link url attrs child ->
406+ Element.link
407+ (id :: makeAttributes attrs)
408+ { url = url
409+ , label = renderElement (0 :: level) child
410+ }
411+
191 Row attrs children ->
412 Row attrs children ->
192 Element.row
413 Element.row
193 (id :: makeAttributes attrs)
414 (id :: makeAttributes attrs)
@@ -227,9 +448,6 @@ renderAttribute level attrIndex attr =
227 Spacing _ ->
448 Spacing _ ->
228 []
449 []
229
450
230− Label _ ->
231− []
232−
233 Nearby { location, element } ->
451 Nearby { location, element } ->
234 case location of
452 case location of
235 Above ->
453 Above ->
@@ -262,30 +480,36 @@ renderAttribute level attrIndex attr =
262{- Convert to Test -}
480{- Convert to Test -}
263
481
264
482
265−toTest : String -> Dict String Found -> Element msg -> Test
483+runTests : Dict String Found -> Element msg -> List LayoutTest
266−toTest label harvested el =
484+runTests harvested el =
267 let
485 let
268 maybeFound =
486 maybeFound =
269 Dict.get "se-0" harvested
487 Dict.get "se-0" harvested
270 in
488 in
271 case maybeFound of
489 case maybeFound of
272 Nothing ->
490 Nothing ->
273− Test.describe label
491+ [ { elementDomId = "se-0"
274− [ Test.test "Find Root" <|
492+ , elementType = EmptyType
275− \_ -> 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+ }
276− ]
501+ ]
277
502
278 Just root ->
503 Just root ->
279− Test.describe label <|
280− createTest
504+ createTest
281− { siblings = []
505+ { siblings = []
282− , parent = root
506+ , parent = root
283− , cache = harvested
507+ , cache = harvested
284− , parentSpacing = 0
508+ , parentSpacing = 0
285− , level = [ 0, 0 ]
509+ , level = [ 0, 0 ]
286− , element = el
510+ , element = el
287− , location = InEl
511+ , location = AtRoot
288− }
512+ }
289
513
290
514
291levelToString : List Int -> String
515levelToString : List Int -> String
@@ -305,7 +529,7 @@ createTest :
305 , location : LayoutContext
529 , location : LayoutContext
306 , parentSpacing : Int
530 , parentSpacing : Int
307 }
531 }
308− -> List Test
532+ -> List LayoutTest
309createTest { siblings, parent, cache, level, element, location, parentSpacing } =
533createTest { siblings, parent, cache, level, element, location, parentSpacing } =
310 let
534 let
311 spacing =
535 spacing =
@@ -315,7 +539,7 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
315 id =
539 id =
316 levelToString level
540 levelToString level
317
541
318− testChildren : Found -> List (Element msg) -> List Test
542+ testChildren : Found -> List (Element msg) -> List LayoutTest
319 testChildren found children =
543 testChildren found children =
320 let
544 let
321 childrenFound =
545 childrenFound =
@@ -343,13 +567,13 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
343 { a
567 { a
344 | index : Int
568 | index : Int
345 , previous : List Found
569 , previous : List Found
346− , tests : List Test
570+ , tests : List LayoutTest
347 , upcoming : List Found
571 , upcoming : List Found
348 }
572 }
349 ->
573 ->
350 { index : Int
574 { index : Int
351 , previous : List Found
575 , previous : List Found
352− , tests : List Test
576+ , tests : List LayoutTest
353 , upcoming : List Found
577 , upcoming : List Found
354 }
578 }
355 applyChildTest found child childTest =
579 applyChildTest found child childTest =
@@ -376,6 +600,9 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
376 El _ _ ->
600 El _ _ ->
377 InEl
601 InEl
378
602
603+ Link _ _ _ ->
604+ InEl
605+
379 Row _ _ ->
606 Row _ _ ->
380 InRow
607 InRow
381
608
@@ -383,10 +610,10 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
383 InColumn
610 InColumn
384
611
385 TextColumn _ _ ->
612 TextColumn _ _ ->
386− InColumn
613+ InTextCol
387
614
388 Paragraph _ _ ->
615 Paragraph _ _ ->
389− InRow
616+ InParagraph
390
617
391 Text _ ->
618 Text _ ->
392 InEl
619 InEl
@@ -413,7 +640,7 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
413 rest
640 rest
414 }
641 }
415
642
416− tests : Found -> List (Attr msg) -> List (Element msg) -> List Test
643+ tests : Found -> List (Attr msg) -> List (Element msg) -> List LayoutTest
417 tests self attributes children =
644 tests self attributes children =
418 let
645 let
419 findBBox elem ( i, gathered ) =
646 findBBox elem ( i, gathered ) =
@@ -453,7 +680,6 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
453
680
454 attributeTests =
681 attributeTests =
455 attributes
682 attributes
456− |> applyLabels
457 |> List.indexedMap
683 |> List.indexedMap
458 -- 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
459 (\i attr ->
685 (\i attr ->
@@ -464,8 +690,9 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
464 { siblings = siblings
690 { siblings = siblings
465 , parent = parent
691 , parent = parent
466 , self = self
692 , self = self
693+ , selfElement = element
467 , children = childrenFoundData
694 , children = childrenFoundData
468− , location = location
695+ , parentLayout = location
469 , parentSpacing = parentSpacing
696 , parentSpacing = parentSpacing
470 }
697 }
471 attr
698 attr
@@ -485,13 +712,26 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
485 []
712 []
486
713
487 _ ->
714 _ ->
488− [ 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+ ]
489
726
490 Just self ->
727 Just self ->
491 case element of
728 case element of
492 El attrs child ->
729 El attrs child ->
493 tests self attrs [ child ]
730 tests self attrs [ child ]
494
731
732+ Link _ attrs child ->
733+ tests self attrs [ child ]
734+
495 Row attrs children ->
735 Row attrs children ->
496 tests self attrs children
736 tests self attrs children
497
737
@@ -511,59 +751,33 @@ createTest { siblings, parent, cache, level, element, location, parentSpacing }
511 []
751 []
512
752
513
753
514−applyLabels : List (Attr msg) -> List (Attr msg)
515−applyLabels attrs =
516− let
517− toLabel attr =
518− case attr of
519− Label label ->
520− Just label
521−
522− _ ->
523− Nothing
524−
525− newLabels =
526− attrs
527− |> List.filterMap toLabel
528− |> String.join ", "
529−
530− applyLabel newLabel attr =
531− case attr of
532− LabeledTest labeled ->
533− LabeledTest
534− { labeled
535− | label =
536− if newLabel == "" then
537− labeled.label
538−
539− else
540− newLabel ++ ", " ++ labeled.label
541− }
542−
543− x ->
544− x
545− in
546− List.map (applyLabel newLabels) attrs
547−
548−
549−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
550createAttributeTest parent cache level attrIndex surroundings attr =
762createAttributeTest parent cache level attrIndex surroundings attr =
551 let
763 let
552− indexLabel =
764+ domId =
553− levelToString (attrIndex :: level)
765+ "#" ++ levelToString level
554 in
766 in
555 case attr of
767 case attr of
556 Attr _ ->
768 Attr _ ->
557 []
769 []
558
770
559− Label _ ->
560− []
561−
562 Spacing _ ->
771 Spacing _ ->
563 []
772 []
564
773
565− AttrTest test ->
774+ AttrTest details ->
775+ [ { elementDomId = domId
776+ , elementType =
777+ toElementType surroundings.selfElement
778+ , label = details.label
566− [ test surroundings
779+ , expectations = details.test surroundings
780+ }
567 ]
781 ]
568
782
569 Nearby nearby ->
783 Nearby nearby ->
@@ -574,16 +788,29 @@ createAttributeTest parent cache level attrIndex surroundings attr =
574 , parentSpacing = 0
788 , parentSpacing = 0
575 , level = attrIndex :: -1 :: level
789 , level = attrIndex :: -1 :: level
576 , location = IsNearby nearby.location
790 , location = IsNearby nearby.location
577− , 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
578 }
801 }
579
802
580 Batch batch ->
803 Batch batch ->
581 batch
804 batch
582− |> List.indexedMap (\i attribute -> createAttributeTest parent cache (attrIndex :: level) i surroundings attribute)
805+ |> List.indexedMap (\i attribute -> createAttributeTest parent cache level i surroundings attribute)
583 |> List.concat
806 |> List.concat
584
807
585 LabeledTest { label, test } ->
808 LabeledTest { label, test } ->
809+ [ { elementDomId = domId
810+ , elementType = toElementType surroundings.selfElement
811+ , label = label
586− [ Test.test (label ++ " #" ++ indexLabel) (test surroundings)
812+ , expectations = test surroundings
813+ }
587 ]
814 ]
588
815
589
816
@@ -593,6 +820,9 @@ addAttribute attr el =
593 El attrs child ->
820 El attrs child ->
594 El (attr :: attrs) child
821 El (attr :: attrs) child
595
822
823+ Link url attrs child ->
824+ Link url (attr :: attrs) child
825+
596 Row attrs children ->
826 Row attrs children ->
597 Row (attr :: attrs) children
827 Row (attr :: attrs) children
598
828
@@ -612,48 +842,6 @@ addAttribute attr el =
612 Text str
842 Text str
613
843
614
844
615−runTests :
616− Random.Seed
617− -> Test
618− ->
619− List
620− ( String
621− , Maybe
622− { given : Maybe String
623− , description : String
624− , reason : Test.Runner.Failure.Reason
625− }
626− )
627−runTests seed tests =
628− let
629− run runner =
630− let
631− ran =
632− List.map Test.Runner.getFailureReason (runner.run ())
633− in
634− List.map2 Tuple.pair runner.labels ran
635−
636− results =
637− case Test.Runner.fromTest 100 seed tests of
638− Test.Runner.Plain rnrs ->
639− List.map run rnrs
640−
641− Test.Runner.Only rnrs ->
642− List.map run rnrs
643−
644− Test.Runner.Skipping rnrs ->
645− List.map run rnrs
646−
647− Test.Runner.Invalid invalid ->
648− let
649− _ =
650− Debug.log "Invalid tests" invalid
651− in
652− []
653− in
654− List.concat results
655−
656−
657compareFormattedColor : Color -> String -> Bool
845compareFormattedColor : Color -> String -> Bool
658compareFormattedColor color expected =
846compareFormattedColor color expected =
659 formatColor color == expected || formatColorWithAlpha color == expected
847 formatColor color == expected || formatColorWithAlpha color == expected
@@ -690,6 +878,21 @@ formatColor (Internal.Rgba red green blue alpha) =
690 ++ ")"
878 ++ ")"
691
879
692
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+
693getSpacing : Element msg -> Maybe Int
896getSpacing : Element msg -> Maybe Int
694getSpacing el =
897getSpacing el =
695 let
898 let
@@ -715,6 +918,9 @@ getSpacing el =
715 El attrs _ ->
918 El attrs _ ->
716 filterAttrs attrs
919 filterAttrs attrs
717
920
921+ Link _ attrs _ ->
922+ filterAttrs attrs
923+
718 Row attrs _ ->
924 Row attrs _ ->
719 filterAttrs attrs
925 filterAttrs attrs
720
926
tests-rendering/src/Testable/Element.elmnew file+1723
@@ -0,0 +1,1723 @@
1+module Testable.Element exposing
2+ ( Length(..)
3+ , above
4+ , alignBottom
5+ , alignLeft
6+ , alignRight
7+ , alignTop
8+ , alpha
9+ , behindContent
10+ , below
11+ , centerX
12+ , centerY
13+ , clip
14+ , column
15+ , el
16+ , expectRoundedEquality
17+ , fill
18+ , fillPortion
19+ , height
20+ , inFront
21+ , isVisible
22+ , label
23+ , layout
24+ , link
25+ , maximum
26+ , minimum
27+ , moveDown
28+ , moveLeft
29+ , moveRight
30+ , moveUp
31+ , none
32+ , onLeft
33+ , onRight
34+ , padding
35+ , paddingXY
36+ , paragraph
37+ , px
38+ , rgb
39+ , rgba
40+ , row
41+ , scrollbarX
42+ , scrollbarY
43+ , scrollbars
44+ , shrink
45+ , spacing
46+ , text
47+ , textColumn
48+ , toProgram
49+ , transparent
50+ , width
51+ )
52+
53+{-| This module should mirror the top level `Element` api, with one important distinction.
54+
55+The resulting `Element msg` structure can either be rendered to Html or transformed into a test suite that can be run.
56+
57+In order to run the test:
58+
59+ - render html
60+ - gather information from the browser
61+ - generate tests
62+
63+-}
64+
65+import Dict
66+import Element
67+import Html.Attributes
68+import 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
82+
83+
84+text : String -> Testable.Element msg
85+text =
86+ Testable.Text
87+
88+
89+el : List (Testable.Attr msg) -> Testable.Element msg -> Testable.Element msg
90+el attrs =
91+ Testable.El (implicitWidthHeightShrink attrs)
92+
93+
94+row : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
95+row attrs =
96+ Testable.Row (implicitWidthHeightShrink attrs)
97+
98+
99+column : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
100+column attrs =
101+ Testable.Column (implicitWidthHeightShrink attrs)
102+
103+
104+none : Testable.Element msg
105+none =
106+ Testable.Empty
107+
108+
109+paragraph : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
110+paragraph attrs =
111+ let
112+ withImplicits =
113+ implicitTest (widthHelper Nothing Nothing (Fill 1))
114+ :: implicitTest (heightHelper Nothing Nothing Shrink)
115+ :: attrs
116+ in
117+ Testable.Paragraph (skipOverridden withImplicits)
118+
119+
120+textColumn : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
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
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)
186+
187+
188+type Length
189+ = Px Int
190+ | Fill Int
191+ | Shrink
192+ | Minimum Int Length
193+ | Maximum Int Length
194+
195+
196+minimum =
197+ Minimum
198+
199+
200+maximum =
201+ Maximum
202+
203+
204+rgb =
205+ Element.rgb
206+
207+
208+rgba =
209+ Element.rgba
210+
211+
212+px : Int -> Length
213+px =
214+ Px
215+
216+
217+fill : Length
218+fill =
219+ Fill 1
220+
221+
222+shrink : Length
223+shrink =
224+ Shrink
225+
226+
227+fillPortion : Int -> Length
228+fillPortion =
229+ Fill
230+
231+
232+transparent : Bool -> Testable.Attr msg
233+transparent on =
234+ Testable.LabeledTest
235+ { label =
236+ if on then
237+ "transparent"
238+
239+ else
240+ "opaque"
241+ , attr = Element.transparent on
242+ , id = Testable.NoId
243+ , test =
244+ \context ->
245+ let
246+ selfTransparency =
247+ context.self.style
248+ |> Dict.get "opacity"
249+ |> Maybe.withDefault "notfound"
250+
251+ value =
252+ if on then
253+ "0"
254+
255+ else
256+ "1"
257+ in
258+ [ Testable.equal value selfTransparency ]
259+ }
260+
261+
262+{-| -}
263+isVisible : Testable.Attr msg
264+isVisible =
265+ Testable.AttrTest
266+ { label = "is-visible"
267+ , id = Testable.NoId
268+ , test =
269+ \context ->
270+ [ Testable.equal context.self.isVisible True ]
271+ }
272+
273+
274+{-| -}
275+alpha : Float -> Testable.Attr msg
276+alpha a =
277+ Testable.LabeledTest
278+ { label = "alpha-" ++ String.fromFloat a
279+ , attr = Element.alpha a
280+ , id = Testable.NoId
281+ , test =
282+ \context ->
283+ let
284+ selfTransparency =
285+ context.self.style
286+ |> Dict.get "opacity"
287+ |> Maybe.withDefault "notfound"
288+ in
289+ [ Testable.equal (String.fromFloat a) selfTransparency ]
290+ }
291+
292+
293+{-| -}
294+padding : Int -> Testable.Attr msg
295+padding pad =
296+ Testable.LabeledTest
297+ { label = "padding " ++ String.fromInt pad
298+ , attr = Element.padding pad
299+ , id = Testable.NoId
300+ , test =
301+ \found ->
302+ [ Testable.true ("Padding " ++ String.fromInt pad ++ " is present")
303+ (List.all ((==) pad)
304+ [ floor found.self.bbox.padding.left
305+ , floor found.self.bbox.padding.right
306+ , floor found.self.bbox.padding.top
307+ , floor found.self.bbox.padding.bottom
308+ ]
309+ )
310+ ]
311+ }
312+
313+
314+{-| -}
315+paddingXY : Int -> Int -> Testable.Attr msg
316+paddingXY x y =
317+ Testable.LabeledTest
318+ { label = "paddingXY " ++ String.fromInt x ++ ", " ++ String.fromInt y
319+ , attr = Element.paddingXY x y
320+ , id = Testable.NoId
321+ , test =
322+ \found ->
323+ [ Testable.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
324+ (List.all ((==) x)
325+ [ floor found.self.bbox.padding.left
326+ , floor found.self.bbox.padding.right
327+ ]
328+ && List.all ((==) y)
329+ [ floor found.self.bbox.padding.top
330+ , floor found.self.bbox.padding.bottom
331+ ]
332+ )
333+ ]
334+ }
335+
336+
337+listIf ls =
338+ List.filter Tuple.first ls
339+ |> List.map Tuple.second
340+
341+
342+widthHelper maybeMin maybeMax len =
343+ let
344+ addMin l =
345+ case maybeMin of
346+ Nothing ->
347+ l
348+
349+ Just minPx ->
350+ l |> Element.minimum minPx
351+
352+ addMax l =
353+ case maybeMax of
354+ Nothing ->
355+ l
356+
357+ Just maxPx ->
358+ l |> Element.maximum maxPx
359+
360+ minLabel =
361+ case maybeMin of
362+ Nothing ->
363+ ""
364+
365+ Just i ->
366+ " min:" ++ String.fromInt i
367+
368+ maxLabel =
369+ case maybeMax of
370+ Nothing ->
371+ ""
372+
373+ Just i ->
374+ " max:" ++ String.fromInt i
375+
376+ minMaxTest actualWidth =
377+ case ( maybeMin, maybeMax ) of
378+ ( Nothing, Nothing ) ->
379+ True
380+
381+ ( Just lower, Nothing ) ->
382+ lower <= actualWidth
383+
384+ ( Just lower, Just higher ) ->
385+ lower <= actualWidth && actualWidth <= higher
386+
387+ ( Nothing, Just higher ) ->
388+ actualWidth <= higher
389+ in
390+ case len of
391+ Minimum m newLen ->
392+ widthHelper (Just m) maybeMax newLen
393+
394+ Maximum m newLen ->
395+ widthHelper maybeMin (Just m) newLen
396+
397+ Px val ->
398+ -- Pixel values should ignore min and max?
399+ Testable.LabeledTest
400+ { label = "width " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
401+ , id = Testable.IsWidth
402+ , attr =
403+ Element.width
404+ (Element.px val
405+ |> addMin
406+ |> addMax
407+ )
408+ , test =
409+ \found ->
410+ [ expectRoundedEquality
411+ { expected = toFloat val
412+ , found = found.self.bbox.width
413+ }
414+ , Testable.true "min/max is upheld" (minMaxTest (floor found.self.bbox.width))
415+ ]
416+ }
417+
418+ Fill portion ->
419+ Testable.LabeledTest
420+ { label =
421+ if portion == 1 then
422+ "width fill" ++ minLabel ++ maxLabel
423+
424+ else
425+ "width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
426+ , id = Testable.IsWidth
427+ , attr =
428+ Element.width
429+ (Element.fillPortion portion
430+ |> addMin
431+ |> addMax
432+ )
433+ , test =
434+ \context ->
435+ if List.member context.parentLayout [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
436+ [ Testable.true "width fill doesn't apply to onright/onleft elements" True ]
437+
438+ else
439+ let
440+ parentAvailableWidth =
441+ context.parent.bbox.width - (context.self.bbox.padding.left + context.self.bbox.padding.right)
442+ in
443+ List.concat
444+ [ case context.parentLayout of
445+ Testable.IsNearby _ ->
446+ [ Testable.true "Nearby Element has fill width"
447+ ((floor context.parent.bbox.width == floor context.self.bbox.width)
448+ || minMaxTest (floor context.self.bbox.width)
449+ )
450+ ]
451+
452+ Testable.InColumn ->
453+ [ Testable.true "Element within column has fill width"
454+ ((floor parentAvailableWidth == floor context.self.bbox.width)
455+ || minMaxTest (floor context.self.bbox.width)
456+ )
457+ ]
458+
459+ Testable.InEl ->
460+ [ Testable.true "Element within element has fill width" <|
461+ (floor parentAvailableWidth == floor context.self.bbox.width)
462+ || minMaxTest (floor context.self.bbox.width)
463+ ]
464+
465+ _ ->
466+ let
467+ spacePerPortion =
468+ parentAvailableWidth / toFloat (List.length context.siblings + 1)
469+ in
470+ [ Testable.true "element has fill width" <|
471+ (floor spacePerPortion == 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+ ]
530+ }
531+
532+ Shrink ->
533+ Testable.LabeledTest
534+ { label = "width shrink" ++ minLabel ++ maxLabel
535+ , attr =
536+ Element.width
537+ (Element.shrink
538+ |> addMin
539+ |> addMax
540+ )
541+ , id = Testable.IsWidth
542+ , test =
543+ \context ->
544+ case context.selfElement of
545+ Testable.El _ _ ->
546+ let
547+ childWidth child =
548+ child.bbox.width
549+
550+ totalChildren =
551+ context.children
552+ |> List.map childWidth
553+ |> List.append (List.map .width context.self.textMetrics)
554+ |> List.sum
555+
556+ horizontalPadding =
557+ context.self.bbox.padding.left + context.self.bbox.padding.right
558+ in
559+ listIf
560+ [ ( (context.parentLayout /= Testable.InParagraph)
561+ && (context.parentLayout /= Testable.InTextCol)
562+ , Testable.rounded "equals children"
563+ { expected = totalChildren + horizontalPadding
564+ , found = context.self.bbox.width
565+ }
566+ )
567+ , ( context.parentLayout /= Testable.AtRoot
568+ , Testable.lessThanOrEqual "not larger than parent"
569+ context.self.bbox.width
570+ context.parent.bbox.width
571+ )
572+ ]
573+
574+ Testable.Link _ _ _ ->
575+ let
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
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+ []
746+ }
747+
748+
749+heightHelper maybeMin maybeMax len =
750+ let
751+ addMin l =
752+ case maybeMin of
753+ Nothing ->
754+ l
755+
756+ Just minPx ->
757+ l |> Element.minimum minPx
758+
759+ addMax l =
760+ case maybeMax of
761+ Nothing ->
762+ l
763+
764+ Just maxPx ->
765+ l |> Element.maximum maxPx
766+
767+ minLabel =
768+ case maybeMin of
769+ Nothing ->
770+ ""
771+
772+ Just i ->
773+ " min:" ++ String.fromInt i
774+
775+ maxLabel =
776+ case maybeMax of
777+ Nothing ->
778+ ""
779+
780+ Just i ->
781+ " max:" ++ String.fromInt i
782+
783+ minMaxTest actualheight =
784+ case ( maybeMin, maybeMax ) of
785+ ( Nothing, Nothing ) ->
786+ True
787+
788+ ( Just lower, Nothing ) ->
789+ lower <= actualheight
790+
791+ ( Just lower, Just higher ) ->
792+ lower <= actualheight && actualheight <= higher
793+
794+ ( Nothing, Just higher ) ->
795+ actualheight <= higher
796+ in
797+ case len of
798+ Minimum m newLen ->
799+ heightHelper (Just m) maybeMax newLen
800+
801+ Maximum m newLen ->
802+ heightHelper maybeMin (Just m) newLen
803+
804+ Px val ->
805+ -- Pixel values should ignore min and max?
806+ Testable.LabeledTest
807+ { label = "height " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
808+ , attr =
809+ Element.height
810+ (Element.px val
811+ |> addMin
812+ |> addMax
813+ )
814+ , id = Testable.IsHeight
815+ , test =
816+ \found ->
817+ [ expectRoundedEquality
818+ { expected = toFloat val
819+ , found = found.self.bbox.height
820+ }
821+ , Testable.true "min/max holds true"
822+ (minMaxTest (floor found.self.bbox.height))
823+ ]
824+ }
825+
826+ Fill portion ->
827+ Testable.LabeledTest
828+ { label = "height fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
829+ , attr =
830+ Element.height
831+ (Element.fillPortion portion
832+ |> addMin
833+ |> addMax
834+ )
835+ , id = Testable.IsHeight
836+ , test =
837+ \context ->
838+ [ if List.member context.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
839+ Testable.true "height fill doesn't apply to above/below elements" True
840+
841+ else
842+ let
843+ parentAvailableHeight =
844+ context.parent.bbox.height - (context.self.bbox.padding.top + context.self.bbox.padding.bottom)
845+ in
846+ case context.parentLayout of
847+ Testable.IsNearby _ ->
848+ Testable.true "Nearby Element has fill height"
849+ ((floor context.parent.bbox.height == floor context.self.bbox.height)
850+ || minMaxTest (floor context.self.bbox.height)
851+ )
852+
853+ Testable.InColumn ->
854+ Testable.true "Element within column has fill height"
855+ ((floor parentAvailableHeight == floor context.self.bbox.height)
856+ || minMaxTest (floor context.self.bbox.height)
857+ )
858+
859+ Testable.InEl ->
860+ Testable.true "Element within el has fill height" <|
861+ (floor parentAvailableHeight == floor context.self.bbox.height)
862+ || minMaxTest (floor context.self.bbox.height)
863+
864+ _ ->
865+ let
866+ spacePerPortion =
867+ parentAvailableHeight / toFloat (List.length context.siblings + 1)
868+ in
869+ Testable.true "el has fill height" <|
870+ (floor spacePerPortion == floor context.self.bbox.height)
871+ || minMaxTest (floor context.self.bbox.height)
872+ ]
873+ }
874+
875+ Shrink ->
876+ Testable.LabeledTest
877+ { label = "height shrink" ++ minLabel ++ maxLabel
878+ , attr =
879+ Element.height
880+ (Element.shrink
881+ |> addMin
882+ |> addMax
883+ )
884+ , id = Testable.IsHeight
885+ , test =
886+ \context ->
887+ case context.selfElement of
888+ Testable.El _ _ ->
889+ let
890+ childHeight child =
891+ child.bbox.height
892+ in
893+ if List.isEmpty context.children then
894+ -- context.self.textMetrics
895+ -- |> List.map Testable.textHeight
896+ -- |> List.sum
897+ -- TODO: apparently the font metrics we have are for the literal characters rendered
898+ -- not for the font itself.
899+ [ Testable.todo "calculate height from actual text metrics"
900+ ]
901+
902+ else
903+ [ expectRoundedEquality
904+ { expected =
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+ []
1012+ }
1013+
1014+
1015+width : Length -> Testable.Attr msg
1016+width len =
1017+ widthHelper Nothing Nothing len
1018+
1019+
1020+height : Length -> Testable.Attr msg
1021+height len =
1022+ heightHelper Nothing Nothing len
1023+
1024+
1025+spacing : Int -> Testable.Attr msg
1026+spacing space =
1027+ Testable.Batch
1028+ [ Testable.Spacing space
1029+ , Testable.LabeledTest
1030+ { label = "spacing: " ++ String.fromInt space
1031+ , attr = Element.spacing space
1032+ , id = Testable.NoId
1033+ , test =
1034+ \found ->
1035+ let
1036+ findDistance child total =
1037+ List.concatMap
1038+ (\otherChild ->
1039+ let
1040+ horizontal =
1041+ round <|
1042+ child.bbox.left
1043+ - otherChild.bbox.right
1044+
1045+ vertical =
1046+ round <|
1047+ child.bbox.top
1048+ - otherChild.bbox.bottom
1049+ in
1050+ [ if horizontal > 0 then
1051+ horizontal
1052+
1053+ else
1054+ space
1055+ , if vertical > 0 then
1056+ vertical
1057+
1058+ else
1059+ space
1060+ ]
1061+ )
1062+ found.children
1063+ ++ total
1064+
1065+ distances =
1066+ List.foldl findDistance [] found.children
1067+
1068+ allAreSpaced =
1069+ List.foldl
1070+ (\x wrong ->
1071+ if not (x >= space) then
1072+ x :: wrong
1073+
1074+ else
1075+ wrong
1076+ )
1077+ []
1078+ distances
1079+ in
1080+ [ Testable.true
1081+ ("All children are at least "
1082+ ++ String.fromInt space
1083+ ++ " pixels apart."
1084+ ++ Debug.toString allAreSpaced
1085+ ++ " are not though"
1086+ )
1087+ (allAreSpaced == [])
1088+ ]
1089+ }
1090+ ]
1091+
1092+
1093+{-| alignLeft needs to account for
1094+
1095+ - parent padding
1096+ - elements to the left
1097+ - spacing value
1098+
1099+in order to calculate the expected result.
1100+
1101+Also need parent rendering context if this is to work with wrapped rows in the future.
1102+
1103+All sibling elements
1104+
1105+-}
1106+alignLeft : Testable.Attr msg
1107+alignLeft =
1108+ Testable.LabeledTest
1109+ { label = "alignLeft"
1110+ , attr = Element.alignLeft
1111+ , id = Testable.NoId
1112+ , test =
1113+ \found ->
1114+ [ if List.member found.parentLayout [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
1115+ Testable.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
1116+
1117+ else if
1118+ List.member found.parentLayout
1119+ [ Testable.IsNearby Testable.InFront
1120+ , Testable.IsNearby Testable.Behind
1121+ , Testable.IsNearby Testable.Above
1122+ , Testable.IsNearby Testable.Below
1123+ ]
1124+ then
1125+ expectRoundedEquality
1126+ { found = found.self.bbox.left
1127+ , expected = found.parent.bbox.left
1128+ }
1129+
1130+ else if List.length found.siblings == 0 then
1131+ expectRoundedEquality
1132+ { found = found.self.bbox.left
1133+ , expected = found.parent.bbox.left + found.parent.bbox.padding.left
1134+ }
1135+
1136+ else
1137+ case found.parentLayout of
1138+ Testable.InRow ->
1139+ let
1140+ siblingsOnLeft =
1141+ List.filter (\x -> x.bbox.right < found.self.bbox.left) found.siblings
1142+
1143+ spacings =
1144+ toFloat (List.length siblingsOnLeft * found.parentSpacing)
1145+
1146+ widthsOnLeft =
1147+ siblingsOnLeft
1148+ |> List.map (.width << .bbox)
1149+ |> List.sum
1150+ in
1151+ expectRoundedEquality
1152+ { found = found.self.bbox.left
1153+ , expected = found.parent.bbox.left + (found.parent.bbox.padding.left + widthsOnLeft + spacings)
1154+ }
1155+
1156+ _ ->
1157+ expectRoundedEquality
1158+ { found = found.self.bbox.left
1159+ , expected = found.parent.bbox.left + found.parent.bbox.padding.left
1160+ }
1161+ ]
1162+ }
1163+
1164+
1165+{-| -}
1166+centerX : Testable.Attr msg
1167+centerX =
1168+ Testable.LabeledTest
1169+ { label = "centerX"
1170+ , attr = Element.centerX
1171+ , id = Testable.NoId
1172+ , test =
1173+ \found ->
1174+ let
1175+ selfCenter : Float
1176+ selfCenter =
1177+ found.self.bbox.left + (found.self.bbox.width / 2)
1178+
1179+ parentCenter : Float
1180+ parentCenter =
1181+ found.parent.bbox.left + (found.parent.bbox.width / 2)
1182+ in
1183+ if List.member found.parentLayout [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
1184+ [ Testable.true "centerX doesn't apply to elements that are onLeft or onRight" True ]
1185+
1186+ else if List.length found.siblings == 0 then
1187+ [ expectRoundedEquality
1188+ { found = selfCenter
1189+ , expected = parentCenter
1190+ }
1191+ ]
1192+
1193+ else
1194+ case found.parentLayout of
1195+ Testable.InRow ->
1196+ let
1197+ siblingsOnLeft =
1198+ List.filter (\x -> x.bbox.right < found.self.bbox.left) found.siblings
1199+
1200+ siblingsOnRight =
1201+ List.filter (\x -> x.bbox.left > found.self.bbox.right) found.siblings
1202+
1203+ widthsOnLeft : Float
1204+ widthsOnLeft =
1205+ siblingsOnLeft
1206+ |> List.map (.width << .bbox)
1207+ |> List.sum
1208+ |> (\x -> x + (toFloat (List.length siblingsOnLeft) * toFloat found.parentSpacing))
1209+
1210+ widthsOnRight =
1211+ siblingsOnRight
1212+ |> List.map (.width << .bbox)
1213+ |> List.sum
1214+ |> (\x -> x + (toFloat (List.length siblingsOnRight) * toFloat found.parentSpacing))
1215+
1216+ expectedCenter : Float
1217+ expectedCenter =
1218+ found.parent.bbox.left
1219+ + widthsOnLeft
1220+ + ((found.parent.bbox.width - (widthsOnRight + widthsOnLeft))
1221+ / 2
1222+ )
1223+ in
1224+ [ expectRoundedEquality
1225+ { found = selfCenter
1226+ , expected = expectedCenter
1227+ }
1228+ ]
1229+
1230+ _ ->
1231+ [ expectRoundedEquality
1232+ { found = selfCenter
1233+ , expected = parentCenter
1234+ }
1235+ ]
1236+ }
1237+
1238+
1239+{-| -}
1240+alignRight : Testable.Attr msg
1241+alignRight =
1242+ Testable.LabeledTest
1243+ { label = "alignRight"
1244+ , attr = Element.alignRight
1245+ , id = Testable.NoId
1246+ , test =
1247+ \found ->
1248+ if List.member found.parentLayout [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
1249+ [ Testable.true "alignRight doesn't apply to elements that are onLeft or onRight" True ]
1250+
1251+ else if
1252+ List.member found.parentLayout
1253+ [ Testable.IsNearby Testable.InFront
1254+ , Testable.IsNearby Testable.Behind
1255+ , Testable.IsNearby Testable.Above
1256+ , Testable.IsNearby Testable.Below
1257+ ]
1258+ then
1259+ [ expectRoundedEquality
1260+ { found = found.self.bbox.right
1261+ , expected = found.parent.bbox.right
1262+ }
1263+ ]
1264+
1265+ else if List.length found.siblings == 0 then
1266+ [ expectRoundedEquality
1267+ { found = found.self.bbox.right
1268+ , expected = found.parent.bbox.right + found.parent.bbox.padding.right
1269+ }
1270+ ]
1271+
1272+ else
1273+ case found.parentLayout of
1274+ Testable.InRow ->
1275+ let
1276+ siblingsOnRight =
1277+ List.filter (\x -> x.bbox.left > found.self.bbox.right) found.siblings
1278+
1279+ spacings =
1280+ toFloat (List.length siblingsOnRight * found.parentSpacing)
1281+
1282+ widthsOnRight =
1283+ siblingsOnRight
1284+ |> List.map (.width << .bbox)
1285+ |> List.sum
1286+ in
1287+ [ expectRoundedEquality
1288+ { found = found.self.bbox.right
1289+ , expected = found.parent.bbox.right - (found.parent.bbox.padding.right + widthsOnRight + spacings)
1290+ }
1291+ ]
1292+
1293+ _ ->
1294+ [ expectRoundedEquality
1295+ { found = found.self.bbox.right
1296+ , expected = found.parent.bbox.right + found.parent.bbox.padding.right
1297+ }
1298+ ]
1299+ }
1300+
1301+
1302+{-| -}
1303+alignTop : Testable.Attr msg
1304+alignTop =
1305+ Testable.LabeledTest
1306+ { label = "alignTop"
1307+ , attr = Element.alignTop
1308+ , id = Testable.NoId
1309+ , test =
1310+ \found ->
1311+ if List.member found.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
1312+ [ Testable.true "alignTop doesn't apply to elements that are above or below" True ]
1313+
1314+ else if
1315+ List.member found.parentLayout
1316+ [ Testable.IsNearby Testable.InFront
1317+ , Testable.IsNearby Testable.Behind
1318+ , Testable.IsNearby Testable.OnRight
1319+ , Testable.IsNearby Testable.OnLeft
1320+ ]
1321+ then
1322+ [ expectRoundedEquality
1323+ { found = found.self.bbox.top
1324+ , expected = found.parent.bbox.top
1325+ }
1326+ ]
1327+
1328+ else if List.length found.siblings == 0 then
1329+ [ expectRoundedEquality
1330+ { found = found.self.bbox.top
1331+ , expected = found.parent.bbox.top + found.parent.bbox.padding.top
1332+ }
1333+ ]
1334+
1335+ else
1336+ case found.parentLayout of
1337+ Testable.InColumn ->
1338+ let
1339+ siblingsAbove =
1340+ List.filter (\x -> x.bbox.bottom < found.self.bbox.top) found.siblings
1341+
1342+ spacings =
1343+ toFloat (List.length siblingsAbove * found.parentSpacing)
1344+
1345+ heightsAbove =
1346+ siblingsAbove
1347+ |> List.map (.height << .bbox)
1348+ |> List.sum
1349+ in
1350+ [ expectRoundedEquality
1351+ { found = found.self.bbox.top
1352+ , expected = found.parent.bbox.top + (found.parent.bbox.padding.top + heightsAbove + spacings)
1353+ }
1354+ ]
1355+
1356+ _ ->
1357+ [ expectRoundedEquality
1358+ { found = found.self.bbox.top
1359+ , expected = found.parent.bbox.top
1360+ }
1361+ ]
1362+ }
1363+
1364+
1365+{-| -}
1366+alignBottom : Testable.Attr msg
1367+alignBottom =
1368+ Testable.LabeledTest
1369+ { label = "alignBottom"
1370+ , attr = Element.alignBottom
1371+ , id = Testable.NoId
1372+ , test =
1373+ \found ->
1374+ if List.member found.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
1375+ [ Testable.true "alignBottom doesn't apply to elements that are above or below" True ]
1376+
1377+ else if
1378+ List.member found.parentLayout
1379+ [ Testable.IsNearby Testable.InFront
1380+ , Testable.IsNearby Testable.Behind
1381+ , Testable.IsNearby Testable.OnRight
1382+ , Testable.IsNearby Testable.OnLeft
1383+ ]
1384+ then
1385+ [ expectRoundedEquality
1386+ { found = found.self.bbox.bottom
1387+ , expected = found.parent.bbox.bottom
1388+ }
1389+ ]
1390+
1391+ else if List.length found.siblings == 0 then
1392+ [ expectRoundedEquality
1393+ { found = found.self.bbox.bottom
1394+ , expected = found.parent.bbox.bottom + found.parent.bbox.padding.bottom
1395+ }
1396+ ]
1397+
1398+ else
1399+ case found.parentLayout of
1400+ Testable.InColumn ->
1401+ let
1402+ siblingsBelow =
1403+ List.filter (\x -> x.bbox.top > found.self.bbox.bottom) found.siblings
1404+
1405+ spacings =
1406+ toFloat (List.length siblingsBelow * found.parentSpacing)
1407+
1408+ heightsBelow =
1409+ siblingsBelow
1410+ |> List.map (.height << .bbox)
1411+ |> List.sum
1412+ in
1413+ [ expectRoundedEquality
1414+ { found = found.self.bbox.bottom
1415+ , expected = found.parent.bbox.bottom - (found.parent.bbox.padding.bottom + heightsBelow + spacings)
1416+ }
1417+ ]
1418+
1419+ _ ->
1420+ [ expectRoundedEquality
1421+ { found = found.self.bbox.bottom
1422+ , expected = found.parent.bbox.bottom + found.parent.bbox.padding.bottom
1423+ }
1424+ ]
1425+ }
1426+
1427+
1428+expectRoundedEquality : { expected : Float, found : Float } -> Testable.LayoutExpectation
1429+expectRoundedEquality { expected, found } =
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)
1438+
1439+
1440+{-| -}
1441+centerY : Testable.Attr msg
1442+centerY =
1443+ Testable.LabeledTest
1444+ { label = "centerY"
1445+ , attr = Element.centerY
1446+ , id = Testable.NoId
1447+ , test =
1448+ \found ->
1449+ let
1450+ selfCenter =
1451+ found.self.bbox.top + (found.self.bbox.height / 2)
1452+
1453+ parentCenter =
1454+ found.parent.bbox.top + (found.parent.bbox.height / 2)
1455+ in
1456+ if List.member found.parentLayout [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
1457+ [ Testable.true "centerY doesn't apply to elements that are above or below" True ]
1458+
1459+ else if List.length found.siblings == 0 then
1460+ [ expectRoundedEquality
1461+ { found = selfCenter
1462+ , expected = parentCenter
1463+ }
1464+ ]
1465+
1466+ else
1467+ case found.parentLayout of
1468+ Testable.InColumn ->
1469+ let
1470+ siblingsOnTop =
1471+ List.filter (\x -> x.bbox.bottom < found.self.bbox.top) found.siblings
1472+
1473+ siblingsBelow =
1474+ List.filter (\x -> x.bbox.top > found.self.bbox.bottom) found.siblings
1475+
1476+ heightsAbove : Float
1477+ heightsAbove =
1478+ siblingsOnTop
1479+ |> List.map (.height << .bbox)
1480+ |> List.sum
1481+ |> (\x -> x + (toFloat (List.length siblingsOnTop) * toFloat found.parentSpacing))
1482+
1483+ heightsBelow =
1484+ siblingsBelow
1485+ |> List.map (.height << .bbox)
1486+ |> List.sum
1487+ |> (\x -> x + (toFloat (List.length siblingsBelow) * toFloat found.parentSpacing))
1488+
1489+ expectedCenter : Float
1490+ expectedCenter =
1491+ found.parent.bbox.top
1492+ + heightsAbove
1493+ + ((found.parent.bbox.height - (heightsBelow + heightsAbove))
1494+ / 2
1495+ )
1496+ in
1497+ [ expectRoundedEquality
1498+ { found = selfCenter
1499+ , expected = expectedCenter
1500+ }
1501+ ]
1502+
1503+ _ ->
1504+ [ expectRoundedEquality
1505+ { found = selfCenter
1506+ , expected = parentCenter
1507+ }
1508+ ]
1509+ }
1510+
1511+
1512+{-| -}
1513+above : Testable.Element msg -> Testable.Attr msg
1514+above element =
1515+ Testable.Nearby
1516+ { location = Testable.Above
1517+ , element = element
1518+ , label = "above"
1519+ , test =
1520+ \found ->
1521+ [ expectRoundedEquality
1522+ { found = found.self.bbox.bottom
1523+ , expected = found.parent.bbox.top
1524+ }
1525+ ]
1526+ }
1527+
1528+
1529+{-| -}
1530+below : Testable.Element msg -> Testable.Attr msg
1531+below element =
1532+ Testable.Nearby
1533+ { location = Testable.Below
1534+ , element = element
1535+ , label = "below"
1536+ , test =
1537+ \found ->
1538+ [ expectRoundedEquality
1539+ { found = found.self.bbox.top
1540+ , expected = found.parent.bbox.bottom
1541+ }
1542+ ]
1543+ }
1544+
1545+
1546+{-| -}
1547+onRight : Testable.Element msg -> Testable.Attr msg
1548+onRight element =
1549+ Testable.Nearby
1550+ { location = Testable.OnRight
1551+ , element = element
1552+ , label = "onRight"
1553+ , test =
1554+ \found ->
1555+ [ expectRoundedEquality
1556+ { found = found.self.bbox.left
1557+ , expected = found.parent.bbox.right
1558+ }
1559+ ]
1560+ }
1561+
1562+
1563+{-| -}
1564+onLeft : Testable.Element msg -> Testable.Attr msg
1565+onLeft element =
1566+ Testable.Nearby
1567+ { location = Testable.OnLeft
1568+ , element = element
1569+ , label = "onLeft"
1570+ , test =
1571+ \found ->
1572+ [ expectRoundedEquality
1573+ { found = found.self.bbox.right
1574+ , expected = found.parent.bbox.left
1575+ }
1576+ ]
1577+ }
1578+
1579+
1580+compare x vs y =
1581+ vs (round x) (round y) || vs (floor x) (floor y)
1582+
1583+
1584+{-| -}
1585+inFront : Testable.Element msg -> Testable.Attr msg
1586+inFront element =
1587+ Testable.Nearby
1588+ { location = Testable.InFront
1589+ , element = element
1590+ , label = "inFront"
1591+ , test =
1592+ \found ->
1593+ [ withinHelper found ]
1594+ }
1595+
1596+
1597+withinHelper found =
1598+ let
1599+ horizontalCheck =
1600+ if found.self.bbox.width > found.parent.bbox.width then
1601+ [ compare found.self.bbox.right (<=) found.parent.bbox.right
1602+ || compare found.self.bbox.left (>=) found.parent.bbox.left
1603+ ]
1604+
1605+ else
1606+ [ compare found.self.bbox.right (<=) found.parent.bbox.right
1607+ , compare found.self.bbox.left (>=) found.parent.bbox.left
1608+ ]
1609+
1610+ verticalCheck =
1611+ if found.self.bbox.width > found.parent.bbox.width then
1612+ [ compare found.self.bbox.top (>=) found.parent.bbox.top
1613+ || compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
1614+ ]
1615+
1616+ else
1617+ [ compare found.self.bbox.top (>=) found.parent.bbox.top
1618+ , compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
1619+ ]
1620+ in
1621+ Testable.true "within the confines of the parent"
1622+ (List.all ((==) True)
1623+ (List.concat
1624+ [ horizontalCheck
1625+ , verticalCheck
1626+ ]
1627+ )
1628+ )
1629+
1630+
1631+{-| -}
1632+behindContent : Testable.Element msg -> Testable.Attr msg
1633+behindContent element =
1634+ Testable.Nearby
1635+ { location = Testable.Behind
1636+ , element = element
1637+ , label = "behindContent"
1638+ , test =
1639+ \found ->
1640+ [ withinHelper found ]
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/src/Testable/Element/Background.elm → tests-rendering/src/Testable/Element/Background.elmrenamed+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/src/Testable/Element/Font.elm → tests-rendering/src/Testable/Element/Font.elmrenamed+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/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.elmnew file+607
@@ -0,0 +1,607 @@
1+port module Testable.Runner exposing
2+ ( Msg
3+ , Testable
4+ , TestableProgram
5+ , program
6+ , rename
7+ , show
8+ , testable
9+ )
10+
11+{-| -}
12+
13+import Browser
14+import Dict exposing (Dict)
15+import Element
16+import Element.Background as Background
17+import Element.Border as Border
18+import Element.Events as Events
19+import Element.Font as Font
20+import Html exposing (Html)
21+import Html.Attributes
22+import Process
23+import Task
24+import Testable
25+import Time
26+
27+
28+show : Testable.Element msg -> Html msg
29+show =
30+ Testable.toHtml
31+
32+
33+type alias TestableProgram =
34+ Program () (Model Msg) Msg
35+
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+
53+palette =
54+ { white = Element.rgb 1 1 1
55+ , red = Element.rgb 1 0 0
56+ , green = Element.rgb 0 1 0
57+ , black = Element.rgb 0 0 0
58+ , lightGrey = Element.rgb 0.7 0.7 0.7
59+ }
60+
61+
62+program : List Testable -> TestableProgram
63+program tests =
64+ let
65+ ( current, upcoming ) =
66+ case tests of
67+ [] ->
68+ ( Nothing, [] )
69+
70+ cur :: remaining ->
71+ ( Just cur, remaining )
72+ in
73+ Browser.element
74+ { init =
75+ always
76+ ( { current = current
77+ , upcoming = upcoming
78+ , finished = []
79+ , selected = 0
80+ , highlightDomId = Nothing
81+ }
82+ , Task.perform (always Analyze)
83+ (Process.sleep 64
84+ |> Task.andThen
85+ (always Time.now)
86+ )
87+ )
88+ , view = view
89+ , update = update
90+ , subscriptions = subscriptions
91+ }
92+
93+
94+subscriptions : Model Msg -> Sub Msg
95+subscriptions model =
96+ Sub.batch
97+ [ styles RefreshBoundingBox
98+ ]
99+
100+
101+type alias Model msg =
102+ { current : Maybe ( String, Testable.Element msg )
103+ , upcoming : List ( String, Testable.Element msg )
104+ , finished : List (WithResults (Testable.Element msg))
105+ , selected : Int
106+ , highlightDomId : Maybe String
107+ }
108+
109+
110+type alias WithResults thing =
111+ { index : Int
112+ , element : thing
113+ , label : String
114+ , results :
115+ List Testable.LayoutTest
116+ }
117+
118+
119+encodeForReport :
120+ List (WithResults (Testable.Element msg))
121+ ->
122+ List
123+ { label : String
124+ , results :
125+ List
126+ { description : String
127+ , passing : Bool
128+ , todo : Bool
129+ }
130+ }
131+encodeForReport withResults =
132+ let
133+ prepareExpectation layoutTest exp =
134+ case exp of
135+ Testable.Todo description ->
136+ { description = description
137+ , passing = False
138+ , todo = True
139+ }
140+
141+ Testable.Expect details ->
142+ { description = details.description
143+ , passing = details.result
144+ , todo = False
145+ }
146+
147+ prepareNode layoutTest =
148+ List.map (prepareExpectation layoutTest) layoutTest.expectations
149+
150+ prepare { label, results } =
151+ { label = label
152+ , results = List.concatMap prepareNode results
153+ }
154+ in
155+ List.map prepare withResults
156+
157+
158+type Msg
159+ = NoOp
160+ | Analyze
161+ | Select Int
162+ | HighlightDomID (Maybe String)
163+ | RefreshBoundingBox
164+ (List
165+ { id : String
166+ , bbox : Testable.BoundingBox
167+ , style : List ( String, String )
168+ , isVisible : Bool
169+ , textMetrics : List Testable.TextMetrics
170+ }
171+ )
172+
173+
174+runTest : Int -> Dict String Testable.Found -> String -> Testable.Element msg -> WithResults (Testable.Element msg)
175+runTest index boxes label element =
176+ let
177+ results =
178+ Testable.runTests boxes element
179+ in
180+ { index = index
181+ , element = element
182+ , label = label
183+ , results = results
184+ }
185+
186+
187+update : Msg -> Model Msg -> ( Model Msg, Cmd Msg )
188+update msg model =
189+ case msg of
190+ NoOp ->
191+ ( model, Cmd.none )
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+
203+ RefreshBoundingBox boxes ->
204+ case model.current of
205+ Nothing ->
206+ ( model
207+ , Cmd.none
208+ )
209+
210+ Just ( label, current ) ->
211+ let
212+ toTuple box =
213+ ( box.id
214+ , { style = Dict.fromList box.style
215+ , bbox = box.bbox
216+ , isVisible = box.isVisible
217+ , textMetrics = box.textMetrics
218+ }
219+ )
220+
221+ foundData =
222+ boxes
223+ |> List.map toTuple
224+ |> Dict.fromList
225+
226+ currentResults =
227+ runTest (List.length model.finished) foundData label current
228+ in
229+ case model.upcoming of
230+ [] ->
231+ ( { model
232+ | current = Nothing
233+ , finished = model.finished ++ [ currentResults ]
234+ }
235+ , report (encodeForReport (currentResults :: model.finished))
236+ )
237+
238+ newCurrent :: remaining ->
239+ ( { model
240+ | finished = model.finished ++ [ currentResults ]
241+ , current = Just newCurrent
242+ , upcoming = remaining
243+ }
244+ , Task.perform (always Analyze)
245+ (Process.sleep 64
246+ |> Task.andThen
247+ (always Time.now)
248+ )
249+ )
250+
251+ Analyze ->
252+ case model.current of
253+ Nothing ->
254+ ( model
255+ , Cmd.none
256+ )
257+
258+ Just ( label, current ) ->
259+ ( model
260+ , analyze (Testable.getIds current)
261+ )
262+
263+
264+view : Model Msg -> Html.Html Msg
265+view model =
266+ case model.current of
267+ Nothing ->
268+ if List.isEmpty model.upcoming then
269+ let
270+ selected =
271+ getByIndex model.selected model.finished
272+ in
273+ Element.layout
274+ [ Font.size 16
275+ , Element.inFront (Element.html (viewElementHighlight model))
276+ , Element.height Element.fill
277+ ]
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+ ]
324+
325+ else
326+ Html.text ""
327+
328+ Just ( label, current ) ->
329+ Testable.toHtml current
330+
331+
332+getByIndex i ls =
333+ List.foldl
334+ (\elem ( index, found ) ->
335+ if i == index then
336+ ( index + 1, Just elem )
337+
338+ else
339+ ( index + 1, found )
340+ )
341+ ( 0, Nothing )
342+ ls
343+ |> Tuple.second
344+
345+
346+viewElementHighlight model =
347+ case model.highlightDomId of
348+ Nothing ->
349+ Html.text ""
350+
351+ Just highlightDomId ->
352+ let
353+ elementHighlight =
354+ highlightDomId ++ " { outline: solid black; }"
355+
356+ testId =
357+ highlightDomId
358+ |> String.dropLeft 1
359+ |> String.append "#tests-"
360+
361+ testHighlight =
362+ testId ++ " { outline: dashed black; }"
363+
364+ styleSheet =
365+ String.join "\n"
366+ [ elementHighlight
367+ , testHighlight
368+ ]
369+ in
370+ Html.node "style"
371+ []
372+ [ Html.text styleSheet
373+ ]
374+
375+
376+hasFailure : WithResults (Testable.Element Msg) -> Int
377+hasFailure myTest =
378+ if
379+ List.any
380+ (not << isPassing)
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+ )
485+ []
486+ items
487+
488+
489+viewLayoutTestGroup group =
490+ let
491+ testId =
492+ group.id
493+ |> String.dropLeft 1
494+ |> String.append "tests-"
495+ in
496+ Element.column
497+ [ Element.spacing 8
498+ , Element.htmlAttribute (Html.Attributes.id testId)
499+ , Events.onMouseEnter (HighlightDomID (Just group.id))
500+ , Events.onMouseLeave (HighlightDomID Nothing)
501+ , Element.htmlAttribute (Html.Attributes.style "user-select" "none")
502+ ]
503+ [ Element.row [ Element.spacing 16 ]
504+ [ Element.el [] (Element.text (Testable.elementTypeToString group.elementType))
505+ , Element.el [ Font.color palette.lightGrey ] (Element.text group.id)
506+ ]
507+ , Element.column
508+ [ Element.spacing 8
509+ , Element.paddingXY 32 0
510+ ]
511+ (List.map viewLayoutTest group.members)
512+ ]
513+
514+
515+type alias Grouped thing =
516+ { id : String
517+ , members : List thing
518+ }
519+
520+
521+viewLayoutTest layoutTest =
522+ Element.column
523+ [ Element.spacing 8
524+ ]
525+ [ Element.row [ Element.spacing 8 ]
526+ [ Element.el [ Font.bold ] (Element.text layoutTest.label)
527+
528+ -- , Element.el [ Font.color palette.lightGrey ] (Element.text layoutTest.elementDomId)
529+ ]
530+ , Element.column [ Element.spacing 8 ]
531+ (List.map viewLayoutExpectation layoutTest.expectations)
532+ ]
533+
534+
535+viewLayoutExpectation expectation =
536+ case expectation of
537+ Testable.Todo label ->
538+ Element.row [ Element.spacing 4 ]
539+ [ todo, Element.text label ]
540+
541+ Testable.Expect details ->
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"
571+
572+
573+formatKeyValue : String -> String -> String
574+formatKeyValue key val =
575+ key ++ ": " ++ val
576+
577+
578+port report :
579+ List
580+ { label : String
581+ , results :
582+ List
583+ { description : String
584+ , passing : Bool
585+ , todo : Bool
586+ }
587+ }
588+ -> Cmd msg
589+
590+
591+port test : String -> Cmd msg
592+
593+
594+port analyze : List String -> Cmd msg
595+
596+
597+port styles :
598+ (List
599+ { id : String
600+ , bbox : Testable.BoundingBox
601+ , style : List ( String, String )
602+ , isVisible : Bool
603+ , textMetrics : List Testable.TextMetrics
604+ }
605+ -> msg
606+ )
607+ -> Sub msg
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/Nearby.elmnew file+179
@@ -0,0 +1,179 @@
1+module Tests.Nearby exposing (view)
2+
3+{-| Testing nearby elements such as those defined with `above`, `below`, etc.
4+-}
5+
6+import Html
7+import Testable
8+import Testable.Element exposing (..)
9+import Testable.Element.Background as Background
10+import Testable.Element.Font as Font
11+import Testable.Generator
12+import Testable.Runner
13+import Tests.Palette as Palette exposing (..)
14+
15+
16+box attrs =
17+ el
18+ ([ width (px 50)
19+ , height (px 50)
20+ , Background.color blue
21+ ]
22+ ++ attrs
23+ )
24+ none
25+
26+
27+justBox size color attrs =
28+ el
29+ ([ width (px size)
30+ , height (px size)
31+ , Background.color color
32+ ]
33+ ++ attrs
34+ )
35+ none
36+
37+
38+littleBox name attrs =
39+ el
40+ ([ label name
41+ , width (px 5)
42+ , height (px 5)
43+ ]
44+ ++ attrs
45+ )
46+ none
47+
48+
49+p attrs =
50+ paragraph
51+ ([ Background.color blue
52+ , Font.color white
53+ , padding 20
54+ ]
55+ ++ attrs
56+ )
57+ [ text "Lorem Ipsum or something or other." ]
58+
59+
60+{-| -}
61+view : List ( String, Testable.Element msg )
62+view =
63+ List.concat
64+ [ [ Tuple.pair "Parent inFront above child inFront" layeredVisibility
65+ , Tuple.pair "Sibling onLeft above sibling inFront" overlappingChildren
66+ , Tuple.pair "el with all nearbys and alignments" master
67+ , Tuple.pair "paragraph with all nearbys and alignments" masterParagraph
68+ ]
69+ , Testable.Generator.generate "Nearbys"
70+ (\element nearby ->
71+ element
72+ [ nearby (box [])
73+ , width (px 200)
74+ , height (px 200)
75+ , Background.color red
76+ ]
77+ none
78+ )
79+ |> Testable.Generator.with Testable.Generator.allElements
80+ |> Testable.Generator.with Testable.Generator.nearbys
81+ , Testable.Generator.generate "Nearbys with alignment"
82+ (\element nearby align ->
83+ element
84+ [ nearby (box [ align ])
85+ , width (px 200)
86+ , height (px 200)
87+ , Background.color red
88+ ]
89+ none
90+ )
91+ |> Testable.Generator.with Testable.Generator.allElements
92+ |> Testable.Generator.with Testable.Generator.nearbys
93+ |> Testable.Generator.with Testable.Generator.alignments
94+ ]
95+
96+
97+layeredVisibility =
98+ el
99+ [ centerX
100+ , inFront (justBox 40 red [ isVisible ])
101+ ]
102+ (el [ inFront (justBox 30 green []) ]
103+ (justBox 50 blue [])
104+ )
105+
106+
107+overlappingChildren =
108+ row [ centerX ]
109+ [ el [ inFront (justBox 40 green []) ]
110+ (justBox 40 darkGrey [])
111+ , el [ onLeft (littleBox "overlapping" [ isVisible, Background.color red ]) ]
112+ (justBox 40 darkGrey [])
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/Tests/Palette.elm → tests-rendering/src/Tests/Palette.elmrenamed

The content is the same.

tests-rendering/src/Tests/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 ElInFixedHeightColumn
7+import Testable.Runner
8+
9+
10+main : Testable.Runner.TestableProgram
11+main =
12+ Testable.Runner.program
13+ [ Testable.Runner.rename " El In Fixed Height Column" ElInFixedHeightColumn.view
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/Tests/Table.elm.todo → tests-rendering/src/Tests/Table.elm.todorenamed

The content is the same.

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+-- ]
tests/Tests/Transparency.elm → tests-rendering/src/Tests/Transparency.elmrenamed

The content is the same.

tests/Tests/WidthHeight.elm → tests-rendering/src/Tests/WidthHeight.elmrenamed

The content is the same.

tests/suite/ClassNames.elm → tests/ClassNames.elmrenamed+5−3
@@ -3,12 +3,12 @@ module ClassNames exposing (suite)
3{-| -}
3{-| -}
4
4
5import Expect
5import Expect
6−import Html
6+import Html exposing (Html)
7−import Internal.Flag as Flag
8import Internal.Style
7import Internal.Style
9−import Test
8+import Test exposing (Test)
10
9
11
10
11+suite : Test
12suite =
12suite =
13 Test.describe "Classname Collisions"
13 Test.describe "Classname Collisions"
14 [ Test.test "No duplicates" <|
14 [ Test.test "No duplicates" <|
@@ -17,6 +17,7 @@ suite =
17 ]
17 ]
18
18
19
19
20+main : Html msg
20main =
21main =
21 Html.div []
22 Html.div []
22 [ Html.text "The following names collide"
23 [ Html.text "The following names collide"
@@ -25,6 +26,7 @@ main =
25 ]
26 ]
26
27
27
28
29+viewPair : ( String, String ) -> Html msg
28viewPair ( name, description ) =
30viewPair ( name, description ) =
29 Html.div []
31 Html.div []
30 [ Html.text name
32 [ Html.text name
tests/suite/Flags.elm → tests/Flags.elmrenamed−1
@@ -3,7 +3,6 @@ module Flags exposing (suite)
3{-| -}
3{-| -}
4
4
5import Expect
5import Expect
6−import Html
7import Internal.Flag as Flag
6import Internal.Flag as Flag
8import Test
7import Test
9
8
tests/Makefiledeleted−13
@@ -1,13 +0,0 @@
1−compile:
2− elm make Tests/Run.elm --output=elm.js
3−
4−local_test:
5− source env/bin/activate
6− source sauce.env
7− python3 automation/selenium_test.py --local
8−
9−remote_test:
10− source env/bin/activate
11− source sauce.env
12− python3 automation/run.py
13−
tests/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/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/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/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/Tests/Nearby.elmdeleted−438
@@ -1,438 +0,0 @@
1−module Tests.Nearby exposing (view)
2−
3−{-| Testing nearby elements such as those defined with `above`, `below`, etc.
4−-}
5−
6−import Html
7−import Testable
8−import Testable.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 exposing (..)
13−
14−
15−box attrs =
16− el
17− ([ width (px 50)
18− , height (px 50)
19− , Background.color blue
20− ]
21− ++ attrs
22− )
23− none
24−
25−
26−justBox size color attrs =
27− el
28− ([ width (px size)
29− , height (px size)
30− , Background.color color
31− ]
32− ++ attrs
33− )
34− none
35−
36−
37−littleBox name attrs =
38− el
39− ([ label name
40− , width (px 5)
41− , height (px 5)
42− ]
43− ++ attrs
44− )
45− none
46−
47−
48−p attrs =
49− paragraph
50− ([ Background.color blue
51− , Font.color white
52− , padding 20
53− ]
54− ++ attrs
55− )
56− [ text "Lorem Ipsum or something or other." ]
57−
58−
59−{-| -}
60−main : Html.Html msg
61−main =
62− Testable.Runner.show view
63−
64−
65−{-| -}
66−view : Testable.Element msg
67−view =
68− let
69− transparentBox attrs =
70− el
71− ([ Font.color white
72− , width (px 50)
73− , height (px 50)
74− , Background.color (rgba 52 101 164 0.8)
75− ]
76− ++ attrs
77− )
78− (text "hi")
79−
80− -- single location name box =
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− ]
264− , text "on paragraph"
265− , row [ width fill, spacing 50, label "Row" ]
266− [ p
267− [ location
268− (el
269− [ label name
270− , width (px 20)
271− , height (px 20)
272− , Background.color darkCharcoal
273− ]
274− none
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− ]
350− ]
351−
352− master =
353− el [ padding 20 ] <|
354− box
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− ]
420−
421−
422−layeredVisibility =
423− el
424− [ centerX
425− , inFront (justBox 40 red [ isVisible ])
426− ]
427− (el [ inFront (justBox 30 green []) ]
428− (justBox 50 blue [])
429− )
430−
431−
432−overlappingChildren =
433− row [ centerX ]
434− [ el [ inFront (justBox 40 green []) ]
435− (justBox 40 darkGrey [])
436− , el [ onLeft (littleBox "overlapping" [ isVisible, Background.color red ]) ]
437− (justBox 40 darkGrey [])
438− ]
tests/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/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/Tests/Run.elmdeleted−27
@@ -1,27 +0,0 @@
1−port module Tests.Run exposing (main)
2−
3−{-| -}
4−
5−import 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−
15−
16−main : Testable.Runner.TestableProgram
17−main =
18− Testable.Runner.program
19− [ Tuple.pair "Basic Element" Tests.Basic.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− , Tuple.pair "Row Alignment" Tests.RowAlignment.view
25− , Tuple.pair "Column Spacing" Tests.ColumnSpacing.view
26− , Tuple.pair "Row Spacing" Tests.RowSpacing.view
27− ]
tests/automation/environments.pydeleted−91
@@ -1,91 +0,0 @@
1−
2−
3−import appium
4−import selenium
5−import selenium.webdriver.firefox.options
6−import os
7−
8−browsers = {'Windows 10':
9− {'internet explorer': ['11.285'], 'MicrosoftEdge': ['18.17763'], 'chrome': ['71.0'], 'firefox': ['64.0']
10− },
11− "macOS 10.14":
12− {'chrome': ['71.0'], 'firefox': ['64.0'], 'safari': ['12.0']}
13−
14− }
15−
16−
17−def desktop():
18−
19− caps = []
20− for platform, brows in browsers.items():
21− for browser, versions in brows.items():
22− for vers in versions:
23− cap = {'platform': platform,
24− 'browserName': browser, 'version': vers}
25−
26− caps.append(
27− {'driver_type': 'desktop',
28− 'capabilities': cap})
29−
30− return caps
31−
32−
33−def local():
34− options = selenium.webdriver.firefox.options.Options()
35− options.set_headless(True)
36−
37− driver = selenium.webdriver.Firefox(options=options)
38− caps = {}
39− caps['browserName'] = "firefox"
40− caps['platform'] = "macOS 10.14"
41− return [{'driver_type': 'local', 'capabilities': caps}]
42−
43−
44−def mobile():
45−
46− caps = {}
47− caps['browserName'] = "Safari"
48− caps['appiumVersion'] = "1.9.1"
49− caps['deviceName'] = "iPhone XS Simulator"
50− caps['deviceOrientation'] = "portrait"
51− caps['platformVersion'] = "12.0"
52− caps['platformName'] = "iOS"
53− return [{'capabilities': caps, 'driver_type': 'app'}]
54−
55−
56−def get_credentials():
57− return {'username': os.environ['SAUCE_USERNAME'], 'access_key': os.environ['SAUCE_ACCESS_KEY']}
58−
59−
60−def start_driver(env, capabilities):
61− if env == 'app':
62− return app(capabilities)
63− elif env == 'desktop':
64− return remote(capabilities)
65− else:
66− options = selenium.webdriver.firefox.options.Options()
67− options.set_headless(True)
68−
69− return selenium.webdriver.Firefox(options=options)
70−
71−
72−def remote(desired_cap):
73− creds = get_credentials()
74− driver = selenium.webdriver.Remote(
75− command_executor='http://{username}:{key}@ondemand.saucelabs.com:80/wd/hub'.format(
76− username=creds['username'],
77− key=creds['access_key']),
78− desired_capabilities=desired_cap)
79−
80− return driver
81−
82−
83−def app(desired_cap):
84− creds = get_credentials()
85− driver = appium.webdriver.Remote(
86− command_executor='http://{username}:{key}@ondemand.saucelabs.com:80/wd/hub'.format(
87− username=creds['username'],
88− key=creds['access_key']),
89− desired_capabilities=desired_cap)
90−
91− return driver
tests/automation/run.pydeleted−105
@@ -1,105 +0,0 @@
1−import os
2−import time
3−import pprint
4−import json
5−import sys
6−
7−import environments
8−
9−
10−def run(env, url):
11− driver = environments.start_driver(env['driver_type'], env['capabilities'])
12−
13− test_run = run_test(driver, url)
14− return {'capabilities': env['capabilities'], 'failures': test_run, 'success': test_run == []}
15−
16−
17−def log_results(all_results):
18−
19− for group in all_results:
20− print(group["label"])
21− all_pass = True
22− for result in group["results"]:
23− name = result[0]
24− val = result[1]
25−
26− if val is not None:
27− print(" Failed - " + name)
28− all_pass = False
29− # { given : Maybe String
30− # , description : String
31− # , reason : Failure.Reason
32− # }
33− pprint.pprint(val)
34−
35− if all_pass:
36− print(" All passed!")
37−
38−
39−def only_failures(all_results):
40− failures = []
41− for group in all_results:
42−
43− for result in group["results"]:
44− name = result[0]
45− val = result[1]
46−
47− if val is not None:
48− failures.append(
49− {'group': group['label'], 'description': name, 'result': val})
50−
51− return failures
52−
53−
54−def run_test(driver, url):
55− print("Running test...")
56− results = None
57− try:
58− # print("Opening Browser")
59− driver.get(url)
60−
61− # print("Checking Results")
62− time.sleep(20)
63− for x in range(60):
64− feedback = driver.execute_script('return test_results')
65− if feedback != "waiting..":
66− results = only_failures(feedback)
67− # log_results(feedback)
68− break
69− time.sleep(1)
70− except Exception as inst:
71− print(type(inst))
72− print(inst.args)
73− print(inst)
74− driver.quit()
75− else:
76− print("Finished!")
77− driver.quit()
78−
79− return results
80−
81−
82−if __name__ == "__main__":
83−
84− url = "http://mdgriffith.github.io/elm-ui/tests/base/"
85−
86− results = []
87− if len(sys.argv) > 1:
88− if sys.argv[1] == "--local":
89− envs = environments.local()
90− else:
91− raise "Unknown argument, run --local to run locally."
92− else:
93− envs = environments.desktop()
94− envs.extend(environments.mobile())
95−
96− for env in envs:
97− pprint.pprint(env['capabilities'])
98− result = run(env, url)
99− results.append(result)
100− print("--")
101−
102− pprint.pprint(results)
103−
104− with open("automation/results/test-results.json", 'w') as RESULTS:
105− RESULTS.write(json.dumps(results, indent=4))
tests/elm.jsondeleted−30
@@ -1,30 +0,0 @@
1−{
2− "type": "application",
3− "source-directories": [
4− "src",
5− "suite",
6− "../src",
7− "."
8− ],
9− "elm-version": "0.19.0",
10− "dependencies": {
11− "direct": {
12− "elm/browser": "1.0.0",
13− "elm/core": "1.0.0",
14− "elm/html": "1.0.0",
15− "elm/json": "1.0.0",
16− "elm/parser": "1.1.0",
17− "elm/random": "1.0.0",
18− "elm/time": "1.0.0",
19− "elm/virtual-dom": "1.0.0",
20− "elm-explorations/test": "1.1.0"
21− },
22− "indirect": {
23− "elm/url": "1.0.0"
24− }
25− },
26− "test-dependencies": {
27− "direct": {},
28− "indirect": {}
29− }
30−}
tests/gather-styles.htmldeleted−110
@@ -1,110 +0,0 @@
1−<!DOCTYPE HTML>
2−<html>
3−
4−<head>
5− <meta charset="UTF-8">
6− <title>Rendering Benchmark Viewer</title>
7− <script src="elm.js"></script>
8−</head>
9−<style>
10−</style>
11−
12−<body id="root"></body>
13−
14−<script type="text/javascript">
15−
16− var node = document.getElementById('root');
17− var app = Elm.Tests.Run.init({ node: node });
18−
19− var test_results = "waiting.."
20−
21− app.ports.report.subscribe(function (results) {
22− test_results = results;
23− })
24−
25− app.ports.analyze.subscribe(function (ids) {
26− // ids : List String
27− var idsLength = ids.length;
28− var results = [];
29− for (var i = 0; i < idsLength; i++) {
30− var id = ids[i];
31− var element = document.getElementById(id);
32− if (element == null) {
33− console.log("id " + id + " not found");
34− }
35− var style = getStyle(element);
36− var bbox = getBoundingBox(element);
37−
38− var visible = isVisible(id, bbox);
39−
40−
41− var result = { "bbox": bbox, "style": style, "id": id, "isVisible": visible };
42− results.push(result);
43−
44− }
45− app.ports.styles.send(results);
46− });
47−
48− function isVisible(id, bbox) {
49− var current = document.getElementById(id);
50− var result = 0;
51− if (current == document.elementFromPoint(bbox['left'], bbox['top'])) {
52− result++;
53− }
54− if (current == document.elementFromPoint(bbox['left'], bbox['bottom'] - 1)) {
55− result++;
56− }
57− if (current == document.elementFromPoint(bbox['right'] - 1, bbox['top'])) {
58− result++;
59− }
60− if (current == document.elementFromPoint(bbox['right'] - 1, bbox['bottom'] - 1)) {
61− result++;
62− }
63−
64−
65− if (result == 4) {
66− return true;
67− } else {
68− return false
69− }
70− }
71−
72− function getStyle(element) {
73− var props = []
74− var style = window.getComputedStyle(element);
75− for (var i = style.length; i--;) {
76− var name = style.item(i);
77− var value = style.getPropertyValue(name);
78− props.push([name, value]);
79− }
80− return props;
81− }
82−
83− function getBoundingBox(element) {
84− var bbox = element.getBoundingClientRect();
85− var style = window.getComputedStyle(element, null);
86−
87− var padding = {
88− 'top': parseFloat(style.getPropertyValue('padding-top'))
89− , 'bottom': parseFloat(style.getPropertyValue('padding-bottom'))
90− , 'left': parseFloat(style.getPropertyValue('padding-left'))
91− , 'right': parseFloat(style.getPropertyValue('padding-right'))
92− };
93−
94− return {
95− 'top': bbox.top
96− , 'bottom': bbox.bottom
97− , 'left': bbox.left
98− , 'right': bbox.right
99− , 'width': bbox.width
100− , 'height': bbox.height
101− , 'padding': padding
102− }
103− }
104−
105−
106−
107−
108−</script>
109−
110−</html>
tests/src/Testable/Element.elmdeleted−1102
@@ -1,1102 +0,0 @@
1−module Testable.Element exposing
2− ( Length(..)
3− , above
4− , alignBottom
5− , alignLeft
6− , alignRight
7− , alignTop
8− , alpha
9− , behindContent
10− , below
11− , centerX
12− , centerY
13− , column
14− , el
15− , expectRoundedEquality
16− , fill
17− , fillPortion
18− , height
19− , heightHelper
20− , inFront
21− , isVisible
22− , label
23− , maximum
24− , minimum
25− , none
26− , onLeft
27− , onRight
28− , padding
29− , paddingXY
30− , paragraph
31− , px
32− , row
33− , shrink
34− , spacing
35− , text
36− , textColumn
37− , transparent
38− , width
39− , widthHelper
40− )
41−
42−{-| This module should mirror the top level `Element` api, with one important distinction.
43−
44−The resulting `Element msg` structure can either be rendered to Html or transformed into a test suite that can be run.
45−
46−In order to run the test:
47−
48− - render html
49− - gather information from the browser
50− - generate tests
51−
52−-}
53−
54−import Dict
55−import Element
56−import Expect
57−import Html.Attributes
58−import Testable
59−
60−
61−text : String -> Testable.Element msg
62−text =
63− Testable.Text
64−
65−
66−el : List (Testable.Attr msg) -> Testable.Element msg -> Testable.Element msg
67−el =
68− Testable.El
69−
70−
71−row : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
72−row =
73− Testable.Row
74−
75−
76−column : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
77−column =
78− Testable.Column
79−
80−
81−none : Testable.Element msg
82−none =
83− Testable.Empty
84−
85−
86−paragraph : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
87−paragraph =
88− Testable.Paragraph
89−
90−
91−textColumn : List (Testable.Attr msg) -> List (Testable.Element msg) -> Testable.Element msg
92−textColumn =
93− Testable.TextColumn
94−
95−
96−type Length
97− = Px Int
98− | Fill Int
99− | Shrink
100− | Minimum Int Length
101− | Maximum Int Length
102−
103−
104−minimum =
105− Minimum
106−
107−
108−maximum =
109− Maximum
110−
111−
112−px : Int -> Length
113−px =
114− Px
115−
116−
117−fill : Length
118−fill =
119− Fill 1
120−
121−
122−shrink : Length
123−shrink =
124− Shrink
125−
126−
127−fillPortion : Int -> Length
128−fillPortion =
129− Fill
130−
131−
132−label : String -> Testable.Attr msg
133−label =
134− Testable.Label
135−
136−
137−transparent : Bool -> Testable.Attr msg
138−transparent on =
139− Testable.LabeledTest
140− { label =
141− if on then
142− "transparent"
143−
144− else
145− "opaque"
146− , attr = Element.transparent on
147− , test =
148− \context _ ->
149− let
150− selfTransparency =
151− context.self.style
152− |> Dict.get "opacity"
153− |> Maybe.withDefault "notfound"
154−
155− value =
156− if on then
157− "0"
158−
159− else
160− "1"
161− in
162− Expect.equal value selfTransparency
163− }
164−
165−
166−{-| -}
167−isVisible : Testable.Attr msg
168−isVisible =
169− Testable.LabeledTest
170− { label = "is-visible"
171− , attr = Element.htmlAttribute (Html.Attributes.style "not" "applicable")
172− , test =
173− \context _ ->
174− Expect.equal context.self.isVisible True
175− }
176−
177−
178−{-| -}
179−alpha : Float -> Testable.Attr msg
180−alpha a =
181− Testable.LabeledTest
182− { label = "alpha-" ++ String.fromFloat a
183− , attr = Element.alpha a
184− , test =
185− \context _ ->
186− let
187− selfTransparency =
188− context.self.style
189− |> Dict.get "opacity"
190− |> Maybe.withDefault "notfound"
191− in
192− Expect.equal (String.fromFloat a) selfTransparency
193− }
194−
195−
196−{-| -}
197−padding : Int -> Testable.Attr msg
198−padding pad =
199− Testable.LabeledTest
200− { label = "padding " ++ String.fromInt pad
201− , attr = Element.padding pad
202− , test =
203− \found _ ->
204− Expect.true ("Padding " ++ String.fromInt pad ++ " is present")
205− (List.all ((==) pad)
206− [ floor found.self.bbox.padding.left
207− , floor found.self.bbox.padding.right
208− , floor found.self.bbox.padding.top
209− , floor found.self.bbox.padding.bottom
210− ]
211− )
212− }
213−
214−
215−{-| -}
216−paddingXY : Int -> Int -> Testable.Attr msg
217−paddingXY x y =
218− Testable.LabeledTest
219− { label = "paddingXY " ++ String.fromInt x ++ ", " ++ String.fromInt y
220− , attr = Element.paddingXY x y
221− , test =
222− \found _ ->
223− Expect.true ("PaddingXY (" ++ String.fromInt x ++ ", " ++ String.fromInt y ++ ") is present")
224− (List.all ((==) x)
225− [ floor found.self.bbox.padding.left
226− , floor found.self.bbox.padding.right
227− ]
228− && List.all ((==) y)
229− [ floor found.self.bbox.padding.top
230− , floor found.self.bbox.padding.bottom
231− ]
232− )
233− }
234−
235−
236−widthHelper maybeMin maybeMax len =
237− let
238− addMin l =
239− case maybeMin of
240− Nothing ->
241− l
242−
243− Just minPx ->
244− l |> Element.minimum minPx
245−
246− addMax l =
247− case maybeMax of
248− Nothing ->
249− l
250−
251− Just maxPx ->
252− l |> Element.maximum maxPx
253−
254− minLabel =
255− case maybeMin of
256− Nothing ->
257− ""
258−
259− Just i ->
260− " min:" ++ String.fromInt i
261−
262− maxLabel =
263− case maybeMax of
264− Nothing ->
265− ""
266−
267− Just i ->
268− " max:" ++ String.fromInt i
269−
270− minMaxTest actualWidth =
271− case ( maybeMin, maybeMax ) of
272− ( Nothing, Nothing ) ->
273− True
274−
275− ( Just lower, Nothing ) ->
276− lower <= actualWidth
277−
278− ( Just lower, Just higher ) ->
279− lower <= actualWidth && actualWidth <= higher
280−
281− ( Nothing, Just higher ) ->
282− actualWidth <= higher
283− in
284− case len of
285− Minimum m newLen ->
286− widthHelper (Just m) maybeMax newLen
287−
288− Maximum m newLen ->
289− widthHelper maybeMin (Just m) newLen
290−
291− Px val ->
292− -- Pixel values should ignore min and max?
293− Testable.LabeledTest
294− { label = "width " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
295− , attr =
296− Element.width
297− (Element.px val
298− |> addMin
299− |> addMax
300− )
301− , test =
302− \found _ ->
303− Expect.true "exact width is exact"
304− ((floor found.self.bbox.width == val)
305− && minMaxTest (floor found.self.bbox.width)
306− )
307− }
308−
309− Fill portion ->
310− Testable.LabeledTest
311− { label = "width fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
312− , attr =
313− Element.width
314− (Element.fillPortion portion
315− |> addMin
316− |> addMax
317− )
318− , test =
319− \context _ ->
320− if List.member context.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
321− Expect.true "width fill doesn't apply to onright/onleft elements" True
322−
323− else
324− let
325− parentAvailableWidth =
326− context.parent.bbox.width - (context.self.bbox.padding.left + context.self.bbox.padding.right)
327− in
328− case context.location of
329− Testable.IsNearby _ ->
330− Expect.true "Nearby Element has fill width"
331− ((floor context.parent.bbox.width == floor context.self.bbox.width)
332− || minMaxTest (floor context.self.bbox.width)
333− )
334−
335− Testable.InColumn ->
336− Expect.true "Element within column has fill width"
337− ((floor parentAvailableWidth == floor context.self.bbox.width)
338− || minMaxTest (floor context.self.bbox.width)
339− )
340−
341− Testable.InEl ->
342− Expect.true "Element within element has fill width" <|
343− (floor parentAvailableWidth == floor context.self.bbox.width)
344− || minMaxTest (floor context.self.bbox.width)
345−
346− _ ->
347− let
348− spacePerPortion =
349− parentAvailableWidth / toFloat (List.length context.siblings + 1)
350− in
351− Expect.true "element has fill width" <|
352− (floor spacePerPortion == floor context.self.bbox.width)
353− || minMaxTest (floor context.self.bbox.width)
354− }
355−
356− Shrink ->
357− Testable.LabeledTest
358− { label = "width shrink" ++ minLabel ++ maxLabel
359− , attr =
360− Element.width
361− (Element.shrink
362− |> addMin
363− |> addMax
364− )
365− , test =
366− \context _ ->
367− let
368− childWidth child =
369− -- TODO: add margin values to widths
370− child.bbox.width
371−
372− totalChildren =
373− context.children
374− |> List.map childWidth
375− |> List.sum
376−
377− horizontalPadding =
378− context.self.bbox.padding.left + context.self.bbox.padding.right
379−
380− spacingValue =
381− toFloat context.parentSpacing * (toFloat (List.length context.children) - 1)
382− in
383− if totalChildren == 0 then
384− -- TODO: The issue is that we have a hard time measuring `text` elements
385− -- So if a element has a text child, then it's width isn't going to show up in the system.
386− expectRoundedEquality context.self.bbox.width context.self.bbox.width
387−
388− else
389− -- This fails if this element is actually a column
390− -- So we need to capture what this element is in order to do this calculation.
391− expectRoundedEquality (totalChildren + horizontalPadding + spacingValue) context.self.bbox.width
392− }
393−
394−
395−heightHelper maybeMin maybeMax len =
396− let
397− addMin l =
398− case maybeMin of
399− Nothing ->
400− l
401−
402− Just minPx ->
403− l |> Element.minimum minPx
404−
405− addMax l =
406− case maybeMax of
407− Nothing ->
408− l
409−
410− Just maxPx ->
411− l |> Element.maximum maxPx
412−
413− minLabel =
414− case maybeMin of
415− Nothing ->
416− ""
417−
418− Just i ->
419− " min:" ++ String.fromInt i
420−
421− maxLabel =
422− case maybeMax of
423− Nothing ->
424− ""
425−
426− Just i ->
427− " max:" ++ String.fromInt i
428−
429− minMaxTest actualheight =
430− case ( maybeMin, maybeMax ) of
431− ( Nothing, Nothing ) ->
432− True
433−
434− ( Just lower, Nothing ) ->
435− lower <= actualheight
436−
437− ( Just lower, Just higher ) ->
438− lower <= actualheight && actualheight <= higher
439−
440− ( Nothing, Just higher ) ->
441− actualheight <= higher
442− in
443− case len of
444− Minimum m newLen ->
445− heightHelper (Just m) maybeMax newLen
446−
447− Maximum m newLen ->
448− heightHelper maybeMin (Just m) newLen
449−
450− Px val ->
451− -- Pixel values should ignore min and max?
452− Testable.LabeledTest
453− { label = "height " ++ String.fromInt val ++ "px" ++ minLabel ++ maxLabel
454− , attr =
455− Element.height
456− (Element.px val
457− |> addMin
458− |> addMax
459− )
460− , test =
461− \found _ ->
462− Expect.true "exact height is exact"
463− ((floor found.self.bbox.height == val)
464− && minMaxTest (floor found.self.bbox.height)
465− )
466− }
467−
468− Fill portion ->
469− Testable.LabeledTest
470− { label = "height fill-" ++ String.fromInt portion ++ minLabel ++ maxLabel
471− , attr =
472− Element.height
473− (Element.fillPortion portion
474− |> addMin
475− |> addMax
476− )
477− , test =
478− \context _ ->
479− if List.member context.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
480− Expect.true "height fill doesn't apply to above/below elements" True
481−
482− else
483− let
484− parentAvailableHeight =
485− context.parent.bbox.height - (context.self.bbox.padding.top + context.self.bbox.padding.bottom)
486− in
487− case context.location of
488− Testable.IsNearby _ ->
489− Expect.true "Nearby Element has fill height"
490− ((floor context.parent.bbox.height == floor context.self.bbox.height)
491− || minMaxTest (floor context.self.bbox.height)
492− )
493−
494− Testable.InColumn ->
495− Expect.true "Element within column has fill height"
496− ((floor parentAvailableHeight == floor context.self.bbox.height)
497− || minMaxTest (floor context.self.bbox.height)
498− )
499−
500− Testable.InEl ->
501− Expect.true "Element within el has fill height" <|
502− (floor parentAvailableHeight == floor context.self.bbox.height)
503− || minMaxTest (floor context.self.bbox.height)
504−
505− _ ->
506− let
507− spacePerPortion =
508− parentAvailableHeight / toFloat (List.length context.siblings + 1)
509− in
510− Expect.true "el has fill height" <|
511− (floor spacePerPortion == floor context.self.bbox.height)
512− || minMaxTest (floor context.self.bbox.height)
513− }
514−
515− Shrink ->
516− Testable.LabeledTest
517− { label = "height shrink" ++ minLabel ++ maxLabel
518− , attr =
519− Element.height
520− (Element.shrink
521− |> addMin
522− |> addMax
523− )
524− , test =
525− \context _ ->
526− let
527− childWidth child =
528− -- TODO: add margin values to heights
529− child.bbox.height
530−
531− totalChildren =
532− context.children
533− |> List.map childWidth
534− |> List.sum
535−
536− verticalPadding =
537− context.self.bbox.padding.top + context.self.bbox.padding.bottom
538−
539− spacingValue =
540− toFloat context.parentSpacing * (toFloat (List.length context.children) - 1)
541− in
542− if totalChildren == 0 then
543− -- TODO: The issue is that we have a hard time measuring `text` elements
544− -- So if a element has a text child, then it's height isn't going to show up in the system.
545− expectRoundedEquality context.self.bbox.height context.self.bbox.height
546−
547− else
548− -- This fails if this element is actually a column
549− -- So we need to capture what this element is in order to do this calculation.
550− expectRoundedEquality (totalChildren + verticalPadding + spacingValue) context.self.bbox.height
551− }
552−
553−
554−width : Length -> Testable.Attr msg
555−width len =
556− widthHelper Nothing Nothing len
557−
558−
559−height : Length -> Testable.Attr msg
560−height len =
561− heightHelper Nothing Nothing len
562−
563−
564−spacing : Int -> Testable.Attr msg
565−spacing space =
566− Testable.Batch
567− [ Testable.Spacing space
568− , Testable.LabeledTest
569− { label = "spacing: " ++ String.fromInt space
570− , attr = Element.spacing space
571− , test =
572− \found _ ->
573− let
574− findDistance child total =
575− List.concatMap
576− (\otherChild ->
577− let
578− horizontal =
579− round <|
580− child.bbox.left
581− - otherChild.bbox.right
582−
583− vertical =
584− round <|
585− child.bbox.top
586− - otherChild.bbox.bottom
587− in
588− [ if horizontal > 0 then
589− horizontal
590−
591− else
592− space
593− , if vertical > 0 then
594− vertical
595−
596− else
597− space
598− ]
599− )
600− found.children
601− ++ total
602−
603− distances =
604− List.foldl findDistance [] found.children
605−
606− allAreSpaced =
607− List.foldl
608− (\x wrong ->
609− if not (x >= space) then
610− x :: wrong
611−
612− else
613− wrong
614− )
615− []
616− distances
617− in
618− Expect.true
619− ("All children are at least "
620− ++ String.fromInt space
621− ++ " pixels apart."
622− ++ Debug.toString allAreSpaced
623− ++ " are not though"
624− )
625− (allAreSpaced == [])
626− }
627− ]
628−
629−
630−{-| alignLeft needs to account for
631−
632− - parent padding
633− - elements to the left
634− - spacing value
635−
636−in order to calculate the expected result.
637−
638−Also need parent rendering context if this is to work with wrapped rows in the future.
639−
640−All sibling elements
641−
642−-}
643−alignLeft : Testable.Attr msg
644−alignLeft =
645− Testable.LabeledTest
646− { label = "alignLeft"
647− , attr = Element.alignLeft
648− , test =
649− \found _ ->
650− if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
651− Expect.true "alignLeft doesn't apply to elements that are onLeft or onRight" True
652−
653− else if
654− List.member found.location
655− [ Testable.IsNearby Testable.InFront
656− , Testable.IsNearby Testable.Behind
657− , Testable.IsNearby Testable.Above
658− , Testable.IsNearby Testable.Below
659− ]
660− then
661− expectRoundedEquality
662− found.self.bbox.left
663− found.parent.bbox.left
664−
665− else if List.length found.siblings == 0 then
666− expectRoundedEquality
667− found.self.bbox.left
668− (found.parent.bbox.left + found.parent.bbox.padding.left)
669−
670− else
671− case found.location of
672− Testable.InRow ->
673− let
674− siblingsOnLeft =
675− List.filter (\x -> x.bbox.right < found.self.bbox.left) found.siblings
676−
677− spacings =
678− toFloat (List.length siblingsOnLeft * found.parentSpacing)
679−
680− widthsOnLeft =
681− siblingsOnLeft
682− |> List.map (.width << .bbox)
683− |> List.sum
684− in
685− expectRoundedEquality
686− found.self.bbox.left
687− (found.parent.bbox.left + (found.parent.bbox.padding.left + widthsOnLeft + spacings))
688−
689− _ ->
690− expectRoundedEquality
691− found.self.bbox.left
692− (found.parent.bbox.left + found.parent.bbox.padding.left)
693− }
694−
695−
696−{-| -}
697−centerX : Testable.Attr msg
698−centerX =
699− Testable.LabeledTest
700− { label = "centerX"
701− , attr = Element.centerX
702− , test =
703− \found _ ->
704− let
705− selfCenter : Float
706− selfCenter =
707− found.self.bbox.left + (found.self.bbox.width / 2)
708−
709− parentCenter : Float
710− parentCenter =
711− found.parent.bbox.left + (found.parent.bbox.width / 2)
712− in
713− if List.member found.location [ Testable.IsNearby Testable.OnRight, Testable.IsNearby Testable.OnLeft ] then
714− Expect.true "centerX doesn't apply to elements that are onLeft or onRight" True
715−
716− else if List.length found.siblings == 0 then
717− expectRoundedEquality
718− selfCenter
719− parentCenter
720−
721− else
722− case found.location of
723− Testable.InRow ->
724− let
725− siblingsOnLeft =
726− List.filter (\x -> x.bbox.right < found.self.bbox.left) found.siblings
727−
728− siblingsOnRight =
729− List.filter (\x -> x.bbox.left > found.self.bbox.right) found.siblings
730−
731− widthsOnLeft : Float
732− widthsOnLeft =
733− siblingsOnLeft
734− |> List.map (.width << .bbox)
735− |> List.sum
736− |> (\x -> x + (toFloat (List.length siblingsOnLeft) * toFloat found.parentSpacing))
737−
738− widthsOnRight =
739− siblingsOnRight
740− |> List.map (.width << .bbox)
741− |> List.sum
742− |> (\x -> x + (toFloat (List.length siblingsOnRight) * toFloat found.parentSpacing))
743−
744− expectedCenter : Float
745− expectedCenter =
746− found.parent.bbox.left
747− + widthsOnLeft
748− + ((found.parent.bbox.width - (widthsOnRight + widthsOnLeft))
749− / 2
750− )
751− in
752− expectRoundedEquality
753− selfCenter
754− expectedCenter
755−
756− _ ->
757− expectRoundedEquality selfCenter parentCenter
758− }
759−
760−
761−{-| -}
762−alignRight : Testable.Attr msg
763−alignRight =
764− Testable.LabeledTest
765− { label = "alignRight"
766− , attr = Element.alignRight
767− , test =
768− \found _ ->
769− if List.member found.location [ Testable.IsNearby Testable.OnLeft, Testable.IsNearby Testable.OnRight ] then
770− Expect.true "alignRight doesn't apply to elements that are onLeft or onRight" True
771−
772− else if
773− List.member found.location
774− [ Testable.IsNearby Testable.InFront
775− , Testable.IsNearby Testable.Behind
776− , Testable.IsNearby Testable.Above
777− , Testable.IsNearby Testable.Below
778− ]
779− then
780− expectRoundedEquality
781− found.self.bbox.right
782− found.parent.bbox.right
783−
784− else if List.length found.siblings == 0 then
785− expectRoundedEquality
786− found.self.bbox.right
787− (found.parent.bbox.right + found.parent.bbox.padding.right)
788−
789− else
790− case found.location of
791− Testable.InRow ->
792− let
793− siblingsOnRight =
794− List.filter (\x -> x.bbox.left > found.self.bbox.right) found.siblings
795−
796− spacings =
797− toFloat (List.length siblingsOnRight * found.parentSpacing)
798−
799− widthsOnRight =
800− siblingsOnRight
801− |> List.map (.width << .bbox)
802− |> List.sum
803− in
804− expectRoundedEquality
805− found.self.bbox.right
806− (found.parent.bbox.right - (found.parent.bbox.padding.right + widthsOnRight + spacings))
807−
808− _ ->
809− expectRoundedEquality
810− found.self.bbox.right
811− (found.parent.bbox.right + found.parent.bbox.padding.right)
812− }
813−
814−
815−{-| -}
816−alignTop : Testable.Attr msg
817−alignTop =
818− Testable.LabeledTest
819− { label = "alignTop"
820− , attr = Element.alignTop
821− , test =
822− \found _ ->
823− if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
824− Expect.true "alignTop doesn't apply to elements that are above or below" True
825−
826− else if
827− List.member found.location
828− [ Testable.IsNearby Testable.InFront
829− , Testable.IsNearby Testable.Behind
830− , Testable.IsNearby Testable.OnRight
831− , Testable.IsNearby Testable.OnLeft
832− ]
833− then
834− expectRoundedEquality
835− found.self.bbox.top
836− found.parent.bbox.top
837−
838− else if List.length found.siblings == 0 then
839− expectRoundedEquality
840− found.self.bbox.top
841− (found.parent.bbox.top + found.parent.bbox.padding.top)
842−
843− else
844− case found.location of
845− Testable.InColumn ->
846− let
847− siblingsAbove =
848− List.filter (\x -> x.bbox.bottom < found.self.bbox.top) found.siblings
849−
850− spacings =
851− toFloat (List.length siblingsAbove * found.parentSpacing)
852−
853− heightsAbove =
854− siblingsAbove
855− |> List.map (.height << .bbox)
856− |> List.sum
857− in
858− expectRoundedEquality
859− found.self.bbox.top
860− (found.parent.bbox.top + (found.parent.bbox.padding.top + heightsAbove + spacings))
861−
862− _ ->
863− expectRoundedEquality
864− found.self.bbox.top
865− found.parent.bbox.top
866− }
867−
868−
869−{-| -}
870−alignBottom : Testable.Attr msg
871−alignBottom =
872− Testable.LabeledTest
873− { label = "alignBottom"
874− , attr = Element.alignBottom
875− , test =
876− \found _ ->
877− if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
878− Expect.true "alignBottom doesn't apply to elements that are above or below" True
879−
880− else if
881− List.member found.location
882− [ Testable.IsNearby Testable.InFront
883− , Testable.IsNearby Testable.Behind
884− , Testable.IsNearby Testable.OnRight
885− , Testable.IsNearby Testable.OnLeft
886− ]
887− then
888− expectRoundedEquality
889− found.self.bbox.bottom
890− found.parent.bbox.bottom
891−
892− else if List.length found.siblings == 0 then
893− expectRoundedEquality
894− found.self.bbox.bottom
895− (found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
896−
897− else
898− case found.location of
899− Testable.InColumn ->
900− let
901− siblingsBelow =
902− List.filter (\x -> x.bbox.top > found.self.bbox.bottom) found.siblings
903−
904− spacings =
905− toFloat (List.length siblingsBelow * found.parentSpacing)
906−
907− heightsBelow =
908− siblingsBelow
909− |> List.map (.height << .bbox)
910− |> List.sum
911− in
912− expectRoundedEquality
913− found.self.bbox.bottom
914− (found.parent.bbox.bottom - (found.parent.bbox.padding.bottom + heightsBelow + spacings))
915−
916− _ ->
917− expectRoundedEquality
918− found.self.bbox.bottom
919− (found.parent.bbox.bottom + found.parent.bbox.padding.bottom)
920− }
921−
922−
923−expectRoundedEquality x y =
924− Expect.true ("within 1 of each other " ++ String.fromFloat x ++ ":" ++ String.fromFloat y)
925− (abs (x - y) < 1)
926−
927−
928−{-| -}
929−centerY : Testable.Attr msg
930−centerY =
931− Testable.LabeledTest
932− { label = "centerY"
933− , attr = Element.centerY
934− , test =
935− \found _ ->
936− let
937− selfCenter =
938− found.self.bbox.top + (found.self.bbox.height / 2)
939−
940− parentCenter =
941− found.parent.bbox.top + (found.parent.bbox.height / 2)
942− in
943− if List.member found.location [ Testable.IsNearby Testable.Above, Testable.IsNearby Testable.Below ] then
944− Expect.true "centerY doesn't apply to elements that are above or below" True
945−
946− else if List.length found.siblings == 0 then
947− expectRoundedEquality
948− selfCenter
949− parentCenter
950−
951− else
952− case found.location of
953− Testable.InColumn ->
954− let
955− siblingsOnTop =
956− List.filter (\x -> x.bbox.bottom < found.self.bbox.top) found.siblings
957−
958− siblingsBelow =
959− List.filter (\x -> x.bbox.top > found.self.bbox.bottom) found.siblings
960−
961− heightsAbove : Float
962− heightsAbove =
963− siblingsOnTop
964− |> List.map (.height << .bbox)
965− |> List.sum
966− |> (\x -> x + (toFloat (List.length siblingsOnTop) * toFloat found.parentSpacing))
967−
968− heightsBelow =
969− siblingsBelow
970− |> List.map (.height << .bbox)
971− |> List.sum
972− |> (\x -> x + (toFloat (List.length siblingsBelow) * toFloat found.parentSpacing))
973−
974− expectedCenter : Float
975− expectedCenter =
976− found.parent.bbox.top
977− + heightsAbove
978− + ((found.parent.bbox.height - (heightsBelow + heightsAbove))
979− / 2
980− )
981− in
982− expectRoundedEquality
983− selfCenter
984− expectedCenter
985−
986− _ ->
987− expectRoundedEquality selfCenter parentCenter
988− }
989−
990−
991−{-| -}
992−above : Testable.Element msg -> Testable.Attr msg
993−above element =
994− Testable.Nearby
995− { location = Testable.Above
996− , element = element
997− , label = "above"
998− , test =
999− \found _ ->
1000− expectRoundedEquality found.self.bbox.bottom found.parent.bbox.top
1001− }
1002−
1003−
1004−{-| -}
1005−below : Testable.Element msg -> Testable.Attr msg
1006−below element =
1007− Testable.Nearby
1008− { location = Testable.Below
1009− , element = element
1010− , label = "below"
1011− , test =
1012− \found _ ->
1013− expectRoundedEquality found.self.bbox.top found.parent.bbox.bottom
1014− }
1015−
1016−
1017−{-| -}
1018−onRight : Testable.Element msg -> Testable.Attr msg
1019−onRight element =
1020− Testable.Nearby
1021− { location = Testable.OnRight
1022− , element = element
1023− , label = "onRight"
1024− , test =
1025− \found _ ->
1026− expectRoundedEquality found.self.bbox.left found.parent.bbox.right
1027− }
1028−
1029−
1030−{-| -}
1031−onLeft : Testable.Element msg -> Testable.Attr msg
1032−onLeft element =
1033− Testable.Nearby
1034− { location = Testable.OnLeft
1035− , element = element
1036− , label = "onLeft"
1037− , test =
1038− \found _ ->
1039− expectRoundedEquality found.self.bbox.right found.parent.bbox.left
1040− }
1041−
1042−
1043−compare x vs y =
1044− vs (round x) (round y) || vs (floor x) (floor y)
1045−
1046−
1047−{-| -}
1048−inFront : Testable.Element msg -> Testable.Attr msg
1049−inFront element =
1050− Testable.Nearby
1051− { location = Testable.InFront
1052− , element = element
1053− , label = "inFront"
1054− , test =
1055− withinHelper
1056− }
1057−
1058−
1059−withinHelper found _ =
1060− let
1061− horizontalCheck =
1062− if found.self.bbox.width > found.parent.bbox.width then
1063− [ compare found.self.bbox.right (<=) found.parent.bbox.right
1064− || compare found.self.bbox.left (>=) found.parent.bbox.left
1065− ]
1066−
1067− else
1068− [ compare found.self.bbox.right (<=) found.parent.bbox.right
1069− , compare found.self.bbox.left (>=) found.parent.bbox.left
1070− ]
1071−
1072− verticalCheck =
1073− if found.self.bbox.width > found.parent.bbox.width then
1074− [ compare found.self.bbox.top (>=) found.parent.bbox.top
1075− || compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
1076− ]
1077−
1078− else
1079− [ compare found.self.bbox.top (>=) found.parent.bbox.top
1080− , compare found.self.bbox.bottom (<=) found.parent.bbox.bottom
1081− ]
1082− in
1083− Expect.true "within the confines of the parent"
1084− (List.all ((==) True)
1085− (List.concat
1086− [ horizontalCheck
1087− , verticalCheck
1088− ]
1089− )
1090− )
1091−
1092−
1093−{-| -}
1094−behindContent : Testable.Element msg -> Testable.Attr msg
1095−behindContent element =
1096− Testable.Nearby
1097− { location = Testable.Behind
1098− , element = element
1099− , label = "behindContent"
1100− , test =
1101− withinHelper
1102− }
tests/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/src/Testable/Runner.elmdeleted−462
@@ -1,462 +0,0 @@
1−port module Testable.Runner exposing (TestableProgram, program, show)
2−
3−{-| -}
4−
5−import Browser
6−import Char
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 Html exposing (Html)
13−import Parser exposing ((|.), (|=))
14−import Process
15−import Random
16−import Set
17−import Task
18−import Test.Runner
19−import Test.Runner.Failure as Failure
20−import Testable
21−import Time
22−
23−
24−show : Testable.Element msg -> Html msg
25−show =
26− Testable.render
27−
28−
29−type alias TestableProgram =
30− Program () (Model Msg) Msg
31−
32−
33−palette =
34− { white = Element.rgb 1 1 1
35− , red = Element.rgb 1 0 0
36− , green = Element.rgb 0 1 0
37− , black = Element.rgb 0 0 0
38− , lightGrey = Element.rgb 0.7 0.7 0.7
39− }
40−
41−
42−program : List ( String, Testable.Element Msg ) -> TestableProgram
43−program tests =
44− let
45− ( current, upcoming ) =
46− case tests of
47− [] ->
48− ( Nothing, [] )
49−
50− cur :: remaining ->
51− ( Just cur, remaining )
52− in
53− Browser.element
54− { init =
55− always
56− ( { current = current
57− , upcoming = upcoming
58− , finished = []
59− }
60− , Task.perform (always Analyze)
61− (Process.sleep 32
62− |> Task.andThen
63− (always Time.now)
64− )
65− )
66− , view = view
67− , update = update
68− , subscriptions = subscriptions
69− }
70−
71−
72−subscriptions : Model Msg -> Sub Msg
73−subscriptions model =
74− Sub.batch
75− [ styles RefreshBoundingBox
76− ]
77−
78−
79−type alias Model msg =
80− { current : Maybe ( String, Testable.Element msg )
81− , upcoming : List ( String, Testable.Element msg )
82− , finished : List (WithResults (Testable.Element msg))
83− }
84−
85−
86−type alias WithResults thing =
87− { element : thing
88− , label : String
89− , results :
90− List
91− ( String
92− , Maybe
93− { given : Maybe String
94− , description : String
95− , reason : Failure.Reason
96− }
97− )
98− }
99−
100−
101−prepareResults :
102− List (WithResults (Testable.Element msg))
103− ->
104− List
105− { label : String
106− , results :
107− List
108− ( String
109− , Maybe
110− { given : Maybe String
111− , description : String
112− }
113− )
114− }
115−prepareResults withResults =
116− let
117− prepareNode ( x, maybeResult ) =
118− ( x
119− , case maybeResult of
120− Nothing ->
121− Nothing
122−
123− Just res ->
124− Just
125− { given = res.given
126− , description = res.description
127− }
128− )
129−
130− prepare { label, results } =
131− { label = label
132− , results = List.map prepareNode results
133− }
134− in
135− List.map prepare withResults
136−
137−
138−type Msg
139− = NoOp
140− | Analyze
141− | RefreshBoundingBox
142− (List
143− { id : String
144− , bbox : Testable.BoundingBox
145− , isVisible : Bool
146− , style : List ( String, String )
147− }
148− )
149−
150−
151−runTest : Dict String Testable.Found -> String -> Testable.Element msg -> WithResults (Testable.Element msg)
152−runTest boxes label element =
153− let
154− tests =
155− Testable.toTest label boxes element
156−
157− seed =
158− Random.initialSeed 227852860
159−
160− results =
161− Testable.runTests seed tests
162− in
163− { element = element
164− , label = label
165− , results = results
166− }
167−
168−
169−update : Msg -> Model Msg -> ( Model Msg, Cmd Msg )
170−update msg model =
171− case msg of
172− NoOp ->
173− ( model, Cmd.none )
174−
175− RefreshBoundingBox boxes ->
176− case model.current of
177− Nothing ->
178− ( model
179− , Cmd.none
180− )
181−
182− Just ( label, current ) ->
183− let
184− toTuple box =
185− ( box.id
186− , { style = Dict.fromList box.style
187− , bbox = box.bbox
188− , isVisible = box.isVisible
189− }
190− )
191−
192− foundData =
193− boxes
194− |> List.map toTuple
195− |> Dict.fromList
196−
197− currentResults =
198− runTest foundData label current
199− in
200− case model.upcoming of
201− [] ->
202− ( { model
203− | current = Nothing
204− , finished = model.finished ++ [ currentResults ]
205− }
206− , report (prepareResults (currentResults :: model.finished))
207− )
208−
209− newCurrent :: remaining ->
210− ( { model
211− | finished = model.finished ++ [ currentResults ]
212− , current = Just newCurrent
213− , upcoming = remaining
214− }
215− , Task.perform (always Analyze)
216− (Process.sleep 32
217− |> Task.andThen
218− (always Time.now)
219− )
220− )
221−
222− Analyze ->
223− case model.current of
224− Nothing ->
225− ( model
226− , Cmd.none
227− )
228−
229− Just ( label, current ) ->
230− ( model
231− , analyze (Testable.getIds current)
232− )
233−
234−
235−view : Model Msg -> Html Msg
236−view model =
237− case model.current of
238− Nothing ->
239− if List.isEmpty model.upcoming then
240− -- (List.map viewResult model.finished)
241− case model.finished of
242− [] ->
243− Element.layout [] <|
244− Element.column
245− [ Element.spacing 20
246− , Element.padding 20
247− , Element.width (Element.px 800)
248−
249− -- , Background.color Color.grey
250− ]
251− [ Element.none ]
252−
253− finished :: remaining ->
254− if False then
255− viewResultsInline finished
256−
257− else
258− Element.layout [] <|
259− Element.column
260− [ Element.spacing 20
261− , Element.padding 20
262− , Element.width (Element.px 800)
263− ]
264− (List.map viewResult (finished :: remaining))
265−
266− else
267− Html.text "running?"
268−
269− Just ( label, current ) ->
270− Testable.render current
271−
272−
273−viewResultsInline : WithResults (Testable.Element Msg) -> Html Msg
274−viewResultsInline testable =
275− Html.div
276− []
277− [ viewResultsAnnotationStylesheet testable.results
278− , Testable.render testable.element
279− ]
280−
281−
282−{-| Our ID is part of our label. This could be fixed farther down the chain, but I think it'd be pretty involved.
283−
284−So, now we can just parse the id out of the label.
285−
286−-}
287−parseId str =
288− str
289− |> Parser.run
290− (Parser.succeed identity
291− |. Parser.chompWhile (\c -> c /= '#')
292− |= Parser.variable
293− { start = \c -> c == '#'
294− , inner = \c -> Char.isAlphaNum c || c == '-'
295− , reserved = Set.empty
296− }
297− )
298− |> Result.toMaybe
299−
300−
301−viewResultsAnnotationStylesheet results =
302− let
303− toStyleClass ( label, maybeFailure ) =
304− case maybeFailure of
305− Nothing ->
306− ""
307−
308− Just failure ->
309− case parseId label of
310− Nothing ->
311− Debug.log "NO ID FOUND" label
312−
313− Just id ->
314− id ++ " { background-color:red; outline: dashed; };"
315−
316− styleSheet =
317− results
318− |> List.map toStyleClass
319− |> String.join ""
320− in
321− Html.node "style"
322− []
323− [ Html.text styleSheet
324− ]
325−
326−
327−viewResult : WithResults (Testable.Element Msg) -> Element.Element Msg
328−viewResult testable =
329− let
330− isPassing result =
331− case Tuple.second result of
332− Nothing ->
333− True
334−
335− Just _ ->
336− False
337−
338− ( passing, failing ) =
339− List.partition isPassing testable.results
340−
341− viewSingle result =
342− case result of
343− ( label, Nothing ) ->
344− Element.el
345− [ Background.color palette.green
346− , Font.color palette.black
347− , Element.paddingXY 20 10
348− , Element.alignLeft
349− , Border.rounded 3
350− ]
351− <|
352− Element.text ("Success! - " ++ label)
353−
354− ( label, Just ({ given, description } as reason) ) ->
355− Element.column
356− [ Background.color palette.red
357− , Font.color palette.black
358− , Element.paddingXY 20 10
359− , Element.alignLeft
360− , Element.width Element.shrink
361−
362− -- , Element.spacing 25
363− , Border.rounded 3
364− ]
365− [ Element.el [ Element.width Element.fill ] <| Element.text label
366− , Element.el [ Element.width Element.fill ] <| Element.text (viewReason reason)
367− ]
368− in
369− Element.column
370− [ Border.width 1
371− , Border.color palette.lightGrey
372− , Element.padding 20
373− , Element.height Element.shrink
374− , Element.alignLeft
375− , Element.spacing 16
376− ]
377− [ Element.el [ Font.bold, Font.size 64 ] (Element.text testable.label)
378− , Element.column [ Element.alignLeft, Element.spacing 20 ]
379− (failing
380− |> List.map viewSingle
381− )
382− , Element.el
383− [ Element.alignLeft
384− , Element.spacing 20
385− , Background.color palette.green
386− , Font.color palette.black
387− , Element.paddingXY 20 10
388− , Element.alignLeft
389− , Border.rounded 3
390− ]
391− (Element.text (String.fromInt (List.length passing) ++ " tests passing!"))
392− ]
393−
394−
395−viewReason { description, reason } =
396− case reason of
397− Failure.Custom ->
398− description
399−
400− Failure.Equality one two ->
401− description ++ " " ++ one ++ " " ++ two
402−
403− Failure.Comparison one two ->
404− description ++ " " ++ one ++ " " ++ two
405−
406− Failure.ListDiff expected actual ->
407− "expected\n"
408− ++ String.join " \n" expected
409− ++ "actual\n"
410− ++ String.join " \n" actual
411−
412− Failure.CollectionDiff { expected, actual, extra, missing } ->
413− String.join "\n"
414− [ formatKeyValue "expected" expected
415− , formatKeyValue "actual" actual
416− , formatKeyValue "extra" (String.join ", " extra)
417− , formatKeyValue "missing" (String.join ", " missing)
418− ]
419−
420− Failure.TODO ->
421− description
422−
423− Failure.Invalid _ ->
424− description
425−
426−
427−formatKeyValue : String -> String -> String
428−formatKeyValue key val =
429− key ++ ": " ++ val
430−
431−
432−port report :
433− List
434− { label : String
435− , results :
436− List
437− ( String
438− , Maybe
439− { given : Maybe String
440− , description : String
441− }
442− )
443− }
444− -> Cmd msg
445−
446−
447−port test : String -> Cmd msg
448−
449−
450−port analyze : List String -> Cmd msg
451−
452−
453−port styles :
454− (List
455− { id : String
456− , bbox : Testable.BoundingBox
457− , style : List ( String, String )
458− , isVisible : Bool
459− }
460− -> msg
461− )
462− -> Sub msg
tests/suite/elm-package.jsondeleted−16
@@ -1,16 +0,0 @@
1−{
2− "version": "1.0.0",
3− "summary": "helpful summary of your project, less than 80 characters",
4− "repository": "https://github.com/user/project.git",
5− "license": "BSD3",
6− "source-directories": [
7− ".",
8− "../../src"
9− ],
10− "exposed-modules": [],
11− "dependencies": {
12− "elm-lang/core": "5.1.1 <= v < 6.0.0",
13− "elm-lang/html": "2.0.0 <= v < 3.0.0"
14− },
15− "elm-version": "0.18.0 <= v < 0.19.0"
16−}
yarn.locknew file+1271
@@ -0,0 +1,1271 @@
1+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+# yarn lockfile v1
3+
4+
5+"@types/color-name@^1.1.1":
6+ version "1.1.1"
7+ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
8+ integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
9+
10+agent-base@^4.1.0:
11+ version "4.3.0"
12+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
13+ integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
14+ dependencies:
15+ es6-promisify "^5.0.0"
16+
17+ajv@^6.5.5:
18+ version "6.12.0"
19+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
20+ integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
21+ dependencies:
22+ fast-deep-equal "^3.1.1"
23+ fast-json-stable-stringify "^2.0.0"
24+ json-schema-traverse "^0.4.1"
25+ uri-js "^4.2.2"
26+
27+ansi-styles@^3.2.1:
28+ version "3.2.1"
29+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
30+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
31+ dependencies:
32+ color-convert "^1.9.0"
33+
34+ansi-styles@^4.1.0:
35+ version "4.2.1"
36+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
37+ integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
38+ dependencies:
39+ "@types/color-name" "^1.1.1"
40+ color-convert "^2.0.1"
41+
42+anymatch@~3.1.1:
43+ version "3.1.1"
44+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
45+ integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
46+ dependencies:
47+ normalize-path "^3.0.0"
48+ picomatch "^2.0.4"
49+
50+asn1@~0.2.3:
51+ version "0.2.4"
52+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
53+ integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
54+ dependencies:
55+ safer-buffer "~2.1.0"
56+
57+assert-plus@1.0.0, assert-plus@^1.0.0:
58+ version "1.0.0"
59+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
60+ integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
61+
62+async-limiter@~1.0.0:
63+ version "1.0.0"
64+ resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
65+ integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
66+
67+asynckit@^0.4.0:
68+ version "0.4.0"
69+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
70+ integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
71+
72+aws-sign2@~0.7.0:
73+ version "0.7.0"
74+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
75+ integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
76+
77+aws4@^1.8.0:
78+ version "1.9.1"
79+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
80+ integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
81+
82+balanced-match@^1.0.0:
83+ version "1.0.0"
84+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
85+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
86+
87+bcrypt-pbkdf@^1.0.0:
88+ version "1.0.2"
89+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
90+ integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
91+ dependencies:
92+ tweetnacl "^0.14.3"
93+
94+binary-extensions@^2.0.0:
95+ version "2.0.0"
96+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
97+ integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
98+
99+binary@^0.3.0:
100+ version "0.3.0"
101+ resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79"
102+ integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=
103+ dependencies:
104+ buffers "~0.1.1"
105+ chainsaw "~0.1.0"
106+
107+binwrap@0.2.2:
108+ version "0.2.2"
109+ resolved "https://registry.yarnpkg.com/binwrap/-/binwrap-0.2.2.tgz#7d1ea74b28332f18dfdc75548aef993041ffafc9"
110+ integrity sha512-Y+Wvypk3JhH5GPZAvlwJAWOVH/OsOhQMSj37vySuWHwQivoALplPxfBA8b973rFJI7OS+O+1YmmYXIiEXVMAcw==
111+ dependencies:
112+ mustache "^3.0.1"
113+ request "^2.88.0"
114+ request-promise "^4.2.4"
115+ tar "^4.4.10"
116+ unzip-stream "^0.3.0"
117+
118+bluebird@^3.5.0:
119+ version "3.7.2"
120+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
121+ integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
122+
123+brace-expansion@^1.1.7:
124+ version "1.1.11"
125+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
126+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
127+ dependencies:
128+ balanced-match "^1.0.0"
129+ concat-map "0.0.1"
130+
131+braces@~3.0.2:
132+ version "3.0.2"
133+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
134+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
135+ dependencies:
136+ fill-range "^7.0.1"
137+
138+buffer-from@^1.0.0:
139+ version "1.1.1"
140+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
141+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
142+
143+buffers@~0.1.1:
144+ version "0.1.1"
145+ resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
146+ integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s=
147+
148+caseless@~0.12.0:
149+ version "0.12.0"
150+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
151+ integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
152+
153+chainsaw@~0.1.0:
154+ version "0.1.0"
155+ resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
156+ integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=
157+ dependencies:
158+ traverse ">=0.3.0 <0.4"
159+
160+chalk@3.0.0:
161+ version "3.0.0"
162+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
163+ integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
164+ dependencies:
165+ ansi-styles "^4.1.0"
166+ supports-color "^7.1.0"
167+
168+chalk@^2.4.2:
169+ version "2.4.2"
170+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
171+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
172+ dependencies:
173+ ansi-styles "^3.2.1"
174+ escape-string-regexp "^1.0.5"
175+ supports-color "^5.3.0"
176+
177+chokidar@3.3.0:
178+ version "3.3.0"
179+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6"
180+ integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==
181+ dependencies:
182+ anymatch "~3.1.1"
183+ braces "~3.0.2"
184+ glob-parent "~5.1.0"
185+ is-binary-path "~2.1.0"
186+ is-glob "~4.0.1"
187+ normalize-path "~3.0.0"
188+ readdirp "~3.2.0"
189+ optionalDependencies:
190+ fsevents "~2.1.1"
191+
192+chownr@^1.1.1:
193+ version "1.1.4"
194+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
195+ integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
196+
197+color-convert@^1.9.0:
198+ version "1.9.3"
199+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
200+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
201+ dependencies:
202+ color-name "1.1.3"
203+
204+color-convert@^2.0.1:
205+ version "2.0.1"
206+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
207+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
208+ dependencies:
209+ color-name "~1.1.4"
210+
211+color-name@1.1.3:
212+ version "1.1.3"
213+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
214+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
215+
216+color-name@~1.1.4:
217+ version "1.1.4"
218+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
219+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
220+
221+combined-stream@^1.0.6, combined-stream@~1.0.6:
222+ version "1.0.8"
223+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
224+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
225+ dependencies:
226+ delayed-stream "~1.0.0"
227+
228+commander@^2.20.0:
229+ version "2.20.0"
230+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
231+ integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
232+
233+concat-map@0.0.1:
234+ version "0.0.1"
235+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
236+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
237+
238+concat-stream@1.6.2:
239+ version "1.6.2"
240+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
241+ integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
242+ dependencies:
243+ buffer-from "^1.0.0"
244+ inherits "^2.0.3"
245+ readable-stream "^2.2.2"
246+ typedarray "^0.0.6"
247+
248+core-util-is@1.0.2, core-util-is@~1.0.0:
249+ version "1.0.2"
250+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
251+ integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
252+
253+cross-spawn@4.0.0:
254+ version "4.0.0"
255+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.0.tgz#8254774ab4786b8c5b3cf4dfba66ce563932c252"
256+ integrity sha1-glR3SrR4a4xbPPTfumbOVjkywlI=
257+ dependencies:
258+ lru-cache "^4.0.1"
259+ which "^1.2.9"
260+
261+cross-spawn@6.0.5:
262+ version "6.0.5"
263+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
264+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
265+ dependencies:
266+ nice-try "^1.0.4"
267+ path-key "^2.0.1"
268+ semver "^5.5.0"
269+ shebang-command "^1.2.0"
270+ which "^1.2.9"
271+
272+cross-spawn@7.0.1:
273+ version "7.0.1"
274+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14"
275+ integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==
276+ dependencies:
277+ path-key "^3.1.0"
278+ shebang-command "^2.0.0"
279+ which "^2.0.1"
280+
281+dashdash@^1.12.0:
282+ version "1.14.1"
283+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
284+ integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
285+ dependencies:
286+ assert-plus "^1.0.0"
287+
288+debug@2.6.9:
289+ version "2.6.9"
290+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
291+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
292+ dependencies:
293+ ms "2.0.0"
294+
295+debug@^3.1.0:
296+ version "3.2.6"
297+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
298+ integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
299+ dependencies:
300+ ms "^2.1.1"
301+
302+debug@^4.1.0:
303+ version "4.1.1"
304+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
305+ integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
306+ dependencies:
307+ ms "^2.1.1"
308+
309+delayed-stream@~1.0.0:
310+ version "1.0.0"
311+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
312+ integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
313+
314+ecc-jsbn@~0.1.1:
315+ version "0.1.2"
316+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
317+ integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
318+ dependencies:
319+ jsbn "~0.1.0"
320+ safer-buffer "^2.1.0"
321+
322+elm-test@^0.19.1-revision2:
323+ version "0.19.1-revision2"
324+ resolved "https://registry.yarnpkg.com/elm-test/-/elm-test-0.19.1-revision2.tgz#e73a7240ac6fd997279c825b7dc342fb65349028"
325+ integrity sha512-zVs2mVeyIE+K9y7/8b333h5xRMDWAoqbBDm7ThLDhyTi7ICxeL3t5uOS4KZCrRk9+4sP6+voSbcBlgr46Q+GiQ==
326+ dependencies:
327+ chalk "3.0.0"
328+ chokidar "3.3.0"
329+ cross-spawn "7.0.1"
330+ elmi-to-json "1.3.0"
331+ find-parent-dir "^0.3.0"
332+ firstline "2.0.2"
333+ fs-extra "8.1.0"
334+ glob "7.1.6"
335+ lodash "4.17.15"
336+ minimist "^1.2.0"
337+ murmur-hash-js "1.0.0"
338+ node-elm-compiler "5.0.4"
339+ split "1.0.1"
340+ supports-color "7.1.0"
341+ temp "0.9.1"
342+ which "2.0.1"
343+ xmlbuilder "^13.0.2"
344+
345+elmi-to-json@1.3.0:
346+ version "1.3.0"
347+ resolved "https://registry.yarnpkg.com/elmi-to-json/-/elmi-to-json-1.3.0.tgz#9de59d66d1df50f775a83405610ad41861a4305a"
348+ integrity sha512-6m1D5/Pb5pUrSOOBgRG3fE2mK19nhmLgZ16jj2KWTVIhT+0GIBuDI1iV0Fee27CZH790J7uMcdGWJ7fnVvpsKg==
349+ dependencies:
350+ binwrap "0.2.2"
351+
352+es6-promise@^4.0.3:
353+ version "4.2.8"
354+ resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
355+ integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
356+
357+es6-promisify@^5.0.0:
358+ version "5.0.0"
359+ resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
360+ integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
361+ dependencies:
362+ es6-promise "^4.0.3"
363+
364+escape-string-regexp@^1.0.5:
365+ version "1.0.5"
366+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
367+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
368+
369+extend@~3.0.2:
370+ version "3.0.2"
371+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
372+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
373+
374+extract-zip@^1.6.6:
375+ version "1.6.7"
376+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
377+ integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
378+ dependencies:
379+ concat-stream "1.6.2"
380+ debug "2.6.9"
381+ mkdirp "0.5.1"
382+ yauzl "2.4.1"
383+
384+extsprintf@1.3.0:
385+ version "1.3.0"
386+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
387+ integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
388+
389+extsprintf@^1.2.0:
390+ version "1.4.0"
391+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
392+ integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
393+
394+fast-deep-equal@^3.1.1:
395+ version "3.1.1"
396+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
397+ integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
398+
399+fast-json-stable-stringify@^2.0.0:
400+ version "2.1.0"
401+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
402+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
403+
404+fd-slicer@~1.0.1:
405+ version "1.0.1"
406+ resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
407+ integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=
408+ dependencies:
409+ pend "~1.2.0"
410+
411+fill-range@^7.0.1:
412+ version "7.0.1"
413+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
414+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
415+ dependencies:
416+ to-regex-range "^5.0.1"
417+
418+find-elm-dependencies@2.0.1:
419+ version "2.0.1"
420+ resolved "https://registry.yarnpkg.com/find-elm-dependencies/-/find-elm-dependencies-2.0.1.tgz#802e4e58379ad7ee7f30e048f8695ab732cf8132"
421+ integrity sha512-KBxPjc7J1CLw90jVateMKvMYMROZRTR7/QT2I3MxT+7I6KuUQUMNUFuS/eQXQnMnyElGKQ1iyPbe7GnEuYnFXw==
422+ dependencies:
423+ firstline "1.2.0"
424+ lodash "4.17.11"
425+
426+find-elm-dependencies@2.0.2:
427+ version "2.0.2"
428+ resolved "https://registry.yarnpkg.com/find-elm-dependencies/-/find-elm-dependencies-2.0.2.tgz#589a759a021e6e2f8f0df805f973313fc4b55693"
429+ integrity sha512-nM5UCbccD1G8CGK2GsM7ykG3ksOAl9E+34jiDfl07CAl2OPnLpBVWY2hlxEmIkSBfdJjSopEowWHrO0cI8RhxQ==
430+ dependencies:
431+ firstline "1.2.0"
432+ lodash "4.17.15"
433+
434+find-parent-dir@^0.3.0:
435+ version "0.3.0"
436+ resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54"
437+ integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=
438+
439+firstline@1.2.0:
440+ version "1.2.0"
441+ resolved "https://registry.yarnpkg.com/firstline/-/firstline-1.2.0.tgz#c9f4886e7f7fbf0afc12d71941dce06b192aea05"
442+ integrity sha1-yfSIbn9/vwr8EtcZQdzgaxkq6gU=
443+
444+firstline@2.0.2:
445+ version "2.0.2"
446+ resolved "https://registry.yarnpkg.com/firstline/-/firstline-2.0.2.tgz#3fdfd894a80e181cd2fa478b07cadb8d446c53cd"
447+ integrity sha512-8KcmfI0jgSECnzdhucm0i7vrwef3BWwgjimW2YkRC5eSFwjb5DibVoA0YvgkYwwxuJi9c+7M7X3b3lX8o9B6wg==
448+
449+forever-agent@~0.6.1:
450+ version "0.6.1"
451+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
452+ integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
453+
454+form-data@~2.3.2:
455+ version "2.3.3"
456+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
457+ integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
458+ dependencies:
459+ asynckit "^0.4.0"
460+ combined-stream "^1.0.6"
461+ mime-types "^2.1.12"
462+
463+fs-extra@8.1.0:
464+ version "8.1.0"
465+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
466+ integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
467+ dependencies:
468+ graceful-fs "^4.2.0"
469+ jsonfile "^4.0.0"
470+ universalify "^0.1.0"
471+
472+fs-minipass@^1.2.5:
473+ version "1.2.7"
474+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
475+ integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
476+ dependencies:
477+ minipass "^2.6.0"
478+
479+fs.realpath@^1.0.0:
480+ version "1.0.0"
481+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
482+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
483+
484+fsevents@~2.1.1:
485+ version "2.1.2"
486+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805"
487+ integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==
488+
489+getpass@^0.1.1:
490+ version "0.1.7"
491+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
492+ integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
493+ dependencies:
494+ assert-plus "^1.0.0"
495+
496+glob-parent@~5.1.0:
497+ version "5.1.0"
498+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2"
499+ integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==
500+ dependencies:
501+ is-glob "^4.0.1"
502+
503+glob@7.1.6, glob@^7.1.3:
504+ version "7.1.6"
505+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
506+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
507+ dependencies:
508+ fs.realpath "^1.0.0"
509+ inflight "^1.0.4"
510+ inherits "2"
511+ minimatch "^3.0.4"
512+ once "^1.3.0"
513+ path-is-absolute "^1.0.0"
514+
515+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
516+ version "4.2.3"
517+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
518+ integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
519+
520+har-schema@^2.0.0:
521+ version "2.0.0"
522+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
523+ integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
524+
525+har-validator@~5.1.3:
526+ version "5.1.3"
527+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
528+ integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
529+ dependencies:
530+ ajv "^6.5.5"
531+ har-schema "^2.0.0"
532+
533+has-flag@^3.0.0:
534+ version "3.0.0"
535+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
536+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
537+
538+has-flag@^4.0.0:
539+ version "4.0.0"
540+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
541+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
542+
543+http-signature@~1.2.0:
544+ version "1.2.0"
545+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
546+ integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
547+ dependencies:
548+ assert-plus "^1.0.0"
549+ jsprim "^1.2.2"
550+ sshpk "^1.7.0"
551+
552+https-proxy-agent@^2.2.1:
553+ version "2.2.1"
554+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
555+ integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
556+ dependencies:
557+ agent-base "^4.1.0"
558+ debug "^3.1.0"
559+
560+immediate@~3.0.5:
561+ version "3.0.6"
562+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
563+ integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
564+
565+inflight@^1.0.4:
566+ version "1.0.6"
567+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
568+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
569+ dependencies:
570+ once "^1.3.0"
571+ wrappy "1"
572+
573+inherits@2, inherits@^2.0.3, inherits@~2.0.3:
574+ version "2.0.4"
575+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
576+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
577+
578+is-binary-path@~2.1.0:
579+ version "2.1.0"
580+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
581+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
582+ dependencies:
583+ binary-extensions "^2.0.0"
584+
585+is-extglob@^2.1.1:
586+ version "2.1.1"
587+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
588+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
589+
590+is-glob@^4.0.1, is-glob@~4.0.1:
591+ version "4.0.1"
592+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
593+ integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
594+ dependencies:
595+ is-extglob "^2.1.1"
596+
597+is-number@^7.0.0:
598+ version "7.0.0"
599+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
600+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
601+
602+is-typedarray@~1.0.0:
603+ version "1.0.0"
604+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
605+ integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
606+
607+isarray@~1.0.0:
608+ version "1.0.0"
609+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
610+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
611+
612+isexe@^2.0.0:
613+ version "2.0.0"
614+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
615+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
616+
617+isstream@~0.1.2:
618+ version "0.1.2"
619+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
620+ integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
621+
622+jsbn@~0.1.0:
623+ version "0.1.1"
624+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
625+ integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
626+
627+json-schema-traverse@^0.4.1:
628+ version "0.4.1"
629+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
630+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
631+
632+json-schema@0.2.3:
633+ version "0.2.3"
634+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
635+ integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
636+
637+json-stringify-safe@~5.0.1:
638+ version "5.0.1"
639+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
640+ integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
641+
642+jsonfile@^4.0.0:
643+ version "4.0.0"
644+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
645+ integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
646+ optionalDependencies:
647+ graceful-fs "^4.1.6"
648+
649+jsprim@^1.2.2:
650+ version "1.4.1"
651+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
652+ integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
653+ dependencies:
654+ assert-plus "1.0.0"
655+ extsprintf "1.3.0"
656+ json-schema "0.2.3"
657+ verror "1.10.0"
658+
659+jszip@^3.1.5:
660+ version "3.2.2"
661+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.2.2.tgz#b143816df7e106a9597a94c77493385adca5bd1d"
662+ integrity sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==
663+ dependencies:
664+ lie "~3.3.0"
665+ pako "~1.0.2"
666+ readable-stream "~2.3.6"
667+ set-immediate-shim "~1.0.1"
668+
669+lie@~3.3.0:
670+ version "3.3.0"
671+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
672+ integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
673+ dependencies:
674+ immediate "~3.0.5"
675+
676+lodash@4.17.11:
677+ version "4.17.11"
678+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
679+ integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
680+
681+lodash@4.17.15, lodash@^4.17.15:
682+ version "4.17.15"
683+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
684+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
685+
686+lru-cache@^4.0.1:
687+ version "4.1.5"
688+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
689+ integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
690+ dependencies:
691+ pseudomap "^1.0.2"
692+ yallist "^2.1.2"
693+
694+mime-db@1.43.0:
695+ version "1.43.0"
696+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
697+ integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
698+
699+mime-types@^2.1.12, mime-types@~2.1.19:
700+ version "2.1.26"
701+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
702+ integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
703+ dependencies:
704+ mime-db "1.43.0"
705+
706+mime@^2.0.3:
707+ version "2.4.4"
708+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
709+ integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
710+
711+minimatch@^3.0.4:
712+ version "3.0.4"
713+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
714+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
715+ dependencies:
716+ brace-expansion "^1.1.7"
717+
718+minimist@0.0.8:
719+ version "0.0.8"
720+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
721+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
722+
723+minimist@^1.2.0:
724+ version "1.2.4"
725+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.4.tgz#40357ef9582f8cd42ba8eaa274ddcf27f0c979b7"
726+ integrity sha512-wTiNDqe4D2rbTJGZk1qcdZgFtY0/r+iuE6GDT7V0/+Gu5MLpIDm4+CssDECR79OJs/OxLPXMzdxy153b5Qy3hg==
727+
728+minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
729+ version "2.9.0"
730+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
731+ integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
732+ dependencies:
733+ safe-buffer "^5.1.2"
734+ yallist "^3.0.0"
735+
736+minizlib@^1.2.1:
737+ version "1.3.3"
738+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
739+ integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
740+ dependencies:
741+ minipass "^2.9.0"
742+
743+mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1:
744+ version "0.5.1"
745+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
746+ integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
747+ dependencies:
748+ minimist "0.0.8"
749+
750+ms@2.0.0:
751+ version "2.0.0"
752+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
753+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
754+
755+ms@^2.1.1:
756+ version "2.1.2"
757+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
758+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
759+
760+murmur-hash-js@1.0.0:
761+ version "1.0.0"
762+ resolved "https://registry.yarnpkg.com/murmur-hash-js/-/murmur-hash-js-1.0.0.tgz#5041049269c96633c866386960b2f4289e75e5b0"
763+ integrity sha1-UEEEkmnJZjPIZjhpYLL0KJ515bA=
764+
765+mustache@^3.0.1:
766+ version "3.2.1"
767+ resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.2.1.tgz#89e78a9d207d78f2799b1e95764a25bf71a28322"
768+ integrity sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==
769+
770+nice-try@^1.0.4:
771+ version "1.0.5"
772+ resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
773+ integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
774+
775+node-elm-compiler@5.0.4:
776+ version "5.0.4"
777+ resolved "https://registry.yarnpkg.com/node-elm-compiler/-/node-elm-compiler-5.0.4.tgz#e139c517c4b91f914cccb0a06e3329e3c49d02ac"
778+ integrity sha512-VQsT8QSierYGkHzRed+b4MnccQVF1+qPHunE8jBoU7jD6YpuRqCDPzEoC2zfyEJS80qVnlMZrqobLnyjzX9lJg==
779+ dependencies:
780+ cross-spawn "6.0.5"
781+ find-elm-dependencies "2.0.2"
782+ lodash "4.17.15"
783+ temp "^0.9.0"
784+
785+node-elm-compiler@^5.0.3:
786+ version "5.0.3"
787+ resolved "https://registry.yarnpkg.com/node-elm-compiler/-/node-elm-compiler-5.0.3.tgz#69ffe02c0db4948c4ad9a4f91f8e45b0840c7943"
788+ integrity sha512-I3CWm/ExYYQ/a9bjB0OL9VsGa3Lmgbb8QOs4y2kEiB/DTkTqkcTaCr/lVyOYjRpgR25TsmOBATscsg6H6aC9Hg==
789+ dependencies:
790+ cross-spawn "4.0.0"
791+ find-elm-dependencies "2.0.1"
792+ lodash "4.17.11"
793+ temp "^0.8.3"
794+
795+normalize-path@^3.0.0, normalize-path@~3.0.0:
796+ version "3.0.0"
797+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
798+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
799+
800+oauth-sign@~0.9.0:
801+ version "0.9.0"
802+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
803+ integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
804+
805+once@^1.3.0:
806+ version "1.4.0"
807+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
808+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
809+ dependencies:
810+ wrappy "1"
811+
812+os-tmpdir@~1.0.1:
813+ version "1.0.2"
814+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
815+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
816+
817+pako@~1.0.2:
818+ version "1.0.10"
819+ resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
820+ integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
821+
822+path-is-absolute@^1.0.0:
823+ version "1.0.1"
824+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
825+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
826+
827+path-key@^2.0.1:
828+ version "2.0.1"
829+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
830+ integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
831+
832+path-key@^3.1.0:
833+ version "3.1.1"
834+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
835+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
836+
837+pend@~1.2.0:
838+ version "1.2.0"
839+ resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
840+ integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
841+
842+performance-now@^2.1.0:
843+ version "2.1.0"
844+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
845+ integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
846+
847+picomatch@^2.0.4:
848+ version "2.2.1"
849+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
850+ integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==
851+
852+process-nextick-args@~2.0.0:
853+ version "2.0.1"
854+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
855+ integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
856+
857+progress@^2.0.1:
858+ version "2.0.3"
859+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
860+ integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
861+
862+proxy-from-env@^1.0.0:
863+ version "1.0.0"
864+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
865+ integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
866+
867+pseudomap@^1.0.2:
868+ version "1.0.2"
869+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
870+ integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
871+
872+psl@^1.1.28:
873+ version "1.7.0"
874+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
875+ integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
876+
877+punycode@^2.1.0, punycode@^2.1.1:
878+ version "2.1.1"
879+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
880+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
881+
882+puppeteer@^1.18.0:
883+ version "1.18.1"
884+ resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.18.1.tgz#4a66f3bdab01115ededf70443ec904c99917a815"
885+ integrity sha512-luUy0HPSuWPsPZ1wAp6NinE0zgetWtudf5zwZ6dHjMWfYpTQcmKveFRox7VBNhQ98OjNA9PQ9PzQyX8k/KrxTg==
886+ dependencies:
887+ debug "^4.1.0"
888+ extract-zip "^1.6.6"
889+ https-proxy-agent "^2.2.1"
890+ mime "^2.0.3"
891+ progress "^2.0.1"
892+ proxy-from-env "^1.0.0"
893+ rimraf "^2.6.1"
894+ ws "^6.1.0"
895+
896+qs@~6.5.2:
897+ version "6.5.2"
898+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
899+ integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
900+
901+readable-stream@^2.2.2, readable-stream@~2.3.6:
902+ version "2.3.6"
903+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
904+ integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
905+ dependencies:
906+ core-util-is "~1.0.0"
907+ inherits "~2.0.3"
908+ isarray "~1.0.0"
909+ process-nextick-args "~2.0.0"
910+ safe-buffer "~5.1.1"
911+ string_decoder "~1.1.1"
912+ util-deprecate "~1.0.1"
913+
914+readdirp@~3.2.0:
915+ version "3.2.0"
916+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839"
917+ integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==
918+ dependencies:
919+ picomatch "^2.0.4"
920+
921+request-promise-core@1.1.3:
922+ version "1.1.3"
923+ resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
924+ integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
925+ dependencies:
926+ lodash "^4.17.15"
927+
928+request-promise@^4.2.4:
929+ version "4.2.5"
930+ resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.5.tgz#186222c59ae512f3497dfe4d75a9c8461bd0053c"
931+ integrity sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==
932+ dependencies:
933+ bluebird "^3.5.0"
934+ request-promise-core "1.1.3"
935+ stealthy-require "^1.1.1"
936+ tough-cookie "^2.3.3"
937+
938+request@^2.88.0:
939+ version "2.88.2"
940+ resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
941+ integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
942+ dependencies:
943+ aws-sign2 "~0.7.0"
944+ aws4 "^1.8.0"
945+ caseless "~0.12.0"
946+ combined-stream "~1.0.6"
947+ extend "~3.0.2"
948+ forever-agent "~0.6.1"
949+ form-data "~2.3.2"
950+ har-validator "~5.1.3"
951+ http-signature "~1.2.0"
952+ is-typedarray "~1.0.0"
953+ isstream "~0.1.2"
954+ json-stringify-safe "~5.0.1"
955+ mime-types "~2.1.19"
956+ oauth-sign "~0.9.0"
957+ performance-now "^2.1.0"
958+ qs "~6.5.2"
959+ safe-buffer "^5.1.2"
960+ tough-cookie "~2.5.0"
961+ tunnel-agent "^0.6.0"
962+ uuid "^3.3.2"
963+
964+rimraf@^2.6.1:
965+ version "2.7.1"
966+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
967+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
968+ dependencies:
969+ glob "^7.1.3"
970+
971+rimraf@^2.6.3, rimraf@~2.6.2:
972+ version "2.6.3"
973+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
974+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
975+ dependencies:
976+ glob "^7.1.3"
977+
978+safe-buffer@^5.0.1, safe-buffer@^5.1.2:
979+ version "5.2.0"
980+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
981+ integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
982+
983+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
984+ version "5.1.2"
985+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
986+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
987+
988+safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
989+ version "2.1.2"
990+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
991+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
992+
993+sax@>=0.6.0:
994+ version "1.2.4"
995+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
996+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
997+
998+selenium-webdriver@^4.0.0-alpha.4:
999+ version "4.0.0-alpha.4"
1000+ resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.4.tgz#73694490e02c941d9d0bf7a36f7c49beb9372512"
1001+ integrity sha512-etJt20d8qInkxMAHIm5SEpPBSS+CdxVcybnxzSIB/GlWErb8pIWrArz/VA6VfUW0/6tIcokepXQ5ufvdzqqk1A==
1002+ dependencies:
1003+ jszip "^3.1.5"
1004+ rimraf "^2.6.3"
1005+ tmp "0.0.30"
1006+ xml2js "^0.4.19"
1007+
1008+semver@^5.5.0:
1009+ version "5.7.1"
1010+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
1011+ integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
1012+
1013+set-immediate-shim@~1.0.1:
1014+ version "1.0.1"
1015+ resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
1016+ integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
1017+
1018+shebang-command@^1.2.0:
1019+ version "1.2.0"
1020+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
1021+ integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
1022+ dependencies:
1023+ shebang-regex "^1.0.0"
1024+
1025+shebang-command@^2.0.0:
1026+ version "2.0.0"
1027+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
1028+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
1029+ dependencies:
1030+ shebang-regex "^3.0.0"
1031+
1032+shebang-regex@^1.0.0:
1033+ version "1.0.0"
1034+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
1035+ integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
1036+
1037+shebang-regex@^3.0.0:
1038+ version "3.0.0"
1039+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
1040+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
1041+
1042+split@1.0.1:
1043+ version "1.0.1"
1044+ resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
1045+ integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==
1046+ dependencies:
1047+ through "2"
1048+
1049+sshpk@^1.7.0:
1050+ version "1.16.1"
1051+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
1052+ integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
1053+ dependencies:
1054+ asn1 "~0.2.3"
1055+ assert-plus "^1.0.0"
1056+ bcrypt-pbkdf "^1.0.0"
1057+ dashdash "^1.12.0"
1058+ ecc-jsbn "~0.1.1"
1059+ getpass "^0.1.1"
1060+ jsbn "~0.1.0"
1061+ safer-buffer "^2.0.2"
1062+ tweetnacl "~0.14.0"
1063+
1064+stealthy-require@^1.1.1:
1065+ version "1.1.1"
1066+ resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
1067+ integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
1068+
1069+string_decoder@~1.1.1:
1070+ version "1.1.1"
1071+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
1072+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
1073+ dependencies:
1074+ safe-buffer "~5.1.0"
1075+
1076+supports-color@7.1.0, supports-color@^7.1.0:
1077+ version "7.1.0"
1078+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
1079+ integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
1080+ dependencies:
1081+ has-flag "^4.0.0"
1082+
1083+supports-color@^5.3.0:
1084+ version "5.5.0"
1085+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
1086+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
1087+ dependencies:
1088+ has-flag "^3.0.0"
1089+
1090+tar@^4.4.10:
1091+ version "4.4.13"
1092+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
1093+ integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
1094+ dependencies:
1095+ chownr "^1.1.1"
1096+ fs-minipass "^1.2.5"
1097+ minipass "^2.8.6"
1098+ minizlib "^1.2.1"
1099+ mkdirp "^0.5.0"
1100+ safe-buffer "^5.1.2"
1101+ yallist "^3.0.3"
1102+
1103+temp@0.9.1, temp@^0.9.0:
1104+ version "0.9.1"
1105+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.1.tgz#2d666114fafa26966cd4065996d7ceedd4dd4697"
1106+ integrity sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==
1107+ dependencies:
1108+ rimraf "~2.6.2"
1109+
1110+temp@^0.8.3:
1111+ version "0.8.4"
1112+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2"
1113+ integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==
1114+ dependencies:
1115+ rimraf "~2.6.2"
1116+
1117+through@2:
1118+ version "2.3.8"
1119+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
1120+ integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
1121+
1122+tmp@0.0.30:
1123+ version "0.0.30"
1124+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed"
1125+ integrity sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=
1126+ dependencies:
1127+ os-tmpdir "~1.0.1"
1128+
1129+to-regex-range@^5.0.1:
1130+ version "5.0.1"
1131+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
1132+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
1133+ dependencies:
1134+ is-number "^7.0.0"
1135+
1136+tough-cookie@^2.3.3, tough-cookie@~2.5.0:
1137+ version "2.5.0"
1138+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
1139+ integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
1140+ dependencies:
1141+ psl "^1.1.28"
1142+ punycode "^2.1.1"
1143+
1144+"traverse@>=0.3.0 <0.4":
1145+ version "0.3.9"
1146+ resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
1147+ integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=
1148+
1149+tunnel-agent@^0.6.0:
1150+ version "0.6.0"
1151+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
1152+ integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
1153+ dependencies:
1154+ safe-buffer "^5.0.1"
1155+
1156+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
1157+ version "0.14.5"
1158+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
1159+ integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
1160+
1161+typedarray@^0.0.6:
1162+ version "0.0.6"
1163+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
1164+ integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
1165+
1166+universalify@^0.1.0:
1167+ version "0.1.2"
1168+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
1169+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
1170+
1171+unzip-stream@^0.3.0:
1172+ version "0.3.0"
1173+ resolved "https://registry.yarnpkg.com/unzip-stream/-/unzip-stream-0.3.0.tgz#c30c054cd6b0d64b13a23cd3ece911eb0b2b52d8"
1174+ integrity sha512-NG1h/MdGIX3HzyqMjyj1laBCmlPYhcO4xEy7gEqqzGiSLw7XqDQCnY4nYSn5XSaH8mQ6TFkaujrO8d/PIZN85A==
1175+ dependencies:
1176+ binary "^0.3.0"
1177+ mkdirp "^0.5.1"
1178+
1179+uri-js@^4.2.2:
1180+ version "4.2.2"
1181+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
1182+ integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
1183+ dependencies:
1184+ punycode "^2.1.0"
1185+
1186+util-deprecate@~1.0.1:
1187+ version "1.0.2"
1188+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
1189+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
1190+
1191+uuid@^3.3.2:
1192+ version "3.4.0"
1193+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
1194+ integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
1195+
1196+verror@1.10.0:
1197+ version "1.10.0"
1198+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
1199+ integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
1200+ dependencies:
1201+ assert-plus "^1.0.0"
1202+ core-util-is "1.0.2"
1203+ extsprintf "^1.2.0"
1204+
1205+which@2.0.1:
1206+ version "2.0.1"
1207+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.1.tgz#f1cf94d07a8e571b6ff006aeb91d0300c47ef0a4"
1208+ integrity sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==
1209+ dependencies:
1210+ isexe "^2.0.0"
1211+
1212+which@^1.2.9:
1213+ version "1.3.1"
1214+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
1215+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
1216+ dependencies:
1217+ isexe "^2.0.0"
1218+
1219+which@^2.0.1:
1220+ version "2.0.2"
1221+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
1222+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
1223+ dependencies:
1224+ isexe "^2.0.0"
1225+
1226+wrappy@1:
1227+ version "1.0.2"
1228+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
1229+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
1230+
1231+ws@^6.1.0:
1232+ version "6.2.1"
1233+ resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
1234+ integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
1235+ dependencies:
1236+ async-limiter "~1.0.0"
1237+
1238+xml2js@^0.4.19:
1239+ version "0.4.19"
1240+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
1241+ integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
1242+ dependencies:
1243+ sax ">=0.6.0"
1244+ xmlbuilder "~9.0.1"
1245+
1246+xmlbuilder@^13.0.2:
1247+ version "13.0.2"
1248+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7"
1249+ integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==
1250+
1251+xmlbuilder@~9.0.1:
1252+ version "9.0.7"
1253+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
1254+ integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
1255+
1256+yallist@^2.1.2:
1257+ version "2.1.2"
1258+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
1259+ integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
1260+
1261+yallist@^3.0.0, yallist@^3.0.3:
1262+ version "3.1.1"
1263+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
1264+ integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
1265+
1266+yauzl@2.4.1:
1267+ version "2.4.1"
1268+ resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
1269+ integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
1270+ dependencies:
1271+ fd-slicer "~1.0.1"