Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 242 lines (202 sloc) 8.495 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
require 'thor/runner'

describe Thor::Runner do
  def when_no_thorfiles_exist
    old_dir = Dir.pwd
    Dir.chdir '..'
    delete = Thor::Base.subclasses.select {|e| e.namespace == 'default' }
    delete.each {|e| Thor::Base.subclasses.delete e }
    yield
    Thor::Base.subclasses.concat delete
    Dir.chdir old_dir
  end

  describe "#help" do
    it "shows information about Thor::Runner itself" do
      capture(:stdout){ Thor::Runner.start(["help"]) }.should =~ /List the available thor tasks/
    end

    it "shows information about an specific Thor::Runner task" do
      content = capture(:stdout){ Thor::Runner.start(["help", "list"]) }
      content.should =~ /List the available thor tasks/
      content.should_not =~ /help \[TASK\]/
    end

    it "shows information about a specific Thor class" do
      content = capture(:stdout){ Thor::Runner.start(["help", "my_script"]) }
      content.should =~ /zoo\s+# zoo around/m
    end

    it "shows information about an specific task from an specific Thor class" do
      content = capture(:stdout){ Thor::Runner.start(["help", "my_script:zoo"]) }
      content.should =~ /zoo around/
      content.should_not =~ /help \[TASK\]/
    end

    it "shows information about a specific Thor group class" do
      content = capture(:stdout){ Thor::Runner.start(["help", "my_counter"]) }
      content.should =~ /my_counter N/
    end

    it "raises error if a class/task cannot be found" do
      content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
      content.strip.should == 'Could not find task "unknown" in "default" namespace.'
    end

    it "raises error if a class/task cannot be found for a setup without thorfiles" do
      when_no_thorfiles_exist do
        Thor::Runner.should_receive :exit
        content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
        content.strip.should == 'Could not find task "unknown".'
      end
    end
  end

  describe "#start" do
    it "invokes a task from Thor::Runner" do
      ARGV.replace ["list"]
      capture(:stdout){ Thor::Runner.start }.should =~ /my_counter N/
    end

    it "invokes a task from a specific Thor class" do
      ARGV.replace ["my_script:zoo"]
      Thor::Runner.start.should be_true
    end

    it "invokes the default task from a specific Thor class if none is specified" do
      ARGV.replace ["my_script"]
      Thor::Runner.start.should == "default task"
    end

    it "forwads arguments to the invoked task" do
      ARGV.replace ["my_script:animal", "horse"]
      Thor::Runner.start.should == ["horse"]
    end

    it "invokes tasks through shortcuts" do
      ARGV.replace ["my_script", "-T", "horse"]
      Thor::Runner.start.should == ["horse"]
    end

    it "invokes a Thor::Group" do
      ARGV.replace ["my_counter", "1", "2", "--third", "3"]
      Thor::Runner.start.should == [1, 2, 3]
    end

    it "raises an error if class/task can't be found" do
      ARGV.replace ["unknown"]
      content = capture(:stderr){ Thor::Runner.start }
      content.strip.should == 'Could not find task "unknown" in "default" namespace.'
    end

    it "raises an error if class/task can't be found in a setup without thorfiles" do
      when_no_thorfiles_exist do
        ARGV.replace ["unknown"]
        Thor::Runner.should_receive :exit
        content = capture(:stderr){ Thor::Runner.start }
        content.strip.should == 'Could not find task "unknown".'
      end
    end

    it "does not swallow NoMethodErrors that occur inside the called method" do
      ARGV.replace ["my_script:call_unexistent_method"]
      lambda { Thor::Runner.start }.should raise_error(NoMethodError)
    end

    it "does not swallow Thor::Group InvocationError" do
      ARGV.replace ["whiny_generator"]
      lambda { Thor::Runner.start }.should raise_error(ArgumentError, /thor wrong_arity takes 1 argument, but it should not/)
    end

    it "does not swallow Thor InvocationError" do
      ARGV.replace ["my_script:animal"]
      content = capture(:stderr) { Thor::Runner.start }
      content.strip.should == 'thor animal requires at least 1 argument: "thor my_script:animal TYPE".'
    end
  end

  describe "tasks" do
    before do
      @location = "#{File.dirname(__FILE__)}/fixtures/task.thor"
      @original_yaml = {
        "random" => {
          :location => @location,
          :filename => "4a33b894ffce85d7b412fc1b36f88fe0",
          :namespaces => ["amazing"]
        }
      }

      root_file = File.join(Thor::Util.thor_root, "thor.yml")

      # Stub load and save to avoid thor.yaml from being overwritten
      YAML.stub!(:load_file).and_return(@original_yaml)
      File.stub!(:exists?).with(root_file).and_return(true)
      File.stub!(:open).with(root_file, "w")
    end

    describe "list" do
      it "gives a list of the available tasks" do
        ARGV.replace ["list"]
        content = capture(:stdout) { Thor::Runner.start }
        content.should =~ /amazing:describe NAME\s+# say that someone is amazing/m
      end

      it "gives a list of the available Thor::Group classes" do
        ARGV.replace ["list"]
        capture(:stdout) { Thor::Runner.start }.should =~ /my_counter N/
      end

      it "can filter a list of the available tasks by --group" do
        ARGV.replace ["list", "--group", "standard"]
        capture(:stdout) { Thor::Runner.start }.should =~ /amazing:describe NAME/
        ARGV.replace []
        capture(:stdout) { Thor::Runner.start }.should_not =~ /my_script:animal TYPE/
        ARGV.replace ["list", "--group", "script"]
        capture(:stdout) { Thor::Runner.start }.should =~ /my_script:animal TYPE/
      end

      it "can skip all filters to show all tasks using --all" do
        ARGV.replace ["list", "--all"]
        content = capture(:stdout) { Thor::Runner.start }
        content.should =~ /amazing:describe NAME/
        content.should =~ /my_script:animal TYPE/
      end

      it "doesn't list superclass tasks in the subclass" do
        ARGV.replace ["list"]
        capture(:stdout) { Thor::Runner.start }.should_not =~ /amazing:help/
      end

      it "presents tasks in the default namespace with an empty namespace" do
        ARGV.replace ["list"]
        capture(:stdout) { Thor::Runner.start }.should =~ /^thor :cow\s+# prints 'moo'/m
      end

      it "runs tasks with an empty namespace from the default namespace" do
        ARGV.replace [":task_conflict"]
        capture(:stdout) { Thor::Runner.start }.should == "task\n"
      end

      it "runs groups even when there is a task with the same name" do
        ARGV.replace ["task_conflict"]
        capture(:stdout) { Thor::Runner.start }.should == "group\n"
      end

      it "runs tasks with no colon in the default namespace" do
        ARGV.replace ["cow"]
        capture(:stdout) { Thor::Runner.start }.should == "moo\n"
      end
    end

    describe "uninstall" do
      before do
        path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename])
        FileUtils.should_receive(:rm_rf).with(path)
      end

      it "uninstalls existing thor modules" do
        silence(:stdout) { Thor::Runner.start(["uninstall", "random"]) }
      end
    end

    describe "installed" do
      before do
        Dir.should_receive(:[]).and_return([])
      end

      it "displays the modules installed in a pretty way" do
        stdout = capture(:stdout) { Thor::Runner.start(["installed"]) }
        stdout.should =~ /random\s*amazing/
        stdout.should =~ /amazing:describe NAME\s+# say that someone is amazing/m
      end
    end

    describe "install/update" do
      before do
        FileUtils.stub!(:mkdir_p)
        FileUtils.stub!(:touch)
        $stdin.stub!(:gets).and_return("Y")

        path = File.join(Thor::Util.thor_root, Digest::MD5.hexdigest(@location + "random"))
        File.should_receive(:open).with(path, "w")
      end

      it "updates existing thor files" do
        path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename])
        if File.directory? path
          FileUtils.should_receive(:rm_rf).with(path)
        else
          File.should_receive(:delete).with(path)
        end
        silence(:stdout) { Thor::Runner.start(["update", "random"]) }
      end

      it "installs thor files" do
        ARGV.replace ["install", @location]
        silence(:stdout) { Thor::Runner.start }
      end
    end
  end
end
Something went wrong with that request. Please try again.