jasmine 1.3.2 → 2.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.travis.yml +3 -37
- data/Gemfile +11 -2
- data/HOW_TO_TEST.markdown +1 -1
- data/README.markdown +10 -10
- data/Rakefile +18 -29
- data/generators/jasmine/templates/jasmine-example/spec/PlayerSpec.js +2 -2
- data/generators/jasmine/templates/jasmine-example/spec/SpecHelper.js +14 -8
- data/generators/jasmine/templates/spec/javascripts/support/jasmine.yml +3 -3
- data/generators/jasmine/templates/spec/javascripts/support/jasmine_helper.rb +1 -0
- data/jasmine.gemspec +11 -41
- data/lib/generators/jasmine/examples/templates/spec/javascripts/helpers/SpecHelper.js +15 -9
- data/lib/generators/jasmine/examples/templates/spec/javascripts/jasmine_examples/PlayerSpec.js +1 -1
- data/lib/jasmine.rb +10 -7
- data/lib/jasmine/asset_bundle.rb +69 -0
- data/lib/jasmine/asset_expander.rb +7 -7
- data/lib/jasmine/command_line_tool.rb +18 -0
- data/lib/jasmine/config.rb +23 -15
- data/lib/jasmine/configuration.rb +14 -8
- data/lib/jasmine/core_configuration.rb +7 -4
- data/lib/jasmine/dependencies.rb +14 -18
- data/lib/jasmine/formatters/base.rb +9 -0
- data/lib/jasmine/formatters/console.rb +45 -0
- data/lib/jasmine/formatters/junit_xml.rb +47 -0
- data/lib/jasmine/path_expander.rb +1 -1
- data/lib/jasmine/railtie.rb +7 -6
- data/lib/jasmine/reporters/api_reporter.rb +38 -0
- data/lib/jasmine/results.rb +28 -9
- data/lib/jasmine/results_processor.rb +1 -1
- data/lib/jasmine/run.html.erb +1 -1
- data/lib/jasmine/run_specs.rb +12 -15
- data/lib/jasmine/runners/http.rb +11 -49
- data/lib/jasmine/selenium_driver.rb +17 -14
- data/lib/jasmine/tasks/jasmine.rake +21 -30
- data/lib/jasmine/tasks/{jasmine_rails3.rake → jasmine_rails.rake} +0 -0
- data/lib/jasmine/version.rb +1 -1
- data/lib/jasmine/yaml_config_parser.rb +9 -0
- data/release_notes/v2.0.0.rc2.md +44 -0
- data/spec/application_spec.rb +33 -38
- data/spec/asset_expander_spec.rb +4 -32
- data/spec/configuration_spec.rb +95 -35
- data/spec/jasmine_command_line_tool_spec.rb +63 -11
- data/spec/jasmine_rails_spec.rb +94 -0
- data/spec/lib/jasmine/formatters/base_spec.rb +9 -0
- data/spec/lib/jasmine/formatters/console_spec.rb +92 -0
- data/spec/lib/jasmine/formatters/junit_xml_spec.rb +55 -0
- data/spec/lib/jasmine/reporters/api_reporter_spec.rb +53 -0
- data/spec/lib/jasmine/runners/http_spec.rb +21 -0
- data/spec/path_expander_spec.rb +25 -0
- data/spec/results_spec.rb +59 -17
- data/spec/spec_helper.rb +36 -18
- data/spec/support/fake_selenium_driver.rb +35 -0
- data/spec/yaml_config_parser_spec.rb +37 -0
- metadata +65 -103
- data/generators/jasmine/templates/jasmine-example/SpecRunner.html +0 -54
- data/lib/jasmine/asset_pipeline_utility.rb +0 -19
- data/lib/jasmine/javascripts/boot.js +0 -28
- data/lib/jasmine/rspec_formatter.rb +0 -92
- data/spec/dependencies_spec.rb +0 -315
- data/spec/jasmine_rails2_spec.rb +0 -89
- data/spec/jasmine_rails3_spec.rb +0 -69
- data/spec/jasmine_self_test_spec.rb +0 -29
- data/spec/rspec_formatter_spec.rb +0 -88
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jasmine::Formatters::Console do
|
4
|
+
describe '#failures' do
|
5
|
+
it 'shows the failure messages' do
|
6
|
+
results = OpenStruct.new(:size => 2, :failures => [failing_result, failing_result], :pending_specs => [])
|
7
|
+
subject = Jasmine::Formatters::Console.new(results)
|
8
|
+
|
9
|
+
subject.failures.should match(/a suite with a failing spec/)
|
10
|
+
subject.failures.should match(/a failure message/)
|
11
|
+
subject.failures.should match(/a stack trace/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#summary' do
|
16
|
+
describe 'when the full suite passes' do
|
17
|
+
it 'shows the spec counts' do
|
18
|
+
results = OpenStruct.new(:size => 1, :failures => [], :pending_specs => [])
|
19
|
+
subject = Jasmine::Formatters::Console.new(results)
|
20
|
+
|
21
|
+
subject.summary.should match(/1 spec/)
|
22
|
+
subject.summary.should match(/0 failures/)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'shows the spec counts (pluralized)' do
|
26
|
+
results = OpenStruct.new(:size => 2, :failures => [], :pending_specs=> [])
|
27
|
+
subject = Jasmine::Formatters::Console.new(results)
|
28
|
+
|
29
|
+
subject.summary.should match(/2 specs/)
|
30
|
+
subject.summary.should match(/0 failures/)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'when there are failures' do
|
35
|
+
it 'shows the spec counts' do
|
36
|
+
results = OpenStruct.new(:size => 2, :failures => [failing_result], :pending_specs=> [])
|
37
|
+
subject = Jasmine::Formatters::Console.new(results)
|
38
|
+
|
39
|
+
subject.summary.should match(/2 specs/)
|
40
|
+
subject.summary.should match(/1 failure/)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'shows the spec counts (pluralized)' do
|
44
|
+
results = OpenStruct.new(:size => 2, :failures => [failing_result, failing_result], :pending_specs=> [])
|
45
|
+
subject = Jasmine::Formatters::Console.new(results)
|
46
|
+
|
47
|
+
subject.summary.should match(/2 specs/)
|
48
|
+
subject.summary.should match(/2 failures/)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'when there are pending specs' do
|
53
|
+
it 'shows the spec counts' do
|
54
|
+
results = OpenStruct.new(:size => 2, :failures => [failing_result], :pending_specs => [pending_result])
|
55
|
+
subject = Jasmine::Formatters::Console.new(results)
|
56
|
+
|
57
|
+
subject.summary.should match(/1 pending spec/)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'shows the spec counts (pluralized)' do
|
61
|
+
results = OpenStruct.new(:size => 2, :failures => [], :pending_specs => [pending_result, pending_result])
|
62
|
+
subject = Jasmine::Formatters::Console.new(results)
|
63
|
+
|
64
|
+
subject.summary.should match(/2 pending specs/)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'when there are no pending specs' do
|
69
|
+
|
70
|
+
it 'should not mention pending specs' do
|
71
|
+
results = OpenStruct.new(:size => 2, :failures => [], :pending_specs => [])
|
72
|
+
subject = Jasmine::Formatters::Console.new(results)
|
73
|
+
|
74
|
+
subject.summary.should_not match(/pending spec[s]/)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def failing_result
|
80
|
+
OpenStruct.new(:full_name => 'a suite with a failing spec', :failed_expectations => [
|
81
|
+
OpenStruct.new(:message => 'a failure message', :stack => 'a stack trace')
|
82
|
+
])
|
83
|
+
end
|
84
|
+
|
85
|
+
def passing_result
|
86
|
+
OpenStruct.new(passing_raw_result)
|
87
|
+
end
|
88
|
+
|
89
|
+
def pending_result
|
90
|
+
OpenStruct.new(pending_raw_result)
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Jasmine::Formatters::JUnitXml do
|
5
|
+
describe '#summary' do
|
6
|
+
describe 'when the full suite passes' do
|
7
|
+
it 'shows the spec counts' do
|
8
|
+
results = OpenStruct.new(:size => 1, :failures => [], :pending_specs => [],
|
9
|
+
:results => [passing_result(fullName: 'Passing test', description: 'test')])
|
10
|
+
subject = Jasmine::Formatters::JUnitXml.new(results)
|
11
|
+
|
12
|
+
xml = Nokogiri::XML(subject.summary)
|
13
|
+
|
14
|
+
testsuite = xml.xpath('/testsuites/testsuite').first
|
15
|
+
testsuite['tests'].should == '1'
|
16
|
+
testsuite['failures'].should == '0'
|
17
|
+
testsuite['name'].should == 'Passing'
|
18
|
+
|
19
|
+
xml.xpath('//testcase').size.should == 1
|
20
|
+
xml.xpath('//testcase').first['name'].should == 'test'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'when there are failures' do
|
25
|
+
it 'shows the spec counts' do
|
26
|
+
results = OpenStruct.new(:size => 2, :failures => [failing_result], :pending_specs=> [],
|
27
|
+
:results => [passing_result, failing_result])
|
28
|
+
subject = Jasmine::Formatters::JUnitXml.new(results)
|
29
|
+
|
30
|
+
xml = Nokogiri::XML(subject.summary)
|
31
|
+
|
32
|
+
testsuite = xml.xpath('/testsuites/testsuite').first
|
33
|
+
testsuite['tests'].should == '1'
|
34
|
+
testsuite['failures'].should == '0'
|
35
|
+
|
36
|
+
testsuite = xml.xpath('/testsuites/testsuite')[1]
|
37
|
+
testsuite['tests'].should == '1'
|
38
|
+
testsuite['failures'].should == '1'
|
39
|
+
|
40
|
+
xml.xpath('//testcase').size.should == 2
|
41
|
+
xml.xpath('//testcase/failure').size.should == 1
|
42
|
+
xml.xpath('//testcase/failure').first['message'].should == 'a failure message'
|
43
|
+
xml.xpath('//testcase/failure').first.content.should == 'a stack trace'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def failing_result(options = {})
|
49
|
+
Jasmine::Results::Result.new(failing_raw_result.merge(options))
|
50
|
+
end
|
51
|
+
|
52
|
+
def passing_result(options = {})
|
53
|
+
Jasmine::Results::Result.new(passing_raw_result.merge(options))
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jasmine::Reporters::ApiReporter do
|
4
|
+
let(:driver) { FakeSeleniumDriver.new }
|
5
|
+
let(:batch_size) { 3 }
|
6
|
+
subject { Jasmine::Reporters::ApiReporter.new(driver, batch_size) }
|
7
|
+
|
8
|
+
describe '#started?' do
|
9
|
+
it "reflects that Jasmine has started" do
|
10
|
+
driver.should_receive(:eval_js).with(Jasmine::Reporters::ApiReporter::STARTED_JS).and_return(false)
|
11
|
+
|
12
|
+
driver.start
|
13
|
+
|
14
|
+
subject.should_not be_started
|
15
|
+
|
16
|
+
driver.should_receive(:eval_js).with(Jasmine::Reporters::ApiReporter::STARTED_JS).and_return(true)
|
17
|
+
|
18
|
+
driver.start
|
19
|
+
|
20
|
+
subject.should be_started
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#finished?' do
|
25
|
+
it "reflects that Jasmine has finished" do
|
26
|
+
driver.should_receive(:eval_js).with(Jasmine::Reporters::ApiReporter::FINISHED_JS).and_return(false)
|
27
|
+
|
28
|
+
subject.should_not be_finished
|
29
|
+
|
30
|
+
driver.should_receive(:eval_js).with(Jasmine::Reporters::ApiReporter::FINISHED_JS).and_return(true)
|
31
|
+
|
32
|
+
driver.finish
|
33
|
+
|
34
|
+
subject.should be_finished
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#results" do
|
39
|
+
it "gets all of the results" do
|
40
|
+
driver.should_receive(:eval_js).with("return jsApiReporter.specResults(0, 3)").and_return(driver.results.slice(0, 3))
|
41
|
+
driver.should_receive(:eval_js).with("return jsApiReporter.specResults(3, 3)").and_return(driver.results.slice(3, 4))
|
42
|
+
|
43
|
+
results = subject.results
|
44
|
+
|
45
|
+
results.size.should == 4
|
46
|
+
results[0]['id'].should == 1
|
47
|
+
results[1]['id'].should == 2
|
48
|
+
results[2]['id'].should == 3
|
49
|
+
results[3]['id'].should == 4
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jasmine::Runners::HTTP do
|
4
|
+
let(:driver) { FakeSeleniumDriver.new }
|
5
|
+
let(:batch_size) { 3 }
|
6
|
+
let(:reporter) { Jasmine::Reporters::ApiReporter.new(driver, batch_size) }
|
7
|
+
subject { Jasmine::Runners::HTTP.new(driver, reporter) }
|
8
|
+
|
9
|
+
describe '#run' do
|
10
|
+
it "gets the results from the Jasmine HTML page" do
|
11
|
+
driver.should_receive(:connect)
|
12
|
+
driver.should_receive(:disconnect)
|
13
|
+
reporter.should_receive(:results).and_return(driver.results)
|
14
|
+
reporter.stub(:started?).and_return(true)
|
15
|
+
reporter.stub(:finished?).and_return(true)
|
16
|
+
|
17
|
+
results = subject.run
|
18
|
+
results.size.should == 4
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/path_expander_spec.rb
CHANGED
@@ -51,6 +51,31 @@ describe Jasmine::PathExpander do
|
|
51
51
|
]
|
52
52
|
end
|
53
53
|
|
54
|
+
it "sorts files" do
|
55
|
+
dir_glob = lambda do |pattern|
|
56
|
+
case pattern
|
57
|
+
when 'some_base/src0*'
|
58
|
+
['some_base/src0.js']
|
59
|
+
when 'some_base/src1*'
|
60
|
+
['some_base/src1zzz.js', 'some_base/src1.js']
|
61
|
+
else
|
62
|
+
raise "Unexpected pattern received: #{pattern}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
expanded_files = Jasmine::PathExpander.expand(
|
67
|
+
'some_base',
|
68
|
+
['src1*', 'src0*'],
|
69
|
+
dir_glob
|
70
|
+
)
|
71
|
+
|
72
|
+
expanded_files.should == [
|
73
|
+
File.join('some_base', 'src1.js'),
|
74
|
+
File.join('some_base', 'src1zzz.js'),
|
75
|
+
File.join('some_base', 'src0.js')
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
54
79
|
it "supports negation of passed patterns" do
|
55
80
|
dir_glob = lambda do |pattern|
|
56
81
|
case pattern
|
data/spec/results_spec.rb
CHANGED
@@ -1,27 +1,69 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Jasmine::Results do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
describe "#failures" do
|
5
|
+
it "should report all the failing spec" do
|
6
|
+
subject = Jasmine::Results.new([failing_raw_result, failing_raw_result])
|
7
|
+
subject.failures.size.should == 2
|
8
|
+
|
9
|
+
subject = Jasmine::Results.new([failing_raw_result, passing_raw_result])
|
10
|
+
subject.failures.size.should == 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#pending_specs" do
|
15
|
+
it "should report all the pending specs" do
|
16
|
+
subject = Jasmine::Results.new([pending_raw_result, pending_raw_result])
|
17
|
+
subject.pending_specs.size.should == 2
|
18
|
+
|
19
|
+
subject = Jasmine::Results.new([pending_raw_result, passing_raw_result])
|
20
|
+
subject.pending_specs.size.should == 1
|
21
|
+
end
|
7
22
|
end
|
8
23
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
24
|
+
describe "#size" do
|
25
|
+
it "should report the spec count" do
|
26
|
+
subject = Jasmine::Results.new([failing_raw_result, failing_raw_result])
|
27
|
+
subject.size.should == 2
|
28
|
+
|
29
|
+
subject = Jasmine::Results.new([failing_raw_result])
|
30
|
+
subject.size.should == 1
|
31
|
+
end
|
16
32
|
end
|
17
33
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
end
|
35
|
+
|
36
|
+
describe Jasmine::Results::Result do
|
37
|
+
describe "data accessors" do
|
38
|
+
it "delegates to raw results" do
|
39
|
+
result = Jasmine::Results::Result.new("status" => "failed")
|
40
|
+
result.status.should == "failed"
|
41
|
+
end
|
42
|
+
|
43
|
+
it "remaps important camelCase names to snake_case" do
|
44
|
+
result = Jasmine::Results::Result.new(failing_raw_result)
|
45
|
+
result.full_name.should == "a suite with a failing spec"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "exposes failed expectations" do
|
49
|
+
result = Jasmine::Results::Result.new(failing_raw_result)
|
50
|
+
|
51
|
+
expectation = result.failed_expectations[0]
|
52
|
+
expectation.message.should == "a failure message"
|
53
|
+
expectation.stack.should == "a stack trace"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "exposes only the last 7 lines of the stack trace" do
|
57
|
+
raw_result = failing_raw_result
|
58
|
+
raw_result["failedExpectations"][0]["stack"] = "1\n2\n3\n4\n5\n6\n7\n8\n9"
|
59
|
+
|
60
|
+
result = Jasmine::Results::Result.new(raw_result)
|
61
|
+
expectation = result.failed_expectations[0].stack
|
62
|
+
expectation.should match(/1/)
|
63
|
+
expectation.should match(/7/)
|
64
|
+
expectation.should_not match(/8/)
|
65
|
+
expectation.should_not match(/9/)
|
66
|
+
end
|
25
67
|
end
|
26
68
|
end
|
27
69
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,28 +1,19 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
3
|
require 'stringio'
|
4
4
|
require 'tmpdir'
|
5
5
|
|
6
6
|
envs = [:default, :development]
|
7
|
-
envs << :debug if ENV[
|
7
|
+
envs << :debug if ENV['DEBUG']
|
8
8
|
Bundler.setup(*envs)
|
9
9
|
|
10
|
-
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__),
|
11
|
-
require
|
12
|
-
|
13
|
-
|
14
|
-
require 'rspec'
|
15
|
-
else
|
16
|
-
require 'spec'
|
17
|
-
end
|
18
|
-
|
10
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../lib')))
|
11
|
+
require 'jasmine'
|
12
|
+
require 'rspec'
|
13
|
+
require 'support/fake_selenium_driver'
|
19
14
|
|
20
15
|
def create_rails(name)
|
21
|
-
|
22
|
-
`rails new #{name}`
|
23
|
-
else
|
24
|
-
`rails #{name}`
|
25
|
-
end
|
16
|
+
`rails new #{name}`
|
26
17
|
end
|
27
18
|
|
28
19
|
def create_temp_dir
|
@@ -33,7 +24,7 @@ def create_temp_dir
|
|
33
24
|
end
|
34
25
|
|
35
26
|
def temp_dir_before
|
36
|
-
@root = File.expand_path(File.join(File.dirname(__FILE__),
|
27
|
+
@root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
37
28
|
@old_dir = Dir::pwd
|
38
29
|
@tmp = create_temp_dir
|
39
30
|
end
|
@@ -53,3 +44,30 @@ module Kernel
|
|
53
44
|
$stdout = STDOUT
|
54
45
|
end
|
55
46
|
end
|
47
|
+
|
48
|
+
def passing_raw_result
|
49
|
+
raw_result('passed', {fullName: 'Passing test', description: 'Passing'})
|
50
|
+
end
|
51
|
+
|
52
|
+
def pending_raw_result
|
53
|
+
raw_result('pending', {fullName: 'Passing test', description: 'Passing'})
|
54
|
+
end
|
55
|
+
|
56
|
+
def failing_raw_result
|
57
|
+
raw_result('failed', {
|
58
|
+
'id' => 124,
|
59
|
+
'description' => 'a failing spec',
|
60
|
+
'fullName' => 'a suite with a failing spec',
|
61
|
+
'failedExpectations' => [
|
62
|
+
{
|
63
|
+
'message' => 'a failure message',
|
64
|
+
'stack' => 'a stack trace'
|
65
|
+
}
|
66
|
+
]
|
67
|
+
})
|
68
|
+
end
|
69
|
+
|
70
|
+
def raw_result(status, options = {})
|
71
|
+
{'status' => status}.merge(options)
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class FakeSeleniumDriver
|
2
|
+
attr_reader :results
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@state = :stopped
|
6
|
+
@results = [ {'id' => 1}, {'id' => 2}, {'id' => 3}, {'id' => 4} ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def eval_js(str)
|
10
|
+
|
11
|
+
case str
|
12
|
+
when Jasmine::Reporters::ApiReporter::STARTED_JS
|
13
|
+
@state == :started
|
14
|
+
when Jasmine::Reporters::ApiReporter::FINISHED_JS
|
15
|
+
@state == :finished
|
16
|
+
else
|
17
|
+
# TODO: When we drop support for Ruby < 1.9, USE NAMED CAPTURES HEYAH
|
18
|
+
if matches = /specResults\((\d+), (\d+)\)/.match(str)
|
19
|
+
length = matches[1]
|
20
|
+
index = matches[2]
|
21
|
+
slice = @results.slice(length.to_i, length.to_i + index.to_i)
|
22
|
+
|
23
|
+
slice.nil? ? [] : slice
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def start
|
29
|
+
@state = :started
|
30
|
+
end
|
31
|
+
|
32
|
+
def finish
|
33
|
+
@state = :finished
|
34
|
+
end
|
35
|
+
end
|