UNPKG

@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
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;',