Testing FriendlyId Slug Candidate Generation in norman/friendly_id
This test suite validates the slug candidate functionality in FriendlyId, focusing on how the library handles URL slug generation and conflict resolution for ActiveRecord models. The tests verify various candidate configurations and edge cases for generating unique, URL-friendly identifiers.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
norman/friendly_id
test/candidates_test.rb
require "helper"
class CandidatesTest < TestCaseClass
include FriendlyId::Test
class Airport
def initialize(code)
@code = code
end
attr_reader :code
alias_method :to_s, :code
end
class City < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
alias_attribute :slug_candidates, :name
end
def model_class
City
end
def with_instances_of(klass = model_class, &block)
transaction do
city1 = klass.create! name: "New York", code: "JFK"
city2 = klass.create! name: "New York", code: "EWR"
yield city1, city2
end
end
alias_method :with_instances, :with_instances_of
test "resolves conflict with candidate" do
with_instances do |city1, city2|
assert_equal "new-york", city1.slug
assert_match(/\Anew-york-([a-z0-9]+-){4}[a-z0-9]+\z/, city2.slug)
end
end
test "accepts candidate as symbol" do
klass = Class.new model_class do
def slug_candidates
:name
end
end
with_instances_of klass do |_, city|
assert_match(/\Anew-york-([a-z0-9]+-){4}[a-z0-9]+\z/, city.slug)
end
end
test "accepts multiple candidates" do
klass = Class.new model_class do
def slug_candidates
[name, code]
end
end
with_instances_of klass do |_, city|
assert_equal "ewr", city.slug
end
end
test "ignores blank candidate" do
klass = Class.new model_class do
def slug_candidates
[name, ""]
end
end
with_instances_of klass do |_, city|
assert_match(/\Anew-york-([a-z0-9]+-){4}[a-z0-9]+\z/, city.slug)
end
end
test "ignores nil candidate" do
klass = Class.new model_class do
def slug_candidates
[name, nil]
end
end
with_instances_of klass do |_, city|
assert_match(/\Anew-york-([a-z0-9]+-){4}[a-z0-9]+\z/, city.slug)
end
end
test "accepts candidate with nested array" do
klass = Class.new model_class do
def slug_candidates
[name, [name, code]]
end
end
with_instances_of klass do |_, city|
assert_equal "new-york-ewr", city.slug
end
end
test "accepts candidate with lambda" do
klass = Class.new City do
def slug_candidates
[name, [name, -> { rand 1000 }]]
end
end
with_instances_of klass do |_, city|
assert_match(/\Anew-york-\d{,3}\z/, city.friendly_id)
end
end
test "accepts candidate with object" do
klass = Class.new City do
def slug_candidates
[name, [name, Airport.new(code)]]
end
end
with_instances_of klass do |_, city|
assert_equal "new-york-ewr", city.friendly_id
end
end
test "allows to iterate through candidates without passing block" do
klass = Class.new model_class do
def slug_candidates
:name
end
end
with_instances_of klass do |_, city|
candidates = FriendlyId::Candidates.new(city, city.slug_candidates)
assert_equal candidates.each, ["new-york"]
end
end
test "iterates through candidates with passed block" do
klass = Class.new model_class do
def slug_candidates
:name
end
end
with_instances_of klass do |_, city|
collected_candidates = []
candidates = FriendlyId::Candidates.new(city, city.slug_candidates)
candidates.each { |candidate| collected_candidates << candidate }
assert_equal collected_candidates, ["new-york"]
end
end
end