require 'spec_helper'

describe Less::Parser do

  cwd = Pathname(__FILE__).dirname

  it "instantiates" do
    lambda { Less::Parser.new }.should_not raise_error
  end

  describe "simple usage" do
    it "parse less into a tree" do
      root = subject.parse(".class {width: 1+1}")
      root.to_css.gsub(/\n/,'').should eql ".class {  width: 2;}"
    end

    it "accepts options when assembling the parse tree" do
      subject.parse(".class {width: 1+1}").to_css(:compress => true).strip.should eql ".class{width:2}"
    end
  end

  it "throws a ParseError if the lesscss is bogus" do
    lambda { subject.parse('{^)') }.should raise_error(Less::ParseError, /missing opening `\(`/)
  end

  it "passes exceptions from the less compiler" do
    lambda { subject.parse('body { color: @a; }').to_css }.should raise_error(Less::ParseError, /variable @a is undefined/)
  end

  describe "when configured with source mapping" do
    subject { Less::Parser.new(:filename => 'one.less', :paths => [ cwd.join('one'), cwd.join('two') ], :dumpLineNumbers => 'mediaquery') }

    it "prints source maps" do
      subject.parse('@import "one.less"; @import "two.less";').to_css(:compress => false).gsub(/\n/,'').strip.should eql "@media -sass-debug-info{filename{font-family:file\\:\\/\\/one\\.less}line{font-family:\\000031}}.one {  width: 1;}@media -sass-debug-info{filename{font-family:file\\:\\/\\/two\\.less}line{font-family:\\000031}}.two {  width: 1;}"
    end

  end

  describe "when configured with multiple load paths" do
    subject { Less::Parser.new :paths => [ cwd.join('one'), cwd.join('two'), cwd.join('faulty') ] }

    it "will load files from both paths" do
      subject.parse('@import "one.less";').to_css.gsub(/\n/,'').strip.should eql ".one {  width: 1;}"
      subject.parse('@import "two.less";').to_css.gsub(/\n/,'').strip.should eql ".two {  width: 1;}"
    end

    it "passes exceptions from less imported less files" do
      lambda { subject.parse('@import "faulty.less";').to_css }.should raise_error(Less::ParseError, /variable @a is undefined/)
    end

    it "will track imported files" do
      subject.parse('@import "one.less";')
      subject.parse('@import "two.less";')
      # Parser#imports returns full path names
      subject.imports.grep(/one\.less$/).should_not be_empty
      subject.imports.grep(/two\.less$/).should_not be_empty
    end

    it "reports type, line, column and filename of (parse) error" do
      begin
        subject.parse('@import "faulty.less";').to_css
      rescue Less::ParseError => e
        e.type.should == 'Name'
        e.filename.should == cwd.join('faulty/faulty.less').to_s
        e.line.should == 2
        e.column.should == 9
      else
        fail "parse error not raised"
      end
    end

  end

  describe "when load paths are specified in as default options" do
    before do
      Less.paths << cwd.join('one')
      Less.paths << cwd.join('two')
      @parser = Less::Parser.new
    end
    after do
      Less.paths.clear
    end

    it "will load files from default load paths" do
      @parser.parse('@import "one.less";').to_css.gsub(/\n/,'').strip.should eql ".one {  width: 1;}"
      @parser.parse('@import "two.less";').to_css.gsub(/\n/,'').strip.should eql ".two {  width: 1;}"
    end
  end

  describe "relative urls" do

    it "keeps relative imports when true" do
      parser = Less::Parser.new :paths => [ cwd ], :relativeUrls => true
      expected = "@import \"some/some.css\";\nbody {\n  background: url('some/assets/logo.png');\n}\n"
      expect( parser.parse('@import "some/some.less";').to_css ).to eql expected
    end

    it "does not keep relative imports when false" do
      parser = Less::Parser.new :paths => [ cwd ], :relativeUrls => false
      expected = "@import \"some.css\";\nbody {\n  background: url('assets/logo.png');\n}\n"
      expect( parser.parse('@import "some/some.less";').to_css ).to eql expected
    end

  end

  # NOTE: runs JS tests from less.js it's a replacement for less-test.js
  describe "less-test", :integration => true do

    TEST_LESS_DIR = File.expand_path('../../lib/less/js/test/less', File.dirname(__FILE__))
    TEST_CSS_DIR =  File.expand_path('../../lib/less/js/test/css' , File.dirname(__FILE__))

    before :all do
      # functions.less test expects these exposed :
      Less.tree.functions[:add] = lambda do |*args| # function (a, b)
        a, b = args[-2], args[-1]
        Less.tree['Dimension'].new(a['value'] + b['value'])
        # return new(less.tree.Dimension)(a.value + b.value);
      end
      Less.tree.functions[:increment] = lambda do |*args| # function (a)
        a = args[-1]
        Less.tree['Dimension'].new(a['value'] + 1)
        # return new(less.tree.Dimension)(a.value + 1);
      end
      Less.tree.functions[:_color] = lambda do |*args| # function (str)
        str = args[-1]
        if str.value == 'evil red'
        # if (str.value === "evil red")
          Less.tree['Color'].new('600')
          # return new(less.tree.Color)("600")
        end
      end
    end

    after :all do
      Less.tree.functions[:add] = nil
      Less.tree.functions[:increment] = nil
      Less.tree.functions[:_color] = nil
    end

    Dir.glob(File.join(TEST_LESS_DIR, '*.less')).each do |less_file|

      base_name = File.basename(less_file, '.less')
      css_file = File.join(TEST_CSS_DIR, "#{base_name}.css")
      raise "missing css file: #{css_file}" unless File.exists?(css_file)

      less_content = File.read(less_file)
      case base_name
        when 'javascript'
          # adjust less .eval line :
          #   title: `process.title`;
          # later replaced by line :
          #   title: `typeof process.title`;
          # with something that won't fail (since we're not in Node.JS)
          less_content.sub!('process.title', '"node"')
      end

      it "#{base_name}.less" do
        parser = Less::Parser.new(:filename => less_file, :paths => [ File.dirname(less_file) ])
        less = parser.parse( less_content )
        less.to_css(:strictMath => true, :silent => true).should == File.read(css_file)
      end

    end

  end

end
