# typed: false # frozen_string_literal: true require "completions" describe Homebrew::Completions do let(:completions_dir) { HOMEBREW_REPOSITORY/"completions" } let(:internal_path) { HOMEBREW_REPOSITORY/"Library/Taps/homebrew/homebrew-bar" } let(:external_path) { HOMEBREW_REPOSITORY/"Library/Taps/foo/homebrew-bar" } before do HOMEBREW_REPOSITORY.cd do system "git", "init" end described_class::SHELLS.each do |shell| (completions_dir/shell).mkpath end internal_path.mkpath external_path.mkpath end after do FileUtils.rm_rf completions_dir FileUtils.rm_rf internal_path FileUtils.rm_rf external_path.dirname end context "when linking or unlinking completions" do def setup_completions(external:) (internal_path/"completions/bash/foo_internal").write "#foo completions" if external (external_path/"completions/bash/foo_external").write "#foo completions" elsif (external_path/"completions/bash/foo_external").exist? (external_path/"completions/bash/foo_external").delete end end def setup_completions_setting(state, setting: "linkcompletions") HOMEBREW_REPOSITORY.cd do system "git", "config", "--replace-all", "homebrew.#{setting}", state.to_s end end def read_completions_setting(setting: "linkcompletions") HOMEBREW_REPOSITORY.cd do Utils.popen_read("git", "config", "--get", "homebrew.#{setting}").chomp.presence end end def delete_completions_setting(setting: "linkcompletions") HOMEBREW_REPOSITORY.cd do system "git", "config", "--unset-all", "homebrew.#{setting}" end end describe ".link!" do it "sets homebrew.linkcompletions to true" do setup_completions_setting false expect { described_class.link! }.not_to raise_error expect(read_completions_setting).to eq "true" end it "sets homebrew.linkcompletions to true if unset" do delete_completions_setting expect { described_class.link! }.not_to raise_error expect(read_completions_setting).to eq "true" end it "keeps homebrew.linkcompletions set to true" do setup_completions_setting true expect { described_class.link! }.not_to raise_error expect(read_completions_setting).to eq "true" end end describe ".unlink!" do it "sets homebrew.linkcompletions to false" do setup_completions_setting true expect { described_class.unlink! }.not_to raise_error expect(read_completions_setting).to eq "false" end it "sets homebrew.linkcompletions to false if unset" do delete_completions_setting expect { described_class.unlink! }.not_to raise_error expect(read_completions_setting).to eq "false" end it "keeps homebrew.linkcompletions set to false" do setup_completions_setting false expect { described_class.unlink! }.not_to raise_error expect(read_completions_setting).to eq "false" end end describe ".link_completions?" do it "returns true if homebrew.linkcompletions is true" do setup_completions_setting true expect(described_class.link_completions?).to be true end it "returns false if homebrew.linkcompletions is false" do setup_completions_setting false expect(described_class.link_completions?).to be false end it "returns false if homebrew.linkcompletions is not set" do expect(described_class.link_completions?).to be false end end describe ".completions_to_link?" do it "returns false if only internal taps have completions" do setup_completions external: false expect(described_class.completions_to_link?).to be false end it "returns true if external taps have completions" do setup_completions external: true expect(described_class.completions_to_link?).to be true end end describe ".show_completions_message_if_needed" do it "doesn't show the message if there are no completions to link" do setup_completions external: false delete_completions_setting setting: :completionsmessageshown expect { described_class.show_completions_message_if_needed }.not_to output.to_stdout end it "doesn't show the message if there are completions to link but the message has already been shown" do setup_completions external: true setup_completions_setting true, setting: :completionsmessageshown expect { described_class.show_completions_message_if_needed }.not_to output.to_stdout end it "shows the message if there are completions to link and the message hasn't already been shown" do setup_completions external: true delete_completions_setting setting: :completionsmessageshown message = /Homebrew completions for external commands are unlinked by default!/ expect { described_class.show_completions_message_if_needed } .to output(message).to_stdout end end end context "when generating completions" do describe ".update_shell_completions!" do it "generates shell completions" do described_class.update_shell_completions! expect(completions_dir/"bash/brew").to be_a_file end end describe ".command_options" do it "returns an array of options for a ruby command" do expected_options = %w[--debug --help --hide --quiet --verbose] expect(described_class.command_options("missing")).to eq expected_options end it "returns an array of options for a shell command" do expected_options = %w[--debug --force --help --merge --preinstall --verbose] expect(described_class.command_options("update")).to eq expected_options end it "handles --[no]- options correctly" do options = described_class.command_options("audit") expect(options.include?("--appcast")).to eq true expect(options.include?("--no-appcast")).to eq true end it "return an empty array if command is not found" do expect(described_class.command_options("foobar")).to eq [] end it "return an empty array for a command with no options" do expect(described_class.command_options("help")).to eq [] end it "will list global options only once if overriden" do count = 0 described_class.command_options("upgrade").each do |opt| count += 1 if opt == "--verbose" end expect(count).to eq 1 end end describe ".command_gets_completions?" do it "returns true for a non-cask command with options" do expect(described_class.command_gets_completions?("install")).to eq true end it "returns false for a non-cask command with no options" do expect(described_class.command_gets_completions?("help")).to eq false end it "returns false for a cask command" do expect(described_class.command_gets_completions?("cask install")).to eq false end end describe ".generate_bash_subcommand_completion" do it "returns nil if completions aren't needed" do expect(described_class.generate_bash_subcommand_completion("help")).to be_nil end it "returns appropriate completion for a ruby command" do completion = described_class.generate_bash_subcommand_completion("missing") expect(completion).to match(/^_brew_missing\(\) {/) expect(completion).to match(/__brewcomp "\n +--debug\n +--help\n +--hide\n +--quiet\n +--verbose/s) expect(completion).to match(/__brew_complete_formulae\n}$/) end it "returns appropriate completion for a shell command" do completion = described_class.generate_bash_subcommand_completion("update") options_regex = /__brewcomp "\n +--debug\n +--force\n +--help\n +--merge\n +--preinstall\n +--verbose/ expect(completion).to match(/^_brew_update\(\) {/) expect(completion).to match(options_regex) end it "returns appropriate completion for a command with multiple named arg types" do completion = described_class.generate_bash_subcommand_completion("upgrade") expect(completion).to match(/__brew_complete_outdated_formulae\n __brew_complete_outdated_casks\n}$/) end end describe ".generate_bash_completion_file" do it "returns the correct completion file" do file = described_class.generate_bash_completion_file(%w[install missing update]) expect(file).to match(/^__brewcomp\(\) {$/) expect(file).to match(/^_brew_install\(\) {$/) expect(file).to match(/^_brew_missing\(\) {$/) expect(file).to match(/^_brew_update\(\) {$/) expect(file).to match(/^_brew\(\) {$/) expect(file).to match(/^ {4}install\) _brew_install ;;/) expect(file).to match(/^ {4}missing\) _brew_missing ;;/) expect(file).to match(/^ {4}update\) _brew_update ;;/) expect(file).to match(/^complete -o bashdefault -o default -F _brew brew$/) end end end end