docbrown/spec/models/navigation_link_spec.rb
Jeremy Friesen 0484c550c5
Create/update by NavigationLink identity (#16312)
Prior to this commit, we were treating all NavigationLink attributes as
unique.  So, were we to change a position of one of the NavigationLinks
during the add_navigation_links rake task, we would have created a new
NavigationLink (and the only one would have remained).

With this commit, we're introducing the concept of the NavigationLink's
surrogate identity, that is to say if we have two NavigationLink objects
with the same `url` and `name` we should consider them the same
NavigationLink.  This allows us to update properties of those
NavigationLinks (via the rake task) without the risk of creating new
entries.

This unblocks PR #16268 which addresses #16076.
2022-01-27 13:51:41 -05:00

93 lines
2.8 KiB
Ruby

require "rails_helper"
RSpec.describe NavigationLink, type: :model do
let(:navigation_link) { create(:navigation_link) }
describe ".create_or_update_by_identity" do
let(:attributes) { attributes_for(:navigation_link).except(:url, :id, :name).stringify_keys }
let(:name) { navigation_link.name }
# I want an existing navigation link, but don't want to apply the `let!` to the declaration as
# that impacts tests in other describe blocks.
before { navigation_link }
context "when the url already exists" do
let(:url) { navigation_link.url }
it "updates the existing NavigationLink" do
expect do
described_class.create_or_update_by_identity(url: url, name: name, **attributes.symbolize_keys)
end.not_to change(described_class, :count)
expect(navigation_link.reload.attributes.slice(*attributes.keys)).to eq(attributes)
end
end
context "when the url does not exist" do
# Creating a different URL
let(:url) { "#{navigation_link.url}-404" }
it "creates a new NavigationLink" do
expect do
described_class.create_or_update_by_identity(url: url, name: name, **attributes)
end.to change(described_class, :count).by(1)
end
end
end
describe "validations" do
describe "presence validations" do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_presence_of(:url) }
it { is_expected.to validate_presence_of(:icon) }
end
it "validates the icon" do
navigation_link.icon = "test.png"
expect(navigation_link).not_to be_valid
navigation_link.icon = "<svg foo='bar'>"
expect(navigation_link).to be_valid
navigation_link.icon = "<svg foo='bar'\nbaz='lol'\n\n more stuff...\n\n. >"
expect(navigation_link).to be_valid
end
context "when validating the URL" do
it "does not allow invalid URLs" do
navigation_link.url = "test"
expect(navigation_link).not_to be_valid
end
it "does allow relative URLs" do
navigation_link.url = "/test"
expect(navigation_link).to be_valid
end
end
end
describe "callbacks" do
let(:base_url) { "https://testforem.com" }
before { allow(URL).to receive(:url).and_return(base_url) }
it "normalizes local URLs to relative URLs on save" do
navigation_link.url = "#{base_url}/test"
navigation_link.save
expect(navigation_link.url).to eq "/test"
end
it "persists relative URLs unchanged" do
navigation_link.url = "/test"
navigation_link.save
expect(navigation_link.url).to eq "/test"
end
it "persists external URLs unchanged" do
url = "https://example.com/test"
navigation_link.url = url
navigation_link.save
expect(navigation_link.url).to eq url
end
end
end