require File.dirname(__FILE__) + '/../../spec_helper'
require 'mspec/runner/formatters/dotted'
require 'mspec/runner/mspec'
require 'mspec/runner/example'
require 'mspec/utils/script'

describe DottedFormatter, "#initialize" do
  it "permits zero arguments" do
    DottedFormatter.new
  end

  it "accepts one argument" do
    DottedFormatter.new nil
  end
end

describe DottedFormatter, "#register" do
  before :each do
    @formatter = DottedFormatter.new
    MSpec.stub(:register)
  end

  it "registers self with MSpec for appropriate actions" do
    MSpec.should_receive(:register).with(:exception, @formatter)
    MSpec.should_receive(:register).with(:before, @formatter)
    MSpec.should_receive(:register).with(:after, @formatter)
    MSpec.should_receive(:register).with(:finish, @formatter)
    @formatter.register
  end

  it "creates TimerAction and TallyAction" do
    timer = double("timer")
    tally = double("tally")
    timer.should_receive(:register)
    tally.should_receive(:register)
    tally.should_receive(:counter)
    TimerAction.should_receive(:new).and_return(timer)
    TallyAction.should_receive(:new).and_return(tally)
    @formatter.register
  end
end

describe DottedFormatter, "#print" do
  before :each do
    $stdout = IOStub.new
  end

  after :each do
    $stdout = STDOUT
  end

  it "writes to $stdout by default" do
    formatter = DottedFormatter.new
    formatter.print "begonias"
    $stdout.should == "begonias"
  end

  it "writes to the file specified when the formatter was created" do
    out = IOStub.new
    File.should_receive(:open).with("some/file", "w").and_return(out)
    formatter = DottedFormatter.new "some/file"
    formatter.print "begonias"
    out.should == "begonias"
  end

  it "flushes the IO output" do
    $stdout.should_receive(:flush)
    formatter = DottedFormatter.new
    formatter.print "begonias"
  end
end

describe DottedFormatter, "#exception" do
  before :each do
    @formatter = DottedFormatter.new
    @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
    @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
  end

  it "sets the #failure? flag" do
    @formatter.exception @failure
    @formatter.failure?.should be_true
    @formatter.exception @error
    @formatter.failure?.should be_false
  end

  it "sets the #exception? flag" do
    @formatter.exception @error
    @formatter.exception?.should be_true
    @formatter.exception @failure
    @formatter.exception?.should be_true
  end

  it "addes the exception to the list of exceptions" do
    @formatter.exceptions.should == []
    @formatter.exception @error
    @formatter.exception @failure
    @formatter.exceptions.should == [@error, @failure]
  end
end

describe DottedFormatter, "#exception?" do
  before :each do
    @formatter = DottedFormatter.new
    @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
    @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
  end

  it "returns false if there have been no exceptions" do
    @formatter.exception?.should be_false
  end

  it "returns true if any exceptions are errors" do
    @formatter.exception @failure
    @formatter.exception @error
    @formatter.exception?.should be_true
  end

  it "returns true if all exceptions are failures" do
    @formatter.exception @failure
    @formatter.exception @failure
    @formatter.exception?.should be_true
  end

  it "returns true if all exceptions are errors" do
    @formatter.exception @error
    @formatter.exception @error
    @formatter.exception?.should be_true
  end
end

describe DottedFormatter, "#failure?" do
  before :each do
    @formatter = DottedFormatter.new
    @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed")
    @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!")
  end

  it "returns false if there have been no exceptions" do
    @formatter.failure?.should be_false
  end

  it "returns false if any exceptions are errors" do
    @formatter.exception @failure
    @formatter.exception @error
    @formatter.failure?.should be_false
  end

  it "returns true if all exceptions are failures" do
    @formatter.exception @failure
    @formatter.exception @failure
    @formatter.failure?.should be_true
  end
end

describe DottedFormatter, "#before" do
  before :each do
    @state = ExampleState.new ContextState.new("describe"), "it"
    @formatter = DottedFormatter.new
    @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!"))
  end

  it "resets the #failure? flag to false" do
    @formatter.failure?.should be_true
    @formatter.before @state
    @formatter.failure?.should be_false
  end

  it "resets the #exception? flag to false" do
    @formatter.exception?.should be_true
    @formatter.before @state
    @formatter.exception?.should be_false
  end
end

describe DottedFormatter, "#after" do
  before :each do
    $stdout = @out = IOStub.new
    @formatter = DottedFormatter.new
    @state = ExampleState.new ContextState.new("describe"), "it"
  end

  after :each do
    $stdout = STDOUT
  end

  it "prints a '.' if there was no exception raised" do
    @formatter.after(@state)
    @out.should == "."
  end

  it "prints an 'F' if there was an expectation failure" do
    exc = SpecExpectationNotMetError.new "failed"
    @formatter.exception ExceptionState.new(@state, nil, exc)
    @formatter.after(@state)
    @out.should == "F"
  end

  it "prints an 'E' if there was an exception other than expectation failure" do
    exc = MSpecExampleError.new("boom!")
    @formatter.exception ExceptionState.new(@state, nil, exc)
    @formatter.after(@state)
    @out.should == "E"
  end

  it "prints an 'E' if there are mixed exceptions and exepctation failures" do
    exc = SpecExpectationNotMetError.new "failed"
    @formatter.exception ExceptionState.new(@state, nil, exc)
    exc = MSpecExampleError.new("boom!")
    @formatter.exception ExceptionState.new(@state, nil, exc)
    @formatter.after(@state)
    @out.should == "E"
  end
end

describe DottedFormatter, "#finish" do
  before :each do
    @tally = double("tally").as_null_object
    TallyAction.stub(:new).and_return(@tally)
    @timer = double("timer").as_null_object
    TimerAction.stub(:new).and_return(@timer)

    $stdout = @out = IOStub.new
    context = ContextState.new "Class#method"
    @state = ExampleState.new(context, "runs")
    MSpec.stub(:register)
    @formatter = DottedFormatter.new
    @formatter.register
  end

  after :each do
    $stdout = STDOUT
  end

  it "prints a failure message for an exception" do
    exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
    @formatter.exception exc
    @formatter.after @state
    @formatter.finish
    @out.should =~ /^1\)\nClass#method runs ERROR$/
  end

  it "prints a backtrace for an exception" do
    exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
    exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
    @formatter.exception exc
    @formatter.after @state
    @formatter.finish
    @out.should =~ %r[path/to/some/file.rb:35:in method$]
  end

  it "prints a summary of elapsed time" do
    @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
    @formatter.finish
    @out.should =~ /^Finished in 2.0 seconds$/
  end

  it "prints a tally of counts" do
    @tally.should_receive(:format).and_return("1 example, 0 failures")
    @formatter.finish
    @out.should =~ /^1 example, 0 failures$/
  end

  it "prints errors, backtraces, elapsed time, and tallies" do
    exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken")
    exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method")
    @formatter.exception exc
    @timer.should_receive(:format).and_return("Finished in 2.0 seconds")
    @tally.should_receive(:format).and_return("1 example, 1 failure")
    @formatter.after @state
    @formatter.finish
    @out.should ==
%[E

1)
Class#method runs ERROR
MSpecExampleError: broken
path/to/some/file.rb:35:in method

Finished in 2.0 seconds

1 example, 1 failure
]
  end
end
