sakai-oae-test-api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,168 @@
1
+ # This file contains custom Utility methods used throughout the Sakai-OAE test scripts,
2
+ # as well as Monkey patches to the Page Object and Watir-webdriver gems.
3
+ #
4
+
5
+ # Contains methods that extend the PageObject module to include
6
+ # methods that are custom to OAE.
7
+ module PageObject
8
+
9
+ # Monkey patch helper method for links to named objects...
10
+ def name_link(name)
11
+ self.link(:text=>/#{Regexp.escape(name)}/i)
12
+ end
13
+
14
+ # Monkey patch helper method for li elements referring to
15
+ # named items...
16
+ def name_li(name)
17
+ self.li(:text=>/#{Regexp.escape(name)}/i)
18
+ end
19
+
20
+ def method_missing(sym, *args, &block)
21
+ @browser.send sym, *args, &block
22
+ end
23
+
24
+ module Accessors
25
+
26
+ # Use this for menus that require floating the mouse
27
+ # over the first link, before you click on the second
28
+ # link...
29
+ def float_menu(name, menu_text, link_text, target_class)
30
+ define_method(name) {
31
+ self.back_to_top
32
+ self.link(:text=>menu_text).fire_event("onmouseover")
33
+ self.link(:text=>/#{link_text}/).click
34
+ self.linger_for_ajax(10)
35
+ eval(target_class).new @browser
36
+ }
37
+ end
38
+
39
+ def open_link(name, klass)
40
+ define_method("open_#{name}") do |value|
41
+ self.back_to_top
42
+ self.link(:text=>/#{value}/).click
43
+ sleep 2
44
+ self.linger_for_ajax(6)
45
+ # This code is necessary because of a null style tag
46
+ # that confuses Watir into thinking the buttons aren't really there.
47
+ if self.div(:id=>"joinrequestbuttons_widget").exist?
48
+ @browser.execute_script("$('#joinrequestbuttons_widget').css({display: 'block'})")
49
+ end
50
+ eval(klass).new @browser
51
+ end
52
+ end
53
+
54
+ # Use this for menu items that are accessed via clicking
55
+ # the div to get to the target menu item.
56
+ def permissions_menu(name, text) #, target_class)
57
+ define_method(name) {
58
+ self.link(:text=>text).fire_event("onmouseover")
59
+ self.link(:text=>text).parent.div(:class=>"lhnavigation_selected_submenu_image").fire_event("onclick")
60
+ self.link(:id=>"lhnavigation_submenu_user_permissions").click
61
+ self.class.class_eval { include ProfilePermissionsPopUp }
62
+ }
63
+ end
64
+
65
+ # Use this for button objects that cause page navigations and thus
66
+ # require a new Page Object.
67
+ def navigating_button(name, id, class_name=nil)
68
+ define_method(name) {
69
+ self.button(:id=>id).click
70
+ sleep 0.2
71
+ browser.wait_for_ajax(2)
72
+ sleep 0.2
73
+ unless class_name==nil
74
+ eval(class_name).new @browser
75
+ end
76
+ }
77
+ end
78
+
79
+ # Use this for links on the page that cause page navigations and
80
+ # thus require a new Page Object.
81
+ def navigating_link(name, link_text, class_name=nil)
82
+ define_method(name) {
83
+ self.link(:text=>/#{Regexp.escape(link_text)}/).click
84
+ sleep 2 # wait_for_ajax keeps throwing unknown JS errors in Selenium-webdriver
85
+ unless class_name==nil
86
+ eval(class_name).new @browser
87
+ end
88
+ }
89
+ end
90
+
91
+ # This method is specifically for defining the contents of
92
+ # the Insert button, found on the Document Edit page. See the module
93
+ # DocumentWidget
94
+ def insert_button(name, id, module_name=nil)
95
+ define_method("insert_#{name}") {
96
+ self.button(:id=>"sakaidocs_insert_dropdown_button").click
97
+ sleep 0.1
98
+ self.button(:id=>id).click
99
+ unless module_name==nil
100
+ self.class.class_eval { include eval(module_name) }
101
+ sleep 0.4
102
+ end
103
+ self.wait_for_ajax(2)
104
+ }
105
+ end
106
+
107
+ end
108
+ end
109
+
110
+ # Extension of the Utilities module.
111
+ # This is included here because it contains methods
112
+ # specific to OAE testing only.
113
+ module Utilities
114
+
115
+ # Returns an array containing a randomized list of the specified
116
+ # number of OAE Category strings (The array returned is one string long if no
117
+ # number is specified).
118
+ def random_categories(number=1)
119
+ categories = ["Agriculture", "Animal Sciences", "Food Sciences", "Plant Sciences", "Soil Sciences", "Architecture & Planning", "City, Urban, Community & Regional Planning", "Environmental Design", "Interior Architecture", "Landscape Architecture", "Real Estate Development", "Arts & Music", "Acting", "Art History, Criticism & Conservation", "Arts Management", "Arts, Entertainment, & Media Management", "Ballet", "Brass Instruments", "Ceramic Arts & Ceramics", "Cinematography & Film/Video Production", "Commercial & Advertising Art", "Commercial Photography", "Conducting", "Costume Design", "Crafts/Craft Design, Folk Art & Artisanry", "Dance", "Design & Applied Arts", "Digital Arts", "Directing & Theatrical Production", "Documentary Production", "Drama/Theatre Arts & Stagecraft", "Drawing", "Fashion/Apparel Design", "Fiber, Textile & Weaving Arts", "Film/Cinema/Video Studies", "Fine & Studio Arts Management", "Game & Interactive Media Design", "Graphic Design", "Illustration", "Industrial & Product Design", "Interior Design", "Intermedia/Multimedia", "Jazz/Jazz Studies", "Keyboard Instruments", "Metal & Jewelry Arts", "Music", "Music History, Literature, & Theory", "Music Management & Merchandising", "Music Pedagogy", "Music Performance", "Music Technology", "Music Theory & Composition", "Musical Theatre", "Musicology & Ethnomusicology", "Painting", "Percussion Instruments", "Photography", "Playwriting & Screenwriting", "Printmaking", "Sculpture", "Stringed Instruments", "Technical Theatre/Theatre Design & Technology", "Theatre Literature, History & Criticism", "Theatre/Theatre Arts Management", "Voice & Opera", "Woodwind Instruments", "Business", "Accounting & Related Services", "Business Administration, Management & Operations", "Business Operations Support & Assistant Services", "Busines & Corporate Communications", "Business & Managerial Economics", "Construction Management", "Entrepreneurial & Small Business Operations", "Finance & Financial Management Services", "Hospitality Administration & Management", "Human Resources Management & Services", "Insurance", "International Business", "Management Information Systems & Services", "Management Sciences & Quantitative Methods", "Marketing", "Real Estate", "Sales & Merchandising", "Taxation", "Telecommunications Management", "Communications", "Animation, Interactive Technology, Video Graphics & Special Effects", "Graphic Communications", "Journalism", "Photographic & Film/Video Technology", "Public Relations & Advertising", "Publishing", "Radio & Television Broadcasting Technology", "Recording Arts Technology", "Computers & Information", "Archives/Archival Administration", "Artificial Intelligence", "Children & Youth Library Services", "Computer Programming", "Computer Science", "Human Computer Interaction", "Informatics", "Information Science/Studies", "Information Technology", "Library & Information Science", "Library Science & Administration", "Education", "Adult & Continuing Education", "Bilingual, Multilingual, & Multicultural Education", "Curriculum & Instruction", "Developmental & Remedial English", "Developmental & Remedial Mathematics", "Early Childhood Education", "Educational Administration & Supervision", "Educational Assessment, Evaluation, & Research", "Educational & Instructional Media Design", "Elementary Education", "High School & Secondary Diploma Programs", "International & Comparative Education", "Outdoor Education", "Second Language Learning", "Secondary Education", "Special Education", "Student Counseling & Personnel Services", "Vocational Education", "Engineering & Technology", "Aerospace, Aeronautical & Astronautical Engineering", "Agricultural Engineering", "Architectural Engineering", "Biomedical & Medical Engineering", "Ceramic Sciences & Engineering", "Chemical Engineering", "Civil Engineering", "Computer Engineering", "Electrical, Electronics & Communications Engineering", "Environmental & Environmental Health Engineering", "Geological & Geophysical Engineering", "Industrial Engineering", "Materials Science", "Mechanical Engineering", "Mining & Mineral Engineering", "Nanotechnology", "Naval Architecture & Marine Engineering", "Nuclear Engineering", "Systems Engineering", "History", "American History", "Asian History", "Canadian History", "European History", "Military History", "Science & Technology History", "Languages & Literatures", "African Languages & Literatures", "Albanian Language & Literature", "American Literature", "American Sign Language", "Ancient Near Eastern & Biblical Languages & Literatures", "Ancient/Classical Greek Language & Literature", "Arabic Language & Literature", "Australian, Oceanic, & Pacific Languages & Literatures", "Baltic Languages & Literatures", "Bengali Language & Literature", "Bosnian, Serbian, & Croatian Languages & Literatures", "Bulgarian Language & Literature", "Burmese Language & Literature", "Catalan Language & Literature", "Celtic Languages & Literatures", "Children's & Adolescent Literature", "Chinese Language & Literature", "Classics & Classical Languages & Literatures", "Comparative Literature", "Creative Writing", "Czech Language & Literature", "Danish Language & Literature", "Dutch/Flemish Language & Literature", "East Asian Languages & Literatures", "English Composition", "English Literature", "Filipino/Tagalog Language & Literature", "French Language & Literature", "Germanic Languages & Literatures", "Hebrew Language & Literature", "Hindi Language & Literature", "Hungarian & Magyar Language & Literature", "Indonesian & Malay Languages & Literatures", "Iranian & Persian Languages & Literatures", "Italian Language & Literature", "Japanese Language & Literature", "Khmer & Cambodian Language & Literature", "Korean Language & Literature", "Lao Language & Literature", "Latin Language & Literature", "Linguistics", "Literature", "Middle & Near Eastern & Semitic Languages & Literatures", "Modern Greek Language & Literature", "Mongolian Language & Literature", "Native American Languages & Literatures", "Norwegian Language & Literature", "Polish Language & Literature", "Portuguese Language & Literature", "Punjabi Language & Literature", "Romance Languages & Literatures", "Romanian Language & Literature", "Russian Language & Literature", "Sanskrit & Classical Indian Languages & Literatures", "Scandinavian Languages & Literatures", "Slavic Languages & Literatures", "Slovak Language & Literature", "South Asian Languages & Literatures", "Southeast Asian, Australasian, & Pacific Languages & Literatures", "Spanish Language & Literature", "Speech & Rhetorical Studies", "Swedish Language & Literature", "Tamil Language & Literature", "Technical & Business Writing", "Thai Language & Literature", "Tibetan Language & Literature", "Turkic, Uralic-Altaic, Caucasian, & Central Asian Languages & Literatures", "Turkish Language & Literature", "Ukrainian Language & Literature", "Uralic Languages & Literatures", "Urdu Language & Literature", "Vietnamese Language & Literature", "Law", "Banking, Corporate, Finance, & Securities Law", "Comparative Law", "Energy, Environment, & Natural Resources Law", "Health Law", "Intellectual Property Law", "International Law & Legal Studies", "Law", "Legal Support Services", "Pre-Law Studies", "Tax Law & Taxation", "Lifelong Learning", "Addiction Prevention & Treatment", "Adult & Continuing Education", "Aircraft Piloting", "Art", "Basic Computer Skills", "Birthing & Parenting", "Board, Card & Role-Playing Games", "Career Exploration", "Collecting", "Computer Games & Programming Skills", "Cooking & Other Domestic Skills", "Dancing", "Handicrafts & Model-Making", "Home Maintenance & Improvement", "Music", "Nature Appreciation", "Personal Health", "Pets", "Reading", "Sports & Exercise", "Theater", "Travel", "Workforce Development & Training", "Writing", "Math", "Algebra & Number Theory", "Applied Mathematics", "Geometry & Geometric Analysis", "Statistics", "Topology & Foundations", "Medicine & Health", "Alternative & Complementary Medicine", "Bioethics & Medical Ethics", "Chiropractic", "Clinical & Medical Laboratory Science & Research", "Communication Disorders Sciences", "Dentistry", "Dietetics & Clinical Nutrition", "Energy & Biologically Based Therapies", "Health & Medical Administration", "Health & Medical Assistance", "Health & Medical Preparatory Programs", "Health Diagnostics, Intervention & Treatment", "Health, Physical Education & Fitness", "Medical Illustration & Informatics", "Medicine", "Mental & Social Health", "Movement & Mind-Body Therapies", "Nursing", "Ophthalmics & Optometrics", "Optometry", "Osteopathic Medicine", "Pharmacy & Pharmaceutical Sciences", "Podiatric Medicine", "Public Health", "Rehabilitation & Therapeutics", "Somatic Bodywork", "Veterinary Medicine", "Military & Security", "Air Force ROTC, Air Science & Operations", "Army ROTC, Military Science & Operations", "Corrections", "Criminal Justice", "Crisis, Emergency & Disaster Management", "Critical Incident Response & Special Police Operations", "Critical Infrastructure Protection", "Cyber & Computer Forensics & Counterterrorism", "Financial Forensics & Fraud Investigation", "Fire Protection", "Fire Science & Fire-fighting", "Fire Systems Technology", "Fire & Arson Investigation & Prevention", "Forensic Science & Technology", "Homeland Security", "Intelligence, Command Control & Information Operations", "Maritime Law Enforcement", "Military Economics & Management", "Military Science & Operational Studies", "Military Technologies", "Navy & Marine ROTC, Naval Science & Operations", "Protective Services Operations", "Security Policy & Strategy", "Terrorism & Counterterrorism Operations", "Wildland & Forest Firefighting & Investigation", "Multidisciplinary Studies", "African Studies", "African-American & Black Studies", "American Studies & Civilization", "Asian Studies & Civilization", "Asian-American Studies", "Balkans Studies", "Baltic Studies", "Canadian Studies", "Caribbean Studies", "Chinese Studies", "Classical & Ancient Studies", "Commonwealth Studies", "Cultural Studies, Critical Theory & Analysis", "Deaf Studies", "Disability Studies", "East Asian Studies", "European Studies & Civilization", "Folklore Studies", "French Studies", "Gay & Lesbian Studies", "General Studies", "German Studies", "Hispanic-American, Puerto Rican, & Mexican-American/Chicano Studies", "Historic Preservation & Conservation", "Holocaust & Related Studies", "Housing & Human Environments", "Humanities & Humanistic Studies", "Intercultural, Multicultural & Diversity Studies", "International & Global Studies", "Irish Studies", "Italian Studies", "Japanese Studies", "Korean Studies", "Latin American & Caribbean Studies", "Medieval & Renaissance Studies", "Museology & Museum Studies", "Native American Studies", "Near & Middle Eastern Studies", "Pacific Area & Pacific Rim Studies", "Peace Studies & Conflict Resolution", "Polish Studies", "Russian, Central European, East European & Eurasian Studies", "Scandinavian Studies", "Slavic Studies", "South Asian Studies", "Southeast Asian Studies", "Spanish & Iberian Studies", "Sustainability Studies", "Systems Science & Theory", "Tibetan Studies", "Ukraine Studies", "Ural-Altaic & Central Asian Studies", "Western European Studies", "Women's Studies", "Work & Family Studies", "Natural & Physical Sciences", "Astronomy", "Astrophysics", "Atmospheric Sciences & Meteorology", "Atomic & Molecular Physics", "Biochemistry, Biophysics & Molecular Biology", "Biology", "Biomathematics, Bioinformatics, & Computational Biology", "Biotechnology", "Botany & Plant Biology", "Cellular Biology & Anatomical Sciences", "Chemistry", "Cognitive Science", "Ecology", "Ecology, Evolution, Systematics, & Population Biology", "Genetics", "Geochemistry & Petrology", "Geological & Earth Sciences", "Geophysics & Seismology", "Hydrology & Water Resources Science", "Marine Biology & Biological Oceanography", "Materials Sciences", "Microbiological Sciences & Immunology", "Neurobiology & Neurosciences", "Neuroscience", "Nuclear Physics", "Oceanography", "Optics", "Paleontology", "Pharmacology & Toxicology", "Physics", "Physiology, Pathology & Related Sciences", "Zoology & Animal Biology", "Philosophy & Religion", "Bible & Biblical Studies", "Buddhist Studies", "Christian Studies", "Divinity & Ministry", "Ethics", "Hindu Studies", "Islamic Studies", "Jewish & Judaic Studies", "Lay Ministry", "Logic", "Missionary Studies & Missiology", "Pastoral Counseling & Specialized Ministries", "Philosophy", "Rabbinical Studies", "Religion & Religious Studies", "Religious Education", "Religious & Sacred Music", "Talmudic Studies", "Theological & Ministerial Studies", "Urban Ministry", "Women's Ministry", "Youth Ministry", "Public Policy & Administration", "Community Organization & Advocacy", "Education Policy Analysis", "Fishing & Fisheries", "Forestry", "Health Policy Analysis", "Human Services", "International Policy Analysis", "Public Administration", "Public Policy Analysis", "Social Work", "Wildlife & Wildlands", "Youth Services", "Social Sciences", "Anthropology", "Archeology", "Criminology", "Demography & Population Studies", "Economics", "Geography & Cartography", "International Relations & National Security Studies", "Political Science & Government", "Psychology", "Sociology", "Urban Studies & Affairs", "Trades & Professions", "Air Traffic Control", "Aircraft Powerplant Technology", "Airframe Mechanics & Aircraft Maintenance & Repair", "Airline Flight Attendant", "Airline Pilot & Flight Crew", "Alternative Fuel Vehicle Technology", "Apparel & Textiles", "Automotive Mechanics", "Aviation Management & Operations", "Avionics Maintenance & Repair", "Bicycle Mechanics & Repair", "Blasting & Blaster", "Boilermaking & Boilermaker", "Building & Construction Finishing, Management, & Inspection", "Building & Construction Site Management", "Building, Home & Construction Inspection", "Building & Property Maintenance", "Cabinetmaking & Millwork", "Carpenters", "Carpet, Floor & Tile Worker", "Commercial Fishing", "Computer Numerically Controlled Machinist", "Concrete Finishing", "Construction, Heavy Equipment & Earthmoving Equipment Operation", "Cosmetology", "Culinary Arts", "Diesel Mechanics", "Diver, Professional & Instructor", "Drywall Installation", "Electrical & Electronics Maintenance & Repair", "Electrician", "Engine Machinist", "Flagging & Traffic Control", "Flight Instruction", "Foods, Nutrition & Related Services", "Funeral Service & Mortuary Science", "Furniture Design & Manufacturing", "Glazing", "Gunsmithing", "Heating, Air Conditioning, Ventilation & Refrigeration Maintenance & Repair", "Heavy & Industrial Equipment Maintenance & Repair", "High Performance & Custom Engine Mechanics", "Insulator", "Ironworking", "Locksmithing & Safe Repair", "Machine Tool Technology & Machinist", "Marine Maintenance, Fitter & Ship Repair", "Marine Science & Merchant Marine Officer", "Marine Transportation", "Masonry", "Medium & Heavy Vehicle & Truck Technology", "Metal Building Assembly", "Metal Fabricator", "Mobile Crane Operation", "Motorcycle Maintenance & Repair", "Musical Instrument Fabrication & Repair", "Painting & Wall Covering", "Pipefitting & Sprinkler Fitting", "Plumbing", "Railroad & Railway Transportation", "Recreation Vehicle (RV) Service", "Roofing", "Sheet Metal Technology & Sheetworking", "Shoe, Boot & Leather Repair", "Small Engine Mechanics", "Tool & Die Technology", "Truck/Bus Driver & Commercial Vehicle Operator & Instructor", "Upholstery", "Watchmaking & Jewelrymaking", "Welding", "Well Drilling", "Woodworking"]
120
+ list = []
121
+ number.times do
122
+ categories.shuffle!
123
+ list << categories.pop
124
+ end
125
+ return list
126
+ end
127
+
128
+ end
129
+
130
+ # Need this to extend Watir to be able to attach to Sakai's non-standard tags...
131
+ module Watir
132
+
133
+ class Browser
134
+
135
+ def linger_for_ajax(timeout=5)
136
+ end_time = ::Time.now + timeout
137
+ while self.execute_script("return jQuery.active") > 0
138
+ sleep 0.2
139
+ break if ::Time.now > end_time
140
+ end
141
+ self.wait(timeout + 10)
142
+ end
143
+
144
+ def back_to_top
145
+ self.execute_script("javascript:window.scrollTo(0,0)")
146
+ end
147
+
148
+ alias return_to_top back_to_top
149
+ alias scroll_to_top back_to_top
150
+
151
+ end
152
+
153
+ class Element
154
+ # attaches to the "headers" tags inside of the assignments table.
155
+ def headers
156
+ @how = :ole_object
157
+ return @o.headers
158
+ end
159
+
160
+ # attaches to the "for" tags in "label" tags.
161
+ def for
162
+ @how = :ole_object
163
+ return @o.for
164
+ end
165
+
166
+ end
167
+ end
168
+
@@ -0,0 +1,109 @@
1
+ # coding: UTF-8
2
+
3
+ # This module contains methods that will be almost
4
+ # universally useful, and don't pertain to any specific
5
+ # area or widget or pop up on a given page.
6
+ #
7
+ # The primary method provided in this module is of the type
8
+ # .open_<page>('target_text'). Depending on the type of <page>
9
+ # specified, the method will return the appropriate Class object.
10
+ module GlobalMethods
11
+
12
+ include PageObject
13
+
14
+ # These create methods of the form
15
+ # open_<name>('target link text')
16
+ # Example usage in a script on a page where there's a calendar link
17
+ # with the text "Moon Phases" ...
18
+ #
19
+ # calendar_page = current_page.open_calendar("Moon Phases")
20
+ #
21
+ # Once the link is clicked, the method
22
+ # returns the specified class object.
23
+ #
24
+ # It's important to note that the methods created
25
+ # assume that the target page will be opened in the
26
+ # same tab. If a new tab is opened, these methods will not work.
27
+ #
28
+ # In addition, the page classes that are instantiated for the
29
+ # target pages assume that the user has editing
30
+ # rights.
31
+ open_link(:document, "ContentDetailsPage")
32
+ open_link(:content, "ContentDetailsPage")
33
+ open_link(:remote_content, "Remote")
34
+ open_link(:library, "Library")
35
+ open_link(:participants, "Participants")
36
+ open_link(:discussions, "Discussions")
37
+ open_link(:tests_and_quizzes, "Tests")
38
+ open_link(:assessments, "Tests")
39
+ open_link(:calendar, "Calendar")
40
+ open_link(:map, "GoogleMaps")
41
+ open_link(:file, "Files")
42
+ open_link(:forum, "Forum")
43
+ open_link(:comments, "Comments")
44
+ open_link(:jisc, "JISC")
45
+ open_link(:assignments, "Assignments")
46
+ open_link(:feed, "RSS")
47
+ open_link(:rss_feed, "RSS")
48
+ open_link(:rss, "RSS")
49
+ open_link(:lti, "LTI")
50
+ open_link(:basic_lti, "LTI")
51
+ open_link(:gadget, "Gadget")
52
+ open_link(:gradebook, "Gradebook")
53
+
54
+ alias open_group open_library
55
+ alias open_course open_library
56
+ alias go_to open_library
57
+
58
+ # The "gritter" notification that appears to confirm
59
+ # when something has happened (like updating a user profile
60
+ # or sending a message).
61
+ div(:notification, :class=>"gritter-with-image")
62
+
63
+ # This method is essentially
64
+ # identical with the
65
+ # open_link methods listed above.
66
+ # It opens page/document items that are listed on the page--for example
67
+ # in the Recent activity box.
68
+ # There is an important distinction, however:
69
+ # This method should be used in cases when clicking the
70
+ # link results in a new browser tab/window being generated.
71
+ # May ultimately convert this to open_public_<object>
72
+ # if that seems the best generic way to handle things.
73
+ #
74
+ # THIS METHOD MAY SOON BE DEPRECATED (if so, it
75
+ # will be included as an alias of open_content)
76
+ def open_page(name)
77
+ name_link(name).click
78
+ self.wait_for_ajax
79
+ self.window(:title=>"rSmart | Content Profile").use
80
+ ContentDetailsPage.new @browser
81
+ end
82
+
83
+ # Clicks the link of the specified name (It will click any link on the page,
84
+ # really, but it should be used for Person links only, because it
85
+ # instantiates the ViewPerson Class)
86
+ def view_person(name)
87
+ name_link(name).click
88
+ sleep 3
89
+ self.wait_for_ajax
90
+ ViewPerson.new @browser
91
+ end
92
+ alias view_profile view_person
93
+
94
+ # Clicks the X in the Notification box, to dismiss it.
95
+ def close_notification
96
+ self.div(:class=>"gritter-item").hover
97
+ self.div(:class=>"gritter-close").fire_event "onclick"
98
+ end
99
+
100
+ # This method exposes the "draggable" menu items in the left-hand
101
+ # menus, so that you can use Watir's <X>.drag_and_drop_on <Y> method.
102
+ # Note that not all matching menu items are necessarily draggable.
103
+ # Also useful if you just want to see if the menu item is visible
104
+ # on the page
105
+ def menu_item(name)
106
+ self.div(:class=>/lhnavigation(_subnav|)_item_content/, :text=>name)
107
+ end
108
+
109
+ end # GlobalMethods
@@ -0,0 +1,1949 @@
1
+ # coding: UTF-8
2
+ require 'sakai-OAE/gem_extensions'
3
+ require 'sakai-OAE/global_methods'
4
+ require 'sakai-OAE/toolbars_and_menus'
5
+ require 'sakai-OAE/pop_up_dialogs'
6
+ require 'sakai-OAE/widgets'
7
+ require 'sakai-OAE/cle_frame_classes'
8
+
9
+ # The Login page for OAE.
10
+ class LoginPage
11
+
12
+ include PageObject
13
+ include HeaderFooterBar
14
+ include GlobalMethods
15
+
16
+ # Page Objects
17
+ div(:expand_categories, :class=>"categories_expand")
18
+
19
+ # Custom Methods...
20
+
21
+ # Returns an array containing the titles of the items
22
+ # displayed in the "Recent activity" box on the login page.
23
+ def recent_activity_list
24
+ list = []
25
+ self.div(:id=>"recentactivity_activity_container").links(:class=>"recentactivity_activity_item_title recentactivity_activity_item_text s3d-regular-links s3d-bold").each do |link|
26
+ list << link.text
27
+ end
28
+ return list.uniq!
29
+ end
30
+
31
+ # Returns an array containing the titles of the items in the
32
+ # Featured Content area of the page.
33
+ def featured_content_list
34
+ list = []
35
+ self.div(:id=>"featuredcontent_content_container").links(:class=>/featuredcontent_content_title/).each do |link|
36
+ list << link.text
37
+ end
38
+ return list
39
+ end
40
+
41
+ end
42
+
43
+ # Page Objects and Methods for the "All Categories" page.
44
+ # Note that this page is distinct from the "Search => All types"
45
+ # page.
46
+ class AllCategoriesPage
47
+
48
+ include PageObject
49
+ include HeaderFooterBar
50
+ include GlobalMethods
51
+
52
+ # Page Objects
53
+ div(:page_title, :class=>"s3d-contentpage-title")
54
+
55
+ # Custom Methods...
56
+
57
+ end
58
+
59
+ # TODO - Write a class description
60
+ class Calendar
61
+
62
+ include PageObject
63
+ include HeaderFooterBar
64
+ include LeftMenuBar
65
+ include HeaderBar
66
+ include DocButtons
67
+ include GlobalMethods
68
+
69
+ def calendar_frame
70
+ self.frame(:src=>/sakai2calendar.launch.html/)
71
+ end
72
+
73
+ end
74
+
75
+ # Methods related to the Content Details page.
76
+ class ContentDetailsPage
77
+
78
+ include PageObject
79
+ include GlobalMethods
80
+ include HeaderFooterBar
81
+ include DocButtons
82
+ include LeftMenuBar
83
+
84
+ # Page Objects
85
+ text_area(:comment_text_area, :id=>"contentcomments_txtMessage")
86
+ button(:comment_button, :text=>"Comment")
87
+ button(:see_more, :id=>"contentmetadata_show_more")
88
+ button(:see_less, :id=>"contentmetadata_show_more")
89
+ button(:permissions_menu_button, :id=>"entity_content_permissions")
90
+ button(:permissions_button, :text=>"Permissions")
91
+ button(:delete_button, :text=>"Delete")
92
+ button(:add_to_button, :id=>"entity_content_save")
93
+ button(:share_button, :id=>"entity_content_share")
94
+
95
+ span(:name, :id=>"entity_name")
96
+ span(:type, :id=>"entity_type")
97
+
98
+ # Custom Methods...
99
+
100
+ # Returns the text of the description display span.
101
+ def description
102
+ self.span(:id=>"contentmetadata_description_display").text
103
+ end
104
+
105
+ # Enters the specified text into the description text area box.
106
+ # Note that this method first fires off the edit_description method
107
+ # because the description text area is not present by default.
108
+ def description=(text)
109
+ edit_description
110
+ self.text_area(:id=>"contentmetadata_description_description").set text
111
+ end
112
+
113
+ # Header row items...
114
+ def update_name=(new_name)
115
+ name_element.click
116
+ self.text_field(:id=>"entity_name_text").set new_name + "\n"
117
+ end
118
+
119
+ # Visibility...
120
+ def change_visibility_private
121
+ # TODO - Write method
122
+ end
123
+
124
+ def change_visibility_logged_in
125
+ # TODO - Write method
126
+ end
127
+
128
+ def change_visibility_public
129
+ # TODO - Write method
130
+ end
131
+
132
+ # Collaboration...
133
+
134
+ #
135
+ def view_collaborators
136
+ # TODO - Write method
137
+ end
138
+
139
+ #
140
+ def change_collaborators
141
+ # TODO - Write method
142
+ end
143
+
144
+ # This method is currently not working TODO - Fix method
145
+ #def change_sharing
146
+ # self.div(:class=>"entity_owns_actions_share has_counts ew_permissions").hover
147
+ # self.div(:class=>"entity_owns_actions_share has_counts ew_permissions").click
148
+ # self.execute_script(%|$('.entity_owns_actions_share.has_counts.ew_permissions').trigger("mouseover");|)
149
+ # wait_for_ajax
150
+ # self.div(:class=>"entity_owns_actions_share has_counts ew_permissions").button(:class=>"s3d-link-button ew_permissions").click
151
+ # self.class.class_eval { include ContentPermissionsPopUp }
152
+ #end
153
+
154
+ def collaboration_share
155
+ self.div(:id=>"entity_actions").button(:text=>"Share").click
156
+ self.wait_until { self.text.include? "Who do you want to share with?" }
157
+ self.class.class_eval { include ShareWithPopUp }
158
+ end
159
+
160
+ # Comments count...
161
+
162
+ # Comments field...
163
+
164
+ # Clicks the Comments button
165
+ def comment
166
+ comment_button
167
+ self.wait_for_ajax(2)
168
+ end
169
+
170
+ # Enters the specified text string into the Comment box.
171
+ def comment_text(text)
172
+ comment_text_area_element.click
173
+ comment_text_area_element.send_keys text
174
+ end
175
+ alias comment_text= comment_text
176
+
177
+ # Clicks the "Add to..." button
178
+ def add_to
179
+ add_to_button
180
+ self.wait_for_ajax(2)
181
+ end
182
+
183
+ # Clicks "Permissions" in the menu...
184
+ def permissions
185
+ permissions_menu_button
186
+ self.wait_for_ajax(2)
187
+ permissions_button
188
+ self.wait_for_ajax(2)
189
+ self.class.class_eval { include ContentPermissionsPopUp }
190
+ end
191
+
192
+ # Returns an array object containing the items displayed in
193
+ # "Related Content" on the page.
194
+ def related_content
195
+ list = []
196
+ self.div(:class=>"relatedcontent_list").links.each do |link|
197
+ list << link.title
198
+ end
199
+ return list
200
+ end
201
+
202
+ # Clicks on the "Share Content" button.
203
+ def share_content
204
+ self.div(:id=>"entity_actions").span(:class=>"entity_share_content").click
205
+ self.wait_until { @browser.text.include? "Who do you want to share with?" }
206
+ self.class.class_eval { include ShareWithPopUp }
207
+ end
208
+
209
+ # Clicks on the "Add to library" button.
210
+ def add_to_library
211
+ self.button(:text=>"Add to library").click
212
+ self.wait_until { self.text.include? "Save to" }
213
+ self.class.class_eval { include SaveContentPopUp }
214
+ end
215
+
216
+ # Opens the description text field for editing.
217
+ def edit_description
218
+ self.div(:id=>"contentmetadata_description_container").fire_event "onmouseover"
219
+ self.div(:id=>"contentmetadata_description_container").fire_event "onclick"
220
+ end
221
+
222
+ # Opens the tag field for editing.
223
+ def edit_tags
224
+ self.div(:id=>"contentmetadata_tags_container").fire_event "onclick"
225
+ end
226
+
227
+ # Opens the Copyright field for editing.
228
+ def edit_copyright
229
+ self.div(:id=>"contentmetadata_copyright_container").fire_event "onclick"
230
+ end
231
+
232
+ # Opens the Categories field for editing.
233
+ def edit_categories
234
+ self.div(:id=>"contentmetadata_locations_container").fire_event "onclick"
235
+ self.class.class_eval { include AddRemoveCategories }
236
+ end
237
+
238
+ # Returns an array containing the tags and categories listed on the page.
239
+ def tags_and_categories_list
240
+ list =[]
241
+ self.div(:id=>"contentmetadata_tags_container").links.each do |link|
242
+ list << link.text
243
+ end
244
+ return list
245
+ end
246
+
247
+ # The "share" button next to the Download button.
248
+ def share_with_others
249
+ self.share_button
250
+ self.wait_for_ajax
251
+ self.class.class_eval { include ShareWithPopUp }
252
+ end
253
+
254
+ # Comments List Stuff
255
+
256
+ # This method grabs all of the information about the last listed comment and
257
+ # returns it in a hash object. Relevant strings are put into the following
258
+ # keys: :poster, :date, :message. The delete button element is defined in the
259
+ # :delete_button key. Note that you'll have to use Watir's .click method to click
260
+ # the button.
261
+ def last_comment
262
+ hash = {}
263
+ comments_table = self.div(:class=>"contentcommentsTable")
264
+ last_message = comments_table.div(:class=>"contentcomments_comment last")
265
+ hash.store(:poster, last_message.span(:class=>"contentcomments_posterDataName s3d-regular-links").link.text)
266
+ hash.store(:date, last_message.span(:class=>"contentcomments_dateComment").text)
267
+ hash.store(:message, last_message.div(:class=>"contentcomments_message").text)
268
+ hash.store(:delete_button, last_message.button(:id=>/contentcomments_delete_\d+/))
269
+ return hash
270
+ end
271
+
272
+ # This method grabs all of the information about the first listed comment and
273
+ # returns it in a hash object. Relevant strings are put into the following
274
+ # keys: :poster, :date, :message. The delete button element is defined in the
275
+ # :delete_button key. Note that you'll have to use Watir's .click method to click
276
+ # the button.
277
+ def first_comment
278
+ hash = {}
279
+ comments_table = self.div(:class=>"contentcommentsTable")
280
+ last_message = comments_table.div(:class=>"contentcomments_comment last")
281
+ hash.store(:poster, last_message.span(:class=>"contentcomments_posterDataName s3d-regular-links").link.text)
282
+ hash.store(:date, last_message.span(:class=>"contentcomments_dateComment").text)
283
+ hash.store(:message, last_message.div(:class=>"contentcomments_message").text)
284
+ hash.store(:delete_button, last_message.button(:id=>/contentcomments_delete_\d+/))
285
+ return hash
286
+ end
287
+
288
+ end
289
+
290
+ # Methods related to the page for creating a new user account
291
+ class CreateNewAccount
292
+
293
+ include PageObject
294
+ include HeaderFooterBar
295
+
296
+ text_field(:user_name, :id=>"username")
297
+ text_field(:institution, :id=>"institution")
298
+ # The password field's method name needs to be
299
+ # "new_password" so that it doesn't conflict with
300
+ # the "password" field in the Sign In menu...
301
+ text_field(:new_password, :id=>"password")
302
+ text_field(:retype_password, :id=>"password_repeat")
303
+ select_list(:role, :id=>"role")
304
+ select_list(:title, :id=>"title")
305
+ text_field(:first_name,:id=>"firstName")
306
+ text_field(:last_name,:id=>"lastName")
307
+ text_field(:email,:id=>"email")
308
+ text_field(:email_confirm, :id=>"emailConfirm")
309
+ text_field(:phone_number,:id=>"phone")
310
+ checkbox(:receive_tips, :id=>"emailContact")
311
+ checkbox(:contact_me_directly, :id=>"contactMe")
312
+ button(:create_account_button, :id=>"save_account")
313
+
314
+ span(:username_error, :id=>"username_error")
315
+ span(:password_error, :id=>"password_error")
316
+ span(:password_repeat_error, :id=>"password_repeat_error")
317
+ span(:title_error, :id=>"title_error")
318
+ span(:firstname_error, :id=>"firstName_error")
319
+ span(:lastname_error, :id=>"lastName_error")
320
+ span(:email_error, :id=>"email_error")
321
+ span(:email_confirm_error, :id=>"emailConfirm_error")
322
+ span(:institution_error, :id=>"institution_error")
323
+ span(:role_error, :id=>"role_error")
324
+ span(:password_strength, :class=>/strength (zero|one|two|three|four)/)
325
+
326
+ # Clicks the 'create account' button, waits for the dashboard,
327
+ # then returns the MyDashboard class object.
328
+ def create_account
329
+ self.back_to_top
330
+ self.create_account_button
331
+ sleep 4
332
+ self.wait_until { self.text.include? "My dashboard" }
333
+ MyDashboard.new @browser
334
+ #self.class.class_eval { include OurAgreementPopUp }
335
+ end
336
+
337
+ end
338
+
339
+ # Methods related to the page that appears when you are
340
+ # creating a new Course.
341
+ class CreateCourses
342
+
343
+ include PageObject
344
+ include HeaderFooterBar
345
+ include LeftMenuBarCreateWorlds
346
+
347
+ # Clicks the button for using the math template,
348
+ # then returns the MathTemplate class object.
349
+ def use_math_template
350
+ self.div(:class=>"selecttemplate_template_large").button(:text=>"Use this template").click
351
+ # TODO - Class goes here
352
+ end
353
+
354
+ # Clicks the button to use the basic template,
355
+ # waits for the new page to load, then returns
356
+ # the CreateGroups class object.
357
+ def use_basic_template
358
+ self.div(:class=>"selecttemplate_template_small selecttemplate_template_right").button(:text=>"Use this template").click
359
+ self.wait_until { self.text.include? "Suggested URL:" }
360
+ CreateGroups.new @browser
361
+ end
362
+
363
+ end
364
+
365
+ # Methods related to the page where a new Course/Group/Project
366
+ # is set up.
367
+ class CreateGroups
368
+
369
+ include PageObject
370
+ include HeaderFooterBar
371
+ include LeftMenuBarCreateWorlds
372
+
373
+ text_field(:title, :id=>"newcreategroup_title")
374
+ text_field(:suggested_url, :id=>"newcreategroup_suggested_url")
375
+ text_area(:description, :id=>"newcreategroup_description")
376
+ text_area(:tags, :name=>"newcreategroup_tags")
377
+ select_list(:can_be_discovered_by, :id=>"newcreategroup_can_be_found_in")
378
+ select_list(:membership, :id=>"newcreategroup_membership")
379
+
380
+ # Clicks the 'Add people' button and waits for the
381
+ # Manage Participants dialog to appear.
382
+ # Includes the ManageParticipants module in the class.
383
+ def add_people
384
+ self.button(:text, "Add people").click
385
+ self.wait_for_ajax(2)
386
+ self.class.class_eval { include ManageParticipants }
387
+ end
388
+
389
+ # Clicks the 'Add more people' button and waits for
390
+ # the Manage Participants dialog to appear.
391
+ # Includes the ManageParticipants module in the class.
392
+ def add_more_people
393
+ self.button(:text, "Add more people").click
394
+ self.wait_for_ajax(2)
395
+ self.class.class_eval { include ManageParticipants }
396
+ end
397
+
398
+ # Clicks the 'List categories' button, waits for
399
+ # the Categories dialog to appear, and then
400
+ # includes the AddRemoveCategories module in the class.
401
+ def list_categories
402
+ self.button(:text=>"List categories").click
403
+ self.wait_for_ajax(2)
404
+ self.class.class_eval { include AddRemoveCategories }
405
+ end
406
+
407
+ # Clicks the 'Create' button for the Course or Group, etc.,
408
+ # then waits until the Course Library page loads. Once that
409
+ # happens, returns the Library class object.
410
+ def create_basic_course
411
+ create_thing
412
+ unless url_error_element.visible?
413
+ self.wait_until(45) { self.text.include? "Add content" }
414
+ self.button(:id=>"group_create_new_area", :class=>"s3d-button s3d-header-button s3d-popout-button").wait_until_present
415
+ Library.new @browser
416
+ end
417
+ end
418
+ alias create_simple_group create_basic_course
419
+ alias create_group create_basic_course
420
+ alias create_research_support_group create_basic_course
421
+
422
+ # Clicks the 'Create' button then waits until the Research Intro page loads. Once that
423
+ # happens, returns the ResearchIntro class object.
424
+ def create_research_project
425
+ create_thing
426
+ unless url_error_element.visible?
427
+ self.button(:id=>"group_create_new_area", :class=>"s3d-button s3d-header-button s3d-popout-button").wait_until_present
428
+ ResearchIntro.new @browser
429
+ end
430
+ end
431
+
432
+
433
+ span(:url_error, :id=>"newcreategroup_suggested_url_error")
434
+
435
+ private
436
+
437
+ # Do not use. This method is used by the public 'create' methods in this class.
438
+ def create_thing
439
+ self.button(:class=>"s3d-button s3d-overlay-button newcreategroup_create_simple_group").click
440
+ sleep 0.3
441
+ self.div(:id=>"sakai_progressindicator").wait_while_present
442
+ sleep 2 # The poor man's wait_for_ajax, since that was failing.
443
+ end
444
+
445
+ end
446
+
447
+ # Methods related to the page for Creating a Research Project
448
+ class CreateResearch
449
+
450
+ include PageObject
451
+ include HeaderFooterBar
452
+ include LeftMenuBarCreateWorlds
453
+
454
+ # Clicks the button for using the Research Project template, waits for the
455
+ # page to load, then returns the CreateGroups class object.
456
+ def use_research_project_template
457
+ self.div(:class=>"selecttemplate_template_large").button(:text=>"Use this template").click
458
+ self.wait_until { self.text.include? "Suggested URL:" }
459
+ CreateGroups.new @browser
460
+ end
461
+
462
+ # Clicks the button for using the Research Support Group template,
463
+ # waits for the page to load, then returns the CreateGroups class object.
464
+ def use_research_support_group_template
465
+ self.div(:class=>"selecttemplate_template_small selecttemplate_template_right").button(:text=>"Use this template").click
466
+ self.wait_until { self.text.include? "Suggested URL:" }
467
+ CreateGroups.new @browser
468
+ end
469
+
470
+ end
471
+
472
+ # Page Object and methods for the SEARCH ALL TYPES page
473
+ # NOT the "Browse All Categories" page!
474
+ class ExploreAll
475
+
476
+ include PageObject
477
+ include GlobalMethods
478
+ include HeaderFooterBar
479
+ include LeftMenuBarSearch
480
+ include ListWidget
481
+ include ListCollections
482
+ include ListContent
483
+ include ListGroups
484
+ include ListPeople
485
+ include ListProjects
486
+ include SearchBar
487
+
488
+ # Returns the results header title (the text prior to the count of the results returned)
489
+ def results_header
490
+ top = self.div(:class=>"searchall_content_main")
491
+ top.div(:id=>"results_header").span.text =~ /^.+(?=.\()/
492
+ $~.to_s
493
+ end
494
+
495
+ end
496
+
497
+ # Methods related to the page for searching Content.
498
+ class ExploreContent
499
+
500
+ include PageObject
501
+ include GlobalMethods
502
+ include HeaderFooterBar
503
+ include LeftMenuBarSearch
504
+ include ListWidget
505
+ include ListContent
506
+ include SearchBar
507
+
508
+ # Returns the results header title (the text prior to the count of the results returned)
509
+ def results_header
510
+ top = self.div(:class=>"searchcontent_content_main")
511
+ top.div(:id=>"results_header").span.text =~ /^.+(?=.\()/
512
+ $~.to_s
513
+ end
514
+
515
+ end
516
+
517
+ # Methods related to the People/Users search page
518
+ class ExplorePeople
519
+
520
+ include PageObject
521
+ include GlobalMethods
522
+ include HeaderFooterBar
523
+ include LeftMenuBarSearch
524
+ include ListWidget
525
+ include ListPeople
526
+ include SearchBar
527
+
528
+ # Returns the results header title (the text prior to the count of the results returned)
529
+ def results_header
530
+ top = self.div(:class=>"searchpeople_content_main")
531
+ top.div(:id=>"results_header").span.text =~ /^.+(?=.\()/
532
+ $~.to_s
533
+ end
534
+
535
+ end
536
+
537
+ # Methods related to the Groups search page.
538
+ class ExploreGroups
539
+
540
+ include PageObject
541
+ include GlobalMethods
542
+ include HeaderFooterBar
543
+ include LeftMenuBarSearch
544
+ include ListWidget
545
+ include ListGroups
546
+ include SearchBar
547
+
548
+ # Returns the results header title (the text prior to the count of the results returned)
549
+ def results_header
550
+ top = self.div(:id=>"searchgroups_widget", :index=>0)
551
+ top.div(:id=>"results_header").span(:id=>"searchgroups_type_title").text
552
+ end
553
+
554
+ end
555
+
556
+ # Methods related to the page for searching Courses.
557
+ class ExploreCourses
558
+
559
+ include PageObject
560
+ include GlobalMethods
561
+ include HeaderFooterBar
562
+ include LeftMenuBarSearch
563
+ include ListWidget
564
+ include ListGroups
565
+ include SearchBar
566
+
567
+ # Returns the results header title (the text prior to the count of the results returned)
568
+ def results_header
569
+ top = self.div(:id=>"searchgroups_widget", :index=>1)
570
+ top.div(:id=>"results_header").span(:id=>"searchgroups_type_title").text
571
+ end
572
+
573
+ # TODO - Describe method
574
+ def courses_count
575
+ #TODO - Write method
576
+ end
577
+
578
+ # Selects the specified item in the 'Filter by' field. Waits for
579
+ # Ajax calls to drop to zero.
580
+ def filter_by=(selection)
581
+ self.select(:id=>"facted_select").select(selection)
582
+ self.wait_for_ajax(2)
583
+ end
584
+
585
+ # Selects the specified item in the 'Sort by' field. Waits for
586
+ # any Ajax calls to finish.
587
+ def sort_by=(selection)
588
+ self.div(:class=>"s3d-search-sort").select().select(selection)
589
+ self.wait_for_ajax(2)
590
+ end
591
+
592
+ end
593
+
594
+ # Methods related to the page for searching for Research Projects.
595
+ class ExploreResearch
596
+
597
+ include PageObject
598
+ include GlobalMethods
599
+ include HeaderFooterBar
600
+ include LeftMenuBarSearch
601
+ include ListWidget
602
+ include ListProjects
603
+ include SearchBar
604
+
605
+ # Returns the results header title (the text prior to the count of the results returned)
606
+ def results_header
607
+ top = self.div(:id=>"searchgroups_widget", :index=>2)
608
+ top.div(:id=>"results_header").span(:id=>"searchgroups_type_title").text
609
+ end
610
+
611
+ end
612
+
613
+ # Methods related to objects found on the Dashboard
614
+ class MyDashboard
615
+
616
+ include PageObject
617
+ include GlobalMethods
618
+ include HeaderFooterBar
619
+ include LeftMenuBarYou
620
+ include ChangePicturePopUp # TODO ... Rethink including this by default
621
+
622
+ # Page Objects
623
+ button(:edit_layout, :text=>"Edit layout")
624
+ radio_button(:one_column, :id=>"layout-picker-onecolumn")
625
+ radio_button(:two_column, :id=>"layout-picker-dev")
626
+ radio_button(:three_column, :id=>"layout-picker-threecolumn")
627
+ button(:save_layout, :id=>"select-layout-finished")
628
+ button(:add_widgets_button, :class=>/dashboard_global_add_widget/) # Do not use for clicking the button. See custom methods
629
+ image(:profile_pic, :id=>"entity_profile_picture")
630
+ div(:my_name, :class=>"s3d-bold entity_name_me")
631
+
632
+ #div(:page_title, :class=>"s3d-contentpage-title")
633
+
634
+ # Custom Methods...
635
+
636
+ # Returns the text contents of the page title div
637
+ def page_title
638
+ self.div(:id=>"s3d-page-container").div(:class=>"s3d-contentpage-title").text
639
+ end
640
+
641
+ # Clicks the 'Add widget' button, waits for the page to load,
642
+ # then includes the AddRemoveWidgets module in the class.
643
+ # Note that this method is specifically "add_widgets" because
644
+ # otherwise there would be a method collision with the "add widget"
645
+ # method in the AddRemoveWidgets module.
646
+ def add_widgets
647
+ self.add_widgets_button
648
+ self.wait_until { self.text.include? "Add widgets" }
649
+ self.class.class_eval { include AddRemoveWidgets }
650
+ end
651
+
652
+ # Returns an array object containing a list of all selected widgets.
653
+ def displayed_widgets
654
+ list = []
655
+ self.div(:class=>"fl-container-flex widget-content").divs(:class=>"s3d-contentpage-title").each do |div|
656
+ list << div.text
657
+ end
658
+ return list
659
+ end
660
+
661
+ # Returns the name of the recent membership item displayed.
662
+ def recent_membership_item
663
+ self.div(:class=>"recentmemberships_widget").link(:class=>/recentmemberships_item_link/).text
664
+ end
665
+
666
+ # Clicks on the membership item displayed in the Recent Memberships widget.
667
+ # Then returns the Library class object.
668
+ def go_to_most_recent_membership
669
+ self.link(:class=>"recentmemberships_item_link s3d-widget-links s3d-bold").click
670
+ sleep 2 # The next line sometimes throws an "unknown javascript error" without this line.
671
+ self.wait_for_ajax(2)
672
+ Library.new @browser
673
+ end
674
+
675
+ # Returns an array containing all the items listed in the "My memberships" dashboard widget.
676
+ def my_memberships_list
677
+ list = []
678
+ self.ul(:class=>"mygroup_items_list").spans(:class=>"mygroups_ellipsis_text").each { |span| list << span.text }
679
+ return list
680
+ end
681
+
682
+ end
683
+
684
+ # Methods related to the My Messages pages.
685
+ class MyMessages
686
+
687
+ include PageObject
688
+ include GlobalMethods
689
+ include HeaderFooterBar
690
+ include LeftMenuBarYou
691
+
692
+ # Page Objects
693
+ button(:accept_invitation, :text=>"Accept invitation")
694
+ button(:ignore_invitation, :text=>"Ignore invitation")
695
+
696
+ # Custom Methods...
697
+
698
+ # Returns the text of the displayed page title
699
+ def page_title
700
+ active_div.span(:id=>"inbox_box_title").text
701
+ end
702
+
703
+ # Clicks the "Compose message" button on any of the
704
+ # My messages pages.
705
+ def compose_message
706
+ active_div.link(:id=>"inbox_create_new_message").click
707
+ self.wait_for_ajax(2)
708
+ self.class.class_eval { include SendMessagePopUp }
709
+ end
710
+
711
+ # Returns an Array containing the list of Email subjects.
712
+ def message_subjects
713
+ list = []
714
+ active_div.divs(:class=>"inbox_subject").each do |div|
715
+ list << div.text
716
+ end
717
+ return list
718
+ end
719
+
720
+ # Clicks on the specified email to open it for reading
721
+ def open_message(subject)
722
+ active_div.div(:class=>"inbox_subject").link(:text=>subject).click
723
+ self.wait_for_ajax(2)
724
+ self.class.class_eval { include SendMessagePopUp }
725
+ end
726
+
727
+ # Clicks the Delete button for the specified message (specified by Subject), then
728
+ # waits for Ajax calls to complete.
729
+ def delete_message(subject)
730
+ subject_div = active_div.div(:class=>"inbox_subject", :text=>subject)
731
+ subject_div.parent.parent.parent.button(:title=>"Delete message").click
732
+ self.wait_for_ajax
733
+ end
734
+
735
+ # Clicks the Reply button for the specified (by Subject) message
736
+ # then waits for the Ajax calls to complete
737
+ def reply_to_message(subject)
738
+ subject_div = active_div.div(:class=>"inbox_subject", :text=>subject)
739
+ subject_div.parent.parent.parent.link(:title=>"Reply").click
740
+ self.wait_for_ajax
741
+ self.class.class_eval { include SendMessagePopUp }
742
+ end
743
+ alias read_message open_message
744
+
745
+ # Message Preview methods...
746
+
747
+ # Returns the Sender text for the specified message (by Subject)
748
+ def preview_sender(subject)
749
+ message_div(subject).div(:class=>"inbox_name").button.text
750
+ end
751
+
752
+ # Returns the message recipient name for the specified message (by Subject)
753
+ def preview_recipient(subject)
754
+ message_div(subject).div(:class=>"inbox_name").span.text
755
+ end
756
+
757
+ # Returns the date/time string for the specified message.
758
+ def preview_date(subject)
759
+ message_div(subject).div(:class=>"inbox_date").span.text
760
+ end
761
+
762
+ # Returns the file path and name of the displayed profile pic of the
763
+ # specified message.
764
+ def preview_profile_pic(subject)
765
+ message_div(subject).parent.image(:class=>"person_icon").src
766
+ end
767
+
768
+ # Returns the text of the Body of the specified message.
769
+ def preview_body(subject)
770
+ message_div(subject).div(:class=>"inbox_excerpt").text
771
+ end
772
+
773
+ # The New Message page is controlled by the SendMessagePopUp module
774
+
775
+ # Read/Reply Page Objects (defined with Watir)
776
+
777
+ # Returns the text of the name of the sender of the message being viewed
778
+ def message_sender
779
+ active_div.div(:id=>"inbox_show_message").div(:class=>"inbox_name").button(:class=>"s3d-regular-links s3d-link-button s3d-bold personinfo_trigger_click personinfo_trigger").text
780
+ end
781
+
782
+ # Returns the recipient name text. The method assumes you're currently
783
+ # viewing a message--that you're not looking at a list of messages.
784
+ def message_recipient
785
+ active_div.div(:class, "inbox_name").span(:class=>"inbox_to_list").text
786
+ end
787
+
788
+ # Returns the date of the message being viewed (as a String object)
789
+ def message_date
790
+ active_div.div(:id=>"inbox_show_message").div(:class=>"inbox_date").span.text
791
+ end
792
+
793
+ # Returns the text of the message subject. The method assumes you're currently
794
+ # viewing a message--that you're not looking at a list of messages.
795
+ def message_subject
796
+ active_div.div(:id=>"inbox_show_message").div(:class=>"inbox_subject").link.text
797
+ end
798
+
799
+ # Returns the text of the message body.The method assumes you're currently
800
+ # viewing a message--that you're not looking at a list of messages.
801
+ def message_body
802
+ active_div.div(:id=>"inbox_show_message").div(:class=>"inbox_excerpt").text
803
+ end
804
+
805
+ # The page element for the "Delete selected" button.
806
+ def delete_selected_element
807
+ active_div.button(:id=>"inbox_delete_selected")
808
+ end
809
+
810
+ # Clicks the "Delete selected" buton and waits for Ajax calls to complete
811
+ def delete_selected
812
+ delete_selected_element.click
813
+ self.wait_for_ajax
814
+ end
815
+
816
+ # The page element for the "Mark as read" button
817
+ def mark_as_read_element
818
+ active_div.button(:id=>"inbox_mark_as_read")
819
+ end
820
+
821
+ # Clicks the "Mark as read" button, then waits for Ajax calls to complete
822
+ def mark_as_read
823
+ mark_as_read_element.click
824
+ sleep 0.3
825
+ self.wait_for_ajax
826
+ end
827
+
828
+ # Checks the checkbox for the specified message in the list.
829
+ def select_message(subject)
830
+ subject_div = active_div.div(:class=>"inbox_subject", :text=>subject)
831
+ subject_div.parent.parent.parent.checkbox.set
832
+ end
833
+
834
+ # Returns "read" or "unread", based on the relevant div's class setting.
835
+ def message_status(subject)
836
+ classname = self.div(:class=>/^inbox_item fl-container fl-fix/, :text=>/#{Regexp.escape(subject)}/).class_name
837
+ if classname =~ /unread/
838
+ return "unread"
839
+ else
840
+ return "read"
841
+ end
842
+ end
843
+
844
+ # Counts the number of displayed messages on the current page.
845
+ # Returns a hash containing counts for read and unread messages on the page.
846
+ # Keys in the hash are :all, :read, and :unread.
847
+ def message_counts
848
+ hash = {}
849
+ total = active_div.divs(:class=>"inbox_items_inner").length
850
+ unread = active_div.divs(:class=>/unread/).length
851
+ read = total - unread
852
+ hash.store(:total, total)
853
+ hash.store(:unread, unread)
854
+ hash.store(:read, read)
855
+ return hash
856
+ end
857
+
858
+ # Search
859
+
860
+ # The page element for the Search text field.
861
+ def search_field_element
862
+ active_div.text_field(:id=>"inbox_search_messages")
863
+ end
864
+
865
+ # Enters the specified text in the Search text box.
866
+ # Note that it appends a line feed on the end of the text
867
+ # so that the search happens immediately. Thus there is
868
+ # no need for a second line in the test script to
869
+ # specify clicking the Search button.
870
+ def search_messages=(text)
871
+ search_field_element.set(text+"\n")
872
+ begin
873
+ active_div.p(:id=>"inbox_search_term").wait_until_present(3)
874
+ rescue
875
+ # do nothing because the result set might be empty
876
+ end
877
+ end
878
+
879
+ # The page element for the Search button
880
+ def search_button_element
881
+ active_div.button(:class=>"s3d-button s3d-overlay-button s3d-search-button")
882
+ end
883
+
884
+ # Clicks the Search button and waits for Ajax calls to complete
885
+ def search_messages
886
+ search_button_element.click
887
+ self.wait_for_ajax
888
+ end
889
+
890
+ # Private Methods in this class
891
+ private
892
+
893
+ # Determines which sub page is currently active,
894
+ # so that all the other methods in the class will work
895
+ # properly.
896
+ def active_div
897
+ container_div = self.div(:id=>"s3d-page-container")
898
+ id = "x"
899
+ mail_divs = container_div.divs(:id=>"inbox_widget")
900
+ if mail_divs.length == 1
901
+ id = mail_divs[0].id
902
+ else
903
+ mail_divs.each do |div|
904
+ if div.visible?
905
+ id = div.parent.parent.parent.id
906
+ break
907
+ end
908
+ end
909
+ end
910
+ if id == "x"
911
+ puts "==========================="
912
+ puts "Couldn't find the visible inbox widget div, so didn't get a valid ID"
913
+ puts "==========================="
914
+ end
915
+ return self.div(:id=>id)
916
+ end
917
+
918
+ # Uses the specified subject text to determine which message div to
919
+ # use.
920
+ def message_div(subject)
921
+ active_div.div(:class=>"inbox_subject",:text=>subject).parent.parent
922
+ end
923
+
924
+ end
925
+
926
+ # Methods related to the Basic Info page in My Profile
927
+ class MyProfileBasicInfo
928
+
929
+ include PageObject
930
+ include GlobalMethods
931
+ include HeaderFooterBar
932
+ include LeftMenuBarYou
933
+
934
+ # Basic Information
935
+ text_field(:given_name, :name=>"firstName")
936
+ text_field(:family_name, :id=>"lastName")
937
+ text_field(:preferred_name, :id=>"preferredName")
938
+ text_area(:tags, :name=>"tags")
939
+ span(:first_name_error, :id=>"firstName_error")
940
+ span(:last_name_error, :id=>"lastName_error")
941
+
942
+ # Clicks the "List categories" button and waits for the
943
+ # pop-up dialog to load. Includes the AddRemoveCategories
944
+ # module in the class object.
945
+ def list_categories
946
+ self.form(:id=>"displayprofilesection_form_basic").button(:text=>"List categories").click
947
+ self.wait_for_ajax(3)
948
+ self.class.class_eval { include AddRemoveCategories }
949
+ end
950
+
951
+ # Returns an array consisting of the contents of the Tags and Categories
952
+ # field. Returns the categories split up among children and parents.
953
+ # In other words, for example, "Engineering & Technology » Computer Engineering"
954
+ # becomes "Engineering & Technology", "Computer Engineering"
955
+ def categories
956
+ list = []
957
+ self.form(:id=>"displayprofilesection_form_basic").ul(:class=>"as-selections").lis.each do |li|
958
+ next if li.text == ""
959
+ string = li.text[/(?<=\n).+/]
960
+ split = string.split(" » ")
961
+ list << split
962
+ end
963
+ list.flatten!
964
+ list.uniq!
965
+ return list
966
+ end
967
+
968
+ # Removes the specified category from the list
969
+ def remove_category(name)
970
+ self.form(:id=>"displayprofilesection_form_basic").li(:text=>/#{Regexp.escape(name)}/).link(:class=>"as-close").click
971
+ self.wait_for_ajax
972
+ end
973
+
974
+ # Clicks the "Update" button and waits for any Ajax calls
975
+ # to complete.
976
+ def update
977
+ self.form(:id=>"displayprofilesection_form_basic").button(:text=>"Update").click
978
+ self.wait_for_ajax(2)
979
+ end
980
+
981
+ end
982
+
983
+ # Methods related to the About Me page in My Profile
984
+ class MyProfileAboutMe
985
+
986
+ include PageObject
987
+ include GlobalMethods
988
+ include HeaderFooterBar
989
+ include LeftMenuBarYou
990
+
991
+ text_area(:about_Me, :id=>"aboutme")
992
+ text_area(:academic_interests, :id=>"academicinterests")
993
+ text_area(:personal_interests, :id=>"personalinterests")
994
+
995
+ # Clicks the "Update" button and waits for any Ajax calls
996
+ # to complete.
997
+ def update
998
+ self.form(:id=>"displayprofilesection_form_aboutme").button(:text=>"Update").click
999
+ self.wait_for_ajax(2)
1000
+ end
1001
+
1002
+ end
1003
+
1004
+ # Methods related to the 'Online' page in My Profile.
1005
+ class MyProfileOnline
1006
+
1007
+ include PageObject
1008
+ include GlobalMethods
1009
+ include HeaderFooterBar
1010
+ include LeftMenuBarYou
1011
+
1012
+ button(:add_another_online, :text=>"Add another Online", :id=>"displayprofilesection_add_online")
1013
+
1014
+ text_field(:site, :id=>/siteOnline_\d+/, :index=>-1)
1015
+ text_field(:url, :id=>/urlOnline_\d+/, :index=>-1)
1016
+
1017
+ # Clicks the "Update" button and waits for any Ajax calls
1018
+ # to complete.
1019
+ def update
1020
+ self.form(:id=>"displayprofilesection_form_online").button(:text=>"Update").click
1021
+ self.wait_for_ajax
1022
+ end
1023
+
1024
+ # Returns a hash object, where the keys are the site names and the
1025
+ # values are the urls.
1026
+ def sites_list
1027
+ hash = {}
1028
+ self.div(:id=>"displayprofilesection_sections_online").divs(:class=>"s3d-form-field-wrapper").each do |div|
1029
+ hash.store(div.text_field(:id=>/siteOnline_/).value, div.text_field(:id=>/urlOnline_/).value)
1030
+ end
1031
+ return hash
1032
+ end
1033
+
1034
+ # Removes the specified Online record from the list by clicking its
1035
+ # "Remove this Online" button.
1036
+ def remove_this_online(text)
1037
+ #find the div...
1038
+ div = self.div(:class=>"online").text_field(:value=>text).parent.parent.parent
1039
+ #click the button
1040
+ div.button(:id=>/displayprofilesection_remove_link_/).click
1041
+ self.wait_for_ajax
1042
+ end
1043
+ end
1044
+
1045
+ # Methods related to the My Profile: Contact Information page
1046
+ class MyProfileContactInfo
1047
+
1048
+ include PageObject
1049
+ include GlobalMethods
1050
+ include HeaderFooterBar
1051
+ include LeftMenuBarYou
1052
+
1053
+ # Page Objects
1054
+ #button(:add_another, :text=>"Add another", :id=>/profilesection_add_link_\d/)
1055
+ text_field(:institution, :id=>"college", :name=>"college")
1056
+ text_field(:department, :id=>"department", :name=>"department")
1057
+ text_field(:title_role, :name=>"role")
1058
+ text_field(:role, :name=>"role")
1059
+ text_field(:email, :name=>"emailContact")
1060
+ text_field(:instant_messaging, :name=>"imContact")
1061
+ text_field(:phone, :name=>"phoneContact")
1062
+ text_field(:mobile, :name=>"mobileContact")
1063
+ text_field(:fax, :name=>"faxContact")
1064
+ text_field(:address, :name=>"addressContact")
1065
+ text_field(:city, :id=>"cityContact")
1066
+ text_field(:state, :name=>"stateContact")
1067
+ text_field(:postal_code, :name=>"postalContact")
1068
+ text_field(:country, :name=>"countryContact")
1069
+
1070
+ # Custom Methods
1071
+
1072
+ # Clicks the "Update" button and waits for any Ajax calls
1073
+ # to complete.
1074
+ def update
1075
+ self.form(:id=>"displayprofilesection_form_contact").button(:text=>"Update").click
1076
+ self.wait_for_ajax(2)
1077
+ end
1078
+
1079
+ # Takes a hash object and uses it to fill out
1080
+ # the fields on the form. The necessary key values in the
1081
+ # hash argument are as follows:
1082
+ # :institution, :department, :title, :email, :im, :phone,
1083
+ # :mobile, :fax, :address, :city, :state, :zip, and :country.
1084
+ # Any keys that are different or missing will be ignored.
1085
+ def fill_out_form(hash)
1086
+ self.email=hash[:email]
1087
+ self.fax=hash[:fax]
1088
+ self.institution=hash[:institution]
1089
+ self.department=hash[:department]
1090
+ self.title_role=hash[:title]
1091
+ self.instant_messaging=hash[:im]
1092
+ self.phone=hash[:phone]
1093
+ self.mobile=hash[:mobile]
1094
+ self.address=hash[:address]
1095
+ self.city=hash[:city]
1096
+ self.state=hash[:state]
1097
+ self.postal_code=hash[:zip]
1098
+ self.country=hash[:country]
1099
+ end
1100
+
1101
+ end
1102
+
1103
+ # Publications
1104
+ class MyProfilePublications
1105
+
1106
+ include PageObject
1107
+ include GlobalMethods
1108
+ include HeaderFooterBar
1109
+ include LeftMenuBarYou
1110
+
1111
+ # Page Objects
1112
+
1113
+ button(:add_another_publication, :text=>"Add another publication", :id=>"displayprofilesection_add_publications")
1114
+ text_field(:main_title, :id=>/maintitle_\d+/, :index=>-1)
1115
+ text_field(:main_author, :name=>/mainauthor_\d+/, :index=>-1)
1116
+ text_field(:co_authors, :id=>/coauthor_\d+/, :index=>-1)
1117
+ text_field(:publisher, :id=>/publisher_\d+/, :index=>-1)
1118
+ text_field(:place_of_publication, :id=>/placeofpublication_\d+/, :index=>-1)
1119
+ text_field(:volume_title, :id=>/volumetitle_\d+/, :index=>-1)
1120
+ text_field(:volume_information, :id=>/volumeinformation_\d+/, :index=>-1)
1121
+ text_field(:year, :id=>/year_\d+/, :index=>-1)
1122
+ text_field(:number, :id=>/number_\d+/, :index=>-1)
1123
+ text_field(:series_title, :id=>/series.title_\d+/, :index=>-1)
1124
+ text_field(:url, :id=>/url_\d+/, :index=>-1)
1125
+ span(:title_error, :id=>/maintitle_\d+_error/)
1126
+ span(:author_error, :id=>/mainauthor_\d+_error/)
1127
+ span(:publisher_error, :id=>/publisher_\d+_error/)
1128
+ span(:place_error, :id=>/placeofpublication_\d+_error/)
1129
+ span(:year_error, :id=>/year_\d+_error/)
1130
+ span(:url_error, :id=>/url_\d+_error/)
1131
+
1132
+ # Custom Methods...
1133
+
1134
+ # Clicks the "Update" button and waits for any Ajax calls
1135
+ # to complete.
1136
+ def update
1137
+ self.form(:id=>"displayprofilesection_form_publications").button(:text=>"Update").click
1138
+ self.wait_for_ajax(2)
1139
+ end
1140
+
1141
+ # Takes a hash object and uses it to fill
1142
+ # in all the publication fields. The key values
1143
+ # that the has must contain are as follows:
1144
+ # :main_title, :main_author, :co_authors, :publisher,
1145
+ # :place, :volume_title, :volume_info, :year, :number,
1146
+ # :series, :url.
1147
+ # Any missing or misspelled key values will be ignored.
1148
+ def fill_out_form(hash)
1149
+ self.main_title=hash[:main_title]
1150
+ self.main_author=hash[:main_author]
1151
+ self.co_authors=hash[:co_authors]
1152
+ self.publisher=hash[:publisher]
1153
+ self.place_of_publication=hash[:place]
1154
+ self.volume_title=hash[:volume_title]
1155
+ self.volume_information=hash[:volume_info]
1156
+ self.year=hash[:year]
1157
+ self.number=hash[:number]
1158
+ self.series_title=hash[:series]
1159
+ self.url=hash[:url]
1160
+ end
1161
+
1162
+ # Clicks the "Remove this publication" link for the publication specified (by
1163
+ # the Main title of the record in question).
1164
+ def remove_this_publication(main_title)
1165
+ target_div_id = self.text_field(:value=>main_title).parent.parent.parent.id
1166
+ self.div(:id=>target_div_id).button(:id=>/displayprofilesection_remove_link_/).click
1167
+ self.wait_for_ajax
1168
+ end
1169
+
1170
+ # Returns an array containing all of the titles of the publications that exist
1171
+ # on the page.
1172
+ def publication_titles
1173
+ array = []
1174
+ self.div(:id=>"displayprofilesection_sections_publications").text_fields(:id=>/maintitle_/).each { |field| array << field.value }
1175
+ return array
1176
+ end
1177
+ alias titles publication_titles
1178
+
1179
+ # Returns an array of hashes. Each hash in the array refers to one of
1180
+ # the listed publications.
1181
+ #
1182
+ # Each hash's key=>value pairs are determined by the field title and field values for the publications.
1183
+ #
1184
+ # Example: "Main title:"=>"War and Peace","Main author:"=>"Tolstoy", etc....
1185
+ def publications_data
1186
+ list = []
1187
+ self.div(:id=>"displayprofilesection_sections_publications").divs(:class=>"displayprofilesection_multiple_section").each do |div|
1188
+ hash = {}
1189
+ div.divs(:class=>"displayprofilesection_field").each { |subdiv| hash.store(subdiv.label(:class=>"s3d-input-label").text, subdiv.text_field.value) }
1190
+ list << hash
1191
+ end
1192
+ return list
1193
+ end
1194
+ alias publication_data publications_data
1195
+ alias publications_list publications_data
1196
+
1197
+ end
1198
+
1199
+ # Methods related to the User's My Library page.
1200
+ class MyLibrary
1201
+
1202
+ include PageObject
1203
+ include GlobalMethods
1204
+ include HeaderFooterBar
1205
+ include ListWidget
1206
+ include ListContent
1207
+ include LeftMenuBarYou
1208
+ include LibraryWidget
1209
+
1210
+ # Page Objects
1211
+ button(:empty_library_add_content_button, :id=>"mylibrary_addcontent")
1212
+
1213
+ # Custom Methods and Page Objects...
1214
+
1215
+ # Defines the div that displays when the library has no contents.
1216
+ # Useful for testing whether the library is empty or not.
1217
+ def empty_library
1218
+ active_div.div(:id=>"mylibrary_empty")
1219
+ end
1220
+
1221
+ # Returns the text of the Page Title.
1222
+ def page_title
1223
+ active_div.div(:class=>"s3d-contentpage-title").text
1224
+ end
1225
+
1226
+ # Private methods...
1227
+ private
1228
+
1229
+ def active_div
1230
+ id = self.div(:id=>/mylibrarycontainer/).parent.id
1231
+ return self.div(:id=>id)
1232
+ end
1233
+
1234
+ end
1235
+
1236
+ # Methods related to the User's My Memberships page.
1237
+ class MyMemberships
1238
+
1239
+ include PageObject
1240
+ include GlobalMethods
1241
+ include HeaderFooterBar
1242
+ include ListWidget
1243
+ include ListGroups
1244
+ include LeftMenuBarYou
1245
+
1246
+ # Page Objects
1247
+ text_field(:search_memberships_field, :id=>"mymemberships_livefilter")
1248
+ select_list(:sort_by_list, :id=>"mymemberships_sortby")
1249
+ div(:gridview_button, :class=>/search-results-gridview/)
1250
+ div(:listview_button, :class=>/search-results-listview/)
1251
+ checkbox(:select_all, :id=>"mymemberships_select_checkbox")
1252
+ button(:add_selected_button, :id=>"mymemberships_addpeople_button") # Don't use this method. Use the custom one defined below
1253
+ button(:message_selected_button, :id=>"mymemberships_message_button") # Don't use this method. Use the custom one defined below
1254
+ link(:create_a_new_group_button, :text=>"Create a new group") # Don't use this method. Use the custom one defined below
1255
+
1256
+ # Custom methods...
1257
+
1258
+ # Returns the text of the Page Title.
1259
+ def page_title
1260
+ active_div.div(:class=>"s3d-contentpage-title").text
1261
+ end
1262
+
1263
+ # Returns the div object for the blue quote bubble that
1264
+ # appears when the page is empty of memberships.
1265
+ def quote_bubble
1266
+ active_div.div(:class=>"s3d-no-results-container")
1267
+ end
1268
+
1269
+ # Clicks the "Create one" link in the quote_bubble
1270
+ # div and then returns the CreateGroups class object.
1271
+ def create_one
1272
+ quote_bubble.link(:text=>"Create one").click
1273
+ self.wait_until { self.button(:class=>/create_simple_group/).present? }
1274
+ CreateGroups.new @browser
1275
+ end
1276
+
1277
+ # Sorts the list of memberships according to the specified value
1278
+ def sort_by=(type)
1279
+ self.sort_by_list=type
1280
+ self.wait_for_ajax
1281
+ end
1282
+
1283
+ # Private methods...
1284
+ private
1285
+
1286
+ def active_div
1287
+ id = self.div(:id=>/mymembershipscontainer/).parent.id
1288
+ return self.div(:id=>id)
1289
+ end
1290
+
1291
+ end
1292
+
1293
+ # Page Objects and Custom Methods for the My Contacts page.
1294
+ class MyContacts
1295
+
1296
+ include PageObject
1297
+ include GlobalMethods
1298
+ include HeaderFooterBar
1299
+ include ListWidget
1300
+ include ListPeople
1301
+ include LeftMenuBarYou
1302
+
1303
+ # Page Objects
1304
+ navigating_link(:find_and_add_people, "Find and add people", "ExplorePeople")
1305
+
1306
+ # Page Objects defined with Watir-webdriver...
1307
+
1308
+ # The text of the Page Title. It should usually return "My contacts".
1309
+ def page_title
1310
+ active_div.div(:class=>"s3d-contentpage-title").text
1311
+ end
1312
+
1313
+ # This returns the the "contacts_invited" page div
1314
+ # (as a Watir page object). This can be used to validate
1315
+ # that there are pending invites on the page. Primarily, however, it's used
1316
+ # by other methods in this class--to differentiate the pending list
1317
+ # from the contacts list.
1318
+ def pending_contacts
1319
+ active_div.div(:id=>"contacts_invited")
1320
+ end
1321
+
1322
+ # Custom Methods...
1323
+
1324
+ # Returns the list of names of people requesting
1325
+ # contact
1326
+ def pending_invites
1327
+ list = []
1328
+ pending_contacts.links(:class=>"s3d-bold s3d-regular-light-links", :title=>/View/).each { |link| list << link.text }
1329
+ return list
1330
+ end
1331
+
1332
+ # Clicks the "accept connection" plus sign for the specified
1333
+ # person in the Pending contacts list
1334
+ def accept_connection(name)
1335
+ self.li(:text=>/#{Regexp.escape(name)}/).button(:title=>"Accept connection").click
1336
+ sleep 0.8
1337
+ self.wait_for_ajax(2)
1338
+ end
1339
+
1340
+ # Clicks the "Find more people" link, then returns
1341
+ # the ExplorePeople class object.
1342
+ def find_more_people
1343
+ active_div.link(:text=>"Find more people").click
1344
+ self.wait_for_ajax
1345
+ ExplorePeople.new @browser
1346
+ end
1347
+
1348
+ # Private methods...
1349
+ private
1350
+
1351
+ # The top div for the contents of the page.
1352
+ # This method is a helper method for other objects
1353
+ # defined on the page.
1354
+ def active_div
1355
+ self.div(:id=>/^contactscontainer\d+/)
1356
+ end
1357
+
1358
+ end
1359
+
1360
+ # Methods related to the page for viewing a User's profile
1361
+ class ViewPerson
1362
+
1363
+ include PageObject
1364
+ include GlobalMethods
1365
+ include HeaderFooterBar
1366
+ include LeftMenuBarYou
1367
+
1368
+ # Page Objects
1369
+ navigating_link(:basic_information, "Basic Information", "ViewPerson")
1370
+ navigating_link(:categories, "Categories", "ViewPerson")
1371
+ navigating_link(:about_me, "About Me", "ViewPerson")
1372
+ navigating_link(:online, "Online", "ViewPerson")
1373
+ navigating_link(:contact_information, "Contact Information", "ViewPerson")
1374
+ navigating_link(:publications, "Publications", "ViewPerson")
1375
+ div(:contact_name, :id=>"entity_name")
1376
+ button(:message_button, :id=>"entity_user_message")
1377
+ button(:add_to_contacts_button, :id=>"entity_user_add_to_contacts")
1378
+ button(:invitation_sent_button, :text=>"Contact invitation sent")
1379
+ button(:accept_invitation_button, :id=>"entity_user_accept_invitation")
1380
+
1381
+ # Custom Methods...
1382
+
1383
+ # Clicks the "Accept invitation" button and waits for any Ajax calls
1384
+ # to complete
1385
+ def accept_invitation
1386
+ self.accept_invitation_button
1387
+ self.wait_for_ajax(2)
1388
+ end
1389
+
1390
+ # Clicks the message button and waits for the Pop-up dialog to appear.
1391
+ def message
1392
+ self.message_button
1393
+ self.wait_until { self.text.include? "Send this message to:" }
1394
+ self.class.class_eval { include SendMessagePopUp }
1395
+ end
1396
+
1397
+ # Clicks the "Add to contacts" button and waits for the Pop-up dialog
1398
+ # to appear.
1399
+ def add_to_contacts
1400
+ self.add_to_contacts_button
1401
+ self.wait_until { @browser.text.include? "Add a personal note to the invitation:" }
1402
+ self.class.class_eval { include AddToContactsPopUp }
1403
+ end
1404
+
1405
+ # Clicks the link to open the user's library profile page.
1406
+ def users_library
1407
+ self.link(:class=>/s3d-bold lhnavigation_toplevel lhnavigation_page_title_value/, :text=>/Library/).click
1408
+ sleep 2
1409
+ self.wait_for_ajax(2)
1410
+ self.class.class_eval { include ListWidget
1411
+ include LibraryWidget }
1412
+ end
1413
+
1414
+ # I believe this is not supported by rSmart's OAE preview
1415
+ #def users_memberships
1416
+ # @browser.link(:class=>/s3d-bold lhnavigation_toplevel lhnavigation_page_title_value/, :text=>/Memberships/).click
1417
+ # self.class.class_eval { include ListWidget }
1418
+ #end
1419
+
1420
+ # Opens the user's Contacts page to display their contacts
1421
+ def users_contacts
1422
+ self.link(:class=>/s3d-bold lhnavigation_toplevel lhnavigation_page_title_value/, :text=>/Contacts/).click
1423
+ self.class.class_eval { include ListWidget }
1424
+ end
1425
+
1426
+ # This method assumes the current page is the Basic Info page.
1427
+ # It returns a hash object where the key => value pair is:
1428
+ # "Field Title" => "Field Value" --e.g.: "Given name:"=>"Billy"
1429
+ def basic_info_data
1430
+ hash = {}
1431
+ target_div = self.div(:class=>"s3d-contentpage-title", :text=>"Basic Information").parent.parent.div(:id=>"displayprofilesection_body")
1432
+ target_div.divs(:class=>"displayprofilesection_field").each { |div| hash.store(div.span(:class=>"s3d-input-label").text, div.span(:class=>"field_value").text) }
1433
+ return hash
1434
+ end
1435
+
1436
+ # Returns an array containing the text of all of the tags and categories
1437
+ # listed on the Basic Information page. Note that it splits them up into
1438
+ # parent and child categories.
1439
+ def tags_and_categories_list
1440
+ list = []
1441
+ target_div = self.div(:class=>"s3d-contentpage-title", :text=>"Basic Information").parent.parent.div(:id=>"displayprofilesection_body")
1442
+ target_div.links.each { |link| list << link.text.split(" » ") }
1443
+ return list.flatten.uniq
1444
+ end
1445
+
1446
+ # This method assumes it will be run on the About Me page.
1447
+ # It returns a hash object where the key => value pair is:
1448
+ # "Field Title" => "Field Value" --e.g.: "About Me:"=>"Text of field"
1449
+ def about_me_data
1450
+ hash = {}
1451
+ target_div = self.div(:class=>"s3d-contentpage-title", :text=>"About Me").parent.parent.div(:id=>"displayprofilesection_body")
1452
+ target_div.divs(:class=>"displayprofilesection_field").each { |div| hash.store(div.span(:class=>"s3d-input-label").text, div.span(:class=>"field_value").text) }
1453
+ return hash
1454
+ end
1455
+
1456
+ # Returns a hash object where the key=>value pair is determined by the
1457
+ # field title and field values shown on the "Online" page.
1458
+ #
1459
+ # Because there can be an arbitrary number of Sites listed on the page,
1460
+ # The hash keys will have a number appended on the end. Example:
1461
+ # { "Site:1"=>"Twitter", "URL:1"=>"www.twitter.com","Site:2"=>"Facebook","URL:2"=>"www.facebook.com"}
1462
+ def online_data
1463
+ hash = {}
1464
+ target_div = self.div(:class=>"s3d-contentpage-title", :text=>"Online").parent.parent.div(:id=>"displayprofilesection_body")
1465
+ x=0
1466
+ target_div.divs(:class=>"displayprofilesection_field").each do |div|
1467
+ div.span(:class=>"s3d-input-label").text == "Site:" ? x+=1 : x
1468
+ hash.store("#{div.span(:class=>"s3d-input-label").text}#{x}", div.span(:class=>"field_value").text)
1469
+ end
1470
+ return hash
1471
+ end
1472
+
1473
+ # Returns a hash object where the key=>value pair is determined by the
1474
+ # field title and field values shown on the "Contact Information" page.
1475
+ #
1476
+ # Example: "Institution:"=>"University of Hard Knocks"
1477
+ def contact_info_data
1478
+ hash = {}
1479
+ target_div = self.div(:class=>"s3d-contentpage-title", :text=>"Contact Information").parent.parent.div(:id=>"displayprofilesection_body")
1480
+ target_div.divs(:class=>"displayprofilesection_field").each { |div| hash.store(div.span(:class=>"s3d-input-label").text, div.span(:class=>"field_value").text) }
1481
+ return hash
1482
+ end
1483
+
1484
+ # Takes a hash object containing the test contact info (see required format below),
1485
+ # evaluates that data against the data returned with the contact_info_data
1486
+ # method and returns true if the data match, and false, if they do not.
1487
+ #
1488
+ # The hash object passed to the method must contain the following keys, exactly:
1489
+ # :institution, :department, :title, :email, :im, :phone, :mobile, :fax,
1490
+ # :address, :city, :state, :zip, :country
1491
+ def expected_contact_info?(hash)
1492
+ info = self.contact_info_data
1493
+ key_map = {"Institution:"=>:institution, "Department:"=>:department, "Title/Role:"=>:title,
1494
+ "Email:"=>:email, "Instant Messaging:"=>:im, "Phone:"=>:phone, "Mobile:"=>:mobile, "Fax:"=>:fax,
1495
+ "Address:"=>:address, "City:"=>:city, "State:"=>:state, "Postal Code:"=>:zip, "Country:"=>:country}
1496
+ fixed = Hash[info.map { |k, v| [key_map[k], v ] } ]
1497
+ fixed==hash ? true : false
1498
+ end
1499
+
1500
+ # Returns an array of hashes. Each hash in the array refers to one of
1501
+ # the listed publications.
1502
+ #
1503
+ # Each hash's key=>value pairs are determined by the field title and field values for the publications.
1504
+ #
1505
+ # Example: "Main title:"=>"War and Peace","Main author:"=>"Tolstoy", etc....
1506
+ def publications_data
1507
+ list = []
1508
+ hash = {}
1509
+ self.div(:class=>"publications").divs(:class=>"displayprofilesection_field").each do |div|
1510
+ if div.span(:class=>"s3d-input-label").text=="Main title:" && hash != {}
1511
+ list << hash
1512
+ hash={}
1513
+ end
1514
+ hash.store(div.span(:class=>"s3d-input-label").text, div.span(:class=>"field_value").text)
1515
+ end
1516
+ list << hash
1517
+ return list
1518
+ end
1519
+ alias publication_data publications_data
1520
+ alias publications_list publications_data
1521
+
1522
+ # Expects to be passed an array containing one or more hashes (see below for
1523
+ # how the hash should be formed). Note that it's okay to send a single hash instead
1524
+ # of an array containing one hash element. The method will do the
1525
+ # necessary cleaning of the data.
1526
+ #
1527
+ # The method returns true if the contents of the hash(es) match the data evaluated against,
1528
+ # otherwise returns false.
1529
+ # The hash(es) should contain all of the following keys...
1530
+ # :main_title, main_author, :co_authors, :publisher, :place, :volume_title,
1531
+ # :volume_info, :year, :number, :series, :url
1532
+ def expected_publications_data?(array_of_hash)
1533
+ expected_data=[]
1534
+ expected_data << array_of_hash
1535
+ expected_data.flatten!
1536
+ data_array = self.publications_data
1537
+ new_array = []
1538
+ key_map = { "Main title:"=>:main_title, "Main author:"=>:main_author,
1539
+ "Co-author(s):"=>:co_authors, "Publisher:"=>:publisher, "Place of publication:"=>:place,
1540
+ "Volume title:"=>:volume_title, "Volume information:"=>:volume_info,
1541
+ "Year:"=>:year, "Number:"=>:number, "Series title:"=>:series, "URL:"=>:url }
1542
+ data_array.each do |hash|
1543
+ fixed = Hash[hash.map { |k, v| [key_map[k], v ] } ]
1544
+ new_array << fixed
1545
+ end
1546
+ expected_data==new_array ? true : false
1547
+ end
1548
+
1549
+ end
1550
+
1551
+ # TODO - describe class
1552
+ class MyPreferences
1553
+
1554
+ include PageObject
1555
+ include GlobalMethods
1556
+
1557
+ end
1558
+
1559
+ # Library pages in Courses/Groups/Research
1560
+ class Library
1561
+
1562
+ include PageObject
1563
+ include GlobalMethods
1564
+ include HeaderFooterBar
1565
+ include HeaderBar
1566
+ include LeftMenuBar
1567
+ include LibraryWidget
1568
+ include ListWidget
1569
+ include ListContent
1570
+
1571
+ # The page element that appears when there are no contents
1572
+ # in the Library
1573
+ def empty_library_element
1574
+ self.div(:id=>data_sakai_ref).div(:id=>"mylibrary_empty")
1575
+ end
1576
+
1577
+ end
1578
+
1579
+ #
1580
+ class Participants
1581
+
1582
+ include PageObject
1583
+ include GlobalMethods
1584
+ include HeaderFooterBar
1585
+ include LeftMenuBar
1586
+ include HeaderBar
1587
+
1588
+ text_field(:search_participants, :id=>"participants_search_field")
1589
+
1590
+ end
1591
+
1592
+ # Methods related to the Discussions "Area" in a Group/Course.
1593
+ class Discussions
1594
+
1595
+ include PageObject
1596
+ include GlobalMethods
1597
+ include HeaderFooterBar
1598
+ include LeftMenuBar
1599
+ include HeaderBar
1600
+ include DocButtons
1601
+
1602
+ button(:add_new_topic, :id=>"discussion_add_new_topic")
1603
+ text_field(:topic_title, :id=>"discussion_create_new_topic_title")
1604
+ text_area(:message_text, :name=>/message.text/i)
1605
+ text_area(:reply_text, :id=>"discussion_topic_reply_text")
1606
+ button(:dont_add_topic, :id=>"discussion_dont_add_topic")
1607
+
1608
+ # Clicks the "Add topic" button.
1609
+ def add_topic
1610
+ self.button(:id=>"discussion_add_topic").click
1611
+ self.wait_for_ajax(2) #
1612
+ #@browser.wait_until { @browser.h1(:class=>"discussion_topic_subject").parent.button(:text=>"Reply").present? }
1613
+ end
1614
+
1615
+ button(:collapse_all, :text=>"Collapse all")
1616
+ button(:expand_all, :text=>"Expand all")
1617
+ button(:dont_add_reply, :id=>"discussion_dont_add_reply")
1618
+ button(:add_reply, :id=>"discussion_add_reply")
1619
+
1620
+ # Clicks the "Reply" button for the specified message.
1621
+ def reply_to(message_title)
1622
+ self.h1(:class=>"discussion_topic_subject", :text=>message_title).parent.button(:text=>"Reply").click
1623
+ self.wait_until { self.h1(:class=>"discussion_topic_subject", :text=>message_title).parent.text_field(:id=>"discussion_topic_reply_text").present? }
1624
+ end
1625
+
1626
+ # Clicks the "Quote" button for the specified message.
1627
+ def quote(message_text)
1628
+ self.span(:class=>"discussion_post_message", :text=>message_text).parent.parent.button(:text=>"Quote").fire_event "onclick"
1629
+ self.wait_for_ajax(2) #
1630
+ #self.wait_until { self.textarea(:name=>"quoted_text", :text=>message_text).present? }
1631
+ end
1632
+
1633
+ # Clicks the "Edit" button for the specified message.
1634
+ def edit(message_text)
1635
+ self.span(:class=>"discussion_post_message", :text=>message_text).parent.parent.button(:text=>"Edit").click
1636
+ self.wait_until { self.textarea(:name=>"discussion_topic_reply_text", :text=>message_text).present? }
1637
+ end
1638
+
1639
+ # Clicks the "Delete" button for a specified message.
1640
+ def delete(message_text)
1641
+ self.span(:class=>"discussion_post_message", :text=>message_text).parent.parent.button(:text=>"Delete").click
1642
+ end
1643
+
1644
+ button(:restore, :text=>"Restore")
1645
+
1646
+ # Clicks the button that expands the thread to view the replies.
1647
+ def view_replies(topic_title)
1648
+ self.h1(:class=>"discussion_topic_subject", :text=>topic_title).parent.button(:class=>"discussion_show_topic_replies s3d-button s3d-link-button").click
1649
+
1650
+ end
1651
+
1652
+ # Clicks the button that collapses an expanded message thread.
1653
+ def hide_replies(topic_title)
1654
+ self.h1(:class=>"discussion_topic_subject", :text=>topic_title).parent.button(:class=>"discussion_show_topic_replies s3d-button s3d-link-button").click
1655
+
1656
+ end
1657
+
1658
+ end
1659
+
1660
+ # Methods related to the Comments "Area" in a Course/Group.
1661
+ class Comments
1662
+
1663
+ include PageObject
1664
+ include GlobalMethods
1665
+ include HeaderFooterBar
1666
+ include LeftMenuBar
1667
+ include HeaderBar
1668
+ include DocButtons
1669
+
1670
+ button(:add_comment, :text=>"Add comment")
1671
+ button(:cancel, :id=>/comments_editComment_cancel/)
1672
+ text_area(:comment, :id=>"comments_txtMessage")
1673
+ text_area(:new_comment, :id=>/comments_editComment_txt_/)
1674
+ button(:undelete, :text=>"Undelete")
1675
+
1676
+ # Clicks the "Submit comment" button.
1677
+ def submit_comment
1678
+ self.button(:text=>"Submit comment").click
1679
+ self.wait_for_ajax(2) #wait_until { self.text.include? "about 0 seconds ago" }
1680
+ end
1681
+
1682
+ # Clicks the "Edit comment" button.
1683
+ def edit_comment
1684
+ self.button(:text=>"Edit comment").click
1685
+ self.wait_for_ajax(2) #wait_until { self.textarea(:title=>"Edit your comment").present? == false }
1686
+ end
1687
+
1688
+ # Clicks the "Edit button" for the specified comment.
1689
+ def edit(comment)
1690
+ comment.gsub!("\n", " ")
1691
+ self.p(:text=>comment).parent.parent.button(:text=>"Edit").click
1692
+ self.wait_for_ajax(2) #wait_until { self.textarea(:title=>"Edit your comment").present? }
1693
+ end
1694
+
1695
+ # Deletes the specified comment.
1696
+ def delete(comment)
1697
+ comment.gsub!("\n", " ")
1698
+ self.div(:text=>comment).parent.button(:text=>"Delete").click
1699
+ self.wait_for_ajax(2) #wait_until { self.button(:text=>"Undelete").present? }
1700
+ end
1701
+
1702
+ end
1703
+
1704
+ # TODO - describe class
1705
+ class RSS
1706
+
1707
+ include PageObject
1708
+ include GlobalMethods
1709
+ include HeaderFooterBar
1710
+ include LeftMenuBar
1711
+ include HeaderBar
1712
+ include DocButtons
1713
+
1714
+ button(:sort_by_source, :text=>"Sort by source")
1715
+ button(:sort_by_date, :text=>"Sort by date")
1716
+
1717
+
1718
+ end
1719
+
1720
+ # TODO - describe class
1721
+ class Tests
1722
+
1723
+ include PageObject
1724
+ include GlobalMethods
1725
+ include HeaderFooterBar
1726
+ include LeftMenuBar
1727
+ include HeaderBar
1728
+ include DocButtons
1729
+
1730
+ # The frame object that contains all of the CLE Tests and Quizzes objects
1731
+ def tests_frame
1732
+ self.frame(:src=>/sakai2samigo.launch.html/)
1733
+ end
1734
+
1735
+ end
1736
+
1737
+ # TODO - describe class
1738
+ class Files
1739
+
1740
+ include PageObject
1741
+ include GlobalMethods
1742
+ include HeaderFooterBar
1743
+ include LeftMenuBar
1744
+ include HeaderBar
1745
+ include DocButtons
1746
+
1747
+ # Edits the page, then opens the File Settings pop up.
1748
+ def files_settings
1749
+ widget_settings
1750
+ self.text_field(:class=>"as-input").when_present { self.class.class_eval { include FilesAndDocsPopUp } }
1751
+ end
1752
+
1753
+ # TODO - Describe method
1754
+ def remove_files_widget
1755
+ remove_widget
1756
+ end
1757
+
1758
+ # TODO - Describe method
1759
+ def files_wrapping
1760
+ widget_wrapping
1761
+ end
1762
+
1763
+ end
1764
+
1765
+ # Methods related to the Forum page in Courses/Groups
1766
+ class Forum
1767
+
1768
+ include GlobalMethods
1769
+ include LeftMenuBar
1770
+ include HeaderBar
1771
+ include HeaderFooterBar
1772
+ include DocButtons
1773
+
1774
+ # The frame that contains the CLE Forums objects
1775
+ def forum_frame
1776
+ self.frame(:src=>/sakai2forums.launch.html/)
1777
+ end
1778
+
1779
+ end
1780
+
1781
+ # TODO - describe class
1782
+ class Gadget
1783
+
1784
+ include PageObject
1785
+ include GlobalMethods
1786
+ include HeaderFooterBar
1787
+ include LeftMenuBar
1788
+ include HeaderBar
1789
+ include DocButtons
1790
+
1791
+ # TODO - Describe method
1792
+ def gadget_frame
1793
+ self.frame(:id=>"ggadget_remotecontent_settings_preview_frame")
1794
+ end
1795
+
1796
+
1797
+
1798
+ end
1799
+
1800
+ # TODO - describe class
1801
+ class Gradebook
1802
+
1803
+ include PageObject
1804
+ include GlobalMethods
1805
+ include HeaderFooterBar
1806
+ include LeftMenuBar
1807
+ include HeaderBar
1808
+ include DocButtons
1809
+
1810
+ # TODO - Describe method
1811
+ def gradebook_frame
1812
+ self.frame(:src=>/sakai2gradebook.launch.html/)
1813
+ end
1814
+
1815
+ end
1816
+
1817
+ # TODO - describe class
1818
+ class LTI
1819
+
1820
+ include PageObject
1821
+ include GlobalMethods
1822
+ include HeaderFooterBar
1823
+ include LeftMenuBar
1824
+ include HeaderBar
1825
+ include DocButtons
1826
+
1827
+ text_field(:url, :id=>"basiclti_settings_ltiurl")
1828
+ text_field(:key, :id=>"basiclti_settings_ltikey")
1829
+ text_field(:secret, :id=>"basiclti_settings_ltisecret")
1830
+ button(:advanced_settings, :id=>"basiclti_settings_advanced_toggle_settings")
1831
+ button(:save, :id=>"basiclti_settings_insert")
1832
+ button(:cancel, :id=>"basiclti_settings_cancel")
1833
+
1834
+
1835
+ end
1836
+
1837
+ # Methods related to the Maps "Area" in a Course/Group.
1838
+ class GoogleMaps
1839
+
1840
+ include PageObject
1841
+ include GlobalMethods
1842
+ include HeaderFooterBar
1843
+ include LeftMenuBar
1844
+ include HeaderBar
1845
+ include DocButtons
1846
+
1847
+ # Defines the Google Maps image as an object.
1848
+ # Use this for verifying the presence of any text it's supposed to
1849
+ # contain (like the specified address it's supposed to be showing).
1850
+ def map_frame
1851
+ self.frame(:id, "googlemaps_iframe_map")
1852
+ end
1853
+
1854
+ # Edits the page, then opens the Google Maps widget's Settings Dialog.
1855
+ def map_settings
1856
+ widget_settings
1857
+ self.text_field(:id=>"googlemaps_input_text_location").when_present { self.class.class_eval { include GoogleMapsPopUp } }
1858
+ end
1859
+
1860
+ # Edits the page, then removes the widget from it.
1861
+ def remove_map
1862
+ remove_widget
1863
+ end
1864
+
1865
+ # Edits the page, then opens the "Appearance" pop-up dialog.
1866
+ def map_wrapping
1867
+ widget_wrapping
1868
+ end
1869
+
1870
+ end
1871
+
1872
+ # TODO - describe class
1873
+ class Remote
1874
+
1875
+ include PageObject
1876
+ include GlobalMethods
1877
+ include HeaderFooterBar
1878
+ include LeftMenuBar
1879
+ include HeaderBar
1880
+ include DocButtons
1881
+
1882
+ # TODO - Describe method
1883
+ def remote_frame
1884
+ self.frame(:id=>"remotecontent_settings_preview_frame")
1885
+ end
1886
+
1887
+
1888
+
1889
+ end
1890
+
1891
+ # TODO - describe class
1892
+ class ResearchIntro
1893
+
1894
+ include PageObject
1895
+ include GlobalMethods
1896
+ include HeaderFooterBar
1897
+ include LeftMenuBar
1898
+ include HeaderBar
1899
+ include DocButtons
1900
+
1901
+ end
1902
+
1903
+
1904
+ # TODO - describe class
1905
+ class Acknowledgements
1906
+
1907
+ include PageObject
1908
+ include GlobalMethods
1909
+ include HeaderFooterBar
1910
+
1911
+ # Page Objects
1912
+ div(:page_title, :class=>"s3d-bold entity_plaintitle")
1913
+ link(:featured, :text=>"Featured")
1914
+ link(:ui_technologies, :text=>"UI Technologies")
1915
+ link(:back_end_technologies, :text=>"Back-end Technologies")
1916
+
1917
+ # Custom Methods
1918
+
1919
+ end
1920
+
1921
+ # TODO - describe class
1922
+ class FourOhFourPage
1923
+
1924
+ include PageObject
1925
+ include GlobalMethods
1926
+ include HeaderFooterBar
1927
+ include CommonErrorElements
1928
+
1929
+ end
1930
+
1931
+ # TODO - describe class
1932
+ class FourOhThreePage
1933
+
1934
+ include PageObject
1935
+ include GlobalMethods
1936
+ include HeaderFooterBar
1937
+ include CommonErrorElements
1938
+
1939
+ end
1940
+
1941
+ # TODO - describe class
1942
+ class FourOhFourPage
1943
+
1944
+ include PageObject
1945
+ include GlobalMethods
1946
+ include HeaderFooterBar
1947
+ include CommonErrorElements
1948
+
1949
+ end