asciidoctor-doctest 1.5.2.0 → 2.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.adoc +48 -68
- data/features/fixtures/html-slim/Rakefile +5 -11
- data/features/generator_html.feature +6 -6
- data/features/test_html.feature +70 -28
- data/lib/asciidoctor/doctest.rb +11 -14
- data/lib/asciidoctor/doctest/asciidoc_converter.rb +85 -0
- data/lib/asciidoctor/doctest/{base_example.rb → example.rb} +6 -27
- data/lib/asciidoctor/doctest/factory.rb +36 -0
- data/lib/asciidoctor/doctest/generator.rb +30 -23
- data/lib/asciidoctor/doctest/html/converter.rb +64 -0
- data/lib/asciidoctor/doctest/{html/normalizer.rb → html_normalizer.rb} +4 -4
- data/lib/asciidoctor/doctest/io.rb +14 -0
- data/lib/asciidoctor/doctest/{asciidoc/examples_suite.rb → io/asciidoc.rb} +4 -8
- data/lib/asciidoctor/doctest/{base_examples_suite.rb → io/base.rb} +28 -46
- data/lib/asciidoctor/doctest/io/xml.rb +69 -0
- data/lib/asciidoctor/doctest/no_fallback_template_converter.rb +42 -0
- data/lib/asciidoctor/doctest/rake_tasks.rb +229 -0
- data/lib/asciidoctor/doctest/test_reporter.rb +110 -0
- data/lib/asciidoctor/doctest/tester.rb +134 -0
- data/lib/asciidoctor/doctest/version.rb +1 -1
- data/spec/asciidoc_converter_spec.rb +64 -0
- data/spec/{base_example_spec.rb → example_spec.rb} +4 -5
- data/spec/factory_spec.rb +46 -0
- data/spec/html/converter_spec.rb +95 -0
- data/spec/{html/normalizer_spec.rb → html_normalizer_spec.rb} +1 -1
- data/spec/{asciidoc/examples_suite_spec.rb → io/asciidoc_spec.rb} +3 -8
- data/spec/{html/examples_suite_spec.rb → io/xml_spec.rb} +3 -106
- data/spec/no_fallback_template_converter_spec.rb +38 -0
- data/spec/shared_examples/{base_examples_suite.rb → base_examples.rb} +25 -28
- data/spec/spec_helper.rb +4 -0
- data/spec/tester_spec.rb +153 -0
- metadata +52 -59
- data/features/fixtures/html-slim/test/html_test.rb +0 -6
- data/features/fixtures/html-slim/test/test_helper.rb +0 -5
- data/lib/asciidoctor/doctest/asciidoc_renderer.rb +0 -111
- data/lib/asciidoctor/doctest/generator_task.rb +0 -115
- data/lib/asciidoctor/doctest/html/example.rb +0 -21
- data/lib/asciidoctor/doctest/html/examples_suite.rb +0 -118
- data/lib/asciidoctor/doctest/test.rb +0 -125
- data/spec/asciidoc_renderer_spec.rb +0 -103
- data/spec/test_spec.rb +0 -164
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'asciidoctor/doctest/minitest_diffy'
|
2
|
+
require 'asciidoctor/doctest/test_reporter'
|
3
|
+
require 'corefines'
|
4
|
+
require 'minitest'
|
5
|
+
|
6
|
+
using Corefines::Object::presence
|
7
|
+
|
8
|
+
module Asciidoctor
|
9
|
+
module DocTest
|
10
|
+
class Tester
|
11
|
+
|
12
|
+
##
|
13
|
+
# @return [Minitest::Reporter] an instance of minitest's +Reporter+
|
14
|
+
# to report test results.
|
15
|
+
attr_reader :reporter
|
16
|
+
|
17
|
+
##
|
18
|
+
# @param input_suite [IO::Base] an instance of {IO::Base} subclass to
|
19
|
+
# read the reference input examples from.
|
20
|
+
#
|
21
|
+
# @param output_suite [IO::Base] an instance of {IO::Base} subclass to
|
22
|
+
# read the output examples from (i.e. an expected output).
|
23
|
+
#
|
24
|
+
# @param converter [#call] a callable that accepts a string content of
|
25
|
+
# an input example and a hash with options for the converter, and
|
26
|
+
# returns the converted content.
|
27
|
+
#
|
28
|
+
# @param reporter [Minitest::Reporter, nil] an instance of minitest's
|
29
|
+
# +Reporter+ to report test results. When omitted or +nil+, then
|
30
|
+
# {TestReporter} is used.
|
31
|
+
#
|
32
|
+
def initialize(input_suite, output_suite, converter, reporter = nil)
|
33
|
+
@input_suite = input_suite
|
34
|
+
@output_suite = output_suite
|
35
|
+
@converter = converter
|
36
|
+
@reporter = reporter || TestReporter.new
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Runs tests for all the input/output examples which name matches
|
41
|
+
# the _pattern_. When some output example is missing, it's reported as
|
42
|
+
# a skipped test.
|
43
|
+
#
|
44
|
+
# @param pattern [String] glob-like pattern to select examples to test
|
45
|
+
# (see {Example#name_match?}).
|
46
|
+
#
|
47
|
+
def run_tests(pattern: '*:*')
|
48
|
+
@reporter.start
|
49
|
+
|
50
|
+
@input_suite.pair_with(@output_suite).each do |input, output|
|
51
|
+
next if input.empty? || !input.name_match?(pattern)
|
52
|
+
test_example input, output
|
53
|
+
end
|
54
|
+
|
55
|
+
@reporter.report
|
56
|
+
@reporter.passed?
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Tests if the given reference input is matching the expected output
|
61
|
+
# after conversion through the tested backend.
|
62
|
+
#
|
63
|
+
# @param input_exmpl [Example] the reference input example.
|
64
|
+
# @param output_exmpl [Example] the expected output example.
|
65
|
+
#
|
66
|
+
def test_example(input_exmpl, output_exmpl)
|
67
|
+
test_with_minitest input_exmpl.name do |test|
|
68
|
+
if output_exmpl.empty?
|
69
|
+
test.skip 'No expected output found'
|
70
|
+
else
|
71
|
+
actual, expected = @converter.convert_examples(input_exmpl, output_exmpl)
|
72
|
+
msg = output_exmpl.desc.presence || input_exmpl.desc
|
73
|
+
|
74
|
+
test.assert_equal expected, actual, msg
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
##
|
82
|
+
# Runs the given block with Minitest as a single test.
|
83
|
+
#
|
84
|
+
# @param [String, Symbol] test name.
|
85
|
+
# @yield [Minitest::Test] Gives the test context to the block.
|
86
|
+
#
|
87
|
+
def test_with_minitest(name, &block)
|
88
|
+
MinitestSingleTest.test! @reporter, name, name, block
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# @private
|
93
|
+
class MinitestSingleTest < Minitest::Test
|
94
|
+
include MinitestDiffy
|
95
|
+
|
96
|
+
# @note Overrides method from +Minitest::Test+.
|
97
|
+
attr_reader :location
|
98
|
+
|
99
|
+
def self.test!(reporter, name, location, callable)
|
100
|
+
new(reporter, name, location, callable).failures
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def initialize(reporter, name, location, callable)
|
106
|
+
super name
|
107
|
+
@reporter = reporter
|
108
|
+
@location = location
|
109
|
+
@callable = callable
|
110
|
+
run
|
111
|
+
@reporter.record(self)
|
112
|
+
end
|
113
|
+
|
114
|
+
# @note Overrides method from +Minitest::Test+.
|
115
|
+
def run
|
116
|
+
with_info_handler do
|
117
|
+
time_it do
|
118
|
+
capture_exceptions do
|
119
|
+
@callable.call(self)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
self # per contract
|
125
|
+
end
|
126
|
+
|
127
|
+
# @note Overrides method from +Minitest::Assertions+.
|
128
|
+
def mu_pp(example)
|
129
|
+
example.to_s
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
describe DocTest::AsciidocConverter do
|
4
|
+
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
it { is_expected.to have_method :convert, :call }
|
8
|
+
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
|
12
|
+
context 'with defaults' do
|
13
|
+
subject { described_class.new.opts }
|
14
|
+
it { is_expected.to include safe: :safe }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with backend_name' do
|
18
|
+
subject { described_class.new(backend_name: 'html5').opts }
|
19
|
+
it { is_expected.to include backend: 'html5' }
|
20
|
+
|
21
|
+
context 'empty string' do
|
22
|
+
subject { described_class.new(backend_name: '').opts }
|
23
|
+
it { is_expected.to_not include :backend }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with template_dirs' do
|
28
|
+
include FakeFS::SpecHelpers
|
29
|
+
|
30
|
+
subject { described_class.new(template_dirs: template_dirs).opts }
|
31
|
+
let(:template_dirs) { ['/tmp/html5'] }
|
32
|
+
|
33
|
+
before { FileUtils.mkpath template_dirs[0] }
|
34
|
+
|
35
|
+
context 'that exists' do
|
36
|
+
it do
|
37
|
+
is_expected.to include(
|
38
|
+
template_dirs: template_dirs,
|
39
|
+
converter: DocTest::NoFallbackTemplateConverter
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'and templates_fallback is true' do
|
44
|
+
subject { described_class.new(template_dirs: template_dirs, templates_fallback: true).opts }
|
45
|
+
it { is_expected.to include template_dirs: template_dirs }
|
46
|
+
it { is_expected.to_not include :converter }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'and custom converter' do
|
50
|
+
subject { described_class.new(template_dirs: template_dirs, converter: converter).opts }
|
51
|
+
let(:converter) { Asciidoctor::Converter::TemplateConverter }
|
52
|
+
|
53
|
+
it { is_expected.to include template_dirs: template_dirs, converter: converter }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "that doesn't exist" do
|
58
|
+
let(:template_dirs) { ['/tmp/html5', '/tmp/revealjs'] }
|
59
|
+
|
60
|
+
it { expect { subject }.to raise_error ArgumentError }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
|
-
describe DocTest::
|
1
|
+
describe DocTest::Example do
|
2
2
|
|
3
3
|
subject(:o) { described_class.new ['foo', 'bar'] }
|
4
4
|
|
5
5
|
it do
|
6
|
-
is_expected.to respond_to :group_name, :local_name, :desc, :opts, :content
|
7
|
-
:content_normalized, :content_pretty
|
6
|
+
is_expected.to respond_to :group_name, :local_name, :desc, :opts, :content
|
8
7
|
end
|
9
8
|
|
10
9
|
describe '#name' do
|
@@ -156,8 +155,8 @@ describe DocTest::BaseExample do
|
|
156
155
|
expect(first).to_not eq second
|
157
156
|
end
|
158
157
|
|
159
|
-
it 'returns false for instances with different
|
160
|
-
expect(second).to receive(:
|
158
|
+
it 'returns false for instances with different content' do
|
159
|
+
expect(second).to receive(:content).and_return('ALLONS-Y!')
|
161
160
|
expect(first).to_not eq second
|
162
161
|
end
|
163
162
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
describe DocTest::Factory do
|
4
|
+
|
5
|
+
subject(:factory) { Module.new { extend DocTest::Factory } }
|
6
|
+
|
7
|
+
let(:default_opts) { {} }
|
8
|
+
|
9
|
+
before do
|
10
|
+
factory.register(:foo, OpenStruct, default_opts)
|
11
|
+
factory.register(:bar, String, 'illegal')
|
12
|
+
factory.register(:baz, Integer)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.create' do
|
16
|
+
it "returns instance of registered class" do
|
17
|
+
expect( factory.create(:foo) ).to eq OpenStruct.new
|
18
|
+
end
|
19
|
+
|
20
|
+
context "with opts" do
|
21
|
+
it "initializes class with opts" do
|
22
|
+
expect( factory.create(:foo, a: 42) ).to eq OpenStruct.new(a: 42)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when class with default_opts" do
|
26
|
+
let(:default_opts) { {a: 1, b: 2} }
|
27
|
+
|
28
|
+
it "initializes class with opts merged with default_opts" do
|
29
|
+
expect( factory.create(:foo, b: 6) ).to eq OpenStruct.new(a: 1, b: 6)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when class with default_opts" do
|
35
|
+
let(:default_opts) { {a: 1, b: 2} }
|
36
|
+
|
37
|
+
it "initializes class with default_opts" do
|
38
|
+
expect( factory.create(:foo) ).to eq OpenStruct.new(default_opts)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with unregistered name" do
|
43
|
+
it { expect { factory.create(:unknown) }.to raise_error(ArgumentError) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module DocTest
|
4
|
+
describe HTML::Converter do
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegator :converter, :convert_examples
|
8
|
+
|
9
|
+
subject(:converter) { described_class.new }
|
10
|
+
|
11
|
+
describe '#convert_examples' do
|
12
|
+
|
13
|
+
let(:input) { Example.new 's:dummy', content: '*chunky* bacon' }
|
14
|
+
let(:output) { Example.new 's:dummy', content: '<b>chunky</b> bacon', opts: opts }
|
15
|
+
let(:opts) { {dummy: 'value'} }
|
16
|
+
let(:converter_opts) { {header_footer: false} }
|
17
|
+
|
18
|
+
subject(:result) { convert_examples input, output }
|
19
|
+
|
20
|
+
let :rendered do
|
21
|
+
<<-EOF
|
22
|
+
<section>
|
23
|
+
<h1>Title</h1>
|
24
|
+
<div>
|
25
|
+
<p><b>Chunky</b> bacon</p>
|
26
|
+
</div>
|
27
|
+
<code>meh</code>
|
28
|
+
</section>
|
29
|
+
<div>
|
30
|
+
<p>why?</p>
|
31
|
+
</div>
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
|
35
|
+
before do
|
36
|
+
expect(converter).to receive(:convert)
|
37
|
+
.with(input.content, converter_opts).and_return(rendered)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns array of converted input content and output content'
|
41
|
+
|
42
|
+
context 'with :exclude option' do
|
43
|
+
let(:opts) { {exclude: ['.//p', './/code']} }
|
44
|
+
|
45
|
+
it 'returns content without HTML (sub)elements specified by XPath' do
|
46
|
+
expect(result.first.gsub(/\s*/, '')).to eq \
|
47
|
+
'<section><h1>Title</h1><div></div></section><div></div>'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with :include option' do
|
52
|
+
let(:opts) { {include: ['.//p']} }
|
53
|
+
|
54
|
+
it 'returns content with only HTML (sub)elements specified by XPath' do
|
55
|
+
expect(result.first.gsub(/\s*/, '')).to eq '<p><b>Chunky</b>bacon</p><p>why?</p>'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with :header_footer option' do
|
60
|
+
let(:opts) { {header_footer: true} }
|
61
|
+
let(:converter_opts) { {header_footer: true} }
|
62
|
+
|
63
|
+
it 'renders content with :header_footer => true' do
|
64
|
+
convert_examples input, output
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with example named /^document.*/' do
|
69
|
+
let(:input) { Example.new 'document:dummy', content: '*chunky* bacon' }
|
70
|
+
let(:converter_opts) { {header_footer: true} }
|
71
|
+
|
72
|
+
it 'renders content with :header_footer => true' do
|
73
|
+
convert_examples input, output
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with example named /inline_.*/' do
|
78
|
+
let(:input) { Example.new 'inline_quoted:dummy', content: '*chunky* bacon' }
|
79
|
+
let(:rendered) { '<p><b>chunky</b> bacon</p>' }
|
80
|
+
|
81
|
+
it 'returns content without top-level <p> tags' do
|
82
|
+
expect(result.first).to eq '<b>chunky</b> bacon'
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with :include option' do
|
86
|
+
let(:opts) { {include: ['.//b']} }
|
87
|
+
|
88
|
+
it 'preferes the include option' do
|
89
|
+
expect(result.first).to eq '<b>chunky</b>'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -1,15 +1,10 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
1
|
using Corefines::String::unindent
|
4
2
|
|
5
|
-
describe DocTest::Asciidoc
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
it_should_behave_like DocTest::BaseExamplesSuite
|
3
|
+
describe DocTest::IO::Asciidoc do
|
9
4
|
|
10
|
-
|
5
|
+
it_should_behave_like DocTest::IO::Base
|
11
6
|
|
12
|
-
subject(:suite) { described_class.new }
|
7
|
+
subject(:suite) { described_class.new(file_ext: '.adoc') }
|
13
8
|
|
14
9
|
|
15
10
|
describe '#initialize' do
|
@@ -1,15 +1,10 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
1
|
using Corefines::String::unindent
|
4
2
|
|
5
|
-
describe DocTest::
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
it_should_behave_like DocTest::BaseExamplesSuite
|
3
|
+
describe DocTest::IO::XML do
|
9
4
|
|
10
|
-
|
5
|
+
it_should_behave_like DocTest::IO::Base
|
11
6
|
|
12
|
-
subject(:suite) { described_class.new }
|
7
|
+
subject(:suite) { described_class.new(file_ext: '.html') }
|
13
8
|
|
14
9
|
|
15
10
|
describe '#initialize' do
|
@@ -143,102 +138,4 @@ describe DocTest::HTML::ExamplesSuite do
|
|
143
138
|
end
|
144
139
|
end
|
145
140
|
end
|
146
|
-
|
147
|
-
|
148
|
-
describe '#convert_example' do
|
149
|
-
|
150
|
-
let(:input) { create_example 's:dummy', content: '*chunky* bacon' }
|
151
|
-
let(:opts) { {dummy: 'value'} }
|
152
|
-
let(:renderer) { double 'AsciidocRenderer' }
|
153
|
-
let(:converter_opts) { {header_footer: false} }
|
154
|
-
|
155
|
-
subject(:result) { suite.convert_example input, opts, renderer }
|
156
|
-
|
157
|
-
let :rendered do
|
158
|
-
<<-EOF
|
159
|
-
<section>
|
160
|
-
<h1>Title</h1>
|
161
|
-
<div>
|
162
|
-
<p><b>Chunky</b> bacon</p>
|
163
|
-
</div>
|
164
|
-
<code>meh</code>
|
165
|
-
</section>
|
166
|
-
<div>
|
167
|
-
<p>why?</p>
|
168
|
-
</div>
|
169
|
-
EOF
|
170
|
-
end
|
171
|
-
|
172
|
-
before do
|
173
|
-
expect(renderer).to receive(:convert)
|
174
|
-
.with(input.content, converter_opts).and_return(rendered)
|
175
|
-
end
|
176
|
-
|
177
|
-
it 'returns instance of HTML::Example' do
|
178
|
-
is_expected.to be_instance_of DocTest::HTML::Example
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'returns Example with the same name as input_example' do
|
182
|
-
expect(result.name).to eq input.name
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'returns Example with the given opts' do
|
186
|
-
expect(result.opts).to eq opts
|
187
|
-
end
|
188
|
-
|
189
|
-
context 'with :exclude option' do
|
190
|
-
let(:opts) { {exclude: ['.//p', './/code']} }
|
191
|
-
|
192
|
-
it 'returns content without HTML (sub)elements specified by XPath' do
|
193
|
-
expect(result.content.gsub(/\s*/, '')).to eq \
|
194
|
-
'<section><h1>Title</h1><div></div></section><div></div>'
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
context 'with :include option' do
|
199
|
-
let(:opts) { {include: ['.//p']} }
|
200
|
-
|
201
|
-
it 'returns content with only HTML (sub)elements specified by XPath' do
|
202
|
-
expect(result.content.gsub(/\s*/, '')).to eq '<p><b>Chunky</b>bacon</p><p>why?</p>'
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
context 'with :header_footer option' do
|
207
|
-
let(:opts) { {header_footer: true} }
|
208
|
-
|
209
|
-
it 'renders content with :header_footer => true' do
|
210
|
-
suite.convert_example input, {}, renderer
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
context 'with example named /^document.*/' do
|
215
|
-
let(:input) { create_example 'document:dummy', content: '*chunky* bacon' }
|
216
|
-
let(:converter_opts) { {header_footer: true} }
|
217
|
-
|
218
|
-
it 'renders content with :header_footer => true' do
|
219
|
-
suite.convert_example input, {}, renderer
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
context 'with example named /inline_.*/' do
|
224
|
-
let(:input) { create_example 'inline_quoted:dummy', content: '*chunky* bacon' }
|
225
|
-
let(:rendered) { '<p><b>chunky</b> bacon</p>' }
|
226
|
-
|
227
|
-
it 'returns content without top-level <p> tags' do
|
228
|
-
expect(result.content).to eq '<b>chunky</b> bacon'
|
229
|
-
end
|
230
|
-
|
231
|
-
it 'does not add implicit include into returned example' do
|
232
|
-
expect(result.opts).to_not include :include
|
233
|
-
end
|
234
|
-
|
235
|
-
context 'with :include option' do
|
236
|
-
let(:opts) { {include: ['.//b']} }
|
237
|
-
|
238
|
-
it 'preferes the include option' do
|
239
|
-
expect(result.content).to eq '<b>chunky</b>'
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
141
|
end
|