screen-object 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/ChangeLog +7 -0
- data/Gemfile +9 -0
- data/Rakefile +28 -0
- data/cucumber.yml +4 -0
- data/features/button.feature +28 -0
- data/features/checkbox.feature +11 -0
- data/features/navigation.feature +10 -0
- data/features/step_definitions/button_step.rb +46 -0
- data/features/step_definitions/checkbox_step.rb +12 -0
- data/features/step_definitions/navigation_steps.rb +14 -0
- data/features/step_definitions/table_step.rb +5 -0
- data/features/step_definitions/textfield_step.rb +5 -0
- data/features/support/appium.txt +9 -0
- data/features/support/appium_ios.txt +11 -0
- data/features/support/env.rb +30 -0
- data/features/support/screen.rb +79 -0
- data/features/support/screens/buttons_screen.rb +8 -0
- data/features/support/screens/landing_screen.rb +6 -0
- data/features/support/screens/main_screen.rb +6 -0
- data/features/table.feature +11 -0
- data/features/textfield.feature +10 -0
- data/lib/screen-object.rb +161 -0
- data/lib/screen-object/.DS_Store +0 -0
- data/lib/screen-object/accessors.rb +457 -0
- data/lib/screen-object/accessors/button.rb +36 -0
- data/lib/screen-object/accessors/checkbox.rb +38 -0
- data/lib/screen-object/accessors/element.rb +144 -0
- data/lib/screen-object/accessors/image.rb +25 -0
- data/lib/screen-object/accessors/table.rb +26 -0
- data/lib/screen-object/accessors/text.rb +46 -0
- data/lib/screen-object/accessors/textfield.rb +41 -0
- data/lib/screen-object/appium_server.rb +58 -0
- data/lib/screen-object/elements.rb +21 -0
- data/lib/screen-object/load_appium.rb +31 -0
- data/lib/screen-object/screen_factory.rb +43 -0
- data/lib/screen-object/version.rb +4 -0
- data/screen-object.gemspec +28 -0
- data/spec/integration/appium_server_spec.rb +24 -0
- data/spec/lib/appium_server_spec.rb +59 -0
- data/spec/lib/screen_object_spec.rb +37 -0
- data/spec/screen-object/button_spec.rb +25 -0
- data/spec/screen-object/checkbox_spec.rb +35 -0
- data/spec/screen-object/element_spec.rb +205 -0
- data/spec/screen-object/image_spec.rb +21 -0
- data/spec/screen-object/text_field_spec.rb +37 -0
- data/spec/screen-object/text_spec.rb +29 -0
- data/spec/spec_helper.rb +29 -0
- metadata +201 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a485a4dff88029c7ac2fa3e3ffe8b63c423606de
|
4
|
+
data.tar.gz: a5fbeb35806fd5724d172a5f1dc97df562f99347
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d43363bd1486fa6efc5272cdb98b3d8943d3dc6664d3a720c07871ccaad2869f5c050520543bb726edd7e1ab3b4066a0a009c9c96f3a6cda9e2afa66807e7552
|
7
|
+
data.tar.gz: 9504a4f5d599ade809cf776d44b2afd2cf7146e3e2172ae2e98ada91d6a13166ae93111a67eb037e25034ba9b3a6da689befe3e2e86a8ca1dc0b69460b553050
|
data/.rspec
ADDED
data/ChangeLog
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
|
10
|
+
#Rake::Task['release'].clear
|
11
|
+
|
12
|
+
|
13
|
+
Cucumber::Rake::Task.new(:features, 'Run the features') do |task|
|
14
|
+
task.profile = 'default'
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
18
|
+
spec.ruby_opts = "-I lib:spec"
|
19
|
+
spec.pattern = 'spec/lib/*_spec.rb'
|
20
|
+
end
|
21
|
+
task :spec
|
22
|
+
|
23
|
+
RSpec::Core::RakeTask.new(:integration) do |spec|
|
24
|
+
spec.ruby_opts = "-I lib:spec"
|
25
|
+
spec.pattern = 'spec/integration/*_spec.rb'
|
26
|
+
end
|
27
|
+
|
28
|
+
task :integration
|
data/cucumber.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Feature: Button
|
2
|
+
In order to access the buttons functionality
|
3
|
+
we need to test all the operations
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given I am on UICatalog screen
|
7
|
+
|
8
|
+
|
9
|
+
Scenario: Verify the button exists
|
10
|
+
When I verify the existence of the button
|
11
|
+
Then I should see the return value as true on button existence
|
12
|
+
|
13
|
+
#Scenario: Verify the output when button doesn't exist
|
14
|
+
# When I verify the existence of the button that is not present
|
15
|
+
# Then I should see the return value as false on button existence
|
16
|
+
|
17
|
+
Scenario: Click the button
|
18
|
+
When I click the UICatalog button
|
19
|
+
Then I should see the message to confirm the button is clicked
|
20
|
+
|
21
|
+
#Scenario: Click the button that does not exist
|
22
|
+
# When I click the button that does not exist
|
23
|
+
|
24
|
+
Scenario: Verify if the value is true when verifying the isenabled method
|
25
|
+
When I verify the existence of the button using isenabled method return value should be true
|
26
|
+
|
27
|
+
Scenario: Verify if the value is false when verifying the isenabled method for disabled button
|
28
|
+
When I verify the existence of the button using isenabled method return value should be false
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Feature: CheckBox
|
2
|
+
In order to access the checkbox functionality
|
3
|
+
we need to test all the operations
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given I am on login page
|
7
|
+
|
8
|
+
|
9
|
+
Scenario: Verify the checkbox exists
|
10
|
+
When I verify the existence of the checkbox
|
11
|
+
Then I should see the return value as true on checkbox existence
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Adding navigation capabilities to the screen-object
|
2
|
+
|
3
|
+
|
4
|
+
Scenario: Navigating to a screen
|
5
|
+
When I navigate to the buttons screen
|
6
|
+
Then the title for the buttons screen should be "Buttons"
|
7
|
+
|
8
|
+
Scenario: Doing multiple things on a page
|
9
|
+
When I navigate to the buttons screen
|
10
|
+
Then I should be able to click several buttons
|
@@ -0,0 +1,46 @@
|
|
1
|
+
Given(/^I am on UICatalog screen$/) do
|
2
|
+
|
3
|
+
end
|
4
|
+
|
5
|
+
When(/^I verify the existence of the button$/) do
|
6
|
+
puts "Checking for the existence of button using exists method. Should be true. -- #{on(Screen).ui_catalog_exists?}"
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
Then(/^I should see the return value as true on button existence$/) do
|
11
|
+
fail unless on(Screen).ui_catalog_exists?
|
12
|
+
end
|
13
|
+
|
14
|
+
When(/^I verify the existence of the button that is not present$/) do
|
15
|
+
puts "Checking for the existence of button using exist method. Should be false -- #{on(Screen).no_button_exists?}"
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
Then(/^I should see the return value as false on button existence$/) do
|
20
|
+
fail unless !on(Screen).no_button_exists?
|
21
|
+
end
|
22
|
+
|
23
|
+
When(/^I verify the existence of the button using isenabled method return value should be true$/) do
|
24
|
+
puts "Checking for the existence of button using enabled method.Should be true -- #{on(Screen).is_ui_catalog_enabled}"
|
25
|
+
fail unless on(Screen).is_ui_catalog_enabled
|
26
|
+
end
|
27
|
+
|
28
|
+
When(/^I verify the existence of the button using isenabled method return value should be false$/) do
|
29
|
+
puts "Checking for the existence of button using enabled method. should be false -- #{on(Screen).no_button_exists?}"
|
30
|
+
fail unless !on(Screen).no_button_exists?
|
31
|
+
end
|
32
|
+
|
33
|
+
When(/^I click the UICatalog button$/) do
|
34
|
+
on(Screen).click_ui_catalog
|
35
|
+
sleep 2
|
36
|
+
end
|
37
|
+
|
38
|
+
Then(/^I should see the message to confirm the button is clicked$/) do
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
When(/^I click the button that does not exist$/) do
|
43
|
+
puts '1'
|
44
|
+
on(Screen).no_button_click
|
45
|
+
puts on(Screen).no_button_click
|
46
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
When(/^I verify the existence of the checkbox$/) do
|
2
|
+
puts "Checking for the existence of button using exists method. Should be true. -- #{on(Screen).remember_me?}"
|
3
|
+
end
|
4
|
+
|
5
|
+
|
6
|
+
Then(/^I should see the return value as true on checkbox existence$/) do
|
7
|
+
fail unless on(Screen).remember_me?
|
8
|
+
puts on(Screen).remember_me_checked?
|
9
|
+
on(Screen).remember_me_uncheck
|
10
|
+
sleep 2
|
11
|
+
puts on(Screen).remember_me_checked?
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
When(/^I navigate to the buttons screen$/) do
|
2
|
+
navigate_to(ButtonsScreen)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then(/^the title for the buttons screen should be "([^"]*)"$/) do |title|
|
6
|
+
on(ButtonsScreen).title_text.should == title
|
7
|
+
end
|
8
|
+
|
9
|
+
Then(/^I should be able to click several buttons$/) do
|
10
|
+
on(ButtonsScreen) do |screen|
|
11
|
+
screen.text_button
|
12
|
+
screen.contact_add
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
When(/^I verify the existence of the text filed return value should be true$/) do
|
2
|
+
puts "Checking for the existence of button using exists method. Should be true. -- #{on(Screen).username_exists?}"
|
3
|
+
fail unless on(Screen).username_exists?
|
4
|
+
on(Screen).enter_username('adf')
|
5
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'appium_lib'
|
3
|
+
require 'cucumber/ast'
|
4
|
+
require 'require_all'
|
5
|
+
require 'screen-object'
|
6
|
+
|
7
|
+
require_relative 'screen'
|
8
|
+
require_rel 'screens'
|
9
|
+
|
10
|
+
appium_txt = IO.readlines(File.dirname(File.expand_path('./', __FILE__)) + "/appium.txt")
|
11
|
+
path = appium_txt[3].split(34.chr)
|
12
|
+
$AppPath = path[1]
|
13
|
+
Appium::Driver.new(Appium.load_appium_txt file: File.expand_path('./', __FILE__), verbose: true)
|
14
|
+
|
15
|
+
Before {
|
16
|
+
ScreenObject::Load_App.start_driver
|
17
|
+
}
|
18
|
+
|
19
|
+
After {
|
20
|
+
ScreenObject::Load_App.quit_driver
|
21
|
+
}
|
22
|
+
|
23
|
+
World(ScreenObject::ScreenFactory)
|
24
|
+
|
25
|
+
|
26
|
+
ScreenObject::ScreenFactory.routes = {
|
27
|
+
default: [[LandingScreen, :go_to_main_screen],
|
28
|
+
[MainScreen, :go_to_buttons_screen],
|
29
|
+
[ButtonsScreen, :title]]
|
30
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class Screen
|
2
|
+
include ScreenObject
|
3
|
+
|
4
|
+
#ios
|
5
|
+
button(:ui_catalog, "name~UICatalog")
|
6
|
+
button(:no_button, "name~nobutton")
|
7
|
+
text(:ui_catalog_text, "xpath~//UIAStaticText[@name='UICatalog']")
|
8
|
+
text_field(:username, "xpath~//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIATextField[1]")
|
9
|
+
text_field(:no_textfield, "name~notextfield")
|
10
|
+
checkbox(:remember, "id~c/rememberMe")
|
11
|
+
table(:table_view, 'xpath~UIAApplication[1]/UIAWindow[2]/UIATableView[1]')
|
12
|
+
|
13
|
+
# Button methods
|
14
|
+
|
15
|
+
def table_cell_count
|
16
|
+
table_view_cell_count
|
17
|
+
end
|
18
|
+
|
19
|
+
def remember_me?
|
20
|
+
remember?
|
21
|
+
end
|
22
|
+
|
23
|
+
def remember_me_checked?
|
24
|
+
remember_checked?
|
25
|
+
end
|
26
|
+
|
27
|
+
def remember_me_uncheck
|
28
|
+
check_remember
|
29
|
+
end
|
30
|
+
|
31
|
+
def ui_catalog_exists?
|
32
|
+
ui_catalog?
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def no_button_exists?
|
37
|
+
no_button?
|
38
|
+
end
|
39
|
+
|
40
|
+
def no_button_click
|
41
|
+
no_button
|
42
|
+
end
|
43
|
+
|
44
|
+
def enter_username(atext)
|
45
|
+
self.username=atext
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_username
|
49
|
+
username
|
50
|
+
end
|
51
|
+
|
52
|
+
def username_exists?
|
53
|
+
username?
|
54
|
+
end
|
55
|
+
|
56
|
+
def no_textfield_exists?
|
57
|
+
no_textfield?
|
58
|
+
end
|
59
|
+
|
60
|
+
def is_ui_catalog_enabled
|
61
|
+
ui_catalog_enabled?
|
62
|
+
end
|
63
|
+
|
64
|
+
def is_nobutton_enabled
|
65
|
+
no_button_isenabled?
|
66
|
+
end
|
67
|
+
|
68
|
+
def click_ui_catalog
|
69
|
+
ui_catalog
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Text Methods
|
74
|
+
|
75
|
+
def UICatalog_text?
|
76
|
+
ui_catalog_text?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Table Methods
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class ButtonsScreen
|
2
|
+
include ScreenObject
|
3
|
+
|
4
|
+
text(:title, "xpath~//UIAApplication[1]/UIAWindow[2]/UIANavigationBar[1]/UIAStaticText[1]")
|
5
|
+
button(:text_button, "name~Button")
|
6
|
+
button(:contact_add, "xpath~//UIAApplication[1]/UIAWindow[2]/UIATableView[1]/UIATableCell[2]")
|
7
|
+
|
8
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#UIAApplication[1]/UIAWindow[2]/UIATableView[1]
|
2
|
+
|
3
|
+
Feature: Table
|
4
|
+
In order to access the table functionality
|
5
|
+
we need to test all the operations
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given I am on UICatalog screen
|
9
|
+
|
10
|
+
Scenario: Verify the table cell count method
|
11
|
+
When I verify the count of table cells I should get an integer
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Button
|
2
|
+
In order to access the text functionality
|
3
|
+
we need to test all the operations
|
4
|
+
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given I am on login page
|
8
|
+
|
9
|
+
Scenario: Verify the text field exists method
|
10
|
+
When I verify the existence of the text filed return value should be true
|
@@ -0,0 +1,161 @@
|
|
1
|
+
=begin
|
2
|
+
***********************************************************************************************************
|
3
|
+
Copyright 2016 Capital One Services, LLC
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and limitations under the License.
|
15
|
+
***********************************************************************************************************
|
16
|
+
=end
|
17
|
+
|
18
|
+
require 'appium_lib'
|
19
|
+
require 'screen-object/load_appium'
|
20
|
+
require 'screen-object/accessors'
|
21
|
+
require 'screen-object/elements'
|
22
|
+
require 'screen-object/screen_factory'
|
23
|
+
require 'screen-object/accessors/element'
|
24
|
+
|
25
|
+
# this module adds screen object when included.
|
26
|
+
# This module will add instance methods and screen object that you use to define and interact with mobile objects
|
27
|
+
|
28
|
+
module ScreenObject
|
29
|
+
|
30
|
+
def self.included(cls)
|
31
|
+
cls.extend ScreenObject::Accessors
|
32
|
+
end
|
33
|
+
|
34
|
+
def driver
|
35
|
+
ScreenObject::AppElements::Element.new('').driver
|
36
|
+
end
|
37
|
+
|
38
|
+
def swipe(start_x,start_y,end_x,end_y,touch_count,duration)
|
39
|
+
driver.swipe(:start_x => start_x, :start_y => start_y, :end_x => end_x, :end_y => end_y,:touchCount => touch_count,:duration => duration)
|
40
|
+
end
|
41
|
+
|
42
|
+
def landscape
|
43
|
+
driver.driver.rotate :landscape
|
44
|
+
end
|
45
|
+
|
46
|
+
def portrait
|
47
|
+
driver.driver.rotate :portrait
|
48
|
+
end
|
49
|
+
|
50
|
+
def back
|
51
|
+
driver.back
|
52
|
+
end
|
53
|
+
|
54
|
+
def wait_until(timeout = 5, message = nil, &block)
|
55
|
+
wait = Selenium::WebDriver::Wait.new(timeout: timeout, message: message)
|
56
|
+
wait.until &block
|
57
|
+
end
|
58
|
+
|
59
|
+
def wait_step(timeout = 5, message = nil, &block)
|
60
|
+
default_wait = driver.default_wait
|
61
|
+
wait = Selenium::WebDriver::Wait.new(:timeout => driver.set_wait(timeout), :message => message)
|
62
|
+
wait.until &block
|
63
|
+
driver.set_wait(default_wait)
|
64
|
+
end
|
65
|
+
|
66
|
+
def enter
|
67
|
+
#pending implementation
|
68
|
+
end
|
69
|
+
|
70
|
+
def scroll_down_find(locator,locator_value,num_loop = 15)
|
71
|
+
scr = driver.window_size
|
72
|
+
screenHeightStart = (scr.height) * 0.5
|
73
|
+
scrollStart = screenHeightStart.to_i
|
74
|
+
screenHeightEnd = (scr.height) * 0.2
|
75
|
+
scrollEnd = screenHeightEnd.to_i
|
76
|
+
for i in 0..num_loop
|
77
|
+
begin
|
78
|
+
if (driver.find_element(locator,locator_value).displayed?)
|
79
|
+
break
|
80
|
+
end
|
81
|
+
rescue
|
82
|
+
driver.swipe(:start_x => 0,:start_y => scrollStart,:end_x =>0,:end_y =>scrollEnd,:touchCount => 2,:duration => 0)
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def scroll_down_click(locator,locator_value,num_loop = 15)
|
89
|
+
scr = driver.window_size
|
90
|
+
screenHeightStart = (scr.height) * 0.5
|
91
|
+
scrollStart = screenHeightStart.to_i
|
92
|
+
screenHeightEnd = (scr.height) * 0.2
|
93
|
+
scrollEnd = screenHeightEnd.to_i
|
94
|
+
for i in 0..num_loop
|
95
|
+
begin
|
96
|
+
if (driver.find_element(locator,locator_value).displayed?)
|
97
|
+
driver.find_element(locator,locator_value).click
|
98
|
+
break
|
99
|
+
end
|
100
|
+
rescue
|
101
|
+
driver.swipe(:start_x => 0,:start_y => scrollStart,:end_x =>0,:end_y =>scrollEnd,:touchCount => 2,:duration => 0)
|
102
|
+
false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def scroll_up_find(locator,locator_value,num_loop = 15)
|
108
|
+
scr = driver.window_size
|
109
|
+
screenHeightStart = (scr.height) * 0.5
|
110
|
+
scrollStart = screenHeightStart.to_i
|
111
|
+
screenHeightEnd = (scr.height) * 0.2
|
112
|
+
scrollEnd = screenHeightEnd.to_i
|
113
|
+
for i in 0..num_loop
|
114
|
+
begin
|
115
|
+
if (driver.find_element(locator,locator_value).displayed?)
|
116
|
+
break
|
117
|
+
end
|
118
|
+
rescue
|
119
|
+
driver.swipe(:start_x => 0,:start_y => scrollEnd,:end_x =>0,:end_y =>scrollStart,:touchCount => 2,:duration => 0)
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def scroll_up_click(locator,locator_value,num_loop = 15)
|
127
|
+
scr = driver.window_size
|
128
|
+
screenHeightStart = (scr.height) * 0.5
|
129
|
+
scrollStart = screenHeightStart.to_i
|
130
|
+
screenHeightEnd = (scr.height) * 0.2
|
131
|
+
scrollEnd = screenHeightEnd.to_i
|
132
|
+
for i in 0..num_loop
|
133
|
+
begin
|
134
|
+
if (driver.find_element(locator,locator_value).displayed?)
|
135
|
+
driver.find_element(locator,locator_value).click
|
136
|
+
break
|
137
|
+
end
|
138
|
+
rescue
|
139
|
+
driver.swipe(:start_x => 0,:start_y => scrollEnd,:end_x =>0,:end_y =>scrollStart,:touchCount => 2,:duration => 0)
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def drag_and_drop_element(source_locator,source_locator_value,target_locator,target_locator_value)
|
147
|
+
l_draggable = driver.find_element(source_locator,source_locator_value)
|
148
|
+
l_droppable = driver.find_element(target_locator,target_locator_value)
|
149
|
+
obj1= Appium::TouchAction.new
|
150
|
+
obj1.long_press(:x => l_draggable.location.x,:y => l_draggable.location.y).move_to(:x => l_droppable.location.x,:y => l_droppable.location.y).release.perform
|
151
|
+
end
|
152
|
+
|
153
|
+
def keyboard_hide
|
154
|
+
begin
|
155
|
+
driver.hide_keyboard
|
156
|
+
rescue
|
157
|
+
false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|