# frozen_string_literal: true require 'pathname' require 'yard' RSpec.describe Yardcheck::Documentation do def yardocs_for(title) selection = YardcheckSpec::YARDOCS.select { |doc| doc.title == title } fail "Unable to find doc with title #{title}" if selection.empty? selection end def doc_for(title) described_class.new(yardocs_for(title)) end def method_object(title) doc_for(title).method_objects.first end def literal(const) Yardcheck::Typedef::Literal.new(const) end def typedef(*members) Yardcheck::Typedef.new(members) end def const(constant) Yardcheck::Const.new(constant) end let(:namespace_add) { method_object('TestApp::Namespace#add') } let(:undocumented) { method_object('TestApp::Namespace#undocumented') } let(:invalid) { method_object('TestApp::Namespace#ignoring_invalid_types') } it 'resolves constant' do expect(namespace_add.namespace).to eql(TestApp::Namespace) end it 'resolves parameters' do expect(namespace_add.params).to eql( left: typedef(literal(const(Integer))), right: typedef(literal(const(Integer))) ) end it 'resolves return value' do expect(namespace_add.return_type).to eql(typedef(literal(const(String)))) end it 'labels instance scope' do expect(namespace_add.scope).to be(:instance) end it 'exposes the selector' do expect(namespace_add.selector).to be(:add) end it 'handles documented returns without types' do aggregate_failures do expect(method_object('TestApp::Namespace#tags_without_types').return_type).to be(nil) expect(method_object('TestApp::Namespace#tags_without_types').params).to eql({}) end end it 'handles returns with a literal nil' do expect(method_object('TestApp::Namespace#return_nil').return_type) .to eql(typedef(literal(const(NilClass)))) end it 'handles methods that return instance of the class' do expect(method_object('TestApp::Namespace#return_self').return_type) .to eql(typedef(literal(const(TestApp::Namespace)))) end it 'supports [undefined]' do expect(method_object('TestApp::Namespace#undefined_return').return_type) .to eql(typedef(Yardcheck::Typedef::Undefined.new)) end it 'supports [Boolean]' do expect(method_object('TestApp::Namespace#bool_return').return_type) .to eql(typedef(literal(const(TrueClass)), literal(const(FalseClass)))) end it 'supports [Array]' do expect(method_object('TestApp::Namespace#array_return').return_type).to eql( typedef(Yardcheck::Typedef::Collection.new(const(Array), [literal(const(String))])) ) end it 'supports multiple @return' do expect(method_object('TestApp::Namespace#multiple_returns').return_type) .to eql(typedef(literal(const(String)), literal(const(NilClass)))) end it 'ignores documented params without names' do expect(method_object('TestApp::Namespace#param_without_name').params).to eql({}) end it 'ignores invalid constant resolve' do expect(method_object('TestApp::Namespace#ignoring_invalid_types').params).to be_empty end it 'produces warnings for unresolvable params and returns' do expect(invalid.warnings).to eql([ Yardcheck::Warning.new( invalid, Yardcheck::Typedef::Parser.new('TestApp::Namespace', %w[What]).parse ), Yardcheck::Warning.new( invalid, Yardcheck::Typedef::Parser.new('TestApp::Namespace', %w[Wow]).parse ) ]) end it 'does not provide a return type when documentation is invalid' do expect(invalid.return_type).to be(nil) end it 'does not produce warnings for normal methods' do aggregate_failures do expect(method_object('TestApp::Namespace#add').warnings).to eql([]) expect(undocumented.warnings).to eql([]) end end it 'exposes source' do yardoc = yardocs_for('TestApp::Namespace#return_self').first allow(yardoc).to receive(:file).and_wrap_original do |method| File.join('./test_app', method.call).to_s end method_object = described_class::MethodObject.new(yardoc) expect(method_object.source).to eql(<<~RUBY.chomp) # @return [Namespace] def return_self self end RUBY end it 'return type of undocumented' do expect(undocumented.return_type).to be(nil) end it 'identifies predicate methods' do aggregate_failures do expect(namespace_add).not_to be_predicate_method expect(method_object('TestApp::Namespace#truthy_predicate?')).to be_predicate_method end end it 'exposes documented raise' do expect(method_object('TestApp::Namespace#always_raise').raise_type).to eql( typedef(literal(const(TestApp::Namespace::AppError))) ) end end