hanami-view/spec/unit/part_spec.rb

172 lines
4.5 KiB
Ruby

# frozen_string_literal: true
require "dry/view/scope_builder"
require "dry/view/render_environment_missing"
RSpec::Matchers.define :scope do |locals|
match do |actual|
locals == actual._locals
end
end
RSpec.describe Dry::View::Part do
let(:name) { :user }
let(:value) { double(:value) }
let(:render_env) {
Dry::View::RenderEnvironment.new(
renderer: renderer,
inflector: Dry::Inflector.new,
context: Dry::View::Context.new,
scope_builder: Dry::View::ScopeBuilder.new,
part_builder: Dry::View::ScopeBuilder.new
)
}
let(:renderer) { spy(:renderer, format: :xml) }
context "with a render environment" do
subject(:part) {
described_class.new(
name: name,
value: value,
render_env: render_env
)
}
describe "#render" do
it "renders a partial with the part available in its scope" do
part.render(:info)
expect(renderer).to have_received(:partial).with(:info, scope(user: part))
end
it "allows the part to be made available on a different name" do
part.render(:info, as: :admin)
expect(renderer).to have_received(:partial).with(:info, scope(admin: part))
end
it "includes extra locals in the scope" do
part.render(:info, extra_local: "hello")
expect(renderer).to have_received(:partial).with(:info, scope(user: part, extra_local: "hello"))
end
end
describe "#to_s" do
before do
allow(value).to receive(:to_s).and_return "to_s on the value"
end
it "delegates to the wrapped value" do
expect(part.to_s).to eq "to_s on the value"
end
end
describe "#new" do
it "preserves render environment" do
new_part = part.new(value: "new value")
expect(new_part._render_env).to be part._render_env
end
end
describe "#inspect" do
it "includes the clsas name, name, and value only" do
expect(part.inspect).to eq "#<Dry::View::Part name=:user value=#<Double :value>>"
end
end
describe "#_format" do
it "returns the render environment's format" do
expect(part._format).to eq :xml
end
end
describe "#method_missing" do
let(:value) { double(greeting: "hello from value") }
it "calls a matching method on the value" do
expect(part.greeting).to eq "hello from value"
end
it "forwards all arguments to the method" do
blk = -> {}
part.greeting "args", &blk
expect(value).to have_received(:greeting).with("args", &blk)
end
it "raises an error if no method matches" do
expect { part.farewell }.to raise_error(NoMethodError)
end
end
describe "#respond_to?" do
let(:value) { double(greeting: "hello from value") }
it "handles convenience methods" do
expect(part).to respond_to(:format)
expect(part).to respond_to(:context)
expect(part).to respond_to(:render)
expect(part).to respond_to(:scope)
expect(part).to respond_to(:value)
end
it "handles value methods" do
expect(part).to respond_to(:greeting)
end
end
end
context "without a render environment" do
subject(:part) {
described_class.new(
name: name,
value: value
)
}
describe "#format" do
it "raises an error" do
expect { part.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end
end
describe "#render" do
it "raises an error" do
expect { part.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end
end
describe "#scope" do
it "raises an error" do
expect { part.scope(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end
end
end
context "without a name provided" do
describe "#_name" do
context "when class has a name" do
before do
Test::MyPart = Class.new(Dry::View::Part)
end
subject(:part) {
Test::MyPart.new(value: value)
}
it "is inferred from the class name" do
expect(part._name).to eq "my_part"
end
end
context "when class is anonymous" do
subject(:part) {
Class.new(Dry::View::Part).new(value: value)
}
it "defaults to 'part'" do
expect(part._name).to eq "part"
end
end
end
end
end