Skip to content

Instantly share code, notes, and snippets.

@okuramasafumi
Created May 30, 2023 08:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save okuramasafumi/471b6f7ac05e161d10baa414fc95f828 to your computer and use it in GitHub Desktop.
Save okuramasafumi/471b6f7ac05e161d10baa414fc95f828 to your computer and use it in GitHub Desktop.
RSpec documentation without executing
require 'syntax_tree'
class RSpecEnvironment
def initialize
@contexts = []
@results = []
end
def klass(k)
@contexts << k
end
def method(m)
@contexts << m
end
def context(c)
@contexts << c
end
alias describe context
def decontext
@contexts.pop
end
def it(i)
indendted_context = @contexts.map.with_index { |c, i| ' ' * i + c }.join("\n")
it_content = "#{' ' * @contexts.size}it #{i}"
@results << "#{indendted_context}\n#{it_content}"
end
def inspect
@results.join("\n")
end
end
class RSpecVisitor < SyntaxTree::Visitor
attr_reader :environment
def initialize
super
@environment = RSpecEnvironment.new
end
visit_methods do
def visit_command_call(node)
if node.receiver.child_nodes.first&.value == 'RSpec' && node.message.value == 'describe'
@environment.klass node.arguments.child_nodes.first.child_nodes.first.value
elsif node.receiver.message.value == 'expect'
subject = node.receiver.arguments.arguments.parts.first.value.value
predicate = node.arguments.parts.first.value.value
@environment.it "expect #{subject} to #{predicate}"
end
super
end
def visit_command(node)
name = node.child_nodes.first.value
if name == 'describe' || name == 'context'
str = node.arguments.child_nodes.first.parts.first.value
if str.start_with?('#')
@environment.method str
else
@environment.context str
end
elsif name == 'it'
str = node.arguments.child_nodes.first.parts.first.value
@environment.it str
end
super
if name == 'describe' || name == 'context'
@environment.decontext
end
end
end
end
visitor = RSpecVisitor.new
visitor.visit(SyntaxTree.parse(SyntaxTree.read('sample_spec.rb')))
p visitor.environment
Sample
#sample
with a context
it works
Sample
#sample
with a context
it expect true to be_truthy
Sample
#foo
some context
it expect true to be_truthy
Sample
#foo
outside context
inside context
it expect obj to be_falsey
RSpec.describe Sample do
describe '#sample' do
context 'with a context' do
subject { 'subject' }
let(:a) { 'a' }
let(:b) { 'b' }
it 'works' do
expect(true).to be_truthy
end
end
end
describe '#foo' do
subject { 1.abs }
context 'some context' do
subject { 'overridden subject' }
it do
expect(true).to be_truthy
end
end
context 'outside context' do
context 'inside context' do
let(:obj) { Object.new }
it do
expect(obj).to be_falsey
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment