wiki.techinc.nl/includes/specials/SpecialJavaScriptTest.php

193 lines
6.6 KiB
PHP
Raw Normal View History

<?php
2012-02-09 09:34:47 +00:00
/**
* Implements Special:JavaScriptTest
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
2012-02-09 09:34:47 +00:00
* @file
* @ingroup SpecialPage
2012-02-09 09:34:47 +00:00
*/
2012-02-09 09:34:47 +00:00
/**
* @ingroup SpecialPage
*/
class SpecialJavaScriptTest extends SpecialPage {
public function __construct() {
parent::__construct( 'JavaScriptTest' );
}
public function execute( $par ) {
$this->getOutput()->disable();
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
if ( $par === 'qunit/export' ) {
// Send the JavaScript payload.
$this->exportJS();
} elseif ( $par === null || $par === '' || $par === 'qunit' || $par === 'qunit/plain' ) {
// Render the page
// (Support "/qunit" and "/qunit/plain" for backwards-compatibility)
$this->renderPage();
} else {
wfHttpError( 404, 'Unknown action', "Unknown action \"$par\"." );
}
}
/**
* Send the standalone JavaScript payload.
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
*
* Loaded by the GUI (on Special:JavaScriptTest), and by the CLI (via grunt-karma).
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
*/
private function exportJS() {
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
$out = $this->getOutput();
$rl = $out->getResourceLoader();
// Allow framing (disabling wgBreakFrames). Otherwise, mediawiki.page.ready
// will close this tab when running from CLI using karma-qunit.
$out->setPreventClickjacking( false );
$query = [
'lang' => 'qqx',
'skin' => 'fallback',
'debug' => (string)ResourceLoader::inDebugMode(),
'target' => 'test',
];
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
$embedContext = new ResourceLoaderContext( $rl, new FauxRequest( $query ) );
$query['only'] = 'scripts';
$startupContext = new ResourceLoaderContext( $rl, new FauxRequest( $query ) );
$modules = $rl->getTestSuiteModuleNames();
$component = $this->getContext()->getRequest()->getVal( 'component' );
if ( $component ) {
$module = 'test.' . $component;
if ( !in_array( 'test.' . $component, $modules ) ) {
wfHttpError(
404,
'Unknown test module',
"'$module' is not a defined test module. "
. 'Register one via the QUnitTestModules attribute in extension.json.'
);
return;
}
$modules = [ 'test.' . $component ];
}
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
qunit: Disable mw.loader.store server-side instead of client-side == What This feature is disabled initially because we want to be able to test it. When mw.loader's own tests are testing mw.loader.store, they are mocking setTimeout and mw.requestIdleCallback, so that they can then make mw.loader.store schedule its "flush" callback and via the mock timer control when it executes and then assert its result. == The Bad Previously an inline JS hack was concatenated right after startup.js, and that seems like it should be early enough to prevent anything from initialising mw.loader.store and scheduling a real 2s flush timer. There is no obvious sign here that the startup module response would request or implement a module (which could inititialise mw.loader.store). However, there is. == …, the Ugly The startup response contains RLQ processing (which is empty in the standalone test runner, so no worries here) and a call to mw.loader.load for RLPAGEMODULES. The RLPAGEMODULES list is empty, but it does still make a call to mw.loader.load(). And that expands the empty array to include jquery+mediawiki.base and thus makes a proper module request, which then initialises mw.loader.store and schedules its 2s flush timeout. This hasn't caused failing tests in CI so far, because there are generally at least 2 seconds of unrelated tests that run first. So by the time mw.loader's test suite comes around, it has been disabled and the previous flush has already completed. == … and the Good Change I5f1067feb0a43d makes the 'mediawiki.jqueryMsg' test suite super fast (previously took 2+ seconds, longer than any other test). This exposes the fact that mw.loader.store was in fact not actually properly disabled from the get go, and so tests would be failing. Bug: T250045 Change-Id: I38c3ad2a9a5813215dbb210bddafcc3cdd70295d
2020-04-22 23:54:20 +00:00
// Disable module storage.
// The unit test for mw.loader.store will enable it (with a mock timers).
$config = new MultiConfig( [
new HashConfig( [ 'ResourceLoaderStorageEnabled' => false ] ),
$rl->getConfig(),
] );
resourceloader: Async all the way Page startup: * Due to the startup module and top queue being asynchronous now, move client-nojs/client-js class handling to OutputPage to ensure there is no flashes of wrongly styled or unstyled content. To preserve compatibility for unsupported browsers, undo the class swap at runtime after the isCompatible() check. ResourceLoader startup module: * Load the startup module with <script async>. * Use DOM methods instead of 'document.write' to create base module request (jquery|mediawiki). mw.loader: * Drop 'async' parameter from mw.loader.load(). * Remove the now-unused code paths for synchronous requests. OutputPage: * Drop '$loadCall' parameter from makeResourceLoaderLink(). Asynchronous is now the default and only way to load JavaScript. This means the 'user' module "conditional document-write scripts" are now a simple "mw.loader.load( url )" call. * Fix incorrect @return of makeResourceLoaderLink(). This returns an array not a string. * Improve documentation of makeResourceLoaderLink(). * Drop '$inHead' parameter from getScriptsForBottomQueue(). No longer used. Compatibility with the $wgResourceLoaderExperimentalAsyncLoading feature is maintained. It just no longer needs to change the way the queue works since it's always asynchronous. The feature flag now only controls whether the bottom queue starts at the bottom or starts at the top. * Remove jQuery.ready() optimisation. This was mostly there to avoid the setTimeout() loop jQuery does to detect dom-ready in IE6/IE7 (which we no longer serve JavaScript at all). And for a bug in Firefox with document.write (which is no longer used as of this commit). Bug: T107399 Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
2015-07-28 02:46:00 +00:00
// Disable autostart because we load modules asynchronously. By default, QUnit would start
// at domready when there are no tests loaded and also fire 'QUnit.done' which then instructs
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
// Karma to exit the browser process before the tests even finished loading.
resourceloader: Async all the way Page startup: * Due to the startup module and top queue being asynchronous now, move client-nojs/client-js class handling to OutputPage to ensure there is no flashes of wrongly styled or unstyled content. To preserve compatibility for unsupported browsers, undo the class swap at runtime after the isCompatible() check. ResourceLoader startup module: * Load the startup module with <script async>. * Use DOM methods instead of 'document.write' to create base module request (jquery|mediawiki). mw.loader: * Drop 'async' parameter from mw.loader.load(). * Remove the now-unused code paths for synchronous requests. OutputPage: * Drop '$loadCall' parameter from makeResourceLoaderLink(). Asynchronous is now the default and only way to load JavaScript. This means the 'user' module "conditional document-write scripts" are now a simple "mw.loader.load( url )" call. * Fix incorrect @return of makeResourceLoaderLink(). This returns an array not a string. * Improve documentation of makeResourceLoaderLink(). * Drop '$inHead' parameter from getScriptsForBottomQueue(). No longer used. Compatibility with the $wgResourceLoaderExperimentalAsyncLoading feature is maintained. It just no longer needs to change the way the queue works since it's always asynchronous. The feature flag now only controls whether the bottom queue starts at the bottom or starts at the top. * Remove jQuery.ready() optimisation. This was mostly there to avoid the setTimeout() loop jQuery does to detect dom-ready in IE6/IE7 (which we no longer serve JavaScript at all). And for a bug in Firefox with document.write (which is no longer used as of this commit). Bug: T107399 Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
2015-07-28 02:46:00 +00:00
$qunitConfig = 'QUnit.config.autostart = false;'
. 'if (window.__karma__) {'
// karma-qunit's use of autostart=false and QUnit.start conflicts with ours.
// Hack around this by replacing 'karma.loaded' with a no-op and perform its duty of calling
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
// `__karma__.start()` ourselves. See <https://github.com/karma-runner/karma-qunit/issues/27>.
resourceloader: Async all the way Page startup: * Due to the startup module and top queue being asynchronous now, move client-nojs/client-js class handling to OutputPage to ensure there is no flashes of wrongly styled or unstyled content. To preserve compatibility for unsupported browsers, undo the class swap at runtime after the isCompatible() check. ResourceLoader startup module: * Load the startup module with <script async>. * Use DOM methods instead of 'document.write' to create base module request (jquery|mediawiki). mw.loader: * Drop 'async' parameter from mw.loader.load(). * Remove the now-unused code paths for synchronous requests. OutputPage: * Drop '$loadCall' parameter from makeResourceLoaderLink(). Asynchronous is now the default and only way to load JavaScript. This means the 'user' module "conditional document-write scripts" are now a simple "mw.loader.load( url )" call. * Fix incorrect @return of makeResourceLoaderLink(). This returns an array not a string. * Improve documentation of makeResourceLoaderLink(). * Drop '$inHead' parameter from getScriptsForBottomQueue(). No longer used. Compatibility with the $wgResourceLoaderExperimentalAsyncLoading feature is maintained. It just no longer needs to change the way the queue works since it's always asynchronous. The feature flag now only controls whether the bottom queue starts at the bottom or starts at the top. * Remove jQuery.ready() optimisation. This was mostly there to avoid the setTimeout() loop jQuery does to detect dom-ready in IE6/IE7 (which we no longer serve JavaScript at all). And for a bug in Firefox with document.write (which is no longer used as of this commit). Bug: T107399 Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
2015-07-28 02:46:00 +00:00
. 'window.__karma__.loaded = function () {};'
. '}';
resourceloader: Move queue formatting out of OutputPage HTML formatting of the queue was distributed over several OutputPage methods. Each method demanding a snippet of HTML by calling makeResourceLoaderLink() with a limited amount of information. As such, makeResourceLoaderLink() was unable to provide the client with the proper state information. Centralising it also allows it to better reduce duplication in HTML output and maintain a more accurate state. Problems fixed by centralising: 1. The 'user' module is special (due to per-user 'version' and 'user' params). It is manually requested via script-src. To avoid a separate (and wrong) request from something that requires it, we set state=loading directly. However, because the module is in the bottom, the old HTML formatter could only put state=loading in the bottom also. This sometimes caused a wrong request to be fired for modules=user if something in the top queue triggered a requirement for it. 2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose of allowing dependencies on style modules without risking duplicate loading on pages where the styles are loaded already. This didn't work, because the state information about page-style modules is output near the stylesheet, which is after the script tag with mw.loader.load(). That runs first, and mw.loader would still make a duplicate request before it learns the state. Changes: * Document reasons for style/script tag order in getHeadHtml (per 09537e83). * Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed before since a duplicate check in makeResourceLoaderLink() verified the origin a second time. * Declare explicit position 'top' on 'user.options' and 'user.tokens' module. Previously, OutputPage hardcoded them in the top. The new formatter doesn't. * Remove getHeadScripts(). * Remove getInlineHeadScripts(). * Remove getExternalHeadScripts(). * Remove buildCssLinks(). * Remove getScriptsForBottomQueue(). * Change where Skin::setupSkinUserCss() is called. This methods lets the skin add modules to the queue. Previously it was called from buildCssLinks(), via headElement(), via prepareQuickTemplate(), via OutputPage::output(). It's now in OutputPage::output() directly (slightly earlier). This is needed because prepareQuickTemplate() calls bottomScripts() before headElement(). And bottomScript() would lazy-initialise the queue and lock it before setupSkinUserCss() is called from headElement(). This makes execution order more predictable instead of being dependent on the arbitrary order of data extraction in prepareQuickTemplate (which varies from one skin to another). * Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early on so. This avoids wrongful loading and fixes problem 1. Effective changes in output: * mw.loader.state() is now before mw.loader.load(). This fixes problem 2. * mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading". * mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1. * The <script async src> tag for 'startup' changed position (slightly). Previously it was after all inline scripts and stylesheets. It's still after all inline scripts and after most stylesheets, but before any user styles. Since the queue is now formatted outside OutputPage, it can't inject the meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle of existing output. This shouldn't have any noticable impact. Bug: T87871 Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
// The below is essentially a pure-javascript version of OutputPage::headElement().
qunit: Disable mw.loader.store server-side instead of client-side == What This feature is disabled initially because we want to be able to test it. When mw.loader's own tests are testing mw.loader.store, they are mocking setTimeout and mw.requestIdleCallback, so that they can then make mw.loader.store schedule its "flush" callback and via the mock timer control when it executes and then assert its result. == The Bad Previously an inline JS hack was concatenated right after startup.js, and that seems like it should be early enough to prevent anything from initialising mw.loader.store and scheduling a real 2s flush timer. There is no obvious sign here that the startup module response would request or implement a module (which could inititialise mw.loader.store). However, there is. == …, the Ugly The startup response contains RLQ processing (which is empty in the standalone test runner, so no worries here) and a call to mw.loader.load for RLPAGEMODULES. The RLPAGEMODULES list is empty, but it does still make a call to mw.loader.load(). And that expands the empty array to include jquery+mediawiki.base and thus makes a proper module request, which then initialises mw.loader.store and schedules its 2s flush timeout. This hasn't caused failing tests in CI so far, because there are generally at least 2 seconds of unrelated tests that run first. So by the time mw.loader's test suite comes around, it has been disabled and the previous flush has already completed. == … and the Good Change I5f1067feb0a43d makes the 'mediawiki.jqueryMsg' test suite super fast (previously took 2+ seconds, longer than any other test). This exposes the fact that mw.loader.store was in fact not actually properly disabled from the get go, and so tests would be failing. Bug: T250045 Change-Id: I38c3ad2a9a5813215dbb210bddafcc3cdd70295d
2020-04-22 23:54:20 +00:00
$startupModule = $rl->getModule( 'startup' );
$startupModule->setConfig( $config );
$code = $rl->makeModuleResponse( $startupContext, [ 'startup' => $startupModule ] );
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
// The following has to be deferred via RLQ because the startup module is asynchronous.
$code .= ResourceLoader::makeLoaderConditionalScript(
// Embed page-specific mw.config variables.
//
// For compatibility with older tests, these will come from the user
// action "viewing Special:JavaScripTest".
//
// This is deprecated since MediaWiki 1.25 and slowly being phased out in favour of:
// 1. tests explicitly mocking the configuration they depend on.
// 2. tests explicitly skipping or not loading code that is only meant
// for real page views (e.g. not loading as dependency, or using a QUnit
// conditional).
ResourceLoader::makeConfigSetScript( array_intersect_key(
$out->getJSVars(),
// Keep a select few that are commonly referenced.
// See https://phabricator.wikimedia.org/T89434.
array_fill_keys(
[
'wgPageName', // used by mediawiki.util
'wgRelevantPageName', // used as input for mw.Title
],
null
)
) )
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
// Embed private modules as they're not allowed to be loaded dynamically
. $rl->makeModuleResponse( $embedContext, [
'user.options' => $rl->getModule( 'user.options' ),
] )
// Load all the test suites
. Xml::encodeJsCall( 'mw.loader.load', [ $modules ] )
);
$encModules = Xml::encodeJsVar( $modules );
$code .= ResourceLoader::makeInlineCodeWithModule( 'mediawiki.base', <<<JAVASCRIPT
var start = window.__karma__ ? window.__karma__.start : QUnit.start;
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
mw.loader.using( $encModules ).always( start );
mw.trackSubscribe( 'resourceloader.exception', function ( topic, err ) {
// Things like "dependency missing" or "unknown module".
// Re-throw so that they are reported as global exceptions by QUnit and Karma.
setTimeout( function () {
throw err;
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
} );
} );
JAVASCRIPT
);
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
header( 'Content-Type: text/javascript; charset=utf-8' );
header( 'Cache-Control: private, no-cache, must-revalidate' );
header( 'Pragma: no-cache' );
resourceloader: Async all the way Page startup: * Due to the startup module and top queue being asynchronous now, move client-nojs/client-js class handling to OutputPage to ensure there is no flashes of wrongly styled or unstyled content. To preserve compatibility for unsupported browsers, undo the class swap at runtime after the isCompatible() check. ResourceLoader startup module: * Load the startup module with <script async>. * Use DOM methods instead of 'document.write' to create base module request (jquery|mediawiki). mw.loader: * Drop 'async' parameter from mw.loader.load(). * Remove the now-unused code paths for synchronous requests. OutputPage: * Drop '$loadCall' parameter from makeResourceLoaderLink(). Asynchronous is now the default and only way to load JavaScript. This means the 'user' module "conditional document-write scripts" are now a simple "mw.loader.load( url )" call. * Fix incorrect @return of makeResourceLoaderLink(). This returns an array not a string. * Improve documentation of makeResourceLoaderLink(). * Drop '$inHead' parameter from getScriptsForBottomQueue(). No longer used. Compatibility with the $wgResourceLoaderExperimentalAsyncLoading feature is maintained. It just no longer needs to change the way the queue works since it's always asynchronous. The feature flag now only controls whether the bottom queue starts at the bottom or starts at the top. * Remove jQuery.ready() optimisation. This was mostly there to avoid the setTimeout() loop jQuery does to detect dom-ready in IE6/IE7 (which we no longer serve JavaScript at all). And for a bug in Firefox with document.write (which is no longer used as of this commit). Bug: T107399 Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
2015-07-28 02:46:00 +00:00
echo $qunitConfig;
resourceloader: Combine base modules and page modules requests This commit implements step 4 and step 5 of the plan outlined at T192623. Before this task began, the typical JavaScript execution flow was: * HTML triggers request for startup module (js req 1). * Startup module contains registry, site config, and triggers a request for the base modules (js req 2). * After the base modules arrive (which define jQuery and mw.loader), the startup module invokes a callback that processes RLQ, which is what will request modules for this page (js req 3). In past weeks, we have: * Made mediawiki.js independent of jQuery. * Spun off 'mediawiki.base' from mediawiki.js – for everything that wasn't needed for defining `mw.loader`. * Moved mediawiki.js from the base module request to being embedded as part of startup.js. The concept of dependencies is native to ResourceLoader, and thanks to the use of closures in mw.loader.implement() responses, we can download any number of interdependant modules in a single request (or parallel requests). Then, when a response arrives, mw.loader takes care to pause or resume execution as-needed. It is normal for ResourceLoader to batch several modules together, including their dependencies. As such, we can eliminate one of the two roundtrips required before a page can request modules. Specifically, we can eliminate "js req 2" (above), by making the two remaining base modules ("jquery" and "mediawiki.base") an implied dependency for all other modules, which ResourceLoader will naturally fetch and execute in the right order as part of the batch request. Bug: T192623 Change-Id: I17cd13dffebd6ae476044d8d038dc3974a1fa176
2018-07-12 20:09:28 +00:00
echo $code;
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
}
private function renderPage() {
$basePath = $this->getConfig()->get( 'ResourceBasePath' );
$headHtml = implode( "\n", [
Html::linkedScript( "$basePath/resources/lib/qunitjs/qunit.js" ),
Html::linkedStyle( "$basePath/resources/lib/qunitjs/qunit.css" ),
Html::linkedStyle( "$basePath/resources/src/qunitjs/qunit-local.css" ),
] );
resourceloader: Async all the way Page startup: * Due to the startup module and top queue being asynchronous now, move client-nojs/client-js class handling to OutputPage to ensure there is no flashes of wrongly styled or unstyled content. To preserve compatibility for unsupported browsers, undo the class swap at runtime after the isCompatible() check. ResourceLoader startup module: * Load the startup module with <script async>. * Use DOM methods instead of 'document.write' to create base module request (jquery|mediawiki). mw.loader: * Drop 'async' parameter from mw.loader.load(). * Remove the now-unused code paths for synchronous requests. OutputPage: * Drop '$loadCall' parameter from makeResourceLoaderLink(). Asynchronous is now the default and only way to load JavaScript. This means the 'user' module "conditional document-write scripts" are now a simple "mw.loader.load( url )" call. * Fix incorrect @return of makeResourceLoaderLink(). This returns an array not a string. * Improve documentation of makeResourceLoaderLink(). * Drop '$inHead' parameter from getScriptsForBottomQueue(). No longer used. Compatibility with the $wgResourceLoaderExperimentalAsyncLoading feature is maintained. It just no longer needs to change the way the queue works since it's always asynchronous. The feature flag now only controls whether the bottom queue starts at the bottom or starts at the top. * Remove jQuery.ready() optimisation. This was mostly there to avoid the setTimeout() loop jQuery does to detect dom-ready in IE6/IE7 (which we no longer serve JavaScript at all). And for a bug in Firefox with document.write (which is no longer used as of this commit). Bug: T107399 Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
2015-07-28 02:46:00 +00:00
$introHtml = $this->msg( 'javascripttest-qunit-intro' )
->params( 'https://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing' )
->parseAsBlock();
$scriptUrl = $this->getPageTitle( 'qunit/export' )->getFullURL( [
'debug' => (string)ResourceLoader::inDebugMode(),
] );
$script = Html::linkedScript( $scriptUrl );
SpecialJavaScriptTest: Add export feature Add an 'export' subpage to SpecialJavaScriptTest which allows one to request a self-sufficient JavaScript payload that will bootstrap a ResourceLoader client and load the test suites. This is needed for using Karma (which only loads JavaScript, no full html pages). As such elements from the Skin and OutputPage will not exist. While all QUnit tests in MediaWiki core and most extensions I've seen already use #qunit-fixture, this is now required. This to prevent leakage of elements from one test to another, but it also prevents tests from depending on elements provided by the server. While the Karma setup is still in the pipeline (might land before this commit loses WIP status), for now this can be tested via the 'Special:JavaScriptTest/qunit/plain' subpage. Refactor: * Use HTTP status code 404 in the response for "noframework". * Simplify HTML footprint by using <div id="qunit"> instead of hardcoding the full structure. This feature was added to QUnit since v1.3.0 (Feb 2012), we're using v1.14.0 (Jan 2014). QUnit's header is automatically derived from document.title. * Remove redundant addModules() for 'test.mediawiki.qunit.testrunner'. This is already added by default. * Move allowClickjacking() call so that it applies to other modes as well. The exported javascript needs to have wgBreakFrame set to false so that test runners can frame it. * Change mediawiki.special.javaScriptTest to not depend on QUnit. It caused QUnit to load on error pages. And in theory the page is suited for other frameworks and shouldn't load QUnit this way. Bug: T74063 Change-Id: I3d4d0df43bb426d9579eb0349b8b5477281a7cfc
2014-12-02 21:48:21 +00:00
header( 'Content-Type: text/html; charset=utf-8' );
echo <<<HTML
<!DOCTYPE html>
<title>QUnit</title>
$headHtml
$introHtml
<div id="qunit"></div>
$script
HTML;
}
protected function getGroupName() {
return 'other';
}
}