561 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			561 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| describe Cask::DSL, :cask do
 | |
|   let(:cask) { Cask::CaskLoader.load(cask_path(token.to_s)) }
 | |
|   let(:token) { "basic-cask" }
 | |
| 
 | |
|   describe "stanzas" do
 | |
|     it "lets you set url, homepage, and version" do
 | |
|       expect(cask.url.to_s).to eq("https://brew.sh/TestCask-1.2.3.dmg")
 | |
|       expect(cask.homepage).to eq("https://brew.sh/")
 | |
|       expect(cask.version.to_s).to eq("1.2.3")
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "when a Cask includes an unknown method" do
 | |
|     let(:attempt_unknown_method) do
 | |
|       Cask::Cask.new("unexpected-method-cask") do
 | |
|         future_feature :not_yet_on_your_machine
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     it "prints an error that it has encountered an unexpected method" do
 | |
|       expected = Regexp.compile(<<~EOS.lines.map(&:chomp).join)
 | |
|         (?m)
 | |
|         Error:
 | |
|         .*
 | |
|         Unexpected method 'future_feature' called on Cask unexpected-method-cask\\.
 | |
|         .*
 | |
|         https://github.com/Homebrew/homebrew-cask#reporting-bugs
 | |
|       EOS
 | |
| 
 | |
|       expect do
 | |
|         expect { attempt_unknown_method }.not_to output.to_stdout
 | |
|       end.to output(expected).to_stderr
 | |
|     end
 | |
| 
 | |
|     it "will simply warn, not throw an exception" do
 | |
|       expect do
 | |
|         attempt_unknown_method
 | |
|       end.not_to raise_error
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "header line" do
 | |
|     context "when invalid" do
 | |
|       let(:token) { "invalid/invalid-header-format" }
 | |
| 
 | |
|       it "raises an error" do
 | |
|         expect { cask }.to raise_error(Cask::CaskUnreadableError)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "when token does not match the file name" do
 | |
|       let(:token) { "invalid/invalid-header-token-mismatch" }
 | |
| 
 | |
|       it "raises an error" do
 | |
|         expect do
 | |
|           cask
 | |
|         end.to raise_error(Cask::CaskTokenMismatchError, /header line does not match the file name/)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "when it contains no DSL version" do
 | |
|       let(:token) { "no-dsl-version" }
 | |
| 
 | |
|       it "does not require a DSL version in the header" do
 | |
|         expect(cask.token).to eq("no-dsl-version")
 | |
|         expect(cask.url.to_s).to eq("https://brew.sh/TestCask-1.2.3.dmg")
 | |
|         expect(cask.homepage).to eq("https://brew.sh/")
 | |
|         expect(cask.version.to_s).to eq("1.2.3")
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "name stanza" do
 | |
|     it "lets you set the full name via a name stanza" do
 | |
|       cask = Cask::Cask.new("name-cask") do
 | |
|         name "Proper Name"
 | |
|       end
 | |
| 
 | |
|       expect(cask.name).to eq([
 | |
|         "Proper Name",
 | |
|       ])
 | |
|     end
 | |
| 
 | |
|     it "Accepts an array value to the name stanza" do
 | |
|       cask = Cask::Cask.new("array-name-cask") do
 | |
|         name ["Proper Name", "Alternate Name"]
 | |
|       end
 | |
| 
 | |
|       expect(cask.name).to eq([
 | |
|         "Proper Name",
 | |
|         "Alternate Name",
 | |
|       ])
 | |
|     end
 | |
| 
 | |
|     it "Accepts multiple name stanzas" do
 | |
|       cask = Cask::Cask.new("multi-name-cask") do
 | |
|         name "Proper Name"
 | |
|         name "Alternate Name"
 | |
|       end
 | |
| 
 | |
|       expect(cask.name).to eq([
 | |
|         "Proper Name",
 | |
|         "Alternate Name",
 | |
|       ])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "desc stanza" do
 | |
|     it "lets you set the description via a desc stanza" do
 | |
|       cask = Cask::Cask.new("desc-cask") do
 | |
|         desc "The package's description"
 | |
|       end
 | |
| 
 | |
|       expect(cask.desc).to eq("The package's description")
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "sha256 stanza" do
 | |
|     it "lets you set checksum via sha256" do
 | |
|       cask = Cask::Cask.new("checksum-cask") do
 | |
|         sha256 "imasha2"
 | |
|       end
 | |
| 
 | |
|       expect(cask.sha256).to eq("imasha2")
 | |
|     end
 | |
| 
 | |
|     context "with a different arm and intel checksum" do
 | |
|       let(:cask) do
 | |
|         Cask::Cask.new("checksum-cask") do
 | |
|           sha256 arm: "imasha2arm", intel: "imasha2intel"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context "when running on arm" do
 | |
|         before do
 | |
|           allow(Hardware::CPU).to receive(:type).and_return(:arm)
 | |
|         end
 | |
| 
 | |
|         it "stores only the arm checksum" do
 | |
|           expect(cask.sha256).to eq("imasha2arm")
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context "when running on intel" do
 | |
|         before do
 | |
|           allow(Hardware::CPU).to receive(:type).and_return(:intel)
 | |
|         end
 | |
| 
 | |
|         it "stores only the intel checksum" do
 | |
|           expect(cask.sha256).to eq("imasha2intel")
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "language stanza" do
 | |
|     context "when language is set explicitly" do
 | |
|       subject(:cask) do
 | |
|         Cask::Cask.new("cask-with-apps") do
 | |
|           language "zh" do
 | |
|             sha256 "abc123"
 | |
|             "zh-CN"
 | |
|           end
 | |
| 
 | |
|           language "en", default: true do
 | |
|             sha256 "xyz789"
 | |
|             "en-US"
 | |
|           end
 | |
| 
 | |
|           url "https://example.org/#{language}.zip"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       matcher :be_the_chinese_version do
 | |
|         match do |cask|
 | |
|           expect(cask.language).to eq("zh-CN")
 | |
|           expect(cask.sha256).to eq("abc123")
 | |
|           expect(cask.url.to_s).to eq("https://example.org/zh-CN.zip")
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       matcher :be_the_english_version do
 | |
|         match do |cask|
 | |
|           expect(cask.language).to eq("en-US")
 | |
|           expect(cask.sha256).to eq("xyz789")
 | |
|           expect(cask.url.to_s).to eq("https://example.org/en-US.zip")
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       before do
 | |
|         config = cask.config
 | |
|         config.languages = languages
 | |
|         cask.config = config
 | |
|       end
 | |
| 
 | |
|       describe "to 'zh'" do
 | |
|         let(:languages) { ["zh"] }
 | |
| 
 | |
|         it { is_expected.to be_the_chinese_version }
 | |
|       end
 | |
| 
 | |
|       describe "to 'zh-XX'" do
 | |
|         let(:languages) { ["zh-XX"] }
 | |
| 
 | |
|         it { is_expected.to be_the_chinese_version }
 | |
|       end
 | |
| 
 | |
|       describe "to 'en'" do
 | |
|         let(:languages) { ["en"] }
 | |
| 
 | |
|         it { is_expected.to be_the_english_version }
 | |
|       end
 | |
| 
 | |
|       describe "to 'xx-XX'" do
 | |
|         let(:languages) { ["xx-XX"] }
 | |
| 
 | |
|         it { is_expected.to be_the_english_version }
 | |
|       end
 | |
| 
 | |
|       describe "to 'xx-XX,zh,en'" do
 | |
|         let(:languages) { ["xx-XX", "zh", "en"] }
 | |
| 
 | |
|         it { is_expected.to be_the_chinese_version }
 | |
|       end
 | |
| 
 | |
|       describe "to 'xx-XX,en-US,zh'" do
 | |
|         let(:languages) { ["xx-XX", "en-US", "zh"] }
 | |
| 
 | |
|         it { is_expected.to be_the_english_version }
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     it "returns an empty array if no languages are specified" do
 | |
|       cask = lambda do
 | |
|         Cask::Cask.new("cask-with-apps") do
 | |
|           url "https://example.org/file.zip"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       expect(cask.call.languages).to be_empty
 | |
|     end
 | |
| 
 | |
|     it "returns an array of available languages" do
 | |
|       cask = lambda do
 | |
|         Cask::Cask.new("cask-with-apps") do
 | |
|           language "zh" do
 | |
|             sha256 "abc123"
 | |
|             "zh-CN"
 | |
|           end
 | |
| 
 | |
|           language "en-US", default: true do
 | |
|             sha256 "xyz789"
 | |
|             "en-US"
 | |
|           end
 | |
| 
 | |
|           url "https://example.org/file.zip"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       expect(cask.call.languages).to eq(["zh", "en-US"])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "app stanza" do
 | |
|     it "allows you to specify app stanzas" do
 | |
|       cask = Cask::Cask.new("cask-with-apps") do
 | |
|         app "Foo.app"
 | |
|         app "Bar.app"
 | |
|       end
 | |
| 
 | |
|       expect(cask.artifacts.map(&:to_s)).to eq(["Foo.app (App)", "Bar.app (App)"])
 | |
|     end
 | |
| 
 | |
|     it "allow app stanzas to be empty" do
 | |
|       cask = Cask::Cask.new("cask-with-no-apps")
 | |
|       expect(cask.artifacts).to be_empty
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "caveats stanza" do
 | |
|     it "allows caveats to be specified via a method define" do
 | |
|       cask = Cask::Cask.new("plain-cask")
 | |
| 
 | |
|       expect(cask.caveats).to be_empty
 | |
| 
 | |
|       cask = Cask::Cask.new("cask-with-caveats") do
 | |
|         def caveats
 | |
|           <<~EOS
 | |
|             When you install this Cask, you probably want to know this.
 | |
|           EOS
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       expect(cask.caveats).to eq("When you install this Cask, you probably want to know this.\n")
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "pkg stanza" do
 | |
|     it "allows installable pkgs to be specified" do
 | |
|       cask = Cask::Cask.new("cask-with-pkgs") do
 | |
|         pkg "Foo.pkg"
 | |
|         pkg "Bar.pkg"
 | |
|       end
 | |
| 
 | |
|       expect(cask.artifacts.map(&:to_s)).to eq(["Foo.pkg (Pkg)", "Bar.pkg (Pkg)"])
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "url stanza" do
 | |
|     let(:token) { "invalid/invalid-two-url" }
 | |
| 
 | |
|     it "prevents defining multiple urls" do
 | |
|       expect { cask }.to raise_error(Cask::CaskInvalidError, /'url' stanza may only appear once/)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "homepage stanza" do
 | |
|     let(:token) { "invalid/invalid-two-homepage" }
 | |
| 
 | |
|     it "prevents defining multiple homepages" do
 | |
|       expect { cask }.to raise_error(Cask::CaskInvalidError, /'homepage' stanza may only appear once/)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "version stanza" do
 | |
|     let(:token) { "invalid/invalid-two-version" }
 | |
| 
 | |
|     it "prevents defining multiple versions" do
 | |
|       expect { cask }.to raise_error(Cask::CaskInvalidError, /'version' stanza may only appear once/)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "arch stanza" do
 | |
|     let(:token) { "invalid/invalid-two-arch" }
 | |
| 
 | |
|     it "prevents defining multiple arches" do
 | |
|       expect { cask }.to raise_error(Cask::CaskInvalidError, /'arch' stanza may only appear once/)
 | |
|     end
 | |
| 
 | |
|     context "when no intel value is specified" do
 | |
|       let(:token) { "arch-arm-only" }
 | |
| 
 | |
|       context "when running on arm" do
 | |
|         before do
 | |
|           allow(Hardware::CPU).to receive(:type).and_return(:arm)
 | |
|         end
 | |
| 
 | |
|         it "returns the value" do
 | |
|           expect(cask.url.to_s).to eq "file://#{TEST_FIXTURE_DIR}/cask/caffeine-arm.zip"
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context "when running on intel" do
 | |
|         before do
 | |
|           allow(Hardware::CPU).to receive(:type).and_return(:intel)
 | |
|         end
 | |
| 
 | |
|         it "defaults to `nil` for the other when no arrays are passed" do
 | |
|           expect(cask.url.to_s).to eq "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "depends_on stanza" do
 | |
|     let(:token) { "invalid/invalid-depends-on-key" }
 | |
| 
 | |
|     it "refuses to load with an invalid depends_on key" do
 | |
|       expect { cask }.to raise_error(Cask::CaskInvalidError)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "depends_on formula" do
 | |
|     context "with one Formula" do
 | |
|       let(:token) { "with-depends-on-formula" }
 | |
| 
 | |
|       it "allows depends_on formula to be specified" do
 | |
|         expect(cask.depends_on.formula).not_to be_nil
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "with multiple Formulae" do
 | |
|       let(:token) { "with-depends-on-formula-multiple" }
 | |
| 
 | |
|       it "allows multiple depends_on formula to be specified" do
 | |
|         expect(cask.depends_on.formula).not_to be_nil
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "depends_on cask" do
 | |
|     context "with a single cask" do
 | |
|       let(:token) { "with-depends-on-cask" }
 | |
| 
 | |
|       it "is allowed" do
 | |
|         expect(cask.depends_on.cask).not_to be_nil
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "when specifying multiple" do
 | |
|       let(:token) { "with-depends-on-cask-multiple" }
 | |
| 
 | |
|       it "is allowed" do
 | |
|         expect(cask.depends_on.cask).not_to be_nil
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "depends_on macos" do
 | |
|     context "when the depends_on macos value is invalid" do
 | |
|       let(:token) { "invalid/invalid-depends-on-macos-bad-release" }
 | |
| 
 | |
|       it "refuses to load" do
 | |
|         expect { cask }.to raise_error(Cask::CaskInvalidError)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "when there are conflicting depends_on macos forms" do
 | |
|       let(:token) { "invalid/invalid-depends-on-macos-conflicting-forms" }
 | |
| 
 | |
|       it "refuses to load" do
 | |
|         expect { cask }.to raise_error(Cask::CaskInvalidError)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "depends_on arch" do
 | |
|     context "when valid" do
 | |
|       let(:token) { "with-depends-on-arch" }
 | |
| 
 | |
|       it "is allowed to be specified" do
 | |
|         expect(cask.depends_on.arch).not_to be_nil
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "with invalid depends_on arch value" do
 | |
|       let(:token) { "invalid/invalid-depends-on-arch-value" }
 | |
| 
 | |
|       it "refuses to load" do
 | |
|         expect { cask }.to raise_error(Cask::CaskInvalidError)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "conflicts_with stanza" do
 | |
|     context "when valid" do
 | |
|       let(:token) { "with-conflicts-with" }
 | |
| 
 | |
|       it "allows conflicts_with stanza to be specified" do
 | |
|         expect(cask.conflicts_with[:formula]).to be_empty
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "with invalid conflicts_with key" do
 | |
|       let(:token) { "invalid/invalid-conflicts-with-key" }
 | |
| 
 | |
|       it "refuses to load invalid conflicts_with key" do
 | |
|         expect { cask }.to raise_error(Cask::CaskInvalidError)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "installer stanza" do
 | |
|     context "when script" do
 | |
|       let(:token) { "with-installer-script" }
 | |
| 
 | |
|       it "allows installer script to be specified" do
 | |
|         expect(cask.artifacts.to_a.first.path).to eq(Pathname("/usr/bin/true"))
 | |
|         expect(cask.artifacts.to_a.first.args[:args]).to eq(["--flag"])
 | |
|         expect(cask.artifacts.to_a.second.path).to eq(Pathname("/usr/bin/false"))
 | |
|         expect(cask.artifacts.to_a.second.args[:args]).to eq(["--flag"])
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "when manual" do
 | |
|       let(:token) { "with-installer-manual" }
 | |
| 
 | |
|       it "allows installer manual to be specified" do
 | |
|         installer = cask.artifacts.first
 | |
|         expect(installer).to be_a(Cask::Artifact::Installer::ManualInstaller)
 | |
|         expect(installer.path).to eq(Pathname("Caffeine.app"))
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "stage_only stanza" do
 | |
|     context "when there is no other activatable artifact" do
 | |
|       let(:token) { "stage-only" }
 | |
| 
 | |
|       it "allows stage_only stanza to be specified" do
 | |
|         expect(cask.artifacts).to contain_exactly a_kind_of Cask::Artifact::StageOnly
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context "when there is are activatable artifacts" do
 | |
|       let(:token) { "invalid/invalid-stage-only-conflict" }
 | |
| 
 | |
|       it "prevents specifying stage_only" do
 | |
|         expect { cask }.to raise_error(Cask::CaskInvalidError, /'stage_only' must be the only activatable artifact/)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "auto_updates stanza" do
 | |
|     let(:token) { "auto-updates" }
 | |
| 
 | |
|     it "allows auto_updates stanza to be specified" do
 | |
|       expect(cask.auto_updates).to be true
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "#appdir" do
 | |
|     context "with interpolation of the appdir in stanzas" do
 | |
|       let(:token) { "appdir-interpolation" }
 | |
| 
 | |
|       it "is allowed" do
 | |
|         expect(cask.artifacts.first.source).to eq(cask.config.appdir/"some/path")
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     it "does not include a trailing slash" do
 | |
|       config = Cask::Config.new(explicit: {
 | |
|         appdir: "/Applications/",
 | |
|       })
 | |
| 
 | |
|       cask = Cask::Cask.new("appdir-trailing-slash", config: config) do
 | |
|         binary "#{appdir}/some/path"
 | |
|       end
 | |
| 
 | |
|       expect(cask.artifacts.first.source).to eq(Pathname("/Applications/some/path"))
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   describe "#artifacts" do
 | |
|     it "sorts artifacts according to the preferable installation order" do
 | |
|       cask = Cask::Cask.new("appdir-trailing-slash") do
 | |
|         postflight do
 | |
|           next
 | |
|         end
 | |
| 
 | |
|         preflight do
 | |
|           next
 | |
|         end
 | |
| 
 | |
|         binary "binary"
 | |
| 
 | |
|         app "App.app"
 | |
|       end
 | |
| 
 | |
|       expect(cask.artifacts.map { |artifact| artifact.class.dsl_key }).to eq [
 | |
|         :preflight,
 | |
|         :app,
 | |
|         :binary,
 | |
|         :postflight,
 | |
|       ]
 | |
|     end
 | |
|   end
 | |
| end
 | 
