
This probably has to wait until 2.7.0 now and will require a bunch of formula changes/deprecations but we should probably start moving in this direction given we're not installing any of these on our CI any more.
536 lines
14 KiB
Ruby
536 lines
14 KiB
Ruby
# typed: false
|
|
# frozen_string_literal: true
|
|
|
|
describe Cask::DSL, :cask do
|
|
let(:cask) { Cask::CaskLoader.load(cask_path(token.to_s)) }
|
|
let(:token) { "basic-cask" }
|
|
|
|
context "stanzas" do
|
|
it "lets you set url, homepage, and version" do
|
|
expect(cask.url.to_s).to eq("https://brew.sh/TestCask.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) {
|
|
Cask::Cask.new("unexpected-method-cask") do
|
|
future_feature :not_yet_on_your_machine
|
|
end
|
|
}
|
|
|
|
it "prints a warning that it has encountered an unexpected method" do
|
|
expected = Regexp.compile(<<~EOS.lines.map(&:chomp).join)
|
|
(?m)
|
|
Warning:
|
|
.*
|
|
Unexpected method 'future_feature' called on Cask unexpected-method-cask\\.
|
|
.*
|
|
https://github.com/Homebrew/homebrew-cask#reporting-bugs
|
|
EOS
|
|
|
|
expect {
|
|
expect { attempt_unknown_method }.not_to output.to_stdout
|
|
}.to output(expected).to_stderr
|
|
end
|
|
|
|
it "will simply warn, not throw an exception" do
|
|
expect {
|
|
attempt_unknown_method
|
|
}.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 {
|
|
cask
|
|
}.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.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
|
|
end
|
|
|
|
describe "language stanza" do
|
|
context "when language is set explicitly" do
|
|
subject(:cask) {
|
|
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/#{language}.zip"
|
|
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
|
|
|
|
context "to 'zh'" do
|
|
let(:languages) { ["zh"] }
|
|
|
|
it { is_expected.to be_the_chinese_version }
|
|
end
|
|
|
|
context "to 'zh-XX'" do
|
|
let(:languages) { ["zh-XX"] }
|
|
|
|
it { is_expected.to be_the_chinese_version }
|
|
end
|
|
|
|
context "to 'en'" do
|
|
let(:languages) { ["en"] }
|
|
|
|
it { is_expected.to be_the_english_version }
|
|
end
|
|
|
|
context "to 'xx-XX'" do
|
|
let(:languages) { ["xx-XX"] }
|
|
|
|
it { is_expected.to be_the_english_version }
|
|
end
|
|
|
|
context "to 'xx-XX,zh,en'" do
|
|
let(:languages) { ["xx-XX", "zh", "en"] }
|
|
|
|
it { is_expected.to be_the_chinese_version }
|
|
end
|
|
|
|
context "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 "appcast stanza" do
|
|
let(:token) { "with-appcast" }
|
|
|
|
it "allows appcasts to be specified" do
|
|
expect(cask.appcast.to_s).to match(/^http/)
|
|
end
|
|
|
|
context "when multiple appcasts are defined" do
|
|
let(:token) { "invalid/invalid-appcast-multiple" }
|
|
|
|
it "raises an error" do
|
|
expect { cask }.to raise_error(Cask::CaskInvalidError, /'appcast' stanza may only appear once/)
|
|
end
|
|
end
|
|
|
|
context "when appcast URL is invalid" do
|
|
let(:token) { "invalid/invalid-appcast-url" }
|
|
|
|
it "refuses to load" do
|
|
expect { cask }.to raise_error(Cask::CaskInvalidError)
|
|
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 "specifying one" do
|
|
let(:token) { "with-depends-on-cask" }
|
|
|
|
it "is allowed" do
|
|
expect(cask.depends_on.cask).not_to be nil
|
|
end
|
|
end
|
|
|
|
context "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 "invalid depends_on macos value" 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 "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 "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 "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 "depends_on x11" do
|
|
context "invalid depends_on x11 value" do
|
|
let(:token) { "invalid/invalid-depends-on-x11-value" }
|
|
|
|
it "refuses to load" do
|
|
expect { cask }.to raise_error(Cask::CaskInvalidError)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "conflicts_with stanza" do
|
|
context "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 "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 "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 "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 "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(&:class).map(&:dsl_key)).to eq [
|
|
:preflight,
|
|
:app,
|
|
:binary,
|
|
:postflight,
|
|
]
|
|
end
|
|
end
|
|
end
|