Chromium Code Reviews| Index: pkg/csslib/test/mixin_test.dart |
| diff --git a/pkg/csslib/test/mixin_test.dart b/pkg/csslib/test/mixin_test.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d1de2e33aa4006964e85c50c83ff3ec23aadaec9 |
| --- /dev/null |
| +++ b/pkg/csslib/test/mixin_test.dart |
| @@ -0,0 +1,685 @@ |
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +library mixin_test; |
| + |
| +import 'package:unittest/unittest.dart'; |
| +import 'testing.dart'; |
| + |
| +var options = ['--warnings_as_errors', '--no-colors', 'memory']; |
|
Jennifer Messerly
2013/09/18 06:44:15
extra space here?
nweiz
2013/09/18 22:40:54
"final", not "var".
terry
2013/10/09 03:40:33
Done.
|
| + |
| +void topLevelMixin() { |
|
nweiz
2013/09/18 22:40:54
Why are you defining a separate function for each
|
| + var errors = []; |
| + final input1 = r''' |
|
nweiz
2013/09/18 22:40:54
Why is this [input1]? There's no other [input] var
terry
2013/10/09 03:40:33
Probably other test at one point. 1 is gone.
On 20
|
| +@mixin silly-links { |
| + a { |
| + color: blue; |
| + background-color: red; |
| + } |
| +} |
| + |
| +@include silly-links; |
| +'''; |
|
nweiz
2013/09/18 22:40:54
These no-indentation strings are difficult to read
terry
2013/10/09 03:40:33
I'll consider it but I fine pre-processing to be a
|
| + |
| + final generated1 = r'''a { |
| + color: #00f; |
| + background-color: #f00; |
| +}'''; |
| + |
| + final stylesheet1 = compileCss(input1, errors: errors, opts: options); |
|
nweiz
2013/09/18 22:40:54
Using an out parameter like [errors] here is a cod
terry
2013/10/09 03:40:33
It's something we've liked so far for tooling.
On
|
| + |
| + expect(stylesheet1 != null, true); |
|
nweiz
2013/09/18 22:40:54
This could be written as "expect(stylesheet, isNot
|
| + expect(errors.isEmpty, true, reason: errors.toString()); |
|
nweiz
2013/09/18 22:40:54
expect(errors, isEmpty, ...)
|
| + expect(prettyPrint(stylesheet1), generated1); |
| + |
| + // TODO(terry): Test using a declaration @mixin as a top-level @include. |
|
nweiz
2013/09/18 22:40:54
Why is this a TODO and not an actual test?
terry
2013/10/09 03:40:33
comment old.
On 2013/09/18 22:40:54, nweiz wrote:
|
| +} |
| + |
| +void topLevelMixin2Includes() { |
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + a { |
| + color: blue; |
| + background-color: red; |
| + } |
| +} |
| +@mixin b { |
| + span { |
| + color: black; |
| + background-color: orange; |
| + } |
| +} |
| +@include a; |
| +@include b; |
| +'''; |
| + |
| + final generated = r'''a { |
| + color: #00f; |
| + background-color: #f00; |
| +} |
| +span { |
| + color: #000; |
| + background-color: #ffa500; |
| +}'''; |
| + |
| + final stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void topLevelMixinMultiRulesets() { |
|
nweiz
2013/09/18 22:40:54
What do multiple rulesets have to do with this tes
terry
2013/10/09 03:40:33
Tests top-level mixins that includes another mixin
|
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + a { |
| + color: blue; |
| + background-color: red; |
| + } |
| +} |
| +@mixin b { |
| + span { |
| + color: black; |
| + background-color: orange; |
| + } |
| + @include c; |
| +} |
| +@mixin c { |
| + #foo-id { |
| + border-top: 1px solid red; |
| + border-bottom: 2px solid green; |
| + } |
| +} |
| +@include a; |
| +@include b; |
| +'''; |
| + |
| + final generated = r'''a { |
| + color: #00f; |
| + background-color: #f00; |
| +} |
| +span { |
| + color: #000; |
| + background-color: #ffa500; |
| +} |
| +#foo-id { |
| + border-top: 1px solid #f00; |
| + border-bottom: 2px solid #008000; |
| +}'''; |
| + |
| + final stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void topLevelMixinMultiRulesets2() { |
|
nweiz
2013/09/18 22:40:54
I have no idea what this test is testing. Is it ju
terry
2013/10/09 03:40:33
1, 2 and 3 nesting levels of includes.
On 2013/09/
|
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + a { |
| + color: blue; |
| + background-color: red; |
| + } |
| +} |
| +@mixin b { |
| + @include a; |
| + span { |
| + color: black; |
| + background-color: orange; |
| + } |
| + @include c; |
| + @include d; |
| +} |
| +@mixin c { |
| + #foo-id { |
| + border-top: 1px solid red; |
| + border-bottom: 2px solid green; |
| + } |
| +} |
| +@mixin d { |
| + a:hover { |
| + cursor: arrow; |
| + } |
| + @include e |
| +} |
| +@mixin e { |
| + #split-bar:visited { |
| + color: gray; |
| + } |
| + @include f; |
| +} |
| +@mixin f { |
| + #split-bar div { |
| + border: 1px solid lightgray; |
| + } |
| +} |
| +@include b; |
| +'''; |
| + |
| + final generated = r'''a { |
| + color: #00f; |
| + background-color: #f00; |
| +} |
| +span { |
| + color: #000; |
| + background-color: #ffa500; |
| +} |
| +#foo-id { |
| + border-top: 1px solid #f00; |
| + border-bottom: 2px solid #008000; |
| +} |
| +a:hover { |
| + cursor: arrow; |
| +} |
| +#split-bar:visited { |
| + color: #808080; |
| +} |
| +#split-bar div { |
| + border: 1px solid #d3d3d3; |
| +}'''; |
| + |
| + final stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void topLevelMixinSelectors() { |
|
nweiz
2013/09/18 22:40:54
All the previous tests had selectors. Saying that
terry
2013/10/09 03:40:33
testing with combinators and selector groups.
On 2
|
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + a, b { |
| + color: blue; |
| + background-color: red; |
| + } |
| + div > span { |
| + color: black; |
| + background-color: orange; |
| + } |
| +} |
| + |
| +@include a; |
| +'''; |
| + |
| + final generated = r'''a, b { |
| + color: #00f; |
| + background-color: #f00; |
| +} |
| +div > span { |
| + color: #000; |
| + background-color: #ffa500; |
| +}'''; |
| + |
| + final stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void declSimpleMixin() { |
| + final errors = []; |
| + final input = r''' |
| +@mixin div-border { |
| + border: 2px dashed red; |
| +} |
| +div { |
| + @include div-border; |
| +} |
| +'''; |
| + |
| + final generated = r'''div { |
| + border: 2px dashed #f00; |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void declMixin2Includes() { |
| + final errors = []; |
| + final input = r''' |
| +@mixin div-border { |
| + border: 2px dashed red; |
| +} |
| +@mixin div-color { |
| + color: blue; |
| +} |
| +div { |
| + @include div-border; |
| + @include div-color; |
| +} |
| +'''; |
| + |
| + final generated = r'''div { |
| + border: 2px dashed #f00; |
| + color: #00f; |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void declMixinNestedIncludes() { |
|
nweiz
2013/09/18 22:40:54
As above, "div-border" is unrelated to what this t
terry
2013/10/09 03:40:33
As test implies for than one level of testing.
On
|
| + final errors = []; |
| + final input = r''' |
| +@mixin div-border { |
| + border: 2px dashed red; |
| +} |
| +@mixin div-color { |
| + @include div-padding; |
| + color: blue; |
| + @include div-margin; |
| +} |
| +@mixin div-padding { |
| + padding: .5em; |
| +} |
| +@mixin div-margin { |
| + margin: 5px; |
| +} |
| +div { |
| + @include div-border; |
| + @include div-color; |
| +} |
| +'''; |
| + |
| + final generated = r'''div { |
| + border: 2px dashed #f00; |
| + padding: .5em; |
| + color: #00f; |
| + margin: 5px; |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void declMixinNestedIncludes2() { |
|
nweiz
2013/09/18 22:40:54
I can't understand what this test tests that the p
terry
2013/10/09 03:40:33
nesting with other selectors pseudo.
On 2013/09/18
|
| + final errors = []; |
| + final input = r''' |
| +@mixin div-border { |
| + border: 2px dashed red; |
| +} |
| +@mixin div-color { |
| + @include div-padding; |
| + @include div-margin; |
| +} |
| +@mixin div-padding { |
| + padding: .5em; |
| +} |
| +@mixin div-margin { |
| + margin: 5px; |
| +} |
| +div { |
| + @include div-border; |
| + @include div-color; |
| +} |
| +'''; |
| + |
| + final generated = r'''div { |
| + border: 2px dashed #f00; |
| + padding: .5em; |
| + margin: 5px; |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void mixinArg() { |
| + final errors = []; |
| + final input = r''' |
| +@mixin div-border { |
| + border: 2px dashed red; |
| +} |
| + |
| +@mixin div-inside(@dist) { |
|
nweiz
2013/09/18 22:40:54
If your arguments are going to use this syntax, yo
terry
2013/10/09 03:40:33
Vars are now different in CSS (var-) but we're sup
|
| + margin-left: var(dist); |
| + |
| +} |
| + |
| +div { |
| + @include left(10px); |
| + @include div-border; |
| +} |
| +'''; |
| + |
| + final generated = r''' '''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| + |
| + // TODO(terry): Test using a top-level @mixin as a declaration @include. |
| +} |
| + |
| +void mixinArgs() { |
| + final errors = []; |
| + var input = r''' |
| +@mixin box-shadow(@shadows...) { |
| + -moz-box-shadow: var(shadows); |
| + -webkit-box-shadow: var(shadows; |
| + box-shadow: var(shadows); |
| +} |
| + |
| +.shadows { |
| + @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999); |
| +}'''; |
| + |
| + var generated = r''' |
| +.shadowed { |
| + -moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; |
| + -webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; |
| + box-shadow: 0px 4px 5px #666, 2px 6px 10px #999; |
| +} |
| +'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void mixinArgs_2() { |
| + final errors = []; |
| + var input = r''' |
| +@mixin colors(@text, @background, @border) { |
| + color: var(text); |
| + background-color: var(background); |
| + border-color: var(border); |
| +} |
| + |
| +@values: #ff0000, #00ff00, #0000ff; |
| +.primary { |
| + @include colors(var(values)); |
| +} |
| +'''; |
| + |
| + var generated = r''' |
| +.primary { |
| + color: #ff0000; |
| + background-color: #00ff00; |
| + border-color: #0000ff; |
| +} |
| +'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void mixinArgs_var() { |
| + final errors = []; |
| + var input = r''' |
| +@mixin wrapped-stylish-mixin(@args...) { |
| + font-weight: bold; |
| + @include stylish-mixin(var(args)); |
| +} |
| + |
| +.stylish { |
| + // The @width argument will get passed on to "stylish-mixin" as a keyword |
| + @include wrapped-stylish-mixin(#00ff00, @width: 100px); |
| +} |
| +'''; |
| + |
| + var generated = r''' |
| +'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void mixin_content_toplevel() { |
| + void mixinArgs_var() { |
| + final errors = []; |
| + var input = r''' |
| +@mixin apply-to-ie6-only { |
| + * html { |
| + @content; |
| + } |
| +} |
| +@include apply-to-ie6-only { |
| + #logo { |
| + background-image: url(/logo.gif); |
| + } |
| +} |
| +'''; |
| + |
| + var generated = r'''* html #logo { |
| + background-image: url(/logo.gif); |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| + } |
| +} |
| + |
| +void mixin_content() { |
| + void mixinArgs_var() { |
| + final errors = []; |
| + var input = r''' |
| +@mixin keyframes { |
| + @-moz-keyframes { @content; } |
| + @-webkit-keyframes { @content; } |
| +} |
| + |
| +@include keyframes { |
| + 0% { opacity: 0; } |
| + 100% { opacity: 1; } |
| +} |
| +'''; |
| + |
| + var generated = r'''@-moz-keyframes { |
| + 0% { opacity: 0; } |
| + 100% { opacity: 1; } |
| +} |
| + |
| +@-webkit-keyframes { |
| + 0% { opacity: 0; } |
| + 100% { opacity: 1; } |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| + } |
| +} |
| + |
| +void mixin_content_var() { |
| + void mixinArgs_var() { |
| + final errors = []; |
| + var input = r''' |
| +@color: white; |
| +@mixin colors(@color: blue) { |
| + background-color: var(color); |
| + @content; |
| + border-color: var(color); |
| +} |
| +.colors { |
| + @include colors { color: var(color); } |
| +} |
| +'''; |
| + |
| + var generated = r'''.colors { |
| + background-color: blue; |
| + color: white; |
| + border-color: blue; |
| +} |
| +'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| + } |
| +} |
| + |
| +void badDeclarationInclude() { |
|
nweiz
2013/09/18 22:40:54
This shouldn't cause an error.
terry
2013/10/09 03:40:33
Seems weird that top-level will close a declaratio
|
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + span { |
| + border: 2px dashed red; |
| + @include b; |
| + } |
| +} |
| + |
| +@mixin b { |
| + #foo-id { |
| + color: red; |
| + } |
| +} |
| + '''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isNotEmpty, true, reason: errors.toString()); |
| + expect(errors.length, 1, reason: errors.toString()); |
| + var error = errors[0]; |
| + expect(error.message, |
| + 'Trying to use top-level ruleset in a declaration - @include b'); |
| + expect(error.span.end.offset, 60); |
| +} |
| + |
| +void badTopInclude() { |
|
nweiz
2013/09/18 22:40:54
This *should* cause an error.
terry
2013/10/09 03:40:33
Currently, we really report everything as a warnin
|
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + span { |
| + border: 2px dashed red; |
| + } |
| + @include b; |
| +} |
| + |
| +@mixin b { |
| + color: red; |
| +} |
| + |
| +@include a; |
| + '''; |
| + |
| + var generated = r'''span { |
| + border: 2px dashed #f00; |
| +}'''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + expect(stylesheet != null, true); |
| + expect(errors.isEmpty, true, reason: errors.toString()); |
| + expect(prettyPrint(stylesheet), generated); |
| +} |
| + |
| +void cycleDetect() { |
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + @include b; |
| +} |
| +@mixin b { |
| + span { |
| + border: 2px dashed red; |
| + } |
| + @include a; |
| +} |
| +@include b; |
| + '''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isNotEmpty, true, reason: errors.toString()); |
|
nweiz
2013/09/18 22:40:54
expect(errors, isNotEmpty)
[reason] here doesn't
terry
2013/10/09 03:40:33
test is gone.
On 2013/09/18 22:40:54, nweiz wrote:
|
| + expect(errors.length, 2, reason: errors.toString()); |
| + var error = errors[0]; |
| + expect(error.message, 'mixin cycle detected @mixin a'); |
|
nweiz
2013/09/18 22:40:54
Test the type of the error, not the message. Testi
|
| + expect(error.span.end.offset, 24); |
| + error = errors[1]; |
| + expect(error.message, 'mixin cycle detected @mixin b'); |
| + expect(error.span.end.offset, 92); |
| +} |
| + |
| +void cycleNesting() { |
| + final errors = []; |
| + final input = r''' |
| +@mixin a { |
| + @include b; |
| +} |
| +@mixin b { |
| + border: 2px dashed red; |
| + @include a; |
| +} |
| +div { |
| + @include b; |
| +} |
| + '''; |
| + |
| + var stylesheet = compileCss(input, errors: errors, opts: options); |
| + |
| + expect(stylesheet != null, true); |
| + expect(errors.isNotEmpty, true, reason: errors.toString()); |
| + expect(errors.length, 2, reason: errors.toString()); |
| + var error = errors[0]; |
| + expect(error.message, 'mixin cycle detected @mixin a'); |
| + expect(error.span.end.offset, 24); |
| + error = errors[1]; |
| + expect(error.message, 'mixin cycle detected @mixin b'); |
| + expect(error.span.end.offset, 77); |
| +} |
| + |
| +main() { |
| + test('top-level mixin', topLevelMixin); |
| + test('top-level mixin 2 @includes', topLevelMixin2Includes); |
|
nweiz
2013/09/18 22:40:54
"2" -> "two", here and elsewhere. Also, at least u
terry
2013/10/09 03:40:33
Done.
|
| + test('top-level multi rulesets', topLevelMixinMultiRulesets); |
| + test('top-level multi rulesets and nesting', topLevelMixinMultiRulesets2); |
| + test('top-level selector groups', topLevelMixinSelectors); |
| + |
| + test('declaration mixin', declSimpleMixin); |
| + test('declaration mixin 2 @includes', declMixin2Includes); |
| + test('declaration mixin includes', declMixinNestedIncludes); |
| + test('declaration mixin includes #2', declMixinNestedIncludes2); |
|
nweiz
2013/09/18 22:40:54
Adding "#2" here doesn't tell me anything about th
terry
2013/10/09 03:40:33
Done.
|
| + |
| + test('detect cycle', cycleDetect); |
| + test('detect cycle nesting', cycleNesting); |
| + test('detect bad top-level as declaration', badDeclarationInclude); |
| + test('detect bad declaration as top-level', badTopInclude); |
| + |
| + // TODO(Terry): More tests to enable. |
|
nweiz
2013/09/18 22:40:54
Don't include tests that don't work. You can add t
terry
2013/10/09 03:40:33
Done.
|
| + // test('mixin arg', mixinArg); |
| + // test('mixin args', mixinArgs); |
| + // test('mixin args fanout', mixinArgs_2); |
| + // test('mixin args with variables', mixinArgs_var); |
| + // test('mixin content top-level', mixin_content_toplevel); |
| + // test('mixin content', mixin_content); |
| + // test('mixin content with variables', mixin_content_var); |
| +} |