selenium-webdriver 0.0.24 → 0.0.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. data/{common/src/rb/CHANGES → CHANGES} +13 -0
  2. data/{common/src/rb/README → README} +1 -1
  3. data/{common/src/rb/lib → lib}/selenium-webdriver.rb +0 -0
  4. data/{common/src/rb/lib → lib}/selenium/webdriver.rb +2 -1
  5. data/{common/src/rb/lib → lib}/selenium/webdriver/bridge_helper.rb +0 -0
  6. data/{common/src/rb/lib → lib}/selenium/webdriver/child_process.rb +0 -0
  7. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome.rb +0 -0
  8. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome/bridge.rb +2 -2
  9. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome/command_executor.rb +0 -0
  10. data/lib/selenium/webdriver/chrome/extension.zip +0 -0
  11. data/{chrome/src/rb/lib → lib}/selenium/webdriver/chrome/launcher.rb +26 -20
  12. data/{common/src/rb/lib → lib}/selenium/webdriver/core_ext/dir.rb +0 -0
  13. data/{common/src/rb/lib → lib}/selenium/webdriver/core_ext/string.rb +0 -0
  14. data/{common/src/rb/lib → lib}/selenium/webdriver/driver.rb +0 -0
  15. data/{common/src/rb/lib → lib}/selenium/webdriver/driver_extensions/takes_screenshot.rb +0 -0
  16. data/{common/src/rb/lib → lib}/selenium/webdriver/element.rb +0 -0
  17. data/{common/src/rb/lib → lib}/selenium/webdriver/error.rb +0 -0
  18. data/{common/src/rb/lib → lib}/selenium/webdriver/file_reaper.rb +2 -1
  19. data/{common/src/rb/lib → lib}/selenium/webdriver/find.rb +0 -0
  20. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox.rb +2 -1
  21. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/binary.rb +32 -6
  22. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/bridge.rb +0 -0
  23. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  24. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/launcher.rb +0 -0
  25. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  26. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  27. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/profile.rb +37 -72
  28. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/profiles_ini.rb +0 -0
  29. data/{firefox/src/rb/lib → lib}/selenium/webdriver/firefox/util.rb +0 -0
  30. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie.rb +2 -2
  31. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie/bridge.rb +7 -2
  32. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie/lib.rb +0 -0
  33. data/{jobbie/prebuilt/Win32/Release → lib/selenium/webdriver/ie/native/win32}/InternetExplorerDriver.dll +0 -0
  34. data/lib/selenium/webdriver/ie/native/x64/InternetExplorerDriver.dll +0 -0
  35. data/{jobbie/src/rb/lib → lib}/selenium/webdriver/ie/util.rb +0 -0
  36. data/{common/src/rb/lib → lib}/selenium/webdriver/keys.rb +0 -0
  37. data/{common/src/rb/lib → lib}/selenium/webdriver/navigation.rb +0 -0
  38. data/{common/src/rb/lib → lib}/selenium/webdriver/options.rb +0 -0
  39. data/{common/src/rb/lib → lib}/selenium/webdriver/platform.rb +5 -1
  40. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote.rb +0 -0
  41. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/bridge.rb +0 -0
  42. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/capabilities.rb +0 -0
  43. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/commands.rb +0 -0
  44. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/http/common.rb +0 -0
  45. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/http/curb.rb +0 -0
  46. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/http/default.rb +5 -1
  47. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/response.rb +0 -0
  48. data/{remote/client/src/rb/lib → lib}/selenium/webdriver/remote/server_error.rb +0 -0
  49. data/{common/src/rb/lib → lib}/selenium/webdriver/target_locator.rb +0 -0
  50. data/{common/src/rb/lib → lib}/selenium/webdriver/timeouts.rb +0 -0
  51. data/lib/selenium/webdriver/zip_helper.rb +30 -0
  52. metadata +73 -214
  53. data/COPYING +0 -204
  54. data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
  55. data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
  56. data/chrome/src/extension/background.html +0 -9
  57. data/chrome/src/extension/background.js +0 -1029
  58. data/chrome/src/extension/content_script.js +0 -1488
  59. data/chrome/src/extension/icons/busy.png +0 -0
  60. data/chrome/src/extension/icons/free.png +0 -0
  61. data/chrome/src/extension/manifest-nonwin.json +0 -19
  62. data/chrome/src/extension/manifest-win.json +0 -20
  63. data/chrome/src/extension/utils.js +0 -231
  64. data/common/src/js/abstractcommandprocessor.js +0 -132
  65. data/common/src/js/asserts.js +0 -296
  66. data/common/src/js/by.js +0 -149
  67. data/common/src/js/command.js +0 -380
  68. data/common/src/js/core/Blank.html +0 -7
  69. data/common/src/js/core/InjectedRemoteRunner.html +0 -8
  70. data/common/src/js/core/RemoteRunner.html +0 -101
  71. data/common/src/js/core/SeleniumLog.html +0 -109
  72. data/common/src/js/core/TestPrompt.html +0 -145
  73. data/common/src/js/core/TestRunner-splash.html +0 -55
  74. data/common/src/js/core/TestRunner.html +0 -165
  75. data/common/src/js/core/icons/all.png +0 -0
  76. data/common/src/js/core/icons/continue.png +0 -0
  77. data/common/src/js/core/icons/continue_disabled.png +0 -0
  78. data/common/src/js/core/icons/pause.png +0 -0
  79. data/common/src/js/core/icons/pause_disabled.png +0 -0
  80. data/common/src/js/core/icons/selected.png +0 -0
  81. data/common/src/js/core/icons/step.png +0 -0
  82. data/common/src/js/core/icons/step_disabled.png +0 -0
  83. data/common/src/js/core/lib/cssQuery/cssQuery-p.js +0 -6
  84. data/common/src/js/core/lib/cssQuery/src/cssQuery-level2.js +0 -142
  85. data/common/src/js/core/lib/cssQuery/src/cssQuery-level3.js +0 -150
  86. data/common/src/js/core/lib/cssQuery/src/cssQuery-standard.js +0 -53
  87. data/common/src/js/core/lib/cssQuery/src/cssQuery.js +0 -356
  88. data/common/src/js/core/lib/prototype.js +0 -2006
  89. data/common/src/js/core/lib/scriptaculous/builder.js +0 -101
  90. data/common/src/js/core/lib/scriptaculous/controls.js +0 -815
  91. data/common/src/js/core/lib/scriptaculous/dragdrop.js +0 -915
  92. data/common/src/js/core/lib/scriptaculous/effects.js +0 -958
  93. data/common/src/js/core/lib/scriptaculous/scriptaculous.js +0 -47
  94. data/common/src/js/core/lib/scriptaculous/slider.js +0 -283
  95. data/common/src/js/core/lib/scriptaculous/unittest.js +0 -383
  96. data/common/src/js/core/lib/snapsie.js +0 -91
  97. data/common/src/js/core/scripts/find_matching_child.js +0 -69
  98. data/common/src/js/core/scripts/htmlutils.js +0 -8716
  99. data/common/src/js/core/scripts/injection.html +0 -72
  100. data/common/src/js/core/scripts/selenium-api.js +0 -3291
  101. data/common/src/js/core/scripts/selenium-browserbot.js +0 -2457
  102. data/common/src/js/core/scripts/selenium-browserdetect.js +0 -153
  103. data/common/src/js/core/scripts/selenium-commandhandlers.js +0 -379
  104. data/common/src/js/core/scripts/selenium-executionloop.js +0 -175
  105. data/common/src/js/core/scripts/selenium-logging.js +0 -148
  106. data/common/src/js/core/scripts/selenium-remoterunner.js +0 -695
  107. data/common/src/js/core/scripts/selenium-testrunner.js +0 -1362
  108. data/common/src/js/core/scripts/selenium-version.js +0 -5
  109. data/common/src/js/core/scripts/ui-doc.html +0 -808
  110. data/common/src/js/core/scripts/ui-element.js +0 -1644
  111. data/common/src/js/core/scripts/ui-map-sample.js +0 -979
  112. data/common/src/js/core/scripts/user-extensions.js +0 -3
  113. data/common/src/js/core/scripts/user-extensions.js.sample +0 -75
  114. data/common/src/js/core/scripts/xmlextras.js +0 -153
  115. data/common/src/js/core/selenium-logo.png +0 -0
  116. data/common/src/js/core/selenium-test.css +0 -43
  117. data/common/src/js/core/selenium.css +0 -316
  118. data/common/src/js/core/xpath/dom.js +0 -566
  119. data/common/src/js/core/xpath/javascript-xpath-0.1.11.js +0 -2816
  120. data/common/src/js/core/xpath/util.js +0 -549
  121. data/common/src/js/core/xpath/xmltoken.js +0 -149
  122. data/common/src/js/core/xpath/xpath.js +0 -2481
  123. data/common/src/js/extension/README +0 -2
  124. data/common/src/js/extension/dommessenger.js +0 -152
  125. data/common/src/js/factory.js +0 -55
  126. data/common/src/js/future.js +0 -141
  127. data/common/src/js/jsunit.js +0 -40
  128. data/common/src/js/jsunit/app/css/jsUnitStyle.css +0 -50
  129. data/common/src/js/jsunit/app/css/readme +0 -10
  130. data/common/src/js/jsunit/app/emptyPage.html +0 -11
  131. data/common/src/js/jsunit/app/jsUnitCore.js +0 -534
  132. data/common/src/js/jsunit/app/jsUnitMockTimeout.js +0 -81
  133. data/common/src/js/jsunit/app/jsUnitTestManager.js +0 -705
  134. data/common/src/js/jsunit/app/jsUnitTestSuite.js +0 -44
  135. data/common/src/js/jsunit/app/jsUnitTracer.js +0 -102
  136. data/common/src/js/jsunit/app/jsUnitVersionCheck.js +0 -59
  137. data/common/src/js/jsunit/app/main-counts-errors.html +0 -12
  138. data/common/src/js/jsunit/app/main-counts-failures.html +0 -13
  139. data/common/src/js/jsunit/app/main-counts-runs.html +0 -13
  140. data/common/src/js/jsunit/app/main-counts.html +0 -21
  141. data/common/src/js/jsunit/app/main-data.html +0 -178
  142. data/common/src/js/jsunit/app/main-errors.html +0 -23
  143. data/common/src/js/jsunit/app/main-frame.html +0 -19
  144. data/common/src/js/jsunit/app/main-loader.html +0 -45
  145. data/common/src/js/jsunit/app/main-progress.html +0 -25
  146. data/common/src/js/jsunit/app/main-results.html +0 -67
  147. data/common/src/js/jsunit/app/main-status.html +0 -13
  148. data/common/src/js/jsunit/app/testContainer.html +0 -16
  149. data/common/src/js/jsunit/app/testContainerController.html +0 -77
  150. data/common/src/js/jsunit/app/xbDebug.js +0 -306
  151. data/common/src/js/jsunit/changelog.txt +0 -60
  152. data/common/src/js/jsunit/css/jsUnitStyle.css +0 -83
  153. data/common/src/js/jsunit/images/green.gif +0 -0
  154. data/common/src/js/jsunit/images/logo_jsunit.gif +0 -0
  155. data/common/src/js/jsunit/images/powerby-transparent.gif +0 -0
  156. data/common/src/js/jsunit/images/red.gif +0 -0
  157. data/common/src/js/jsunit/licenses/JDOM_license.txt +0 -56
  158. data/common/src/js/jsunit/licenses/Jetty_license.html +0 -213
  159. data/common/src/js/jsunit/licenses/MPL-1.1.txt +0 -470
  160. data/common/src/js/jsunit/licenses/gpl-2.txt +0 -340
  161. data/common/src/js/jsunit/licenses/index.html +0 -141
  162. data/common/src/js/jsunit/licenses/lgpl-2.1.txt +0 -504
  163. data/common/src/js/jsunit/licenses/mpl-tri-license-c.txt +0 -35
  164. data/common/src/js/jsunit/licenses/mpl-tri-license-html.txt +0 -35
  165. data/common/src/js/jsunit/readme.txt +0 -19
  166. data/common/src/js/jsunit/testRunner.html +0 -167
  167. data/common/src/js/jsunit/version.txt +0 -1
  168. data/common/src/js/key.js +0 -117
  169. data/common/src/js/localcommandprocessor.js +0 -171
  170. data/common/src/js/testcase.js +0 -219
  171. data/common/src/js/timing.js +0 -89
  172. data/common/src/js/webdriver.js +0 -860
  173. data/common/src/js/webelement.js +0 -483
  174. data/firefox/prebuilt/Win32/Release/webdriver-firefox.dll +0 -0
  175. data/firefox/prebuilt/amd64/libnoblur64.so +0 -0
  176. data/firefox/prebuilt/i386/libnoblur.so +0 -0
  177. data/firefox/prebuilt/linux/Release/libwebdriver-firefox.so +0 -0
  178. data/firefox/prebuilt/linux64/Release/libwebdriver-firefox.so +0 -0
  179. data/firefox/prebuilt/nsICommandProcessor.xpt +0 -0
  180. data/firefox/prebuilt/nsINativeEvents.xpt +0 -0
  181. data/firefox/prebuilt/nsIResponseHandler.xpt +0 -0
  182. data/firefox/src/extension/chrome.manifest +0 -3
  183. data/firefox/src/extension/components/badCertListener.js +0 -295
  184. data/firefox/src/extension/components/dispatcher.js +0 -495
  185. data/firefox/src/extension/components/driver-component.js +0 -130
  186. data/firefox/src/extension/components/errorcode.js +0 -70
  187. data/firefox/src/extension/components/firefoxDriver.js +0 -831
  188. data/firefox/src/extension/components/json2.js +0 -273
  189. data/firefox/src/extension/components/keytest.html +0 -554
  190. data/firefox/src/extension/components/nsCommandProcessor.js +0 -684
  191. data/firefox/src/extension/components/promptService.js +0 -208
  192. data/firefox/src/extension/components/request.js +0 -219
  193. data/firefox/src/extension/components/response.js +0 -276
  194. data/firefox/src/extension/components/screenshooter.js +0 -81
  195. data/firefox/src/extension/components/session.js +0 -314
  196. data/firefox/src/extension/components/sessionstore.js +0 -226
  197. data/firefox/src/extension/components/socketListener.js +0 -435
  198. data/firefox/src/extension/components/utils.js +0 -1335
  199. data/firefox/src/extension/components/webLoadingListener.js +0 -57
  200. data/firefox/src/extension/components/webdriverserver.js +0 -110
  201. data/firefox/src/extension/components/wrappedElement.js +0 -706
  202. data/firefox/src/extension/content/fxdriver.xul +0 -30
  203. data/firefox/src/extension/content/server.js +0 -95
  204. data/firefox/src/extension/idl/nsICommandProcessor.idl +0 -38
  205. data/firefox/src/extension/idl/nsIResponseHandler.idl +0 -34
  206. data/firefox/src/extension/install.rdf +0 -29
  207. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
@@ -1,1644 +0,0 @@
1
- //******************************************************************************
2
- // Globals, including constants
3
-
4
- var UI_GLOBAL = {
5
- UI_PREFIX: 'ui'
6
- , XHTML_DOCTYPE: '<!DOCTYPE html PUBLIC '
7
- + '"-//W3C//DTD XHTML 1.0 Strict//EN" '
8
- + '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
9
- , XHTML_XMLNS: 'http://www.w3.org/1999/xhtml'
10
- };
11
-
12
- //*****************************************************************************
13
- // Exceptions
14
-
15
- function UIElementException(message)
16
- {
17
- this.message = message;
18
- this.name = 'UIElementException';
19
- }
20
-
21
- function UIArgumentException(message)
22
- {
23
- this.message = message;
24
- this.name = 'UIArgumentException';
25
- }
26
-
27
- function PagesetException(message)
28
- {
29
- this.message = message;
30
- this.name = 'PagesetException';
31
- }
32
-
33
- function UISpecifierException(message)
34
- {
35
- this.message = message;
36
- this.name = 'UISpecifierException';
37
- }
38
-
39
- function CommandMatcherException(message)
40
- {
41
- this.message = message;
42
- this.name = 'CommandMatcherException';
43
- }
44
-
45
- //*****************************************************************************
46
- // UI-Element core
47
-
48
- /**
49
- * The UIElement object. This has been crafted along with UIMap to make
50
- * specifying UI elements using JSON as simple as possible. Object construction
51
- * will fail if 1) a proper name isn't provided, 2) a faulty args argument is
52
- * given, or 3) getLocator() returns undefined for a valid permutation of
53
- * default argument values. See ui-doc.html for the documentation on the
54
- * builder syntax.
55
- *
56
- * @param uiElementShorthand an object whose contents conform to the
57
- * UI-Element builder syntax.
58
- *
59
- * @return a new UIElement object
60
- * @throws UIElementException
61
- */
62
- function UIElement(uiElementShorthand)
63
- {
64
- // a shorthand object might look like:
65
- //
66
- // {
67
- // name: 'topic'
68
- // , description: 'sidebar links to topic categories'
69
- // , args: [
70
- // {
71
- // name: 'name'
72
- // , description: 'the name of the topic'
73
- // , defaultValues: topLevelTopics
74
- // }
75
- // ]
76
- // , getLocator: function(args) {
77
- // return this._listXPath +
78
- // "/a[text()=" + args.name.quoteForXPath() + "]";
79
- // }
80
- // , getGenericLocator: function() {
81
- // return this._listXPath + '/a';
82
- // }
83
- // // maintain testcases for getLocator()
84
- // , testcase1: {
85
- // // defaultValues used if args not specified
86
- // args: { name: 'foo' }
87
- // , xhtml: '<div id="topiclist">'
88
- // + '<ul><li><a expected-result="1">foo</a></li></ul>'
89
- // + '</div>'
90
- // }
91
- // // set a local element variable
92
- // , _listXPath: "//div[@id='topiclist']/ul/li"
93
- // }
94
- //
95
- // name cannot be null or an empty string. Enforce the same requirement for
96
- // the description.
97
-
98
- /**
99
- * Recursively returns all permutations of argument-value pairs, given
100
- * a list of argument definitions. Each argument definition will have
101
- * a set of default values to use in generating said pairs. If an argument
102
- * has no default values defined, it will not be included among the
103
- * permutations.
104
- *
105
- * @param args a list of UIArguments
106
- * @param inDocument the document object to pass to the getDefaultValues()
107
- * method of each argument.
108
- *
109
- * @return a list of associative arrays containing key value pairs
110
- */
111
- this.permuteArgs = function(args, inDocument) {
112
- if (args.length == 0) {
113
- return [];
114
- }
115
-
116
- var permutations = [];
117
- var arg = args[0];
118
- var remainingArgs = args.slice(1);
119
- var subsequentPermutations = this.permuteArgs(remainingArgs,
120
- inDocument);
121
- var defaultValues = arg.getDefaultValues(inDocument);
122
-
123
- // skip arguments for which no default values are defined. If the
124
- // argument is a required one, then no permutations are possible.
125
- if (defaultValues.length == 0) {
126
- if (arg.required) {
127
- return [];
128
- }
129
- else {
130
- return subsequentPermutations;
131
- }
132
- }
133
-
134
- for (var i = 0; i < defaultValues.length; ++i) {
135
- var value = defaultValues[i];
136
- var permutation;
137
-
138
- if (subsequentPermutations.length == 0) {
139
- permutation = {};
140
- permutation[arg.name] = value + "";
141
- permutations.push(permutation);
142
- }
143
- else {
144
- for (var j = 0; j < subsequentPermutations.length; ++j) {
145
- permutation = clone(subsequentPermutations[j]);
146
- permutation[arg.name] = value + "";
147
- permutations.push(permutation);
148
- }
149
- }
150
- }
151
-
152
- return permutations;
153
- }
154
-
155
-
156
-
157
- /**
158
- * Returns a list of all testcases for this UIElement.
159
- */
160
- this.getTestcases = function()
161
- {
162
- return this.testcases;
163
- }
164
-
165
-
166
-
167
- /**
168
- * Run all unit tests, stopping at the first failure, if any. Return true
169
- * if no failures encountered, false otherwise. See the following thread
170
- * regarding use of getElementById() on XML documents created by parsing
171
- * text via the DOMParser:
172
- *
173
- * http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/2b1b82b3c53a1282/
174
- */
175
- this.test = function()
176
- {
177
- var parser = new DOMParser();
178
- var testcases = this.getTestcases();
179
- testcaseLoop: for (var i = 0; i < testcases.length; ++i) {
180
- var testcase = testcases[i];
181
- var xhtml = UI_GLOBAL.XHTML_DOCTYPE + '<html xmlns="'
182
- + UI_GLOBAL.XHTML_XMLNS + '">' + testcase.xhtml + '</html>';
183
- var doc = parser.parseFromString(xhtml, "text/xml");
184
- if (doc.firstChild.nodeName == 'parsererror') {
185
- safe_alert('Error parsing XHTML in testcase "' + testcase.name
186
- + '" for UI element "' + this.name + '": ' + "\n"
187
- + doc.firstChild.firstChild.nodeValue);
188
- }
189
-
190
- // we're no longer using the default locators when testing, because
191
- // args is now required
192
- var locator = parse_locator(this.getLocator(testcase.args));
193
- var results;
194
- if (locator.type == 'xpath' || (locator.type == 'implicit' &&
195
- locator.string.substring(0, 2) == '//')) {
196
- // try using the javascript xpath engine to avoid namespace
197
- // issues. The xpath does have to be lowercase however, it
198
- // seems.
199
- results = eval_xpath(locator.string, doc,
200
- { allowNativeXpath: false, returnOnFirstMatch: true });
201
- }
202
- else {
203
- // piece the locator back together
204
- locator = (locator.type == 'implicit')
205
- ? locator.string
206
- : locator.type + '=' + locator.string;
207
- results = eval_locator(locator, doc);
208
- }
209
- if (results.length && results[0].hasAttribute('expected-result')) {
210
- continue testcaseLoop;
211
- }
212
-
213
- // testcase failed
214
- if (is_IDE()) {
215
- var msg = 'Testcase "' + testcase.name
216
- + '" failed for UI element "' + this.name + '":';
217
- if (!results.length) {
218
- msg += '\n"' + locator + '" did not match any elements!';
219
- }
220
- else {
221
- msg += '\n' + results[0] + ' was not the expected result!';
222
- }
223
- safe_alert(msg);
224
- }
225
- return false;
226
- }
227
- return true;
228
- };
229
-
230
-
231
-
232
- /**
233
- * Creates a set of locators using permutations of default values for
234
- * arguments used in the locator construction. The set is returned as an
235
- * object mapping locators to key-value arguments objects containing the
236
- * values passed to getLocator() to create the locator.
237
- *
238
- * @param opt_inDocument (optional) the document object of the "current"
239
- * page when this method is invoked. Some arguments
240
- * may have default value lists that are calculated
241
- * based on the contents of the page.
242
- *
243
- * @return a list of locator strings
244
- * @throws UIElementException
245
- */
246
- this.getDefaultLocators = function(opt_inDocument) {
247
- var defaultLocators = {};
248
- if (this.args.length == 0) {
249
- defaultLocators[this.getLocator({})] = {};
250
- }
251
- else {
252
- var permutations = this.permuteArgs(this.args, opt_inDocument);
253
- if (permutations.length != 0) {
254
- for (var i = 0; i < permutations.length; ++i) {
255
- var args = permutations[i];
256
- var locator = this.getLocator(args);
257
- if (!locator) {
258
- throw new UIElementException('Error in UIElement(): '
259
- + 'no getLocator return value for element "' + name
260
- + '"');
261
- }
262
- defaultLocators[locator] = args;
263
- }
264
- }
265
- else {
266
- // try using no arguments. Parse the locator to make sure it's
267
- // really good. If it doesn't work, fine.
268
- try {
269
- var locator = this.getLocator();
270
- parse_locator(locator);
271
- defaultLocators[locator] = {};
272
- }
273
- catch (e) {
274
- safe_log('debug', e.message);
275
- }
276
- }
277
- }
278
- return defaultLocators;
279
- };
280
-
281
-
282
-
283
- /**
284
- * Validate the structure of the shorthand notation this object is being
285
- * initialized with. Throws an exception if there's a validation error.
286
- *
287
- * @param uiElementShorthand
288
- *
289
- * @throws UIElementException
290
- */
291
- this.validate = function(uiElementShorthand)
292
- {
293
- var msg = "UIElement validation error:\n" + print_r(uiElementShorthand);
294
- if (!uiElementShorthand.name) {
295
- throw new UIElementException(msg + 'no name specified!');
296
- }
297
- if (!uiElementShorthand.description) {
298
- throw new UIElementException(msg + 'no description specified!');
299
- }
300
- if (!uiElementShorthand.locator
301
- && !uiElementShorthand.getLocator
302
- && !uiElementShorthand.xpath
303
- && !uiElementShorthand.getXPath) {
304
- throw new UIElementException(msg + 'no locator specified!');
305
- }
306
- };
307
-
308
-
309
-
310
- this.init = function(uiElementShorthand)
311
- {
312
- this.validate(uiElementShorthand);
313
-
314
- this.name = uiElementShorthand.name;
315
- this.description = uiElementShorthand.description;
316
-
317
- // construct a new getLocator() method based on the locator property,
318
- // or use the provided function. We're deprecating the xpath property
319
- // and getXPath() function, but still allow for them for backwards
320
- // compatability.
321
- if (uiElementShorthand.locator) {
322
- this.getLocator = function(args) {
323
- return uiElementShorthand.locator;
324
- };
325
- }
326
- else if (uiElementShorthand.getLocator) {
327
- this.getLocator = uiElementShorthand.getLocator;
328
- }
329
- else if (uiElementShorthand.xpath) {
330
- this.getLocator = function(args) {
331
- return uiElementShorthand.xpath;
332
- };
333
- }
334
- else {
335
- this.getLocator = uiElementShorthand.getXPath;
336
- }
337
-
338
- if (uiElementShorthand.genericLocator) {
339
- this.getGenericLocator = function() {
340
- return uiElementShorthand.genericLocator;
341
- };
342
- }
343
- else if (uiElementShorthand.getGenericLocator) {
344
- this.getGenericLocator = uiElementShorthand.getGenericLocator;
345
- }
346
-
347
- if (uiElementShorthand.getOffsetLocator) {
348
- this.getOffsetLocator = uiElementShorthand.getOffsetLocator;
349
- }
350
-
351
- // get the testcases and local variables
352
- this.testcases = [];
353
- var localVars = {};
354
- for (var attr in uiElementShorthand) {
355
- if (attr.match(/^testcase/)) {
356
- var testcase = uiElementShorthand[attr];
357
- if (uiElementShorthand.args &&
358
- uiElementShorthand.args.length && !testcase.args) {
359
- safe_alert('No args defined in ' + attr + ' for UI element '
360
- + this.name + '! Skipping testcase.');
361
- continue;
362
- }
363
- testcase.name = attr;
364
- this.testcases.push(testcase);
365
- }
366
- else if (attr.match(/^_/)) {
367
- this[attr] = uiElementShorthand[attr];
368
- localVars[attr] = uiElementShorthand[attr];
369
- }
370
- }
371
-
372
- // create the arguments
373
- this.args = []
374
- this.argsOrder = [];
375
- if (uiElementShorthand.args) {
376
- for (var i = 0; i < uiElementShorthand.args.length; ++i) {
377
- var arg = new UIArgument(uiElementShorthand.args[i], localVars);
378
- this.args.push(arg);
379
- this.argsOrder.push(arg.name);
380
-
381
- // if an exception is thrown when invoking getDefaultValues()
382
- // with no parameters passed in, assume the method requires an
383
- // inDocument parameter, and thus may only be invoked at run
384
- // time. Mark the UI element object accordingly.
385
- try {
386
- arg.getDefaultValues();
387
- }
388
- catch (e) {
389
- this.isDefaultLocatorConstructionDeferred = true;
390
- }
391
- }
392
-
393
- }
394
-
395
- if (!this.isDefaultLocatorConstructionDeferred) {
396
- this.defaultLocators = this.getDefaultLocators();
397
- }
398
- };
399
-
400
-
401
-
402
- this.init(uiElementShorthand);
403
- }
404
-
405
- // hang this off the UIElement "namespace". This is a composite strategy.
406
- UIElement.defaultOffsetLocatorStrategy = function(locatedElement, pageElement) {
407
- var strategies = [
408
- UIElement.linkXPathOffsetLocatorStrategy
409
- , UIElement.preferredAttributeXPathOffsetLocatorStrategy
410
- , UIElement.simpleXPathOffsetLocatorStrategy
411
- ];
412
-
413
- for (var i = 0; i < strategies.length; ++i) {
414
- var strategy = strategies[i];
415
- var offsetLocator = strategy(locatedElement, pageElement);
416
-
417
- if (offsetLocator) {
418
- return offsetLocator;
419
- }
420
- }
421
-
422
- return null;
423
- };
424
-
425
- UIElement.simpleXPathOffsetLocatorStrategy = function(locatedElement,
426
- pageElement)
427
- {
428
- if (is_ancestor(locatedElement, pageElement)) {
429
- var xpath = "";
430
- var recorder = Recorder.get(locatedElement.ownerDocument.defaultView);
431
- var locatorBuilders = recorder.locatorBuilders;
432
- var currentNode = pageElement;
433
-
434
- while (currentNode != null && currentNode != locatedElement) {
435
- xpath = locatorBuilders.relativeXPathFromParent(currentNode)
436
- + xpath;
437
- currentNode = currentNode.parentNode;
438
- }
439
-
440
- var results = eval_xpath(xpath, locatedElement.ownerDocument,
441
- { contextNode: locatedElement });
442
-
443
- if (results.length > 0 && results[0] == pageElement) {
444
- return xpath;
445
- }
446
- }
447
-
448
- return null;
449
- };
450
-
451
- UIElement.linkXPathOffsetLocatorStrategy = function(locatedElement, pageElement)
452
- {
453
- if (pageElement.nodeName == 'A' && is_ancestor(locatedElement, pageElement))
454
- {
455
- var text = pageElement.textContent
456
- .replace(/^\s+/, "")
457
- .replace(/\s+$/, "");
458
-
459
- if (text) {
460
- var xpath = '/descendant::a[normalize-space()='
461
- + text.quoteForXPath() + ']';
462
-
463
- var results = eval_xpath(xpath, locatedElement.ownerDocument,
464
- { contextNode: locatedElement });
465
-
466
- if (results.length > 0 && results[0] == pageElement) {
467
- return xpath;
468
- }
469
- }
470
- }
471
-
472
- return null;
473
- };
474
-
475
- // compare to the "xpath:attributes" locator strategy defined in the IDE source
476
- UIElement.preferredAttributeXPathOffsetLocatorStrategy =
477
- function(locatedElement, pageElement)
478
- {
479
- // this is an ordered listing of single attributes
480
- var preferredAttributes = [
481
- 'name'
482
- , 'value'
483
- , 'type'
484
- , 'action'
485
- , 'alt'
486
- , 'title'
487
- , 'class'
488
- , 'src'
489
- , 'href'
490
- , 'onclick'
491
- ];
492
-
493
- if (is_ancestor(locatedElement, pageElement)) {
494
- var xpathBase = '/descendant::' + pageElement.nodeName.toLowerCase();
495
-
496
- for (var i = 0; i < preferredAttributes.length; ++i) {
497
- var name = preferredAttributes[i];
498
- var value = pageElement.getAttribute(name);
499
-
500
- if (value) {
501
- var xpath = xpathBase + '[@' + name + '='
502
- + value.quoteForXPath() + ']';
503
-
504
- var results = eval_xpath(xpath, locatedElement.ownerDocument,
505
- { contextNode: locatedElement });
506
-
507
- if (results.length > 0 && results[0] == pageElement) {
508
- return xpath;
509
- }
510
- }
511
- }
512
- }
513
-
514
- return null;
515
- };
516
-
517
-
518
-
519
- /**
520
- * Constructs a UIArgument. This is mostly for checking that the values are
521
- * valid.
522
- *
523
- * @param uiArgumentShorthand
524
- * @param localVars
525
- *
526
- * @throws UIArgumentException
527
- */
528
- function UIArgument(uiArgumentShorthand, localVars)
529
- {
530
- /**
531
- * @param uiArgumentShorthand
532
- *
533
- * @throws UIArgumentException
534
- */
535
- this.validate = function(uiArgumentShorthand)
536
- {
537
- var msg = "UIArgument validation error:\n"
538
- + print_r(uiArgumentShorthand);
539
-
540
- // try really hard to throw an exception!
541
- if (!uiArgumentShorthand.name) {
542
- throw new UIArgumentException(msg + 'no name specified!');
543
- }
544
- if (!uiArgumentShorthand.description) {
545
- throw new UIArgumentException(msg + 'no description specified!');
546
- }
547
- if (!uiArgumentShorthand.defaultValues &&
548
- !uiArgumentShorthand.getDefaultValues) {
549
- throw new UIArgumentException(msg + 'no default values specified!');
550
- }
551
- };
552
-
553
-
554
-
555
- /**
556
- * @param uiArgumentShorthand
557
- * @param localVars a list of local variables
558
- */
559
- this.init = function(uiArgumentShorthand, localVars)
560
- {
561
- this.validate(uiArgumentShorthand);
562
-
563
- this.name = uiArgumentShorthand.name;
564
- this.description = uiArgumentShorthand.description;
565
- this.required = uiArgumentShorthand.required || false;
566
-
567
- if (uiArgumentShorthand.defaultValues) {
568
- var defaultValues = uiArgumentShorthand.defaultValues;
569
- this.getDefaultValues =
570
- function() { return defaultValues; }
571
- }
572
- else {
573
- this.getDefaultValues = uiArgumentShorthand.getDefaultValues;
574
- }
575
-
576
- for (var name in localVars) {
577
- this[name] = localVars[name];
578
- }
579
- }
580
-
581
-
582
-
583
- this.init(uiArgumentShorthand, localVars);
584
- }
585
-
586
-
587
-
588
- /**
589
- * The UISpecifier constructor is overloaded. If less than three arguments are
590
- * provided, the first argument will be considered a UI specifier string, and
591
- * will be split out accordingly. Otherwise, the first argument will be
592
- * considered the path.
593
- *
594
- * @param uiSpecifierStringOrPagesetName a UI specifier string, or the pageset
595
- * name of the UI specifier
596
- * @param elementName the name of the element
597
- * @param args an object associating keys to values
598
- *
599
- * @return new UISpecifier object
600
- */
601
- function UISpecifier(uiSpecifierStringOrPagesetName, elementName, args)
602
- {
603
- /**
604
- * Initializes this object from a UI specifier string of the form:
605
- *
606
- * pagesetName::elementName(arg1=value1, arg2=value2, ...)
607
- *
608
- * into its component parts, and returns them as an object.
609
- *
610
- * @return an object containing the components of the UI specifier
611
- * @throws UISpecifierException
612
- */
613
- this._initFromUISpecifierString = function(uiSpecifierString) {
614
- var matches = /^(.*)::([^\(]+)\((.*)\)$/.exec(uiSpecifierString);
615
- if (matches == null) {
616
- throw new UISpecifierException('Error in '
617
- + 'UISpecifier._initFromUISpecifierString(): "'
618
- + this.string + '" is not a valid UI specifier string');
619
- }
620
- this.pagesetName = matches[1];
621
- this.elementName = matches[2];
622
- this.args = (matches[3]) ? parse_kwargs(matches[3]) : {};
623
- };
624
-
625
-
626
-
627
- /**
628
- * Override the toString() method to return the UI specifier string when
629
- * evaluated in a string context. Combines the UI specifier components into
630
- * a canonical UI specifier string and returns it.
631
- *
632
- * @return a UI specifier string
633
- */
634
- this.toString = function() {
635
- // empty string is acceptable for the path, but it must be defined
636
- if (this.pagesetName == undefined) {
637
- throw new UISpecifierException('Error in UISpecifier.toString(): "'
638
- + this.pagesetName + '" is not a valid UI specifier pageset '
639
- + 'name');
640
- }
641
- if (!this.elementName) {
642
- throw new UISpecifierException('Error in UISpecifier.unparse(): "'
643
- + this.elementName + '" is not a valid UI specifier element '
644
- + 'name');
645
- }
646
- if (!this.args) {
647
- throw new UISpecifierException('Error in UISpecifier.unparse(): "'
648
- + this.args + '" are not valid UI specifier args');
649
- }
650
-
651
- uiElement = UIMap.getInstance()
652
- .getUIElement(this.pagesetName, this.elementName);
653
- if (uiElement != null) {
654
- var kwargs = to_kwargs(this.args, uiElement.argsOrder);
655
- }
656
- else {
657
- // probably under unit test
658
- var kwargs = to_kwargs(this.args);
659
- }
660
-
661
- return this.pagesetName + '::' + this.elementName + '(' + kwargs + ')';
662
- };
663
-
664
- // construct the object
665
- if (arguments.length < 2) {
666
- this._initFromUISpecifierString(uiSpecifierStringOrPagesetName);
667
- }
668
- else {
669
- this.pagesetName = uiSpecifierStringOrPagesetName;
670
- this.elementName = elementName;
671
- this.args = (args) ? clone(args) : {};
672
- }
673
- }
674
-
675
-
676
-
677
- function Pageset(pagesetShorthand)
678
- {
679
- /**
680
- * Returns true if the page is included in this pageset, false otherwise.
681
- * The page is specified by a document object.
682
- *
683
- * @param inDocument the document object representing the page
684
- */
685
- this.contains = function(inDocument)
686
- {
687
- var urlParts = parseUri(unescape(inDocument.location.href));
688
- var path = urlParts.path
689
- .replace(/^\//, "")
690
- .replace(/\/$/, "");
691
- if (!this.pathRegexp.test(path)) {
692
- return false;
693
- }
694
- for (var paramName in this.paramRegexps) {
695
- var paramRegexp = this.paramRegexps[paramName];
696
- if (!paramRegexp.test(urlParts.queryKey[paramName])) {
697
- return false;
698
- }
699
- }
700
- if (!this.pageContent(inDocument)) {
701
- return false;
702
- }
703
-
704
- return true;
705
- }
706
-
707
-
708
-
709
- this.getUIElements = function()
710
- {
711
- var uiElements = [];
712
- for (var uiElementName in this.uiElements) {
713
- uiElements.push(this.uiElements[uiElementName]);
714
- }
715
- return uiElements;
716
- };
717
-
718
-
719
-
720
- /**
721
- * Returns a list of UI specifier string stubs representing all UI elements
722
- * for this pageset. Stubs contain all required arguments, but leave
723
- * argument values blank. Each element stub is paired with the element's
724
- * description.
725
- *
726
- * @return a list of UI specifier string stubs
727
- */
728
- this.getUISpecifierStringStubs = function()
729
- {
730
- var stubs = [];
731
- for (var name in this.uiElements) {
732
- var uiElement = this.uiElements[name];
733
- var args = {};
734
- for (var i = 0; i < uiElement.args.length; ++i) {
735
- args[uiElement.args[i].name] = '';
736
- }
737
- var uiSpecifier = new UISpecifier(this.name, uiElement.name, args);
738
- stubs.push([
739
- UI_GLOBAL.UI_PREFIX + '=' + uiSpecifier.toString()
740
- , uiElement.description
741
- ]);
742
- }
743
- return stubs;
744
- }
745
-
746
-
747
-
748
- /**
749
- * Throws an exception on validation failure.
750
- */
751
- this._validate = function(pagesetShorthand)
752
- {
753
- var msg = "Pageset validation error:\n"
754
- + print_r(pagesetShorthand);
755
- if (!pagesetShorthand.name) {
756
- throw new PagesetException(msg + 'no name specified!');
757
- }
758
- if (!pagesetShorthand.description) {
759
- throw new PagesetException(msg + 'no description specified!');
760
- }
761
- if (!pagesetShorthand.paths &&
762
- !pagesetShorthand.pathRegexp &&
763
- !pagesetShorthand.pageContent) {
764
- throw new PagesetException(msg
765
- + 'no path, pathRegexp, or pageContent specified!');
766
- }
767
- };
768
-
769
-
770
-
771
- this.init = function(pagesetShorthand)
772
- {
773
- this._validate(pagesetShorthand);
774
-
775
- this.name = pagesetShorthand.name;
776
- this.description = pagesetShorthand.description;
777
-
778
- var pathPrefixRegexp = pagesetShorthand.pathPrefix
779
- ? RegExp.escape(pagesetShorthand.pathPrefix) : "";
780
- var pathRegexp = '^' + pathPrefixRegexp;
781
-
782
- if (pagesetShorthand.paths != undefined) {
783
- pathRegexp += '(?:';
784
- for (var i = 0; i < pagesetShorthand.paths.length; ++i) {
785
- if (i > 0) {
786
- pathRegexp += '|';
787
- }
788
- pathRegexp += RegExp.escape(pagesetShorthand.paths[i]);
789
- }
790
- pathRegexp += ')$';
791
- }
792
- else if (pagesetShorthand.pathRegexp) {
793
- pathRegexp += '(?:' + pagesetShorthand.pathRegexp + ')$';
794
- }
795
-
796
- this.pathRegexp = new RegExp(pathRegexp);
797
- this.paramRegexps = {};
798
- for (var paramName in pagesetShorthand.paramRegexps) {
799
- this.paramRegexps[paramName] =
800
- new RegExp(pagesetShorthand.paramRegexps[paramName]);
801
- }
802
- this.pageContent = pagesetShorthand.pageContent ||
803
- function() { return true; };
804
- this.uiElements = {};
805
- };
806
-
807
-
808
-
809
- this.init(pagesetShorthand);
810
- }
811
-
812
-
813
-
814
- /**
815
- * Construct the UI map object, and return it. Once the object is instantiated,
816
- * it binds to a global variable and will not leave scope.
817
- *
818
- * @return new UIMap object
819
- */
820
- function UIMap()
821
- {
822
- // the singleton pattern, split into two parts so that "new" can still
823
- // be used, in addition to "getInstance()"
824
- UIMap.self = this;
825
-
826
- // need to attach variables directly to the Editor object in order for them
827
- // to be in scope for Editor methods
828
- if (is_IDE()) {
829
- Editor.uiMap = this;
830
- Editor.UI_PREFIX = UI_GLOBAL.UI_PREFIX;
831
- }
832
-
833
- this.pagesets = new Object();
834
-
835
-
836
-
837
- /**
838
- * pageset[pagesetName]
839
- * regexp
840
- * elements[elementName]
841
- * UIElement
842
- */
843
- this.addPageset = function(pagesetShorthand)
844
- {
845
- try {
846
- var pageset = new Pageset(pagesetShorthand);
847
- }
848
- catch (e) {
849
- safe_alert("Could not create pageset from shorthand:\n"
850
- + print_r(pagesetShorthand) + "\n" + e.message);
851
- return false;
852
- }
853
-
854
- if (this.pagesets[pageset.name]) {
855
- safe_alert('Could not add pageset "' + pageset.name
856
- + '": a pageset with that name already exists!');
857
- return false;
858
- }
859
-
860
- this.pagesets[pageset.name] = pageset;
861
- return true;
862
- };
863
-
864
-
865
-
866
- /**
867
- * @param pagesetName
868
- * @param uiElementShorthand a representation of a UIElement object in
869
- * shorthand JSON.
870
- */
871
- this.addElement = function(pagesetName, uiElementShorthand)
872
- {
873
- try {
874
- var uiElement = new UIElement(uiElementShorthand);
875
- }
876
- catch (e) {
877
- safe_alert("Could not create UI element from shorthand:\n"
878
- + print_r(uiElementShorthand) + "\n" + e.message);
879
- return false;
880
- }
881
-
882
- // run the element's unit tests only for the IDE, and only when the
883
- // IDE is starting. Make a rough guess as to the latter condition.
884
- if (is_IDE() && !editor.selDebugger && !uiElement.test()) {
885
- safe_alert('Could not add UI element "' + uiElement.name
886
- + '": failed testcases!');
887
- return false;
888
- }
889
-
890
- try {
891
- this.pagesets[pagesetName].uiElements[uiElement.name] = uiElement;
892
- }
893
- catch (e) {
894
- safe_alert("Could not add UI element '" + uiElement.name
895
- + "' to pageset '" + pagesetName + "':\n" + e.message);
896
- return false;
897
- }
898
-
899
- return true;
900
- };
901
-
902
-
903
-
904
- /**
905
- * Returns the pageset for a given UI specifier string.
906
- *
907
- * @param uiSpecifierString
908
- * @return a pageset object
909
- */
910
- this.getPageset = function(uiSpecifierString)
911
- {
912
- try {
913
- var uiSpecifier = new UISpecifier(uiSpecifierString);
914
- return this.pagesets[uiSpecifier.pagesetName];
915
- }
916
- catch (e) {
917
- return null;
918
- }
919
- }
920
-
921
-
922
-
923
- /**
924
- * Returns the UIElement that a UISpecifierString or pageset and element
925
- * pair refer to.
926
- *
927
- * @param pagesetNameOrUISpecifierString
928
- * @return a UIElement, or null if none is found associated with
929
- * uiSpecifierString
930
- */
931
- this.getUIElement = function(pagesetNameOrUISpecifierString, uiElementName)
932
- {
933
- var pagesetName = pagesetNameOrUISpecifierString;
934
- if (arguments.length == 1) {
935
- var uiSpecifierString = pagesetNameOrUISpecifierString;
936
- try {
937
- var uiSpecifier = new UISpecifier(uiSpecifierString);
938
- pagesetName = uiSpecifier.pagesetName;
939
- var uiElementName = uiSpecifier.elementName;
940
- }
941
- catch (e) {
942
- return null;
943
- }
944
- }
945
- try {
946
- return this.pagesets[pagesetName].uiElements[uiElementName];
947
- }
948
- catch (e) {
949
- return null;
950
- }
951
- };
952
-
953
-
954
-
955
- /**
956
- * Returns a list of pagesets that "contains" the provided page,
957
- * represented as a document object. Containership is defined by the
958
- * Pageset object's contain() method.
959
- *
960
- * @param inDocument the page to get pagesets for
961
- * @return a list of pagesets
962
- */
963
- this.getPagesetsForPage = function(inDocument)
964
- {
965
- var pagesets = [];
966
- for (var pagesetName in this.pagesets) {
967
- var pageset = this.pagesets[pagesetName];
968
- if (pageset.contains(inDocument)) {
969
- pagesets.push(pageset);
970
- }
971
- }
972
- return pagesets;
973
- };
974
-
975
-
976
-
977
- /**
978
- * Returns a list of all pagesets.
979
- *
980
- * @return a list of pagesets
981
- */
982
- this.getPagesets = function()
983
- {
984
- var pagesets = [];
985
- for (var pagesetName in this.pagesets) {
986
- pagesets.push(this.pagesets[pagesetName]);
987
- }
988
- return pagesets;
989
- };
990
-
991
-
992
-
993
- /**
994
- * Returns a list of elements on a page that a given UI specifier string,
995
- * maps to. If no elements are mapped to, returns an empty list..
996
- *
997
- * @param uiSpecifierString a String that specifies a UI element with
998
- * attendant argument values
999
- * @param inDocument the document object the specified UI element
1000
- * appears in
1001
- * @return a potentially-empty list of elements
1002
- * specified by uiSpecifierString
1003
- */
1004
- this.getPageElements = function(uiSpecifierString, inDocument)
1005
- {
1006
- var locator = this.getLocator(uiSpecifierString);
1007
- var results = locator ? eval_locator(locator, inDocument) : [];
1008
- return results;
1009
- };
1010
-
1011
-
1012
-
1013
- /**
1014
- * Returns the locator string that a given UI specifier string maps to, or
1015
- * null if it cannot be mapped.
1016
- *
1017
- * @param uiSpecifierString
1018
- */
1019
- this.getLocator = function(uiSpecifierString)
1020
- {
1021
- try {
1022
- var uiSpecifier = new UISpecifier(uiSpecifierString);
1023
- }
1024
- catch (e) {
1025
- safe_alert('Could not create UISpecifier for string "'
1026
- + uiSpecifierString + '": ' + e.message);
1027
- return null;
1028
- }
1029
-
1030
- var uiElement = this.getUIElement(uiSpecifier.pagesetName,
1031
- uiSpecifier.elementName);
1032
- try {
1033
- return uiElement.getLocator(uiSpecifier.args);
1034
- }
1035
- catch (e) {
1036
- return null;
1037
- }
1038
- }
1039
-
1040
-
1041
-
1042
- /**
1043
- * Finds and returns a UI specifier string given an element and the page
1044
- * that it appears on.
1045
- *
1046
- * @param pageElement the document element to map to a UI specifier
1047
- * @param inDocument the document the element appears in
1048
- * @return a UI specifier string, or false if one cannot be
1049
- * constructed
1050
- */
1051
- this.getUISpecifierString = function(pageElement, inDocument)
1052
- {
1053
- var is_fuzzy_match =
1054
- BrowserBot.prototype.locateElementByUIElement.is_fuzzy_match;
1055
- var pagesets = this.getPagesetsForPage(inDocument);
1056
-
1057
- for (var i = 0; i < pagesets.length; ++i) {
1058
- var pageset = pagesets[i];
1059
- var uiElements = pageset.getUIElements();
1060
-
1061
- for (var j = 0; j < uiElements.length; ++j) {
1062
- var uiElement = uiElements[j];
1063
-
1064
- // first test against the generic locator, if there is one.
1065
- // This should net some performance benefit when recording on
1066
- // more complicated pages.
1067
- if (uiElement.getGenericLocator) {
1068
- var passedTest = false;
1069
- var results =
1070
- eval_locator(uiElement.getGenericLocator(), inDocument);
1071
- for (var i = 0; i < results.length; ++i) {
1072
- if (results[i] == pageElement) {
1073
- passedTest = true;
1074
- break;
1075
- }
1076
- }
1077
- if (!passedTest) {
1078
- continue;
1079
- }
1080
- }
1081
-
1082
- var defaultLocators;
1083
- if (uiElement.isDefaultLocatorConstructionDeferred) {
1084
- defaultLocators = uiElement.getDefaultLocators(inDocument);
1085
- }
1086
- else {
1087
- defaultLocators = uiElement.defaultLocators;
1088
- }
1089
-
1090
- //safe_alert(print_r(uiElement.defaultLocators));
1091
- for (var locator in defaultLocators) {
1092
- var locatedElements = eval_locator(locator, inDocument);
1093
- if (locatedElements.length) {
1094
- var locatedElement = locatedElements[0];
1095
- }
1096
- else {
1097
- continue;
1098
- }
1099
-
1100
- // use a heuristic to determine whether the element
1101
- // specified is the "same" as the element we're matching
1102
- if (is_fuzzy_match) {
1103
- if (is_fuzzy_match(locatedElement, pageElement)) {
1104
- return UI_GLOBAL.UI_PREFIX + '=' +
1105
- new UISpecifier(pageset.name, uiElement.name,
1106
- defaultLocators[locator]);
1107
- }
1108
- }
1109
- else {
1110
- if (locatedElement == pageElement) {
1111
- return UI_GLOBAL.UI_PREFIX + '=' +
1112
- new UISpecifier(pageset.name, uiElement.name,
1113
- defaultLocators[locator]);
1114
- }
1115
- }
1116
-
1117
- // ok, matching the element failed. See if an offset
1118
- // locator can complete the match.
1119
- if (uiElement.getOffsetLocator) {
1120
- for (var k = 0; k < locatedElements.length; ++k) {
1121
- var offsetLocator = uiElement
1122
- .getOffsetLocator(locatedElements[k], pageElement);
1123
- if (offsetLocator) {
1124
- return UI_GLOBAL.UI_PREFIX + '=' +
1125
- new UISpecifier(pageset.name,
1126
- uiElement.name,
1127
- defaultLocators[locator])
1128
- + '->' + offsetLocator;
1129
- }
1130
- }
1131
- }
1132
- }
1133
- }
1134
- }
1135
- return false;
1136
- };
1137
-
1138
-
1139
-
1140
- /**
1141
- * Returns a sorted list of UI specifier string stubs representing possible
1142
- * UI elements for all pagesets, paired the their descriptions. Stubs
1143
- * contain all required arguments, but leave argument values blank.
1144
- *
1145
- * @return a list of UI specifier string stubs
1146
- */
1147
- this.getUISpecifierStringStubs = function() {
1148
- var stubs = [];
1149
- var pagesets = this.getPagesets();
1150
- for (var i = 0; i < pagesets.length; ++i) {
1151
- stubs = stubs.concat(pagesets[i].getUISpecifierStringStubs());
1152
- }
1153
- stubs.sort(function(a, b) {
1154
- if (a[0] < b[0]) {
1155
- return -1;
1156
- }
1157
- return a[0] == b[0] ? 0 : 1;
1158
- });
1159
- return stubs;
1160
- }
1161
- }
1162
-
1163
- UIMap.getInstance = function() {
1164
- return (UIMap.self == null) ? new UIMap() : UIMap.self;
1165
- }
1166
-
1167
- //******************************************************************************
1168
- // Rollups
1169
-
1170
- /**
1171
- * The Command object isn't available in the Selenium RC. We introduce an
1172
- * object with the identical constructor. In the IDE, this will be redefined,
1173
- * which is just fine.
1174
- *
1175
- * @param command
1176
- * @param target
1177
- * @param value
1178
- */
1179
- if (typeof(Command) == 'undefined') {
1180
- function Command(command, target, value) {
1181
- this.command = command != null ? command : '';
1182
- this.target = target != null ? target : '';
1183
- this.value = value != null ? value : '';
1184
- }
1185
- }
1186
-
1187
-
1188
-
1189
- /**
1190
- * A CommandMatcher object matches commands during the application of a
1191
- * RollupRule. It's specified with a shorthand format, for example:
1192
- *
1193
- * new CommandMatcher({
1194
- * command: 'click'
1195
- * , target: 'ui=allPages::.+'
1196
- * })
1197
- *
1198
- * which is intended to match click commands whose target is an element in the
1199
- * allPages PageSet. The matching expressions are given as regular expressions;
1200
- * in the example above, the command must be "click"; "clickAndWait" would be
1201
- * acceptable if 'click.*' were used. Here's a more complete example:
1202
- *
1203
- * new CommandMatcher({
1204
- * command: 'type'
1205
- * , target: 'ui=loginPages::username()'
1206
- * , value: '.+_test'
1207
- * , updateArgs: function(command, args) {
1208
- * args.username = command.value;
1209
- * }
1210
- * })
1211
- *
1212
- * Here, the command and target are fixed, but there is variability in the
1213
- * value of the command. When a command matches, the username is saved to the
1214
- * arguments object.
1215
- */
1216
- function CommandMatcher(commandMatcherShorthand)
1217
- {
1218
- /**
1219
- * Ensure the shorthand notation used to initialize the CommandMatcher has
1220
- * all required values.
1221
- *
1222
- * @param commandMatcherShorthand an object containing information about
1223
- * the CommandMatcher
1224
- */
1225
- this.validate = function(commandMatcherShorthand) {
1226
- var msg = "CommandMatcher validation error:\n"
1227
- + print_r(commandMatcherShorthand);
1228
- if (!commandMatcherShorthand.command) {
1229
- throw new CommandMatcherException(msg + 'no command specified!');
1230
- }
1231
- if (!commandMatcherShorthand.target) {
1232
- throw new CommandMatcherException(msg + 'no target specified!');
1233
- }
1234
- if (commandMatcherShorthand.minMatches &&
1235
- commandMatcherShorthand.maxMatches &&
1236
- commandMatcherShorthand.minMatches >
1237
- commandMatcherShorthand.maxMatches) {
1238
- throw new CommandMatcherException(msg + 'minMatches > maxMatches!');
1239
- }
1240
- };
1241
-
1242
- /**
1243
- * Initialize this object.
1244
- *
1245
- * @param commandMatcherShorthand an object containing information used to
1246
- * initialize the CommandMatcher
1247
- */
1248
- this.init = function(commandMatcherShorthand) {
1249
- this.validate(commandMatcherShorthand);
1250
-
1251
- this.command = commandMatcherShorthand.command;
1252
- this.target = commandMatcherShorthand.target;
1253
- this.value = commandMatcherShorthand.value || null;
1254
- this.minMatches = commandMatcherShorthand.minMatches || 1;
1255
- this.maxMatches = commandMatcherShorthand.maxMatches || 1;
1256
- this.updateArgs = commandMatcherShorthand.updateArgs ||
1257
- function(command, args) { return args; };
1258
- };
1259
-
1260
- /**
1261
- * Determines whether a given command matches. Updates args by "reference"
1262
- * and returns true if it does; return false otherwise.
1263
- *
1264
- * @param command the command to attempt to match
1265
- */
1266
- this.isMatch = function(command) {
1267
- var re = new RegExp('^' + this.command + '$');
1268
- if (! re.test(command.command)) {
1269
- return false;
1270
- }
1271
- re = new RegExp('^' + this.target + '$');
1272
- if (! re.test(command.target)) {
1273
- return false;
1274
- }
1275
- if (this.value != null) {
1276
- re = new RegExp('^' + this.value + '$');
1277
- if (! re.test(command.value)) {
1278
- return false;
1279
- }
1280
- }
1281
-
1282
- // okay, the command matches
1283
- return true;
1284
- };
1285
-
1286
- // initialization
1287
- this.init(commandMatcherShorthand);
1288
- }
1289
-
1290
-
1291
-
1292
- function RollupRuleException(message)
1293
- {
1294
- this.message = message;
1295
- this.name = 'RollupRuleException';
1296
- }
1297
-
1298
- function RollupRule(rollupRuleShorthand)
1299
- {
1300
- /**
1301
- * Ensure the shorthand notation used to initialize the RollupRule has all
1302
- * required values.
1303
- *
1304
- * @param rollupRuleShorthand an object containing information about the
1305
- * RollupRule
1306
- */
1307
- this.validate = function(rollupRuleShorthand) {
1308
- var msg = "RollupRule validation error:\n"
1309
- + print_r(rollupRuleShorthand);
1310
- if (!rollupRuleShorthand.name) {
1311
- throw new RollupRuleException(msg + 'no name specified!');
1312
- }
1313
- if (!rollupRuleShorthand.description) {
1314
- throw new RollupRuleException(msg + 'no description specified!');
1315
- }
1316
- // rollupRuleShorthand.args is optional
1317
- if (!rollupRuleShorthand.commandMatchers &&
1318
- !rollupRuleShorthand.getRollup) {
1319
- throw new RollupRuleException(msg
1320
- + 'no command matchers specified!');
1321
- }
1322
- if (!rollupRuleShorthand.expandedCommands &&
1323
- !rollupRuleShorthand.getExpandedCommands) {
1324
- throw new RollupRuleException(msg
1325
- + 'no expanded commands specified!');
1326
- }
1327
-
1328
- return true;
1329
- };
1330
-
1331
- /**
1332
- * Initialize this object.
1333
- *
1334
- * @param rollupRuleShorthand an object containing information used to
1335
- * initialize the RollupRule
1336
- */
1337
- this.init = function(rollupRuleShorthand) {
1338
- this.validate(rollupRuleShorthand);
1339
-
1340
- this.name = rollupRuleShorthand.name;
1341
- this.description = rollupRuleShorthand.description;
1342
- this.pre = rollupRuleShorthand.pre || '';
1343
- this.post = rollupRuleShorthand.post || '';
1344
- this.alternateCommand = rollupRuleShorthand.alternateCommand;
1345
- this.args = rollupRuleShorthand.args || [];
1346
-
1347
- if (rollupRuleShorthand.commandMatchers) {
1348
- // construct the rule from the list of CommandMatchers
1349
- this.commandMatchers = [];
1350
- var matchers = rollupRuleShorthand.commandMatchers;
1351
- for (var i = 0; i < matchers.length; ++i) {
1352
- if (matchers[i].updateArgs && this.args.length == 0) {
1353
- // enforce metadata for arguments
1354
- var msg = "RollupRule validation error:\n"
1355
- + print_r(rollupRuleShorthand)
1356
- + 'no argument metadata provided!';
1357
- throw new RollupRuleException(msg);
1358
- }
1359
- this.commandMatchers.push(new CommandMatcher(matchers[i]));
1360
- }
1361
-
1362
- // returns false if the rollup doesn't match, or a rollup command
1363
- // if it does. If returned, the command contains the
1364
- // replacementIndexes property, which indicates which commands it
1365
- // substitutes for.
1366
- this.getRollup = function(commands) {
1367
- // this is a greedy matching algorithm
1368
- var replacementIndexes = [];
1369
- var commandMatcherQueue = this.commandMatchers;
1370
- var matchCount = 0;
1371
- var args = {};
1372
- for (var i = 0, j = 0; i < commandMatcherQueue.length;) {
1373
- var matcher = commandMatcherQueue[i];
1374
- if (j >= commands.length) {
1375
- // we've run out of commands! If the remaining matchers
1376
- // do not have minMatches requirements, this is a
1377
- // match. Otherwise, it's not.
1378
- if (matcher.minMatches > 0) {
1379
- return false;
1380
- }
1381
- ++i;
1382
- matchCount = 0; // unnecessary, but let's be consistent
1383
- }
1384
- else {
1385
- if (matcher.isMatch(commands[j])) {
1386
- ++matchCount;
1387
- if (matchCount == matcher.maxMatches) {
1388
- // exhausted this matcher's matches ... move on
1389
- // to next matcher
1390
- ++i;
1391
- matchCount = 0;
1392
- }
1393
- args = matcher.updateArgs(commands[j], args);
1394
- replacementIndexes.push(j);
1395
- ++j; // move on to next command
1396
- }
1397
- else {
1398
- //alert(matchCount + ', ' + matcher.minMatches);
1399
- if (matchCount < matcher.minMatches) {
1400
- return false;
1401
- }
1402
- // didn't match this time, but we've satisfied the
1403
- // requirements already ... move on to next matcher
1404
- ++i;
1405
- matchCount = 0;
1406
- // still gonna look at same command
1407
- }
1408
- }
1409
- }
1410
-
1411
- var rollup;
1412
- if (this.alternateCommand) {
1413
- rollup = new Command(this.alternateCommand,
1414
- commands[0].target, commands[0].value);
1415
- }
1416
- else {
1417
- rollup = new Command('rollup', this.name);
1418
- rollup.value = to_kwargs(args);
1419
- }
1420
- rollup.replacementIndexes = replacementIndexes;
1421
- return rollup;
1422
- };
1423
- }
1424
- else {
1425
- this.getRollup = function(commands) {
1426
- var result = rollupRuleShorthand.getRollup(commands);
1427
- if (result) {
1428
- var rollup = new Command(
1429
- result.command
1430
- , result.target
1431
- , result.value
1432
- );
1433
- rollup.replacementIndexes = result.replacementIndexes;
1434
- return rollup;
1435
- }
1436
- return false;
1437
- };
1438
- }
1439
-
1440
- this.getExpandedCommands = function(kwargs) {
1441
- var commands = [];
1442
- var expandedCommands = (rollupRuleShorthand.expandedCommands
1443
- ? rollupRuleShorthand.expandedCommands
1444
- : rollupRuleShorthand.getExpandedCommands(
1445
- parse_kwargs(kwargs)));
1446
- for (var i = 0; i < expandedCommands.length; ++i) {
1447
- var command = expandedCommands[i];
1448
- commands.push(new Command(
1449
- command.command
1450
- , command.target
1451
- , command.value
1452
- ));
1453
- }
1454
- return commands;
1455
- };
1456
- };
1457
-
1458
- this.init(rollupRuleShorthand);
1459
- }
1460
-
1461
-
1462
-
1463
- /**
1464
- *
1465
- */
1466
- function RollupManager()
1467
- {
1468
- // singleton pattern
1469
- RollupManager.self = this;
1470
-
1471
- this.init = function()
1472
- {
1473
- this.rollupRules = {};
1474
- if (is_IDE()) {
1475
- Editor.rollupManager = this;
1476
- }
1477
- };
1478
-
1479
- /**
1480
- * Adds a new RollupRule to the repository. Returns true on success, or
1481
- * false if the rule couldn't be added.
1482
- *
1483
- * @param rollupRuleShorthand shorthand JSON specification of the new
1484
- * RollupRule, possibly including CommandMatcher
1485
- * shorthand too.
1486
- * @return true if the rule was added successfully,
1487
- * false otherwise.
1488
- */
1489
- this.addRollupRule = function(rollupRuleShorthand)
1490
- {
1491
- try {
1492
- var rule = new RollupRule(rollupRuleShorthand);
1493
- this.rollupRules[rule.name] = rule;
1494
- }
1495
- catch(e) {
1496
- smart_alert("Could not create RollupRule from shorthand:\n\n"
1497
- + e.message);
1498
- return false;
1499
- }
1500
- return true;
1501
- };
1502
-
1503
- /**
1504
- * Returns a RollupRule by name.
1505
- *
1506
- * @param rollupName the name of the rule to fetch
1507
- * @return the RollupRule, or null if it isn't found.
1508
- */
1509
- this.getRollupRule = function(rollupName)
1510
- {
1511
- return (this.rollupRules[rollupName] || null);
1512
- };
1513
-
1514
- /**
1515
- * Returns a list of name-description pairs for use in populating the
1516
- * auto-populated target dropdown in the IDE. Rules that have an alternate
1517
- * command defined are not included in the list, as they are not bona-fide
1518
- * rollups.
1519
- *
1520
- * @return a list of name-description pairs
1521
- */
1522
- this.getRollupRulesForDropdown = function()
1523
- {
1524
- var targets = [];
1525
- var names = keys(this.rollupRules).sort();
1526
- for (var i = 0; i < names.length; ++i) {
1527
- var name = names[i];
1528
- if (this.rollupRules[name].alternateCommand) {
1529
- continue;
1530
- }
1531
- targets.push([ name, this.rollupRules[name].description ]);
1532
- }
1533
- return targets;
1534
- };
1535
-
1536
- /**
1537
- * Applies all rules to the current editor commands, asking the user in
1538
- * each case if it's okay to perform the replacement. The rules are applied
1539
- * repeatedly until there are no more matches. The algorithm should
1540
- * remember when the user has declined a replacement, and not ask to do it
1541
- * again.
1542
- *
1543
- * @return the list of commands with rollup replacements performed
1544
- */
1545
- this.applyRollupRules = function()
1546
- {
1547
- var commands = editor.getTestCase().commands;
1548
- var blacklistedRollups = {};
1549
-
1550
- // so long as rollups were performed, we need to keep iterating through
1551
- // the commands starting at the beginning, because further rollups may
1552
- // potentially be applied on the newly created ones.
1553
- while (true) {
1554
- var performedRollup = false;
1555
- for (var i = 0; i < commands.length; ++i) {
1556
- // iterate through commands
1557
- for (var rollupName in this.rollupRules) {
1558
- var rule = this.rollupRules[rollupName];
1559
- var rollup = rule.getRollup(commands.slice(i));
1560
- if (rollup) {
1561
- // since we passed in a sliced version of the commands
1562
- // array to the getRollup() method, we need to re-add
1563
- // the offset to the replacementIndexes
1564
- var k = 0;
1565
- for (; k < rollup.replacementIndexes.length; ++k) {
1566
- rollup.replacementIndexes[k] += i;
1567
- }
1568
-
1569
- // build the confirmation message
1570
- var msg = "Perform the following command rollup?\n\n";
1571
- for (k = 0; k < rollup.replacementIndexes.length; ++k) {
1572
- var replacementIndex = rollup.replacementIndexes[k];
1573
- var command = commands[replacementIndex];
1574
- msg += '[' + replacementIndex + ']: ';
1575
- msg += command + "\n";
1576
- }
1577
- msg += "\n";
1578
- msg += rollup;
1579
-
1580
- // check against blacklisted rollups
1581
- if (blacklistedRollups[msg]) {
1582
- continue;
1583
- }
1584
-
1585
- // highlight the potentially replaced rows
1586
- for (k = 0; k < commands.length; ++k) {
1587
- var command = commands[k];
1588
- command.result = '';
1589
- if (rollup.replacementIndexes.indexOf(k) != -1) {
1590
- command.selectedForReplacement = true;
1591
- }
1592
- editor.view.rowUpdated(replacementIndex);
1593
- }
1594
-
1595
- // get confirmation from user
1596
- if (confirm(msg)) {
1597
- // perform rollup
1598
- var deleteRanges = [];
1599
- var replacementIndexes = rollup.replacementIndexes;
1600
- for (k = 0; k < replacementIndexes.length; ++k) {
1601
- // this is expected to be list of ranges. A
1602
- // range has a start, and a list of commands.
1603
- // The deletion only checks the length of the
1604
- // command list.
1605
- deleteRanges.push({
1606
- start: replacementIndexes[k]
1607
- , commands: [ 1 ]
1608
- });
1609
- }
1610
- editor.view.executeAction(new TreeView
1611
- .DeleteCommandAction(editor.view,deleteRanges));
1612
- editor.view.insertAt(i, rollup);
1613
-
1614
- performedRollup = true;
1615
- }
1616
- else {
1617
- // cleverly remember not to try this rollup again
1618
- blacklistedRollups[msg] = true;
1619
- }
1620
-
1621
- // unhighlight
1622
- for (k = 0; k < commands.length; ++k) {
1623
- commands[k].selectedForReplacement = false;
1624
- editor.view.rowUpdated(k);
1625
- }
1626
- }
1627
- }
1628
- }
1629
- if (!performedRollup) {
1630
- break;
1631
- }
1632
- }
1633
- return commands;
1634
- };
1635
-
1636
- this.init();
1637
- }
1638
-
1639
- RollupManager.getInstance = function() {
1640
- return (RollupManager.self == null)
1641
- ? new RollupManager()
1642
- : RollupManager.self;
1643
- }
1644
-