kuality-coeus 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +16 -18
- data/chromedriver.log +46 -0
- data/features/grants_gov/grants_gov_forms/code_and_form_mapping.feature +26 -0
- data/features/grants_gov/grants_gov_forms/forms_validation.feature +1 -0
- data/features/grants_gov/grants_gov_forms/s2s_questionnaire.feature +19 -0
- data/features/grants_gov/s2s_submission.feature +30 -0
- data/features/grants_gov/s2s_validation.feature +36 -0
- data/features/proposal_development/create_budget_versions.feature +31 -0
- data/features/proposal_development/create_proposal.feature +25 -0
- data/features/proposal_development/key_personnel_validations.feature +41 -0
- data/features/proposal_development/proposal_actions_validations.feature +46 -0
- data/features/proposal_development/proposal_permissions.feature +73 -0
- data/features/proposal_development/proposal_workflow.feature +105 -0
- data/features/proposal_development/special_review_validations.feature +12 -0
- data/features/step_definitions/action_list.rb +12 -0
- data/features/step_definitions/awards/create_award.rb +3 -0
- data/features/step_definitions/grants_gov/forms.rb +34 -0
- data/features/step_definitions/grants_gov/s2s_submission.rb +51 -0
- data/features/step_definitions/institutional_proposals/create_institutional_proposal.rb +3 -0
- data/features/step_definitions/proposal_development/budget_versions.rb +102 -0
- data/features/step_definitions/proposal_development/create_proposal.rb +157 -0
- data/features/step_definitions/proposal_development/edit_proposal.rb +79 -0
- data/features/step_definitions/proposal_development/institute_rates.rb +21 -0
- data/features/step_definitions/proposal_development/key_personnel_validations.rb +74 -0
- data/features/step_definitions/proposal_development/proposal_actions_validations.rb +73 -0
- data/features/step_definitions/proposal_development/proposal_permissions.rb +141 -0
- data/features/step_definitions/proposal_development/proposal_workflow.rb +81 -0
- data/features/step_definitions/proposal_development/s2s_validation.rb +30 -0
- data/features/step_definitions/proposal_development/special_review_validations.rb +7 -0
- data/features/step_definitions/test.rb +3 -0
- data/features/step_definitions/users.rb +65 -0
- data/features/support/env.rb +35 -0
- data/features/test.feature +13 -0
- data/kuality-coeus.gemspec +1 -1
- data/lib/chromedriver.log +4483 -0
- data/lib/kuality-coeus.rb +5 -2
- data/lib/kuality-coeus/core_extensions.rb +10 -0
- data/lib/kuality-coeus/data_objects/award/award.rb +126 -0
- data/lib/kuality-coeus/data_objects/award/award_transaction.rb +57 -0
- data/lib/kuality-coeus/data_objects/budget/budget_periods.rb +114 -0
- data/lib/kuality-coeus/data_objects/budget/budget_versions.rb +221 -0
- data/lib/kuality-coeus/data_objects/budget/subaward_budget.rb +65 -0
- data/lib/kuality-coeus/data_objects/committee_document/committee_document.rb +65 -0
- data/lib/kuality-coeus/data_objects/committee_document/committee_members.rb +30 -0
- data/lib/kuality-coeus/data_objects/committee_document/committee_schedule.rb +27 -0
- data/lib/kuality-coeus/data_objects/committee_document/member_roles.rb +28 -0
- data/lib/kuality-coeus/data_objects/conflict_of_interest/financial_entity.rb +48 -0
- data/lib/kuality-coeus/data_objects/grants_gov/phs_fellowship_questionnaire.rb +90 -0
- data/lib/kuality-coeus/data_objects/grants_gov/phs_training_budget_questionnaire.rb +5 -0
- data/lib/kuality-coeus/data_objects/institutional_proposal/institutional_proposal.rb +20 -0
- data/lib/kuality-coeus/data_objects/institutional_proposal/intellectual_property_review.rb +62 -0
- data/lib/kuality-coeus/data_objects/navigation.rb +64 -0
- data/lib/kuality-coeus/data_objects/proposal_development/compliance_questions.rb +47 -0
- data/lib/kuality-coeus/data_objects/proposal_development/custom_data.rb +41 -0
- data/lib/kuality-coeus/data_objects/proposal_development/degrees.rb +40 -0
- data/lib/kuality-coeus/data_objects/proposal_development/key_personnel.rb +299 -0
- data/lib/kuality-coeus/data_objects/proposal_development/kuali_university_questions.rb +58 -0
- data/lib/kuality-coeus/data_objects/proposal_development/permissions.rb +107 -0
- data/lib/kuality-coeus/data_objects/proposal_development/personnel_attachments.rb +46 -0
- data/lib/kuality-coeus/data_objects/proposal_development/proposal_attachments.rb +41 -0
- data/lib/kuality-coeus/data_objects/proposal_development/proposal_development.rb +301 -0
- data/lib/kuality-coeus/data_objects/proposal_development/proposal_questions.rb +52 -0
- data/lib/kuality-coeus/data_objects/proposal_development/s2s_questionnaire.rb +129 -0
- data/lib/kuality-coeus/data_objects/proposal_development/special_review.rb +79 -0
- data/lib/kuality-coeus/data_objects/rates/institute_rate.rb +209 -0
- data/lib/kuality-coeus/data_objects/user.rb +303 -33
- data/lib/kuality-coeus/gem_extensions.rb +26 -0
- data/lib/kuality-coeus/page_objects/000_base_page.rb +196 -14
- data/lib/kuality-coeus/page_objects/action_list.rb +32 -0
- data/lib/kuality-coeus/page_objects/award/award.rb +25 -0
- data/lib/kuality-coeus/page_objects/award/award_actions.rb +7 -0
- data/lib/kuality-coeus/page_objects/award/award_budget_versions.rb +9 -0
- data/lib/kuality-coeus/page_objects/award/commitments.rb +27 -0
- data/lib/kuality-coeus/page_objects/award/contacts.rb +19 -0
- data/lib/kuality-coeus/page_objects/award/custom_data.rb +6 -0
- data/lib/kuality-coeus/page_objects/award/payment_reports_terms.rb +26 -0
- data/lib/kuality-coeus/page_objects/award/special_review.rb +7 -0
- data/lib/kuality-coeus/page_objects/award/time_and_money.rb +36 -0
- data/lib/kuality-coeus/page_objects/budget/budget_actions.rb +31 -0
- data/lib/kuality-coeus/page_objects/budget/budget_versions.rb +13 -0
- data/lib/kuality-coeus/page_objects/budget/modular_budget.rb +16 -0
- data/lib/kuality-coeus/page_objects/budget/non-personnel.rb +11 -0
- data/lib/kuality-coeus/page_objects/budget/parameters.rb +65 -0
- data/lib/kuality-coeus/page_objects/budget/personnel.rb +56 -0
- data/lib/kuality-coeus/page_objects/budget/rates.rb +7 -0
- data/lib/kuality-coeus/page_objects/budget/summary.rb +5 -0
- data/lib/kuality-coeus/page_objects/budget_document.rb +19 -0
- data/lib/kuality-coeus/page_objects/central_admin.rb +4 -2
- data/lib/kuality-coeus/page_objects/committee/committee.rb +10 -10
- data/lib/kuality-coeus/page_objects/committee/members.rb +4 -4
- data/lib/kuality-coeus/page_objects/committee_document.rb +1 -6
- data/lib/kuality-coeus/page_objects/confirmation.rb +16 -0
- data/lib/kuality-coeus/page_objects/disclosure/disclosure.rb +56 -0
- data/lib/kuality-coeus/page_objects/disclosure/disclosure_actions.rb +9 -0
- data/lib/kuality-coeus/page_objects/document_header.rb +5 -0
- data/lib/kuality-coeus/page_objects/financial_entities.rb +1 -4
- data/lib/kuality-coeus/page_objects/financial_entities/my_financial_entities.rb +6 -0
- data/lib/kuality-coeus/page_objects/financial_entities/new_financial_entity.rb +11 -0
- data/lib/kuality-coeus/page_objects/financial_entities/reporter.rb +5 -3
- data/lib/kuality-coeus/page_objects/identity/person.rb +57 -0
- data/lib/kuality-coeus/page_objects/institute_rates_maintenance.rb +18 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal.rb +18 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/contacts.rb +32 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/custom_data.rb +6 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/distribution.rb +20 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/institutional_proposal.rb +27 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/institutional_proposal_actions.rb +7 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/intellectual_property_review.rb +20 -0
- data/lib/kuality-coeus/page_objects/institutional_proposal/special_review.rb +6 -0
- data/lib/kuality-coeus/page_objects/kc_awards.rb +48 -0
- data/lib/kuality-coeus/page_objects/kc_protocol.rb +15 -0
- data/lib/kuality-coeus/page_objects/login.rb +4 -5
- data/lib/kuality-coeus/page_objects/lookup_pages/000_lookups.rb +10 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/action_list_filter.rb +6 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/address_book_lookup.rb +4 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/development_proposal_lookup.rb +3 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/document_search.rb +10 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/group_lookup.rb +3 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/institute_rates_lookup.rb +12 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/opportunity_lookup.rb +6 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/organization_lookup.rb +5 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/person_extended_attributes.rb +5 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/person_lookup.rb +10 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/role_lookup.rb +6 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/sponsor_lookup.rb +9 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/unit_admin_lookup.rb +3 -0
- data/lib/kuality-coeus/page_objects/lookup_pages/unit_lookup.rb +3 -0
- data/lib/kuality-coeus/page_objects/maintenance.rb +7 -0
- data/lib/kuality-coeus/page_objects/notification_editor.rb +6 -0
- data/lib/kuality-coeus/page_objects/proposal_development.rb +7 -2
- data/lib/kuality-coeus/page_objects/proposal_development/abstracts_and_attachments.rb +54 -0
- data/lib/kuality-coeus/page_objects/proposal_development/key_personnel.rb +126 -0
- data/lib/kuality-coeus/page_objects/proposal_development/pd_custom_data.rb +6 -0
- data/lib/kuality-coeus/page_objects/proposal_development/permissions.rb +44 -0
- data/lib/kuality-coeus/page_objects/proposal_development/phs_fellowship_questionnaire.rb +44 -0
- data/lib/kuality-coeus/page_objects/proposal_development/phs_training_budget_questionnaire.rb +50 -0
- data/lib/kuality-coeus/page_objects/proposal_development/proposal.rb +47 -8
- data/lib/kuality-coeus/page_objects/proposal_development/proposal_actions.rb +62 -0
- data/lib/kuality-coeus/page_objects/proposal_development/proposal_summary.rb +9 -0
- data/lib/kuality-coeus/page_objects/proposal_development/questions.rb +111 -0
- data/lib/kuality-coeus/page_objects/proposal_development/s2s.rb +35 -0
- data/lib/kuality-coeus/page_objects/proposal_development/special_review.rb +6 -0
- data/lib/kuality-coeus/page_objects/protocol/custom_data.rb +6 -0
- data/lib/kuality-coeus/page_objects/protocol/permissions.rb +5 -0
- data/lib/kuality-coeus/page_objects/protocol/personnel.rb +5 -0
- data/lib/kuality-coeus/page_objects/protocol/protocol.rb +11 -0
- data/lib/kuality-coeus/page_objects/protocol/questionnaire.rb +5 -0
- data/lib/kuality-coeus/page_objects/protocol/special_review.rb +6 -0
- data/lib/kuality-coeus/page_objects/rejection_confirmation.rb +5 -0
- data/lib/kuality-coeus/page_objects/researcher.rb +4 -2
- data/lib/kuality-coeus/page_objects/shared/unit_administrator.rb +13 -0
- data/lib/kuality-coeus/page_objects/system/person_extended_attributes.rb +18 -0
- data/lib/kuality-coeus/page_objects/system_admin.rb +7 -0
- data/lib/kuality-coeus/page_objects/unit.rb +2 -4
- data/lib/kuality-coeus/utilities.rb +39 -0
- data/libpeerconnection.log +0 -0
- metadata +140 -5
- data/lib/kuality-coeus/data_objects/committee_document.rb +0 -65
- data/lib/kuality-coeus/data_objects/proposal_development.rb +0 -53
- data/lib/kuality-coeus/navigation.rb +0 -3
@@ -1,40 +1,317 @@
|
|
1
|
+
class UserCollection < Hash
|
2
|
+
|
3
|
+
# Returns an array of all users with the specified role. Takes the role name as a string.
|
4
|
+
# The array is shuffled so that #have_role('role name')[0] will be a random selection
|
5
|
+
# from the list of matching users.
|
6
|
+
def have_role(role)
|
7
|
+
self.find_all{|user| user[1][:roles] != nil && user[1][:roles].include?(role)}.shuffle
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns an array of all users with the specified campus code. Takes the code as a string.
|
11
|
+
# The array is shuffled so that #with_campus_code('code')[0] will be a random selection
|
12
|
+
# from the list of matching users.
|
13
|
+
def with_campus_code(code)
|
14
|
+
self.find_all{|user| user[1][:campus_code]==code }.shuffle
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns an array of all users with the specified affiliation type. Takes the type name as a string.
|
18
|
+
# The array is shuffled so that #with_affiliation_type('type name')[0] will be a random selection
|
19
|
+
# from the list of matching users.
|
20
|
+
def with_affiliation_type(type)
|
21
|
+
self.find_all{|user| user[1][:affiliation_type]==type }.shuffle
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_employee_type(type)
|
25
|
+
self.find_all{|user| user[1][:employee_type]==type }.shuffle
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_primary_dept_code(code)
|
29
|
+
self.find_all{|user| user[1][:primary_dept_code]==code }.shuffle
|
30
|
+
end
|
31
|
+
|
32
|
+
# This is a short cut to "knowing" which user can be used as a PI for
|
33
|
+
# a Grants.gov proposal. At some point this should be eliminated--as in,
|
34
|
+
# when we know better what exactly a grants.gov PI requires.
|
35
|
+
def era_commons_user(username)
|
36
|
+
self.find{ |user| user[1][:era_commons_user_name]==username }[0]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
1
41
|
class UserObject
|
2
42
|
|
3
43
|
include Foundry
|
4
44
|
include DataFactory
|
45
|
+
include Navigation
|
46
|
+
|
47
|
+
attr_accessor :user_name, :principal_id,
|
48
|
+
:first_name, :last_name,
|
49
|
+
:description, :affiliation_type, :campus_code,
|
50
|
+
:employee_id, :employee_status, :employee_type, :base_salary, :primary_dept_code,
|
51
|
+
:groups, :roles, :role_qualifiers, :addresses, :phones, :emails,
|
52
|
+
:primary_title, :directory_title, :citizenship_type,
|
53
|
+
:era_commons_user_name, :graduate_student_count, :billing_element,
|
54
|
+
:directory_department
|
5
55
|
|
6
|
-
|
56
|
+
USERS = UserCollection[YAML.load_file("#{File.dirname(__FILE__)}/users.yml")]
|
57
|
+
|
58
|
+
ROLES = {
|
59
|
+
# Add roles here as needed for testing...
|
60
|
+
'Aggregator' => '110',
|
61
|
+
'approver' => '103',
|
62
|
+
'Award Budget Aggregator' => '113',
|
63
|
+
'Award Budget Approver' => '112',
|
64
|
+
'Award Budget Modifier' => '102',
|
65
|
+
'Award Budget Viewer' => '101',
|
66
|
+
'Award Viewer' => '123',
|
67
|
+
'Budget Creator' => '108',
|
68
|
+
'Create Proposal Log' => '140',
|
69
|
+
'Departments Awards Viewer' => '121',
|
70
|
+
'IACUC Protocol Aggregator' => '1421',
|
71
|
+
'IACUC Protocol Approver' => '1638',
|
72
|
+
'Institutional Proposal Viewer' => '118',
|
73
|
+
'IRB Administrator' => '128',
|
74
|
+
'IRB Approver' => '99',
|
75
|
+
'IRB Reviewer' => '127',
|
76
|
+
'KC Super User' => '177',
|
77
|
+
'Maintain IRB Questionnaire' => '161',
|
78
|
+
'Maintain Proposal Questionnaire' => '162',
|
79
|
+
'Manager' => '98',
|
80
|
+
'Narrative Writer' => '109',
|
81
|
+
'Negotiation Creator' => '1382',
|
82
|
+
'OSP Administrator' => '131',
|
83
|
+
'OSPApprover' => '100',
|
84
|
+
'Protocol Aggregator' => '105',
|
85
|
+
'Proposal Creator' => '111',
|
86
|
+
'System User' => '90',
|
87
|
+
'Unassigned' => '106',
|
88
|
+
'Viewer' => '107',
|
89
|
+
'View Subaward' => '1409',
|
90
|
+
'View Proposal Log' => '142'
|
91
|
+
}
|
7
92
|
|
8
93
|
def initialize(browser, opts={})
|
9
94
|
@browser = browser
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
95
|
+
@user_name=case
|
96
|
+
when opts.empty?
|
97
|
+
'admin'
|
98
|
+
when opts.key?(:user)
|
99
|
+
opts[:user]
|
100
|
+
when opts.key?(:role)
|
101
|
+
USERS.have_role(ROLES[opts[:role]])[0][0]
|
102
|
+
end
|
103
|
+
defaults = USERS[@user_name]
|
104
|
+
defaults.nil? ? options=opts : options=defaults.merge(opts)
|
105
|
+
set_options options
|
17
106
|
end
|
18
107
|
|
19
|
-
def
|
20
|
-
|
108
|
+
def create
|
109
|
+
# First we have to make sure we're logged in with
|
110
|
+
# a user that has permissions to create other users...
|
111
|
+
if login_info_div.text =~ /admin$/
|
112
|
+
visit(SystemAdmin).person unless PersonLookup.new(@browser).principal_id.present?
|
113
|
+
else
|
114
|
+
@logged_in_user_name = login_info_div.text[/\w+$/]
|
115
|
+
visit Login do |log_in|
|
116
|
+
log_in.close_parents
|
117
|
+
log_in.username.set 'admin'
|
118
|
+
log_in.login
|
119
|
+
end
|
120
|
+
visit(SystemAdmin).person
|
121
|
+
end
|
122
|
+
# Now we're certain the create button will be there, so...
|
123
|
+
on(PersonLookup).create
|
124
|
+
on Person do |add|
|
125
|
+
add.expand_all
|
126
|
+
add.principal_name.set @user_name
|
127
|
+
fill_out add, :description, :affiliation_type, :campus_code, :first_name, :last_name
|
128
|
+
# TODO: These "default" checkboxes will need to be reworked if and when
|
129
|
+
# a test is going to require multiple affiliations, names, addresses, etc.
|
130
|
+
# Until then, there's no need to do anything other than set the necessary single values
|
131
|
+
# as "default"...
|
132
|
+
add.affiliation_default.set
|
133
|
+
add.name_default.set
|
134
|
+
add.add_name
|
135
|
+
add.add_affiliation
|
136
|
+
# TODO: Another thing that will need to be changed if ever there's a need to test multiple
|
137
|
+
# lines of employment:
|
138
|
+
unless @employee_id.nil?
|
139
|
+
fill_out add, :employee_id, :employee_status, :employee_type, :base_salary, :primary_dept_code
|
140
|
+
add.primary_employment.set
|
141
|
+
add.add_employment_information
|
142
|
+
end
|
143
|
+
unless @roles.nil?
|
144
|
+
@roles.each do |role|
|
145
|
+
add.role_id.set role
|
146
|
+
add.add_role
|
147
|
+
end
|
148
|
+
end
|
149
|
+
unless @role_qualifiers.nil?
|
150
|
+
@role_qualifiers.each do |role, unit|
|
151
|
+
add.unit_number(role).set unit
|
152
|
+
add.add_role_qualifier role
|
153
|
+
end
|
154
|
+
end
|
155
|
+
unless @groups.nil?
|
156
|
+
@groups.each do |group|
|
157
|
+
add.group_id.set group
|
158
|
+
add.add_group
|
159
|
+
end
|
160
|
+
end
|
161
|
+
unless @addresses.nil?
|
162
|
+
@addresses.each do |address|
|
163
|
+
add.address_type.fit address[:type]
|
164
|
+
add.line_1.fit address[:line_1]
|
165
|
+
add.city.fit address[:city]
|
166
|
+
add.state.pick! address[:state]
|
167
|
+
add.country.pick! address[:country]
|
168
|
+
add.zip.fit address[:zip]
|
169
|
+
add.address_default.fit address[:default]
|
170
|
+
add.add_address
|
171
|
+
end
|
172
|
+
end
|
173
|
+
unless @phones.nil?
|
174
|
+
@phones.each do |phone|
|
175
|
+
add.phone_type.fit phone[:type]
|
176
|
+
add.phone_number.fit phone[:number]
|
177
|
+
add.phone_default.fit phone[:default]
|
178
|
+
add.add_phone
|
179
|
+
end
|
180
|
+
end
|
181
|
+
unless @emails.nil?
|
182
|
+
@emails.each do |email|
|
183
|
+
add.email.fit email[:email]
|
184
|
+
add.email_type.pick! email[:type]
|
185
|
+
add.email_default.fit email[:default]
|
186
|
+
add.add_email
|
187
|
+
end
|
188
|
+
end
|
189
|
+
# A breaking of the design pattern, but there's no other
|
190
|
+
# way to obtain this number:
|
191
|
+
@principal_id = add.principal_id
|
192
|
+
add.blanket_approve
|
193
|
+
end
|
194
|
+
visit(SystemAdmin).person_extended_attributes
|
195
|
+
on(PersonExtendedAttributesLookup).create
|
196
|
+
on PersonExtendedAttributes do |page|
|
197
|
+
page.expand_all
|
198
|
+
fill_out page, :description, :primary_title, :directory_title, :citizenship_type,
|
199
|
+
:era_commons_user_name, :graduate_student_count, :billing_element,
|
200
|
+
:principal_id, :directory_department
|
201
|
+
page.blanket_approve
|
202
|
+
end
|
203
|
+
# Now that we're done with the user creation, we can log back in
|
204
|
+
# with the other user, if necessary...
|
205
|
+
unless @logged_in_user_name.nil?
|
206
|
+
s_o.click
|
207
|
+
on Login do |log_in|
|
208
|
+
log_in.username.set @logged_in_user_name
|
209
|
+
log_in.login
|
210
|
+
end
|
211
|
+
end
|
21
212
|
end
|
22
213
|
|
214
|
+
# Keep in mind...
|
215
|
+
# - This method does nothing if the user
|
216
|
+
# is already logged in
|
217
|
+
# - If some other user is logged in, they
|
218
|
+
# will be automatically logged out
|
219
|
+
# - This method will close all child
|
220
|
+
# tabs/windows and return to the window
|
221
|
+
# with the header frame, so it can see
|
222
|
+
# who is currently logged in
|
23
223
|
def sign_in
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
224
|
+
unless logged_in?
|
225
|
+
if username_field.present?
|
226
|
+
# Do nothing because we're already there
|
227
|
+
else
|
228
|
+
visit Researcher do |page|
|
229
|
+
page.return_to_portal
|
230
|
+
page.close_children
|
231
|
+
page.logout
|
232
|
+
end
|
233
|
+
end
|
234
|
+
on Login do |log_in|
|
235
|
+
log_in.username.set @user_name
|
236
|
+
log_in.login
|
237
|
+
end
|
238
|
+
on(Researcher).logout_button.wait_until_present
|
29
239
|
end
|
30
240
|
end
|
31
241
|
alias_method :log_in, :sign_in
|
32
242
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
243
|
+
def sign_out
|
244
|
+
# This _might_ cause an infinite loop, but I'm
|
245
|
+
# hoping not...
|
246
|
+
on(Researcher) do |page|
|
247
|
+
page.return_to_portal
|
248
|
+
page.close_children
|
249
|
+
end
|
250
|
+
if s_o.present?
|
251
|
+
s_o.click
|
36
252
|
else
|
253
|
+
visit Login do |page|
|
254
|
+
if page.username.present?
|
255
|
+
# do nothing because we're already logged out...
|
256
|
+
else
|
257
|
+
sign_out
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
alias_method :log_out, :sign_out
|
263
|
+
|
264
|
+
def exist?
|
265
|
+
visit SystemAdmin do |page|
|
266
|
+
if username_field.present?
|
267
|
+
UserObject.new(@browser).log_in
|
268
|
+
end
|
269
|
+
page.person
|
270
|
+
end
|
271
|
+
on PersonLookup do |search|
|
272
|
+
search.principal_name.set @user_name
|
273
|
+
search.search
|
274
|
+
begin
|
275
|
+
if search.item_row(@user_name).present?
|
276
|
+
# FIXME!
|
277
|
+
# This is a coding abomination to include
|
278
|
+
# this here, but it's here until I can come
|
279
|
+
# up with a better solution...
|
280
|
+
@principal_id = search.item_row(@user_name).link(title: /^Person Principal ID=\d+/).text
|
281
|
+
return true
|
282
|
+
else
|
283
|
+
return false
|
284
|
+
end
|
285
|
+
rescue
|
286
|
+
return false
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
alias_method :exists?, :exist?
|
291
|
+
|
292
|
+
def logged_in?
|
293
|
+
# Are we on the login page already?
|
294
|
+
if username_field.present?
|
295
|
+
# Yes! So, we're not logged in...
|
37
296
|
false
|
297
|
+
# No, the Kuali header is showing...
|
298
|
+
elsif login_info_div.present?
|
299
|
+
# So, is the user currently listed as logged in?
|
300
|
+
return login_info_div.text.include? @user_name
|
301
|
+
else # We're on some page that has no Kuali header, so...
|
302
|
+
begin
|
303
|
+
# We'll assume that the portal window exists, and go to it.
|
304
|
+
on(Researcher).return_to_portal
|
305
|
+
# Oops. Apparently there's no portal window, so...
|
306
|
+
rescue
|
307
|
+
# We'll close any extra tabs/windows
|
308
|
+
visit(Login).close_children if @browser.windows.size > 1
|
309
|
+
# And make sure that we're using the "parent" window
|
310
|
+
@browser.windows[0].use
|
311
|
+
end
|
312
|
+
# Now that things are hopefully in a clean state, we'll start
|
313
|
+
# the process again...
|
314
|
+
logged_in?
|
38
315
|
end
|
39
316
|
end
|
40
317
|
|
@@ -42,28 +319,21 @@ class UserObject
|
|
42
319
|
!logged_in?
|
43
320
|
end
|
44
321
|
|
45
|
-
def log_out
|
46
|
-
s_o.click if s_o.present?
|
47
|
-
end
|
48
|
-
alias_method :sign_out, :log_out
|
49
|
-
|
50
322
|
#========
|
51
323
|
private
|
52
324
|
#========
|
53
325
|
|
54
|
-
def
|
55
|
-
|
56
|
-
log_in.username.set @name
|
57
|
-
log_in.login
|
58
|
-
end
|
326
|
+
def s_o
|
327
|
+
@browser.button(value: 'Logout')
|
59
328
|
end
|
60
329
|
|
61
|
-
def
|
62
|
-
@browser.
|
330
|
+
def login_info_div
|
331
|
+
@browser.div(id: 'login-info')
|
63
332
|
end
|
64
333
|
|
65
|
-
def
|
66
|
-
@browser.
|
334
|
+
def username_field
|
335
|
+
Login.new(@browser).username
|
67
336
|
end
|
68
337
|
|
69
|
-
end
|
338
|
+
end
|
339
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Watir
|
2
|
+
module Container
|
3
|
+
def frm
|
4
|
+
case
|
5
|
+
when frame(id: 'iframeportlet').exist?
|
6
|
+
frame(id: 'iframeportlet')
|
7
|
+
when frame(id: /easyXDM_default\d+_provider/).frame(id: 'iframeportlet').exist?
|
8
|
+
frame(id: /easyXDM_default\d+_provider/).frame(id: 'iframeportlet')
|
9
|
+
when frame(id: /easyXDM_default\d+_provider/).exist?
|
10
|
+
frame(id: /easyXDM_default\d+_provider/)
|
11
|
+
else
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Because of the unique way we
|
18
|
+
# set up radio buttons in Coeus,
|
19
|
+
# we can use this method in our
|
20
|
+
# radio button definitions.
|
21
|
+
class Radio
|
22
|
+
def fit answer
|
23
|
+
set unless answer==nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,35 +1,217 @@
|
|
1
1
|
class BasePage < PageFactory
|
2
2
|
|
3
|
+
action(:use_new_tab) { |b| b.windows.last.use }
|
4
|
+
action(:return_to_portal) { |b| b.portal_window.use }
|
5
|
+
action(:close_children) { |b| b.windows[1..-1].each{ |w| w.close} }
|
6
|
+
action(:close_parents) { |b| b.windows[0..-2].each{ |w| w.close} }
|
7
|
+
action(:loading) { |b| b.frm.image(alt: 'working...').wait_while_present }
|
8
|
+
element(:logout_button) { |b| b.button(title: 'Click to logout.') }
|
9
|
+
action(:logout) { |b| b.logout_button.click }
|
10
|
+
|
11
|
+
element(:portal_window) { |b| b.windows(title: 'Kuali Portal Index')[0] }
|
12
|
+
|
13
|
+
action(:form_tab) { |name, b| b.frm.h2(text: /#{name}/) }
|
14
|
+
action(:form_status) { |name, b| b.form_tab(name).text[/(?<=\()\w+/] }
|
15
|
+
|
3
16
|
class << self
|
4
17
|
|
5
|
-
def
|
6
|
-
|
18
|
+
def glbl(*titles)
|
19
|
+
titles.each do |title|
|
20
|
+
action(damballa(title)) { |b| b.frm.button(class: 'globalbuttons', title: title).click; b.loading }
|
21
|
+
end
|
7
22
|
end
|
8
23
|
|
9
24
|
def document_header_elements
|
10
|
-
|
11
|
-
|
25
|
+
value(:doc_title) { |b| b.frm.div(id: 'headerarea').h1.text }
|
26
|
+
element(:headerinfo_table) { |b| b.frm.div(id: 'headerarea').table(class: 'headerinfo') }
|
12
27
|
value(:document_id) { |p| p.headerinfo_table[0][1].text }
|
13
28
|
alias_method :doc_nbr, :document_id
|
14
|
-
value(:
|
29
|
+
value(:document_status) { |p| p.headerinfo_table[0][3].text }
|
15
30
|
value(:initiator) { |p| p.headerinfo_table[1][1].text }
|
31
|
+
alias_method :disposition, :initiator
|
16
32
|
value(:last_updated) {|p| p.headerinfo_table[1][3].text }
|
17
33
|
alias_method :created, :last_updated
|
18
34
|
value(:committee_id) { |p| p.headerinfo_table[2][1].text }
|
19
35
|
alias_method :sponsor_name, :committee_id
|
36
|
+
alias_method :budget_name, :committee_id
|
20
37
|
value(:committee_name) { |p| p.headerinfo_table[2][3].text }
|
21
38
|
alias_method :pi, :committee_name
|
22
39
|
end
|
23
40
|
|
41
|
+
# Included here because this is such a common field in KC
|
42
|
+
def description_field
|
43
|
+
element(:description) { |b| b.frm.text_field(name: 'document.documentHeader.documentDescription') }
|
44
|
+
end
|
45
|
+
|
24
46
|
def global_buttons
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
47
|
+
glbl 'save', 'Reject', 'blanket approve', 'close', 'cancel', 'reload',
|
48
|
+
'Delete Proposal', 'approve', 'disapprove',
|
49
|
+
'Generate All Periods', 'Calculate All Periods', 'Default Periods',
|
50
|
+
'Calculate Current Period', 'submit', 'Send Notification'
|
51
|
+
# Explicitly defining the "recall" button to keep the method name at "recall" instead of "recall_current_document"...
|
52
|
+
element(:recall_button) { |b| b.frm.button(class: 'globalbuttons', title: 'Recall current document') }
|
53
|
+
action(:recall) { |b| b.recall_button.click; b.loading }
|
54
|
+
action(:edit) { |b| b.frm.button(name: 'methodToCall.editOrVersion').click; b.loading }
|
55
|
+
action(:delete_selected) { |b| b.frm.button(class: 'globalbuttons', name: 'methodToCall.deletePerson').click; b.loading }
|
56
|
+
element(:send_button) { |b| b.frm.button(class: 'globalbuttons', name: 'methodToCall.sendNotification', title: 'send') }
|
57
|
+
action(:send_fyi) { |b| b.send_button.click; b.loading }
|
58
|
+
end
|
59
|
+
|
60
|
+
def tab_buttons
|
61
|
+
action(:expand_all) { |b| b.frm.button(name: 'methodToCall.showAllTabs').click; b.loading }
|
62
|
+
end
|
63
|
+
|
64
|
+
def tiny_buttons
|
65
|
+
action(:search) { |b| b.frm.button(title: 'search', value: 'search').click; b.loading }
|
66
|
+
action(:clear) { |b| b.frm.button(name: 'methodToCall.clearValues').click; b.loading }
|
67
|
+
action(:cancel_button) { |b| b.frm.link(title: 'cancel').click; b.loading }
|
68
|
+
action(:yes) { |b| b.frm.button(name: 'methodToCall.rejectYes').click; b.loading }
|
69
|
+
action(:no) {|b| b.frm.button(name: 'methodToCall.rejectNo').click; b.loading }
|
70
|
+
action(:add) { |b| b.frm.button(name: 'methodToCall.addNotificationRecipient.anchor').click; b.loading }
|
71
|
+
end
|
72
|
+
|
73
|
+
def search_results_table
|
74
|
+
element(:results_table) { |b| b.frm.table(id: 'row') }
|
75
|
+
|
76
|
+
action(:edit_item) { |match, p| p.results_table.row(text: /#{match}/m).link(text: 'edit').click; b.use_new_tab; b.close_parents }
|
77
|
+
alias_method :edit_person, :edit_item
|
78
|
+
|
79
|
+
action(:item_row) { |match, b| b.results_table.row(text: /#{match}/m) }
|
80
|
+
action(:open_item) { |match, b| b.item_row(match).link(text: /#{match}/).click; b.use_new_tab; b.close_parents }
|
81
|
+
action(:delete_item) { |match, p| p.item_row(match).link(text: 'delete').click; p.use_new_tab; p.close_parents }
|
82
|
+
|
83
|
+
action(:return_value) { |match, p| p.item_row(match).link(text: 'return value').click }
|
84
|
+
action(:return_random) { |b| b.return_value_links[rand(b.return_value_links.length)].click }
|
85
|
+
element(:return_value_links) { |b| b.results_table.links(text: 'return value') }
|
86
|
+
end
|
87
|
+
|
88
|
+
def budget_header_elements
|
89
|
+
action(:return_to_proposal) { |b| b.frm.button(name: 'methodToCall.returnToProposal').click; b.loading }
|
90
|
+
buttons 'Budget Version', 'Parameters', 'Rates', 'Summary', 'Personnel', 'Non-Personnel',
|
91
|
+
'Distribution & Income', 'Budget Actions'
|
92
|
+
# Need the _tab suffix because of method collisions
|
93
|
+
action(:modular_budget_tab) { |b| b.frm.button(value: 'Modular Budget').click }
|
94
|
+
end
|
95
|
+
|
96
|
+
def budget_versions_elements
|
97
|
+
element(:name) { |b| b.frm.text_field(name: 'newBudgetVersionName') }
|
98
|
+
action(:add) { |b| b.frm.button(name: 'methodToCall.addBudgetVersion').click }
|
99
|
+
action(:version) { |budget, p| p.budgetline(budget).td(class: 'tab-subhead', index: 2).text }
|
100
|
+
action(:direct_cost) { |budget, p| p.budgetline(budget).td(class: 'tab-subhead', index: 3).text }
|
101
|
+
action(:f_and_a) { |budget, p| p.budgetline(budget).td(class: 'tab-subhead', index: 4).text }
|
102
|
+
action(:total) { |budget, p| p.budgetline(budget).td(class: 'tab-subhead', index: 5).text }
|
103
|
+
# Called "budget status" to avoid method collision...
|
104
|
+
action(:budget_status) { |budget, p| p.budgetline(budget).select(title: 'Budget Status') }
|
105
|
+
action(:open) { |budget, p| p.budgetline(budget).button(alt: 'open budget').click }
|
106
|
+
action(:copy) { |budget, p| p.budgetline(budget).button(alt: 'copy budget').click }
|
107
|
+
action(:f_and_a_rate_type) { |budget, p| p.budget_table(budget)[0][3].text }
|
108
|
+
action(:cost_sharing) { |budget, p| p.budget_table(budget)[1][1].text }
|
109
|
+
action(:budget_last_updated) { |budget, p| p.budget_table(budget)[1][3].text }
|
110
|
+
action(:unrecovered_f_and_a) { |budget, p| p.budget_table(budget)[2][1].text }
|
111
|
+
action(:last_updated_by) { |budget, p| p.budget_table(budget)[2][3].text }
|
112
|
+
action(:comments) { |budget, p| p.budget_table(budget)[3][1].text }
|
113
|
+
|
114
|
+
private
|
115
|
+
element(:b_v_table) { |b| b.frm.table(id: 'budget-versions-table') }
|
116
|
+
action(:budgetline) { |budget, p| p.b_v_table.td(class: 'tab-subhead', text: budget).parent }
|
117
|
+
action(:budget_table) { |budget, p| p.b_v_table.tbodys[p.target_index(budget)].table }
|
118
|
+
action(:target_index) do |budget, p|
|
119
|
+
i=p.b_v_table.tbodys.find_index { |tbody| tbody.td(class: 'tab-subhead', index: 1).text==budget }
|
120
|
+
i+1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def special_review
|
125
|
+
element(:add_type) { |b| b.frm.select(id: 'specialReviewHelper.newSpecialReview.specialReviewTypeCode') }
|
126
|
+
element(:add_approval_status) { |b| b.frm.select(id: 'specialReviewHelper.newSpecialReview.approvalTypeCode') }
|
127
|
+
element(:add_protocol_number) { |b| b.frm.text_field(id: 'specialReviewHelper.newSpecialReview.protocolNumber') }
|
128
|
+
element(:add_application_date) { |b| b.frm.text_field(id: 'specialReviewHelper.newSpecialReview.applicationDate') }
|
129
|
+
element(:add_approval_date) { |b| b.frm.text_field(id: 'specialReviewHelper.newSpecialReview.approvalDate') }
|
130
|
+
element(:add_expiration_date) { |b| b.frm.text_field(id: 'specialReviewHelper.newSpecialReview.expirationDate') }
|
131
|
+
element(:add_exemption_number) { |b| b.frm.select(id: 'specialReviewHelper.newSpecialReview.exemptionTypeCodes') }
|
132
|
+
|
133
|
+
action(:add) { |b| b.frm.button(name: 'methodToCall.addSpecialReview.anchorSpecialReview').click }
|
134
|
+
|
135
|
+
element(:save_button) { |b| b.frm.button(name: 'methodToCall.save') }
|
136
|
+
end
|
137
|
+
|
138
|
+
def custom_data
|
139
|
+
element(:graduate_student_count) { |b| b.target_row('Graduate Student Count').text_field }
|
140
|
+
element(:billing_element) { |b| b.target_row('Billing Element').text_field }
|
141
|
+
element(:save_button) { |b| b.frm.button(name: 'methodToCall.save') }
|
142
|
+
element(:asdf_tab) { |b| b.frm.div(id: 'tab-asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf-div') }
|
143
|
+
action(:target_row) { |text, b| b.frm.trs(class: 'datatable').find { |row| row.text.include? text } }
|
144
|
+
end
|
145
|
+
|
146
|
+
def route_log
|
147
|
+
element(:route_log_iframe) { |b| b.frm.frame(name: 'routeLogIFrame') }
|
148
|
+
element(:actions_taken_table) { |b| b.route_log_iframe.div(id: 'tab-ActionsTaken-div').table }
|
149
|
+
value(:actions_taken) { |b| (b.actions_taken_table.rows.collect{ |row| row[1].text }.compact.uniq).reject{ |action| action==''} }
|
150
|
+
element(:pnd_act_req_table) { |b| b.route_log_iframe.div(id: 'tab-PendingActionRequests-div').table }
|
151
|
+
value(:action_requests) { |b| (b.pnd_act_req_table.rows.collect{ |row| row[1].text}).reject{ |action| action==''} }
|
152
|
+
action(:show_future_action_requests) { |b| b.route_log_iframe.h2(text: 'Future Action Requests').parent.parent.image(title: 'show').click }
|
153
|
+
element(:future_actions_table) { |b| b.route_log_iframe.div(id: 'tab-FutureActionRequests-div').table }
|
154
|
+
action(:requested_action_for) { |name, b| b.future_actions_table.tr(text: /#{name}/).td(index: 2).text }
|
155
|
+
end
|
156
|
+
|
157
|
+
# Gathers all errors on the page and puts them in an array called "errors"
|
158
|
+
def error_messages
|
159
|
+
element(:errors) do |b|
|
160
|
+
errs = []
|
161
|
+
b.left_errmsg_tabs.each do |div|
|
162
|
+
if div.div.div.exist?
|
163
|
+
errs << div.div.divs.collect{ |div| div.text }
|
164
|
+
elsif div.li.exist?
|
165
|
+
errs << div.lis.collect{ |li| li.text }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
b.left_errmsg.each do |div|
|
169
|
+
if div.div.div.exist?
|
170
|
+
errs << div.div.divs.collect{ |div| div.text }
|
171
|
+
elsif div.li.exist?
|
172
|
+
errs << div.lis.collect{ |li| li.text }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
errs.flatten
|
176
|
+
end
|
177
|
+
element(:left_errmsg_tabs) { |b| b.frm.divs(class: 'left-errmsg-tab') }
|
178
|
+
element(:left_errmsg) { |b| b.frm.divs(class: 'left-errmsg') }
|
179
|
+
element(:error_messages_div) { |b| b.frm.div(class: 'error') }
|
180
|
+
end
|
181
|
+
|
182
|
+
def validation_elements
|
183
|
+
element(:validation_button) { |b| b.frm.button(name: 'methodToCall.activate') }
|
184
|
+
action(:show_data_validation) { |b| b.frm.button(id: 'tab-DataValidation-imageToggle').click; b.validation_button.wait_until_present }
|
185
|
+
action(:turn_on_validation) { |b| b.validation_button.click; b.special_review_button.wait_until_present }
|
186
|
+
element(:validation_errors_and_warnings) { |b| errs = []; b.validation_err_war_fields.each { |field| errs << field.html[/(?<=>).*(?=<)/] }; errs }
|
187
|
+
element(:validation_err_war_fields) { |b| b.frm.tds(width: '94%') }
|
31
188
|
end
|
32
|
-
|
33
|
-
end
|
34
189
|
|
35
|
-
|
190
|
+
def links(*links_text)
|
191
|
+
links_text.each { |link| elementate(:link, link) }
|
192
|
+
end
|
193
|
+
|
194
|
+
def buttons(*buttons_text)
|
195
|
+
buttons_text.each { |button| elementate(:button, button) }
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
# A helper method that converts the passed string into snake case. See the StringFactory
|
201
|
+
# module for more info.
|
202
|
+
#
|
203
|
+
def damballa(text)
|
204
|
+
StringFactory::damballa(text)
|
205
|
+
end
|
206
|
+
|
207
|
+
def elementate(type, text)
|
208
|
+
identifiers={:link=>:text, :button=>:value}
|
209
|
+
el_name=damballa("#{text}_#{type}")
|
210
|
+
act_name=damballa(text)
|
211
|
+
element(el_name) { |b| b.frm.send(type, identifiers[type]=>text) }
|
212
|
+
action(act_name) { |b| b.frm.send(type, identifiers[type]=>text).click }
|
213
|
+
end
|
214
|
+
|
215
|
+
end # self
|
216
|
+
|
217
|
+
end # BasePage
|