#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../test_helper'

begin
require 'sass/less'

class LessConversionTest < Test::Unit::TestCase
  def test_variable_declarations
    assert_renders <<SCSS, <<LESS
$var1: 2px 3px;
$var2: $var1 + 7px;

$var3: fizz;

foo {
  prop: $var1 $var2 $var3; }
SCSS
@var1: 2px 3px;
@var2: @var1 + 7px;

@var3: fizz;

foo {prop: @var1 @var2 @var3}
LESS
  end

  def test_nested_variable_declarations
    assert_renders <<SCSS, <<LESS
.foo {
  $var: 2px;
  prop: $var; }
SCSS
.foo {
  @var: 2px;
  prop: @var; }
LESS
  end

  def test_import
    path = relative_path_to(File.dirname(__FILE__) + "/templates/importee.less")
    resolved_path = relative_path_to(File.dirname(__FILE__) + "/templates/importee")
    assert_renders <<SCSS, <<LESS
@import "#{resolved_path}";
@import "#{resolved_path}";

@import "#{resolved_path}";
@import "#{resolved_path}";
@import "#{resolved_path}";
SCSS
@import url(#{path});
@import url("#{path}");

@import url('#{path}');
@import '#{path}';
@import "#{path}";
LESS
  end

  def test_mixins_found_through_import
    path = relative_path_to(File.dirname(__FILE__) + "/templates/importee.less")
    resolved_path = relative_path_to(File.dirname(__FILE__) + "/templates/importee")
    assert_renders <<SCSS, <<LESS
@import "#{resolved_path}";

.baz {
  @extend .foo;
  @include bar; }
SCSS
@import "#{path}";

.baz {.foo; .bar;}
LESS
  end

  # Selectors

  def test_element_selector
    assert_renders <<SCSS, <<LESS
foo {
  a: b; }
SCSS
foo {a: b}
LESS
  end

  def test_class_selector
    assert_renders <<SCSS, <<LESS
.foo {
  a: b; }
SCSS
.foo {a: b}
LESS
  end

  def test_id_selector
    assert_renders <<SCSS, <<LESS
#foo {
  a: b; }
SCSS
#foo {a: b}
LESS
  end

  def test_pseudoclass_selector
    assert_renders <<SCSS, <<LESS
:foo {
  a: b; }
SCSS
:foo {a: b}
LESS
  end

  def test_pseudoelement_selector
    assert_renders <<SCSS, <<LESS
::foo {
  a: b; }
SCSS
::foo {a: b}
LESS
  end

  def test_comma_selector
    assert_renders <<SCSS, <<LESS
foo, .bar .baz, :bang {
  a: b; }
SCSS
foo, .bar .baz, :bang {a: b}
LESS
  end

  def test_nested_comma_selector
    assert_renders <<SCSS, <<LESS
foo bar, .baz {
  .bang, &:bip bap {
    a: b; } }
SCSS
foo bar, .baz {
  .bang, :bip bap {a: b} }
LESS
  end

  def test_simple_selector_sequence
    assert_renders <<SCSS, <<LESS
a.foo#bar[attr=val] {
  a: b; }
SCSS
a.foo#bar[attr=val] {a: b}
LESS
  end

  def test_descendant_selector
    assert_renders <<SCSS, <<LESS
.foo .bar {
  a: b; }
SCSS
.foo .bar {a: b}
LESS
  end

  def test_child_selector
    assert_renders <<SCSS, <<LESS
.foo > .bar {
  a: b; }
SCSS
.foo > .bar {a: b}
LESS
  end

  def test_adjacent_selector
    assert_renders <<SCSS, <<LESS
.foo + .bar {
  a: b; }
SCSS
.foo + .bar {a: b}
LESS
  end

  def test_pseudoclass_in_sequence
    assert_renders <<SCSS, <<LESS
.foo:bar {
  a: b; }
SCSS
.foo:bar {a: b}
LESS
  end

  def test_pseudoelement_in_sequence
    assert_renders <<SCSS, <<LESS
.foo::bar {
  a: b; }
SCSS
.foo::bar {a: b}
LESS
  end

  # Properties

  def test_space_separated_props
    assert_renders <<SCSS, <<LESS
foo {
  a: foo bar baz; }
SCSS
foo {a: foo bar baz}
LESS
  end

  def test_comma_separated_props
    assert_renders <<SCSS, <<LESS
foo {
  a: foo, bar, baz; }
SCSS
foo {a: foo, bar, baz}
LESS
  end

  def test_numbers
    assert_renders <<SCSS, <<LESS
foo {
  a: 1 2.3 -8 5px 3%; }
SCSS
foo {a: 1 2.3 -8 5px 3%}
LESS
  end

  def test_colors
    assert_renders <<SCSS, <<LESS
foo {
  a: red #abcdef blue; }
SCSS
foo {a: #f00 #abcdef blue}
LESS
  end

  def test_strings
    assert_renders <<SCSS, <<LESS
foo {
  a: "foo @var bar" "baz bang" "quote'quote" 'quote"quote'; }
SCSS
foo {a: "foo @var bar" 'baz bang' "quote'quote" 'quote"quote'}
LESS
  end

  def test_slash
    assert_renders <<SCSS, <<LESS
foo {
  a: small/8px 7em/8px;
  b: 8/4;
  c: (8 / 4); }
SCSS
foo {
  a: small/8px 7em/8px;
  b: 8/4;
  c: 8 / 4; }
LESS
  end

  def test_url
    assert_renders <<SCSS, <<LESS
foo {
  a: url("http://foobar.com/fizzle.html?foo=bar&bar=baz");
  b: url("http://foobar.com/fizzle.html?foo=bar&bar=baz");
  c: url("http://foobar.com/fizzle.html?foo=bar&bar=baz"); }
SCSS
foo {
  a: url(http://foobar.com/fizzle.html?foo=bar&bar=baz);
  b: url("http://foobar.com/fizzle.html?foo=bar&bar=baz");
  c: url('http://foobar.com/fizzle.html?foo=bar&bar=baz'); }
LESS
  end

  def test_functions
    assert_renders <<SCSS, <<LESS
foo {
  a: baz(12px) rgba(80, 70 120, 0.76);
  b: faz(1px + 3px) bang($var, #aaaaaa * 3); }
SCSS
foo {
  a: baz(12px) rgba(80, 70 120, 0.76);
  b: faz(1px + 3px) bang(@var, #aaa * 3); }
LESS
  end

  def test_alpha_function
    assert_renders <<SCSS, <<LESS
foo {
  a: alpha(opacity=2px);
  b: alpha(opacity = $var); }
SCSS
foo {
  a: alpha(opacity=2px);
  b: alpha(opacity=@var); }
LESS
  end

  def test_variables
    assert_renders <<SCSS, <<LESS
foo {
  a: $var1 $var-foo; }
SCSS
foo {a: @var1 @var-foo}
LESS
  end

  def test_operators
    assert_renders <<SCSS, <<LESS
foo {
  a: 1px + 2px;
  b: #bbaa88 - #aa1122;
  c: 5 * 3;
  d: (8 / 4); }
SCSS
foo {
  a: 1px + 2px;
  b: #ba8 - #a12;
  c: 5 * 3;
  d: 8 / 4; }
LESS
  end

  def test_operator_precedence
    assert_renders <<SCSS, <<LESS
foo {
  a: 1 + 2 * 3 + 4;
  b: 1 * 2 + 3 * 4;
  c: 1 - 2 + 2 - 4;
  d: 1 + 2 - 3 + 4;
  e: 1 / 2 - 3 / 4;
  f: 1 - 2 / 3 - 4;
  g: 1 / 2 * 3 / 4;
  h: (1 + 2) * (3 + 4);
  i: 1 * (2 + 3) * 4;
  j: 1 - (2 + 2) - 4;
  k: 1 + 2 - (3 + 4);
  l: 1 / (2 - 3) / 4;
  m: (1 - 2) / (3 - 4);
  n: 1 / (2 * 3) / 4; }
SCSS
foo {
  a: 1 + 2 * 3 + 4;
  b: 1 * 2 + 3 * 4;
  c: 1 - 2 + 2 - 4;
  d: 1 + 2 - 3 + 4;
  e: 1 / 2 - 3 / 4;
  f: 1 - 2 / 3 - 4;
  g: 1 / 2 * 3 / 4;
  h: (1 + 2) * (3 + 4);
  i: 1 * (2 + 3) * 4;
  j: 1 - (2 + 2) - 4;
  k: 1 + 2 - (3 + 4);
  l: 1 / (2 - 3) / 4;
  m: (1 - 2) / (3 - 4);
  n: 1 / (2 * 3) / 4; }
LESS
  end

  def test_operators_with_parens
    assert_renders <<SCSS, <<LESS
foo {
  a: 1px + 2px * 3;
  b: (1px - 2px) / 3; }
SCSS
foo {
  a: 1px + (2px * 3);
  b: (1px - (2px)) / 3; }
LESS
  end

  def test_unary_minus
    assert_renders <<SCSS, <<LESS
foo {
  a: 1px + -3px; }
SCSS
foo {a: 1px + (- 3px)}
LESS
  end

  # Nested Rules

  def test_single_nested_rule
    assert_renders <<SCSS, <<LESS
foo bar {
  a: b; }
SCSS
foo {bar {a: b}}
LESS
  end

  def test_single_nested_rule_with_props
    assert_renders <<SCSS, <<LESS
foo {
  bar {
    a: b; }
  c: d;
  e: f; }
SCSS
foo {
  bar {a: b}
  c: d;
  e: f; }
LESS
  end

  def test_two_nested_rules
    assert_renders <<SCSS, <<LESS
foo {
  bar {
    a: b; }
  baz {
    c: d; } }
SCSS
foo {
  bar {a: b}
  baz {c: d} }
LESS
  end

  def test_two_nested_rules_with_props
    assert_renders <<SCSS, <<LESS
foo {
  bar {
    a: b; }
  baz {
    c: d; }
  e: f;
  g: h; }
SCSS
foo {
  bar {a: b}
  baz {c: d}
  e: f;
  g: h; }
LESS
  end

  def test_nested_rules_with_combinators
    assert_renders <<SCSS, <<LESS
foo {
  > bar {
    a: b; }
  + baz {
    c: d; } }
SCSS
foo {
  > bar {a: b}
  + baz {c: d} }
LESS
  end

  def test_nested_pseudo_rules
    assert_renders <<SCSS, <<LESS
foo {
  &:bar {
    a: b; }
  &::baz {
    c: d; } }
SCSS
foo {
  :bar {a: b}
  ::baz {c: d} }
LESS
  end

  # Mixins

  def test_class_inheritance
    assert_renders <<SCSS, <<LESS
.foo {
  a: b; }

.bar {
  @extend .foo; }
SCSS
.foo {a: b}
.bar {.foo;}
LESS
  end

  def test_multiple_class_inheritance
    assert_renders <<SCSS, <<LESS
.foo {
  a: b; }

.bar {
  c: d; }

.baz {
  @extend .foo;
  @extend .bar; }
SCSS
.foo {a: b}
.bar {c: d}
.baz {.foo, .bar;}
LESS
  end

  def test_pseudoclass_inheritance
    assert_renders <<SCSS, <<LESS
:foo {
  a: b; }

:bar {
  @extend :foo; }
SCSS
:foo {a: b}
:bar {:foo;}
LESS
  end

  def test_multiple_pseudoclass_inheritance
    assert_renders <<SCSS, <<LESS
:foo:bar {
  a: b; }

:baz {
  @extend :foo:bar; }
SCSS
:foo:bar {a: b}
:baz {:foo:bar;}
LESS
  end

  def test_abstract_mixin
    assert_renders <<SCSS, <<LESS
@mixin foo {
  a: b; }

.bar {
  @include foo; }
SCSS
.foo() {a: b}
.bar {.foo;}
LESS
  end

  def test_mixin_with_args
    assert_renders <<SCSS, <<LESS
@mixin foo($a: 2px, $b: red) {
  a: $a; }

.bar {
  @include foo; }
SCSS
.foo(@a: 2px, @b: #f00) {a: @a}
.bar {.foo;}
LESS

    assert_renders <<SCSS, <<LESS
@mixin foo($a: 2px + 3px, $b: red) {
  a: $a; }

.bar {
  @include foo(5px); }
SCSS
.foo(@a: 2px + 3px, @b: #f00) {a: @a}
.bar {.foo(5px);}
LESS
  end

  ## Disallowed Mixins

  def test_nested_mixin
    assert_warning(<<WARN) {assert_renders <<SCSS, <<LESS}
WARNING: Sass doesn't support mixing in selector sequences.
Replacing ".foo .bar" with "@extend .bar"
WARN
.foo .bar {
  a: b; }

.bar {
  // .foo .bar;
  @extend .bar; }
SCSS
.foo .bar {a: b}
.bar {.foo .bar;}
LESS
  end

  def test_child_selector_mixin
    assert_warning(<<WARN) {assert_renders <<SCSS, <<LESS}
WARNING: Sass doesn't support mixing in selector sequences.
Replacing "> .bar" with "@extend .bar"
WARN
.foo > .bar {
  a: b; }

.bar {
  // > .bar;
  @extend .bar; }
SCSS
.foo > .bar {a: b}
.bar {> .bar;}
LESS
  end

  # Accessors

  def test_property_accessor
    assert_warning(<<WARN) {assert_renders <<SCSS, <<LESS}
WARNING: Sass doesn't support attribute accessors.
Ignoring .magic-box['content']
WARN
.magic-box {
  content: "gold"; }

.foo {
  content: /* .magic-box['content'] */; }
SCSS
.magic-box {content: "gold"}
.foo {content: .magic-box['content']}
LESS
  end

  def test_variable_accessor
    assert_warning(<<WARN) {assert_renders <<SCSS, <<LESS}
WARNING: Sass doesn't support attribute accessors.
Ignoring .magic-box[@content]
WARN
.magic-box {
  $content: "gold";
  content: $content; }

.foo {
  content: /* .magic-box[@content] */; }
SCSS
.magic-box {@content: "gold"; content: @content}
.foo {content: .magic-box[@content]}
LESS
  end

  private

  def assert_renders(scss, less)
    assert_equal(scss, Less::Engine.new(less).to_tree.to_sass_tree.to_scss)
  end

  # Necessary because Less can't import absolute files
  def relative_path_to(file)
    file = Pathname.new(file)
    pwd = file.absolute? ? Dir.pwd : "."
    file.relative_path_from(Pathname.new(pwd))
  end
end

rescue LoadError => e
  unless defined?(Gem)
    begin
      require 'rubygems'
      retry
    rescue Exception
    end
  end
  puts "\nCouldn't require less, skipping some tests."
end
