How to use length_after_offset method in wpt

Best JavaScript code snippet using wpt

testharness.js

Source:testharness.js Github

copy

Full Screen

1/*global self*/2/*jshint latedef: nofunc*/3/*4Distributed under both the W3C Test Suite License [1] and the W3C53-clause BSD License [2]. To contribute to a W3C Test Suite, see the6policies and contribution forms [3].7[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license8[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license9[3] http://www.w3.org/2004/10/27-testcases10*/11/* Documentation: https://web-platform-tests.org/writing-tests/testharness-api.html12 * (../docs/_writing-tests/testharness-api.md) */13(function (global_scope)14{15 var debug = false;16 // default timeout is 10 seconds, test can override if needed17 var settings = {18 output:true,19 harness_timeout:{20 "normal":10000,21 "long":6000022 },23 test_timeout:null,24 message_events: ["start", "test_state", "result", "completion"]25 };26 var xhtml_ns = "http://www.w3.org/1999/xhtml";27 /*28 * TestEnvironment is an abstraction for the environment in which the test29 * harness is used. Each implementation of a test environment has to provide30 * the following interface:31 *32 * interface TestEnvironment {33 * // Invoked after the global 'tests' object has been created and it's34 * // safe to call add_*_callback() to register event handlers.35 * void on_tests_ready();36 *37 * // Invoked after setup() has been called to notify the test environment38 * // of changes to the test harness properties.39 * void on_new_harness_properties(object properties);40 *41 * // Should return a new unique default test name.42 * DOMString next_default_test_name();43 *44 * // Should return the test harness timeout duration in milliseconds.45 * float test_timeout();46 * };47 */48 /*49 * A test environment with a DOM. The global object is 'window'. By default50 * test results are displayed in a table. Any parent windows receive51 * callbacks or messages via postMessage() when test events occur. See52 * apisample11.html and apisample12.html.53 */54 function WindowTestEnvironment() {55 this.name_counter = 0;56 this.window_cache = null;57 this.output_handler = null;58 this.all_loaded = false;59 var this_obj = this;60 this.message_events = [];61 this.dispatched_messages = [];62 this.message_functions = {63 start: [add_start_callback, remove_start_callback,64 function (properties) {65 this_obj._dispatch("start_callback", [properties],66 {type: "start", properties: properties});67 }],68 test_state: [add_test_state_callback, remove_test_state_callback,69 function(test) {70 this_obj._dispatch("test_state_callback", [test],71 {type: "test_state",72 test: test.structured_clone()});73 }],74 result: [add_result_callback, remove_result_callback,75 function (test) {76 this_obj.output_handler.show_status();77 this_obj._dispatch("result_callback", [test],78 {type: "result",79 test: test.structured_clone()});80 }],81 completion: [add_completion_callback, remove_completion_callback,82 function (tests, harness_status) {83 var cloned_tests = map(tests, function(test) {84 return test.structured_clone();85 });86 this_obj._dispatch("completion_callback", [tests, harness_status],87 {type: "complete",88 tests: cloned_tests,89 status: harness_status.structured_clone()});90 }]91 }92 on_event(window, 'load', function() {93 this_obj.all_loaded = true;94 });95 on_event(window, 'message', function(event) {96 if (event.data && event.data.type === "getmessages" && event.source) {97 // A window can post "getmessages" to receive a duplicate of every98 // message posted by this environment so far. This allows subscribers99 // from fetch_tests_from_window to 'catch up' to the current state of100 // this environment.101 for (var i = 0; i < this_obj.dispatched_messages.length; ++i)102 {103 event.source.postMessage(this_obj.dispatched_messages[i], "*");104 }105 }106 });107 }108 WindowTestEnvironment.prototype._dispatch = function(selector, callback_args, message_arg) {109 this.dispatched_messages.push(message_arg);110 this._forEach_windows(111 function(w, same_origin) {112 if (same_origin) {113 try {114 var has_selector = selector in w;115 } catch(e) {116 // If document.domain was set at some point same_origin can be117 // wrong and the above will fail.118 has_selector = false;119 }120 if (has_selector) {121 try {122 w[selector].apply(undefined, callback_args);123 } catch (e) {124 if (debug) {125 throw e;126 }127 }128 }129 }130 if (supports_post_message(w) && w !== self) {131 w.postMessage(message_arg, "*");132 }133 });134 };135 WindowTestEnvironment.prototype._forEach_windows = function(callback) {136 // Iterate over the windows [self ... top, opener]. The callback is passed137 // two objects, the first one is the window object itself, the second one138 // is a boolean indicating whether or not it's on the same origin as the139 // current window.140 var cache = this.window_cache;141 if (!cache) {142 cache = [[self, true]];143 var w = self;144 var i = 0;145 var so;146 while (w != w.parent) {147 w = w.parent;148 so = is_same_origin(w);149 cache.push([w, so]);150 i++;151 }152 w = window.opener;153 if (w) {154 cache.push([w, is_same_origin(w)]);155 }156 this.window_cache = cache;157 }158 forEach(cache,159 function(a) {160 callback.apply(null, a);161 });162 };163 WindowTestEnvironment.prototype.on_tests_ready = function() {164 var output = new Output();165 this.output_handler = output;166 var this_obj = this;167 add_start_callback(function (properties) {168 this_obj.output_handler.init(properties);169 });170 add_test_state_callback(function(test) {171 this_obj.output_handler.show_status();172 });173 add_result_callback(function (test) {174 this_obj.output_handler.show_status();175 });176 add_completion_callback(function (tests, harness_status) {177 this_obj.output_handler.show_results(tests, harness_status);178 });179 this.setup_messages(settings.message_events);180 };181 WindowTestEnvironment.prototype.setup_messages = function(new_events) {182 var this_obj = this;183 forEach(settings.message_events, function(x) {184 var current_dispatch = this_obj.message_events.indexOf(x) !== -1;185 var new_dispatch = new_events.indexOf(x) !== -1;186 if (!current_dispatch && new_dispatch) {187 this_obj.message_functions[x][0](this_obj.message_functions[x][2]);188 } else if (current_dispatch && !new_dispatch) {189 this_obj.message_functions[x][1](this_obj.message_functions[x][2]);190 }191 });192 this.message_events = new_events;193 }194 WindowTestEnvironment.prototype.next_default_test_name = function() {195 var suffix = this.name_counter > 0 ? " " + this.name_counter : "";196 this.name_counter++;197 return get_title() + suffix;198 };199 WindowTestEnvironment.prototype.on_new_harness_properties = function(properties) {200 this.output_handler.setup(properties);201 if (properties.hasOwnProperty("message_events")) {202 this.setup_messages(properties.message_events);203 }204 };205 WindowTestEnvironment.prototype.add_on_loaded_callback = function(callback) {206 on_event(window, 'load', callback);207 };208 WindowTestEnvironment.prototype.test_timeout = function() {209 var metas = document.getElementsByTagName("meta");210 for (var i = 0; i < metas.length; i++) {211 if (metas[i].name == "timeout") {212 if (metas[i].content == "long") {213 return settings.harness_timeout.long;214 }215 break;216 }217 }218 return settings.harness_timeout.normal;219 };220 /*221 * Base TestEnvironment implementation for a generic web worker.222 *223 * Workers accumulate test results. One or more clients can connect and224 * retrieve results from a worker at any time.225 *226 * WorkerTestEnvironment supports communicating with a client via a227 * MessagePort. The mechanism for determining the appropriate MessagePort228 * for communicating with a client depends on the type of worker and is229 * implemented by the various specializations of WorkerTestEnvironment230 * below.231 *232 * A client document using testharness can use fetch_tests_from_worker() to233 * retrieve results from a worker. See apisample16.html.234 */235 function WorkerTestEnvironment() {236 this.name_counter = 0;237 this.all_loaded = true;238 this.message_list = [];239 this.message_ports = [];240 }241 WorkerTestEnvironment.prototype._dispatch = function(message) {242 this.message_list.push(message);243 for (var i = 0; i < this.message_ports.length; ++i)244 {245 this.message_ports[i].postMessage(message);246 }247 };248 // The only requirement is that port has a postMessage() method. It doesn't249 // have to be an instance of a MessagePort, and often isn't.250 WorkerTestEnvironment.prototype._add_message_port = function(port) {251 this.message_ports.push(port);252 for (var i = 0; i < this.message_list.length; ++i)253 {254 port.postMessage(this.message_list[i]);255 }256 };257 WorkerTestEnvironment.prototype.next_default_test_name = function() {258 var suffix = this.name_counter > 0 ? " " + this.name_counter : "";259 this.name_counter++;260 return get_title() + suffix;261 };262 WorkerTestEnvironment.prototype.on_new_harness_properties = function() {};263 WorkerTestEnvironment.prototype.on_tests_ready = function() {264 var this_obj = this;265 add_start_callback(266 function(properties) {267 this_obj._dispatch({268 type: "start",269 properties: properties,270 });271 });272 add_test_state_callback(273 function(test) {274 this_obj._dispatch({275 type: "test_state",276 test: test.structured_clone()277 });278 });279 add_result_callback(280 function(test) {281 this_obj._dispatch({282 type: "result",283 test: test.structured_clone()284 });285 });286 add_completion_callback(287 function(tests, harness_status) {288 this_obj._dispatch({289 type: "complete",290 tests: map(tests,291 function(test) {292 return test.structured_clone();293 }),294 status: harness_status.structured_clone()295 });296 });297 };298 WorkerTestEnvironment.prototype.add_on_loaded_callback = function() {};299 WorkerTestEnvironment.prototype.test_timeout = function() {300 // Tests running in a worker don't have a default timeout. I.e. all301 // worker tests behave as if settings.explicit_timeout is true.302 return null;303 };304 /*305 * Dedicated web workers.306 * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope307 *308 * This class is used as the test_environment when testharness is running309 * inside a dedicated worker.310 */311 function DedicatedWorkerTestEnvironment() {312 WorkerTestEnvironment.call(this);313 // self is an instance of DedicatedWorkerGlobalScope which exposes314 // a postMessage() method for communicating via the message channel315 // established when the worker is created.316 this._add_message_port(self);317 }318 DedicatedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);319 DedicatedWorkerTestEnvironment.prototype.on_tests_ready = function() {320 WorkerTestEnvironment.prototype.on_tests_ready.call(this);321 // In the absence of an onload notification, we a require dedicated322 // workers to explicitly signal when the tests are done.323 tests.wait_for_finish = true;324 };325 /*326 * Shared web workers.327 * https://html.spec.whatwg.org/multipage/workers.html#sharedworkerglobalscope328 *329 * This class is used as the test_environment when testharness is running330 * inside a shared web worker.331 */332 function SharedWorkerTestEnvironment() {333 WorkerTestEnvironment.call(this);334 var this_obj = this;335 // Shared workers receive message ports via the 'onconnect' event for336 // each connection.337 self.addEventListener("connect",338 function(message_event) {339 this_obj._add_message_port(message_event.source);340 }, false);341 }342 SharedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);343 SharedWorkerTestEnvironment.prototype.on_tests_ready = function() {344 WorkerTestEnvironment.prototype.on_tests_ready.call(this);345 // In the absence of an onload notification, we a require shared346 // workers to explicitly signal when the tests are done.347 tests.wait_for_finish = true;348 };349 /*350 * Service workers.351 * http://www.w3.org/TR/service-workers/352 *353 * This class is used as the test_environment when testharness is running354 * inside a service worker.355 */356 function ServiceWorkerTestEnvironment() {357 WorkerTestEnvironment.call(this);358 this.all_loaded = false;359 this.on_loaded_callback = null;360 var this_obj = this;361 self.addEventListener("message",362 function(event) {363 if (event.data && event.data.type && event.data.type === "connect") {364 this_obj._add_message_port(event.source);365 }366 }, false);367 // The oninstall event is received after the service worker script and368 // all imported scripts have been fetched and executed. It's the369 // equivalent of an onload event for a document. All tests should have370 // been added by the time this event is received, thus it's not371 // necessary to wait until the onactivate event. However, tests for372 // installed service workers need another event which is equivalent to373 // the onload event because oninstall is fired only on installation. The374 // onmessage event is used for that purpose since tests using375 // testharness.js should ask the result to its service worker by376 // PostMessage. If the onmessage event is triggered on the service377 // worker's context, that means the worker's script has been evaluated.378 on_event(self, "install", on_all_loaded);379 on_event(self, "message", on_all_loaded);380 function on_all_loaded() {381 if (this_obj.all_loaded)382 return;383 this_obj.all_loaded = true;384 if (this_obj.on_loaded_callback) {385 this_obj.on_loaded_callback();386 }387 }388 }389 ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);390 ServiceWorkerTestEnvironment.prototype.add_on_loaded_callback = function(callback) {391 if (this.all_loaded) {392 callback();393 } else {394 this.on_loaded_callback = callback;395 }396 };397 /*398 * JavaScript shells.399 *400 * This class is used as the test_environment when testharness is running401 * inside a JavaScript shell.402 */403 function ShellTestEnvironment() {404 this.name_counter = 0;405 this.all_loaded = false;406 this.on_loaded_callback = null;407 Promise.resolve().then(function() {408 this.all_loaded = true409 if (this.on_loaded_callback) {410 this.on_loaded_callback();411 }412 }.bind(this));413 this.message_list = [];414 this.message_ports = [];415 }416 ShellTestEnvironment.prototype.next_default_test_name = function() {417 var suffix = this.name_counter > 0 ? " " + this.name_counter : "";418 this.name_counter++;419 return "Untitled" + suffix;420 };421 ShellTestEnvironment.prototype.on_new_harness_properties = function() {};422 ShellTestEnvironment.prototype.on_tests_ready = function() {};423 ShellTestEnvironment.prototype.add_on_loaded_callback = function(callback) {424 if (this.all_loaded) {425 callback();426 } else {427 this.on_loaded_callback = callback;428 }429 };430 ShellTestEnvironment.prototype.test_timeout = function() {431 // Tests running in a shell don't have a default timeout, so behave as432 // if settings.explicit_timeout is true.433 return null;434 };435 function create_test_environment() {436 if ('document' in global_scope) {437 return new WindowTestEnvironment();438 }439 if ('DedicatedWorkerGlobalScope' in global_scope &&440 global_scope instanceof DedicatedWorkerGlobalScope) {441 return new DedicatedWorkerTestEnvironment();442 }443 if ('SharedWorkerGlobalScope' in global_scope &&444 global_scope instanceof SharedWorkerGlobalScope) {445 return new SharedWorkerTestEnvironment();446 }447 if ('ServiceWorkerGlobalScope' in global_scope &&448 global_scope instanceof ServiceWorkerGlobalScope) {449 return new ServiceWorkerTestEnvironment();450 }451 if ('WorkerGlobalScope' in global_scope &&452 global_scope instanceof WorkerGlobalScope) {453 return new DedicatedWorkerTestEnvironment();454 }455 if (!('location' in global_scope)) {456 return new ShellTestEnvironment();457 }458 throw new Error("Unsupported test environment");459 }460 var test_environment = create_test_environment();461 function is_shared_worker(worker) {462 return 'SharedWorker' in global_scope && worker instanceof SharedWorker;463 }464 function is_service_worker(worker) {465 // The worker object may be from another execution context,466 // so do not use instanceof here.467 return 'ServiceWorker' in global_scope &&468 Object.prototype.toString.call(worker) == '[object ServiceWorker]';469 }470 /*471 * API functions472 */473 function test(func, name, properties)474 {475 if (tests.promise_setup_called) {476 tests.status.status = tests.status.ERROR;477 tests.status.message = '`test` invoked after `promise_setup`';478 tests.complete();479 }480 var test_name = name ? name : test_environment.next_default_test_name();481 var test_obj = new Test(test_name, properties);482 var value = test_obj.step(func, test_obj, test_obj);483 if (value !== undefined) {484 var msg = "Test named \"" + test_name +485 "\" inappropriately returned a value";486 try {487 if (value && value.hasOwnProperty("then")) {488 msg += ", consider using `promise_test` instead";489 }490 } catch (err) {}491 tests.status.status = tests.status.ERROR;492 tests.status.message = msg;493 }494 if (test_obj.phase === test_obj.phases.STARTED) {495 test_obj.done();496 }497 }498 function async_test(func, name, properties)499 {500 if (tests.promise_setup_called) {501 tests.status.status = tests.status.ERROR;502 tests.status.message = '`async_test` invoked after `promise_setup`';503 tests.complete();504 }505 if (typeof func !== "function") {506 properties = name;507 name = func;508 func = null;509 }510 var test_name = name ? name : test_environment.next_default_test_name();511 var test_obj = new Test(test_name, properties);512 if (func) {513 test_obj.step(func, test_obj, test_obj);514 }515 return test_obj;516 }517 function promise_test(func, name, properties) {518 if (typeof func !== "function") {519 properties = name;520 name = func;521 func = null;522 }523 var test_name = name ? name : test_environment.next_default_test_name();524 var test = new Test(test_name, properties);525 test._is_promise_test = true;526 // If there is no promise tests queue make one.527 if (!tests.promise_tests) {528 tests.promise_tests = Promise.resolve();529 }530 tests.promise_tests = tests.promise_tests.then(function() {531 return new Promise(function(resolve) {532 var promise = test.step(func, test, test);533 test.step(function() {534 assert(!!promise, "promise_test", null,535 "test body must return a 'thenable' object (received ${value})",536 {value:promise});537 assert(typeof promise.then === "function", "promise_test", null,538 "test body must return a 'thenable' object (received an object with no `then` method)",539 null);540 });541 // Test authors may use the `step` method within a542 // `promise_test` even though this reflects a mixture of543 // asynchronous control flow paradigms. The "done" callback544 // should be registered prior to the resolution of the545 // user-provided Promise to avoid timeouts in cases where the546 // Promise does not settle but a `step` function has thrown an547 // error.548 add_test_done_callback(test, resolve);549 Promise.resolve(promise)550 .catch(test.step_func(551 function(value) {552 if (value instanceof AssertionError) {553 throw value;554 }555 assert(false, "promise_test", null,556 "Unhandled rejection with value: ${value}", {value:value});557 }))558 .then(function() {559 test.done();560 });561 });562 });563 }564 /**565 * Make a copy of a Promise in the current realm.566 *567 * @param {Promise} promise the given promise that may be from a different568 * realm569 * @returns {Promise}570 *571 * An arbitrary promise provided by the caller may have originated in572 * another frame that have since navigated away, rendering the frame's573 * document inactive. Such a promise cannot be used with `await` or574 * Promise.resolve(), as microtasks associated with it may be prevented575 * from being run. See https://github.com/whatwg/html/issues/5319 for a576 * particular case.577 *578 * In functions we define here, there is an expectation from the caller579 * that the promise is from the current realm, that can always be used with580 * `await`, etc. We therefore create a new promise in this realm that581 * inherit the value and status from the given promise.582 */583 function bring_promise_to_current_realm(promise) {584 return new Promise(promise.then.bind(promise));585 }586 function promise_rejects_js(test, constructor, promise, description) {587 return bring_promise_to_current_realm(promise)588 .then(test.unreached_func("Should have rejected: " + description))589 .catch(function(e) {590 assert_throws_js_impl(constructor, function() { throw e },591 description, "promise_rejects_js");592 });593 }594 /**595 * Assert that a Promise is rejected with the right DOMException.596 *597 * @param test the test argument passed to promise_test598 * @param {number|string} type. See documentation for assert_throws_dom.599 *600 * For the remaining arguments, there are two ways of calling601 * promise_rejects_dom:602 *603 * 1) If the DOMException is expected to come from the current global, the604 * third argument should be the promise expected to reject, and a fourth,605 * optional, argument is the assertion description.606 *607 * 2) If the DOMException is expected to come from some other global, the608 * third argument should be the DOMException constructor from that global,609 * the fourth argument the promise expected to reject, and the fifth,610 * optional, argument the assertion description.611 */612 function promise_rejects_dom(test, type, promiseOrConstructor, descriptionOrPromise, maybeDescription) {613 let constructor, promise, description;614 if (typeof promiseOrConstructor === "function" &&615 promiseOrConstructor.name === "DOMException") {616 constructor = promiseOrConstructor;617 promise = descriptionOrPromise;618 description = maybeDescription;619 } else {620 constructor = self.DOMException;621 promise = promiseOrConstructor;622 description = descriptionOrPromise;623 assert(maybeDescription === undefined,624 "Too many args pased to no-constructor version of promise_rejects_dom");625 }626 return bring_promise_to_current_realm(promise)627 .then(test.unreached_func("Should have rejected: " + description))628 .catch(function(e) {629 assert_throws_dom_impl(type, function() { throw e }, description,630 "promise_rejects_dom", constructor);631 });632 }633 function promise_rejects_exactly(test, exception, promise, description) {634 return bring_promise_to_current_realm(promise)635 .then(test.unreached_func("Should have rejected: " + description))636 .catch(function(e) {637 assert_throws_exactly_impl(exception, function() { throw e },638 description, "promise_rejects_exactly");639 });640 }641 /**642 * This constructor helper allows DOM events to be handled using Promises,643 * which can make it a lot easier to test a very specific series of events,644 * including ensuring that unexpected events are not fired at any point.645 */646 function EventWatcher(test, watchedNode, eventTypes, timeoutPromise)647 {648 if (typeof eventTypes == 'string') {649 eventTypes = [eventTypes];650 }651 var waitingFor = null;652 // This is null unless we are recording all events, in which case it653 // will be an Array object.654 var recordedEvents = null;655 var eventHandler = test.step_func(function(evt) {656 assert_true(!!waitingFor,657 'Not expecting event, but got ' + evt.type + ' event');658 assert_equals(evt.type, waitingFor.types[0],659 'Expected ' + waitingFor.types[0] + ' event, but got ' +660 evt.type + ' event instead');661 if (Array.isArray(recordedEvents)) {662 recordedEvents.push(evt);663 }664 if (waitingFor.types.length > 1) {665 // Pop first event from array666 waitingFor.types.shift();667 return;668 }669 // We need to null out waitingFor before calling the resolve function670 // since the Promise's resolve handlers may call wait_for() which will671 // need to set waitingFor.672 var resolveFunc = waitingFor.resolve;673 waitingFor = null;674 // Likewise, we should reset the state of recordedEvents.675 var result = recordedEvents || evt;676 recordedEvents = null;677 resolveFunc(result);678 });679 for (var i = 0; i < eventTypes.length; i++) {680 watchedNode.addEventListener(eventTypes[i], eventHandler, false);681 }682 /**683 * Returns a Promise that will resolve after the specified event or684 * series of events has occurred.685 *686 * @param options An optional options object. If the 'record' property687 * on this object has the value 'all', when the Promise688 * returned by this function is resolved, *all* Event689 * objects that were waited for will be returned as an690 * array.691 *692 * For example,693 *694 * ```js695 * const watcher = new EventWatcher(t, div, [ 'animationstart',696 * 'animationiteration',697 * 'animationend' ]);698 * return watcher.wait_for([ 'animationstart', 'animationend' ],699 * { record: 'all' }).then(evts => {700 * assert_equals(evts[0].elapsedTime, 0.0);701 * assert_equals(evts[1].elapsedTime, 2.0);702 * });703 * ```704 */705 this.wait_for = function(types, options) {706 if (waitingFor) {707 return Promise.reject('Already waiting for an event or events');708 }709 if (typeof types == 'string') {710 types = [types];711 }712 if (options && options.record && options.record === 'all') {713 recordedEvents = [];714 }715 return new Promise(function(resolve, reject) {716 var timeout = test.step_func(function() {717 // If the timeout fires after the events have been received718 // or during a subsequent call to wait_for, ignore it.719 if (!waitingFor || waitingFor.resolve !== resolve)720 return;721 // This should always fail, otherwise we should have722 // resolved the promise.723 assert_true(waitingFor.types.length == 0,724 'Timed out waiting for ' + waitingFor.types.join(', '));725 var result = recordedEvents;726 recordedEvents = null;727 var resolveFunc = waitingFor.resolve;728 waitingFor = null;729 resolveFunc(result);730 });731 if (timeoutPromise) {732 timeoutPromise().then(timeout);733 }734 waitingFor = {735 types: types,736 resolve: resolve,737 reject: reject738 };739 });740 };741 function stop_watching() {742 for (var i = 0; i < eventTypes.length; i++) {743 watchedNode.removeEventListener(eventTypes[i], eventHandler, false);744 }745 };746 test._add_cleanup(stop_watching);747 return this;748 }749 expose(EventWatcher, 'EventWatcher');750 function setup(func_or_properties, maybe_properties)751 {752 var func = null;753 var properties = {};754 if (arguments.length === 2) {755 func = func_or_properties;756 properties = maybe_properties;757 } else if (func_or_properties instanceof Function) {758 func = func_or_properties;759 } else {760 properties = func_or_properties;761 }762 tests.setup(func, properties);763 test_environment.on_new_harness_properties(properties);764 }765 function promise_setup(func, maybe_properties)766 {767 if (typeof func !== "function") {768 tests.set_status(tests.status.ERROR,769 "promise_test invoked without a function");770 tests.complete();771 return;772 }773 tests.promise_setup_called = true;774 if (!tests.promise_tests) {775 tests.promise_tests = Promise.resolve();776 }777 tests.promise_tests = tests.promise_tests778 .then(function()779 {780 var properties = maybe_properties || {};781 var result;782 tests.setup(null, properties);783 result = func();784 test_environment.on_new_harness_properties(properties);785 if (!result || typeof result.then !== "function") {786 throw "Non-thenable returned by function passed to `promise_setup`";787 }788 return result;789 })790 .catch(function(e)791 {792 tests.set_status(tests.status.ERROR,793 String(e),794 e && e.stack);795 tests.complete();796 });797 }798 function done() {799 if (tests.tests.length === 0) {800 // `done` is invoked after handling uncaught exceptions, so if the801 // harness status is already set, the corresponding message is more802 // descriptive than the generic message defined here.803 if (tests.status.status === null) {804 tests.status.status = tests.status.ERROR;805 tests.status.message = "done() was called without first defining any tests";806 }807 tests.complete();808 return;809 }810 if (tests.file_is_test) {811 // file is test files never have asynchronous cleanup logic,812 // meaning the fully-synchronous `done` function can be used here.813 tests.tests[0].done();814 }815 tests.end_wait();816 }817 function generate_tests(func, args, properties) {818 forEach(args, function(x, i)819 {820 var name = x[0];821 test(function()822 {823 func.apply(this, x.slice(1));824 },825 name,826 Array.isArray(properties) ? properties[i] : properties);827 });828 }829 /*830 * Register a function as a DOM event listener to the given object for the831 * event bubbling phase.832 *833 * This function was deprecated in November of 2019.834 */835 function on_event(object, event, callback)836 {837 object.addEventListener(event, callback, false);838 }839 function step_timeout(f, t) {840 var outer_this = this;841 var args = Array.prototype.slice.call(arguments, 2);842 return setTimeout(function() {843 f.apply(outer_this, args);844 }, t * tests.timeout_multiplier);845 }846 expose(test, 'test');847 expose(async_test, 'async_test');848 expose(promise_test, 'promise_test');849 expose(promise_rejects_js, 'promise_rejects_js');850 expose(promise_rejects_dom, 'promise_rejects_dom');851 expose(promise_rejects_exactly, 'promise_rejects_exactly');852 expose(generate_tests, 'generate_tests');853 expose(setup, 'setup');854 expose(promise_setup, 'promise_setup');855 expose(done, 'done');856 expose(on_event, 'on_event');857 expose(step_timeout, 'step_timeout');858 /*859 * Return a string truncated to the given length, with ... added at the end860 * if it was longer.861 */862 function truncate(s, len)863 {864 if (s.length > len) {865 return s.substring(0, len - 3) + "...";866 }867 return s;868 }869 /*870 * Return true if object is probably a Node object.871 */872 function is_node(object)873 {874 // I use duck-typing instead of instanceof, because875 // instanceof doesn't work if the node is from another window (like an876 // iframe's contentWindow):877 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295878 try {879 var has_node_properties = ("nodeType" in object &&880 "nodeName" in object &&881 "nodeValue" in object &&882 "childNodes" in object);883 } catch (e) {884 // We're probably cross-origin, which means we aren't a node885 return false;886 }887 if (has_node_properties) {888 try {889 object.nodeType;890 } catch (e) {891 // The object is probably Node.prototype or another prototype892 // object that inherits from it, and not a Node instance.893 return false;894 }895 return true;896 }897 return false;898 }899 var replacements = {900 "0": "0",901 "1": "x01",902 "2": "x02",903 "3": "x03",904 "4": "x04",905 "5": "x05",906 "6": "x06",907 "7": "x07",908 "8": "b",909 "9": "t",910 "10": "n",911 "11": "v",912 "12": "f",913 "13": "r",914 "14": "x0e",915 "15": "x0f",916 "16": "x10",917 "17": "x11",918 "18": "x12",919 "19": "x13",920 "20": "x14",921 "21": "x15",922 "22": "x16",923 "23": "x17",924 "24": "x18",925 "25": "x19",926 "26": "x1a",927 "27": "x1b",928 "28": "x1c",929 "29": "x1d",930 "30": "x1e",931 "31": "x1f",932 "0xfffd": "ufffd",933 "0xfffe": "ufffe",934 "0xffff": "uffff",935 };936 /*937 * Convert a value to a nice, human-readable string938 */939 function format_value(val, seen)940 {941 if (!seen) {942 seen = [];943 }944 if (typeof val === "object" && val !== null) {945 if (seen.indexOf(val) >= 0) {946 return "[...]";947 }948 seen.push(val);949 }950 if (Array.isArray(val)) {951 let output = "[";952 if (val.beginEllipsis !== undefined) {953 output += "…, ";954 }955 output += val.map(function(x) {return format_value(x, seen);}).join(", ");956 if (val.endEllipsis !== undefined) {957 output += ", …";958 }959 return output + "]";960 }961 switch (typeof val) {962 case "string":963 val = val.replace(/\\/g, "\\\\");964 for (var p in replacements) {965 var replace = "\\" + replacements[p];966 val = val.replace(RegExp(String.fromCharCode(p), "g"), replace);967 }968 return '"' + val.replace(/"/g, '\\"') + '"';969 case "boolean":970 case "undefined":971 return String(val);972 case "number":973 // In JavaScript, -0 === 0 and String(-0) == "0", so we have to974 // special-case.975 if (val === -0 && 1/val === -Infinity) {976 return "-0";977 }978 return String(val);979 case "object":980 if (val === null) {981 return "null";982 }983 // Special-case Node objects, since those come up a lot in my tests. I984 // ignore namespaces.985 if (is_node(val)) {986 switch (val.nodeType) {987 case Node.ELEMENT_NODE:988 var ret = "<" + val.localName;989 for (var i = 0; i < val.attributes.length; i++) {990 ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';991 }992 ret += ">" + val.innerHTML + "</" + val.localName + ">";993 return "Element node " + truncate(ret, 60);994 case Node.TEXT_NODE:995 return 'Text node "' + truncate(val.data, 60) + '"';996 case Node.PROCESSING_INSTRUCTION_NODE:997 return "ProcessingInstruction node with target " + format_value(truncate(val.target, 60)) + " and data " + format_value(truncate(val.data, 60));998 case Node.COMMENT_NODE:999 return "Comment node <!--" + truncate(val.data, 60) + "-->";1000 case Node.DOCUMENT_NODE:1001 return "Document node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children");1002 case Node.DOCUMENT_TYPE_NODE:1003 return "DocumentType node";1004 case Node.DOCUMENT_FRAGMENT_NODE:1005 return "DocumentFragment node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children");1006 default:1007 return "Node object of unknown type";1008 }1009 }1010 /* falls through */1011 default:1012 try {1013 return typeof val + ' "' + truncate(String(val), 1000) + '"';1014 } catch(e) {1015 return ("[stringifying object threw " + String(e) +1016 " with type " + String(typeof e) + "]");1017 }1018 }1019 }1020 expose(format_value, "format_value");1021 /*1022 * Assertions1023 */1024 function assert_true(actual, description)1025 {1026 assert(actual === true, "assert_true", description,1027 "expected true got ${actual}", {actual:actual});1028 }1029 expose(assert_true, "assert_true");1030 function assert_false(actual, description)1031 {1032 assert(actual === false, "assert_false", description,1033 "expected false got ${actual}", {actual:actual});1034 }1035 expose(assert_false, "assert_false");1036 function same_value(x, y) {1037 if (y !== y) {1038 //NaN case1039 return x !== x;1040 }1041 if (x === 0 && y === 0) {1042 //Distinguish +0 and -01043 return 1/x === 1/y;1044 }1045 return x === y;1046 }1047 function assert_equals(actual, expected, description)1048 {1049 /*1050 * Test if two primitives are equal or two objects1051 * are the same object1052 */1053 if (typeof actual != typeof expected) {1054 assert(false, "assert_equals", description,1055 "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}",1056 {expected:expected, actual:actual});1057 return;1058 }1059 assert(same_value(actual, expected), "assert_equals", description,1060 "expected ${expected} but got ${actual}",1061 {expected:expected, actual:actual});1062 }1063 expose(assert_equals, "assert_equals");1064 function assert_not_equals(actual, expected, description)1065 {1066 /*1067 * Test if two primitives are unequal or two objects1068 * are different objects1069 */1070 assert(!same_value(actual, expected), "assert_not_equals", description,1071 "got disallowed value ${actual}",1072 {actual:actual});1073 }1074 expose(assert_not_equals, "assert_not_equals");1075 function assert_in_array(actual, expected, description)1076 {1077 assert(expected.indexOf(actual) != -1, "assert_in_array", description,1078 "value ${actual} not in array ${expected}",1079 {actual:actual, expected:expected});1080 }1081 expose(assert_in_array, "assert_in_array");1082 // This function was deprecated in July of 2015.1083 // See https://github.com/web-platform-tests/wpt/issues/20331084 function assert_object_equals(actual, expected, description)1085 {1086 assert(typeof actual === "object" && actual !== null, "assert_object_equals", description,1087 "value is ${actual}, expected object",1088 {actual: actual});1089 //This needs to be improved a great deal1090 function check_equal(actual, expected, stack)1091 {1092 stack.push(actual);1093 var p;1094 for (p in actual) {1095 assert(expected.hasOwnProperty(p), "assert_object_equals", description,1096 "unexpected property ${p}", {p:p});1097 if (typeof actual[p] === "object" && actual[p] !== null) {1098 if (stack.indexOf(actual[p]) === -1) {1099 check_equal(actual[p], expected[p], stack);1100 }1101 } else {1102 assert(same_value(actual[p], expected[p]), "assert_object_equals", description,1103 "property ${p} expected ${expected} got ${actual}",1104 {p:p, expected:expected[p], actual:actual[p]});1105 }1106 }1107 for (p in expected) {1108 assert(actual.hasOwnProperty(p),1109 "assert_object_equals", description,1110 "expected property ${p} missing", {p:p});1111 }1112 stack.pop();1113 }1114 check_equal(actual, expected, []);1115 }1116 expose(assert_object_equals, "assert_object_equals");1117 function assert_array_equals(actual, expected, description)1118 {1119 const max_array_length = 20;1120 function shorten_array(arr, offset = 0) {1121 // Make ", …" only show up when it would likely reduce the length, not accounting for1122 // fonts.1123 if (arr.length < max_array_length + 2) {1124 return arr;1125 }1126 // By default we want half the elements after the offset and half before1127 // But if that takes us past the end of the array, we have more before, and1128 // if it takes us before the start we have more after.1129 const length_after_offset = Math.floor(max_array_length / 2);1130 let upper_bound = Math.min(length_after_offset + offset, arr.length);1131 const lower_bound = Math.max(upper_bound - max_array_length, 0);1132 if (lower_bound === 0) {1133 upper_bound = max_array_length;1134 }1135 const output = arr.slice(lower_bound, upper_bound);1136 if (lower_bound > 0) {1137 output.beginEllipsis = true;1138 }1139 if (upper_bound < arr.length) {1140 output.endEllipsis = true;1141 }1142 return output;1143 }1144 assert(typeof actual === "object" && actual !== null && "length" in actual,1145 "assert_array_equals", description,1146 "value is ${actual}, expected array",1147 {actual:actual});1148 assert(actual.length === expected.length,1149 "assert_array_equals", description,1150 "lengths differ, expected array ${expected} length ${expectedLength}, got ${actual} length ${actualLength}",1151 {expected:shorten_array(expected, expected.length - 1), expectedLength:expected.length,1152 actual:shorten_array(actual, actual.length - 1), actualLength:actual.length1153 });1154 for (var i = 0; i < actual.length; i++) {1155 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),1156 "assert_array_equals", description,1157 "expected property ${i} to be ${expected} but was ${actual} (expected array ${arrayExpected} got ${arrayActual})",1158 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing",1159 actual:actual.hasOwnProperty(i) ? "present" : "missing",1160 arrayExpected:shorten_array(expected, i), arrayActual:shorten_array(actual, i)});1161 assert(same_value(expected[i], actual[i]),1162 "assert_array_equals", description,1163 "expected property ${i} to be ${expected} but got ${actual} (expected array ${arrayExpected} got ${arrayActual})",1164 {i:i, expected:expected[i], actual:actual[i],1165 arrayExpected:shorten_array(expected, i), arrayActual:shorten_array(actual, i)});1166 }1167 }1168 expose(assert_array_equals, "assert_array_equals");1169 function assert_array_approx_equals(actual, expected, epsilon, description)1170 {1171 /*1172 * Test if two primitive arrays are equal within +/- epsilon1173 */1174 assert(actual.length === expected.length,1175 "assert_array_approx_equals", description,1176 "lengths differ, expected ${expected} got ${actual}",1177 {expected:expected.length, actual:actual.length});1178 for (var i = 0; i < actual.length; i++) {1179 assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),1180 "assert_array_approx_equals", description,1181 "property ${i}, property expected to be ${expected} but was ${actual}",1182 {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing",1183 actual:actual.hasOwnProperty(i) ? "present" : "missing"});1184 assert(typeof actual[i] === "number",1185 "assert_array_approx_equals", description,1186 "property ${i}, expected a number but got a ${type_actual}",1187 {i:i, type_actual:typeof actual[i]});1188 assert(Math.abs(actual[i] - expected[i]) <= epsilon,1189 "assert_array_approx_equals", description,1190 "property ${i}, expected ${expected} +/- ${epsilon}, expected ${expected} but got ${actual}",1191 {i:i, expected:expected[i], actual:actual[i], epsilon:epsilon});1192 }1193 }1194 expose(assert_array_approx_equals, "assert_array_approx_equals");1195 function assert_approx_equals(actual, expected, epsilon, description)1196 {1197 /*1198 * Test if two primitive numbers are equal within +/- epsilon1199 */1200 assert(typeof actual === "number",1201 "assert_approx_equals", description,1202 "expected a number but got a ${type_actual}",1203 {type_actual:typeof actual});1204 assert(Math.abs(actual - expected) <= epsilon,1205 "assert_approx_equals", description,1206 "expected ${expected} +/- ${epsilon} but got ${actual}",1207 {expected:expected, actual:actual, epsilon:epsilon});1208 }1209 expose(assert_approx_equals, "assert_approx_equals");1210 function assert_less_than(actual, expected, description)1211 {1212 /*1213 * Test if a primitive number is less than another1214 */1215 assert(typeof actual === "number",1216 "assert_less_than", description,1217 "expected a number but got a ${type_actual}",1218 {type_actual:typeof actual});1219 assert(actual < expected,1220 "assert_less_than", description,1221 "expected a number less than ${expected} but got ${actual}",1222 {expected:expected, actual:actual});1223 }1224 expose(assert_less_than, "assert_less_than");1225 function assert_greater_than(actual, expected, description)1226 {1227 /*1228 * Test if a primitive number is greater than another1229 */1230 assert(typeof actual === "number",1231 "assert_greater_than", description,1232 "expected a number but got a ${type_actual}",1233 {type_actual:typeof actual});1234 assert(actual > expected,1235 "assert_greater_than", description,1236 "expected a number greater than ${expected} but got ${actual}",1237 {expected:expected, actual:actual});1238 }1239 expose(assert_greater_than, "assert_greater_than");1240 function assert_between_exclusive(actual, lower, upper, description)1241 {1242 /*1243 * Test if a primitive number is between two others1244 */1245 assert(typeof actual === "number",1246 "assert_between_exclusive", description,1247 "expected a number but got a ${type_actual}",1248 {type_actual:typeof actual});1249 assert(actual > lower && actual < upper,1250 "assert_between_exclusive", description,1251 "expected a number greater than ${lower} " +1252 "and less than ${upper} but got ${actual}",1253 {lower:lower, upper:upper, actual:actual});1254 }1255 expose(assert_between_exclusive, "assert_between_exclusive");1256 function assert_less_than_equal(actual, expected, description)1257 {1258 /*1259 * Test if a primitive number is less than or equal to another1260 */1261 assert(typeof actual === "number",1262 "assert_less_than_equal", description,1263 "expected a number but got a ${type_actual}",1264 {type_actual:typeof actual});1265 assert(actual <= expected,1266 "assert_less_than_equal", description,1267 "expected a number less than or equal to ${expected} but got ${actual}",1268 {expected:expected, actual:actual});1269 }1270 expose(assert_less_than_equal, "assert_less_than_equal");1271 function assert_greater_than_equal(actual, expected, description)1272 {1273 /*1274 * Test if a primitive number is greater than or equal to another1275 */1276 assert(typeof actual === "number",1277 "assert_greater_than_equal", description,1278 "expected a number but got a ${type_actual}",1279 {type_actual:typeof actual});1280 assert(actual >= expected,1281 "assert_greater_than_equal", description,1282 "expected a number greater than or equal to ${expected} but got ${actual}",1283 {expected:expected, actual:actual});1284 }1285 expose(assert_greater_than_equal, "assert_greater_than_equal");1286 function assert_between_inclusive(actual, lower, upper, description)1287 {1288 /*1289 * Test if a primitive number is between to two others or equal to either of them1290 */1291 assert(typeof actual === "number",1292 "assert_between_inclusive", description,1293 "expected a number but got a ${type_actual}",1294 {type_actual:typeof actual});1295 assert(actual >= lower && actual <= upper,1296 "assert_between_inclusive", description,1297 "expected a number greater than or equal to ${lower} " +1298 "and less than or equal to ${upper} but got ${actual}",1299 {lower:lower, upper:upper, actual:actual});1300 }1301 expose(assert_between_inclusive, "assert_between_inclusive");1302 function assert_regexp_match(actual, expected, description) {1303 /*1304 * Test if a string (actual) matches a regexp (expected)1305 */1306 assert(expected.test(actual),1307 "assert_regexp_match", description,1308 "expected ${expected} but got ${actual}",1309 {expected:expected, actual:actual});1310 }1311 expose(assert_regexp_match, "assert_regexp_match");1312 function assert_class_string(object, class_string, description) {1313 var actual = {}.toString.call(object);1314 var expected = "[object " + class_string + "]";1315 assert(same_value(actual, expected), "assert_class_string", description,1316 "expected ${expected} but got ${actual}",1317 {expected:expected, actual:actual});1318 }1319 expose(assert_class_string, "assert_class_string");1320 function assert_own_property(object, property_name, description) {1321 assert(object.hasOwnProperty(property_name),1322 "assert_own_property", description,1323 "expected property ${p} missing", {p:property_name});1324 }1325 expose(assert_own_property, "assert_own_property");1326 function assert_not_own_property(object, property_name, description) {1327 assert(!object.hasOwnProperty(property_name),1328 "assert_not_own_property", description,1329 "unexpected property ${p} is found on object", {p:property_name});1330 }1331 expose(assert_not_own_property, "assert_not_own_property");1332 function _assert_inherits(name) {1333 return function (object, property_name, description)1334 {1335 assert(typeof object === "object" || typeof object === "function" ||1336 // Or has [[IsHTMLDDA]] slot1337 String(object) === "[object HTMLAllCollection]",1338 name, description,1339 "provided value is not an object");1340 assert("hasOwnProperty" in object,1341 name, description,1342 "provided value is an object but has no hasOwnProperty method");1343 assert(!object.hasOwnProperty(property_name),1344 name, description,1345 "property ${p} found on object expected in prototype chain",1346 {p:property_name});1347 assert(property_name in object,1348 name, description,1349 "property ${p} not found in prototype chain",1350 {p:property_name});1351 };1352 }1353 expose(_assert_inherits("assert_inherits"), "assert_inherits");1354 expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");1355 function assert_readonly(object, property_name, description)1356 {1357 var initial_value = object[property_name];1358 try {1359 //Note that this can have side effects in the case where1360 //the property has PutForwards1361 object[property_name] = initial_value + "a"; //XXX use some other value here?1362 assert(same_value(object[property_name], initial_value),1363 "assert_readonly", description,1364 "changing property ${p} succeeded",1365 {p:property_name});1366 } finally {1367 object[property_name] = initial_value;1368 }1369 }1370 expose(assert_readonly, "assert_readonly");1371 /**1372 * Assert a JS Error with the expected constructor is thrown.1373 *1374 * @param {object} constructor The expected exception constructor.1375 * @param {Function} func Function which should throw.1376 * @param {string} description Error description for the case that the error is not thrown.1377 */1378 function assert_throws_js(constructor, func, description)1379 {1380 assert_throws_js_impl(constructor, func, description,1381 "assert_throws_js");1382 }1383 expose(assert_throws_js, "assert_throws_js");1384 /**1385 * Like assert_throws_js but allows specifying the assertion type1386 * (assert_throws_js or promise_rejects_js, in practice).1387 */1388 function assert_throws_js_impl(constructor, func, description,1389 assertion_type)1390 {1391 try {1392 func.call(this);1393 assert(false, assertion_type, description,1394 "${func} did not throw", {func:func});1395 } catch (e) {1396 if (e instanceof AssertionError) {1397 throw e;1398 }1399 // Basic sanity-checks on the thrown exception.1400 assert(typeof e === "object",1401 assertion_type, description,1402 "${func} threw ${e} with type ${type}, not an object",1403 {func:func, e:e, type:typeof e});1404 assert(e !== null,1405 assertion_type, description,1406 "${func} threw null, not an object",1407 {func:func});1408 // Basic sanity-check on the passed-in constructor1409 assert(typeof constructor == "function",1410 assertion_type, description,1411 "${constructor} is not a constructor",1412 {constructor:constructor});1413 var obj = constructor;1414 while (obj) {1415 if (typeof obj === "function" &&1416 obj.name === "Error") {1417 break;1418 }1419 obj = Object.getPrototypeOf(obj);1420 }1421 assert(obj != null,1422 assertion_type, description,1423 "${constructor} is not an Error subtype",1424 {constructor:constructor});1425 // And checking that our exception is reasonable1426 assert(e.constructor === constructor &&1427 e.name === constructor.name,1428 assertion_type, description,1429 "${func} threw ${actual} (${actual_name}) expected instance of ${expected} (${expected_name})",1430 {func:func, actual:e, actual_name:e.name,1431 expected:constructor,1432 expected_name:constructor.name});1433 }1434 }1435 /**1436 * Assert a DOMException with the expected type is thrown.1437 *1438 * @param {number|string} type The expected exception name or code. See the1439 * table of names and codes at1440 * https://heycam.github.io/webidl/#dfn-error-names-table1441 * If a number is passed it should be one of the numeric code values1442 * in that table (e.g. 3, 4, etc). If a string is passed it can1443 * either be an exception name (e.g. "HierarchyRequestError",1444 * "WrongDocumentError") or the name of the corresponding error code1445 * (e.g. "HIERARCHY_REQUEST_ERR", "WRONG_DOCUMENT_ERR").1446 *1447 * For the remaining arguments, there are two ways of calling1448 * promise_rejects_dom:1449 *1450 * 1) If the DOMException is expected to come from the current global, the1451 * second argument should be the function expected to throw and a third,1452 * optional, argument is the assertion description.1453 *1454 * 2) If the DOMException is expected to come from some other global, the1455 * second argument should be the DOMException constructor from that global,1456 * the third argument the function expected to throw, and the fourth, optional,1457 * argument the assertion description.1458 */1459 function assert_throws_dom(type, funcOrConstructor, descriptionOrFunc, maybeDescription)1460 {1461 let constructor, func, description;1462 if (funcOrConstructor.name === "DOMException") {1463 constructor = funcOrConstructor;1464 func = descriptionOrFunc;1465 description = maybeDescription;1466 } else {1467 constructor = self.DOMException;1468 func = funcOrConstructor;1469 description = descriptionOrFunc;1470 assert(maybeDescription === undefined,1471 "Too many args pased to no-constructor version of assert_throws_dom");1472 }1473 assert_throws_dom_impl(type, func, description, "assert_throws_dom", constructor)1474 }1475 expose(assert_throws_dom, "assert_throws_dom");1476 /**1477 * Similar to assert_throws_dom but allows specifying the assertion type1478 * (assert_throws_dom or promise_rejects_dom, in practice). The1479 * "constructor" argument must be the DOMException constructor from the1480 * global we expect the exception to come from.1481 */1482 function assert_throws_dom_impl(type, func, description, assertion_type, constructor)1483 {1484 try {1485 func.call(this);1486 assert(false, assertion_type, description,1487 "${func} did not throw", {func:func});1488 } catch (e) {1489 if (e instanceof AssertionError) {1490 throw e;1491 }1492 // Basic sanity-checks on the thrown exception.1493 assert(typeof e === "object",1494 assertion_type, description,1495 "${func} threw ${e} with type ${type}, not an object",1496 {func:func, e:e, type:typeof e});1497 assert(e !== null,1498 assertion_type, description,1499 "${func} threw null, not an object",1500 {func:func});1501 // Sanity-check our type1502 assert(typeof type == "number" ||1503 typeof type == "string",1504 assertion_type, description,1505 "${type} is not a number or string",1506 {type:type});1507 var codename_name_map = {1508 INDEX_SIZE_ERR: 'IndexSizeError',1509 HIERARCHY_REQUEST_ERR: 'HierarchyRequestError',1510 WRONG_DOCUMENT_ERR: 'WrongDocumentError',1511 INVALID_CHARACTER_ERR: 'InvalidCharacterError',1512 NO_MODIFICATION_ALLOWED_ERR: 'NoModificationAllowedError',1513 NOT_FOUND_ERR: 'NotFoundError',1514 NOT_SUPPORTED_ERR: 'NotSupportedError',1515 INUSE_ATTRIBUTE_ERR: 'InUseAttributeError',1516 INVALID_STATE_ERR: 'InvalidStateError',1517 SYNTAX_ERR: 'SyntaxError',1518 INVALID_MODIFICATION_ERR: 'InvalidModificationError',1519 NAMESPACE_ERR: 'NamespaceError',1520 INVALID_ACCESS_ERR: 'InvalidAccessError',1521 TYPE_MISMATCH_ERR: 'TypeMismatchError',1522 SECURITY_ERR: 'SecurityError',1523 NETWORK_ERR: 'NetworkError',1524 ABORT_ERR: 'AbortError',1525 URL_MISMATCH_ERR: 'URLMismatchError',1526 QUOTA_EXCEEDED_ERR: 'QuotaExceededError',1527 TIMEOUT_ERR: 'TimeoutError',1528 INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError',1529 DATA_CLONE_ERR: 'DataCloneError'1530 };1531 var name_code_map = {1532 IndexSizeError: 1,1533 HierarchyRequestError: 3,1534 WrongDocumentError: 4,1535 InvalidCharacterError: 5,1536 NoModificationAllowedError: 7,1537 NotFoundError: 8,1538 NotSupportedError: 9,1539 InUseAttributeError: 10,1540 InvalidStateError: 11,1541 SyntaxError: 12,1542 InvalidModificationError: 13,1543 NamespaceError: 14,1544 InvalidAccessError: 15,1545 TypeMismatchError: 17,1546 SecurityError: 18,1547 NetworkError: 19,1548 AbortError: 20,1549 URLMismatchError: 21,1550 QuotaExceededError: 22,1551 TimeoutError: 23,1552 InvalidNodeTypeError: 24,1553 DataCloneError: 25,1554 EncodingError: 0,1555 NotReadableError: 0,1556 UnknownError: 0,1557 ConstraintError: 0,1558 DataError: 0,1559 TransactionInactiveError: 0,1560 ReadOnlyError: 0,1561 VersionError: 0,1562 OperationError: 0,1563 NotAllowedError: 01564 };1565 var code_name_map = {};1566 for (var key in name_code_map) {1567 if (name_code_map[key] > 0) {1568 code_name_map[name_code_map[key]] = key;1569 }1570 }1571 var required_props = {};1572 var name;1573 if (typeof type === "number") {1574 if (type === 0) {1575 throw new AssertionError('Test bug: ambiguous DOMException code 0 passed to assert_throws_dom()');1576 } else if (!(type in code_name_map)) {1577 throw new AssertionError('Test bug: unrecognized DOMException code "' + type + '" passed to assert_throws_dom()');1578 }1579 name = code_name_map[type];1580 required_props.code = type;1581 } else if (typeof type === "string") {1582 name = type in codename_name_map ? codename_name_map[type] : type;1583 if (!(name in name_code_map)) {1584 throw new AssertionError('Test bug: unrecognized DOMException code name or name "' + type + '" passed to assert_throws_dom()');1585 }1586 required_props.code = name_code_map[name];1587 }1588 if (required_props.code === 0 ||1589 ("name" in e &&1590 e.name !== e.name.toUpperCase() &&1591 e.name !== "DOMException")) {1592 // New style exception: also test the name property.1593 required_props.name = name;1594 }1595 for (var prop in required_props) {1596 assert(prop in e && e[prop] == required_props[prop],1597 assertion_type, description,1598 "${func} threw ${e} that is not a DOMException " + type + ": property ${prop} is equal to ${actual}, expected ${expected}",1599 {func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]});1600 }1601 // Check that the exception is from the right global. This check is last1602 // so more specific, and more informative, checks on the properties can1603 // happen in case a totally incorrect exception is thrown.1604 assert(e.constructor === constructor,1605 assertion_type, description,1606 "${func} threw an exception from the wrong global",1607 {func});1608 }1609 }1610 /**1611 * Assert the provided value is thrown.1612 *1613 * @param {value} exception The expected exception.1614 * @param {Function} func Function which should throw.1615 * @param {string} description Error description for the case that the error is not thrown.1616 */1617 function assert_throws_exactly(exception, func, description)1618 {1619 assert_throws_exactly_impl(exception, func, description,1620 "assert_throws_exactly");1621 }1622 expose(assert_throws_exactly, "assert_throws_exactly");1623 /**1624 * Like assert_throws_exactly but allows specifying the assertion type1625 * (assert_throws_exactly or promise_rejects_exactly, in practice).1626 */1627 function assert_throws_exactly_impl(exception, func, description,1628 assertion_type)1629 {1630 try {1631 func.call(this);1632 assert(false, assertion_type, description,1633 "${func} did not throw", {func:func});1634 } catch (e) {1635 if (e instanceof AssertionError) {1636 throw e;1637 }1638 assert(same_value(e, exception), assertion_type, description,1639 "${func} threw ${e} but we expected it to throw ${exception}",1640 {func:func, e:e, exception:exception});1641 }1642 }1643 function assert_unreached(description) {1644 assert(false, "assert_unreached", description,1645 "Reached unreachable code");1646 }1647 expose(assert_unreached, "assert_unreached");1648 function assert_any(assert_func, actual, expected_array)1649 {1650 var args = [].slice.call(arguments, 3);1651 var errors = [];1652 var passed = false;1653 forEach(expected_array,1654 function(expected)1655 {1656 try {1657 assert_func.apply(this, [actual, expected].concat(args));1658 passed = true;1659 } catch (e) {1660 errors.push(e.message);1661 }1662 });1663 if (!passed) {1664 throw new AssertionError(errors.join("\n\n"));1665 }1666 }1667 expose(assert_any, "assert_any");1668 /**1669 * Assert that a feature is implemented, based on a 'truthy' condition.1670 *1671 * This function should be used to early-exit from tests in which there is1672 * no point continuing without support for a non-optional spec or spec1673 * feature. For example:1674 *1675 * assert_implements(window.Foo, 'Foo is not supported');1676 *1677 * @param {object} condition The truthy value to test1678 * @param {string} description Error description for the case that the condition is not truthy.1679 */1680 function assert_implements(condition, description) {1681 assert(!!condition, "assert_implements", description);1682 }1683 expose(assert_implements, "assert_implements")1684 /**1685 * Assert that an optional feature is implemented, based on a 'truthy' condition.1686 *1687 * This function should be used to early-exit from tests in which there is1688 * no point continuing without support for an explicitly optional spec or1689 * spec feature. For example:1690 *1691 * assert_implements_optional(video.canPlayType("video/webm"),1692 * "webm video playback not supported");1693 *1694 * @param {object} condition The truthy value to test1695 * @param {string} description Error description for the case that the condition is not truthy.1696 */1697 function assert_implements_optional(condition, description) {1698 if (!condition) {1699 throw new OptionalFeatureUnsupportedError(description);1700 }1701 }1702 expose(assert_implements_optional, "assert_implements_optional")1703 function Test(name, properties)1704 {1705 if (tests.file_is_test && tests.tests.length) {1706 throw new Error("Tried to create a test with file_is_test");1707 }1708 this.name = name;1709 this.phase = (tests.is_aborted || tests.phase === tests.phases.COMPLETE) ?1710 this.phases.COMPLETE : this.phases.INITIAL;1711 this.status = this.NOTRUN;1712 this.timeout_id = null;1713 this.index = null;1714 this.properties = properties || {};1715 this.timeout_length = settings.test_timeout;1716 if (this.timeout_length !== null) {1717 this.timeout_length *= tests.timeout_multiplier;1718 }1719 this.message = null;1720 this.stack = null;1721 this.steps = [];1722 this._is_promise_test = false;1723 this.cleanup_callbacks = [];1724 this._user_defined_cleanup_count = 0;1725 this._done_callbacks = [];1726 // Tests declared following harness completion are likely an indication1727 // of a programming error, but they cannot be reported1728 // deterministically.1729 if (tests.phase === tests.phases.COMPLETE) {1730 return;1731 }1732 tests.push(this);1733 }1734 Test.statuses = {1735 PASS:0,1736 FAIL:1,1737 TIMEOUT:2,1738 NOTRUN:3,1739 PRECONDITION_FAILED:41740 };1741 Test.prototype = merge({}, Test.statuses);1742 Test.prototype.phases = {1743 INITIAL:0,1744 STARTED:1,1745 HAS_RESULT:2,1746 CLEANING:3,1747 COMPLETE:41748 };1749 Test.prototype.structured_clone = function()1750 {1751 if (!this._structured_clone) {1752 var msg = this.message;1753 msg = msg ? String(msg) : msg;1754 this._structured_clone = merge({1755 name:String(this.name),1756 properties:merge({}, this.properties),1757 phases:merge({}, this.phases)1758 }, Test.statuses);1759 }1760 this._structured_clone.status = this.status;1761 this._structured_clone.message = this.message;1762 this._structured_clone.stack = this.stack;1763 this._structured_clone.index = this.index;1764 this._structured_clone.phase = this.phase;1765 return this._structured_clone;1766 };1767 Test.prototype.step = function(func, this_obj)1768 {1769 if (this.phase > this.phases.STARTED) {1770 return;1771 }1772 this.phase = this.phases.STARTED;1773 //If we don't get a result before the harness times out that will be a test timeout1774 this.set_status(this.TIMEOUT, "Test timed out");1775 tests.started = true;1776 tests.notify_test_state(this);1777 if (this.timeout_id === null) {1778 this.set_timeout();1779 }1780 this.steps.push(func);1781 if (arguments.length === 1) {1782 this_obj = this;1783 }1784 try {1785 return func.apply(this_obj, Array.prototype.slice.call(arguments, 2));1786 } catch (e) {1787 if (this.phase >= this.phases.HAS_RESULT) {1788 return;1789 }1790 var status = e instanceof OptionalFeatureUnsupportedError ? this.PRECONDITION_FAILED : this.FAIL;1791 var message = String((typeof e === "object" && e !== null) ? e.message : e);1792 var stack = e.stack ? e.stack : null;1793 this.set_status(status, message, stack);1794 this.phase = this.phases.HAS_RESULT;1795 this.done();1796 }1797 };1798 Test.prototype.step_func = function(func, this_obj)1799 {1800 var test_this = this;1801 if (arguments.length === 1) {1802 this_obj = test_this;1803 }1804 return function()1805 {1806 return test_this.step.apply(test_this, [func, this_obj].concat(1807 Array.prototype.slice.call(arguments)));1808 };1809 };1810 Test.prototype.step_func_done = function(func, this_obj)1811 {1812 var test_this = this;1813 if (arguments.length === 1) {1814 this_obj = test_this;1815 }1816 return function()1817 {1818 if (func) {1819 test_this.step.apply(test_this, [func, this_obj].concat(1820 Array.prototype.slice.call(arguments)));1821 }1822 test_this.done();1823 };1824 };1825 Test.prototype.unreached_func = function(description)1826 {1827 return this.step_func(function() {1828 assert_unreached(description);1829 });1830 };1831 Test.prototype.step_timeout = function(f, timeout) {1832 var test_this = this;1833 var args = Array.prototype.slice.call(arguments, 2);1834 return setTimeout(this.step_func(function() {1835 return f.apply(test_this, args);1836 }), timeout * tests.timeout_multiplier);1837 }1838 /*1839 * Private method for registering cleanup functions. `testharness.js`1840 * internals should use this method instead of the public `add_cleanup`1841 * method in order to hide implementation details from the harness status1842 * message in the case errors.1843 */1844 Test.prototype._add_cleanup = function(callback) {1845 this.cleanup_callbacks.push(callback);1846 };1847 /*1848 * Schedule a function to be run after the test result is known, regardless1849 * of passing or failing state. The behavior of this function will not1850 * influence the result of the test, but if an exception is thrown, the1851 * test harness will report an error.1852 */1853 Test.prototype.add_cleanup = function(callback) {1854 this._user_defined_cleanup_count += 1;1855 this._add_cleanup(callback);1856 };1857 Test.prototype.set_timeout = function()1858 {1859 if (this.timeout_length !== null) {1860 var this_obj = this;1861 this.timeout_id = setTimeout(function()1862 {1863 this_obj.timeout();1864 }, this.timeout_length);1865 }1866 };1867 Test.prototype.set_status = function(status, message, stack)1868 {1869 this.status = status;1870 this.message = message;1871 this.stack = stack ? stack : null;1872 };1873 Test.prototype.timeout = function()1874 {1875 this.timeout_id = null;1876 this.set_status(this.TIMEOUT, "Test timed out");1877 this.phase = this.phases.HAS_RESULT;1878 this.done();1879 };1880 Test.prototype.force_timeout = Test.prototype.timeout;1881 /**1882 * Update the test status, initiate "cleanup" functions, and signal test1883 * completion.1884 */1885 Test.prototype.done = function()1886 {1887 if (this.phase >= this.phases.CLEANING) {1888 return;1889 }1890 if (this.phase <= this.phases.STARTED) {1891 this.set_status(this.PASS, null);1892 }1893 if (global_scope.clearTimeout) {1894 clearTimeout(this.timeout_id);1895 }1896 this.cleanup();1897 };1898 function add_test_done_callback(test, callback)1899 {1900 if (test.phase === test.phases.COMPLETE) {1901 callback();1902 return;1903 }1904 test._done_callbacks.push(callback);1905 }1906 /*1907 * Invoke all specified cleanup functions. If one or more produce an error,1908 * the context is in an unpredictable state, so all further testing should1909 * be cancelled.1910 */1911 Test.prototype.cleanup = function() {1912 var error_count = 0;1913 var bad_value_count = 0;1914 function on_error() {1915 error_count += 1;1916 // Abort tests immediately so that tests declared within subsequent1917 // cleanup functions are not run.1918 tests.abort();1919 }1920 var this_obj = this;1921 var results = [];1922 this.phase = this.phases.CLEANING;1923 forEach(this.cleanup_callbacks,1924 function(cleanup_callback) {1925 var result;1926 try {1927 result = cleanup_callback();1928 } catch (e) {1929 on_error();1930 return;1931 }1932 if (!is_valid_cleanup_result(this_obj, result)) {1933 bad_value_count += 1;1934 // Abort tests immediately so that tests declared1935 // within subsequent cleanup functions are not run.1936 tests.abort();1937 }1938 results.push(result);1939 });1940 if (!this._is_promise_test) {1941 cleanup_done(this_obj, error_count, bad_value_count);1942 } else {1943 all_async(results,1944 function(result, done) {1945 if (result && typeof result.then === "function") {1946 result1947 .then(null, on_error)1948 .then(done);1949 } else {1950 done();1951 }1952 },1953 function() {1954 cleanup_done(this_obj, error_count, bad_value_count);1955 });1956 }1957 };1958 /**1959 * Determine if the return value of a cleanup function is valid for a given1960 * test. Any test may return the value `undefined`. Tests created with1961 * `promise_test` may alternatively return "thenable" object values.1962 */1963 function is_valid_cleanup_result(test, result) {1964 if (result === undefined) {1965 return true;1966 }1967 if (test._is_promise_test) {1968 return result && typeof result.then === "function";1969 }1970 return false;1971 }1972 function cleanup_done(test, error_count, bad_value_count) {1973 if (error_count || bad_value_count) {1974 var total = test._user_defined_cleanup_count;1975 tests.status.status = tests.status.ERROR;1976 tests.status.message = "Test named '" + test.name +1977 "' specified " + total +1978 " 'cleanup' function" + (total > 1 ? "s" : "");1979 if (error_count) {1980 tests.status.message += ", and " + error_count + " failed";1981 }1982 if (bad_value_count) {1983 var type = test._is_promise_test ?1984 "non-thenable" : "non-undefined";1985 tests.status.message += ", and " + bad_value_count +1986 " returned a " + type + " value";1987 }1988 tests.status.message += ".";1989 tests.status.stack = null;1990 }1991 test.phase = test.phases.COMPLETE;1992 tests.result(test);1993 forEach(test._done_callbacks,1994 function(callback) {1995 callback();1996 });1997 test._done_callbacks.length = 0;1998 }1999 /*2000 * A RemoteTest object mirrors a Test object on a remote worker. The2001 * associated RemoteWorker updates the RemoteTest object in response to2002 * received events. In turn, the RemoteTest object replicates these events2003 * on the local document. This allows listeners (test result reporting2004 * etc..) to transparently handle local and remote events.2005 */2006 function RemoteTest(clone) {2007 var this_obj = this;2008 Object.keys(clone).forEach(2009 function(key) {2010 this_obj[key] = clone[key];2011 });2012 this.index = null;2013 this.phase = this.phases.INITIAL;2014 this.update_state_from(clone);2015 this._done_callbacks = [];2016 tests.push(this);2017 }2018 RemoteTest.prototype.structured_clone = function() {2019 var clone = {};2020 Object.keys(this).forEach(2021 (function(key) {2022 var value = this[key];2023 // `RemoteTest` instances are responsible for managing2024 // their own "done" callback functions, so those functions2025 // are not relevant in other execution contexts. Because of2026 // this (and because Function values cannot be serialized2027 // for cross-realm transmittance), the property should not2028 // be considered when cloning instances.2029 if (key === '_done_callbacks' ) {2030 return;2031 }2032 if (typeof value === "object" && value !== null) {2033 clone[key] = merge({}, value);2034 } else {2035 clone[key] = value;2036 }2037 }).bind(this));2038 clone.phases = merge({}, this.phases);2039 return clone;2040 };2041 /**2042 * `RemoteTest` instances are objects which represent tests running in2043 * another realm. They do not define "cleanup" functions (if necessary,2044 * such functions are defined on the associated `Test` instance within the2045 * external realm). However, `RemoteTests` may have "done" callbacks (e.g.2046 * as attached by the `Tests` instance responsible for tracking the overall2047 * test status in the parent realm). The `cleanup` method delegates to2048 * `done` in order to ensure that such callbacks are invoked following the2049 * completion of the `RemoteTest`.2050 */2051 RemoteTest.prototype.cleanup = function() {2052 this.done();2053 };2054 RemoteTest.prototype.phases = Test.prototype.phases;2055 RemoteTest.prototype.update_state_from = function(clone) {2056 this.status = clone.status;2057 this.message = clone.message;2058 this.stack = clone.stack;2059 if (this.phase === this.phases.INITIAL) {2060 this.phase = this.phases.STARTED;2061 }2062 };2063 RemoteTest.prototype.done = function() {2064 this.phase = this.phases.COMPLETE;2065 forEach(this._done_callbacks,2066 function(callback) {2067 callback();2068 });2069 }2070 /*2071 * A RemoteContext listens for test events from a remote test context, such2072 * as another window or a worker. These events are then used to construct2073 * and maintain RemoteTest objects that mirror the tests running in the2074 * remote context.2075 *2076 * An optional third parameter can be used as a predicate to filter incoming2077 * MessageEvents.2078 */2079 function RemoteContext(remote, message_target, message_filter) {2080 this.running = true;2081 this.started = false;2082 this.tests = new Array();2083 this.early_exception = null;2084 var this_obj = this;2085 // If remote context is cross origin assigning to onerror is not2086 // possible, so silently catch those errors.2087 try {2088 remote.onerror = function(error) { this_obj.remote_error(error); };2089 } catch (e) {2090 // Ignore.2091 }2092 // Keeping a reference to the remote object and the message handler until2093 // remote_done() is seen prevents the remote object and its message channel2094 // from going away before all the messages are dispatched.2095 this.remote = remote;2096 this.message_target = message_target;2097 this.message_handler = function(message) {2098 var passesFilter = !message_filter || message_filter(message);2099 // The reference to the `running` property in the following2100 // condition is unnecessary because that value is only set to2101 // `false` after the `message_handler` function has been2102 // unsubscribed.2103 // TODO: Simplify the condition by removing the reference.2104 if (this_obj.running && message.data && passesFilter &&2105 (message.data.type in this_obj.message_handlers)) {2106 this_obj.message_handlers[message.data.type].call(this_obj, message.data);2107 }2108 };2109 if (self.Promise) {2110 this.done = new Promise(function(resolve) {2111 this_obj.doneResolve = resolve;2112 });2113 }2114 this.message_target.addEventListener("message", this.message_handler);2115 }2116 RemoteContext.prototype.remote_error = function(error) {2117 if (error.preventDefault) {2118 error.preventDefault();2119 }2120 // Defer interpretation of errors until the testing protocol has2121 // started and the remote test's `allow_uncaught_exception` property2122 // is available.2123 if (!this.started) {2124 this.early_exception = error;2125 } else if (!this.allow_uncaught_exception) {2126 this.report_uncaught(error);2127 }2128 };2129 RemoteContext.prototype.report_uncaught = function(error) {2130 var message = error.message || String(error);2131 var filename = (error.filename ? " " + error.filename: "");2132 // FIXME: Display remote error states separately from main document2133 // error state.2134 tests.set_status(tests.status.ERROR,2135 "Error in remote" + filename + ": " + message,2136 error.stack);2137 };2138 RemoteContext.prototype.start = function(data) {2139 this.started = true;2140 this.allow_uncaught_exception = data.properties.allow_uncaught_exception;2141 if (this.early_exception && !this.allow_uncaught_exception) {2142 this.report_uncaught(this.early_exception);2143 }2144 };2145 RemoteContext.prototype.test_state = function(data) {2146 var remote_test = this.tests[data.test.index];2147 if (!remote_test) {2148 remote_test = new RemoteTest(data.test);2149 this.tests[data.test.index] = remote_test;2150 }2151 remote_test.update_state_from(data.test);2152 tests.notify_test_state(remote_test);2153 };2154 RemoteContext.prototype.test_done = function(data) {2155 var remote_test = this.tests[data.test.index];2156 remote_test.update_state_from(data.test);2157 remote_test.done();2158 tests.result(remote_test);2159 };2160 RemoteContext.prototype.remote_done = function(data) {2161 if (tests.status.status === null &&2162 data.status.status !== data.status.OK) {2163 tests.set_status(data.status.status, data.status.message, data.status.sack);2164 }2165 this.message_target.removeEventListener("message", this.message_handler);2166 this.running = false;2167 // If remote context is cross origin assigning to onerror is not2168 // possible, so silently catch those errors.2169 try {2170 this.remote.onerror = null;2171 } catch (e) {2172 // Ignore.2173 }2174 this.remote = null;2175 this.message_target = null;2176 if (this.doneResolve) {2177 this.doneResolve();2178 }2179 if (tests.all_done()) {2180 tests.complete();2181 }2182 };2183 RemoteContext.prototype.message_handlers = {2184 start: RemoteContext.prototype.start,2185 test_state: RemoteContext.prototype.test_state,2186 result: RemoteContext.prototype.test_done,2187 complete: RemoteContext.prototype.remote_done2188 };2189 /*2190 * Harness2191 */2192 function TestsStatus()2193 {2194 this.status = null;2195 this.message = null;2196 this.stack = null;2197 }2198 TestsStatus.statuses = {2199 OK:0,2200 ERROR:1,2201 TIMEOUT:2,2202 PRECONDITION_FAILED:32203 };2204 TestsStatus.prototype = merge({}, TestsStatus.statuses);2205 TestsStatus.prototype.structured_clone = function()2206 {2207 if (!this._structured_clone) {2208 var msg = this.message;2209 msg = msg ? String(msg) : msg;2210 this._structured_clone = merge({2211 status:this.status,2212 message:msg,2213 stack:this.stack2214 }, TestsStatus.statuses);2215 }2216 return this._structured_clone;2217 };2218 function Tests()2219 {2220 this.tests = [];2221 this.num_pending = 0;2222 this.phases = {2223 INITIAL:0,2224 SETUP:1,2225 HAVE_TESTS:2,2226 HAVE_RESULTS:3,2227 COMPLETE:42228 };2229 this.phase = this.phases.INITIAL;2230 this.properties = {};2231 this.wait_for_finish = false;2232 this.processing_callbacks = false;2233 this.allow_uncaught_exception = false;2234 this.file_is_test = false;2235 // This value is lazily initialized in order to avoid introducing a2236 // dependency on ECMAScript 2015 Promises to all tests.2237 this.promise_tests = null;2238 this.promise_setup_called = false;2239 this.timeout_multiplier = 1;2240 this.timeout_length = test_environment.test_timeout();2241 this.timeout_id = null;2242 this.start_callbacks = [];2243 this.test_state_callbacks = [];2244 this.test_done_callbacks = [];2245 this.all_done_callbacks = [];2246 this.pending_remotes = [];2247 this.status = new TestsStatus();2248 var this_obj = this;2249 test_environment.add_on_loaded_callback(function() {2250 if (this_obj.all_done()) {2251 this_obj.complete();2252 }2253 });2254 this.set_timeout();2255 }2256 Tests.prototype.setup = function(func, properties)2257 {2258 if (this.phase >= this.phases.HAVE_RESULTS) {2259 return;2260 }2261 if (this.phase < this.phases.SETUP) {2262 this.phase = this.phases.SETUP;2263 }2264 this.properties = properties;2265 for (var p in properties) {2266 if (properties.hasOwnProperty(p)) {2267 var value = properties[p];2268 if (p == "allow_uncaught_exception") {2269 this.allow_uncaught_exception = value;2270 } else if (p == "explicit_done" && value) {2271 this.wait_for_finish = true;2272 } else if (p == "explicit_timeout" && value) {2273 this.timeout_length = null;2274 if (this.timeout_id)2275 {2276 clearTimeout(this.timeout_id);2277 }2278 } else if (p == "single_test" && value) {2279 this.set_file_is_test();2280 } else if (p == "timeout_multiplier") {2281 this.timeout_multiplier = value;2282 if (this.timeout_length) {2283 this.timeout_length *= this.timeout_multiplier;2284 }2285 }2286 }2287 }2288 if (func) {2289 try {2290 func();2291 } catch (e) {2292 this.status.status = e instanceof OptionalFeatureUnsupportedError ? this.status.PRECONDITION_FAILED : this.status.ERROR;2293 this.status.message = String(e);2294 this.status.stack = e.stack ? e.stack : null;2295 this.complete();2296 }2297 }2298 this.set_timeout();2299 };2300 Tests.prototype.set_file_is_test = function() {2301 if (this.tests.length > 0) {2302 throw new Error("Tried to set file as test after creating a test");2303 }2304 this.wait_for_finish = true;2305 this.file_is_test = true;2306 // Create the test, which will add it to the list of tests2307 async_test();2308 };2309 Tests.prototype.set_status = function(status, message, stack)2310 {2311 this.status.status = status;2312 this.status.message = message;2313 this.status.stack = stack ? stack : null;2314 };2315 Tests.prototype.set_timeout = function() {2316 if (global_scope.clearTimeout) {2317 var this_obj = this;2318 clearTimeout(this.timeout_id);2319 if (this.timeout_length !== null) {2320 this.timeout_id = setTimeout(function() {2321 this_obj.timeout();2322 }, this.timeout_length);2323 }2324 }2325 };2326 Tests.prototype.timeout = function() {2327 var test_in_cleanup = null;2328 if (this.status.status === null) {2329 forEach(this.tests,2330 function(test) {2331 // No more than one test is expected to be in the2332 // "CLEANUP" phase at any time2333 if (test.phase === test.phases.CLEANING) {2334 test_in_cleanup = test;2335 }2336 test.phase = test.phases.COMPLETE;2337 });2338 // Timeouts that occur while a test is in the "cleanup" phase2339 // indicate that some global state was not properly reverted. This2340 // invalidates the overall test execution, so the timeout should be2341 // reported as an error and cancel the execution of any remaining2342 // tests.2343 if (test_in_cleanup) {2344 this.status.status = this.status.ERROR;2345 this.status.message = "Timeout while running cleanup for " +2346 "test named \"" + test_in_cleanup.name + "\".";2347 tests.status.stack = null;2348 } else {2349 this.status.status = this.status.TIMEOUT;2350 }2351 }2352 this.complete();2353 };2354 Tests.prototype.end_wait = function()2355 {2356 this.wait_for_finish = false;2357 if (this.all_done()) {2358 this.complete();2359 }2360 };2361 Tests.prototype.push = function(test)2362 {2363 if (this.phase < this.phases.HAVE_TESTS) {2364 this.start();2365 }2366 this.num_pending++;2367 test.index = this.tests.push(test);2368 this.notify_test_state(test);2369 };2370 Tests.prototype.notify_test_state = function(test) {2371 var this_obj = this;2372 forEach(this.test_state_callbacks,2373 function(callback) {2374 callback(test, this_obj);2375 });2376 };2377 Tests.prototype.all_done = function() {2378 return this.tests.length > 0 && test_environment.all_loaded &&2379 (this.num_pending === 0 || this.is_aborted) && !this.wait_for_finish &&2380 !this.processing_callbacks &&2381 !this.pending_remotes.some(function(w) { return w.running; });2382 };2383 Tests.prototype.start = function() {2384 this.phase = this.phases.HAVE_TESTS;2385 this.notify_start();2386 };2387 Tests.prototype.notify_start = function() {2388 var this_obj = this;2389 forEach (this.start_callbacks,2390 function(callback)2391 {2392 callback(this_obj.properties);2393 });2394 };2395 Tests.prototype.result = function(test)2396 {2397 // If the harness has already transitioned beyond the `HAVE_RESULTS`2398 // phase, subsequent tests should not cause it to revert.2399 if (this.phase <= this.phases.HAVE_RESULTS) {2400 this.phase = this.phases.HAVE_RESULTS;2401 }2402 this.num_pending--;2403 this.notify_result(test);2404 };2405 Tests.prototype.notify_result = function(test) {2406 var this_obj = this;2407 this.processing_callbacks = true;2408 forEach(this.test_done_callbacks,2409 function(callback)2410 {2411 callback(test, this_obj);2412 });2413 this.processing_callbacks = false;2414 if (this_obj.all_done()) {2415 this_obj.complete();2416 }2417 };2418 Tests.prototype.complete = function() {2419 if (this.phase === this.phases.COMPLETE) {2420 return;2421 }2422 var this_obj = this;2423 var all_complete = function() {2424 this_obj.phase = this_obj.phases.COMPLETE;2425 this_obj.notify_complete();2426 };2427 var incomplete = filter(this.tests,2428 function(test) {2429 return test.phase < test.phases.COMPLETE;2430 });2431 /**2432 * To preserve legacy behavior, overall test completion must be2433 * signaled synchronously.2434 */2435 if (incomplete.length === 0) {2436 all_complete();2437 return;2438 }2439 all_async(incomplete,2440 function(test, testDone)2441 {2442 if (test.phase === test.phases.INITIAL) {2443 test.phase = test.phases.COMPLETE;2444 testDone();2445 } else {2446 add_test_done_callback(test, testDone);2447 test.cleanup();2448 }2449 },2450 all_complete);2451 };2452 /**2453 * Update the harness status to reflect an unrecoverable harness error that2454 * should cancel all further testing. Update all previously-defined tests2455 * which have not yet started to indicate that they will not be executed.2456 */2457 Tests.prototype.abort = function() {2458 this.status.status = this.status.ERROR;2459 this.is_aborted = true;2460 forEach(this.tests,2461 function(test) {2462 if (test.phase === test.phases.INITIAL) {2463 test.phase = test.phases.COMPLETE;2464 }2465 });2466 };2467 /*2468 * Determine if any tests share the same `name` property. Return an array2469 * containing the names of any such duplicates.2470 */2471 Tests.prototype.find_duplicates = function() {2472 var names = Object.create(null);2473 var duplicates = [];2474 forEach (this.tests,2475 function(test)2476 {2477 if (test.name in names && duplicates.indexOf(test.name) === -1) {2478 duplicates.push(test.name);2479 }2480 names[test.name] = true;2481 });2482 return duplicates;2483 };2484 function code_unit_str(char) {2485 return 'U+' + char.charCodeAt(0).toString(16);2486 }2487 function sanitize_unpaired_surrogates(str) {2488 return str.replace(/([\ud800-\udbff])(?![\udc00-\udfff])/g,2489 function(_, unpaired)2490 {2491 return code_unit_str(unpaired);2492 })2493 // This replacement is intentionally implemented without an2494 // ES2018 negative lookbehind assertion to support runtimes2495 // which do not yet implement that language feature.2496 .replace(/(^|[^\ud800-\udbff])([\udc00-\udfff])/g,2497 function(_, previous, unpaired) {2498 if (/[\udc00-\udfff]/.test(previous)) {2499 previous = code_unit_str(previous);2500 }2501 return previous + code_unit_str(unpaired);2502 });2503 }2504 function sanitize_all_unpaired_surrogates(tests) {2505 forEach (tests,2506 function (test)2507 {2508 var sanitized = sanitize_unpaired_surrogates(test.name);2509 if (test.name !== sanitized) {2510 test.name = sanitized;2511 delete test._structured_clone;2512 }2513 });2514 }2515 Tests.prototype.notify_complete = function() {2516 var this_obj = this;2517 var duplicates;2518 if (this.status.status === null) {2519 duplicates = this.find_duplicates();2520 // Some transports adhere to UTF-8's restriction on unpaired2521 // surrogates. Sanitize the titles so that the results can be2522 // consistently sent via all transports.2523 sanitize_all_unpaired_surrogates(this.tests);2524 // Test names are presumed to be unique within test files--this2525 // allows consumers to use them for identification purposes.2526 // Duplicated names violate this expectation and should therefore2527 // be reported as an error.2528 if (duplicates.length) {2529 this.status.status = this.status.ERROR;2530 this.status.message =2531 duplicates.length + ' duplicate test name' +2532 (duplicates.length > 1 ? 's' : '') + ': "' +2533 duplicates.join('", "') + '"';2534 } else {2535 this.status.status = this.status.OK;2536 }2537 }2538 forEach (this.all_done_callbacks,2539 function(callback)2540 {2541 callback(this_obj.tests, this_obj.status);2542 });2543 };2544 /*2545 * Constructs a RemoteContext that tracks tests from a specific worker.2546 */2547 Tests.prototype.create_remote_worker = function(worker) {2548 var message_port;2549 if (is_service_worker(worker)) {2550 message_port = navigator.serviceWorker;2551 worker.postMessage({type: "connect"});2552 } else if (is_shared_worker(worker)) {2553 message_port = worker.port;2554 message_port.start();2555 } else {2556 message_port = worker;2557 }2558 return new RemoteContext(worker, message_port);2559 };2560 /*2561 * Constructs a RemoteContext that tracks tests from a specific window.2562 */2563 Tests.prototype.create_remote_window = function(remote) {2564 remote.postMessage({type: "getmessages"}, "*");2565 return new RemoteContext(2566 remote,2567 window,2568 function(msg) {2569 return msg.source === remote;2570 }2571 );2572 };2573 Tests.prototype.fetch_tests_from_worker = function(worker) {2574 if (this.phase >= this.phases.COMPLETE) {2575 return;2576 }2577 var remoteContext = this.create_remote_worker(worker);2578 this.pending_remotes.push(remoteContext);2579 return remoteContext.done;2580 };2581 function fetch_tests_from_worker(port) {2582 return tests.fetch_tests_from_worker(port);2583 }2584 expose(fetch_tests_from_worker, 'fetch_tests_from_worker');2585 Tests.prototype.fetch_tests_from_window = function(remote) {2586 if (this.phase >= this.phases.COMPLETE) {2587 return;2588 }2589 this.pending_remotes.push(this.create_remote_window(remote));2590 };2591 function fetch_tests_from_window(window) {2592 tests.fetch_tests_from_window(window);2593 }2594 expose(fetch_tests_from_window, 'fetch_tests_from_window');2595 function timeout() {2596 if (tests.timeout_length === null) {2597 tests.timeout();2598 }2599 }2600 expose(timeout, 'timeout');2601 function add_start_callback(callback) {2602 tests.start_callbacks.push(callback);2603 }2604 function add_test_state_callback(callback) {2605 tests.test_state_callbacks.push(callback);2606 }2607 function add_result_callback(callback) {2608 tests.test_done_callbacks.push(callback);2609 }2610 function add_completion_callback(callback) {2611 tests.all_done_callbacks.push(callback);2612 }2613 expose(add_start_callback, 'add_start_callback');2614 expose(add_test_state_callback, 'add_test_state_callback');2615 expose(add_result_callback, 'add_result_callback');2616 expose(add_completion_callback, 'add_completion_callback');2617 function remove(array, item) {2618 var index = array.indexOf(item);2619 if (index > -1) {2620 array.splice(index, 1);2621 }2622 }2623 function remove_start_callback(callback) {2624 remove(tests.start_callbacks, callback);2625 }2626 function remove_test_state_callback(callback) {2627 remove(tests.test_state_callbacks, callback);2628 }2629 function remove_result_callback(callback) {2630 remove(tests.test_done_callbacks, callback);2631 }2632 function remove_completion_callback(callback) {2633 remove(tests.all_done_callbacks, callback);2634 }2635 /*2636 * Output listener2637 */2638 function Output() {2639 this.output_document = document;2640 this.output_node = null;2641 this.enabled = settings.output;2642 this.phase = this.INITIAL;2643 }2644 Output.prototype.INITIAL = 0;2645 Output.prototype.STARTED = 1;2646 Output.prototype.HAVE_RESULTS = 2;2647 Output.prototype.COMPLETE = 3;2648 Output.prototype.setup = function(properties) {2649 if (this.phase > this.INITIAL) {2650 return;2651 }2652 //If output is disabled in testharnessreport.js the test shouldn't be2653 //able to override that2654 this.enabled = this.enabled && (properties.hasOwnProperty("output") ?2655 properties.output : settings.output);2656 };2657 Output.prototype.init = function(properties) {2658 if (this.phase >= this.STARTED) {2659 return;2660 }2661 if (properties.output_document) {2662 this.output_document = properties.output_document;2663 } else {2664 this.output_document = document;2665 }2666 this.phase = this.STARTED;2667 };2668 Output.prototype.resolve_log = function() {2669 var output_document;2670 if (this.output_node) {2671 return;2672 }2673 if (typeof this.output_document === "function") {2674 output_document = this.output_document.apply(undefined);2675 } else {2676 output_document = this.output_document;2677 }2678 if (!output_document) {2679 return;2680 }2681 var node = output_document.getElementById("log");2682 if (!node) {2683 if (output_document.readyState === "loading") {2684 return;2685 }2686 node = output_document.createElementNS("http://www.w3.org/1999/xhtml", "div");2687 node.id = "log";2688 if (output_document.body) {2689 output_document.body.appendChild(node);2690 } else {2691 var root = output_document.documentElement;2692 var is_html = (root &&2693 root.namespaceURI == "http://www.w3.org/1999/xhtml" &&2694 root.localName == "html");2695 var is_svg = (output_document.defaultView &&2696 "SVGSVGElement" in output_document.defaultView &&2697 root instanceof output_document.defaultView.SVGSVGElement);2698 if (is_svg) {2699 var foreignObject = output_document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");2700 foreignObject.setAttribute("width", "100%");2701 foreignObject.setAttribute("height", "100%");2702 root.appendChild(foreignObject);2703 foreignObject.appendChild(node);2704 } else if (is_html) {2705 root.appendChild(output_document.createElementNS("http://www.w3.org/1999/xhtml", "body"))2706 .appendChild(node);2707 } else {2708 root.appendChild(node);2709 }2710 }2711 }2712 this.output_document = output_document;2713 this.output_node = node;2714 };2715 Output.prototype.show_status = function() {2716 if (this.phase < this.STARTED) {2717 this.init();2718 }2719 if (!this.enabled || this.phase === this.COMPLETE) {2720 return;2721 }2722 this.resolve_log();2723 if (this.phase < this.HAVE_RESULTS) {2724 this.phase = this.HAVE_RESULTS;2725 }2726 var done_count = tests.tests.length - tests.num_pending;2727 if (this.output_node) {2728 if (done_count < 100 ||2729 (done_count < 1000 && done_count % 100 === 0) ||2730 done_count % 1000 === 0) {2731 this.output_node.textContent = "Running, " +2732 done_count + " complete, " +2733 tests.num_pending + " remain";2734 }2735 }2736 };2737 Output.prototype.show_results = function (tests, harness_status) {2738 if (this.phase >= this.COMPLETE) {2739 return;2740 }2741 if (!this.enabled) {2742 return;2743 }2744 if (!this.output_node) {2745 this.resolve_log();2746 }2747 this.phase = this.COMPLETE;2748 var log = this.output_node;2749 if (!log) {2750 return;2751 }2752 var output_document = this.output_document;2753 while (log.lastChild) {2754 log.removeChild(log.lastChild);2755 }2756 var stylesheet = output_document.createElementNS(xhtml_ns, "style");2757 stylesheet.textContent = stylesheetContent;2758 var heads = output_document.getElementsByTagName("head");2759 if (heads.length) {2760 heads[0].appendChild(stylesheet);2761 }2762 var status_text_harness = {};2763 status_text_harness[harness_status.OK] = "OK";2764 status_text_harness[harness_status.ERROR] = "Error";2765 status_text_harness[harness_status.TIMEOUT] = "Timeout";2766 status_text_harness[harness_status.PRECONDITION_FAILED] = "Optional Feature Unsupported";2767 var status_text = {};2768 status_text[Test.prototype.PASS] = "Pass";2769 status_text[Test.prototype.FAIL] = "Fail";2770 status_text[Test.prototype.TIMEOUT] = "Timeout";2771 status_text[Test.prototype.NOTRUN] = "Not Run";2772 status_text[Test.prototype.PRECONDITION_FAILED] = "Optional Feature Unsupported";2773 var status_number = {};2774 forEach(tests,2775 function(test) {2776 var status = status_text[test.status];2777 if (status_number.hasOwnProperty(status)) {2778 status_number[status] += 1;2779 } else {2780 status_number[status] = 1;2781 }2782 });2783 function status_class(status)2784 {2785 return status.replace(/\s/g, '').toLowerCase();2786 }2787 var summary_template = ["section", {"id":"summary"},2788 ["h2", {}, "Summary"],2789 function()2790 {2791 var status = status_text_harness[harness_status.status];2792 var rv = [["section", {},2793 ["p", {},2794 "Harness status: ",2795 ["span", {"class":status_class(status)},2796 status2797 ],2798 ]2799 ]];2800 if (harness_status.status === harness_status.ERROR) {2801 rv[0].push(["pre", {}, harness_status.message]);2802 if (harness_status.stack) {2803 rv[0].push(["pre", {}, harness_status.stack]);2804 }2805 }2806 return rv;2807 },2808 ["p", {}, "Found ${num_tests} tests"],2809 function() {2810 var rv = [["div", {}]];2811 var i = 0;2812 while (status_text.hasOwnProperty(i)) {2813 if (status_number.hasOwnProperty(status_text[i])) {2814 var status = status_text[i];2815 rv[0].push(["div", {"class":status_class(status)},2816 ["label", {},2817 ["input", {type:"checkbox", checked:"checked"}],2818 status_number[status] + " " + status]]);2819 }2820 i++;2821 }2822 return rv;2823 },2824 ];2825 log.appendChild(render(summary_template, {num_tests:tests.length}, output_document));2826 forEach(output_document.querySelectorAll("section#summary label"),2827 function(element)2828 {2829 on_event(element, "click",2830 function(e)2831 {2832 if (output_document.getElementById("results") === null) {2833 e.preventDefault();2834 return;2835 }2836 var result_class = element.parentNode.getAttribute("class");2837 var style_element = output_document.querySelector("style#hide-" + result_class);2838 var input_element = element.querySelector("input");2839 if (!style_element && !input_element.checked) {2840 style_element = output_document.createElementNS(xhtml_ns, "style");2841 style_element.id = "hide-" + result_class;2842 style_element.textContent = "table#results > tbody > tr."+result_class+"{display:none}";2843 output_document.body.appendChild(style_element);2844 } else if (style_element && input_element.checked) {2845 style_element.parentNode.removeChild(style_element);2846 }2847 });2848 });2849 // This use of innerHTML plus manual escaping is not recommended in2850 // general, but is necessary here for performance. Using textContent2851 // on each individual <td> adds tens of seconds of execution time for2852 // large test suites (tens of thousands of tests).2853 function escape_html(s)2854 {2855 return s.replace(/\&/g, "&amp;")2856 .replace(/</g, "&lt;")2857 .replace(/"/g, "&quot;")2858 .replace(/'/g, "&#39;");2859 }2860 function has_assertions()2861 {2862 for (var i = 0; i < tests.length; i++) {2863 if (tests[i].properties.hasOwnProperty("assert")) {2864 return true;2865 }2866 }2867 return false;2868 }2869 function get_assertion(test)2870 {2871 if (test.properties.hasOwnProperty("assert")) {2872 if (Array.isArray(test.properties.assert)) {2873 return test.properties.assert.join(' ');2874 }2875 return test.properties.assert;2876 }2877 return '';2878 }2879 log.appendChild(document.createElementNS(xhtml_ns, "section"));2880 var assertions = has_assertions();2881 var html = "<h2>Details</h2><table id='results' " + (assertions ? "class='assertions'" : "" ) + ">" +2882 "<thead><tr><th>Result</th><th>Test Name</th>" +2883 (assertions ? "<th>Assertion</th>" : "") +2884 "<th>Message</th></tr></thead>" +2885 "<tbody>";2886 for (var i = 0; i < tests.length; i++) {2887 html += '<tr class="' +2888 escape_html(status_class(status_text[tests[i].status])) +2889 '"><td>' +2890 escape_html(status_text[tests[i].status]) +2891 "</td><td>" +2892 escape_html(tests[i].name) +2893 "</td><td>" +2894 (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "") +2895 escape_html(tests[i].message ? tests[i].message : " ") +2896 (tests[i].stack ? "<pre>" +2897 escape_html(tests[i].stack) +2898 "</pre>": "") +2899 "</td></tr>";2900 }2901 html += "</tbody></table>";2902 try {2903 log.lastChild.innerHTML = html;2904 } catch (e) {2905 log.appendChild(document.createElementNS(xhtml_ns, "p"))2906 .textContent = "Setting innerHTML for the log threw an exception.";2907 log.appendChild(document.createElementNS(xhtml_ns, "pre"))2908 .textContent = html;2909 }2910 };2911 /*2912 * Template code2913 *2914 * A template is just a JavaScript structure. An element is represented as:2915 *2916 * [tag_name, {attr_name:attr_value}, child1, child2]2917 *2918 * the children can either be strings (which act like text nodes), other templates or2919 * functions (see below)2920 *2921 * A text node is represented as2922 *2923 * ["{text}", value]2924 *2925 * String values have a simple substitution syntax; ${foo} represents a variable foo.2926 *2927 * It is possible to embed logic in templates by using a function in a place where a2928 * node would usually go. The function must either return part of a template or null.2929 *2930 * In cases where a set of nodes are required as output rather than a single node2931 * with children it is possible to just use a list2932 * [node1, node2, node3]2933 *2934 * Usage:2935 *2936 * render(template, substitutions) - take a template and an object mapping2937 * variable names to parameters and return either a DOM node or a list of DOM nodes2938 *2939 * substitute(template, substitutions) - take a template and variable mapping object,2940 * make the variable substitutions and return the substituted template2941 *2942 */2943 function is_single_node(template)2944 {2945 return typeof template[0] === "string";2946 }2947 function substitute(template, substitutions)2948 {2949 if (typeof template === "function") {2950 var replacement = template(substitutions);2951 if (!replacement) {2952 return null;2953 }2954 return substitute(replacement, substitutions);2955 }2956 if (is_single_node(template)) {2957 return substitute_single(template, substitutions);2958 }2959 return filter(map(template, function(x) {2960 return substitute(x, substitutions);2961 }), function(x) {return x !== null;});2962 }2963 function substitute_single(template, substitutions)2964 {2965 var substitution_re = /\$\{([^ }]*)\}/g;2966 function do_substitution(input) {2967 var components = input.split(substitution_re);2968 var rv = [];2969 for (var i = 0; i < components.length; i += 2) {2970 rv.push(components[i]);2971 if (components[i + 1]) {2972 rv.push(String(substitutions[components[i + 1]]));2973 }2974 }2975 return rv;2976 }2977 function substitute_attrs(attrs, rv)2978 {2979 rv[1] = {};2980 for (var name in template[1]) {2981 if (attrs.hasOwnProperty(name)) {2982 var new_name = do_substitution(name).join("");2983 var new_value = do_substitution(attrs[name]).join("");2984 rv[1][new_name] = new_value;2985 }2986 }2987 }2988 function substitute_children(children, rv)2989 {2990 for (var i = 0; i < children.length; i++) {2991 if (children[i] instanceof Object) {2992 var replacement = substitute(children[i], substitutions);2993 if (replacement !== null) {2994 if (is_single_node(replacement)) {2995 rv.push(replacement);2996 } else {2997 extend(rv, replacement);2998 }2999 }3000 } else {3001 extend(rv, do_substitution(String(children[i])));3002 }3003 }3004 return rv;3005 }3006 var rv = [];3007 rv.push(do_substitution(String(template[0])).join(""));3008 if (template[0] === "{text}") {3009 substitute_children(template.slice(1), rv);3010 } else {3011 substitute_attrs(template[1], rv);3012 substitute_children(template.slice(2), rv);3013 }3014 return rv;3015 }3016 function make_dom_single(template, doc)3017 {3018 var output_document = doc || document;3019 var element;3020 if (template[0] === "{text}") {3021 element = output_document.createTextNode("");3022 for (var i = 1; i < template.length; i++) {3023 element.data += template[i];3024 }3025 } else {3026 element = output_document.createElementNS(xhtml_ns, template[0]);3027 for (var name in template[1]) {3028 if (template[1].hasOwnProperty(name)) {3029 element.setAttribute(name, template[1][name]);3030 }3031 }3032 for (var i = 2; i < template.length; i++) {3033 if (template[i] instanceof Object) {3034 var sub_element = make_dom(template[i]);3035 element.appendChild(sub_element);3036 } else {3037 var text_node = output_document.createTextNode(template[i]);3038 element.appendChild(text_node);3039 }3040 }3041 }3042 return element;3043 }3044 function make_dom(template, substitutions, output_document)3045 {3046 if (is_single_node(template)) {3047 return make_dom_single(template, output_document);3048 }3049 return map(template, function(x) {3050 return make_dom_single(x, output_document);3051 });3052 }3053 function render(template, substitutions, output_document)3054 {3055 return make_dom(substitute(template, substitutions), output_document);3056 }3057 /*3058 * Utility functions3059 */3060 function assert(expected_true, function_name, description, error, substitutions)3061 {3062 if (expected_true !== true) {3063 var msg = make_message(function_name, description,3064 error, substitutions);3065 throw new AssertionError(msg);3066 }3067 }3068 function AssertionError(message)3069 {3070 this.message = message;3071 this.stack = this.get_stack();3072 }3073 expose(AssertionError, "AssertionError");3074 AssertionError.prototype = Object.create(Error.prototype);3075 AssertionError.prototype.get_stack = function() {3076 var stack = new Error().stack;3077 // IE11 does not initialize 'Error.stack' until the object is thrown.3078 if (!stack) {3079 try {3080 throw new Error();3081 } catch (e) {3082 stack = e.stack;3083 }3084 }3085 // 'Error.stack' is not supported in all browsers/versions3086 if (!stack) {3087 return "(Stack trace unavailable)";3088 }3089 var lines = stack.split("\n");3090 // Create a pattern to match stack frames originating within testharness.js. These include the3091 // script URL, followed by the line/col (e.g., '/resources/testharness.js:120:21').3092 // Escape the URL per http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript3093 // in case it contains RegExp characters.3094 var script_url = get_script_url();3095 var re_text = script_url ? script_url.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') : "\\btestharness.js";3096 var re = new RegExp(re_text + ":\\d+:\\d+");3097 // Some browsers include a preamble that specifies the type of the error object. Skip this by3098 // advancing until we find the first stack frame originating from testharness.js.3099 var i = 0;3100 while (!re.test(lines[i]) && i < lines.length) {3101 i++;3102 }3103 // Then skip the top frames originating from testharness.js to begin the stack at the test code.3104 while (re.test(lines[i]) && i < lines.length) {3105 i++;3106 }3107 // Paranoid check that we didn't skip all frames. If so, return the original stack unmodified.3108 if (i >= lines.length) {3109 return stack;3110 }3111 return lines.slice(i).join("\n");3112 }3113 function OptionalFeatureUnsupportedError(message)3114 {3115 AssertionError.call(this, message);3116 }3117 OptionalFeatureUnsupportedError.prototype = Object.create(AssertionError.prototype);3118 expose(OptionalFeatureUnsupportedError, "OptionalFeatureUnsupportedError");3119 function make_message(function_name, description, error, substitutions)3120 {3121 for (var p in substitutions) {3122 if (substitutions.hasOwnProperty(p)) {3123 substitutions[p] = format_value(substitutions[p]);3124 }3125 }3126 var node_form = substitute(["{text}", "${function_name}: ${description}" + error],3127 merge({function_name:function_name,3128 description:(description?description + " ":"")},3129 substitutions));3130 return node_form.slice(1).join("");3131 }3132 function filter(array, callable, thisObj) {3133 var rv = [];3134 for (var i = 0; i < array.length; i++) {3135 if (array.hasOwnProperty(i)) {3136 var pass = callable.call(thisObj, array[i], i, array);3137 if (pass) {3138 rv.push(array[i]);3139 }3140 }3141 }3142 return rv;3143 }3144 function map(array, callable, thisObj)3145 {3146 var rv = [];3147 rv.length = array.length;3148 for (var i = 0; i < array.length; i++) {3149 if (array.hasOwnProperty(i)) {3150 rv[i] = callable.call(thisObj, array[i], i, array);3151 }3152 }3153 return rv;3154 }3155 function extend(array, items)3156 {3157 Array.prototype.push.apply(array, items);3158 }3159 function forEach(array, callback, thisObj)3160 {3161 for (var i = 0; i < array.length; i++) {3162 if (array.hasOwnProperty(i)) {3163 callback.call(thisObj, array[i], i, array);3164 }3165 }3166 }3167 /**3168 * Immediately invoke a "iteratee" function with a series of values in3169 * parallel and invoke a final "done" function when all of the "iteratee"3170 * invocations have signaled completion.3171 *3172 * If all callbacks complete synchronously (or if no callbacks are3173 * specified), the `done_callback` will be invoked synchronously. It is the3174 * responsibility of the caller to ensure asynchronicity in cases where3175 * that is desired.3176 *3177 * @param {array} value Zero or more values to use in the invocation of3178 * `iter_callback`3179 * @param {function} iter_callback A function that will be invoked once for3180 * each of the provided `values`. Two3181 * arguments will be available in each3182 * invocation: the value from `values` and3183 * a function that must be invoked to3184 * signal completion3185 * @param {function} done_callback A function that will be invoked after3186 * all operations initiated by the3187 * `iter_callback` function have signaled3188 * completion3189 */3190 function all_async(values, iter_callback, done_callback)3191 {3192 var remaining = values.length;3193 if (remaining === 0) {3194 done_callback();3195 }3196 forEach(values,3197 function(element) {3198 var invoked = false;3199 var elDone = function() {3200 if (invoked) {3201 return;3202 }3203 invoked = true;3204 remaining -= 1;3205 if (remaining === 0) {3206 done_callback();3207 }3208 };3209 iter_callback(element, elDone);3210 });3211 }3212 function merge(a,b)3213 {3214 var rv = {};3215 var p;3216 for (p in a) {3217 rv[p] = a[p];3218 }3219 for (p in b) {3220 rv[p] = b[p];3221 }3222 return rv;3223 }3224 function expose(object, name)3225 {3226 var components = name.split(".");3227 var target = global_scope;3228 for (var i = 0; i < components.length - 1; i++) {3229 if (!(components[i] in target)) {3230 target[components[i]] = {};3231 }3232 target = target[components[i]];3233 }3234 target[components[components.length - 1]] = object;3235 }3236 function is_same_origin(w) {3237 try {3238 'random_prop' in w;3239 return true;3240 } catch (e) {3241 return false;3242 }3243 }3244 /** Returns the 'src' URL of the first <script> tag in the page to include the file 'testharness.js'. */3245 function get_script_url()3246 {3247 if (!('document' in global_scope)) {3248 return undefined;3249 }3250 var scripts = document.getElementsByTagName("script");3251 for (var i = 0; i < scripts.length; i++) {3252 var src;3253 if (scripts[i].src) {3254 src = scripts[i].src;3255 } else if (scripts[i].href) {3256 //SVG case3257 src = scripts[i].href.baseVal;3258 }3259 var matches = src && src.match(/^(.*\/|)testharness\.js$/);3260 if (matches) {3261 return src;3262 }3263 }3264 return undefined;3265 }3266 /** Returns the <title> or filename or "Untitled" */3267 function get_title()3268 {3269 if ('document' in global_scope) {3270 //Don't use document.title to work around an Opera bug in XHTML documents3271 var title = document.getElementsByTagName("title")[0];3272 if (title && title.firstChild && title.firstChild.data) {3273 return title.firstChild.data;3274 }3275 }3276 if ('META_TITLE' in global_scope && META_TITLE) {3277 return META_TITLE;3278 }3279 if ('location' in global_scope) {3280 return location.pathname.substring(location.pathname.lastIndexOf('/') + 1, location.pathname.indexOf('.'));3281 }3282 return "Untitled";3283 }3284 function supports_post_message(w)3285 {3286 var supports;3287 var type;3288 // Given IE implements postMessage across nested iframes but not across3289 // windows or tabs, you can't infer cross-origin communication from the presence3290 // of postMessage on the current window object only.3291 //3292 // Touching the postMessage prop on a window can throw if the window is3293 // not from the same origin AND post message is not supported in that3294 // browser. So just doing an existence test here won't do, you also need3295 // to wrap it in a try..catch block.3296 try {3297 type = typeof w.postMessage;3298 if (type === "function") {3299 supports = true;3300 }3301 // IE8 supports postMessage, but implements it as a host object which3302 // returns "object" as its `typeof`.3303 else if (type === "object") {3304 supports = true;3305 }3306 // This is the case where postMessage isn't supported AND accessing a3307 // window property across origins does NOT throw (e.g. old Safari browser).3308 else {3309 supports = false;3310 }3311 } catch (e) {3312 // This is the case where postMessage isn't supported AND accessing a3313 // window property across origins throws (e.g. old Firefox browser).3314 supports = false;3315 }3316 return supports;3317 }3318 /**3319 * Setup globals3320 */3321 var tests = new Tests();3322 if (global_scope.addEventListener) {3323 var error_handler = function(error, message, stack) {3324 var optional_unsupported = error instanceof OptionalFeatureUnsupportedError;3325 if (tests.file_is_test) {3326 var test = tests.tests[0];3327 if (test.phase >= test.phases.HAS_RESULT) {3328 return;3329 }3330 var status = optional_unsupported ? test.PRECONDITION_FAILED : test.FAIL;3331 test.set_status(status, message, stack);3332 test.phase = test.phases.HAS_RESULT;3333 } else if (!tests.allow_uncaught_exception) {3334 var status = optional_unsupported ? tests.status.PRECONDITION_FAILED : tests.status.ERROR;3335 tests.status.status = status;3336 tests.status.message = message;3337 tests.status.stack = stack;3338 }3339 // Do not transition to the "complete" phase if the test has been3340 // configured to allow uncaught exceptions. This gives the test an3341 // opportunity to define subtests based on the exception reporting3342 // behavior.3343 if (!tests.allow_uncaught_exception) {3344 done();3345 }3346 };3347 addEventListener("error", function(e) {3348 var message = e.message;3349 var stack;3350 if (e.error && e.error.stack) {3351 stack = e.error.stack;3352 } else {3353 stack = e.filename + ":" + e.lineno + ":" + e.colno;3354 }3355 error_handler(e.error, message, stack);3356 }, false);3357 addEventListener("unhandledrejection", function(e) {3358 var message;3359 if (e.reason && e.reason.message) {3360 message = "Unhandled rejection: " + e.reason.message;3361 } else {3362 message = "Unhandled rejection";3363 }3364 var stack;3365 if (e.reason && e.reason.stack) {3366 stack = e.reason.stack;3367 }3368 error_handler(e.reason, message, stack);3369 }, false);3370 }3371 test_environment.on_tests_ready();3372 /**3373 * Stylesheet3374 */3375 var stylesheetContent = "\3376html {\3377 font-family:DejaVu Sans, Bitstream Vera Sans, Arial, Sans;\3378}\3379\3380#log .warning,\3381#log .warning a {\3382 color: black;\3383 background: yellow;\3384}\3385\3386#log .error,\3387#log .error a {\3388 color: white;\3389 background: red;\3390}\3391\3392section#summary {\3393 margin-bottom:1em;\3394}\3395\3396table#results {\3397 border-collapse:collapse;\3398 table-layout:fixed;\3399 width:100%;\3400}\3401\3402table#results th:first-child,\3403table#results td:first-child {\3404 width:8em;\3405}\3406\3407table#results th:last-child,\3408table#results td:last-child {\3409 width:50%;\3410}\3411\3412table#results.assertions th:last-child,\3413table#results.assertions td:last-child {\3414 width:35%;\3415}\3416\3417table#results th {\3418 padding:0;\3419 padding-bottom:0.5em;\3420 border-bottom:medium solid black;\3421}\3422\3423table#results td {\3424 padding:1em;\3425 padding-bottom:0.5em;\3426 border-bottom:thin solid black;\3427}\3428\3429tr.pass > td:first-child {\3430 color:green;\3431}\3432\3433tr.fail > td:first-child {\3434 color:red;\3435}\3436\3437tr.timeout > td:first-child {\3438 color:red;\3439}\3440\3441tr.notrun > td:first-child {\3442 color:blue;\3443}\3444\3445tr.optionalunsupported > td:first-child {\3446 color:blue;\3447}\3448\3449.pass > td:first-child, .fail > td:first-child, .timeout > td:first-child, .notrun > td:first-child, .optionalunsupported > td:first-child {\3450 font-variant:small-caps;\3451}\3452\3453table#results span {\3454 display:block;\3455}\3456\3457table#results span.expected {\3458 font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace;\3459 white-space:pre;\3460}\3461\3462table#results span.actual {\3463 font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace;\3464 white-space:pre;\3465}\3466\3467span.ok {\3468 color:green;\3469}\3470\3471tr.error {\3472 color:red;\3473}\3474\3475span.timeout {\3476 color:red;\3477}\3478\3479span.ok, span.timeout, span.error {\3480 font-variant:small-caps;\3481}\3482";3483})(this);...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptexturize = require('wptexturize');2var str = 'This is a test string';3var offset = 5;4var length = 10;5var result = wptexturize.length_after_offset(str, offset, length);6console.log(result);7var wptexturize = require('wptexturize');8var str = 'This is a test string';9var result = wptexturize.remove_shortcode(str);10console.log(result);11var wptexturize = require('wptexturize');12var str = 'This is a test string';13var result = wptexturize.remove_shortcodes(str);14console.log(result);15var wptexturize = require('wptexturize');16var str = 'This is a test string';17var result = wptexturize.replace(str);18console.log(result);19var wptexturize = require('wptexturize');20var str = 'This is a test string';21var result = wptexturize.replace_with(str);22console.log(result);23var wptexturize = require('wptexturize');24var str = 'This is a test string';25var result = wptexturize.restore_no_texturize_tags(str);26console.log(result);27var wptexturize = require('wptexturize');28var str = 'This is a test string';29var result = wptexturize.restore_no_texturize_shortcodes(str);30console.log(result

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptexturize = require('wptexturize');2var text = 'This is a test text';3var offset = 5;4var length = 10;5var result = wptexturize.length_after_offset(text, offset, length);6console.log(result);

Full Screen

Using AI Code Generation

copy

Full Screen

1var text = 'This is a test string';2var offset = 4;3var length = 6;4var result = wptexturize.length_after_offset(text, offset, length);5console.log(result);6var text = 'This is a test string';7var offset = 4;8var length = 6;9var result = wptexturize.length_after_offset(text, offset, length);10console.log(result);11var text = 'This is a test string';12var offset = 4;13var length = 6;14var result = wptexturize.length_after_offset(text, offset, length);15console.log(result);16var text = 'This is a test string';17var offset = 4;18var length = 6;19var result = wptexturize.length_after_offset(text, offset, length);20console.log(result);21var text = 'This is a test string';22var offset = 4;23var length = 6;24var result = wptexturize.length_after_offset(text, offset, length);25console.log(result);26var text = 'This is a test string';27var offset = 4;28var length = 6;29var result = wptexturize.length_after_offset(text, offset, length);30console.log(result);31var text = 'This is a test string';32var offset = 4;33var length = 6;34var result = wptexturize.length_after_offset(text, offset, length);35console.log(result);

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptexturize = require('wptexturize');2var text = "It's the test text.";3var length = wptexturize.length_after_offset(text, 0);4console.log(length);5var wptexturize = require('wptexturize');6var text = "It's the test text.";7var length = wptexturize.length_after_offset(text, 10);8console.log(length);9var wptexturize = require('wptexturize');10var text = "It's the test text.";11var length = wptexturize.length_after_offset(text, 20);12console.log(length);13var wptexturize = require('wptexturize');14var text = "It's the test text.";15var length = wptexturize.length_after_offset(text, 30);16console.log(length);17var wptexturize = require('wptexturize');18var text = "It's the test text.";19var length = wptexturize.length_after_offset(text, 40);20console.log(length);21var wptexturize = require('wptexturize');22var text = "It's the test text.";23var length = wptexturize.length_after_offset(text, 50);24console.log(length);25var wptexturize = require('wptexturize');26var text = "It's the test text.";27var length = wptexturize.length_after_offset(text, 60);28console.log(length);29var wptexturize = require('wptexturize');30var text = "It's the test text.";

Full Screen

Using AI Code Generation

copy

Full Screen

1var text = "This is a test text to test the method";2var offset = 0;3var length = 10;4var result = wptexturize.length_after_offset(text, offset, length);5console.log(result);6var text = "This is a test text to test the method";7var offset = 0;8var length = 10;9var result = wptexturize.length_after_offset(text, offset, length);10console.log(result);11var text = "This is a test text to test the method";12var offset = 0;13var length = 10;14var result = wptexturize.length_after_offset(text, offset, length);15console.log(result);16var text = "This is a test text to test the method";17var offset = 0;18var length = 10;19var result = wptexturize.length_after_offset(text, offset, length);20console.log(result);21var text = "This is a test text to test the method";22var offset = 0;23var length = 10;24var result = wptexturize.length_after_offset(text, offset, length);25console.log(result);26var text = "This is a test text to test the method";27var offset = 0;28var length = 10;29var result = wptexturize.length_after_offset(text, offset, length);30console.log(result);31var text = "This is a test text to test the method";32var offset = 0;33var length = 10;34var result = wptexturize.length_after_offset(text, offset, length);

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptexturize = require('wptexturize');2var str = "This is a string of text.";3var offset = 5;4var length = wptexturize.length_after_offset(str, offset);5console.log(length);6var str = "This is a string of text.";7var offset = 5;8var length = wptexturize.length_after_offset(str, offset);9var offset = wptexturize.offset_after_length(str, length);10console.log(offset);11var str = "This is a string of text.";12var offset = 5;13var length = wptexturize.length_after_offset(str, offset);14var offset = wptexturize.offset_after_length(str, length);15var str = wptexturize.texturize(str, offset);16console.log(str);17var str = "This is a string of text.";18var offset = 5;19var length = wptexturize.length_after_offset(str, offset);20var offset = wptexturize.offset_after_length(str, length);21var str = wptexturize.texturize(str, offset);22var str = wptexturize.de_texturize(str, offset);23console.log(str);

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful