@sencha/cmd-linux-64
Version:
Productivity and performance optimization tool for building applications with Sencha Ext JS
1,638 lines (1,514 loc) • 77.7 kB
JavaScript
var Fashion = require('../../index.js');
var assert = require('assert');
var helpers = require('../helpers.js');
describe('syntax', function () {
describe('variables', function () {
helpers.test('should support a simple variable', [
'$var: foo;',
'blat {a: $var;}'
], [
'blat {',
' a: foo;',
'}'
]);
helpers.test('should do simple variable addition', [
'foo {',
' $var: 2;',
' $another-var: 4;',
' a: $var;',
' b: $var + $another-var;}'
], [
'foo {',
' a: 2;',
' b: 6;',
'}'
]);
helpers.test('should not override an existing value if we set it with !default', [
'$var: 1;',
'$var: 2 !default;',
'foo {a: $var;}'
], [
'foo {',
' a: 1;',
'}'
]);
helpers.test('should assign the variable if !default is appended to the definition and the value does not exit yet', [
'$var: 2 !default;',
'foo {a: $var;}'
], [
'foo {',
' a: 2;',
'}'
]);
helpers.test('should allow for multiline variable assignment', [
'foo {',
'$var1: 1 +',
' 2;',
'$var2: true and',
' false;',
'$var3: a b',
' c;',
'a: $var1;',
'b: $var2;',
'c: $var3; }'
], [
'foo {',
' a: 3;',
' b: false;',
' c: a b c;',
'}'
]);
});
describe('scripting', function () {
helpers.test('should support simple sass script functionality', [
'foo {',
' a: 1 + 2;',
' b: 1 - 2;',
' c: foo + bar;',
' d: floor(12.3px); }'
], [
'foo {',
' a: 3;',
' b: -1;',
' c: foobar;',
' d: 12px;',
'}'
]);
});
describe('conditionals', function () {
helpers.test('should support if statements', [
'@if "foo" == "foo" {foo {a: b;}}',
'@if "foo" != "foo" {bar {a: b;}}'
], [
'foo {',
' a: b;',
'}'
]);
helpers.test('should support else if statements', [
'@if "foo" != "foo" {foo {a: b;}}',
'@else if "foo" == "foo" {bar {a: b;}}',
'@else if true {baz {a: b;}}'
], [
'bar {',
' a: b;',
'}'
]);
helpers.test('should support else statements', [
'@if "foo" != "foo" {foo {a: b;}}',
'@else {bar {a: b;}}'
], [
'bar {',
' a: b;',
'}'
]);
});
describe('comments', function () {
// Comments are not being preserved
helpers.test('should allow a comment after an if statement', [
'foo {',
' @if true {a: b;}',
' /* This is a comment */',
' c: d; }'
], [
'foo {',
' a: b;',
' /* This is a comment */',
' c: d;',
'}'
]);
// Comments are not being preserved
helpers.test('should allow a comment after an else statement', [
'foo {',
' @if true {a: b;}',
' @else {x: y;}',
' /* This is a comment */',
' c: d; }'
], [
'foo {',
' a: b;',
' /* This is a comment */',
' c: d;',
'}'
]);
helpers.xtest('should allow an inline comment and ignore it in the output', [
'foo {a: 1 + /* flang */ bar;}'
], [
'foo {',
' a: 1bar;',
'}'
]);
helpers.test('should ignore one-line comments in selectors', [
'.foo {// bar: baz;}',
' baz: bang; //}',
'}'
], [
'.foo {',
' baz: bang;',
'}'
]);
helpers.test('should not fail when using // in strings', [
'.foo bar[val="//"] {',
' baz: bang; //}',
'}'
], [
'.foo bar[val="//"] {',
' baz: bang;',
'}'
]);
helpers.test('should ignore one-line comments in a rule definition', [
'foo {a: 1 + // flang }',
' blang; }'
], [
'foo {',
' a: 1blang;',
'}'
]);
});
describe('rulesets', function () {
helpers.test('should support nested rulesets', [
'foo {bar {a: b;}}'
], [
'foo bar {',
' a: b;',
'}'
]);
helpers.test('should support multiple nested rulesets', [
'foo {',
' bar {a: b;}',
' baz {b: c;}}'
], [
'foo bar {',
' a: b;',
'}',
'foo baz {',
' b: c;',
'}'
]);
helpers.test('should support deeply nested rulesets', [
'foo {',
' bar {baz {a: b;}}',
' bang {bip {a: b;}}}'
], [
'foo bar baz {',
' a: b;',
'}',
'foo bang bip {',
' a: b;',
'}'
]);
helpers.test('should support direct selector rulesets', [
'foo {',
' a: b;',
' > bar {',
' c: d;',
' }',
'}'
], [
'foo {',
' a: b;',
'}',
'foo > bar {',
' c: d;',
'}'
]);
helpers.test('should support nested direct selector rulesets', [
'foo {',
' a: b;',
' bar {',
' c: d;',
' > bar2 {',
' e: f;',
' }',
' }',
'}'
], [
'foo {',
' a: b;',
'}',
'foo bar {',
' c: d;',
'}',
'foo bar > bar2 {',
' e: f;',
'}'
]);
helpers.test('should support a declaration before a nested ruleset', [
'foo {',
' a: b;',
' bar {c: d;}}'
], [
'foo {',
' a: b;',
'}',
'foo bar {',
' c: d;',
'}'
]);
helpers.test('should support a declaration after a nested ruleset', [
'foo {',
' bar {c: d;}',
' a: b;}'
], [
'foo {',
' a: b;',
'}',
'foo bar {',
' c: d;',
'}'
]);
helpers.test('should support a combination of declarations and nested rulesets', [
'foo {',
' ump: nump;',
' grump: clump;',
' bar {',
' blat: bang;',
' habit: rabbit;',
' baz {a: b;}',
' bip {c: d;}}',
' bibble {',
' bap {e: f;}}}'
], [
'foo {',
' ump: nump;',
' grump: clump;',
'}',
'foo bar {',
' blat: bang;',
' habit: rabbit;',
'}',
'foo bar baz {',
' a: b;',
'}',
'foo bar bip {',
' c: d;',
'}',
'foo bibble bap {',
' e: f;',
'}'
]);
});
describe('selectors', function () {
helpers.test('should support multiple selectors in combination with nested rulesets', [
'foo,',
'bar {',
' baz,',
' bang {a: b;}}'
], [
'foo baz,',
'foo bang,',
'bar baz,',
'bar bang {',
' a: b;',
'}'
]);
helpers.test('should support the & selector prefix and "bar &.bar" syntax', [
'foo {',
' &:hover {a: b;}',
' bar &.baz {c: d;}}'
], [
'foo:hover {',
' a: b;',
'}',
'bar foo.baz {',
' c: d;',
'}'
]);
helpers.test('should support crazy combination of & prefixes', [
'foo {',
' &.test {a: b;}',
' bar {',
' ie7 &.aah,',
' &.test,',
' .huh {b: c;}',
' huh {.ie6 &.test {d: e;}}',
' }',
'}'
], [
'foo.test {',
' a: b;',
'}',
'ie7 foo bar.aah,',
'foo bar.test,',
'foo bar .huh {',
' b: c;',
'}',
'.ie6 foo bar huh.test {',
' d: e;',
'}'
// The old system would output the following. We have a better new way now
// 'foo bar .huh {',
// ' b: c;',
// '}',
// 'foo bar.test {',
// ' b: c;',
// '}',
// 'foo.test {',
// ' a: b;',
// '}',
// 'ie7 foo bar.aah {',
// ' b: c;',
// '}',
// '.ie6 foo bar huh.test {',
// ' d: e;',
// '}'
]);
helpers.test('should support nested &', [
'foo {',
' &.test {a: b;}',
' &.bar,',
' soap {',
' ie7 &.aah,',
' &.test,',
' .huh {b: c;}',
' huh {.ie6 &.test {d: e;}}',
' }',
'}'
], [
'foo.test {',
' a: b;',
'}',
'ie7 foo.bar.aah,',
'foo.bar.test,',
'foo.bar .huh,',
'ie7 foo soap.aah,',
'foo soap.test,',
'foo soap .huh {',
' b: c;',
'}',
'.ie6 foo.bar huh.test,',
'.ie6 foo soap huh.test {',
' d: e;',
'}'
]);
helpers.test('should support new-lines in selectors', [
'foo',
'bar {a: b;}'
], [
'foo bar {',
' a: b;',
'}'
]);
helpers.test('should support new-lines in combination with spaces in selectors', [
'foo',
'bar {',
' baz',
' bang {a: b;}',
' bip bop {c: d;}}'
], [
'foo bar baz bang {',
' a: b;',
'}',
'foo bar bip bop {',
' c: d;',
'}'
]);
helpers.test('should support a selector as a function with a string argument (double quotes)', [
'@function get-name($name) {',
' @return $name;',
'}',
'#{get-name("foo")} {',
' bar: baz;',
'}'
], [
'foo {',
' bar: baz;',
'}'
]);
helpers.test('should support a selector as a function with a string argument (single quotes)', [
'@function get-name($name) {',
' @return $name;',
'}',
'#{get-name(\'foo\')} {',
' bar: baz;',
'}'
], [
'foo {',
' bar: baz;',
'}'
]);
});
describe('mixins', function () {
helpers.test('should support simple mixins', [
'@mixin foo {',
' .foo {a: b;}}',
'bar {',
' @include foo;',
' c: d;}'
], [
'bar {',
' c: d;',
'}',
'bar .foo {',
' a: b;',
'}'
]);
helpers.test('should support mixins without nested selectors', [
'@mixin foo {a: b;}',
'bar {',
' @include foo;',
' c: d;}'
], [
'bar {',
' a: b;',
' c: d;',
'}'
]);
helpers.test('should support mixins with empty arguments', [
'@mixin foo() {a: b;}',
'.foo {@include foo();}'
], [
'.foo {',
' a: b;',
'}'
]);
helpers.test('should support mixins defined with empty arguments, but called without', [
'@mixin foo() {a: b;}',
'.foo {@include foo;}'
], [
'.foo {',
' a: b;',
'}'
]);
helpers.test('should support defining a mixin without arguments, but calling it with them', [
'@mixin foo {a: b;}',
'.foo {@include foo();}'
], [
'.foo {',
' a: b;',
'}'
]);
helpers.test('should support unnamed arguments', [
'@mixin foo($a) {a: $a;}',
'.foo {@include foo(bar);}'
], [
'.foo {',
' a: bar;',
'}'
]);
helpers.test('should support multiple sequential arguments', [
'@mixin foo($a, $b) {',
' a: $a;',
' b: $b; }',
'.foo {@include foo(bar, 12px);}'
], [
'.foo {',
' a: bar;',
' b: 12px;',
'}'
]);
helpers.test('should support named arguments', [
'@mixin foo($a, $b) {',
' a: $a;',
' b: $b; }',
'.foo {@include foo($a: bar, $b: 12px);}'
], [
'.foo {',
' a: bar;',
' b: 12px;',
'}'
]);
helpers.test('should support default values when using named arguments', [
'@mixin foo($a: bar, $b) {',
' a: $a;',
' b: $b; }',
'.foo {@include foo($b: 12px);}'
], [
'.foo {',
' a: bar;',
' b: 12px;',
'}'
]);
helpers.test('should allow a mixture of required and named arguments', [
'@mixin a-mixin($required, $arg1: default-val1, $arg2: default-val2) {',
' required: $required;',
' arg1: $arg1;',
' arg2: $arg2;',
'}',
'.mixed { @include a-mixin(foo, $arg2: non-default-val2); }'
], [
'.mixed {',
' required: foo;',
' arg1: default-val1;',
' arg2: non-default-val2;',
'}'
]);
helpers.test('should calling a mixin with required and named arguments, passing the required argument as a named argument', [
'@mixin a-mixin($required, $arg1: default-val1, $arg2: default-val2) {',
' required: $required;',
' arg1: $arg1;',
' arg2: $arg2; }',
'.mixed { @include a-mixin($required: foo); }'
], [
'.mixed {',
' required: foo;',
' arg1: default-val1;',
' arg2: default-val2;',
'}'
]);
helpers.test('should support calling a mixin with required and named arguments using out of order named arguments only', [
'@mixin a-mixin($required, $arg1: default-val1, $arg2: default-val2) {',
' required: $required;',
' arg1: $arg1;',
' arg2: $arg2; }',
'.mixed { @include a-mixin($arg2: non-default-val2, $arg1: non-default-val1, $required: foo); }'
], [
'.mixed {',
' required: foo;',
' arg1: non-default-val1;',
' arg2: non-default-val2;',
'}'
]);
helpers.test('should support passing content body to mixin', [
'@mixin apply-to-ie6-only {',
' * html {',
' @content;',
' }',
'}',
'@include apply-to-ie6-only {',
' #logo {',
' background-image: url(logo.gif);',
' }',
'}'
], [
'* html #logo {',
' background-image: url(logo.gif);',
'}'
]);
helpers.test('should manage scopes when calling content body', [
'$color: #fff;',
'@mixin colors($color: blue) {',
' background-color: $color;',
' @content;',
' border-color: $color;',
'}',
'.colors {',
' @include colors { color: $color; }',
'}'
], [
'.colors {',
' background-color: blue;',
' color: #fff;',
' border-color: blue;',
'}'
]);
var allowSetScopeWas;
helpers.test('should not support overriding global variables when configured', [
'$foo: "bar";',
'@mixin mix() {',
' $foo: "baz";',
' body { background: $foo; }',
'}',
'body { background: $foo; }',
'@include mix();',
'body { background: $foo; }'
], [
'body {',
' background: "bar";',
'}',
'body {',
' background: "baz";',
'}',
'body {',
' background: "bar";',
'}'
], false, function(){
allowSetScopeWas = Fashion.Runtime.allowSetScopedVariables;
Fashion.Runtime.allowSetScopedVariables = false;
}, function(){
Fashion.Runtime.allowSetScopedVariables = allowSetScopeWas;
});
helpers.test('should support overriding global variables when configured', [
'$foo: "bar";',
'@mixin mix() {',
' $foo: "baz";',
' body { background: $foo; }',
'}',
'body { background: $foo; }',
'@include mix();',
'body { background: $foo; }'
], [
'body {',
' background: "bar";',
'}',
'body {',
' background: "baz";',
'}',
'body {',
' background: "baz";',
'}'
], false, function(){
allowSetScopeWas = Fashion.Runtime.allowSetScopedVariables;
Fashion.Runtime.allowSetScopedVariables = true;
}, function(){
Fashion.Runtime.allowSetScopedVariables = allowSetScopeWas;
});
helpers.test('should support overriding global variables with !global', [
'$foo: "bar";',
'@mixin mix() {',
' $foo: "baz" !global;',
' body { background: $foo; }',
'}',
'body { background: $foo; }',
'@include mix();',
'body { background: $foo; }'
], [
'body {',
' background: "bar";',
'}',
'body {',
' background: "baz";',
'}',
'body {',
' background: "baz";',
'}'
], false, function(){
allowSetScopeWas = Fashion.Runtime.allowSetScopedVariables;
Fashion.Runtime.allowSetScopedVariables = false;
}, function(){
Fashion.Runtime.allowSetScopedVariables = allowSetScopeWas;
});
helpers.test('should handle colliding variable names in content body', [
'@mixin smartphone {',
' $sidebar-width: 400px;',
' customWidth: $sidebar-width;',
' @content;',
'}',
'',
'#sidebar {',
' $sidebar-width: 300px;',
' width: $sidebar-width;',
' @include smartphone {',
' width: ($sidebar-width / 3);',
' }',
'}'
], [
'#sidebar {',
' width: 300px;',
' customWidth: 400px;',
' width: 100px;',
'}'
])
helpers.test("should support parameter name lookups", [
'@mixin the-mixin ($a1: "foo", $a2: $a1 + " bar", $a3: $a2 + " baz") {',
' body { background: $a3 }',
'}',
'',
'#rule {',
' @include the-mixin ($a1: "not-foo");',
'}'
], [
'#rule body {',
' background: "not-foo bar baz";',
'}'
]);
helpers.test("should support parameter name lookups with proper scope management", [
'$suffix: " end.";',
'@mixin the-mixin (',
' $a1: "foo", ',
' $a2: $a1 + " bar", ',
' $a3: $a2 + " baz" + $suffix) ',
'{',
' body { background: $a3 }',
'}',
'@mixin the-mixin-2 {',
' $suffix: " not-end.";',
' #rule {',
' @include the-mixin ($a1: "not-foo");',
' }',
'}',
'@include the-mixin-2;'
], [
'#rule body {',
' background: "not-foo bar baz end.";',
'}'
],false, function(){
allowSetScopeWas = Fashion.Runtime.allowSetScopedVariables;
Fashion.Runtime.allowSetScopedVariables = false;
}, function(){
Fashion.Runtime.allowSetScopedVariables = allowSetScopeWas;
});
helpers.test("should manages default parameters for mixin calls", [
'$suffix: " the end";',
'@mixin mixa( $a: "a", $b: $a + "b", $c: $a + $b + $suffix) {',
' body {',
' background: $c;',
' }',
'}',
'@mixin mixb() {',
' $x: "xyz ";',
' $b: "foo";',
' @include mixa($a: $x);',
'}',
'@include mixb();',
], [
'body {',
' background: "xyz xyz b the end";',
'}'
]);
});
describe('debugging', function () {
helpers.test('should support the @debug statement', [
'foo {a: b;}',
'@debug "hello world!";',
'$x: 5;',
'@debug $x * 6;',
'bar {c: d;}'
], [
'foo {',
' a: b;',
'}',
'bar {',
' c: d;',
'}'
]);
helpers.test('should support the @warn statement', [
'foo {a: b;}',
'@warn "hello world!";',
'$y: 4;',
'@warn 3 * $y;',
'bar {c: d;}'
], [
'foo {',
' a: b;',
'}',
'bar {',
' c: d;',
'}'
]);
});
describe('inline expressions', function () {
helpers.test('should replace inline variables', [
'$a: bar;',
'foo #{$a} baz {a: b;}'
], [
'foo bar baz {',
' a: b;',
'}'
]);
helpers.test('should not get confused about inline expressions in an id', [
'$zzz: abc;',
'##{$zzz} { a: b;}'
], [
'#abc {',
' a: b;',
'}'
]);
helpers.test('should support variable replace inside of a literal value', [
'$value : bip;',
'foo {',
' bar: -moz-#{$value};',
'}'
], [
'foo {',
' bar: -moz-bip;',
'}'
]);
helpers.test('should support inline variables as part of the property of a variable assignment', [
'$a : a;',
'$b : b;',
'div { -foo-#{$a}-#{$b}-foo: foo;}'
], [
'div {',
' -foo-a-b-foo: foo;',
'}'
]);
helpers.test('should support inline expressions', [
'foo #{1 + 2} baz {a: b}'
], [
'foo 3 baz {',
' a: b;',
'}'
]);
});
describe('misc', function () {
helpers.test('should not require to have a semi-colon at the end of the last rule in the ruleset', [
'blat {a: test}'
], [
'blat {',
' a: test;',
'}'
]);
});
describe('functions', function () {
var allowSetScopeWas;
helpers.test('should support named arguments as part of native functions', [
'.keyed { color: rgba($color: #a7c, $alpha: 0.4); }'
], [
'.keyed {',
' color: rgba(170, 119, 204, 0.4);',
'}'
]);
helpers.test("should support variable references with proper scope management", [
'$suffix: " end.";',
'@function funcA ($a1: "foo", $a2: $a1 + " bar", $a3: $a2 + " baz" + $suffix) ',
'{',
' @return $a3;',
'};',
'@function funcB() {',
' $suffix: " not-end.";',
' $a1: "bar";',
' @return funcA($a2: $a1 + " not-bar");',
'};',
'body { background: funcB(); }'
], [
'body {',
' background: "bar not-bar baz end.";',
'}'
],false, function(){
allowSetScopeWas = Fashion.Runtime.allowSetScopedVariables;
Fashion.Runtime.allowSetScopedVariables = false;
}, function(){
Fashion.Runtime.allowSetScopedVariables = allowSetScopeWas;
});
helpers.test("should skip cycles in name references", [
'@mixin replace-text($img, $x: 50%, $y: 50%) {',
' background: $img $x $y;',
'}',
'@mixin replace-text-with-dimensions($img, $x: 50%, $y: 50%, $inline: false) {',
' @include replace-text(if($inline, inline-image($img), $img), $x, $y);',
'}',
'body {',
' @include replace-text-with-dimensions("foo.png");',
'}'
], [
'body {',
' background: "foo.png" 50% 50%;',
'}'
]);
});
describe('CSS @-rules', function () {
// this suite exists just to make sure CSS @-rules are not parsed as directives.
helpers.test('should support @font-face', [
'@font-face {',
' a: b;',
'}'
], [
'@font-face {',
' a: b;',
'}'
]);
helpers.test('should support @keyframes', [
'@keyframes {',
' a: b;',
'}'
], [
'@keyframes {',
' a: b;',
'}'
]);
// TODO: @media should actually be parsed as a directive, but that is currently
// not supported, so make sure it can still be used as a CSS @-rule.
helpers.test('should support @media', [
'@media {',
' a: b;',
'}'
], [
'@media {',
' a: b;',
'}'
]);
helpers.test('should support @media with arguments', [
"@media screen and (max-width: 980px) and (orientation: landscape), screen and (max-width: 760px) and (orientation: portrait) {",
" .quarterly-main .thumb-wrap {",
" float: none;",
" width: 100%;",
" padding: 5px 0;",
" }",
"}"
], [
"@media screen and (max-width: 980px) and (orientation: landscape),",
"screen and (max-width: 760px) and (orientation: portrait) {",
" .quarterly-main .thumb-wrap {",
" float: none;",
" width: 100%;",
" padding: 5px 0;",
" }",
"}"
]);
helpers.test('should compare numbers with units that are not comparable', [
'$foo: dynamic(123px);',
'$bar: dynamic(123em);',
'a {',
' a: if($foo == $bar, 2, 3);',
' b: if($foo != $bar, 2, 3);',
'}'
], [
'a {',
' a: 3;',
' b: 2;',
'}'
]);
helpers.test('should compare numbers with units that are not comparable using math', [
'@function remove-unit($value) {',
' @return $value / ($value * 0 + 1);',
'}',
'$foo: dynamic(123px);',
'$bar: dynamic(123em);',
'a {',
' a: remove-unit($foo);',
'}'
], [
'a {',
' a: 123;',
'}'
]);
helpers.test('should support media queries in mixins', [
'@mixin background_general($imageName) {',
'@media only screen and (-webkit-device-pixel-ratio: 2) {',
'background-image: url(../images/xhdpi/#{$imageName}) !important;',
'}',
'@media screen and (-webkit-device-pixel-ratio: 1.5) {',
'background-image: url(../images/hdpi/#{$imageName}) !important;',
'}',
'@media only screen and (-webkit-device-pixel-ratio: 3) {',
'background-image: url(../images/xhdpi/#{$imageName}) !important;',
'}',
'@media only screen and (-webkit-device-pixel-ratio: 3.5) {',
'background-image: url(../images/xhdpi/#{$imageName}) !important;',
'}',
'@media only screen and (-webkit-device-pixel-ratio: 4) {',
'background-image: url(../images/xhdpi/#{$imageName}) !important;',
'}',
'background-image: url(../images/hdpi/#{$imageName}) !important;',
'}',
'',
'.sizeChanges {',
'@media only screen and (min-device-width: 600px) , (min-device-width : 768px) and (max-device-width : 1024px) and (orientation: landscape) {',
'@include background_general("test.png");/**nesting this inside a media query causes the error **/',
'}',
'}',
], [
'@media only screen and (min-device-width: 600px),',
'(min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {',
' .sizeChanges {',
' background-image: url(../images/hdpi/test.png) !important;',
' }',
'}',
'@media only screen and (min-device-width: 600px) and screen and (-webkit-device-pixel-ratio: 2),',
'(min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and screen and (-webkit-device-pixel-ratio: 2) {',
' .sizeChanges {',
' background-image: url(../images/xhdpi/test.png) !important;',
' }',
'}',
'@media only screen and (min-device-width: 600px) and and (-webkit-device-pixel-ratio: 1.5),',
'(min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and and (-webkit-device-pixel-ratio: 1.5) {',
' .sizeChanges {',
' background-image: url(../images/hdpi/test.png) !important;',
' }',
'}',
'@media only screen and (min-device-width: 600px) and screen and (-webkit-device-pixel-ratio: 3),',
'(min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and screen and (-webkit-device-pixel-ratio: 3) {',
' .sizeChanges {',
' background-image: url(../images/xhdpi/test.png) !important;',
' }',
'}',
'@media only screen and (min-device-width: 600px) and screen and (-webkit-device-pixel-ratio: 3.5),',
'(min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and screen and (-webkit-device-pixel-ratio: 3.5) {',
' .sizeChanges {',
' background-image: url(../images/xhdpi/test.png) !important;',
' }',
'}',
'@media only screen and (min-device-width: 600px) and screen and (-webkit-device-pixel-ratio: 4),',
'(min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) and screen and (-webkit-device-pixel-ratio: 4) {',
' .sizeChanges {',
' background-image: url(../images/xhdpi/test.png) !important;',
' }',
'}',
])
});
describe('CSS @-rules (failing)', function () {
helpers.xtest('should support @charset', [
'@charset "UTF-8";'
], [
'@charset "UTF-8";'
]);
helpers.test('should support @document', [
'@document url(http://www.w3.org/)'
], [
'@document url(http://www.w3.org/);'
]);
helpers.test('should support @page', [
'@page :pseudo-class {a: b}'
], [
"@page :pseudo-class {",
" a: b;",
"}"
]);
helpers.test('should support @supports', [
'@supports (display: flexbox) {',
' body {display: flexbox;}',
'}'
], [
'@supports (display: flexbox) {',
' body {',
' display: flexbox;',
' }',
'}'
]);
});
describe('compat', function () {
helpers.test('should skip / operations for font properties', [
'.foo { font : bold 3em/1.5em; not-font: bold (3em/1.5em);}'
], [
'.foo {',
' font: bold 3em/1.5em;',
' not-font: bold 2;',
'}'
]);
helpers.test("should skip / operations for any / operator in font properties", [
'$fa-font-size-base: 14px;',
'body {',
' font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration',
'}'
], [
'body {',
' font: normal normal normal 14px/1 FontAwesome;',
'}'
]);
helpers.test('should support classes in an inline expression', [
'foo#{".bar"} baz {a: b}'
], [
'foo.bar baz {',
' a: b;',
'}'
]);
helpers.test('should support an inline expression at the beginning of a selector', [
'#{"foo"}.bar baz {a: b}'
], [
'foo.bar baz {',
' a: b;',
'}'
]);
helpers.test('should support an inline expression with quotes in it', [
'#{"foo" + " bar"} {a: b;}'
], [
'foo bar {',
' a: b;',
'}'
]);
helpers.test('should support an inline expression without a space after it', [
'#{"foo" + " bar"}baz {a: b;}'
], [
'foo barbaz {',
' a: b;',
'}'
]);
helpers.test('should support an inline variable as part of a pseudo selector', [
'$zzz: zzz;',
':#{$zzz}::#{$zzz} { a: b; }'
], [
':zzz::zzz {',
' a: b;',
'}'
]);
helpers.test('should an inline expression inside of a element attribute selector', [
'$zzz: zzz;',
'[#{$zzz}=foo] { a: b; }'
], [
'[zzz=foo] {',
' a: b;',
'}'
]);
helpers.test('should support an inline expression as part of a property name', [
'foo {bar#{1 + 2}: blip}'
], [
'foo {',
' bar3: blip;',
'}'
]);
helpers.test('should support an inline expression with strings as part of a property name', [
'foo {bar#{"baz" + "bang"}: blip;}'
], [
'foo {',
' barbazbang: blip;',
'}'
]);
helpers.test('should support string concatenation in an inline expression that is part of a property name', [
'foo {#{"baz" + "bang"}: blip}'
], [
'foo {',
' bazbang: blip;',
'}'
]);
helpers.test('should support an inline expression as part of a class name', [
'a.#{"foo"} b',
'{color: red;}'
], [
'a.foo b {',
' color: red;',
'}'
]);
helpers.test('should support a pound sign inside of the value of a variable used inside an inline expression', [
'$bar : "#foo";',
'ul li#{$bar} a span.label { foo: bar; }'
], [
'ul li#foo a span.label {',
' foo: bar;',
'}'
]);
helpers.test('should support multiple selectors in combination with new-lines in selectors and nested rulesets', [
'foo, bar',
'baz {',
' bang, bip',
' bop {a: b;}}'
], [
'foo bang,',
'foo bip bop,',
'bar baz bang,',
'bar baz bip bop {',
' a: b;',
'}'
]);
// This breaks because a literal with a : is always considered to be a variable assignment even though it could be a selector
helpers.test('should support crazy ambiguous selectors with many pseudo classes and elements in combination with nested rulesets', [
'foo {',
' bar:baz:bang:bop:biddle:woo:look:at:all:these:pseudoclasses {a: b;};',
' bar:baz bang bop biddle woo look at all these elems {a: b;};',
' bar:baz bang bop biddle woo look at all these elems; }'
], [
'foo {',
' bar: baz bang bop biddle woo look at all these elems;',
'}',
'foo bar:baz:bang:bop:biddle:woo:look:at:all:these:pseudoclasses {',
' a: b;',
'}',
'foo bar:baz bang bop biddle woo look at all these elems {',
' a: b;',
'}'
]);
// This breaks because we don't support selectors staring with :
helpers.test('should support nested rules with fancy selectors', [
'foo {',
' .bar {a: b;}',
' :baz {c: d;}',
' bang:bop {e: f;}}'
], [
'foo .bar {',
' a: b;',
'}',
'foo :baz {',
' c: d;',
'}',
'foo bang:bop {',
' e: f;',
'}'
]);
helpers.test('should support looping over lists', [
'a {',
' @each $number in 1px 2px 3px 4px {',
' b: $number;',
' }',
'}',
'c {',
' @each $str in foo, bar, baz, bang {',
' d: $str;',
' }',
'}'
], [
'a {',
' b: 1px;',
' b: 2px;',
' b: 3px;',
' b: 4px;',
'}',
'c {',
' d: foo;',
' d: bar;',
' d: baz;',
' d: bang;',
'}'
]);
helpers.test('should support the "from 1 to 5" syntax', [
'.foo {',
' @for $var from 1 to 5 {a: $var;}',
'}'
], [
'.foo {',
' a: 1;',
' a: 2;',
' a: 3;',
' a: 4;',
'}'
]);
helpers.test('should support the "from 1 through 5" syntax', [
'.foo {',
' @for $var from 1 through 5 {a: $var;}',
'}'
], [
'.foo {',
' a: 1;',
' a: 2;',
' a: 3;',
' a: 4;',
' a: 5;',
'}'
]);
helpers.test('should support unicode characters in variable names', [
'$vär: foo;',
'blat {a: $vär;}'
], [
'blat {',
' a: foo;',
'}'
]);
// def test_css_import_directive
// assert_equal "@import url(foo.css);\n", render('@import "foo.css";')
// assert_equal "@import url(foo.css);\n", render("@import 'foo.css';")
// assert_equal "@import url(\"foo.css\");\n", render('@import url("foo.css");')
// assert_equal "@import url('foo.css');\n", render("@import url('foo.css');")
// assert_equal "@import url(foo.css);\n", render('@import url(foo.css);')
// end
//
// def test_media_import
// assert_equal("@import \"./fonts.sass\" all;\n", render("@import \"./fonts.sass\" all;"))
// end
//
// def test_http_import
// assert_equal("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";\n",
// render("@import \"http://fonts.googleapis.com/css?family=Droid+Sans\";"))
// end
//
// def test_url_import
// assert_equal("@import url(fonts.sass);\n", render("@import url(fonts.sass);"))
// end
// We have to support the following syntax
// @-webkit-keyframes x-loading-spinner-rotate{
// 0%{ -webkit-transform: rotate(0deg); }
// 8.32%{ -webkit-transform: rotate(0deg); }
//
// 8.33%{ -webkit-transform: rotate(30deg); }
// 16.65%{ -webkit-transform: rotate(30deg); }
//
// 16.66%{ -webkit-transform: rotate(60deg); }
// 24.99%{ -webkit-transform: rotate(60deg); }
//
// 25%{ -webkit-transform: rotate(90deg); }
// 33.32%{ -webkit-transform: rotate(90deg); }
//
// 33.33%{ -webkit-transform: rotate(120deg); }
// 41.65%{ -webkit-transform: rotate(120deg); }
//
// 41.66%{ -webkit-transform: rotate(150deg); }
// 49.99%{ -webkit-transform: rotate(150deg); }
//
// 50%{ -webkit-transform: rotate(180deg); }
// 58.32%{ -webkit-transform: rotate(180deg); }
//
// 58.33%{ -webkit-transform: rotate(210deg); }
// 66.65%{ -webkit-transform: rotate(210deg); }
//
// 66.66%{ -webkit-transform: rotate(240deg); }
// 74.99%{ -webkit-transform: rotate(240deg); }
//
// 75%{ -webkit-transform: rotate(270deg); }
// 83.32%{ -webkit-transform: rotate(270deg); }
//
// 83.33%{ -webkit-transform: rotate(300deg); }
// 91.65%{ -webkit-transform: rotate(300deg); }
//
// 91.66%{ -webkit-transform: rotate(330deg); }
// 100%{ -webkit-transform: rotate(330deg); }
// }
});
describe('compat', function(){
helpers.test('should support an inline expression with quotes inside a string', [
'foo[val="bar #{"foo" + " bar"} baz"] {a: b}'
], [
'foo[val="bar foo bar baz"] {',
' a: b;',
'}'
]);
helpers.test('should support nested inline expressions with quotes inside a string', [
'foo[val="bar #{#{"foo"} + #{" bar"}} baz"] {a: b}'
], [
'foo[val="bar foo bar baz"] {',
' a: b;',
'}'
]);
helpers.test('should support an inline expression as an argument to a psuedo selector', [
'foo:nth-child(#{5 + "n+" + 6}) {a: b}'
], [
'foo:nth-child(5n+6) {',
' a: b;',
'}'
]);
helpers.test('Indexed Pseudo-classes should remain intact when the same token is used multiple times', [
'foo:nth-child(5n+5) {a: b}'
], [
'foo:nth-child(5n+5) {',
' a: b;',
'}'
]);
helpers.test('Indexed Pseudo-classes should remain intact', [
'foo:nth-child(5n+8) {a: b}'
], [
'foo:nth-child(5n+8) {',
' a: b;',
'}'
]);
// We don't support namespacing yet
describe('namespacing', function () {
helpers.test('should support single namespaced selector', [
'foo {',
' bar: baz;',
' bang: {',
' bip: 1px;',
' bop: bar;}}'
], [
'foo {',
' bar: baz;',
' bang-bip: 1px;',
' bang-bop: bar;',
'}'
]);
helpers.test('should support multiple namespaced selectors', [
'foo {',
' bar: baz;',
' bang: {',
' bip: 1px;',
' bop: bar;}',
' buzz: {',
' fram: "foo";',
' frum: moo;}}'
], [
'foo {',
' bar: baz;',
' bang-bip: 1px;',
' bang-bop: bar;',
' buzz-fram: "foo";',
' buzz-frum: moo;',
'}'
]);
helpers.test('should support deeply nested namespacing', [
'foo {',
' bar: baz;',
' bang: {',
' bip: 1px;',
' bop: bar;',
' blat:{baf:bort}}}'
], [
'foo {',
' bar: baz;',
' bang-bip: 1px;',
' bang-bop: bar;',
' bang-blat-baf: bort;',
'}'
]);
helpers.test('should support a mixture of assignment and namespacing', [
'foo {',
' bar: baz {',
' bip: bop;',
' bing: bop;}}'
], [
'foo {',
' bar: baz;',
' bar-bip: bop;',
' bar-bing: bop;',
'}'
]);
helpers.test('should support a mixture of an expression assignment and nested namespacing', [
'foo {',
' bar: baz + bang {',
' bip: bop;',
' bing: bop; }}'
], [
'foo {',
' bar: bazbang;',
' bar-bip: bop;',
' bar-bing: bop;',
'}'
]);
helpers.test('should not get confused about a psuedo selector being a namespaced variable assignment', [
'foo {',
' bar:baz {',
' bip: bop;}}'
], [
'foo bar:baz {',
' bip: bop;',
'}'
]);
// The following should throw a syntax error since there is no space between bar:1px
helpers.test('should DESC', [
'foo {',
' bar:1px {',
' bip: bop}}'
], [
'foo bar:1px {',
' bip: bop;',
'}'
]);
});
helpers.test('should support "@while $i != 5" syntax', [
'$i: 1;',
'.foo {',
' @while $i != 5 {',
' a: $i;',
' $i: $i + 1;',
' }',
'}'
], [
'.foo {',
' a: 1;',
' a: 2;',
' a: 3;',
' a: 4;',
'}'
], false, function(){
Fashion.Runtime.allowSetScopedVariables = true;
}, function(){
Fashion.Runtime.allowSetScopedVariables = true;
});
// The parser fails on this
helpers.test('should support multiple selectors with accidental empty selectors', [
'#foo #bar,,',
',#baz #boom, {a: b;}',
'#bip #bop, ,, {c: d;}'
], [
'#foo #bar,',
'#baz #boom {',
' a: b;',
'}',
'#bip #bop {',
' c: d;',
'}'
]);
});
helpers.test("should allow nulls as default when configured", [
'$name: null !default;',
'$name: "foo" !default;',
'foo { background: $name + "-foo"; }',
], [
''
], false, function(){
Fashion.Runtime.allowNullDefaults = true;
});
helpers.test("should not allow nulls as default when configured", [
'$name: null !default;',
'$name: "foo" !default;',
'',
'foo {',
' background: $name;',
'}',
], [
'foo {',
' background: "foo";',
'}'
], false, function(){
Fashion.Runtime.allowNullDefaults = false;
});
helpers.test("should create scopes for global rulesets", [
'$foo: red;',
'',
'a {',
' $foo: blue;',
' color: $foo;',
'',
' &:hover {',
' $foo: orange;',
' color: $foo',
' }',
' background-color: $foo;',
'}',
'',
'b {',
' color: $foo;',