]> source.dussan.org Git - jquery.git/commitdiff
Build: Make Karma work in ES modules mode
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Mon, 16 Dec 2019 18:33:49 +0000 (19:33 +0100)
committerGitHub <noreply@github.com>
Mon, 16 Dec 2019 18:33:49 +0000 (19:33 +0100)
Also, run such a suite in CI to make sure modules are working as expected
when used directly.

Closes gh-4550

.travis.yml
Gruntfile.js
package.json
test/data/testinit-jsdom.js
test/data/testinit.js
test/index.html
test/jquery.js
test/karma.context.html
test/karma.debug.html
test/unit/selector.js

index 214a809781888953cb4c74dcf19288c1acd2649d..114a9e9b268218b40e9d558f819ca50819c06c22 100644 (file)
@@ -17,6 +17,20 @@ matrix:
       addons:
         chrome: stable
         firefox: latest
+    # Run ES module tests.
+    - node_js: "12"
+      env:
+        - NPM_SCRIPT="test:esmodules"
+        - BROWSERS="ChromeHeadless"
+      addons:
+        chrome: stable
+    # Run AMD tests.
+    - node_js: "12"
+      env:
+        - NPM_SCRIPT="test:amd"
+        - BROWSERS="ChromeHeadless"
+      addons:
+        chrome: stable
     # Run tests on Firefox ESR as well.
     - node_js: "12"
       env:
index 351700743c1b80e3bd9dc6d897a244c177594e3f..2a6226a03f0d03d6b4243b5038e2905ca0729d22 100644 (file)
@@ -141,6 +141,14 @@ module.exports = function( grunt ) {
                                                ]
                                        }
                                ],
+                               client: {
+                                       qunit: {
+
+                                               // We're running `QUnit.start()` ourselves via `loadTests()`
+                                               // in test/jquery.js
+                                               autostart: false
+                                       }
+                               },
                                files: [
                                        "test/data/jquery-1.9.1.js",
                                        "node_modules/sinon/pkg/sinon.js",
@@ -150,32 +158,6 @@ module.exports = function( grunt ) {
 
                                        "test/jquery.js",
 
-                                       // Replacement for testinit.js#loadTests()
-                                       "test/data/testrunner.js",
-                                       "test/unit/basic.js",
-                                       "test/unit/core.js",
-                                       "test/unit/callbacks.js",
-                                       "test/unit/deferred.js",
-                                       "test/unit/deprecated.js",
-                                       "test/unit/support.js",
-                                       "test/unit/data.js",
-                                       "test/unit/queue.js",
-                                       "test/unit/attributes.js",
-                                       "test/unit/event.js",
-                                       "test/unit/selector.js",
-                                       "test/unit/traversing.js",
-                                       "test/unit/manipulation.js",
-                                       "test/unit/wrap.js",
-                                       "test/unit/css.js",
-                                       "test/unit/serialize.js",
-                                       "test/unit/ajax.js",
-                                       "test/unit/effects.js",
-                                       "test/unit/offset.js",
-                                       "test/unit/dimensions.js",
-                                       "test/unit/animation.js",
-                                       "test/unit/tween.js",
-                                       "test/unit/ready.js",
-
                                        { pattern: "dist/jquery.*", included: false, served: true },
                                        { pattern: "src/**", type: "module", included: false, served: true },
                                        { pattern: "amd/**", included: false, served: true },
@@ -195,6 +177,36 @@ module.exports = function( grunt ) {
                        main: {
                                browsers: isTravis && travisBrowsers || [ "ChromeHeadless", "FirefoxHeadless" ]
                        },
+                       esmodules: {
+                               browsers: isTravis && travisBrowsers || [ "ChromeHeadless" ],
+                               options: {
+                                       client: {
+                                               qunit: {
+
+                                                       // We're running `QUnit.start()` ourselves via `loadTests()`
+                                                       // in test/jquery.js
+                                                       autostart: false,
+
+                                                       esmodules: true
+                                               }
+                                       }
+                               }
+                       },
+                       amd: {
+                               browsers: isTravis && travisBrowsers || [ "ChromeHeadless" ],
+                               options: {
+                                       client: {
+                                               qunit: {
+
+                                                       // We're running `QUnit.start()` ourselves via `loadTests()`
+                                                       // in test/jquery.js
+                                                       autostart: false,
+
+                                                       amd: true
+                                               }
+                                       }
+                               }
+                       },
 
                        jsdom: {
                                options: {
@@ -206,7 +218,7 @@ module.exports = function( grunt ) {
                                                // choosing a version etc. for jsdom.
                                                "dist/jquery.js",
 
-                                               // Replacement for testinit.js#loadTests()
+                                               // A partial replacement for testinit.js#loadTests()
                                                "test/data/testrunner.js",
 
                                                // jsdom only runs basic tests
index abfee25898bedc91177ad4005072424b6b8526b8..73789b2c2f700752fa9f134277d3b5bd947f791d 100644 (file)
@@ -72,7 +72,9 @@
     "start": "grunt watch",
     "test:browserless": "grunt && grunt test:slow",
     "test:browser": "grunt && grunt karma:main",
-    "test": "grunt && grunt test:slow && grunt karma:main",
+    "test:esmodules": "grunt && grunt karma:esmodules",
+    "test:amd": "grunt && grunt karma:amd",
+    "test": "grunt && grunt test:slow && grunt karma:main && grunt karma:esmodules && grunt karma:amd",
     "jenkins": "npm run test:browserless"
   },
   "commitplease": {
index 37e57d8c4212104a47426fea5242f767ca53a268..bdb3c178e8ce7478fc432d963874fb96ff18416a 100644 (file)
@@ -41,3 +41,17 @@ function url( value ) {
        return baseURL + value + ( /\?/.test( value ) ? "&" : "?" ) +
                new Date().getTime() + "" + parseInt( Math.random() * 100000, 10 );
 }
+
+// The file-loading part of testinit.js#loadTests is handled by
+// jsdom Karma config; here we just need to trigger relevant APIs.
+this.loadTests = function() {
+
+       // Delay the initialization until after all the files are loaded
+       // as in the JSDOM case we load them via Karma (see Gruntfile.js)
+       // instead of directly in testinit.js.
+       window.addEventListener( "load", function() {
+               window.__karma__.start();
+               jQuery.noConflict();
+               QUnit.start();
+       } );
+};
index 4088e4671ef3d1a609027e4d842330a73dc6bcf5..61ffcbc8fdf0b659c18b722165619e83b29ccc8d 100644 (file)
@@ -301,10 +301,11 @@ this.loadTests = function() {
 
        // QUnit.config is populated from QUnit.urlParams but only at the beginning
        // of the test run. We need to read both.
-       var amd = QUnit.config.amd || QUnit.urlParams.amd;
+       var esmodules = QUnit.config.esmodules || QUnit.urlParams.esmodules,
+               amd = QUnit.config.amd || QUnit.urlParams.amd;
 
        // Directly load tests that need evaluation before DOMContentLoaded.
-       if ( !amd || document.readyState === "loading" ) {
+       if ( ( !esmodules && !amd ) || document.readyState === "loading" ) {
                document.write( "<script src='" + parentUrl + "test/unit/ready.js'><\x2Fscript>" );
        } else {
                QUnit.module( "ready", function() {
@@ -360,7 +361,11 @@ this.loadTests = function() {
                                }
 
                        } else {
-                               QUnit.load();
+                               if ( window.__karma__ && window.__karma__.start ) {
+                                       window.__karma__.start();
+                               } else {
+                                       QUnit.load();
+                               }
 
                                /**
                                 * Run in noConflict mode
index fff50faab79aeb77f972ee98ddea0e3bd5040e3e..6260c49dafb592867a5fd8b0e7d81b5f1f887d81 100644 (file)
@@ -19,7 +19,7 @@
        <!-- See testinit for the list of tests -->
        <script src="data/testinit.js"></script>
 
-       <!-- A script that includes jQuery min, dev, ES modules or AMD -->
+       <!-- A script that includes jQuery min, dev, ES modules or AMD modules -->
        <!-- Adds "basic" URL option, even to iframes -->
        <!-- iframes will not load AMD as loading needs to be synchronous for some tests -->
        <!-- Also executes the function above to load tests -->
                // Load tests if they have not been loaded
                // This is in a different script tag to ensure that
                // jQuery is on the page when the testrunner executes
-               if ( !QUnit.urlParams.esmodules && !QUnit.urlParams.amd ) {
+               // QUnit.config is populated from QUnit.urlParams but only at the beginning
+               // of the test run. We need to read both.
+               var esmodules = QUnit.config.esmodules || QUnit.urlParams.esmodules,
+                       amd = QUnit.config.amd || QUnit.urlParams.amd;
+
+               // Workaround: Remove call to `window.__karma__.loaded()`
+               // in favour of calling `window.__karma__.start()` from `loadTests()`
+               // because tests such as unit/ready.js should run after document ready.
+               if ( !esmodules && !amd ) {
                        loadTests();
                }
        </script>
index c34c7c4919a40323dcc5a4555e7bd14a9472377a..be24453954a7e12145ca21f1dc59578c12aec02d 100644 (file)
@@ -8,36 +8,35 @@
                parentUrl = activeScript && activeScript.src ?
                        activeScript.src.replace( /[?#].*/, "" ) + FILEPATH.replace( /[^/]+/g, ".." ) + "/" :
                        "../",
-               QUnit = window.QUnit || parent.QUnit,
-               require = window.require || parent.require,
+               QUnit = window.QUnit,
+               require = window.require,
 
                // Default to unminified jQuery for directly-opened iframes
-               urlParams = QUnit ?
-                       QUnit.urlParams :
+               config = QUnit ?
+
+                       // QUnit.config is populated from QUnit.urlParams but only at the beginning
+                       // of the test run. We need to read both.
+                       {
+                               esmodules: !!( QUnit.config.esmodules || QUnit.urlParams.esmodules ),
+                               amd: !!( QUnit.config.amd || QUnit.urlParams.amd )
+                       } :
+
                        { dev: true },
-               src = urlParams.dev ?
+               src = config.dev ?
                        "dist/jquery.js" :
                        "dist/jquery.min.js";
 
        // Define configuration parameters controlling how jQuery is loaded
        if ( QUnit ) {
-
-               // ES modules loading is asynchronous and incompatible with synchronous
-               // test loading in Karma.
-               if ( !window.__karma__ ) {
-                       QUnit.config.urlConfig.push( {
-                               id: "esmodules",
-                               label: "Load as modules",
-                               tooltip: "Load the jQuery module file (and its dependencies)"
-                       } );
-                       QUnit.config.urlConfig.push( {
-                               id: "amd",
-                               label: "Load with AMD",
-                               tooltip: "Load the AMD jQuery file (and its dependencies)"
-                       } );
-               }
-
                QUnit.config.urlConfig.push( {
+                       id: "esmodules",
+                       label: "Load as modules",
+                       tooltip: "Load the jQuery module file (and its dependencies)"
+               }, {
+                       id: "amd",
+                       label: "Load with AMD",
+                       tooltip: "Load the AMD jQuery file (and its dependencies)"
+               }, {
                        id: "dev",
                        label: "Load unminified",
                        tooltip: "Load the development (unminified) jQuery file"
 
        // Honor ES modules loading on the main window (detected by seeing QUnit on it).
        // This doesn't apply to iframes because they synchronously expect jQuery to be there.
-       if ( urlParams.esmodules && window.QUnit ) {
+       if ( config.esmodules && QUnit ) {
 
                // Support: IE 11+, Edge 12 - 18+
                // IE/Edge don't support the dynamic import syntax so they'd crash
                // with a SyntaxError here.
                dynamicImportSource = "" +
-                       "import( `${ parentUrl }src/jquery.js` ).then( ( { default: jQuery } ) => {\n" +
-                       "       window.jQuery = jQuery;\n" +
-                       "       if ( typeof loadTests === \"function\" ) {\n" +
-                       "               // Include tests if specified\n" +
-                       "               loadTests();\n" +
-                       "       }\n" +
-                       "} );";
+                       "import( `${ parentUrl }src/jquery.js` )\n" +
+                       "       .then( ( { default: jQuery } ) => {\n" +
+                       "               window.jQuery = jQuery;\n" +
+                       "               if ( typeof loadTests === \"function\" ) {\n" +
+                       "                       // Include tests if specified\n" +
+                       "                       loadTests();\n" +
+                       "               }\n" +
+                       "       } )\n" +
+                       "       .catch( error => {\n" +
+                       "               console.error( error );\n" +
+                       "               QUnit.done();\n" +
+                       "       } );";
 
                eval( dynamicImportSource );
 
        // Apply similar treatment for AMD modules
-       } else if ( urlParams.amd && window.QUnit ) {
+       } else if ( config.amd && QUnit ) {
                require.config( {
                        baseUrl: parentUrl
                } );
index e4f8a4f88541962af7d2c7cb329fffcd4e185d53..8439bef945bb2d584137188e4f5bc54bcc502894 100644 (file)
@@ -1,45 +1,44 @@
 <!DOCTYPE html>
 <html lang="en" id="html">
 <head>
-  <meta charset="utf-8">
-  <title>CONTEXT</title>
-  <!-- Karma serves this page from /context.html. Other files are served from /base -->
-  <link rel="stylesheet" href="/base/test/data/testsuite.css" />
+       <meta charset="utf-8">
+       <title>CONTEXT</title>
+       <!-- Karma serves this page from /context.html. Other files are served from /base -->
+       <link rel="stylesheet" href="/base/test/data/testsuite.css" />
 </head>
 <body id="body">
-  <div id="qunit"></div>
+       <div id="qunit"></div>
 
-  <!-- Start: jQuery Test HTML -->
-  <!-- this iframe is outside the #qunit-fixture so it won't waste time by constantly reloading; the tests are "safe" and clean up after themselves -->
-  <iframe id="loadediframe" name="loadediframe" style="display:none;" src="/base/test/data/iframe.html"></iframe>
-  <div id="qunit-fixture"></div>
-  <!-- End: jQuery Test HTML -->
+       <!-- Start: jQuery Test HTML -->
+       <!-- this iframe is outside the #qunit-fixture so it won't waste time by constantly reloading; the tests are "safe" and clean up after themselves -->
+       <iframe id="loadediframe" name="loadediframe" style="display:none;" src="/base/test/data/iframe.html"></iframe>
+       <div id="qunit-fixture"></div>
+       <!-- End: jQuery Test HTML -->
 
-  <!-- Start: Karma boilerplate -->
-  <script src="/context.js"></script>
-  <script>
-    %CLIENT_CONFIG%
-    window.__karma__.setupContext(window);
+       <!-- Start: Karma boilerplate -->
+       <script src="/context.js"></script>
+       <script>
+               %CLIENT_CONFIG%
+               window.__karma__.setupContext(window);
 
-    %MAPPINGS%
-  </script>
-  %SCRIPTS%
-  <!-- End: Karma boilerplate -->
+               %MAPPINGS%
+       </script>
+       %SCRIPTS%
+       <!-- End: Karma boilerplate -->
 
-  <script src="/base/test/data/qunit-fixture.js"></script>
-  <script>
-    // Workaround: Remove call to window.__karma__.loaded()
-    // in favour of calling window.__karma__.start() at window.onload
-    // because tests such as unit/ready.js should run after document ready
-    window.addEventListener('load', function() {
-      window.__karma__.start();
+       <script src="/base/test/data/qunit-fixture.js"></script>
+       <script>
+               // QUnit.config is populated from QUnit.urlParams but only at the beginning
+               // of the test run. We need to read both.
+               var esmodules = QUnit.config.esmodules || QUnit.urlParams.esmodules,
+                       amd = QUnit.config.amd || QUnit.urlParams.amd;
 
-      // Workaround: https://github.com/karma-runner/karma-qunit/issues/92
-      QUnit.testStart(function () {
-        // Restore content
-        document.getElementById("qunit-fixture").innerHTML = QUnit.config.fixture;
-      });
-    });
-  </script>
+               // Workaround: Remove call to `window.__karma__.loaded()`
+               // in favour of calling `window.__karma__.start()` from `loadTests()`
+               // because tests such as unit/ready.js should run after document ready.
+               if ( !esmodules && !amd ) {
+                       loadTests();
+               }
+       </script>
 </body>
 </html>
index 9fdb4f6417d2108b106d67dcfa5d067e8decb5ea..2e1083c41047ae18b06d0c236fefa20cfd548e6b 100644 (file)
@@ -2,46 +2,45 @@
 <html lang="en" id="html">
 <head>
 %X_UA_COMPATIBLE%
-  <title>DEBUG</title>
-  <meta charset="utf-8">
-  <!-- Karma serves this page from /context.html. Other files are served from /base -->
-  <link rel="stylesheet" href="/base/node_modules/qunit/qunit/qunit.css" />
-  <link rel="stylesheet" href="/base/test/data/testsuite.css" />
+       <title>DEBUG</title>
+       <meta charset="utf-8">
+       <!-- Karma serves this page from /context.html. Other files are served from /base -->
+       <link rel="stylesheet" href="/base/node_modules/qunit/qunit/qunit.css" />
+       <link rel="stylesheet" href="/base/test/data/testsuite.css" />
 </head>
 <body id="body">
-  <div id="qunit"></div>
+       <div id="qunit"></div>
 
-  <!-- Start: jQuery Test HTML -->
-  <!-- this iframe is outside the #qunit-fixture so it won't waste time by constantly reloading; the tests are "safe" and clean up after themselves -->
-  <iframe id="loadediframe" name="loadediframe" style="display:none;" src="/base/test/data/iframe.html"></iframe>
-  <div id="qunit-fixture"></div>
-  <!-- End: jQuery Test HTML -->
+       <!-- Start: jQuery Test HTML -->
+       <!-- this iframe is outside the #qunit-fixture so it won't waste time by constantly reloading; the tests are "safe" and clean up after themselves -->
+       <iframe id="loadediframe" name="loadediframe" style="display:none;" src="/base/test/data/iframe.html"></iframe>
+       <div id="qunit-fixture"></div>
+       <!-- End: jQuery Test HTML -->
 
-  <!-- Start: Karma boilerplate -->
-  <script src="/context.js"></script>
-  <script src="/debug.js"></script>
-  <script>
-    %CLIENT_CONFIG%
+       <!-- Start: Karma boilerplate -->
+       <script src="/context.js"></script>
+       <script src="/debug.js"></script>
+       <script>
+               %CLIENT_CONFIG%
 
-    %MAPPINGS%
-  </script>
-  %SCRIPTS%
-  <!-- End: Karma boilerplate -->
+               %MAPPINGS%
+       </script>
+       %SCRIPTS%
+       <!-- End: Karma boilerplate -->
 
-  <script src="/base/test/data/qunit-fixture.js"></script>
-  <script>
-    // Workaround: Remove call to window.__karma__.loaded()
-    // in favour of calling window.__karma__.start() at window.onload
-    // because tests such as unit/ready.js should run after document ready
-    window.addEventListener('load', function() {
-      window.__karma__.start();
+       <script src="/base/test/data/qunit-fixture.js"></script>
+       <script>
+               // QUnit.config is populated from QUnit.urlParams but only at the beginning
+               // of the test run. We need to read both.
+               var esmodules = QUnit.config.esmodules || QUnit.urlParams.esmodules,
+                       amd = QUnit.config.amd || QUnit.urlParams.amd;
 
-      // Workaround: https://github.com/karma-runner/karma-qunit/issues/92
-      QUnit.testStart(function () {
-        // Restore content
-        document.getElementById("qunit-fixture").innerHTML = QUnit.config.fixture;
-      });
-    });
-  </script>
+               // Workaround: Remove call to `window.__karma__.loaded()`
+               // in favour of calling `window.__karma__.start()` from `loadTests()`
+               // because tests such as unit/ready.js should run after document ready.
+               if ( !esmodules && !amd ) {
+                       loadTests();
+               }
+       </script>
 </body>
 </html>
index f5e15e8c2fb47d1f760f729f954047941de1e8e4..a8204242a8ad42cacc87b54ae3991e92d2fd1dff 100644 (file)
@@ -1092,7 +1092,7 @@ QUnit.test( "pseudo - misc", function( assert ) {
                assert.t( "Multi-pseudo", "#ap:has(*), #ap:has(*)", [ "ap" ] );
                assert.t( "Multi-pseudo with leading nonexistent id", "#nonexistent:has(*), #ap:has(*)", [ "ap" ] );
 
-               assert.t( "Tokenization stressor", "a[class*=blog]:not(:has(*, :contains(!)), :contains(!)), br:contains(]), p:contains(]), :not(:empty):not(:parent)", [ "ap", "mark", "yahoo", "simon" ] );
+               assert.t( "Tokenization stressor", "a[class*=blog]:not(:has(*, :contains(!)), :contains(!)), br:contains(]), p:contains(]):not(.qunit-source), :not(:empty):not(:parent):not(.qunit-source)", [ "ap", "mark", "yahoo", "simon" ] );
        } else {
                assert.ok( "skip", ":has not supported in selector-native" );
                assert.ok( "skip", ":has not supported in selector-native" );