# frozen_string_literal: true require File.dirname(__FILE__) + '/spec_helper' RSpec.describe YARD::Templates::Template do def template(path) YARD::Templates::Engine.template!(path, '/full/path/' + path.to_s) end before :each do YARD::Templates::ErbCache.clear! end describe ".include_parent" do it "does not include parent directory if parent directory is a template root path" do mod = template('q') expect(mod).not_to include(template('')) end it "includes overridden parent directory" do allow(Engine).to receive(:template_paths).and_return(['/foo', '/bar']) expect(File).to receive(:directory?).with('/foo/a/b').and_return(true) expect(File).to receive(:directory?).with('/bar/a/b').and_return(false) expect(File).to receive(:directory?).with('/foo/a').at_least(1).times.and_return(true) expect(File).to receive(:directory?).with('/bar/a').at_least(1).times.and_return(true) ancestors = Engine.template('a/b').ancestors.map(&:class_name) expect(ancestors[0, 3]).to eq %w(Template__foo_a_b Template__bar_a Template__foo_a) end it "includes parent directory template if exists" do mod1 = template('x') mod2 = template('x/y') expect(mod2).to include(mod1) end end describe ".full_paths" do it "lists full_path" do mod = template(:a) expect(mod.full_paths).to eq ['/full/path/a'] end it "lists paths of included modules" do mod = template(:a) mod.send(:include, template(:b)) expect(mod.full_paths).to eq ['/full/path/a', '/full/path/b'] end it "lists paths from modules of included modules" do mod = template(:c) mod.send(:include, template(:d)) mod.send(:include, template(:a)) expect(mod.full_paths).to eq ['c', 'a', 'b', 'd'].map {|o| '/full/path/' + o } end it "only lists full paths of modules that respond to full_paths" do mod = template(:d) mod.send(:include, Enumerable) expect(mod.full_paths).to eq ['/full/path/d'] end end describe ".load_setup_rb" do it "loads setup.rb file for module" do expect(File).to receive(:file?).with('/full/path/e/setup.rb').and_return(true) expect(File).to receive(:read).with('/full/path/e/setup.rb').and_return(String.new('def success; end')) expect(template(:e).new).to respond_to(:success) end end describe ".T" do it "loads template from absolute path" do mod = template(:a) expect(Engine).to receive(:template).with('other') mod.T('other') end end describe ".find_file" do it "finds file in the module's full_path" do expect(File).to receive(:file?).with('/full/path/a/basename').and_return(false) expect(File).to receive(:file?).with('/full/path/b/basename').and_return(true) expect(template(:a).find_file('basename')).to eq '/full/path/b/basename' end it "returns nil if no file is found" do expect(File).to receive(:file?).with('/full/path/a/basename').and_return(false) expect(File).to receive(:file?).with('/full/path/b/basename').and_return(false) expect(template(:a).find_file('basename')).to be nil end end describe ".find_nth_file" do it "finds 2nd existing file in template paths" do expect(File).to receive(:file?).with('/full/path/a/basename').and_return(true) expect(File).to receive(:file?).with('/full/path/b/basename').and_return(true) expect(template(:a).find_nth_file('basename', 2)).to eq '/full/path/b/basename' end it "returns nil if no file is found" do expect(File).to receive(:file?).with('/full/path/a/basename').and_return(true) expect(File).to receive(:file?).with('/full/path/b/basename').and_return(true) expect(template(:a).find_nth_file('basename', 3)).to be nil end end describe ".extra_includes" do it "is included when a module is initialized" do module MyModule; end Template.extra_includes << MyModule expect(template(:e).new).to be_kind_of(MyModule) end it "supports lambdas in list" do module MyModule2; end Template.extra_includes << lambda {|opts| MyModule2 if opts.format == :html } expect(template(:f).new(:format => :html)).to be_kind_of(MyModule2) metaclass = (class << template(:g).new(:format => :text); self end) expect(metaclass.ancestors).not_to include(MyModule2) end end describe ".is_a?" do it "is kind of Template" do expect(template(:e).is_a?(Template)).to be true end end describe "#T" do it "delegates to class method" do expect(template(:e)).to receive(:T).with('test') template(:e).new.T('test') end end describe "#init" do it "is called during initialization" do module YARD::Templates::Engine::Template__full_path_e # rubocop:disable Style/ClassAndModuleCamelCase def init; sections 1, 2, 3 end end expect(template(:e).new.sections).to eq Section.new(nil, 1, 2, 3) end end describe "#file" do it "reads the file if it exists" do expect(File).to receive(:file?).with('/full/path/e/abc').and_return(true) expect(IO).to receive(:read).with('/full/path/e/abc').and_return('hello world') expect(template(:e).new.file('abc')).to eq 'hello world' end it "raises ArgumentError if the file does not exist" do expect(File).to receive(:file?).with('/full/path/e/abc').and_return(false) expect { template(:e).new.file('abc') }.to raise_error(ArgumentError) end it "replaces {{{__super__}}} with inherited template contents if allow_inherited=true" do expect(File).to receive(:file?).with('/full/path/a/abc').twice.and_return(true) expect(File).to receive(:file?).with('/full/path/b/abc').and_return(true) expect(IO).to receive(:read).with('/full/path/a/abc').and_return(String.new('foo {{{__super__}}}')) expect(IO).to receive(:read).with('/full/path/b/abc').and_return(String.new('bar')) expect(template(:a).new.file('abc', true)).to eq "foo bar" end it "does not replace {{{__super__}}} with inherited template contents if allow_inherited=false" do expect(File).to receive(:file?).with('/full/path/a/abc').and_return(true) expect(IO).to receive(:read).with('/full/path/a/abc').and_return('foo {{{__super__}}}') expect(template(:a).new.file('abc')).to eq "foo {{{__super__}}}" end end describe "#superb" do it "returns the inherited erb template contents" do expect(File).to receive(:file?).with('/full/path/a/test.erb').and_return(true) expect(File).to receive(:file?).with('/full/path/b/test.erb').and_return(true) expect(IO).to receive(:read).with('/full/path/b/test.erb').and_return('bar') template = template(:a).new template.section = :test expect(template.superb).to eq "bar" end it "works inside an erb template" do expect(File).to receive(:file?).with('/full/path/a/test.erb').twice.and_return(true) expect(File).to receive(:file?).with('/full/path/b/test.erb').and_return(true) expect(IO).to receive(:read).with('/full/path/a/test.erb').and_return('foo<%= superb %>!') expect(IO).to receive(:read).with('/full/path/b/test.erb').and_return('bar') template = template(:a).new template.section = :test expect(template.erb(:test)).to eq "foobar!" end end describe "#sections" do it "allows sections to be set if arguments are provided" do mod = template(:e).new mod.sections 1, 2, [3] expect(mod.sections).to eq Section.new(nil, 1, 2, [3]) end end describe "#run" do it "renders all sections" do mod = template(:e).new allow(mod).to receive(:render_section) {|section| section.name.to_s } mod.sections :a, :b, :c expect(mod.run).to eq 'abc' end it "renders all sections with options" do mod = template(:e).new allow(mod).to receive(:render_section) {|section| section.name.to_s } expect(mod).to receive(:add_options).with(:a => 1).and_yield mod.sections :a expect(mod.run(:a => 1)).to eq 'a' end it "runs section list if provided" do mod = template(:e).new expect(mod).to receive(:render_section).exactly(2).times do |section| expect([:q, :x]).to include(section.name) section.name.to_s end mod.run({}, [:q, :x]) end it "accepts a nil section as empty string" do mod = template(:e).new allow(mod).to receive(:render_section) { nil } mod.sections :a expect(mod.run).to eq "" end end describe "#add_options" do it "sets instance variables in addition to options" do mod = template(:f).new mod.send(:add_options, :a => 1, :b => 2) expect(mod.options).to eq(:a => 1, :b => 2) expect(mod.instance_variable_get("@a")).to eq 1 expect(mod.instance_variable_get("@b")).to eq 2 end it "sets instance variables and options only for the block" do mod = template(:f).new mod.send(:add_options, :a => 100, :b => 200) do expect(mod.options).to eq(:a => 100, :b => 200) end expect(mod.options).not_to eq(:a => 100, :b => 200) end end describe "#render_section" do it "calls method if method exists by section name as Symbol" do mod = template(:f).new expect(mod).to receive(:respond_to?).with(:a).and_return(true) expect(mod).to receive(:respond_to?).with('a').and_return(true) expect(mod).to receive(:send).with(:a).and_return('a') expect(mod).to receive(:send).with('a').and_return('a') expect(mod.run({}, [:a, 'a'])).to eq 'aa' end it "calls erb if no method exists by section name" do mod = template(:f).new expect(mod).to receive(:respond_to?).with(:a).and_return(false) expect(mod).to receive(:respond_to?).with('a').and_return(false) expect(mod).to receive(:erb).with(:a).and_return('a') expect(mod).to receive(:erb).with('a').and_return('a') expect(mod.run({}, [:a, 'a'])).to eq 'aa' end it "runs a template if section is one" do mod2 = template(:g) expect(mod2).to receive(:run) mod = template(:f).new mod.sections mod2 mod.run end it "runs a template instance if section is one" do mod2 = template(:g).new expect(mod2).to receive(:run) mod = template(:f).new mod.sections mod2 mod.run end end describe "#yield" do it "yields a subsection" do mod = template(:e).new mod.sections :a, [:b, :c] class << mod def a; "(" + yield + ")" end def b; "b" end def c; "c" end end expect(mod.run).to eq "(b)" end it "yields a subsection within a yielded subsection" do mod = template(:e).new mod.sections :a, [:b, [:c]] class << mod def a; "(" + yield + ")" end def b; yield end def c; "c" end end expect(mod.run).to eq "(c)" end it "supports arbitrary nesting" do mod = template(:e).new mod.sections :a, [:b, [:c, [:d, [:e]]]] class << mod def a; "(" + yield + ")" end def b; yield end def c; yield end def d; yield end def e; "e" end end expect(mod.run).to eq "(e)" end it "yields first two elements if yield is called twice" do mod = template(:e).new mod.sections :a, [:b, :c, :d] class << mod def a; "(" + yield + yield + ")" end def b; 'b' end def c; "c" end end expect(mod.run).to eq "(bc)" end it "ignores any subsections inside subsection yields" do mod = template(:e).new mod.sections :a, [:b, [:c], :d] class << mod def a; "(" + yield + yield + ")" end def b; 'b' end def d; "d" end end expect(mod.run).to eq "(bd)" end it "allows extra options passed via yield" do mod = template(:e).new mod.sections :a, [:b] class << mod def a; "(" + yield(:x => "a") + ")" end def b; options.x + @x end end expect(mod.run).to eq "(aa)" end end describe "#yieldall" do it "yields all subsections" do mod = template(:e).new mod.sections :a, [:b, [:d, [:e]], :c] class << mod def a; "(" + yieldall + ")" end def b; "b" + yieldall end def c; "c" end def d; 'd' + yieldall end def e; 'e' end end expect(mod.run).to eq "(bdec)" end it "yields options to all subsections" do mod = template(:e).new mod.sections :a, [:b, :c] class << mod def a; "(" + yieldall(:x => "2") + ")" end def b; @x end def c; @x end end expect(mod.run).to eq "(22)" end it "yields all subsections more than once" do mod = template(:e).new mod.sections :a, [:b] class << mod def a; "(" + yieldall + yieldall + ")" end def b; "b" end end expect(mod.run).to eq "(bb)" end it "does not yield if no yieldall is called" do mod = template(:e).new mod.sections :a, [:b] class << mod def a; "()" end def b; "b" end end expect(mod.run).to eq "()" end end end