guard-rspec 3.1.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +11 -0
- data/Guardfile +5 -0
- data/{LICENSE → LICENSE.txt} +2 -0
- data/README.md +23 -149
- data/Rakefile +5 -0
- data/guard-rspec.gemspec +26 -0
- data/lib/guard/rspec.rb +18 -99
- data/lib/guard/rspec/command.rb +61 -0
- data/lib/guard/rspec/deprecator.rb +51 -0
- data/lib/guard/rspec/formatters/focuser.rb +29 -0
- data/lib/guard/rspec/formatters/notifier.rb +47 -0
- data/lib/guard/rspec/inspector.rb +44 -40
- data/lib/guard/rspec/runner.rb +45 -246
- data/lib/guard/rspec/version.rb +1 -1
- data/spec/lib/guard/rspec/command_spec.rb +59 -0
- data/spec/lib/guard/rspec/deprecator_spec.rb +57 -0
- data/spec/lib/guard/rspec/formatter_spec.rb +0 -0
- data/spec/lib/guard/rspec/formatters/focuser_spec.rb +25 -0
- data/spec/lib/guard/rspec/formatters/notifier_spec.rb +34 -0
- data/spec/lib/guard/rspec/inspector_spec.rb +94 -0
- data/spec/lib/guard/rspec/runner_spec.rb +136 -0
- data/spec/lib/guard/rspec_spec.rb +82 -0
- data/spec/spec_helper.rb +18 -0
- metadata +90 -20
- data/lib/guard/rspec/formatter.rb +0 -68
data/lib/guard/rspec/version.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'launchy'
|
3
|
+
|
4
|
+
describe Guard::RSpec::Command do
|
5
|
+
let(:options) { { } }
|
6
|
+
let(:paths) { %w[path1 path2] }
|
7
|
+
let(:command) { Guard::RSpec::Command.new(paths, options) }
|
8
|
+
|
9
|
+
describe '.initialize' do
|
10
|
+
|
11
|
+
it "sets paths at the end" do
|
12
|
+
expect(command).to match /path1 path2$/
|
13
|
+
end
|
14
|
+
|
15
|
+
it "sets custom failure exit code" do
|
16
|
+
expect(command).to match /--failure-exit-code 2/
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sets notifier formatter" do
|
20
|
+
expect(command).to match %r{-r .*/lib/guard/rspec/formatters/notifier.rb -f Guard::RSpec::Formatters::Notifier}
|
21
|
+
end
|
22
|
+
|
23
|
+
it "sets focuser formatter" do
|
24
|
+
expect(command).to match %r{-r .*/lib/guard/rspec/formatters/focuser.rb -f Guard::RSpec::Formatters::Focuser}
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with custom cmd" do
|
28
|
+
let(:options) { { cmd: 'rspec -t ~slow' } }
|
29
|
+
|
30
|
+
it "uses custom cmd" do
|
31
|
+
expect(command).to match /^rspec -t ~slow/
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with RSpec defined formatter" do
|
36
|
+
let(:formatters) { [['doc','output']] }
|
37
|
+
before { RSpec::Core::ConfigurationOptions.stub(:new) { double(parse_options: { formatters: formatters }) } }
|
38
|
+
|
39
|
+
it "uses them" do
|
40
|
+
expect(command).to match %r{-f doc -o output}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "with no RSpec defined formatter" do
|
45
|
+
it "sets default progress formatter" do
|
46
|
+
expect(command).to match %r{-f progress}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with formatter in cmd" do
|
51
|
+
let(:options) { { cmd: 'rspec -f doc' } }
|
52
|
+
|
53
|
+
it "sets no other formatters" do
|
54
|
+
expect(command).to match %r{-f doc}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Guard::RSpec::Deprecator do
|
4
|
+
let(:options) { {} }
|
5
|
+
let(:deprecator) { Guard::RSpec::Deprecator.new(options) }
|
6
|
+
|
7
|
+
describe '#warns_about_deprecated_options' do
|
8
|
+
|
9
|
+
describe 'handling of environment variable SPEC_OPTS' do
|
10
|
+
it "shows warning if SPEC_OPTS is set" do
|
11
|
+
ENV['SPEC_OPTS'] = '-f p'
|
12
|
+
expect(Guard::UI).to receive(:warning).with(
|
13
|
+
'The SPEC_OPTS environment variable is present. This can conflict with guard-rspec, particularly notifications.')
|
14
|
+
deprecator.warns_about_deprecated_options
|
15
|
+
ENV['SPEC_OPTS'] = nil # otherwise other specs pick it up and fail
|
16
|
+
end
|
17
|
+
it "does not show warning if SPEC_OPTS is unset" do
|
18
|
+
expect(Guard::UI).to_not receive(:warning).with(
|
19
|
+
'The SPEC_OPTS environment variable is present. This can conflict with guard-rspec, particularly notifications.')
|
20
|
+
deprecator.warns_about_deprecated_options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "with version option" do
|
25
|
+
let(:options) { { version: 1 } }
|
26
|
+
|
27
|
+
it "shows deprecation warning" do
|
28
|
+
expect(Guard::UI).to receive(:warning).with(
|
29
|
+
'Guard::RSpec DEPRECATION WARNING: The :version option is deprecated. Only RSpec ~> 2.14 is now supported.')
|
30
|
+
deprecator.warns_about_deprecated_options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "with exclude option" do
|
35
|
+
let(:options) { { exclude: "**" } }
|
36
|
+
|
37
|
+
it "shows deprecation warning" do
|
38
|
+
expect(Guard::UI).to receive(:warning).with(
|
39
|
+
'Guard::RSpec DEPRECATION WARNING: The :exclude option is deprecated. Please Guard ignore method instead. https://github.com/guard/guard#ignore')
|
40
|
+
deprecator.warns_about_deprecated_options
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
%w[color drb fail_fast formatter env bundler binstubs rvm cli spring turnip zeus foreman].each do |option|
|
45
|
+
describe "with #{option} option" do
|
46
|
+
let(:options) { { option.to_sym => 1 } }
|
47
|
+
|
48
|
+
it "shows deprecation warning" do
|
49
|
+
expect(Guard::UI).to receive(:warning).with(
|
50
|
+
"Guard::RSpec DEPRECATION WARNING: The :#{option} option is deprecated. Please customize the new :cmd option to fit your need.")
|
51
|
+
deprecator.warns_about_deprecated_options
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
File without changes
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
require 'guard/rspec/formatters/focuser'
|
4
|
+
|
5
|
+
describe Guard::RSpec::Formatters::Focuser do
|
6
|
+
let(:formatter) { Guard::RSpec::Formatters::Focuser.new(StringIO.new) }
|
7
|
+
|
8
|
+
describe "#dump_summary" do
|
9
|
+
context "with failures" do
|
10
|
+
let(:example) { double(
|
11
|
+
execution_result: { status: 'failed' },
|
12
|
+
metadata: { location: 'failed_location' }
|
13
|
+
) }
|
14
|
+
after { File.delete('./tmp/rspec_guard_result') }
|
15
|
+
|
16
|
+
it "writes failed location in tmp dir" do
|
17
|
+
formatter.stub(:examples) { [example] }
|
18
|
+
formatter.dump_summary(123, 3, 1, 0)
|
19
|
+
result = File.open('./tmp/rspec_guard_result').read
|
20
|
+
expect(result).to eq "failed_location\n"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
require 'guard/rspec/formatters/notifier'
|
4
|
+
|
5
|
+
describe Guard::RSpec::Formatters::Notifier do
|
6
|
+
let(:formatter) { Guard::RSpec::Formatters::Notifier.new(StringIO.new) }
|
7
|
+
|
8
|
+
describe "#dump_summary" do
|
9
|
+
context "with only success" do
|
10
|
+
it "notifies success" do
|
11
|
+
expect(Guard::Notifier).to receive(:notify).with(
|
12
|
+
"3 examples, 0 failures\nin 123.0 seconds", title: "RSpec results", image: :success, priority:-2)
|
13
|
+
formatter.dump_summary(123, 3, 0, 0)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with pending" do
|
18
|
+
it "notifies pending too" do
|
19
|
+
expect(Guard::Notifier).to receive(:notify).with(
|
20
|
+
"3 examples, 0 failures (1 pending)\nin 123.0 seconds", title: "RSpec results", image: :pending, priority:-1)
|
21
|
+
formatter.dump_summary(123, 3, 0, 1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with failures" do
|
26
|
+
it "notifies failures too" do
|
27
|
+
expect(Guard::Notifier).to receive(:notify).with(
|
28
|
+
"3 examples, 1 failures\nin 123.0 seconds", title: "RSpec results", image: :failed, priority:2)
|
29
|
+
formatter.dump_summary(123, 3, 1, 0)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Guard::RSpec::Inspector do
|
4
|
+
let(:options) { { } }
|
5
|
+
let(:inspector) { Guard::RSpec::Inspector.new(options) }
|
6
|
+
before { Guard::UI.stub(:warning) }
|
7
|
+
|
8
|
+
describe '.initialize' do
|
9
|
+
it "sets empty failed paths" do
|
10
|
+
expect(inspector.failed_paths).to be_empty
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#paths" do
|
15
|
+
let(:paths) { %w[spec/lib/guard/rspec/inspector_spec.rb] }
|
16
|
+
|
17
|
+
it "returns spec paths if no args" do
|
18
|
+
expect(inspector.paths).to eq %w[spec]
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with custom spec_paths" do
|
22
|
+
let(:options) { { spec_paths: %w[custom_spec] } }
|
23
|
+
|
24
|
+
it "returns custom spec paths if no args" do
|
25
|
+
expect(inspector.paths).to eq %w[custom_spec]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns new paths" do
|
30
|
+
expect(inspector.paths(paths)).to eq paths
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns uniq new paths" do
|
34
|
+
expect(inspector.paths(paths + paths)).to eq paths
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns compact new paths" do
|
38
|
+
expect(inspector.paths(paths + [nil])).to eq paths
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns only rspec file" do
|
42
|
+
expect(inspector.paths(paths + %w[foo])).to eq paths
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with focused paths" do
|
46
|
+
before {
|
47
|
+
FileUtils.mkdir_p('tmp')
|
48
|
+
File.open(Guard::RSpec::Inspector::FOCUSED_FILE_PATH,'w') { |f|
|
49
|
+
f.puts 'spec/lib/guard/rspec/command_spec.rb'
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
it "returns them" do
|
54
|
+
expect(inspector.paths(paths)).to eq %w[spec/lib/guard/rspec/command_spec.rb]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "without focused paths" do
|
59
|
+
it "returns new paths" do
|
60
|
+
expect(inspector.paths(paths)).to eq paths
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "with keep_failed options and failed_paths" do
|
65
|
+
let(:options) { { keep_failed: true } }
|
66
|
+
let(:failed_paths) { %w[spec/lib/guard/rspec/command_spec.rb] }
|
67
|
+
before { inspector.failed_paths = failed_paths }
|
68
|
+
|
69
|
+
it "returns failed paths alongs new paths" do
|
70
|
+
expect(inspector.paths(paths)).to eq failed_paths + paths
|
71
|
+
end
|
72
|
+
|
73
|
+
it "adds new paths to failed_paths" do
|
74
|
+
inspector.paths(paths)
|
75
|
+
expect(inspector.failed_paths).to eq failed_paths + paths
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#clear_paths" do
|
81
|
+
before { inspector.failed_paths = %w[failed_path1 failed_path2] }
|
82
|
+
|
83
|
+
it "clears all failed_paths if no args" do
|
84
|
+
inspector.clear_paths
|
85
|
+
expect(inspector.failed_paths).to be_empty
|
86
|
+
end
|
87
|
+
|
88
|
+
it "clears given failed_path" do
|
89
|
+
inspector.clear_paths(%w[failed_path1 failed_path3])
|
90
|
+
expect(inspector.failed_paths).to eq %w[failed_path2]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'launchy'
|
3
|
+
|
4
|
+
describe Guard::RSpec::Runner do
|
5
|
+
let(:default_options) { {
|
6
|
+
all_after_pass: false,
|
7
|
+
notification: true,
|
8
|
+
run_all: { message: 'Running all specs' },
|
9
|
+
launchy: nil,
|
10
|
+
} }
|
11
|
+
let(:options) { {} }
|
12
|
+
let(:runner) { Guard::RSpec::Runner.new(options) }
|
13
|
+
let(:inspector) { double(Guard::RSpec::Inspector) }
|
14
|
+
before {
|
15
|
+
Guard::UI.stub(:info)
|
16
|
+
Kernel.stub(:system) { true }
|
17
|
+
Guard::RSpec::Inspector.stub(:new) { inspector }
|
18
|
+
Guard::RSpec::Command.stub(:new) { 'rspec' }
|
19
|
+
}
|
20
|
+
|
21
|
+
describe '.initialize' do
|
22
|
+
it 'instanciates inspector with options' do
|
23
|
+
expect(Guard::RSpec::Inspector).to receive(:new).with(default_options.merge(foo: :bar))
|
24
|
+
Guard::RSpec::Runner.new(foo: :bar)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#reloads" do
|
29
|
+
it "clears inspector failed_paths" do
|
30
|
+
expect(inspector).to receive(:clear_paths)
|
31
|
+
runner.reload
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#run_all" do
|
36
|
+
before {
|
37
|
+
inspector.stub(:paths) { %w[spec1 spec2] }
|
38
|
+
inspector.stub(:clear_paths) { true }
|
39
|
+
}
|
40
|
+
|
41
|
+
it "prints default message" do
|
42
|
+
expect(Guard::UI).to receive(:info).with(default_options[:run_all][:message], reset: true)
|
43
|
+
runner.run_all
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with custom message" do
|
47
|
+
let(:options) { { run_all: { message: 'Custom message' } } }
|
48
|
+
|
49
|
+
it "prints custom message" do
|
50
|
+
expect(Guard::UI).to receive(:info).with('Custom message', reset: true)
|
51
|
+
runner.run_all
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with custom cmd" do
|
56
|
+
let(:options) { { run_all: { cmd: 'rspec -t ~slow' } } }
|
57
|
+
|
58
|
+
it "builds command with custom options" do
|
59
|
+
expect(Guard::RSpec::Command).to receive(:new).with(kind_of(Array), hash_including(cmd: 'rspec -t ~slow'))
|
60
|
+
runner.run_all
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "builds commands with all spec paths" do
|
65
|
+
expect(Guard::RSpec::Command).to receive(:new).with(%w[spec1 spec2], kind_of(Hash))
|
66
|
+
runner.run_all
|
67
|
+
end
|
68
|
+
|
69
|
+
it "clears inspector paths if run is success" do
|
70
|
+
expect(inspector).to receive(:clear_paths)
|
71
|
+
runner.run_all
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#run" do
|
76
|
+
let(:paths) { %w[spec_path1 spec_path2] }
|
77
|
+
before {
|
78
|
+
inspector.stub(:failed_paths) { [] }
|
79
|
+
inspector.stub(:paths) { paths }
|
80
|
+
inspector.stub(:clear_paths) { true }
|
81
|
+
}
|
82
|
+
|
83
|
+
it "prints running message" do
|
84
|
+
expect(Guard::UI).to receive(:info).with('Running: spec_path1 spec_path2', reset: true)
|
85
|
+
runner.run(paths)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns if no paths are given" do
|
89
|
+
inspector.stub(:paths) { [] }
|
90
|
+
expect(Guard::UI).to_not receive(:info)
|
91
|
+
runner.run([])
|
92
|
+
end
|
93
|
+
|
94
|
+
it "builds commands with spec paths" do
|
95
|
+
expect(Guard::RSpec::Command).to receive(:new).with(%w[spec_path1 spec_path2], kind_of(Hash))
|
96
|
+
runner.run(paths)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "clears inspector paths if run is success" do
|
100
|
+
expect(inspector).to receive(:clear_paths).with(paths)
|
101
|
+
runner.run(paths)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "notifies failure" do
|
105
|
+
Kernel.stub(:system) { false }
|
106
|
+
expect(Guard::Notifier).to receive(:notify).with('Failed', title: 'RSpec results', image: :failed, priority: 2)
|
107
|
+
runner.run(paths)
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with all_after_pass option and old failed spec paths" do
|
111
|
+
let(:options) { { all_after_pass: true } }
|
112
|
+
before {
|
113
|
+
inspector.stub(:failed_paths) { %w[failed_path] }
|
114
|
+
inspector.stub(:paths).with(paths) { paths }
|
115
|
+
}
|
116
|
+
|
117
|
+
it "re-runs all if run is success" do
|
118
|
+
expect(runner).to receive(:run_all)
|
119
|
+
runner.run(paths)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "with launchy option" do
|
124
|
+
let(:options) { { launchy: 'launchy_path' } }
|
125
|
+
before {
|
126
|
+
Pathname.stub(:new).with('launchy_path') { double(exist?: true) }
|
127
|
+
}
|
128
|
+
|
129
|
+
it "opens Launchy" do
|
130
|
+
expect(Launchy).to receive(:open).with('launchy_path')
|
131
|
+
runner.run(paths)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Guard::RSpec do
|
4
|
+
let(:default_options) { { all_on_start: false } }
|
5
|
+
let(:options) { { } }
|
6
|
+
let(:plugin) { Guard::RSpec.new(options) }
|
7
|
+
let(:runner) { double(Guard::RSpec::Runner) }
|
8
|
+
before {
|
9
|
+
Guard::UI.stub(:info)
|
10
|
+
Guard::RSpec::Deprecator.stub(:warns_about_deprecated_options)
|
11
|
+
Guard::RSpec::Runner.stub(:new) { runner }
|
12
|
+
}
|
13
|
+
|
14
|
+
describe '.initialize' do
|
15
|
+
it 'instanciates Runner with options' do
|
16
|
+
expect(Guard::RSpec::Runner).to receive(:new).with(default_options.merge(foo: :bar))
|
17
|
+
Guard::RSpec.new(foo: :bar)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'warns deprecated options' do
|
21
|
+
expect(Guard::RSpec::Deprecator).to receive(:warns_about_deprecated_options).with(default_options.merge(foo: :bar))
|
22
|
+
Guard::RSpec.new(foo: :bar)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#start' do
|
27
|
+
it "doesn't call #run_all by default" do
|
28
|
+
expect(plugin).to_not receive(:run_all)
|
29
|
+
plugin.start
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with all_on_start at true' do
|
33
|
+
let(:options) { { all_on_start: true } }
|
34
|
+
|
35
|
+
it 'calls #run_all' do
|
36
|
+
expect(plugin).to receive(:run_all)
|
37
|
+
plugin.start
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#run_all' do
|
43
|
+
it "runs all specs via runner" do
|
44
|
+
expect(runner).to receive(:run_all) { true }
|
45
|
+
plugin.run_all
|
46
|
+
end
|
47
|
+
|
48
|
+
it "throws task_has_failed if runner return false" do
|
49
|
+
runner.stub(:run_all) { false }
|
50
|
+
expect(plugin).to receive(:throw).with(:task_has_failed)
|
51
|
+
plugin.run_all
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#reload' do
|
56
|
+
it "reloads via runner" do
|
57
|
+
expect(runner).to receive(:reload)
|
58
|
+
plugin.reload
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#run_on_modifications' do
|
63
|
+
let(:paths) { %w[path1 path2] }
|
64
|
+
it "runs all specs via runner" do
|
65
|
+
expect(runner).to receive(:run).with(paths) { true }
|
66
|
+
plugin.run_on_modifications(paths)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does nothing if paths empty" do
|
70
|
+
expect(runner).to_not receive(:run)
|
71
|
+
plugin.run_on_modifications([])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "throws task_has_failed if runner return false" do
|
75
|
+
runner.stub(:run) { false }
|
76
|
+
expect(plugin).to receive(:throw).with(:task_has_failed)
|
77
|
+
plugin.run_on_modifications(paths)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|