#!/usr/bin/env ruby
require File.join(File.dirname(__FILE__), "spec_helper")

require 'spec'
require 'irb'
require 'delegate'
require File.join(File.dirname(__FILE__), '..', 'lib', 'utility_belt', 'interactive_editor')

# Using SimpleDelegator allows us to replace the constants without triggering a
# "constant redefined" warning.
module StubPlatform
  ARCH = SimpleDelegator.new(:x86)
  OS   = SimpleDelegator.new(:unix)
  IMPL = SimpleDelegator.new(:linux)
end

# Sneak a stub Platform class into InteractiveEditor so that we don't have to
# override constants on the real Platform module
#
# P.S. This ugliness is why libraries should always prefer to expose methods
# over constants.
class InteractiveEditor
  Platform = StubPlatform
end

describe InteractiveEditor, "given no clues as to what editor to use" do
  before :each do
    ENV.delete("VISUAL")
    ENV.delete("EDITOR")
    Kernel.stub!(:test).and_return(false)
    File.stub!(:executable?).and_return(false)
  end

  it "should complain" do
    proc do
      InteractiveEditor.sensible_editor
    end.should raise_error
  end
end

describe InteractiveEditor,
         "given a Mac OS X platform and no editor environment vars" do

  before :each do
    ENV.delete("VISUAL")
    ENV.delete("EDITOR")
    @old_impl = StubPlatform::IMPL.__getobj__
    StubPlatform::IMPL.__setobj__(:macosx)
  end

  after :each do
    StubPlatform::IMPL.__setobj__(@old_impl)
  end

  it "should use the OS X 'open' command as the default editor" do
    InteractiveEditor.sensible_editor.should == "open"
  end
end

# xdg-open is a facility from the freedesktop.org, available on some recent free
# desktop operating systems (like Ubuntu).  It uses the desktop environments
# filetype associations to determine what program to open a file in.
describe InteractiveEditor,
         "given a Linux OS and no environment vars" do

  before :each do
    ENV.delete("VISUAL")
    ENV.delete("EDITOR")
    @old_impl = StubPlatform::IMPL.__getobj__
    StubPlatform::OS.__setobj__(:linux)
    File.stub!(:executable?).and_return(true)
  end

  after :each do
    StubPlatform::IMPL.__setobj__(@old_impl)
  end

  it "should attempt to use 'xdg-open' command as the default editor" do
    File.should_receive(:executable?).
      with("/usr/bin/xdg-open").
      and_return(true)
    InteractiveEditor.sensible_editor.should == "/usr/bin/xdg-open"
  end
end

# /usr/bin/sensible-editor is a Debian-ism AFAIK
describe InteractiveEditor,
         "given the existence of /usr/bin/sensible-editor and no xdg-open" do
  it "should use /usr/bin/sensible-editor as the default editor" do
    File.should_receive(:executable?).
      with("/usr/bin/xdg-open").
      and_return(false)
    File.should_receive(:executable?).
      with("/usr/bin/sensible-editor").
      and_return(true)
    InteractiveEditor.sensible_editor.should == "/usr/bin/sensible-editor"
  end
end

describe InteractiveEditor, "given an EDITOR environment variable" do
  before :each do
    File.stub!(:executable?).and_return(true)
    ENV["EDITOR"] = "MY_EDITOR"
  end

  after :each do
    ENV.delete("EDITOR")
  end

  it "should use the EDITOR environment variable to determine a sensible editor" do
    InteractiveEditor.sensible_editor.should == "MY_EDITOR"
  end
end

describe InteractiveEditor, "given a VISUAL environment variable" do
  before :each do
    File.stub!(:executable?).and_return(true)
    ENV["EDITOR"] = "MY_EDITOR"
    ENV["VISUAL"] = "MY_VISUAL_EDITOR"
  end

  after :each do
    ENV.delete("EDITOR")
    ENV.delete("VISUAL")
  end

  it "should use the environment variable to determine a sensible editor" do
    InteractiveEditor.sensible_editor.should == "MY_VISUAL_EDITOR"
  end
end

describe InteractiveEditing, "(calling out to an external editor)" do
  before :each do
    @it          = Object.new
    @it.extend(InteractiveEditing)
    @editor      = stub("Editor", :edit_interactively => nil)
    @editor_path = stub("Editor Path")
    InteractiveEditor.stub!(:sensible_editor).and_return(@editor_path)
    InteractiveEditor.stub!(:new).and_return(@editor)
  end

  it "should use InteractiveEditor to determine default editor" do
    @it.edit_interactively
    IRB.conf[:interactive_editors][@editor_path].should equal(@editor)
  end
end
