require 'builder' describe Builder::XmlMarkup do let(:options) { {} } let(:target) { nil } let(:builder) { args = options args[:target] = target if target Builder::XmlMarkup.new args } RSpec::Matchers.define :produce_xml do |expected_xml| def get_xml(actual) if target raise "Expected subject to be a #{target.class} object but was a #{actual.class}" unless actual.class == target.class actual.string else # Deal with string mutation class actual.to_s end end match do |actual_xml| get_xml(actual) == expected_xml end failure_message do |actual_xml| "Expected '#{expected_xml}' but got '#{get_xml(actual)}'" end end RSpec.shared_context :examples do context 'basic case' do subject { builder.person { |b| b.name("Jim"); b.phone("555-1234") } } it { is_expected.to produce_xml 'Jim555-1234' } end context 'indent' do let(:options) { {indent: 2}} subject { builder.person { |b| b.name("Jim"); b.phone("555-1234") } } it { is_expected.to produce_xml "\n Jim\n 555-1234\n\n" } end describe 'attributes' do subject { builder.sample escaped: input[0], unescaped: input[1] } context 'normal' do let(:input) { ['This and That', 'Here and There' ]} it { is_expected.to produce_xml '' } end context 'xml entities' do context 'amp' do let(:input) { ['This&That', 'Here&There' ]} it { is_expected.to produce_xml '' } end context 'lt' do let(:input) { ['This' } end context 'gt' do let(:input) { ['This>That', 'Here>There' ]} it { is_expected.to produce_xml '' } end context 'apos' do let(:input) { ["This'That", 'Here'There' ]} it { is_expected.to produce_xml '' } end end context 'quotes' do subject { builder.sample escaped: 'This"That' } it { is_expected.to produce_xml '' } end context 'line feed' do subject { builder.sample escaped: "This\nThat" } it { is_expected.to produce_xml '' } end context 'carriage return' do subject { builder.sample escaped: "This\rThat" } it { is_expected.to produce_xml '' } end end describe '#comment!' do # indent required for some reason (see builder source) let(:options) { {indent: 1}} subject { builder.comment! 'This is a comment' } it { is_expected.to produce_xml "\n" } end describe '#instruct!' do # indent required for some reason (see builder source) let(:options) { {indent: 1}} subject { builder.instruct! :xml, version: '1.0', encoding: 'UTF-8' } it { is_expected.to produce_xml ''"\n" } end describe 'declare!' do subject { lambda { builder.declare! :DOCTYPE, :chapter, :SYSTEM, "../dtds/chapter.dtd" } } it { is_expected.to raise_exception 'declare! is not currently supported on Opal because symbols cannot be detected easily.' } end end context 'default output' do include_context :examples end context 'StringIO output' do let(:target) { StringIO.new } include_context :examples end end