require 'spec_helper'

describe Testrbl do
  around do |example|
    run "rm -rf tmp && mkdir tmp"
    Dir.chdir "tmp" do
      example.call
    end
    run "rm -rf tmp"
  end

  def run(cmd, options={})
    result = `#{cmd} 2>&1`
    raise "FAILED #{cmd} --> #{result}" if $?.success? != !options[:fail]
    result
  end

  def testrbl(command, options={})
    run "#{File.expand_path("../../bin/testrbl", __FILE__)} #{command}", options
  end

  def write(file, content)
    folder = File.dirname(file)
    run "mkdir -p #{folder}" unless File.exist?(folder)
    File.open(file, 'w'){|f| f.write content }
  end

  it "has a VERSION" do
    Testrbl::VERSION.should =~ /^[\.\da-z]+$/
  end

  context "with a simple setup" do
    before do
      write "a_test.rb", <<-RUBY
        require 'test/unit'

        class Xxx < Test::Unit::TestCase
          def test_xxx
            puts 'ABC'
          end

          def test_yyy
            puts 'BCD'
          end
        end
      RUBY
    end

    it "runs by exact line" do
      result = testrbl "a_test.rb:4"
      result.should include "ABC\n"
      result.should_not include "BCD"
    end

    it "runs by above a line" do
      result = testrbl "a_test.rb:5"
      result.should include "ABC\n"
      result.should_not include "BCD"
    end

    it "does not run when line is before test" do
      result = testrbl "a_test.rb:3", :fail => true
      result.should include "no test found before line 3"
      result.should_not include "ABC"
    end

    it "runs whole file without number" do
      result = testrbl "a_test.rb"
      result.should include "ABC\n"
      result.should include "BCD"
    end

    it "runs with options" do
      pending "works in real console..." do
        result = testrbl "a_test.rb -n '/xxx/'"
        result.should include "ABC"
        result.should_not include "BCD"
      end
    end
  end

  context "test with string" do
    before do
      write "a_test.rb", <<-RUBY
        require 'test/unit'

        class Xxx < Test::Unit::TestCase
          test "a" do
            puts 'ABC'
          end

          test "b" do
            puts 'BCD'
          end

          test "c d" do
            puts 'CDE'
          end

          test "c" do
            puts 'DEF'
          end
        end
      RUBY
    end

    it "runs test" do
      result = testrbl "a_test.rb:8"
      result.should_not include "ABC\n"
      result.should include "BCD\n"
      result.should_not include "CDE\n"
    end

    it "runs test explicitly" do
      result = testrbl "a_test.rb:16"
      result.should_not include "CDE\n"
      result.should include "DEF\n"
    end
  end

  context "shoulda" do
    before do
      write "a_test.rb", <<-RUBY
        require 'test/unit'
        require 'shoulda'

        class Xxx < Test::Unit::TestCase
          should "a" do
            puts 'ABC'
          end

          should "b" do
            puts 'BCD'
          end

          context "c" do
            should "d" do
              puts 'CDE'
            end

            should "e" do
              puts 'DEF'
            end

            should "..'?! [(" do
              puts 'EFG'
            end
          end

          context "g a" do
            should "a" do
              puts "FGH"
            end
          end

          should "g" do
            puts "GHI"
          end
        end
      RUBY
    end

    it "runs should" do
      result = testrbl "a_test.rb:9"
      result.should_not include "ABC\n"
      result.should include "BCD\n"
      result.should_not include "CDE\n"
    end

    it "runs stuff with regex special chars" do
      result = testrbl "a_test.rb:22"
      result.should_not include "DEF\n"
      result.should include "EFG\n"
    end

    it "runs context" do
      result = testrbl "a_test.rb:13"
      result.should_not include "ABC\n"
      result.should_not include "BCD\n"
      result.should include "CDE\n"
      result.should include "DEF\n"
    end

    it "runs via nested context" do
      result = testrbl "a_test.rb:28"
      result.should_not include "ABC\n"
      result.should_not include "EFG\n"
      result.should include "FGH\n"
    end

    it "runs should explicitly" do
      result = testrbl "a_test.rb:33"
      result.should_not include "ABC\n"
      result.should include "GHI\n"
      result.should_not include "FGH\n"
    end
  end

  context "minitest test" do
    before do
      write "a_test.rb", <<-RUBY
        require 'minitest/autorun'

        class Xxx < MiniTest::Unit::TestCase
          def test_xxx
            puts 'ABC'
          end

          def test_yyy
            puts 'BCD'
          end
        end
      RUBY
    end

    it "runs" do
      result = testrbl "a_test.rb:4"
      result.should include "ABC\n"
      result.should_not include "BCD"
    end
  end

  context "minitest spec" do
    before do
      write "a_test.rb", <<-RUBY
        require 'minitest/autorun'

        describe "a" do
          it "b" do
            puts "ABC"
          end

          it "c" do
            puts "BCD"
          end
        end
      RUBY
    end

    it "runs" do
      result = testrbl "a_test.rb:4"
      result.should include "ABC\n"
      result.should_not include "BCD"
    end
  end

  context "multiple files / folders" do
    before do
      write "a_test.rb", <<-RUBY
        require 'test/unit'

        class Xxx1 < Test::Unit::TestCase
          def test_xxx
            puts 'ABC'
          end
        end
      RUBY

      write "a/a_test.rb", <<-RUBY
        require 'test/unit'

        class Xxx2 < Test::Unit::TestCase
          def test_xxx
            puts 'BCD'
          end
        end
      RUBY

      write "a/b/c_test.rb", <<-RUBY
        require 'test/unit'

        class Xxx3 < Test::Unit::TestCase
          def test_xxx
            puts 'CDE'
          end
        end
      RUBY

      write "a/c/c_test.rb", <<-RUBY
        require 'test/unit'

        class Xxx4 < Test::Unit::TestCase
          def test_xxx
            puts 'DEF'
          end
        end
      RUBY
    end

    it "runs a folder with subfolders" do
      result = testrbl "a"
      result.should_not include "ABC\n"
      result.should include "BCD\n"
      result.should include "CDE\n"
    end

    it "runs files and folders" do
      result = testrbl "a/b a/c/c_test.rb"
      result.should_not include "ABC\n"
      result.should_not include "BCD\n"
      result.should include "CDE\n"
      result.should include "DEF\n"
    end

    it "runs multiple files" do
      result = testrbl "a/b/c_test.rb a/c/c_test.rb"
      result.should_not include "ABC\n"
      result.should_not include "BCD\n"
      result.should include "CDE\n"
      result.should include "DEF\n"
    end
  end

  describe ".pattern_from_line" do
    def call(line)
      Testrbl.pattern_from_line(line)
    end

    it "finds simple tests" do
      call("  def test_xxx\n").should == ["  ", "xxx"]
    end

    it "does not find other methods" do
      call("  def xxx\n").should == nil
    end

    it "finds calls with single quotes" do
      call("  test 'xx xx' do\n").should == ["  ", "^test: xx xx$"]
    end

    it "finds test do calls" do
      call("  test \"xx xx\" do\n").should == ["  ", "^test: xx xx$"]
    end

    it "finds should do calls" do
      call("  should \"xx xx\" do\n").should == ["  ", "should xx xx. $"]
    end

    it "finds context do calls" do
      call("  context \"xx xx\" do\n").should == ["  ", "xx xx"]
    end

    it "finds context do calls with classes" do
      call("  context Foobar do\n").should == ["  ", "Foobar"]
    end

    it "finds minitest it do calls" do
      call("  it \"xx xx\" do\n").should == ["  ", "\\d+_xx_xx$"]
    end

    it "finds complex minitest it do calls" do
      call("  it \"xx ._-..  ___ xx\" do\n").should == ["  ", "\\d+_xx_______xx$"]
    end

    it "does not find minitest describe do calls since we cannot run them" do
      call("  describe Foobar do\n").should == nil
    end

    it "escapes regex chars" do
      call("  context \"xx .* xx\" do\n").should == ["  ", "xx \\.\\* xx"]
    end

    it "escapes single quotes which we use to build the pattern" do
      call("  context \"xx ' xx\" do\n").should == ["  ", "xx . xx"]
    end
  end
end