OLD | NEW |
1 /* | 1 /* |
2 * testharness-helpers contains various useful extensions to testharness.js to | 2 * testharness-helpers contains various useful extensions to testharness.js to |
3 * allow them to be used across multiple tests before they have been | 3 * allow them to be used across multiple tests before they have been |
4 * upstreamed. This file is intended to be usable from both document and worker | 4 * upstreamed. This file is intended to be usable from both document and worker |
5 * environments, so code should for example not rely on the DOM. | 5 * environments, so code should for example not rely on the DOM. |
6 */ | 6 */ |
7 | 7 |
8 // 'promise_test' is a new kind of testharness test that handles some | 8 // A testharness test that simplifies testing with promises. |
9 // boilerplate for testing with promises. | 9 // |
| 10 // * The |func| argument should be a reference to a function that optionally |
| 11 // takes a test object as an argument and returns a Promise (or really any |
| 12 // thenable object). |
| 13 // |
| 14 // * Resolution of the promise completes the test. A rejection causes the test |
| 15 // to fail. The test harness waits for the promise to resolve. |
| 16 // |
| 17 // * Assertions can be made at any point in the promise handlers. Assertions |
| 18 // only need to be wrapped in test.step_func()s if the promise chain contains |
| 19 // rejection handlers. |
| 20 // |
| 21 // E.g.: |
| 22 // promise_test(function(t) { |
| 23 // return method_that_returns_a_promise() |
| 24 // .then(function(result) { |
| 25 // assert_equals(result, expected_result, "Should be expected."); |
| 26 // }); |
| 27 // }, 'Promise based test'); |
10 function promise_test(func, name, properties) { | 28 function promise_test(func, name, properties) { |
11 properties = properties || {}; | 29 properties = properties || {}; |
12 var test = async_test(name, properties); | 30 var test = async_test(name, properties); |
13 Promise.resolve(test.step(func, test, test)) | 31 Promise.resolve(test.step(func, test, test)) |
14 .then(function() { test.done(); }) | 32 .then(function() { test.done(); }) |
15 .catch(test.step_func(function(value) { | 33 .catch(test.step_func(function(value) { |
16 throw value; | 34 throw value; |
17 })); | 35 })); |
18 } | 36 } |
19 | 37 |
20 // Returns a promise that fulfills after the provided |promise| is fulfilled. | 38 // Returns a promise that fulfills after the provided |promise| is fulfilled. |
21 // The |test| succeeds only if |promise| rejects with an exception matching | 39 // The |test| succeeds only if |promise| rejects with an exception matching |
22 // |code|. Accepted values for |code| follow those accepted for assert_throws(). | 40 // |code|. Accepted values for |code| follow those accepted for assert_throws(). |
23 // The optional |description| describes the test being performed. | 41 // The optional |description| describes the test being performed. |
| 42 // |
24 // E.g.: | 43 // E.g.: |
25 // assert_promise_rejects( | 44 // assert_promise_rejects( |
26 // new Promise(...), // something that should throw an exception. | 45 // new Promise(...), // something that should throw an exception. |
27 // 'NotFoundError', | 46 // 'NotFoundError', |
28 // 'Should throw NotFoundError.'); | 47 // 'Should throw NotFoundError.'); |
29 // | 48 // |
30 // assert_promise_rejects( | 49 // assert_promise_rejects( |
31 // new Promise(...), | 50 // new Promise(...), |
32 // new TypeError(), | 51 // new TypeError(), |
33 // 'Should throw TypeError'); | 52 // 'Should throw TypeError'); |
34 function assert_promise_rejects(promise, code, description) { | 53 function assert_promise_rejects(promise, code, description) { |
35 return promise.then( | 54 return promise.then( |
36 function() { | 55 function() { |
37 throw 'assert_promise_rejects: ' + description + ' Promise did not reject.
'; | 56 throw 'assert_promise_rejects: ' + description + ' Promise did not reject.
'; |
38 }, | 57 }, |
39 function(e) { | 58 function(e) { |
40 if (code !== undefined) { | 59 if (code !== undefined) { |
41 assert_throws(code, function() { throw e; }, description); | 60 assert_throws(code, function() { throw e; }, description); |
42 } | 61 } |
43 }); | 62 }); |
44 } | 63 } |
| 64 |
| 65 // Asserts that two objects |actual| and |expected| are weakly equal under the |
| 66 // following definition: |
| 67 // |
| 68 // |a| and |b| are weakly equal if any of the following are true: |
| 69 // 1. If |a| is not an 'object', and |a| === |b|. |
| 70 // 2. If |a| is an 'object', and all of the following are true: |
| 71 // 2.1 |a.p| is weakly equal to |b.p| for all own properties |p| of |a|. |
| 72 // 2.2 Every own property of |b| is an own property of |a|. |
| 73 // |
| 74 // This is a replacement for the the version of assert_object_equals() in |
| 75 // testharness.js. The latter doesn't handle own properties correctly. I.e. if |
| 76 // |a.p| is not an own property, it still requires that |b.p| be an own |
| 77 // property. |
| 78 // |
| 79 // Note that |actual| must not contain cyclic references. |
| 80 self.assert_object_equals = function(actual, expected, description) { |
| 81 var object_stack = []; |
| 82 |
| 83 function _is_equal(actual, expected, prefix) { |
| 84 if (typeof actual !== 'object') { |
| 85 assert_equals(actual, expected, prefix); |
| 86 return; |
| 87 } |
| 88 assert_true(typeof expected === 'object', prefix); |
| 89 assert_equals(object_stack.indexOf(actual), -1, |
| 90 prefix + ' must not contain cyclic references.'); |
| 91 |
| 92 object_stack.push(actual); |
| 93 |
| 94 Object.getOwnPropertyNames(expected).forEach(function(property) { |
| 95 assert_own_property(actual, property, prefix); |
| 96 _is_equal(actual[property], expected[property], |
| 97 prefix + '.' + property); |
| 98 }); |
| 99 Object.getOwnPropertyNames(actual).forEach(function(property) { |
| 100 assert_own_property(expected, property, prefix); |
| 101 }); |
| 102 |
| 103 object_stack.pop(); |
| 104 } |
| 105 |
| 106 _is_equal(actual, expected, |
| 107 (description ? description + ' :' : '') + '[object]'); |
| 108 }; |
| 109 |
| 110 // Equivalent to assert_in_array, but uses a weaker equivalence relation |
| 111 // (assert_object_equals) than '==='. |
| 112 function assert_object_in_array(actual, expected_array, description) { |
| 113 assert_true(expected_array.some(function(element) { |
| 114 try { |
| 115 assert_object_equals(actual, element); |
| 116 return true; |
| 117 } catch (e) { |
| 118 return false; |
| 119 } |
| 120 }), description); |
| 121 } |
| 122 |
| 123 // Assert that the two arrays |actual| and |expected| contain the same set of |
| 124 // elements as determined by assert_object_equals. The order is not significant. |
| 125 // |
| 126 // |expected| is assumed to not contain any duplicates as determined by |
| 127 // assert_object_equals(). |
| 128 function assert_array_equivalent(actual, expected, description) { |
| 129 assert_true(Array.isArray(actual), description); |
| 130 assert_equals(actual.length, expected.length, description); |
| 131 expected.forEach(function(expected_element) { |
| 132 // assert_in_array treats the first argument as being 'actual', and the |
| 133 // second as being 'expected array'. We are switching them around because |
| 134 // we want to be resilient against the |actual| array containing |
| 135 // duplicates. |
| 136 assert_object_in_array(expected_element, actual, description); |
| 137 }); |
| 138 } |
| 139 |
| 140 // Asserts that two arrays |actual| and |expected| contain the same set of |
| 141 // elements as determined by assert_object_equals(). The corresponding elements |
| 142 // must occupy corresponding indices in their respective arrays. |
| 143 function assert_array_objects_equals(actual, expected, description) { |
| 144 assert_true(Array.isArray(actual), description); |
| 145 assert_equals(actual.length, expected.length, description); |
| 146 actual.forEach(function(value, index) { |
| 147 assert_object_equals(value, expected[index], |
| 148 description + ' : object[' + index + ']'); |
| 149 }); |
| 150 } |
OLD | NEW |