Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run with line number #154

Merged
merged 5 commits into from
Nov 29, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ Options:
-b, [--bin=BIN] # The location of the PhantomJS binary
-d, [--spec-dir=SPEC_DIR] # The directory with the Jasmine specs
# Default: spec/javascripts
-l, [--line-number=N] # The line which identifies the spec to be run
-u, [--url=URL] # The url of the Jasmine test runner_options
# Default: nil
-t, [--timeout=N] # The maximum time in seconds to wait for the spec
Expand Down
6 changes: 6 additions & 0 deletions lib/guard/jasmine/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ class CLI < Thor
aliases: '-d',
desc: 'The directory with the Jasmine specs'

method_option :line_number,
type: :numeric,
aliases: '-l',
desc: 'The line which identifies the spec to be run'

method_option :url,
type: :string,
aliases: '-u',
Expand Down Expand Up @@ -166,6 +171,7 @@ def spec(*paths)
runner_options = {}
runner_options[:port] = options.port || CLI.find_free_server_port
runner_options[:spec_dir] = options.spec_dir || (File.exists?(File.join('spec', 'javascripts')) ? File.join('spec', 'javascripts') : 'spec')
runner_options[:line_number] = options.line_number
runner_options[:server] = options.server.to_sym == :auto ? ::Guard::Jasmine::Server.detect_server(runner_options[:spec_dir]) : options.server.to_sym
runner_options[:server_mount] = options.mount || (defined?(JasmineRails) ? '/specs' : '/jasmine')
runner_options[:jasmine_url] = options.url || "http://localhost:#{ runner_options[:port] }#{ options.server.to_sym == :jasmine_gem ? '/' : runner_options[:server_mount] }"
Expand Down
95 changes: 87 additions & 8 deletions lib/guard/jasmine/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def run(paths, options = { })
notify_start_message(paths, options)

results = paths.inject([]) do |results, file|
results << evaluate_response(run_jasmine_spec(file, options), file, options) if File.exist?(file)
results << evaluate_response(run_jasmine_spec(file, options), file, options) if File.exist?(file_and_line_number_parts(file)[0])

results
end.compact
Expand Down Expand Up @@ -141,9 +141,7 @@ def phantomjs_script
end

# The suite name must be extracted from the spec that
# will be run. This is done by parsing from the head of
# the spec file until the first `describe` function is
# found.
# will be run.
#
# @param [String] file the spec file
# @param [Hash] options the options for the execution
Expand All @@ -153,16 +151,97 @@ def phantomjs_script
def query_string_for_suite(file, options)
return '' if file == options[:spec_dir]

query_string = ''
query_string = query_string_for_suite_from_line_number(file, options)

unless query_string
query_string = query_string_for_suite_from_first_describe(file, options)
end

query_string = query_string ? "?spec=#{ query_string }" : ''

URI.encode(query_string)
end

# When providing a line number by either the option or by
# a number directly after the file name the suite is extracted
# fromt the corresponding line number in the file.
#
# @param [String] file the spec file
# @param [Hash] options the options for the execution
# @option options [Fixnum] :line_number the line number to run
# @return [String] the suite name
#
def query_string_for_suite_from_line_number(file, options)
file_name, line_number = file_and_line_number_parts(file)
line_number ||= options[:line_number]

if line_number
lines = it_and_describe_lines(file_name, 0, line_number)
last = lines.pop

last_indentation = last[/^\s*/].length
# keep only lines with lower indentation
lines.delete_if { |x| x[/^\s*/].length >= last_indentation }
# remove all 'it'
lines.delete_if { |x| x =~ /^\s*it/ }

lines << last
lines.map { |x| spec_title(x) }.join(' ')
end
end

# The suite name must be extracted from the spec that
# will be run. This is done by parsing from the head of
# the spec file until the first `describe` function is
# found.
#
# @param [String] file the spec file
# @param [Hash] options the options for the execution
# @return [String] the suite name
#
def query_string_for_suite_from_first_describe(file, options)
File.foreach(file) do |line|
if line =~ /describe\s*[("']+(.*?)["')]+/
query_string = "?spec=#{ $1 }"
break
return $1
end
end
end

URI.encode(query_string)
# Splits the file name into the physical file name
# and the line number if present. E.g.:
# 'some_spec.js.coffee:10' -> ['some_spec.js.coffee', 10].
#
# If the line number is missing the second part of the
# returned array is `nil`.
#
# @param [String] file the spec file
# @return [Array] `[file_name, line_number]`
#
def file_and_line_number_parts(file)
match = file.match(/^(.+?)(?::(\d+))?$/)
[match[1], match[2].nil? ? nil : match[2].to_i]
end

# Returns all lines of the file that are either a
# 'describe' or a 'it' declaration.
#
# @param [String] file the spec file
# @param [Numeric] from the first line in the range
# @param [Numeric] to the last line in the range
# @Return [Array] the line contents
#
def it_and_describe_lines(file, from, to)
File.readlines(file)[from, to].
select { |x| x =~ /^\s*(it|describe)/ }
end

# Extracts the title of a 'description' or a 'it' declaration.
#
# @param [String] the line content
# @return [String] the extracted title
#
def spec_title(line)
line[/['"](.+?)['"]/, 1]
end

# Evaluates the JSON response that the PhantomJS script
Expand Down
10 changes: 10 additions & 0 deletions spec/guard/jasmine/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
cli.start(['spec', '--spec-dir', 'specs'])
end

it 'sets the line number' do
runner.should_receive(:run).with(anything(), hash_including(line_number: 1)).and_return [true, []]
cli.start(['spec', '--line-number', 1])
end

it 'detects the server type' do
server.should_receive(:detect_server).with('specs')
cli.start(['spec', '--spec-dir', 'specs'])
Expand Down Expand Up @@ -274,6 +279,11 @@
cli.start(['spec'])
end

it 'sets the line number' do
runner.should_receive(:run).with(anything(), hash_including(line_number: nil)).and_return [true, []]
cli.start(['spec'])
end

it 'disables the focus mode' do
runner.should_receive(:run).with(anything(), hash_including(focus: false)).and_return [true, []]
cli.start(['spec', '-f', 'false'])
Expand Down
41 changes: 41 additions & 0 deletions spec/guard/jasmine/runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,47 @@
end
end

context 'when passed a line number' do
before do
File.stub(:readlines).and_return([
'describe "TestContext", ->', # 1
' describe "Inner TestContext", ->', # 2
' describe "Unrelated TestContext", ->', # 3
' it "does something", ->', # 4
' # some code', # 5
' # some assertion', # 6
' it "does something else", ->', # 7
' # some assertion', # 8
' it "does something a lot else", ->', # 9
' # some assertion' # 10
])
end

context 'with the spec file name' do
it 'executes the example for line number on example' do
IO.should_receive(:popen).with("#{ phantomjs_command } \"http://localhost:8888/jasmine?spec=TestContext%20Inner%20TestContext%20does%20something%20else\" 60000 failure true failure failure false true ''", "r:UTF-8")
runner.run(['spec/javascripts/a.js.coffee:7'], defaults)
end

it 'executes the example for line number within example' do
IO.should_receive(:popen).with("#{ phantomjs_command } \"http://localhost:8888/jasmine?spec=TestContext%20Inner%20TestContext%20does%20something%20else\" 60000 failure true failure failure false true ''", "r:UTF-8")
runner.run(['spec/javascripts/a.js.coffee:8'], defaults)
end

it 'executes all examples within describe' do
IO.should_receive(:popen).with("#{ phantomjs_command } \"http://localhost:8888/jasmine?spec=TestContext\" 60000 failure true failure failure false true ''", "r:UTF-8")
runner.run(['spec/javascripts/a.js.coffee:1'], defaults)
end
end

context 'with the cli argument' do
it 'executes the example for line number on example' do
IO.should_receive(:popen).with("#{ phantomjs_command } \"http://localhost:8888/jasmine?spec=TestContext%20Inner%20TestContext%20does%20something%20else\" 60000 failure true failure failure false true ''", "r:UTF-8")
runner.run(['spec/javascripts/a.js.coffee'], defaults.merge(line_number: 7))
end
end
end

context 'when passed the spec directory' do
it 'requests all jasmine specs from the server' do
IO.should_receive(:popen).with("#{ phantomjs_command } \"http://localhost:8888/jasmine\" 60000 failure true failure failure false true ''", "r:UTF-8")
Expand Down