From 341c6d1b5abe4829f59fbc32e93f6a6a1afb900f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Mon, 16 Dec 2019 19:33:49 +0100 Subject: [PATCH] Build: Make Karma work in ES modules mode Also, run such a suite in CI to make sure modules are working as expected when used directly. Closes gh-4550 --- .travis.yml | 14 ++++++++ Gruntfile.js | 66 +++++++++++++++++++++--------------- package.json | 4 ++- test/data/testinit-jsdom.js | 14 ++++++++ test/data/testinit.js | 11 ++++-- test/index.html | 12 +++++-- test/jquery.js | 64 ++++++++++++++++++----------------- test/karma.context.html | 65 ++++++++++++++++++----------------- test/karma.debug.html | 67 ++++++++++++++++++------------------- test/unit/selector.js | 2 +- 10 files changed, 188 insertions(+), 131 deletions(-) diff --git a/.travis.yml b/.travis.yml index 214a80978..114a9e9b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/Gruntfile.js b/Gruntfile.js index 351700743..2a6226a03 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -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 diff --git a/package.json b/package.json index abfee2589..73789b2c2 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/test/data/testinit-jsdom.js b/test/data/testinit-jsdom.js index 37e57d8c4..bdb3c178e 100644 --- a/test/data/testinit-jsdom.js +++ b/test/data/testinit-jsdom.js @@ -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(); + } ); +}; diff --git a/test/data/testinit.js b/test/data/testinit.js index 4088e4671..61ffcbc8f 100644 --- a/test/data/testinit.js +++ b/test/data/testinit.js @@ -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( " - + @@ -29,7 +29,15 @@ // 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(); } diff --git a/test/jquery.js b/test/jquery.js index c34c7c491..be2445395 100644 --- a/test/jquery.js +++ b/test/jquery.js @@ -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" @@ -46,24 +45,29 @@ // 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 } ); diff --git a/test/karma.context.html b/test/karma.context.html index e4f8a4f88..8439bef94 100644 --- a/test/karma.context.html +++ b/test/karma.context.html @@ -1,45 +1,44 @@ - - CONTEXT - - + + CONTEXT + + -
+
- - - -
- + + + +
+ - - - + - %SCRIPTS% - + %MAPPINGS% + + %SCRIPTS% + - - + + // 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(); + } + diff --git a/test/karma.debug.html b/test/karma.debug.html index 9fdb4f641..2e1083c41 100644 --- a/test/karma.debug.html +++ b/test/karma.debug.html @@ -2,46 +2,45 @@ %X_UA_COMPATIBLE% - DEBUG - - - - + DEBUG + + + + -
+
- - - -
- + + + +
+ - - - - + + - %SCRIPTS% - + %MAPPINGS% + + %SCRIPTS% + - - + + // 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(); + } + diff --git a/test/unit/selector.js b/test/unit/selector.js index f5e15e8c2..a8204242a 100644 --- a/test/unit/selector.js +++ b/test/unit/selector.js @@ -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" ); -- 2.39.5