page-object 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +10 -1
- data/Gemfile +3 -0
- data/README.md +2 -2
- data/cucumber.yml +2 -0
- data/features/element.feature +81 -8
- data/features/form.feature +28 -0
- data/features/hidden_field.feature +32 -0
- data/features/html/static_elements.html +26 -0
- data/features/list_item.feature +36 -0
- data/features/ordered_list.feature +39 -0
- data/features/step_definitions/accessor_steps.rb +71 -0
- data/features/step_definitions/element_steps.rb +23 -0
- data/features/support/page.rb +43 -0
- data/features/text_area.feature +32 -0
- data/features/text_field.feature +1 -2
- data/features/unordered_list.feature +39 -0
- data/lib/page-object.rb +48 -3
- data/lib/page-object/accessors.rb +282 -111
- data/lib/page-object/elements.rb +6 -0
- data/lib/page-object/elements/element.rb +5 -1
- data/lib/page-object/elements/form.rb +25 -0
- data/lib/page-object/elements/hidden_field.rb +28 -0
- data/lib/page-object/elements/list_item.rb +7 -0
- data/lib/page-object/elements/ordered_list.rb +31 -0
- data/lib/page-object/elements/text_area.rb +24 -0
- data/lib/page-object/elements/text_field.rb +1 -1
- data/lib/page-object/elements/unordered_list.rb +31 -0
- data/lib/page-object/platforms/selenium_button.rb +1 -1
- data/lib/page-object/platforms/selenium_element.rb +3 -0
- data/lib/page-object/platforms/selenium_form.rb +14 -0
- data/lib/page-object/platforms/selenium_image.rb +6 -2
- data/lib/page-object/platforms/selenium_ordered_list.rb +18 -0
- data/lib/page-object/platforms/selenium_unordered_list.rb +18 -0
- data/lib/page-object/platforms/watir_element.rb +3 -0
- data/lib/page-object/platforms/watir_form.rb +14 -0
- data/lib/page-object/platforms/watir_ordered_list.rb +18 -0
- data/lib/page-object/platforms/watir_unordered_list.rb +18 -0
- data/lib/page-object/selenium_page_object.rb +101 -0
- data/lib/page-object/version.rb +2 -1
- data/lib/page-object/watir_page_object.rb +103 -2
- data/spec/page-object/accessors_spec.rb +203 -2
- data/spec/page-object/elements/form_spec.rb +24 -0
- data/spec/page-object/elements/hidden_field_spec.rb +32 -0
- data/spec/page-object/elements/list_item_spec.rb +22 -0
- data/spec/page-object/elements/ordered_list_spec.rb +43 -0
- data/spec/page-object/elements/text_area_spec.rb +32 -0
- data/spec/page-object/elements/text_field_spec.rb +1 -1
- data/spec/page-object/elements/unordered_list_spec.rb +43 -0
- metadata +39 -5
data/lib/page-object/version.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'page-object/elements'
|
2
2
|
|
3
3
|
module PageObject
|
4
|
+
#
|
5
|
+
# Watir implementation of the page object platform driver. You should not use the
|
6
|
+
# class directly. Instead you should include the PageObject module in your page object
|
7
|
+
# and use the methods dynamically added from the PageObject::Accessors module.
|
8
|
+
#
|
4
9
|
class WatirPageObject
|
5
10
|
def initialize(browser)
|
6
11
|
@browser = browser
|
@@ -66,6 +71,53 @@ module PageObject
|
|
66
71
|
Elements::TextField.new(element, :platform => :watir)
|
67
72
|
end
|
68
73
|
|
74
|
+
#
|
75
|
+
# platform method to get the value stored in a hidden field
|
76
|
+
# See PageObject::Accessors#hidden_field
|
77
|
+
#
|
78
|
+
def hidden_field_value_for(identifier)
|
79
|
+
identifier = Elements::HiddenField.watir_identifier_for identifier
|
80
|
+
@browser.hidden(identifier).value
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# platform method to retrieve a hidden field element
|
85
|
+
# See PageObject::Accessors#hidden_field
|
86
|
+
#
|
87
|
+
def hidden_field_for(identifier)
|
88
|
+
identifier = Elements::HiddenField.watir_identifier_for identifier
|
89
|
+
element = @browser.hidden(identifier)
|
90
|
+
Elements::HiddenField.new(element, :platform => :watir)
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# platform method to set text in a textarea
|
95
|
+
# See PageObject::Accessors#text_area
|
96
|
+
#
|
97
|
+
def text_area_value_set(identifier, value)
|
98
|
+
identifier = Elements::TextArea.watir_identifier_for identifier
|
99
|
+
@browser.textarea(identifier).send_keys(value)
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# platform method to get the text from a textarea
|
104
|
+
# See PageObject::Accessors#text_area
|
105
|
+
#
|
106
|
+
def text_area_value_for(identifier)
|
107
|
+
identifier = Elements::TextArea.watir_identifier_for identifier
|
108
|
+
@browser.textarea(identifier).value
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# platform method to get the text area element
|
113
|
+
# See PageObject::Accessors#text_area
|
114
|
+
#
|
115
|
+
def text_area_for(identifier)
|
116
|
+
identifier = Elements::TextArea.watir_identifier_for identifier
|
117
|
+
element = @browser.textarea(identifier)
|
118
|
+
Elements::TextArea.new(element, :platform => :watir)
|
119
|
+
end
|
120
|
+
|
69
121
|
#
|
70
122
|
# platform method to get the currently selected value from a select list
|
71
123
|
# See PageObject::Accessors#select_list
|
@@ -259,7 +311,7 @@ module PageObject
|
|
259
311
|
# See PageObject::Accessors#cell
|
260
312
|
#
|
261
313
|
def cell_text_for(identifier)
|
262
|
-
identifier = Elements::
|
314
|
+
identifier = Elements::TableCell.watir_identifier_for identifier
|
263
315
|
@browser.td(identifier).text
|
264
316
|
end
|
265
317
|
|
@@ -268,7 +320,7 @@ module PageObject
|
|
268
320
|
# See PageObject::Accessors#cell
|
269
321
|
#
|
270
322
|
def cell_for(identifier)
|
271
|
-
identifier = Elements::
|
323
|
+
identifier = Elements::TableCell.watir_identifier_for identifier
|
272
324
|
element = @browser.td(identifier)
|
273
325
|
PageObject::Elements::TableCell.new(element, :platform => :watir)
|
274
326
|
end
|
@@ -282,5 +334,54 @@ module PageObject
|
|
282
334
|
element = @browser.image(identifier)
|
283
335
|
PageObject::Elements::Image.new(element, :platform => :watir)
|
284
336
|
end
|
337
|
+
|
338
|
+
#
|
339
|
+
# platform method to retrieve a form element
|
340
|
+
# See PageObject::Accessors#form
|
341
|
+
#
|
342
|
+
def form_for(identifier)
|
343
|
+
identifier = Elements::Form.watir_identifier_for identifier
|
344
|
+
element = @browser.form(identifier)
|
345
|
+
PageObject::Elements::Form.new(element, :platform => :watir)
|
346
|
+
end
|
347
|
+
|
348
|
+
#
|
349
|
+
# platform method to retrieve the text from a list item
|
350
|
+
# See PageObject::Accessors#list_item
|
351
|
+
#
|
352
|
+
def list_item_text_for(identifier)
|
353
|
+
identifier = Elements::ListItem.watir_identifier_for identifier
|
354
|
+
@browser.li(identifier).text
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# platform method to retrieve a list item element
|
359
|
+
# See PageObject::Accessors#list_item
|
360
|
+
#
|
361
|
+
def list_item_for(identifier)
|
362
|
+
identifier = Elements::ListItem.watir_identifier_for identifier
|
363
|
+
element = @browser.li(identifier)
|
364
|
+
PageObject::Elements::ListItem.new(element, :platform => :watir)
|
365
|
+
end
|
366
|
+
|
367
|
+
#
|
368
|
+
# platform method to retrieve an unordered list element
|
369
|
+
# See PageObject::Accessors#unordered_list
|
370
|
+
#
|
371
|
+
def unordered_list_for(identifier)
|
372
|
+
identifier = Elements::UnorderedList.watir_identifier_for identifier
|
373
|
+
element = @browser.ul(identifier)
|
374
|
+
PageObject::Elements::UnorderedList.new(element, :platform => :watir)
|
375
|
+
end
|
376
|
+
|
377
|
+
#
|
378
|
+
# platform method to retrieve an ordered list element
|
379
|
+
# See PageObject::Accessors#ordered_list
|
380
|
+
#
|
381
|
+
def ordered_list_for(identifier)
|
382
|
+
identifier = Elements::OrderedList.watir_identifier_for identifier
|
383
|
+
element = @browser.ol(identifier)
|
384
|
+
PageObject::Elements::OrderedList.new(element, :platform => :watir)
|
385
|
+
end
|
285
386
|
end
|
286
387
|
end
|
@@ -5,6 +5,8 @@ class TestPageObject
|
|
5
5
|
|
6
6
|
link(:google_search, :link => 'Google Search')
|
7
7
|
text_field(:first_name, :id => 'first_name')
|
8
|
+
hidden_field(:social_security_number, :id => 'ssn')
|
9
|
+
text_area(:address, :id => 'address')
|
8
10
|
select_list(:state, :id => 'state')
|
9
11
|
checkbox(:active, :id => 'is_active_id')
|
10
12
|
radio_button(:first, :id => 'first_choice')
|
@@ -14,6 +16,10 @@ class TestPageObject
|
|
14
16
|
cell(:total, :id => 'total')
|
15
17
|
span(:alert, :id => 'alert_id')
|
16
18
|
image(:logo, :id => 'logo')
|
19
|
+
form(:login, :id => 'login')
|
20
|
+
list_item(:item_one, :id => 'one')
|
21
|
+
unordered_list(:menu, :id => 'main_menu')
|
22
|
+
ordered_list(:top_five, :id => 'top')
|
17
23
|
end
|
18
24
|
|
19
25
|
describe PageObject::Accessors do
|
@@ -100,7 +106,7 @@ describe PageObject::Accessors do
|
|
100
106
|
selenium_page_object.first_name = 'Katie'
|
101
107
|
end
|
102
108
|
|
103
|
-
it "should
|
109
|
+
it "should retrieve a text field element" do
|
104
110
|
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
105
111
|
element = selenium_page_object.first_name_text_field
|
106
112
|
element.should be_instance_of PageObject::Elements::TextField
|
@@ -109,6 +115,93 @@ describe PageObject::Accessors do
|
|
109
115
|
end
|
110
116
|
|
111
117
|
|
118
|
+
describe "hidden field accessors" do
|
119
|
+
context "when called on a page object" do
|
120
|
+
it "should generate accessor methods" do
|
121
|
+
watir_page_object.should respond_to(:social_security_number)
|
122
|
+
watir_page_object.should respond_to(:social_security_number_hidden_field)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "watir implementation" do
|
127
|
+
it "should get the text from a hidden field" do
|
128
|
+
watir_browser.should_receive(:hidden).and_return(watir_browser)
|
129
|
+
watir_browser.should_receive(:value).and_return("value")
|
130
|
+
watir_page_object.social_security_number.should == "value"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should retrieve a hidden field element" do
|
134
|
+
watir_browser.should_receive(:hidden).and_return(watir_browser)
|
135
|
+
element = watir_page_object.social_security_number_hidden_field
|
136
|
+
element.should be_instance_of(PageObject::Elements::HiddenField)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "selenium implementation" do
|
141
|
+
it "should get the text from a hidden field" do
|
142
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
143
|
+
selenium_browser.should_receive(:attribute).with('value').and_return("12345")
|
144
|
+
selenium_page_object.social_security_number.should == "12345"
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should retrieve a hidden field element" do
|
148
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
149
|
+
element = selenium_page_object.social_security_number_hidden_field
|
150
|
+
element.should be_instance_of PageObject::Elements::HiddenField
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "text area accessors" do
|
156
|
+
context "when called on a page object" do
|
157
|
+
it "should generate accessor methods" do
|
158
|
+
watir_page_object.should respond_to(:address)
|
159
|
+
watir_page_object.should respond_to(:address=)
|
160
|
+
watir_page_object.should respond_to(:address_text_area)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "watir implementation" do
|
165
|
+
it "should set some text on the text area" do
|
166
|
+
watir_browser.should_receive(:textarea).and_return(watir_browser)
|
167
|
+
watir_browser.should_receive(:send_keys).with("123 main street")
|
168
|
+
watir_page_object.address = "123 main street"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should get the text from the text area" do
|
172
|
+
watir_browser.should_receive(:textarea).and_return(watir_browser)
|
173
|
+
watir_browser.should_receive(:value).and_return("123 main street")
|
174
|
+
watir_page_object.address.should == "123 main street"
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should retrieve a text area element" do
|
178
|
+
watir_browser.should_receive(:textarea).and_return(watir_browser)
|
179
|
+
element = watir_page_object.address_text_area
|
180
|
+
element.should be_instance_of PageObject::Elements::TextArea
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "selenium implementation" do
|
185
|
+
it "should set some text on the text area" do
|
186
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
187
|
+
selenium_browser.should_receive(:send_keys).with("123 main street")
|
188
|
+
selenium_page_object.address = "123 main street"
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should get the text from the text area" do
|
192
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
193
|
+
selenium_browser.should_receive(:attribute).with('value').and_return("123 main street")
|
194
|
+
selenium_page_object.address.should == "123 main street"
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should retrieve a text area element" do
|
198
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
199
|
+
element = selenium_page_object.address_text_area
|
200
|
+
element.should be_instance_of PageObject::Elements::TextArea
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
112
205
|
describe "select_list accessors" do
|
113
206
|
context "when called on a page object" do
|
114
207
|
it "should generate accessor methods" do
|
@@ -426,7 +519,6 @@ describe PageObject::Accessors do
|
|
426
519
|
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
427
520
|
element = selenium_page_object.cart_table
|
428
521
|
element.should be_instance_of(PageObject::Elements::Table)
|
429
|
-
|
430
522
|
end
|
431
523
|
end
|
432
524
|
end
|
@@ -485,4 +577,113 @@ describe PageObject::Accessors do
|
|
485
577
|
end
|
486
578
|
end
|
487
579
|
end
|
580
|
+
|
581
|
+
describe "form accessors" do
|
582
|
+
context "when called on a page object" do
|
583
|
+
it "should generate accessor methods" do
|
584
|
+
watir_page_object.should respond_to(:login_form)
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
context "watir implementation" do
|
589
|
+
it "should retrieve the form element from the page" do
|
590
|
+
watir_browser.should_receive(:form).and_return(watir_browser)
|
591
|
+
element = watir_page_object.login_form
|
592
|
+
element.should be_instance_of PageObject::Elements::Form
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
context "selenium implementation" do
|
597
|
+
it "should retrieve the form element from the page" do
|
598
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
599
|
+
element = selenium_page_object.login_form
|
600
|
+
element.should be_instance_of PageObject::Elements::Form
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
describe "list item accessors" do
|
606
|
+
context "when called on a page object" do
|
607
|
+
it "should generate accessor methods" do
|
608
|
+
watir_page_object.should respond_to(:item_one)
|
609
|
+
watir_page_object.should respond_to(:item_one_list_item)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
context "watir implementation" do
|
614
|
+
it "should retrieve the text from the list item" do
|
615
|
+
watir_browser.should_receive(:li).and_return(watir_browser)
|
616
|
+
watir_browser.should_receive(:text).and_return("value")
|
617
|
+
watir_page_object.item_one.should == "value"
|
618
|
+
end
|
619
|
+
|
620
|
+
it "should retrieve the list item element from the page" do
|
621
|
+
watir_browser.should_receive(:li).and_return(watir_browser)
|
622
|
+
element = watir_page_object.item_one_list_item
|
623
|
+
element.should be_instance_of PageObject::Elements::ListItem
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
context "selenium implementation" do
|
628
|
+
it "should retrieve the text from the list item" do
|
629
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
630
|
+
selenium_browser.should_receive(:text).and_return("value")
|
631
|
+
selenium_page_object.item_one.should == "value"
|
632
|
+
end
|
633
|
+
|
634
|
+
it "should retrieve the list item from the page" do
|
635
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
636
|
+
element = selenium_page_object.item_one_list_item
|
637
|
+
element.should be_instance_of PageObject::Elements::ListItem
|
638
|
+
end
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
describe "unordered list accessors" do
|
643
|
+
context "when called on a page object" do
|
644
|
+
it "should generate accessor methods" do
|
645
|
+
watir_page_object.should respond_to(:menu_unordered_list)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
context "watir implementation" do
|
650
|
+
it "should retrieve the element from the page" do
|
651
|
+
watir_browser.should_receive(:ul).and_return(watir_browser)
|
652
|
+
element = watir_page_object.menu_unordered_list
|
653
|
+
element.should be_instance_of PageObject::Elements::UnorderedList
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
context "selenium implementation" do
|
658
|
+
it "should retrieve the element from the page" do
|
659
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
660
|
+
element = selenium_page_object.menu_unordered_list
|
661
|
+
element.should be_instance_of PageObject::Elements::UnorderedList
|
662
|
+
end
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
describe "ordered list accessors" do
|
667
|
+
context "when called on a page object" do
|
668
|
+
it "should generate accessor methods" do
|
669
|
+
watir_page_object.should respond_to(:top_five_ordered_list)
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
context "watir implementation" do
|
674
|
+
it "should retrieve the element from the page" do
|
675
|
+
watir_browser.should_receive(:ol).and_return(watir_browser)
|
676
|
+
element = watir_page_object.top_five_ordered_list
|
677
|
+
element.should be_instance_of PageObject::Elements::OrderedList
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
context "selenium implementation" do
|
682
|
+
it "should retrieve the element from the page" do
|
683
|
+
selenium_browser.should_receive(:find_element).and_return(selenium_browser)
|
684
|
+
element = selenium_page_object.top_five_ordered_list
|
685
|
+
element.should be_instance_of PageObject::Elements::OrderedList
|
686
|
+
end
|
687
|
+
end
|
688
|
+
end
|
488
689
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'page-object/elements'
|
3
|
+
|
4
|
+
describe PageObject::Elements::Form do
|
5
|
+
describe "interface" do
|
6
|
+
let(:form_element) { double('form_element') }
|
7
|
+
|
8
|
+
context "for watir" do
|
9
|
+
it "should submit a form" do
|
10
|
+
form = PageObject::Elements::Form.new(form_element, :platform => :watir)
|
11
|
+
form_element.should_receive(:submit)
|
12
|
+
form.submit
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "for selenium" do
|
17
|
+
it "should submit a form" do
|
18
|
+
form = PageObject::Elements::Form.new(form_element, :platform => :selenium)
|
19
|
+
form_element.should_receive(:submit)
|
20
|
+
form.submit
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'page-object/elements'
|
3
|
+
|
4
|
+
describe PageObject::Elements::HiddenField do
|
5
|
+
let(:hiddenfield) { PageObject::Elements::HiddenField }
|
6
|
+
|
7
|
+
describe "when mapping how to find an element" do
|
8
|
+
it "should map watir types to same" do
|
9
|
+
[:class, :id, :index, :name, :tag_name, :text, :xpath].each do |t|
|
10
|
+
identifier = hiddenfield.watir_identifier_for t => 'value'
|
11
|
+
identifier.keys.first.should == t
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should map selenium types to watir" do
|
16
|
+
identifier = hiddenfield.watir_identifier_for :css => 'value'
|
17
|
+
identifier.keys.first.should == :tag_name
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should map selenium types to same" do
|
21
|
+
[:class, :css, :id, :name, :xpath].each do |t|
|
22
|
+
key, value = hiddenfield.selenium_identifier_for t => 'value'
|
23
|
+
key.should == t
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should map watir types to selenium" do
|
28
|
+
key, value = hiddenfield.selenium_identifier_for :tag_name => 'value'
|
29
|
+
key.should == :css
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'page-object/elements'
|
3
|
+
|
4
|
+
describe PageObject::Elements::ListItem do
|
5
|
+
let(:list_item) { PageObject::Elements::ListItem }
|
6
|
+
|
7
|
+
describe "when mapping how to find an element" do
|
8
|
+
it "should map watir types to same" do
|
9
|
+
[:class, :id, :index, :xpath].each do |t|
|
10
|
+
identifier = list_item.watir_identifier_for t => 'value'
|
11
|
+
identifier.keys.first.should == t
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should map selenium types to same" do
|
16
|
+
[:class, :id, :name, :xpath].each do |t|
|
17
|
+
key, value = list_item.selenium_identifier_for t => 'value'
|
18
|
+
key.should == t
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|