Commit 3bf3df38 3bf3df38dab43ca3092f61617fd2c25ed218bb8a by Laurent Jouanneau

Merge branch 'master' into geckofix1

2 parents 90646423 c878b2b3
...@@ -3,15 +3,18 @@ branches: ...@@ -3,15 +3,18 @@ branches:
3 - "master" 3 - "master"
4 4
5 before_install: 5 before_install:
6 - if [[ $ENGINE == 'phantomjs' ]]; then export ENGINE_DOWNLOAD='http://phantomjs.googlecode.com/files'; fi 6 - echo "Installing $ENGINE $ENGINE_VERSION from $ENGINE_ARCHIVE_URL"
7 - if [[ $ENGINE == 'slimerjs' ]]; then export ENGINE_DOWNLOAD='http://download.slimerjs.org/v0.8'; fi 7 - wget $ENGINE_ARCHIVE_URL --output-document=engine.tar.bz2
8
9 - echo "Installing $ENGINE $ENGINE_VERSION"
10 - wget $(echo $ENGINE_DOWNLOAD)/$(echo $ENGINE)-$(echo $ENGINE_VERSION)-linux-x86_64.tar.bz2 --output-document=engine.tar.bz2
11 - mkdir engine && tar --strip-components=1 -xvf engine.tar.bz2 -C engine 8 - mkdir engine && tar --strip-components=1 -xvf engine.tar.bz2 -C engine
12 9 - if [[ $ENGINE == 'phantomjs' ]]; then
13 - if [[ $ENGINE == 'phantomjs' ]]; then ls -la engine/bin/phantomjs && engine/bin/phantomjs --version; fi 10 ENGINE_EXECUTABLE="engine/bin/phantomjs";
14 - if [[ $ENGINE == 'slimerjs' ]]; then ls -la engine/slimerjs && engine/slimerjs --version; fi 11 elif [[ $ENGINE == 'slimerjs' ]]; then
12 ENGINE_EXECUTABLE="engine/slimerjs";
13 else
14 echo "Unsupported engine $ENGINE";
15 fi
16 - ls -la $ENGINE_EXECUTABLE && $ENGINE_EXECUTABLE --version;
17 - export ENGINE_EXECUTABLE="$ENGINE_EXECUTABLE $ENGINE_FLAGS"
15 18
16 install: 19 install:
17 - sudo apt-get install -qq mono-devel mono-mcs 20 - sudo apt-get install -qq mono-devel mono-mcs
...@@ -20,18 +23,32 @@ before_script: ...@@ -20,18 +23,32 @@ before_script:
20 - "npm install -g jshint@2.0.1" 23 - "npm install -g jshint@2.0.1"
21 24
22 script: 25 script:
23 - make test 26 - "make $MAKE_TEST_COMMAND"
24 - make test-dotNET
25 27
26 env: 28 env:
27 matrix:
28 - ENGINE="phantomjs" ENGINE_VERSION="1.8.2"
29 - ENGINE="phantomjs" ENGINE_VERSION="1.9.0"
30 - ENGINE="phantomjs" ENGINE_VERSION="1.9.1"
31 - ENGINE="phantomjs" ENGINE_VERSION="1.9.2"
32 global: 29 global:
33 - PHANTOMJS_EXECUTABLE="engine/bin/phantomjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes" 30 - ENGINE_FLAGS="--local-to-remote-url-access=yes --ignore-ssl-errors=yes"
34 - SLIMERJS_EXECUTABLE="engine/slimerjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes" 31 matrix:
32 - ENGINE="phantomjs" ENGINE_VERSION="1.8.2" MAKE_TEST_COMMAND="test-dotNET"
33 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.8.2-linux-x86_64.tar.bz2"
34 - ENGINE="phantomjs" ENGINE_VERSION="1.8.2" MAKE_TEST_COMMAND="test"
35 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.8.2-linux-x86_64.tar.bz2"
36 - ENGINE="phantomjs" ENGINE_VERSION="1.9.0" MAKE_TEST_COMMAND="test-dotNET"
37 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.9.0-linux-x86_64.tar.bz2"
38 - ENGINE="phantomjs" ENGINE_VERSION="1.9.0" MAKE_TEST_COMMAND="test"
39 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.9.0-linux-x86_64.tar.bz2"
40 - ENGINE="phantomjs" ENGINE_VERSION="1.9.1" MAKE_TEST_COMMAND="test-dotNET"
41 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2"
42 - ENGINE="phantomjs" ENGINE_VERSION="1.9.1" MAKE_TEST_COMMAND="test"
43 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.9.1-linux-x86_64.tar.bz2"
44 - ENGINE="phantomjs" ENGINE_VERSION="1.9.2" MAKE_TEST_COMMAND="test-dotNET"
45 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2"
46 - ENGINE="phantomjs" ENGINE_VERSION="1.9.2" MAKE_TEST_COMMAND="test"
47 ENGINE_ARCHIVE_URL="https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2"
48 - ENGINE="slimerjs" ENGINE_VERSION="0.8.4" MAKE_TEST_COMMAND="test-dotNET"
49 ENGINE_ARCHIVE_URL="http://download.slimerjs.org/v0.8/0.8.4/slimerjs-0.8.4-linux-x86_64.tar.bz2"
50 - ENGINE="slimerjs" ENGINE_VERSION="0.8.4" MAKE_TEST_COMMAND="test"
51 ENGINE_ARCHIVE_URL="http://download.slimerjs.org/v0.8/0.8.4/slimerjs-0.8.4-linux-x86_64.tar.bz2"
35 52
36 notifications: 53 notifications:
37 irc: 54 irc:
......
...@@ -19,20 +19,20 @@ and [SlimerJS](http://slimerjs.org/). It eases the process of defining a full na ...@@ -19,20 +19,20 @@ and [SlimerJS](http://slimerjs.org/). It eases the process of defining a full na
19 scenario and provides useful high-level functions, methods & syntaxic sugar for doing common 19 scenario and provides useful high-level functions, methods & syntaxic sugar for doing common
20 tasks such as: 20 tasks such as:
21 21
22 - defining & ordering [navigation steps](http://casperjs.org/quickstart.html) 22 - defining & ordering [navigation steps](http://docs.casperjs.org/en/latest/quickstart.html)
23 - [filling forms](http://casperjs.org/api.html#casper.fill) 23 - [filling forms](http://docs.casperjs.org/en/latest/modules/casper.html#fill)
24 - [clicking links](http://casperjs.org/api.html#casper.click) 24 - [clicking links](http://docs.casperjs.org/en/latest/modules/casper.html#click)
25 - [capturing screenshots](http://casperjs.org/api.html#casper.captureSelector) of a page (or an area) 25 - [capturing screenshots](http://docs.casperjs.org/en/latest/modules/casper.html#captureselector) of a page (or an area)
26 - [making assertions on remote DOM](http://casperjs.org/api.html#tester) 26 - [making assertions on remote DOM](http://docs.casperjs.org/en/latest/modules/tester.html)
27 - [logging](http://casperjs.org/logging.html) & [events](http://casperjs.org/events-filters.html) 27 - [logging](http://docs.casperjs.org/en/latest/logging.html) & [events](http://docs.casperjs.org/en/latest/events-filters.html)
28 - [downloading base64](http://casperjs.org/api.html#casper.download) encoded resources, even binary ones 28 - [downloading](http://docs.casperjs.org/en/latest/modules/casper.html#download) resources, even binary ones
29 - catching errors and react accordingly 29 - catching errors and react accordingly
30 - writing [functional test suites](http://casperjs.org/testing.html), exporting results as JUnit XML (xUnit) 30 - writing [functional test suites](http://docs.casperjs.org/en/latest/testing.html), exporting results as JUnit XML (xUnit)
31 31
32 Browse the [sample examples repository](https://github.com/n1k0/casperjs/tree/master/samples). 32 Browse the [sample examples repository](https://github.com/n1k0/casperjs/tree/master/samples).
33 Don't hesitate to pull request for any cool example of yours as well! 33 Don't hesitate to pull request for any cool example of yours as well!
34 34
35 **Read the [full documentation](http://casperjs.org/) on casperjs dedicated website.** 35 **Read the [full documentation](http://docs.casperjs.org/) on casperjs documentation website.**
36 36
37 Subscribe to the [project mailing-list](https://groups.google.com/forum/#!forum/casperjs) 37 Subscribe to the [project mailing-list](https://groups.google.com/forum/#!forum/casperjs)
38 38
...@@ -45,7 +45,7 @@ First [install CasperJS](http://docs.casperjs.org/en/latest/installation.html), ...@@ -45,7 +45,7 @@ First [install CasperJS](http://docs.casperjs.org/en/latest/installation.html),
45 Sample test to see if some dropdown can be opened: 45 Sample test to see if some dropdown can be opened:
46 46
47 ```javascript 47 ```javascript
48 casper.test.begin('a twitter bootsrap dropdown can be opened', 2, function(test) { 48 casper.test.begin('a twitter bootstrap dropdown can be opened', 2, function(test) {
49 casper.start('http://twitter.github.com/bootstrap/javascript.html#dropdowns', function() { 49 casper.start('http://twitter.github.com/bootstrap/javascript.html#dropdowns', function() {
50 test.assertExists('#navbar-example'); 50 test.assertExists('#navbar-example');
51 this.click('#dropdowns .nav-pills .dropdown:last-of-type a.dropdown-toggle'); 51 this.click('#dropdowns .nav-pills .dropdown:last-of-type a.dropdown-toggle');
......
...@@ -89,7 +89,8 @@ ENGINE_NATIVE_ARGS = [] ...@@ -89,7 +89,8 @@ ENGINE_NATIVE_ARGS = []
89 ENGINE_EXECUTABLE = '' 89 ENGINE_EXECUTABLE = ''
90 90
91 CASPER_ARGS = [] 91 CASPER_ARGS = []
92 CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)), '..')) 92 CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)),
93 '..'))
93 SYS_ARGS = sys.argv[1:] 94 SYS_ARGS = sys.argv[1:]
94 95
95 # retrieve the engine name 96 # retrieve the engine name
...@@ -98,22 +99,29 @@ for arg in SYS_ARGS: ...@@ -98,22 +99,29 @@ for arg in SYS_ARGS:
98 ENGINE = arg[9:].lower() 99 ENGINE = arg[9:].lower()
99 break 100 break
100 101
101 if ENGINE in SUPPORTED_ENGINES: 102 if not ENGINE in SUPPORTED_ENGINES:
102 ENGINE_NATIVE_ARGS = SUPPORTED_ENGINES[ENGINE]['native_args']
103 ENGINE_EXECUTABLE = os.environ.get(SUPPORTED_ENGINES[ENGINE]['env_varname'], SUPPORTED_ENGINES[ENGINE]['default_exec'])
104 else:
105 print('Bad engine name. Only phantomjs and slimerjs are supported') 103 print('Bad engine name. Only phantomjs and slimerjs are supported')
106 sys.exit(1) 104 sys.exit(1)
107 105
106 ENGINE_NATIVE_ARGS = SUPPORTED_ENGINES[ENGINE]['native_args']
107 ENGINE_EXECUTABLE = os.environ.get(SUPPORTED_ENGINES[ENGINE]['env_varname'],
108 SUPPORTED_ENGINES[ENGINE]['default_exec'])
109
110 def extract_arg_name(arg):
111 "parse out any option name"
112 try:
113 return arg.split('=', 1)[0].replace('--', '', 1)
114 except IndexError:
115 return arg
108 116
109 for arg in SYS_ARGS: 117 for arg in SYS_ARGS:
118 arg_name = extract_arg_name(arg)
110 found = False 119 found = False
111 for native in ENGINE_NATIVE_ARGS: 120 for native in ENGINE_NATIVE_ARGS:
112 if arg.startswith('--%s' % native): 121 if arg_name == native:
113 ENGINE_ARGS.append(arg) 122 ENGINE_ARGS.append(arg)
114 found = True 123 found = True
115 if not found: 124 if not found and arg_name != 'engine':
116 if arg.startswith('--engine=') == False:
117 CASPER_ARGS.append(arg) 125 CASPER_ARGS.append(arg)
118 126
119 CASPER_COMMAND = ENGINE_EXECUTABLE.split(' ') 127 CASPER_COMMAND = ENGINE_EXECUTABLE.split(' ')
......
...@@ -6,7 +6,11 @@ ...@@ -6,7 +6,11 @@
6 The ``clientutils`` module 6 The ``clientutils`` module
7 ========================== 7 ==========================
8 8
9 Casper ships with a few client-side utilities which are injected in the remote DOM environment, and accessible from there through the ``__utils__`` object instance of the ``ClientUtils`` class from the ``clientutils`` module. 9 Casper ships with a few client-side utilities which are injected in the remote DOM environment, and accessible from there through the ``__utils__`` object instance of the ``ClientUtils`` class from the ``clientutils`` module::
10
11 casper.evaluate(function() {
12 __utils__.echo("Hello World!");
13 });
10 14
11 .. note:: 15 .. note::
12 16
...@@ -21,7 +25,7 @@ Bookmarklet ...@@ -21,7 +25,7 @@ Bookmarklet
21 25
22 A bookmarklet is also available to help injecting Casper's client-side utilities in the DOM of your favorite browser. 26 A bookmarklet is also available to help injecting Casper's client-side utilities in the DOM of your favorite browser.
23 27
24 Just drag the link above onto your favorites toobar; when clicking, a ``__utils__`` object will be available within the console of your browser: 28 Just drag the following link onto your favorites toobar; when clicking it, a ``__utils__`` object will be available within the console of your browser:
25 29
26 .. raw:: html 30 .. raw:: html
27 31
...@@ -292,6 +296,19 @@ To get the form values:: ...@@ -292,6 +296,19 @@ To get the form values::
292 296
293 __utils__.getFormValues('form#login'); // {username: 'foo', password: 'bar'} 297 __utils__.getFormValues('form#login'); // {username: 'foo', password: 'bar'}
294 298
299 .. index:: log
300
301 ``log()``
302 -------------------------------------------------------------------------------
303
304 **Signature:** ``log(String message[, String level])``
305
306 Logs a message with an optional level. Will format the message a way CasperJS will be able to log phantomjs side. Default level is ``debug``::
307
308 casper.start('http://foo.ner/').thenEvaluate(function() {
309 __utils__.log("We've got a problem on client side", 'error');
310 });
311
295 ``mouseEvent()`` 312 ``mouseEvent()``
296 ------------------------------------------------------------------------------- 313 -------------------------------------------------------------------------------
297 314
......
...@@ -160,9 +160,9 @@ Asserts that a given subject is `falsy <http://11heavens.com/falsy-and-truthy-in ...@@ -160,9 +160,9 @@ Asserts that a given subject is `falsy <http://11heavens.com/falsy-and-truthy-in
160 ``assertField()`` 160 ``assertField()``
161 ------------------------------------------------------------------------------- 161 -------------------------------------------------------------------------------
162 162
163 **Signature:** ``assertField(String inputName, String expected[, String message, Object options])`` 163 **Signature:** ``assertField(String|Object input, String expected[, String message, Object options])``
164 164
165 Asserts that a given form field has the provided value:: 165 Asserts that a given form field has the provided value with input name or :ref:`selector expression <selectors>`::
166 166
167 casper.test.begin('assertField() tests', 1, function(test) { 167 casper.test.begin('assertField() tests', 1, function(test) {
168 casper.start('http://www.google.fr/', function() { 168 casper.start('http://www.google.fr/', function() {
...@@ -173,6 +173,16 @@ Asserts that a given form field has the provided value:: ...@@ -173,6 +173,16 @@ Asserts that a given form field has the provided value::
173 }); 173 });
174 }); 174 });
175 175
176 // Path usage with type 'css'
177 casper.test.begin('assertField() tests', 1, function(test) {
178 casper.start('http://www.google.fr/', function() {
179 this.fill('form[name="gs"]', { q: 'plop' }, false);
180 test.assertField({type: 'css', path: '.q.foo'}, 'plop');
181 }).run(function() {
182 test.done();
183 });
184 });
185
176 .. versionadded:: 1.0 186 .. versionadded:: 1.0
177 187
178 This also works with any input type: ``select``, ``textarea``, etc. 188 This also works with any input type: ``select``, ``textarea``, etc.
...@@ -182,6 +192,63 @@ This also works with any input type: ``select``, ``textarea``, etc. ...@@ -182,6 +192,63 @@ This also works with any input type: ``select``, ``textarea``, etc.
182 The `options` parameter allows to set the options to use with 192 The `options` parameter allows to set the options to use with
183 :ref:`ClientUtils#getFieldValue() <clientutils_getfieldvalue>`. 193 :ref:`ClientUtils#getFieldValue() <clientutils_getfieldvalue>`.
184 194
195 `input` parameter introspects whether or not a `type` key is passed in with `xpath` or `css` and a property `path` specified along with it.
196
197 ``assertFieldName()``
198 -------------------------------------------------------------------------------
199
200 **Signature:** ``assertFieldName(String inputName, String expected[, String message, Object options])``
201
202 .. versionadded:: 1.1-beta3
203
204 Asserts that a given form field has the provided value::
205
206 casper.test.begin('assertField() tests', 1, function(test) {
207 casper.start('http://www.google.fr/', function() {
208 this.fill('form[name="gs"]', { q: 'plop' }, false);
209 test.assertField('q', 'plop', 'did not plop', {formSelector: 'plopper'});
210 }).run(function() {
211 test.done();
212 });
213 });
214
215 ``assertFieldCSS()``
216 -------------------------------------------------------------------------------
217
218 **Signature:** ``assertFieldCSS(String cssSelector, String expected, String message)``
219
220 .. versionadded:: 1.1
221
222 Asserts that a given form field has the provided value given a CSS selector::
223
224 casper.test.begin('assertField() tests', 1, function(test) {
225 casper.start('http://www.google.fr/', function() {
226 this.fill('form[name="gs"]', { q: 'plop' }, false);
227 test.assertField('q', 'plop', 'did not plop', 'input.plop');
228 }).run(function() {
229 test.done();
230 });
231 });
232
233 ``assertFieldXPath()``
234 -------------------------------------------------------------------------------
235
236 **Signature:** ``assertFieldXPath(String xpathSelector, String expected, String message)``
237
238 .. versionadded:: 1.1
239
240 Asserts that a given form field has the provided value given a XPath selector::
241
242 casper.test.begin('assertField() tests', 1, function(test) {
243 casper.start('http://www.google.fr/', function() {
244 this.fill('form[name="gs"]', { q: 'plop' }, false);
245 test.assertField('q', 'plop', 'did not plop', '/html/body/form[0]/input[1]');
246 }).run(function() {
247 test.done();
248 });
249 });
250
251
185 .. index:: HTTP, HTTP Status Code 252 .. index:: HTTP, HTTP Status Code
186 253
187 ``assertHttpStatus()`` 254 ``assertHttpStatus()``
...@@ -716,6 +783,16 @@ That will give something like this: ...@@ -716,6 +783,16 @@ That will give something like this:
716 In c.js:0 783 In c.js:0
717 assertEquals: Subject equals the expected value 784 assertEquals: Subject equals the expected value
718 785
786 .. note::
787
788 In CasperJS 1.1, you can store test successes by recording them listening to the tester ``pass`` event::
789
790 var failures = [];
791
792 casper.test.on("fail", function(failure) {
793 failures.push(failure);
794 });
795
719 ``getPasses()`` 796 ``getPasses()``
720 ------------------------------------------------------------------------------- 797 -------------------------------------------------------------------------------
721 798
...@@ -723,6 +800,8 @@ That will give something like this: ...@@ -723,6 +800,8 @@ That will give something like this:
723 800
724 .. versionadded:: 1.0 801 .. versionadded:: 1.0
725 802
803 .. deprecated:: 1.1
804
726 Retrieves a report for successful test cases in the current test suite:: 805 Retrieves a report for successful test cases in the current test suite::
727 806
728 casper.test.assertEquals(true, true); 807 casper.test.assertEquals(true, true);
...@@ -751,6 +830,16 @@ That will give something like this:: ...@@ -751,6 +830,16 @@ That will give something like this::
751 } 830 }
752 PASS 1 tests executed, 1 passed, 0 failed. 831 PASS 1 tests executed, 1 passed, 0 failed.
753 832
833 .. note::
834
835 In CasperJS 1.1, you can store test successes by recording them listening to the tester ``pass`` event::
836
837 var successes = [];
838
839 casper.test.on("pass", function(success) {
840 successes.push(success);
841 });
842
754 ``info()`` 843 ``info()``
755 ------------------------------------------------------------------------------- 844 -------------------------------------------------------------------------------
756 845
......
...@@ -159,6 +159,15 @@ Options are prefixed with a double-dash (``--``): ...@@ -159,6 +159,15 @@ Options are prefixed with a double-dash (``--``):
159 - ``--xunit=<filename>`` will export test suite results in a :ref:`XUnit XML file <xunit_report>` 159 - ``--xunit=<filename>`` will export test suite results in a :ref:`XUnit XML file <xunit_report>`
160 - ``--direct`` will print :doc:`log messages <logging>` directly to the console 160 - ``--direct`` will print :doc:`log messages <logging>` directly to the console
161 - ``--log-level=<logLevel>`` sets the logging level (see the :doc:`related section <logging>`) 161 - ``--log-level=<logLevel>`` sets the logging level (see the :doc:`related section <logging>`)
162 - ``--auto-exit=no`` prevents the test runner to exit when all the tests have been executed; this usually allows performing supplementary operations, though implies to exit casper manually listening to the ``exit`` tester event::
163
164 // $ casperjs test --auto-exit=no
165 casper.test.on("exit", function() {
166 someTediousAsyncProcess(function() {
167 casper.exit();
168 });
169 });
170
162 171
163 .. versionadded:: 1.0 172 .. versionadded:: 1.0
164 173
......
...@@ -94,11 +94,13 @@ CasperJS 1.1 now internally uses PhantomJS' native ``require()`` function, but i ...@@ -94,11 +94,13 @@ CasperJS 1.1 now internally uses PhantomJS' native ``require()`` function, but i
94 As of 1.1, CasperJS now uses native PhantomJS' ``require()`` function which doesn't support the ``__file__`` builtin variable within custom modules like 1.0 allowed. 94 As of 1.1, CasperJS now uses native PhantomJS' ``require()`` function which doesn't support the ``__file__`` builtin variable within custom modules like 1.0 allowed.
95 95
96 96
97 ``Tester#getFailures()`` and ``Tester#getSuccesses()`` methods removed 97 ``Tester#getFailures()`` and ``Tester#getPasses()`` methods removed
98 ---------------------------------------------------------------------- 98 -------------------------------------------------------------------
99 99
100 These two methods have been removed from the :doc:`Tester <../modules/tester>` API. 100 These two methods have been removed from the :doc:`Tester <../modules/tester>` API.
101 101
102 You can retrieve test failure and success records by simply accessing `tester.currentSuite.failures` and `tester.currentSuite.passes` instead.
103
102 104
103 Step and run completion callbacks don't throw anymore 105 Step and run completion callbacks don't throw anymore
104 ----------------------------------------------------- 106 -----------------------------------------------------
......
...@@ -134,15 +134,20 @@ ...@@ -134,15 +134,20 @@
134 * @return Boolean 134 * @return Boolean
135 */ 135 */
136 this.elementVisible = function elementVisible(elem) { 136 this.elementVisible = function elementVisible(elem) {
137 var style;
137 try { 138 try {
138 var comp = window.getComputedStyle(elem, null); 139 style = window.getComputedStyle(elem, null);
139 return comp.visibility !== 'hidden' &&
140 comp.display !== 'none' &&
141 elem.offsetHeight > 0 &&
142 elem.offsetWidth > 0;
143 } catch (e) { 140 } catch (e) {
144 return false; 141 return false;
145 } 142 }
143 var hidden = style.visibility === 'hidden' || style.display === 'none';
144 if (hidden) {
145 return false;
146 }
147 if (style.display === "inline") {
148 return true;
149 }
150 return elem.clientHeight > 0 && elem.clientWidth > 0;
146 } 151 }
147 152
148 /** 153 /**
...@@ -290,7 +295,7 @@ ...@@ -290,7 +295,7 @@
290 * 295 *
291 * @param String selector CSS3 selector 296 * @param String selector CSS3 selector
292 * @param HTMLElement|null scope Element to search child elements within 297 * @param HTMLElement|null scope Element to search child elements within
293 * @return NodeList|undefined 298 * @return Array|undefined
294 */ 299 */
295 this.findAll = function findAll(selector, scope) { 300 this.findAll = function findAll(selector, scope) {
296 scope = scope || this.options.scope; 301 scope = scope || this.options.scope;
...@@ -299,7 +304,7 @@ ...@@ -299,7 +304,7 @@
299 if (pSelector.type === 'xpath') { 304 if (pSelector.type === 'xpath') {
300 return this.getElementsByXPath(pSelector.path, scope); 305 return this.getElementsByXPath(pSelector.path, scope);
301 } else { 306 } else {
302 return scope.querySelectorAll(pSelector.path); 307 return Array.prototype.slice.call(scope.querySelectorAll(pSelector.path));
303 } 308 }
304 } catch (e) { 309 } catch (e) {
305 this.log('findAll(): invalid selector provided "' + selector + '":' + e, "error"); 310 this.log('findAll(): invalid selector provided "' + selector + '":' + e, "error");
...@@ -560,10 +565,19 @@ ...@@ -560,10 +565,19 @@
560 } 565 }
561 } 566 }
562 var formSelector = ''; 567 var formSelector = '';
563 if (options && options.formSelector) { 568 if (options.formSelector) {
564 formSelector = options.formSelector + ' '; 569 formSelector = options.formSelector + ' ';
565 } 570 }
566 var inputs = this.findAll(formSelector + '[name="' + inputName + '"]'); 571 var inputs = this.findAll(formSelector + '[name="' + inputName + '"]');
572
573 if (options.inputSelector) {
574 inputs = inputs.concat(this.findAll(options.inputSelector));
575 }
576
577 if (options.inputXPath) {
578 inputs = inputs.concat(this.getElementsByXPath(options.inputXPath));
579 }
580
567 switch (inputs.length) { 581 switch (inputs.length) {
568 case 0: return undefined; 582 case 0: return undefined;
569 case 1: return getSingleValue(inputs[0]); 583 case 1: return getSingleValue(inputs[0]);
......
...@@ -449,6 +449,21 @@ Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, messa ...@@ -449,6 +449,21 @@ Tester.prototype.assertEvalEqual = function assertEvalEquals(fn, expected, messa
449 }); 449 });
450 }; 450 };
451 451
452 function baseFieldAssert(inputName, expected, actual, message) {
453 /*jshint validthis:true */
454 "use strict";
455
456 return this.assert(utils.equals(actual, expected), message, {
457 type: 'assertField',
458 standard: f('"%s" input field has the value "%s"', inputName, expected),
459 values: {
460 inputName: inputName,
461 actual: actual,
462 expected: expected
463 }
464 });
465 }
466
452 /** 467 /**
453 * Asserts that the provided assertion fails (used for internal testing). 468 * Asserts that the provided assertion fails (used for internal testing).
454 * 469 *
...@@ -473,26 +488,67 @@ Tester.prototype.assertFail = function assertFail(fn, message) { ...@@ -473,26 +488,67 @@ Tester.prototype.assertFail = function assertFail(fn, message) {
473 /** 488 /**
474 * Asserts that a given input field has the provided value. 489 * Asserts that a given input field has the provided value.
475 * 490 *
476 * @param String inputName The name attribute of the input element 491 * @param String|Object input The name attribute of the input element
492 * or an object with the selector
477 * @param String expected The expected value of the input element 493 * @param String expected The expected value of the input element
478 * @param String message Test description 494 * @param String message Test description
479 * @param Object options ClientUtils#getFieldValue options (optional) 495 * @param Object options ClientUtils#getFieldValue options (optional)
480 * @return Object An assertion result object 496 * @return Object An assertion result object
481 */ 497 */
482 Tester.prototype.assertField = function assertField(inputName, expected, message, options) { 498 Tester.prototype.assertField = function assertField(input, expected, message, options) {
483 "use strict"; 499 "use strict";
500
501 if (typeof input === 'object') {
502 switch (input.type) {
503 case 'css':
504 return this.assertFieldCSS(input.path, expected, message);
505 case 'xpath':
506 return this.assertFieldXPath(input.path, expected, message);
507 default:
508 throw new CasperError('Invalid regexp.');
509 // no default
510 }
511 }
512
484 var actual = this.casper.evaluate(function(inputName, options) { 513 var actual = this.casper.evaluate(function(inputName, options) {
485 return __utils__.getFieldValue(inputName, options); 514 return __utils__.getFieldValue(inputName, options);
486 }, inputName, options); 515 }, input, options);
487 return this.assert(utils.equals(actual, expected), message, { 516
488 type: 'assertField', 517 return baseFieldAssert.call(this, input, expected, actual, message);
489 standard: f('"%s" input field has the value "%s"', inputName, expected), 518 };
490 values: { 519
491 inputName: inputName, 520 /**
492 actual: actual, 521 * Asserts that a given input field by CSS selector has the provided value.
493 expected: expected 522 *
494 } 523 * @param Object cssSelector The CSS selector to use for the assert field value
495 }); 524 * @param String expected The expected value of the input element
525 * @param String message Test description
526 * @return Object An assertion result object
527 */
528 Tester.prototype.assertFieldCSS = function assertFieldCSS(cssSelector, expected, message) {
529 "use strict";
530 var actual = this.casper.evaluate(function(inputName, cssSelector) {
531 return __utils__.getFieldValue(inputName, {inputSelector: cssSelector});
532 }, null, cssSelector);
533
534 return baseFieldAssert.call(this, null, expected, actual, message);
535 };
536
537 /**
538 * Asserts that a given input field by XPath selector has the provided value.
539 *
540 * @param Object xPathSelector The XPath selector to use for the assert field value
541 * @param String expected The expected value of the input element
542 * @param String message Test description
543 * @return Object An assertion result object
544 */
545 Tester.prototype.assertFieldXPath = function assertFieldXPath(xPathSelector, expected, message) {
546 "use strict";
547 var actual = this.casper.evaluate(function(inputName, xPathSelector) {
548 return __utils__.getFieldValue(inputName, {inputXPath: xPathSelector});
549 }, null, xPathSelector);
550
551 return baseFieldAssert.call(this, null, expected, actual, message);
496 }; 552 };
497 553
498 /** 554 /**
...@@ -1426,11 +1482,13 @@ Tester.prototype.renderFailureDetails = function renderFailureDetails() { ...@@ -1426,11 +1482,13 @@ Tester.prototype.renderFailureDetails = function renderFailureDetails() {
1426 /** 1482 /**
1427 * Render tests results, an optionally exit phantomjs. 1483 * Render tests results, an optionally exit phantomjs.
1428 * 1484 *
1429 * @param Boolean exit 1485 * @param Boolean exit Exit casper after results have been rendered?
1486 * @param Number status Exit status code (default: 0)
1487 * @param String save Optional path to file where to save the results log
1430 */ 1488 */
1431 Tester.prototype.renderResults = function renderResults(exit, status, save) { 1489 Tester.prototype.renderResults = function renderResults(exit, status, save) {
1432 "use strict"; 1490 "use strict";
1433 /*jshint maxstatements:20*/ 1491 /*jshint maxstatements:25*/
1434 save = save || this.options.save; 1492 save = save || this.options.save;
1435 var exitStatus = 0, 1493 var exitStatus = 0,
1436 failed = this.suiteResults.countFailed(), 1494 failed = this.suiteResults.countFailed(),
...@@ -1468,6 +1526,7 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) { ...@@ -1468,6 +1526,7 @@ Tester.prototype.renderResults = function renderResults(exit, status, save) {
1468 this.saveResults(save); 1526 this.saveResults(save);
1469 } 1527 }
1470 if (exit === true) { 1528 if (exit === true) {
1529 this.emit("exit");
1471 this.casper.exit(status ? ~~status : exitStatus); 1530 this.casper.exit(status ? ~~status : exitStatus);
1472 } 1531 }
1473 }; 1532 };
......
1 #!/bin/bash
2 #
3 # A little helper script to build the RPM.
4
5 if [ ! -f "../package.json" ]; then
6 echo "Execute rpm build script in rpm directory"
7 exit 1
8 fi
9
10 name="casperjs"
11 name=${name%.spec}
12 topdir=$(mktemp -d)
13 # Get version from package.json
14 version=$(grep '"version"' ../package.json | sed 's/.*"\(.*\)": "\(.*\)".*/\2/' | sed 's/[-]//')
15 builddir=${TMPDIR:-/tmp}/${name}-${version}
16 sourcedir="${topdir}/SOURCES"
17 buildroot="${topdir}/BUILD/${name}-${version}-root"
18
19 mkdir -p ${topdir}/{RPMS,SRPMS,SOURCES,BUILD}
20 mkdir -p ${buildroot} ${builddir}
21
22 echo "=> Copying sources..."
23 ( cd .. && tar cf - ./[A-Z]* ./package.json ./bin ./samples ./tests ./modules | tar xf - -C ${builddir} )
24
25 echo "=> Creating source tarball under ${sourcedir}..."
26 ( cd ${builddir}/.. && tar zcf ${sourcedir}/${name}-${version}.tar.gz ${name}-${version} )
27
28 echo "=> Building RPM..."
29 rpm=$(rpmbuild --define "_topdir ${topdir}" --define "_version ${version}" --buildroot ${buildroot} --clean -bb ${name}.spec | awk '/\/RPMS\// { print $2; }')
30
31 if [ $? -ne 0 ]; then
32 echo "Failed to build RPM package."
33 exit 1
34 fi
35
36 echo ${rpm}
37 cp ${rpm} ${TMPDIR:-/tmp}/
38 rm -fr ${topdir}
39
40 echo "RPM package build finished."
41 echo ${TMPDIR:-/tmp}/${rpm##*/}
1 %define name casperjs
2 %if "%{_version}"
3 %define version %{_version}
4 %else
5 %define version 1.0
6 %endif
7 %define release 1
8 %define prefix /usr
9
10 %define mybuilddir %{_builddir}/%{name}-%{version}-root
11
12 Summary: open source navigation scripting & testing utility written in Javascript
13 Name: %{name}
14 Version: %{version}
15 License: BSD
16 Release: %{release}
17 Packager: Jan Schaumann <jschauma@etsy.com>
18 Group: Utilities/Misc
19 Source: %{name}-%{version}.tar.gz
20 BuildRoot: /tmp/%{name}-%{version}-root
21
22 Requires: phantomjs
23
24 %description
25 CasperJS is an open source navigation scripting & testing utility written
26 in Javascript and based on PhantomJS. It eases the process of defining a
27 full navigation scenario and provides useful high-level functions, methods
28 & syntactic sugar for doing common tasks
29
30 %prep
31 %setup -q
32
33 %install
34 mkdir -p %{mybuilddir}%{prefix}/bin
35 mkdir -p %{mybuilddir}%{prefix}/share/%{name}/bin
36 mkdir -p %{mybuilddir}%{prefix}/share/%{name}/modules
37 mkdir -p %{mybuilddir}%{prefix}/share/%{name}/samples
38 mkdir -p %{mybuilddir}%{prefix}/share/%{name}/tests
39
40 cp bin/%{name} %{mybuilddir}%{prefix}/share/%{name}/bin/
41 ln -s %{prefix}/share/%{name}/bin/%{name} %{mybuilddir}%{prefix}/bin/%{name}
42 cp bin/bootstrap.js %{mybuilddir}%{prefix}/share/%{name}/bin/
43 # Yes, this tool needs this file in the 'bin' directory.
44 cp bin/usage.txt %{mybuilddir}%{prefix}/share/%{name}/bin/
45 cp CHANGELOG.md %{mybuilddir}%{prefix}/share/%{name}/
46 cp CONTRIBUTING.md %{mybuilddir}%{prefix}/share/%{name}/
47 cp CONTRIBUTORS.md %{mybuilddir}%{prefix}/share/%{name}/
48 cp LICENSE.md %{mybuilddir}%{prefix}/share/%{name}/
49 cp README.md %{mybuilddir}%{prefix}/share/%{name}/
50 cp package.json %{mybuilddir}%{prefix}/share/%{name}/
51 cp -R modules/* %{mybuilddir}%{prefix}/share/%{name}/modules/
52 cp -R samples/* %{mybuilddir}%{prefix}/share/%{name}/samples/
53 cp -R tests/* %{mybuilddir}%{prefix}/share/%{name}/tests/
54
55 %files
56 %defattr(0444,root,root)
57 %attr(0555,root,root)%{prefix}/bin/%{name}
58 %attr(0555,root,root)%{prefix}/share/%{name}/bin/%{name}
59 %attr(0555,root,root)%{prefix}/share/%{name}/bin/bootstrap.js
60 %{prefix}/share/%{name}/bin/usage.txt
61 %{prefix}/share/%{name}/CHANGELOG.md
62 %{prefix}/share/%{name}/CONTRIBUTING.md
63 %{prefix}/share/%{name}/CONTRIBUTORS.md
64 %{prefix}/share/%{name}/LICENSE.md
65 %{prefix}/share/%{name}/README.md
66 %{prefix}/share/%{name}/package.json
67 %{prefix}/share/%{name}/modules/*
68 %{prefix}/share/%{name}/samples/*
69 %{prefix}/share/%{name}/tests/*
70
71 %changelog
72 * Fri Nov 15 2013 Yasuo Ohgaki <yohgaki@ohgaki.net>
73 - update spec for master and other branches
74
75 * Mon Dec 24 2012 Nicolas Perriault <nicolas@perriault.net>
76 - removed 'injector.js' module
77
78 * Mon Dec 10 2012 Jan Schaumann <jschauma@etsy.com>
79 - include 'tests'
80
81 * Mon Nov 26 2012 Jan Schaumann <jschauma@etsy.com>
82 - first rpm version
...@@ -9,12 +9,13 @@ import unittest ...@@ -9,12 +9,13 @@ import unittest
9 TEST_ROOT = os.path.abspath(os.path.dirname(__file__)) 9 TEST_ROOT = os.path.abspath(os.path.dirname(__file__))
10 CASPERJS_ROOT = os.path.abspath(os.path.join(TEST_ROOT, '..', '..')) 10 CASPERJS_ROOT = os.path.abspath(os.path.join(TEST_ROOT, '..', '..'))
11 CASPER_EXEC = os.path.join(CASPERJS_ROOT, 'bin', 'casperjs') 11 CASPER_EXEC = os.path.join(CASPERJS_ROOT, 'bin', 'casperjs')
12 PHANTOMJS_EXEC = os.environ['PHANTOMJS_EXECUTABLE'] 12 ENGINE_EXEC = os.environ.get('ENGINE_EXECUTABLE',
13 os.environ.get('PHANTOMJS_EXECUTABLE',
14 "phantomjs"))
13 # make it to an absolute path, because some test change the working directory 15 # make it to an absolute path, because some test change the working directory
14 # and relative path to phantomjs would be invalid 16 # and relative path to phantomjs would be invalid
15 if not os.path.isabs(PHANTOMJS_EXEC): 17 if not os.path.isabs(ENGINE_EXEC):
16 os.environ['PHANTOMJS_EXECUTABLE'] = os.path.join(CASPERJS_ROOT, 18 os.environ['ENGINE_EXECUTABLE'] = os.path.join(CASPERJS_ROOT, ENGINE_EXEC)
17 PHANTOMJS_EXEC)
18 19
19 class TimeoutException(Exception): 20 class TimeoutException(Exception):
20 pass 21 pass
...@@ -316,6 +317,21 @@ class TestCommandOutputTest(CasperExecTestBase): ...@@ -316,6 +317,21 @@ class TestCommandOutputTest(CasperExecTestBase):
316 ], failing=True) 317 ], failing=True)
317 318
318 @timeout(20) 319 @timeout(20)
320 def test_exit_test(self):
321 script_path = os.path.join(TEST_ROOT, 'tester', 'exit.js')
322 self.assertCommandOutputContains('test ' + script_path, [
323 script_path,
324 '# sample',
325 'PASS Subject is strictly true',
326 'PASS 1 test executed',
327 '1 passed',
328 '0 failed',
329 '0 dubious',
330 '0 skipped.',
331 'exited'
332 ])
333
334 @timeout(20)
319 def test_skipped_test(self): 335 def test_skipped_test(self):
320 script_path = os.path.join(TEST_ROOT, 'tester', 'skipped.js') 336 script_path = os.path.join(TEST_ROOT, 'tester', 'skipped.js')
321 self.assertCommandOutputContains('test ' + script_path, [ 337 self.assertCommandOutputContains('test ' + script_path, [
......
1 casper.test.on("exit", function() {
2 console.log("exited");
3 })
4
5 casper.test.begin("sample", function(test) {
6 test.assert(true);
7 test.done();
8 });
...@@ -59,8 +59,9 @@ function checkArgs() { ...@@ -59,8 +59,9 @@ function checkArgs() {
59 casper.options.colorizerType = cls; 59 casper.options.colorizerType = cls;
60 casper.colorizer = colorizer.create(cls); 60 casper.colorizer = colorizer.create(cls);
61 } 61 }
62 casper.test.options.concise = casper.cli.get('concise') || false; 62 casper.test.options.concise = casper.cli.get('concise', false);
63 casper.test.options.failFast = casper.cli.get('fail-fast') || false; 63 casper.test.options.failFast = casper.cli.get('fail-fast', false);
64 casper.test.options.autoExit = casper.cli.get('auto-exit') !== "no";
64 65
65 // test paths are passed as args 66 // test paths are passed as args
66 if (casper.cli.args.length) { 67 if (casper.cli.args.length) {
...@@ -99,7 +100,7 @@ function initRunner() { ...@@ -99,7 +100,7 @@ function initRunner() {
99 100
100 // test suites completion listener 101 // test suites completion listener
101 casper.test.on('tests.complete', function() { 102 casper.test.on('tests.complete', function() {
102 this.renderResults(true, undefined, casper.cli.get('xunit') || undefined); 103 this.renderResults(this.options.autoExit, undefined, casper.cli.get('xunit') || undefined);
103 if (this.options.failFast && this.testResults.failures.length > 0) { 104 if (this.options.failFast && this.testResults.failures.length > 0) {
104 casper.warn('Test suite failed fast, all tests may not have been executed.'); 105 casper.warn('Test suite failed fast, all tests may not have been executed.');
105 } 106 }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
6 </head> 6 </head>
7 <body> 7 <body>
8 <form action="result.html" enctype="multipart/form-data"> 8 <form action="result.html" enctype="multipart/form-data">
9 <input type="text" name="email" placeholder="email"> 9 <input type="text" name="email" placeholder="email" id="email">
10 <input type="password" name="password" placeholder="password"> 10 <input type="password" name="password" placeholder="password">
11 <input type="text" name="language" placeholder="language"> 11 <input type="text" name="language" placeholder="language">
12 <input type="whatever" name="strange"> 12 <input type="whatever" name="strange">
......
...@@ -51,17 +51,17 @@ casper.test.begin('ClientUtils.exists() tests', 5, function(test) { ...@@ -51,17 +51,17 @@ casper.test.begin('ClientUtils.exists() tests', 5, function(test) {
51 casper.test.begin('ClientUtils.findAll() tests', 7, function(test) { 51 casper.test.begin('ClientUtils.findAll() tests', 7, function(test) {
52 var clientutils = require('clientutils').create(); 52 var clientutils = require('clientutils').create();
53 fakeDocument('<ul class="foo"><li>bar</li><li>baz</li></ul>'); 53 fakeDocument('<ul class="foo"><li>bar</li><li>baz</li></ul>');
54 test.assertType(clientutils.findAll('li'), 'nodelist', 54 test.assertType(clientutils.findAll('li'), 'array',
55 'ClientUtils.findAll() can find matching DOM elements'); 55 'ClientUtils.findAll() can find matching DOM elements');
56 test.assertEquals(clientutils.findAll('li').length, 2, 56 test.assertEquals(clientutils.findAll('li').length, 2,
57 'ClientUtils.findAll() can find matching DOM elements'); 57 'ClientUtils.findAll() can find matching DOM elements');
58 test.assertType(clientutils.findAll('ol'), 'nodelist', 58 test.assertType(clientutils.findAll('ol'), 'array',
59 'ClientUtils.findAll() can find matching DOM elements'); 59 'ClientUtils.findAll() can find matching DOM elements');
60 test.assertEquals(clientutils.findAll('ol').length, 0, 60 test.assertEquals(clientutils.findAll('ol').length, 0,
61 'ClientUtils.findAll() can find matching DOM elements'); 61 'ClientUtils.findAll() can find matching DOM elements');
62 // scoped 62 // scoped
63 var scope = clientutils.findOne('ul'); 63 var scope = clientutils.findOne('ul');
64 test.assertType(clientutils.findAll('li', scope), 'nodelist', 64 test.assertType(clientutils.findAll('li', scope), 'array',
65 'ClientUtils.findAll() can find matching DOM elements within a given scope'); 65 'ClientUtils.findAll() can find matching DOM elements within a given scope');
66 test.assertEquals(clientutils.findAll('li', scope).length, 2, 66 test.assertEquals(clientutils.findAll('li', scope).length, 2,
67 'ClientUtils.findAll() can find matching DOM elements within a given scope'); 67 'ClientUtils.findAll() can find matching DOM elements within a given scope');
......
...@@ -129,8 +129,94 @@ casper.test.begin('Tester.assertField(): nonexistent fields', 2, function(test) ...@@ -129,8 +129,94 @@ casper.test.begin('Tester.assertField(): nonexistent fields', 2, function(test)
129 test.assertFail(function() { 129 test.assertFail(function() {
130 test.assertField('nonexistent', ''); 130 test.assertField('nonexistent', '');
131 }, 'Tester.assertField() only checks for existing fields'); 131 }, 'Tester.assertField() only checks for existing fields');
132 }).run(function() {
133 test.done();
134 });
135 });
136
137 casper.test.begin('Tester.assertField(): CSS selectors', 1, function(test) {
138 casper.start('tests/site/form.html', function() {
139 this.fill('form[action="result.html"]', {
140 'email': 'albert@camus.com'
141 });
142
143 test.assertField({
144 type: 'css',
145 path: '#email'
146 },
147 'albert@camus.com',
148 'Tester.assertField() works as expected with CSS selectors'
149 );
150 }).run(function() {
151 test.done();
152 });
153 });
154
155 casper.test.begin('Tester.assertField(): XPath selectors', 1, function(test) {
156 casper.start('tests/site/form.html', function() {
157 this.fill('form[action="result.html"]', {
158 'email': 'albert@camus.com'
132 }); 159 });
133 casper.run(function() { 160
161 test.assertField({
162 type: 'xpath',
163 path: '/html/body/form[1]/input[1]'
164 },
165 'albert@camus.com',
166 'Tester.assertField() works as expected with XPath selectors'
167 );
168 }).run(function() {
134 test.done(); 169 test.done();
135 }) 170 });
171 });
172
173 casper.test.begin('Tester.assertField(): invalid selectors', 1, function(test) {
174 casper.start('tests/site/form.html', function() {
175 this.fill('form[action="result.html"]', {
176 'email': 'albert@camus.com'
177 });
178
179 test.assertRaise(function() {
180 test.assertField({
181 type: 'albert'
182 },
183 'albert@camus.com',
184 'Tester.assertField() works as expected with XPath selectors'
185 );
186 }, [], 'should throw an error for an invalid selector');
187 }).run(function() {
188 test.done();
189 });
190 });
191
192 casper.test.begin('Tester.assertFieldCSS(): CSS selectors', 1, function(test) {
193 casper.start('tests/site/form.html', function() {
194 this.fill('form[action="result.html"]', {
195 'email': 'albert@camus.com'
196 });
197
198 test.assertFieldCSS(
199 '#email',
200 'albert@camus.com',
201 'Tester.assertFieldCSS() works as expected with CSS selectors'
202 );
203 }).run(function() {
204 test.done();
205 });
206 });
207
208 casper.test.begin('Tester.assertFieldXPath(): XPath selectors', 1, function(test) {
209 casper.start('tests/site/form.html', function() {
210 this.fill('form[action="result.html"]', {
211 'email': 'albert@camus.com'
212 });
213
214 test.assertFieldXPath(
215 '/html/body/form[1]/input[1]',
216 'albert@camus.com',
217 'Tester.assertFieldXPath() works as expected with XPath selectors'
218 );
219 }).run(function() {
220 test.done();
221 });
136 }); 222 });
......