require 'helper' describe Thor::Actions do def runner(options={}) @runner ||= MyCounter.new([1], options, { :destination_root => destination_root }) end def action(*args, &block) capture(:stdout) { runner.send(*args, &block) } end def file File.join(destination_root, "foo") end describe "on include" do it "adds runtime options to the base class" do expect(MyCounter.class_options.keys).to include(:pretend) expect(MyCounter.class_options.keys).to include(:force) expect(MyCounter.class_options.keys).to include(:quiet) expect(MyCounter.class_options.keys).to include(:skip) end end describe "#initialize" do it "has default behavior invoke" do expect(runner.behavior).to eq(:invoke) end it "can have behavior revoke" do expect(MyCounter.new([1], {}, :behavior => :revoke).behavior).to eq(:revoke) end it "when behavior is set to force, overwrite options" do runner = MyCounter.new([1], { :force => false, :skip => true }, :behavior => :force) expect(runner.behavior).to eq(:invoke) expect(runner.options.force).to be_true expect(runner.options.skip).not_to be_true end it "when behavior is set to skip, overwrite options" do runner = MyCounter.new([1], ["--force"], :behavior => :skip) expect(runner.behavior).to eq(:invoke) expect(runner.options.force).not_to be_true expect(runner.options.skip).to be_true end end describe "accessors" do describe "#destination_root=" do it "gets the current directory and expands the path to set the root" do base = MyCounter.new([1]) base.destination_root = "here" expect(base.destination_root).to eq(File.expand_path(File.join(File.dirname(__FILE__), "..", "here"))) end it "does not use the current directory if one is given" do root = File.expand_path("/") base = MyCounter.new([1]) base.destination_root = root expect(base.destination_root).to eq(root) end it "uses the current directory if none is given" do base = MyCounter.new([1]) expect(base.destination_root).to eq(File.expand_path(File.join(File.dirname(__FILE__), ".."))) end end describe "#relative_to_original_destination_root" do it "returns the path relative to the absolute root" do expect(runner.relative_to_original_destination_root(file)).to eq("foo") end it "does not remove dot if required" do expect(runner.relative_to_original_destination_root(file, false)).to eq("./foo") end it "always use the absolute root" do runner.inside("foo") do expect(runner.relative_to_original_destination_root(file)).to eq("foo") end end it "creates proper relative paths for absolute file location" do expect(runner.relative_to_original_destination_root('/test/file')).to eq("/test/file") end it "does not fail with files constaining regexp characters" do runner = MyCounter.new([1], {}, { :destination_root => File.join(destination_root, "fo[o-b]ar") }) expect(runner.relative_to_original_destination_root("bar")).to eq("bar") end describe "#source_paths_for_search" do it "add source_root to source_paths_for_search" do expect(MyCounter.source_paths_for_search).to include(File.expand_path("fixtures", File.dirname(__FILE__))) end it "keeps only current source root in source paths" do expect(ClearCounter.source_paths_for_search).to include(File.expand_path("fixtures/bundle", File.dirname(__FILE__))) expect(ClearCounter.source_paths_for_search).not_to include(File.expand_path("fixtures", File.dirname(__FILE__))) end it "customized source paths should be before source roots" do expect(ClearCounter.source_paths_for_search[0]).to eq(File.expand_path("fixtures/doc", File.dirname(__FILE__))) expect(ClearCounter.source_paths_for_search[1]).to eq(File.expand_path("fixtures/bundle", File.dirname(__FILE__))) end it "keeps inherited source paths at the end" do expect(ClearCounter.source_paths_for_search.last).to eq(File.expand_path("fixtures/broken", File.dirname(__FILE__))) end end end describe "#find_in_source_paths" do it "raises an error if source path is empty" do expect { A.new.find_in_source_paths("foo") }.to raise_error(Thor::Error, /Currently you have no source paths/) end it "finds a template inside the source path" do expect(runner.find_in_source_paths("doc")).to eq(File.expand_path("doc", source_root)) expect{ runner.find_in_source_paths("README") }.to raise_error new_path = File.join(source_root, "doc") runner.instance_variable_set(:@source_paths, nil) runner.source_paths.unshift(new_path) expect(runner.find_in_source_paths("README")).to eq(File.expand_path("README", new_path)) end end end describe "#inside" do it "executes the block inside the given folder" do runner.inside("foo") do expect(Dir.pwd).to eq(file) end end it "changes the base root" do runner.inside("foo") do expect(runner.destination_root).to eq(file) end end it "creates the directory if it does not exist" do runner.inside("foo") do expect(File.exists?(file)).to be_true end end describe "when pretending" do it "no directories should be created" do runner.inside("bar", :pretend => true) {} expect(File.exists?("bar")).to be_false end end describe "when verbose" do it "logs status" do expect(capture(:stdout) { runner.inside("foo", :verbose => true) {} }).to match(/inside foo/) end it "uses padding in next status" do expect(capture(:stdout) { runner.inside("foo", :verbose => true) do runner.say_status :cool, :padding end }).to match(/cool padding/) end it "removes padding after block" do expect(capture(:stdout) { runner.inside("foo", :verbose => true) {} runner.say_status :no, :padding }).to match(/no padding/) end end end describe "#in_root" do it "executes the block in the root folder" do runner.inside("foo") do runner.in_root { expect(Dir.pwd).to eq(destination_root) } end end it "changes the base root" do runner.inside("foo") do runner.in_root { expect(runner.destination_root).to eq(destination_root) } end end it "returns to the previous state" do runner.inside("foo") do runner.in_root { } expect(runner.destination_root).to eq(file) end end end describe "#apply" do before do @template = <<-TEMPLATE @foo = "FOO" say_status :cool, :padding TEMPLATE @template.stub(:read).and_return(@template) @file = '/' runner.stub(:open).and_return(@template) end it "accepts a URL as the path" do @file = "http://gist.github.com/103208.txt" runner.should_receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template) action(:apply, @file) end it "accepts a secure URL as the path" do @file = "https://gist.github.com/103208.txt" runner.should_receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template) action(:apply, @file) end it "accepts a local file path with spaces" do @file = File.expand_path("fixtures/path with spaces", File.dirname(__FILE__)) runner.should_receive(:open).with(@file).and_return(@template) action(:apply, @file) end it "opens a file and executes its content in the instance binding" do action :apply, @file expect(runner.instance_variable_get("@foo")).to eq("FOO") end it "applies padding to the content inside the file" do expect(action(:apply, @file)).to match(/cool padding/) end it "logs its status" do expect(action(:apply, @file)).to match(/ apply #{@file}\n/) end it "does not log status" do content = action(:apply, @file, :verbose => false) expect(content).to match(/cool padding/) expect(content).not_to match(/apply http/) end end describe "#run" do before do runner.should_receive(:system).with("ls") end it "executes the command given" do action :run, "ls" end it "logs status" do expect(action(:run, "ls")).to eq(" run ls from \".\"\n") end it "does not log status if required" do expect(action(:run, "ls", :verbose => false)).to be_empty end it "accepts a color as status" do runner.shell.should_receive(:say_status).with(:run, 'ls from "."', :yellow) action :run, "ls", :verbose => :yellow end end describe "#run_ruby_script" do before do Thor::Util.stub!(:ruby_command).and_return("/opt/jruby") runner.should_receive(:system).with("/opt/jruby script.rb") end it "executes the ruby script" do action :run_ruby_script, "script.rb" end it "logs status" do expect(action(:run_ruby_script, "script.rb")).to eq(" run jruby script.rb from \".\"\n") end it "does not log status if required" do expect(action(:run_ruby_script, "script.rb", :verbose => false)).to be_empty end end describe "#thor" do it "executes the thor command" do runner.should_receive(:system).with("thor list") action :thor, :list, :verbose => true end it "converts extra arguments to command arguments" do runner.should_receive(:system).with("thor list foo bar") action :thor, :list, "foo", "bar" end it "converts options hash to switches" do runner.should_receive(:system).with("thor list foo bar --foo") action :thor, :list, "foo", "bar", :foo => true runner.should_receive(:system).with("thor list --foo 1 2 3") action :thor, :list, :foo => [1,2,3] end it "logs status" do runner.should_receive(:system).with("thor list") expect(action(:thor, :list)).to eq(" run thor list from \".\"\n") end it "does not log status if required" do runner.should_receive(:system).with("thor list --foo 1 2 3") expect(action(:thor, :list, :foo => [1,2,3], :verbose => false)).to be_empty end it "captures the output when :capture is given" do runner.should_receive(:`).with("thor foo bar") action(:thor, "foo", "bar", :capture => true) end end end