# frozen_string_literal: true require "spec_helper" describe Clamp::Option::Definition do context "with String argument" do let(:option) do described_class.new("--key-file", "FILE", "SSH identity") end it "has a long_switch" do expect(option.long_switch).to eql "--key-file" end it "has a type" do expect(option.type).to eql "FILE" end it "has a description" do expect(option.description).to eql "SSH identity" end describe "#attribute_name" do it "is derived from the (long) switch" do expect(option.attribute_name).to eql "key_file" end it "can be overridden" do option = described_class.new("--key-file", "FILE", "SSH identity", attribute_name: "ssh_identity") expect(option.attribute_name).to eql "ssh_identity" end end describe "#write_method" do it "is derived from the attribute_name" do expect(option.write_method).to eql "key_file=" end end describe "#default_value" do it "defaults to nil" do option = described_class.new("-n", "N", "iterations") expect(option.default_value).to eql nil end it "can be overridden" do option = described_class.new("-n", "N", "iterations", default: 1) expect(option.default_value).to eql 1 end end describe "#help" do it "combines switch, type and description" do expect(option.help).to eql ["--key-file FILE", "SSH identity"] end end end context "flag" do let(:option) do described_class.new("--verbose", :flag, "Blah blah blah") end describe "#default_conversion_block" do it "converts truthy values to true" do expect(option.default_conversion_block.call("true")).to eql true expect(option.default_conversion_block.call("yes")).to eql true end it "converts falsey values to false" do expect(option.default_conversion_block.call("false")).to eql false expect(option.default_conversion_block.call("no")).to eql false end end describe "#help" do it "excludes option argument" do expect(option.help).to eql ["--verbose", "Blah blah blah"] end end end context "negatable flag" do let(:option) do described_class.new("--[no-]force", :flag, "Force installation") end it "handles both positive and negative forms" do expect(option.handles?("--force")).to be true expect(option.handles?("--no-force")).to be true end describe "#flag_value" do it "returns true for the positive variant" do expect(option.flag_value("--force")).to be true expect(option.flag_value("--no-force")).to be false end end describe "#attribute_name" do it "is derived from the (long) switch" do expect(option.attribute_name).to eql "force" end end end context "with both short and long switches" do let(:option) do described_class.new(["-k", "--key-file"], "FILE", "SSH identity") end it "handles both switches" do expect(option.handles?("--key-file")).to be true expect(option.handles?("-k")).to be true end describe "#help" do it "includes both switches" do expect(option.help).to eql ["-k, --key-file FILE", "SSH identity"] end end end context "with an associated environment variable" do let(:option) do described_class.new("-x", "X", "mystery option", environment_variable: "APP_X") end describe "#help" do it "describes environment variable" do expect(option.help).to eql ["-x X", "mystery option (default: $APP_X)"] end end context "and a default value" do let(:option) do described_class.new("-x", "X", "mystery option", environment_variable: "APP_X", default: "xyz") end describe "#help" do it "describes both environment variable and default" do expect(option.help).to eql ["-x X", %{mystery option (default: $APP_X, or "xyz")}] end end end end context "multivalued" do let(:option) do described_class.new(["-H", "--header"], "HEADER", "extra header", multivalued: true) end it "is multivalued" do expect(option).to be_multivalued end describe "#default_value" do it "defaults to an empty Array" do expect(option.default_value).to eql [] end it "can be overridden" do option = described_class.new("-H", "HEADER", "extra header", multivalued: true, default: [1, 2, 3]) expect(option.default_value).to eql [1, 2, 3] end end describe "#attribute_name" do it "gets a _list suffix" do expect(option.attribute_name).to eql "header_list" end end describe "#append_method" do it "is derived from the attribute_name" do expect(option.append_method).to eql "append_to_header_list" end end end describe "in subcommand" do let(:command_class) do Class.new(Clamp::Command) do subcommand "foo", "FOO!" do option "--bar", "BAR", "Bars foo." end end end describe "Command#help" do it "includes help for each option exactly once" do subcommand = command_class.send(:find_subcommand, "foo") subcommand_help = subcommand.subcommand_class.help("") expect(subcommand_help.lines.grep(/--bar BAR/).count).to eql 1 end end end describe "a required option" do it "rejects :default" do expect do described_class.new("--key-file", "FILE", "SSH identity", required: true, default: "hello") end.to raise_error(ArgumentError) end it "rejects :flag options" do expect do described_class.new("--awesome", :flag, "Be awesome?", required: true) end.to raise_error(ArgumentError) end end describe "a hidden option" do let(:option) { described_class.new("--unseen", :flag, "Something", hidden: true) } it "is hidden" do expect(option).to be_hidden end end describe "a hidden option in a command" do let(:command_class) do Class.new(Clamp::Command) do option "--unseen", :flag, "Something", hidden: true def execute # this space intentionally left blank end end end it "is not shown in the help" do expect(command_class.help("foo")).not_to match(/^ +--unseen +Something$/) end it "sets the expected accessor" do command = command_class.new("foo") command.run(["--unseen"]) expect(command.unseen?).to be_truthy end end end