Build: migrate most grunt tasks off of grunt

Updated tasks include:

- lint
- npmcopy
- build, minify, and process for distribution.
- new custom build command using yargs
- compare size of minified/gzip built files
- pretest scripts, including qunit-fixture, babel transpilation, and npmcopy
- node smoke tests
- promises aplus tests
- new watch task using `rollup.watch` directly

Also:

- upgraded husky and added the new lint command
- updated lint config to use new "flat" config format. See https://eslint.org/docs/latest/use/configure/configuration-files-new
- Temporarily disabled one lint rule until flat config is supported by eslint-plugin-import. See https://github.com/import-js/eslint-plugin-import/issues/2556
- committed package-lock.json
- updated all test scripts to use the new build
- added an express test server that uses middleware-mockserver (this can be used to run tests without karma)
- build-all-variants is now build:all

Close gh-5318
This commit is contained in:
Timmy Willison 2023-09-18 12:39:00 -04:00 committed by GitHub
parent f75daab091
commit 2bdecf8b7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 9968 additions and 1650 deletions

View File

@ -10,7 +10,7 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[package.json]
[*.{json,yml}]
indent_style = space
indent_size = 2

View File

@ -1,22 +0,0 @@
external
node_modules
*.min.js
dist/**
!dist/jquery.js
!dist/jquery.min.js
!dist/jquery.slim.js
!dist/jquery.slim.min.js
dist-module/**
!dist-module/jquery.module.js
!dist-module/jquery.module.min.js
!dist-module/jquery.slim.module.js
!dist-module/jquery.slim.module.min.js
test/data/jquery-1.9.1.js
test/data/badcall.js
test/data/badjson.js
test/data/json_obj.js
test/data/readywaitasset.js
test/data/readywaitloader.js
test/data/support/csp.js
test/data/support/getComputedSupport.js
test/data/core/jquery-iterability-transpiled.js

View File

@ -1,27 +0,0 @@
{
"root": true,
"extends": "jquery",
"reportUnusedDisableDirectives": true,
"parserOptions": {
"ecmaVersion": 5
},
// The browser env is not enabled on purpose so that code takes
// all browser-only globals from window instead of assuming
// they're available as globals. This makes it possible to use
// jQuery with tools like jsdom which provide a custom window
// implementation.
"env": {},
"globals": {
"window": true
},
"rules": {
"one-var": ["error", {"var": "always"}],
"strict": ["error", "function"]
}
}

View File

@ -1,20 +0,0 @@
{
"root": true,
"extends": "jquery",
"reportUnusedDisableDirectives": true,
"parserOptions": {
"ecmaVersion": 2018
},
"env": {
"es6": true,
"node": true
},
"rules": {
"strict": ["error", "global"]
}
}

View File

@ -1,5 +0,0 @@
{
"root": true,
"extends": "./.eslintrc-node.json"
}

View File

@ -61,15 +61,25 @@ jobs:
run: |
export FIREFOX_SOURCE_URL='https://download.mozilla.org/?product=firefox-esr-latest&lang=en-US&os=linux64'
wget --no-verbose $FIREFOX_SOURCE_URL -O - | tar -jx -C ${HOME}
if: "contains(matrix.NAME, 'Firefox ESR')"
if: contains(matrix.NAME, 'Firefox ESR')
- name: Install dependencies
run: |
npm install
run: npm install
- name: Install Playwright dependencies
run: npx playwright-webkit install-deps
if: "matrix.NPM_SCRIPT == 'test:browser' && contains(matrix.BROWSERS, 'WebkitHeadless')"
if: matrix.NPM_SCRIPT == 'test:browser' && contains(matrix.BROWSERS, 'WebkitHeadless')
- name: Build jQuery for Lint
run: npm run build:all
if: matrix.NPM_SCRIPT == 'test:browserless'
- name: Lint code
run: npm run lint
if: matrix.NODE_VERSION == '18.x'
- name: Prepare tests
run: npm run pretest
- name: Run tests
env:

7
.gitignore vendored
View File

@ -8,19 +8,18 @@
.bower.json
.sizecache.json
yarn.lock
package-lock.json
.eslintcache
npm-debug.log*
# Ignore everything in `dist` folder except for the ESLint config
# Ignore everything in `dist` folder except for
# the ESLint config & package.json files
/dist/*
!/dist/.eslintrc.json
!/dist/package.json
# Ignore everything in the `dist-module` folder except for the ESLint config,
# package.json & Node module wrapper files
/dist-module/*
!/dist-module/.eslintrc.json
!/dist-module/package.json
!/dist-module/jquery.node-module-wrapper.js
!/dist-module/jquery.node-module-wrapper.slim.js

4
.husky/commit-msg Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx commitplease .git/COMMIT_EDITMSG

5
.husky/pre-commit Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint
npm run qunit-fixture

View File

@ -1,5 +1,5 @@
.eslintignore
.eslintrc.json
eslint.config.js
/.editorconfig
/.gitattributes

View File

@ -1,20 +1,6 @@
"use strict";
module.exports = function( grunt ) {
function readOptionalJSON( filepath ) {
const stripJSONComments = require( "strip-json-comments" );
let data = {};
try {
data = JSON.parse( stripJSONComments(
fs.readFileSync( filepath, { encoding: "utf8" } )
) );
} catch ( e ) {}
return data;
}
const fs = require( "fs" );
const { spawn } = require( "child_process" );
const gzip = require( "gzip-js" );
const nodeV16OrNewer = !/^v1[0-5]\./.test( process.version );
const nodeV17OrNewer = !/^v1[0-6]\./.test( process.version );
const customBrowsers = process.env.BROWSERS && process.env.BROWSERS.split( "," );
@ -34,137 +20,8 @@ module.exports = function( grunt ) {
grunt.option( "filename", "jquery.js" );
}
grunt.option( "dist-folder", grunt.option( "esm" ) ? "dist-module" : "dist" );
const builtJsFiles = [
"dist/jquery.js",
"dist/jquery.min.js",
"dist/jquery.slim.js",
"dist/jquery.slim.min.js",
"dist-module/jquery.module.js",
"dist-module/jquery.module.min.js",
"dist-module/jquery.slim.module.js",
"dist-module/jquery.slim.module.min.js"
];
const builtJsMinFiles = builtJsFiles
.filter( filepath => filepath.endsWith( ".min.js" ) );
grunt.initConfig( {
pkg: grunt.file.readJSON( "package.json" ),
dst: readOptionalJSON( "dist/.destination.json" ),
compare_size: {
files: builtJsMinFiles,
options: {
compress: {
gz: function( contents ) {
return gzip.zip( contents, {} ).length;
}
},
cache: "build/.sizecache.json"
}
},
babel: {
options: {
sourceMap: "inline",
retainLines: true,
plugins: [ "@babel/transform-for-of" ]
},
tests: {
files: {
"test/data/core/jquery-iterability-transpiled.js":
"test/data/core/jquery-iterability-transpiled-es6.js"
}
}
},
build: {
all: {
dest: "dist/jquery.js",
minimum: [
"core"
],
// Exclude specified modules if the module matching the key is removed
removeWith: {
ajax: [ "manipulation/_evalUrl", "deprecated/ajax-event-alias" ],
callbacks: [ "deferred" ],
css: [ "effects", "dimensions", "offset" ],
"css/showHide": [ "effects" ],
deferred: {
remove: [ "ajax", "effects", "queue", "core/ready" ],
include: [ "core/ready-no-deferred" ]
},
event: [ "deprecated/ajax-event-alias", "deprecated/event" ],
selector: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
}
}
},
npmcopy: {
all: {
options: {
destPrefix: "external"
},
files: {
"bootstrap/bootstrap.css": "bootstrap/dist/css/bootstrap.css",
"bootstrap/bootstrap.min.css": "bootstrap/dist/css/bootstrap.min.css",
"bootstrap/bootstrap.min.css.map": "bootstrap/dist/css/bootstrap.min.css.map",
"core-js-bundle/core-js-bundle.js": "core-js-bundle/minified.js",
"core-js-bundle/LICENSE": "core-js-bundle/LICENSE",
"npo/npo.js": "native-promise-only/lib/npo.src.js",
"qunit/qunit.js": "qunit/qunit/qunit.js",
"qunit/qunit.css": "qunit/qunit/qunit.css",
"qunit/LICENSE.txt": "qunit/LICENSE.txt",
"requirejs/require.js": "requirejs/require.js",
"sinon/sinon.js": "sinon/pkg/sinon.js",
"sinon/LICENSE.txt": "sinon/LICENSE"
}
}
},
jsonlint: {
pkg: {
src: [ "package.json" ]
}
},
eslint: {
options: {
maxWarnings: 0
},
// We have to explicitly declare "src" property otherwise "newer"
// task wouldn't work properly :/
dist: {
src: builtJsFiles
},
dev: {
src: [
"src/**/*.js",
"Gruntfile.js",
"test/**/*.js",
"build/**/*.js",
// Ignore files from .eslintignore
// See https://github.com/sindresorhus/grunt-eslint/issues/119
...fs
.readFileSync( `${ __dirname }/.eslintignore`, "utf-8" )
.split( "\n" )
.filter( filePath => filePath )
.map( filePath => filePath[ 0 ] === "!" ?
filePath.slice( 1 ) :
`!${ filePath }`
),
// Explicitly ignore `dist/` & `dist-module/` as it could be unignored
// by the above `.eslintignore` parsing.
"!dist/**/*.js",
"!dist-module/**/*.js"
]
}
},
testswarm: {
tests: [
@ -230,9 +87,14 @@ module.exports = function( grunt ) {
"external/npo/npo.js",
"external/requirejs/require.js",
"test/data/testinit.js",
"test/jquery.js",
{
pattern: "external/**",
included: false,
served: true,
nocache: true
},
{
pattern: "dist/jquery.*",
included: false,
@ -246,7 +108,6 @@ module.exports = function( grunt ) {
served: true,
nocache: true
},
{ pattern: "external/**", included: false, served: true },
{
pattern: "test/**/*.@(js|css|jpg|html|xml|svg)",
included: false,
@ -328,108 +189,27 @@ module.exports = function( grunt ) {
browsers: [ "IE" ],
singleRun: false
}
},
watch: {
files: [ "<%= eslint.dev.src %>" ],
tasks: [ "dev" ]
},
minify: {
all: {
files: {
[ "<%= grunt.option('dist-folder') %>/" +
"<%= grunt.option('filename').replace(/\\.js$/, '.min.js') %>" ]:
"<%= grunt.option('dist-folder') %>/<%= grunt.option('filename') %>"
},
options: {
sourceMap: {
filename: "<%= grunt.option('dist-folder') %>/" +
"<%= grunt.option('filename')" +
".replace(/\\.js$/, '.min.map') %>",
// The map's `files` & `sources` property are set incorrectly, fix
// them via overrides from the task config.
// See https://github.com/swc-project/swc/issues/7588#issuecomment-1624345254
overrides: {
file: "jquery.min.js",
sources: [
"jquery.js"
]
}
},
swc: {
format: {
ecma: grunt.option( "esm" ) ? 2015 : 5,
asciiOnly: true,
comments: false,
preamble: "/*! jQuery v4.0.0-pre | " +
"(c) OpenJS Foundation and other contributors | " +
"jquery.org/license */\n"
},
compress: {
ecma: grunt.option( "esm" ) ? 2015 : 5,
hoist_funs: false,
loops: false
},
mangle: true
}
}
}
}
} );
// Load grunt tasks from NPM packages
require( "load-grunt-tasks" )( grunt, {
pattern: nodeV16OrNewer ? [ "grunt-*" ] : [ "grunt-*", "!grunt-eslint" ]
pattern: [ "grunt-*" ]
} );
// Integrate jQuery specific tasks
grunt.loadTasks( "build/tasks" );
grunt.loadTasks( "build/grunt-tasks" );
grunt.registerTask( "print_old_node_message", ( ...args ) => {
var task = args.join( ":" );
grunt.log.writeln( "Old Node.js detected, running the task \"" + task + "\" skipped..." );
} );
grunt.registerTask( "build-all-variants",
"Build all variants of the full/slim build & a script/ESM one",
function() {
const done = this.async();
spawn( "npm run build-all-variants", {
stdio: "inherit",
shell: true
} )
.on( "close", code => {
done( code === 0 );
} );
} );
grunt.registerTask( "print_jsdom_message", () => {
grunt.log.writeln( "Node.js 17 or newer detected, skipping jsdom tests..." );
} );
grunt.registerTask( "lint", [
"jsonlint",
// Running the full eslint task without breaking it down to targets
// would run the dist target first which would point to errors in the built
// file, making it harder to fix them. We want to check the built file only
// if we already know the source files pass the linter.
runIfNewNode( "eslint:dev" ),
runIfNewNode( "eslint:dist" )
] );
grunt.registerTask( "lint:newer", [
"newer:jsonlint",
// Don't replace it with just the task; see the above comment.
runIfNewNode( "newer:eslint:dev" ),
runIfNewNode( "newer:eslint:dist" )
] );
grunt.registerTask( "test:fast", [ "node_smoke_tests:commonjs:jquery" ] );
grunt.registerTask( "test:slow", [
runIfNewNode( "promises_aplus_tests" ),
grunt.registerTask( "test:jsdom", [
// Support: Node.js 17+
// jsdom fails to connect to the Karma server in Node 17+.
@ -437,33 +217,11 @@ module.exports = function( grunt ) {
nodeV17OrNewer ? "print_jsdom_message" : runIfNewNode( "karma:jsdom" )
] );
grunt.registerTask( "test:prepare", [
"npmcopy",
"qunit_fixture",
"babel:tests"
] );
grunt.registerTask( "test", [
"test:prepare",
"test:fast",
"test:slow"
] );
grunt.registerTask( "dev", [
"build:*:*",
runIfNewNode( "newer:eslint:dev" ),
"newer:minify",
"dist:*",
"qunit_fixture",
"compare_size"
"test:jsdom"
] );
grunt.registerTask( "default", [
runIfNewNode( "eslint:dev" ),
"build-all-variants",
"test:prepare",
runIfNewNode( "eslint:dist" ),
"test:fast",
"compare_size"
"test"
] );
};

156
README.md
View File

@ -47,41 +47,47 @@ How to build your own jQuery
First, [clone the jQuery git repo](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository).
Then, enter the jquery directory and run the build script:
Then, enter the jquery directory, install dependencies, and run the build script:
```bash
cd jquery && npm run build
```
The built version of jQuery will be put in the `dist/` subdirectory, along with the minified copy and associated map file.
If you want to create custom build or help with jQuery development, it would be better to install [grunt command line interface](https://github.com/gruntjs/grunt-cli) as a global package:
```
npm install -g grunt-cli
```
Make sure you have `grunt` installed by testing:
```
grunt -V
cd jquery
npm install
npm run build
```
Now by running the `grunt` command, in the jquery directory, you can build a full version of jQuery, just like with an `npm run build` command:
```
grunt
The built version of jQuery will be placed in the `dist/` directory, along with a minified copy and associated map file.
## Build all jQuery release files
To build all variants of jQuery, run the following command:
```bash
npm run build:all
```
There are many other tasks available for jQuery Core:
```
grunt -help
This will create all of the variants that jQuery includes in a release, including `jquery.js`, `jquery.slim.js`, `jquery.module.js`, and `jquery.slim.module.js` along their associated minified files and sourcemaps.
`jquery.module.js` and `jquery.slim.module.js` are [ECMAScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) that export `jQuery` and `$` as named exports are placed in the `dist-module/` directory rather than the `dist/` directory.
## Building a Custom jQuery
The build script can be used to create a custom version of jQuery that includes only the modules you need.
Any module may be excluded except for `core`. When excluding `selector`, it is not removed but replaced with a small wrapper around native `querySelectorAll` (see below for more information).
### Build Script Help
To see the full list of available options for the build script, run the following:
```bash
npm run build -- --help
```
### Modules
Special builds can be created that exclude subsets of jQuery functionality.
This allows for smaller custom builds when the builder is certain that those parts of jQuery are not being used.
For example, an app that only used JSONP for `$.ajax()` and did not need to calculate offsets or positions of elements could exclude the offset and ajax/xhr modules.
To exclude a module, pass its path relative to the `src` folder (without the `.js` extension) to the `--exclude` option. When using the `--include` option, the default includes are dropped and a build is created with only those modules.
Any module may be excluded except for `core`, and `selector`. To exclude a module, pass its path relative to the `src` folder (without the `.js` extension).
Some example modules that can be excluded are:
Some example modules that can be excluded or included are:
- **ajax**: All AJAX functionality: `$.ajax()`, `$.get()`, `$.post()`, `$.ajaxSetup()`, `.load()`, transports, and ajax event shorthands such as `.ajaxStart()`.
- **ajax/xhr**: The XMLHTTPRequest AJAX transport only.
@ -97,96 +103,81 @@ Some example modules that can be excluded are:
- **offset**: The `.offset()`, `.position()`, `.offsetParent()`, `.scrollLeft()`, and `.scrollTop()` methods.
- **wrap**: The `.wrap()`, `.wrapAll()`, `.wrapInner()`, and `.unwrap()` methods.
- **core/ready**: Exclude the ready module if you place your scripts at the end of the body. Any ready callbacks bound with `jQuery()` will simply be called immediately. However, `jQuery(document).ready()` will not be a function and `.on("ready", ...)` or similar will not be triggered.
- **deferred**: Exclude jQuery.Deferred. This also removes jQuery.Callbacks. *Note* that modules that depend on jQuery.Deferred(AJAX, effects, core/ready) will not be removed and will still expect jQuery.Deferred to be there. Include your own jQuery.Deferred implementation or exclude those modules as well (`grunt custom:-deferred,-ajax,-effects,-core/ready`).
- **deferred**: Exclude jQuery.Deferred. This also excludes all modules that rely on Deferred, including **ajax**, **effects**, and **queue**, but replaces **core/ready** with **core/ready-no-deferred**.
- **exports/global**: Exclude the attachment of global jQuery variables ($ and jQuery) to the window.
- **exports/amd**: Exclude the AMD definition.
As a special case, you may also replace the full jQuery `selector` module by using a special flag `grunt custom:-selector`.
- **selector**: The full jQuery selector engine. When this module is excluded, it is replaced by a rudimentary selector engine based on the browser's `querySelectorAll` method that does not support jQuery selector extensions or enhanced semantics. See the [selector-native.js](https://github.com/jquery/jquery/blob/main/src/selector-native.js) file for details.
- **selector**: The full jQuery selector engine. When this module is excluded, it is replaced with a rudimentary selector engine based on the browser's `querySelectorAll` method that does not support jQuery selector extensions or enhanced semantics. See the [selector-native.js](https://github.com/jquery/jquery/blob/main/src/selector-native.js) file for details.
*Note*: Excluding the full `selector` module will also exclude all jQuery selector extensions (such as `effects/animatedSelector` and `css/hiddenVisibleSelectors`).
The build process shows a message for each dependent module it excludes or includes.
##### AMD name
As an option, you can set the module name for jQuery's AMD definition. By default, it is set to "jquery", which plays nicely with plugins and third-party libraries, but there may be cases where you'd like to change this. Simply pass it to the `--amd` parameter:
You can set the module name for jQuery's AMD definition. By default, it is set to "jquery", which plays nicely with plugins and third-party libraries, but there may be cases where you'd like to change this. Pass it to the `--amd` parameter:
```bash
grunt custom --amd="custom-name"
npm run build -- --amd="custom-name"
```
Or, to define anonymously, set the name to an empty string.
Or, to define anonymously, leave the name blank.
```bash
grunt custom --amd=""
npm run build -- --amd
```
##### File name
##### File name and directory
The default name for the built jQuery file is `jquery.js`; it is placed under the `dist/` directory. It's possible to change the file name using the `--filename` parameter:
The default name for the built jQuery file is `jquery.js`; it is placed under the `dist/` directory. It's possible to change the file name using `--filename` and the directory using `--dir`. `--dir` is relative to the project root.
```bash
grunt custom:slim --filename="jquery.slim.js"
npm run build -- --slim --filename="jquery.slim.js" --dir="/tmp"
```
This would create a slim version of jQuery and place it under `dist/jquery.slim.js`. In fact, this is exactly the command we use to generate the slim jQuery during the release process.
This would create a slim version of jQuery and place it under `tmp/jquery.slim.js`.
##### ECMAScript Module (ESM) mode
By default, jQuery generates a regular script JavaScript file. You can also generate an ECMAScript module exporting `jQuery` as the default export using the `--esm` parameter:
```bash
grunt custom --esm
```
The default is `script` but you can also pass it explicitly via `--no-esm`:
```bash
grunt custom --no-esm
npm run build -- --filename=jquery.module.js --esm
```
#### Custom Build Examples
To create a custom build, first check out the version:
```bash
git pull; git checkout VERSION
```
Where VERSION is the version you want to customize. Then, make sure all Node dependencies are installed:
```bash
npm install
```
Create the custom build using the `grunt custom` option, listing the modules to be excluded.
Create a custom build using `npm run build`, listing the modules to be excluded. Excluding a top-level module also excludes its corresponding directory of modules.
Exclude all **ajax** functionality:
```bash
grunt custom:-ajax
npm run build -- --exclude=ajax
```
Excluding **css** removes modules depending on CSS: **effects**, **offset**, **dimensions**.
```bash
grunt custom:-css
npm run build -- --exclude=css
```
Exclude a bunch of modules:
Exclude a bunch of modules (`-e` is an alias for `--exclude`):
```bash
grunt custom:-ajax/jsonp,-css,-deprecated,-dimensions,-effects,-offset,-wrap
npm run build -- -e ajax/jsonp -e css -e deprecated -e dimensions -e effects -e offset -e wrap
```
There is also a special alias to generate a build with the same configuration as the official jQuery Slim build is generated:
There is a special alias to generate a build with the same configuration as the official jQuery Slim build:
```bash
grunt custom:slim
npm run build -- --filename=jquery.slim.js --slim
```
For questions or requests regarding custom builds, please start a thread on the [Developing jQuery Core](https://forum.jquery.com/developing-jquery-core) section of the forum. Due to the combinatorics and custom nature of these builds, they are not regularly tested in jQuery's unit test process.
Or, to create the slim build as an esm module:
```bash
npm run build -- --filename=jquery.slim.module.js --slim --esm
```
*Non-official custom builds are not regularly tested. Use them at your own risk.*
Running the Unit Tests
--------------------------------------
@ -197,10 +188,10 @@ Make sure you have the necessary dependencies:
npm install
```
Start `grunt watch` or `npm start` to auto-build jQuery as you work:
Start `npm start` to auto-build jQuery as you work:
```bash
grunt watch
npm start
```
@ -213,35 +204,6 @@ Run the unit tests with a local server that supports PHP. Ensure that you run th
Building to a different directory
---------------------------------
To copy the built jQuery files from `/dist` to another directory:
```bash
grunt && grunt dist:/path/to/special/location/
```
With this example, the output files would be:
```bash
/path/to/special/location/jquery.js
/path/to/special/location/jquery.min.js
```
To add a permanent copy destination, create a file in `dist/` called ".destination.json". Inside the file, paste and customize the following:
```json
{
"/Absolute/path/to/other/destination": true
}
```
Additionally, both methods can be combined.
Essential Git
-------------

75
build/command.js Executable file
View File

@ -0,0 +1,75 @@
"use strict";
const { build } = require( "./tasks/build" );
const yargs = require( "yargs/yargs" );
const slimExclude = require( "./tasks/lib/slim-exclude" );
const argv = yargs( process.argv.slice( 2 ) )
.version( false )
.command( {
command: "[options]",
describe: "Build a jQuery bundle"
} )
.option( "filename", {
alias: "f",
type: "string",
description:
"Set the filename of the built file. Defaults to jquery.js."
} )
.option( "dir", {
alias: "d",
type: "string",
description:
"Set the dir to which to output the built file. Defaults to /dist."
} )
.option( "version", {
alias: "v",
type: "string",
description:
"Set the version to include in the built file. " +
"Defaults to the version in package.json plus the " +
"short commit SHA and any excluded modules."
} )
.option( "watch", {
alias: "w",
type: "boolean",
description:
"Watch the source files and rebuild when they change."
} )
.option( "exclude", {
alias: "e",
type: "array",
description:
"Modules to exclude from the build. " +
"Specifying this option will cause the " +
"specified modules to be excluded from the build."
} )
.option( "include", {
alias: "i",
type: "array",
description:
"Modules to include in the build. " +
"Specifying this option will override the " +
"default included modules and only include these modules."
} )
.option( "esm", {
type: "boolean",
description:
"Build an ES module (ESM) bundle. " +
"By default, a UMD bundle is built."
} )
.option( "slim", {
alias: "s",
type: "boolean",
description: "Build a slim bundle, which excludes " +
slimExclude.join( ", " )
} )
.option( "amd", {
type: "string",
description:
"Set the name of the AMD module. Leave blank to make an anonymous module."
} )
.help()
.argv;
build( argv );

View File

@ -6,31 +6,80 @@
"use strict";
module.exports = function( grunt ) {
const fs = require( "fs" );
const path = require( "path" );
const util = require( "util" );
const exec = util.promisify( require( "child_process" ).exec );
const rollup = require( "rollup" );
const slimBuildFlags = require( "./lib/slim-build-flags" );
const excludedFromSlim = require( "./lib/slim-exclude" );
const rollupFileOverrides = require( "./lib/rollup-plugin-file-overrides" );
const srcFolder = path.resolve( `${ __dirname }/../../src` );
const read = function( fileName ) {
return grunt.file.read( `${ srcFolder }/${ fileName }` );
const pkg = require( "../../package.json" );
const isCleanWorkingDir = require( "./lib/isCleanWorkingDir" );
const minify = require( "./minify" );
const getTimestamp = require( "./lib/getTimestamp" );
const verifyNodeVersion = require( "./lib/verifyNodeVersion" );
const srcFolder = path.resolve( __dirname, "../../src" );
const minimum = [ "core" ];
// Exclude specified modules if the module matching the key is removed
const removeWith = {
ajax: [ "manipulation/_evalUrl", "deprecated/ajax-event-alias" ],
callbacks: [ "deferred" ],
css: [ "effects", "dimensions", "offset" ],
"css/showHide": [ "effects" ],
deferred: {
remove: [ "ajax", "effects", "queue", "core/ready" ],
include: [ "core/ready-no-deferred" ]
},
event: [ "deprecated/ajax-event-alias", "deprecated/event" ],
selector: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
};
const inputFileName = "jquery.js";
const inputRollupOptions = {
input: `${ srcFolder }/${ inputFileName }`
};
async function read( filename ) {
return fs.promises.readFile( path.join( srcFolder, filename ), "utf8" );
}
function getOutputRollupOptions( {
esm = false
} = {} ) {
// Remove the src folder and file extension
// and ensure unix-style path separators
function moduleName( filename ) {
return filename
.replace( `${srcFolder}${path.sep}`, "" )
.replace( /\.js$/, "" )
.split( path.sep )
.join( path.posix.sep );
}
async function readdirRecursive( dir, all = [] ) {
let files;
try {
files = await fs.promises.readdir( path.join( srcFolder, dir ), {
withFileTypes: true
} );
} catch ( e ) {
return all;
}
for ( const file of files ) {
const filepath = path.join( dir, file.name );
if ( file.isDirectory() ) {
all.push( ...( await readdirRecursive( filepath ) ) );
} else {
all.push( moduleName( filepath ) );
}
}
return all;
}
async function getOutputRollupOptions( { esm = false } = {} ) {
const wrapperFileName = `wrapper${esm ? "-esm" : ""}.js`;
const wrapperSource = await read( wrapperFileName );
// Catch `// @CODE` and subsequent comment lines event if they don't start
// in the first column.
const wrapper = read( wrapperFileName )
.split( /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/ );
const wrapper = wrapperSource.split(
/[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/
);
return {
@ -39,17 +88,13 @@ module.exports = function( grunt ) {
// generate any extra wrappers so there's nothing for us to remove.
format: "esm",
intro: `${ wrapper[ 0 ].replace( /\n*$/, "" ) }`,
intro: wrapper[ 0 ].replace( /\n*$/, "" ),
outro: wrapper[ 1 ].replace( /^\n*/, "" )
};
}
const fileOverrides = new Map();
function getOverride( filePath ) {
return fileOverrides.get( path.resolve( filePath ) );
}
function setOverride( filePath, source ) {
// We want normalized paths in overrides as they will be matched
@ -57,218 +102,162 @@ module.exports = function( grunt ) {
fileOverrides.set( path.resolve( filePath ), source );
}
grunt.registerMultiTask(
"build",
"Build jQuery ECMAScript modules, " +
"(include/exclude modules with +/- flags), embed date/version",
async function() {
const done = this.async();
try {
const flags = this.flags;
const optIn = flags[ "*" ];
let name = grunt.option( "filename" );
const esm = !!grunt.option( "esm" );
const distFolder = grunt.option( "dist-folder" );
const minimum = this.data.minimum;
const removeWith = this.data.removeWith;
const excluded = [];
const included = [];
let version = grunt.config( "pkg.version" );
// We'll skip printing the whole big exclusions for a bare `build:*:*:slim` which
// usually comes from `custom:slim`.
const isPureSlim = !!( flags.slim && flags[ "*" ] &&
Object.keys( flags ).length === 2 );
delete flags[ "*" ];
if ( flags.slim ) {
delete flags.slim;
for ( const flag of slimBuildFlags ) {
flags[ flag ] = true;
}
function unique( array ) {
return [ ...new Set( array ) ];
}
async function checkExclude( exclude, include ) {
const included = [ ...include ];
const excluded = [ ...exclude ];
/**
* Recursively calls the excluder to remove on all modules in the list
* @param {Array} list
* @param {String} [prepend] Prepend this to the module name.
* Indicates we're walking a directory
*/
const excludeList = ( list, prepend ) => {
if ( list ) {
prepend = prepend ? `${ prepend }/` : "";
list.forEach( function( module ) {
// Exclude var modules as well
if ( module === "var" ) {
excludeList(
fs.readdirSync( `${ srcFolder }/${ prepend }${ module }` ),
prepend + module
);
return;
}
if ( prepend ) {
// Skip if this is not a js file and we're walking files in a dir
if ( !( module = /([\w-\/]+)\.js$/.exec( module ) ) ) {
return;
for ( const module of exclude ) {
if ( minimum.indexOf( module ) !== -1 ) {
throw new Error( `Module \"${module}\" is a minimum requirement.` );
}
// Prepend folder name if passed
// Remove .js extension
module = prepend + module[ 1 ];
}
// Avoid infinite recursion
if ( excluded.indexOf( module ) === -1 ) {
excluder( "-" + module );
}
} );
}
};
/**
* Adds the specified module to the excluded or included list, depending on the flag
* @param {String} flag A module path relative to
* the src directory starting with + or - to indicate
* whether it should be included or excluded
*/
const excluder = flag => {
let additional;
const m = /^(\+|-|)([\w\/-]+)$/.exec( flag );
const exclude = m[ 1 ] === "-";
const module = m[ 2 ];
if ( exclude ) {
// Can't exclude certain modules
if ( minimum.indexOf( module ) === -1 ) {
// Add to excluded
if ( excluded.indexOf( module ) === -1 ) {
grunt.log.writeln( flag );
excluded.push( module );
// Exclude all files in the folder of the same name
// Exclude all files in the dir of the same name
// These are the removable dependencies
// It's fine if the directory is not there
try {
// `selector` is a special case as we don't just remove
// the module, but we replace it with `selector-native`
// which re-uses parts of the `src/selector` folder.
// which re-uses parts of the `src/selector` dir.
if ( module !== "selector" ) {
excludeList(
fs.readdirSync( `${ srcFolder }/${ module }` ),
module
);
const files = await readdirRecursive( module );
excluded.push( ...files );
}
} catch ( e ) {
grunt.verbose.writeln( e );
}
}
additional = removeWith[ module ];
// Check removeWith list
const additional = removeWith[ module ];
if ( additional ) {
excludeList( additional.remove || additional );
if ( additional.include ) {
included.push( ...additional.include );
grunt.log.writeln( "+" + additional.include );
const [ additionalExcluded, additionalIncluded ] = await checkExclude(
additional.remove || additional,
additional.include || []
);
excluded.push( ...additionalExcluded );
included.push( ...additionalIncluded );
}
}
} else {
grunt.log.error( "Module \"" + module + "\" is a minimum requirement." );
}
} else {
grunt.log.writeln( flag );
included.push( module );
}
};
// Filename can be passed to the command line using
// command line options
// e.g. grunt build --filename=jquery-custom.js
name = name ? `${ distFolder }/${ name }` : this.data.dest;
// append commit id to version
if ( process.env.COMMIT ) {
version += " " + process.env.COMMIT;
}
// figure out which files to exclude based on these rules in this order:
// dependency explicit exclude
// > explicit exclude
// > explicit include
// > dependency implicit exclude
// > implicit exclude
// examples:
// * none (implicit exclude)
// *:* all (implicit include)
// *:*:-css all except css and dependents (explicit > implicit)
// *:*:-css:+effects same (excludes effects because explicit include is
// trumped by explicit exclude of dependency)
// *:+effects none except effects and its dependencies
// (explicit include trumps implicit exclude of dependency)
for ( const flag in flags ) {
excluder( flag );
return [ unique( excluded ), unique( included ) ];
}
// Remove the jQuery export from the entry file, we'll use our own
// custom wrapper.
setOverride( inputRollupOptions.input,
read( inputFileName ).replace( /\n*export \{ jQuery, jQuery as \$ };\n*/, "\n" ) );
async function writeCompiled( { code, dir, filename, version } ) {
const compiledContents = code
// Embed Version
.replace( /@VERSION/g, version )
// Embed Date
// yyyy-mm-ddThh:mmZ
.replace( /@DATE/g, new Date().toISOString().replace( /:\d+\.\d+Z$/, "Z" ) );
await fs.promises.writeFile( path.join( dir, filename ), compiledContents );
console.log( `[${getTimestamp()}] ${filename} v${version} created.` );
}
// Build jQuery ECMAScript modules
async function build( {
amd,
dir = "dist",
exclude = [],
filename = "jquery.js",
include = [],
esm = false,
slim = false,
version,
watch = false
} = {} ) {
const pureSlim = slim && !exclude.length && !include.length;
// Add the short commit hash to the version string
// when the version is not for a release.
if ( !version ) {
const { stdout } = await exec( "git rev-parse --short HEAD" );
const isClean = await isCleanWorkingDir();
// "+[slim.]SHA" is semantically correct
// Add ".dirty" as well if the working dir is not clean
version = `${pkg.version}+${slim ? "slim." : ""}${stdout.trim()}${isClean ? "" : ".dirty"}`;
} else if ( slim ) {
version += "+slim";
}
await fs.promises.mkdir( dir, { recursive: true } );
// Exclude slim modules when slim is true
const [ excluded, included ] = await checkExclude(
slim ? exclude.concat( excludedFromSlim ) : exclude,
include
);
// Replace exports/global with a noop noConflict
if ( excluded.includes( "exports/global" ) ) {
const index = excluded.indexOf( "exports/global" );
setOverride( `${ srcFolder }/exports/global.js`,
"import jQuery from \"../core.js\";\n\n" +
"jQuery.noConflict = function() {};" );
setOverride(
`${srcFolder}/exports/global.js`,
"import { jQuery } from \"../core.js\";\n\n" +
"jQuery.noConflict = function() {};"
);
excluded.splice( index, 1 );
}
// Set a desired AMD name.
let amdName = grunt.option( "amd" );
if ( amdName != null ) {
if ( amdName ) {
grunt.log.writeln( "Naming jQuery with AMD name: " + amdName );
if ( amd != null ) {
if ( amd ) {
console.log( "Naming jQuery with AMD name: " + amd );
} else {
grunt.log.writeln( "AMD name now anonymous" );
console.log( "AMD name now anonymous" );
}
// Replace the AMD name in the AMD export
// No name means an anonymous define
const amdExportContents = await read( "exports/amd.js" );
setOverride(
`${srcFolder}/exports/amd.js`,
amdExportContents.replace(
// Remove the comma for anonymous defines
setOverride( `${ srcFolder }/exports/amd.js`,
read( "exports/amd.js" )
.replace( /(\s*)"jquery"(,\s*)/,
amdName ? "$1\"" + amdName + "\"$2" : "" ) );
/(\s*)"jquery"(,\s*)/,
amd ? `$1\"${amd}\"$2` : " "
)
);
}
grunt.verbose.writeflags( excluded, "Excluded" );
grunt.verbose.writeflags( included, "Included" );
// Indicate a Slim build without listing all the exclusions
// to save space.
if ( isPureSlim ) {
version += " slim";
// Append excluded modules to version.
} else if ( excluded.length ) {
// Skip adding exclusions for slim builds.
// Don't worry about semver syntax for these.
if ( !pureSlim && excluded.length ) {
version += " -" + excluded.join( ",-" );
}
if ( excluded.length ) {
// Append extra included modules to version.
if ( !pureSlim && included.length ) {
version += " +" + included.join( ",+" );
}
// Set pkg.version to version with excludes or with the "slim" marker,
// so minified file picks it up but skip the commit hash the same way
// it's done for the full build.
const commitlessVersion = version.replace( " " + process.env.COMMIT, "" );
grunt.config.set( "pkg.version", commitlessVersion );
grunt.verbose.writeln( "Version changed to " + commitlessVersion );
const inputOptions = {
input: `${srcFolder}/jquery.js`
};
const includedImports = included
.map( ( module ) => `import "./${module}.js";` )
.join( "\n" );
const jQueryFileContents = await read( "jquery.js" );
if ( include.length ) {
// If include is specified, only add those modules.
setOverride( inputOptions.input, includedImports );
} else {
// Remove the jQuery export from the entry file, we'll use our own
// custom wrapper.
setOverride(
inputOptions.input,
jQueryFileContents.replace( /\n*export \{ jQuery, jQuery as \$ };\n*/, "\n" ) +
includedImports
);
}
// Replace excluded modules with empty sources.
for ( const module of excluded ) {
@ -277,79 +266,96 @@ module.exports = function( grunt ) {
// The `selector` module is not removed, but replaced
// with `selector-native`.
module === "selector" ? read( "selector-native.js" ) : ""
module === "selector" ? await read( "selector-native.js" ) : ""
);
}
}
// Turn off opt-in if necessary
if ( !optIn ) {
// Remove the default inclusions, they will be overwritten with the explicitly
// included ones.
setOverride( inputRollupOptions.input, "" );
}
// Import the explicitly included modules.
if ( included.length ) {
setOverride( inputRollupOptions.input,
getOverride( inputRollupOptions.input ) + included
.map( module => `import "./${module}.js";` )
.join( "\n" ) );
}
const bundle = await rollup.rollup( {
...inputRollupOptions,
...inputOptions,
plugins: [ rollupFileOverrides( fileOverrides ) ]
} );
const outputRollupOptions =
getOutputRollupOptions( { esm } );
const outputOptions = await getOutputRollupOptions( { esm } );
const { output: [ { code } ] } = await bundle.generate( outputRollupOptions );
const compiledContents = code
// Embed Version
.replace( /@VERSION/g, version )
// Embed Date
// yyyy-mm-ddThh:mmZ
.replace(
/@DATE/g,
( new Date() ).toISOString()
.replace( /:\d+\.\d+Z$/, "Z" )
);
grunt.file.write( name, compiledContents );
grunt.log.ok( `File '${ name }' created.` );
done();
} catch ( err ) {
done( err );
if ( watch ) {
const watcher = rollup.watch( {
...inputOptions,
output: [ outputOptions ],
plugins: [ rollupFileOverrides( fileOverrides ) ],
watch: {
include: `${srcFolder}/**`,
skipWrite: true
}
} );
// Special "alias" task to make custom build creation less grawlix-y
// Translation example
//
// grunt custom:+ajax,-dimensions,-effects,-offset
//
// Becomes:
//
// grunt build:*:*:+ajax:-dimensions:-effects:-offset
//
// There's also a special "slim" alias that resolves to the jQuery Slim build
// configuration:
//
// grunt custom:slim
grunt.registerTask( "custom", function() {
const args = this.args;
const modules = args.length ?
args[ 0 ].split( "," ).join( ":" ) :
"";
watcher.on( "event", async( event ) => {
switch ( event.code ) {
case "ERROR":
console.error( event.error );
break;
case "BUNDLE_END":
const {
output: [ { code } ]
} = await event.result.generate( outputOptions );
grunt.log.writeln( "Creating custom build...\n" );
grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "minify", "dist" ] );
await writeCompiled( {
code,
dir,
filename,
version
} );
};
await minify( { dir, filename, esm } );
break;
}
} );
return watcher;
} else {
const {
output: [ { code } ]
} = await bundle.generate( outputOptions );
await writeCompiled( { code, dir, filename, version } );
await minify( { dir, filename, esm } );
}
}
async function buildDefaultFiles( { version, watch } = {} ) {
await Promise.all( [
build( { version, watch } ),
build( { filename: "jquery.slim.js", slim: true, version, watch } ),
build( {
dir: "dist-module",
filename: "jquery.module.js",
esm: true,
version,
watch
} ),
build( {
dir: "dist-module",
filename: "jquery.slim.module.js",
esm: true,
slim: true,
version,
watch
} )
] );
// Earlier Node.js versions do not support the ESM format.
if ( !verifyNodeVersion() ) {
return;
}
const { compareSize } = await import( "./compare_size.mjs" );
return compareSize( {
files: [
"dist/jquery.min.js",
"dist/jquery.slim.min.js",
"dist-module/jquery.module.min.js",
"dist-module/jquery.slim.module.min.js"
]
} );
}
module.exports = { build, buildDefaultFiles };

View File

@ -0,0 +1,128 @@
import chalk from "chalk";
import fs from "node:fs";
import { promisify } from "node:util";
import zlib from "node:zlib";
import { exec as nodeExec } from "node:child_process";
import isCleanWorkingDir from "./lib/isCleanWorkingDir.js";
const gzip = promisify( zlib.gzip );
const exec = promisify( nodeExec );
async function getBranchName() {
const { stdout } = await exec( "git rev-parse --abbrev-ref HEAD" );
return stdout.trim();
}
async function getCache( loc ) {
try {
const contents = await fs.promises.readFile( loc, "utf8" );
return JSON.parse( contents );
} catch ( err ) {
return {};
}
}
function saveCache( loc, cache ) {
return fs.promises.writeFile( loc, JSON.stringify( cache ) );
}
function compareSizes( existing, current, padLength ) {
if ( typeof current !== "number" ) {
return chalk.grey( `${existing}`.padStart( padLength ) );
}
const delta = current - existing;
if ( delta > 0 ) {
return chalk.red( `+${delta}`.padStart( padLength ) );
}
return chalk.green( `${delta}`.padStart( padLength ) );
}
export async function compareSize( { cache = ".sizecache.json", files } = {} ) {
if ( !files || !files.length ) {
throw new Error( "No files specified" );
}
const branch = await getBranchName();
const sizeCache = await getCache( cache );
let rawPadLength = 0;
let gzPadLength = 0;
const results = await Promise.all(
files.map( async function( filename ) {
let contents = await fs.promises.readFile( filename, "utf8" );
// Remove the banner for size comparisons.
// The version string can vary widely by short SHA.
contents = contents.replace( /\/\*\! jQuery[^\n]+/, "" );
const size = Buffer.byteLength( contents, "utf8" );
const gzippedSize = ( await gzip( contents ) ).length;
// Add one to give space for the `+` or `-` in the comparison
rawPadLength = Math.max( rawPadLength, size.toString().length + 1 );
gzPadLength = Math.max( gzPadLength, gzippedSize.toString().length + 1 );
return { filename, raw: size, gz: gzippedSize };
} )
);
const header = "raw".padStart( rawPadLength ) +
"gz".padStart( gzPadLength + 1 ) +
" Filename";
const sizes = results.map( function( result ) {
const rawSize = result.raw.toString().padStart( rawPadLength );
const gzSize = result.gz.toString().padStart( gzPadLength );
return `${rawSize} ${gzSize} ${result.filename}`;
} );
const comparisons = Object.keys( sizeCache ).map( function( branch ) {
const branchSizes = Object.keys( sizeCache[ branch ] ).map( function( filename ) {
const branchResult = sizeCache[ branch ][ filename ];
const compareResult = results.find( function( result ) {
return result.filename === filename;
} ) || {};
const compareRaw = compareSizes( branchResult.raw, compareResult.raw, rawPadLength );
const compareGz = compareSizes( branchResult.gz, compareResult.gz, gzPadLength );
return `${compareRaw} ${compareGz} ${filename}`;
} );
return [
"", // New line before each branch
chalk.bold( branch ),
header,
...branchSizes
].join( "\n" );
} );
const output = [
"", // Opening new line
chalk.bold( "Sizes" ),
header,
...sizes,
...comparisons,
"" // Closing new line
].join( "\n" );
console.log( output );
// Only save cache for the current branch
// if the working directory is clean.
if ( await isCleanWorkingDir() ) {
sizeCache[ branch ] = {};
results.forEach( function( result ) {
sizeCache[ branch ][ result.filename ] = {
raw: result.raw,
gz: result.gz
};
} );
await saveCache( cache, sizeCache );
console.log( `Saved cache for ${branch}.` );
}
return results;
}

View File

@ -1,72 +1,31 @@
"use strict";
module.exports = function( grunt ) {
const fs = require( "fs" );
const filename = grunt.option( "filename" );
const distFolder = grunt.option( "dist-folder" );
const distPaths = [
`${ distFolder }/${ filename }`,
`${ distFolder }/${ filename.replace( ".js", ".min.js" ) }`,
`${ distFolder }/${ filename.replace( ".js", ".min.map" ) }`
];
// Process files for distribution.
module.exports = async function processForDist( text, filename ) {
if ( !text ) {
throw new Error( "text required for processForDist" );
}
// Process files for distribution
grunt.registerTask( "dist", function() {
let stored, flags, paths, nonascii;
// Check for stored destination paths
// ( set in dist/.destination.json )
stored = Object.keys( grunt.config( "dst" ) );
// Allow command line input as well
flags = Object.keys( this.flags );
// Combine all output target paths
paths = [].concat( stored, flags ).filter( function( path ) {
return path !== "*";
} );
// Ensure the dist files are pure ASCII
nonascii = false;
distPaths.forEach( function( filename ) {
let i, c;
const text = fs.readFileSync( filename, "utf8" );
if ( !filename ) {
throw new Error( "filename required for processForDist" );
}
// Ensure files use only \n for line endings, not \r\n
if ( /\x0d\x0a/.test( text ) ) {
grunt.log.writeln( filename + ": Incorrect line endings (\\r\\n)" );
nonascii = true;
throw new Error( filename + ": Incorrect line endings (\\r\\n)" );
}
// Ensure only ASCII chars so script tags don't need a charset attribute
if ( text.length !== Buffer.byteLength( text, "utf8" ) ) {
grunt.log.writeln( filename + ": Non-ASCII characters detected:" );
for ( i = 0; i < text.length; i++ ) {
c = text.charCodeAt( i );
let message = filename + ": Non-ASCII characters detected:\n";
for ( let i = 0; i < text.length; i++ ) {
const c = text.charCodeAt( i );
if ( c > 127 ) {
grunt.log.writeln( "- position " + i + ": " + c );
grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) );
message += "- position " + i + ": " + c + "\n";
message += "==> " + text.substring( i - 20, i + 20 );
break;
}
}
nonascii = true;
throw new Error( message );
}
// Optionally copy dist files to other locations
paths.forEach( function( path ) {
let created;
if ( !/\/$/.test( path ) ) {
path += "/";
}
created = path + filename.replace( "dist/", "" );
grunt.file.write( created, text );
grunt.log.writeln( "File '" + created + "' created." );
} );
} );
return !nonascii;
} );
};

View File

@ -0,0 +1,9 @@
"use strict";
module.exports = function getTimestamp() {
const now = new Date();
const hours = now.getHours().toString().padStart( 2, "0" );
const minutes = now.getMinutes().toString().padStart( 2, "0" );
const seconds = now.getSeconds().toString().padStart( 2, "0" );
return `${hours}:${minutes}:${seconds}`;
};

View File

@ -0,0 +1,9 @@
"use strict";
const util = require( "util" );
const exec = util.promisify( require( "child_process" ).exec );
module.exports = async function isCleanWorkingDir() {
const { stdout } = await exec( "git status --untracked-files=no --porcelain" );
return !stdout.trim();
};

View File

@ -2,9 +2,9 @@
// NOTE: keep it in sync with test/data/testinit.js
module.exports = [
"-ajax",
"-callbacks",
"-deferred",
"-effects",
"-queue"
"ajax",
"callbacks",
"deferred",
"effects",
"queue"
];

View File

@ -1,16 +0,0 @@
"use strict";
// Run Node with provided parameters: the first one being the Grunt
// done function and latter ones being files to be tested.
// See the comment in ../node_smoke_tests.js for more information.
module.exports = function spawnTest( done, command ) {
var spawn = require( "child_process" ).spawn;
spawn( command, {
stdio: "inherit",
shell: true
} )
.on( "close", function( code ) {
done( code === 0 );
} );
};

View File

@ -0,0 +1,12 @@
"use strict";
const { version } = require( "process" );
const nodeV16OrNewer = !/^v1[0-5]\./.test( version );
module.exports = function verifyNodeVersion() {
if ( !nodeV16OrNewer ) {
console.log( "Old Node.js detected, task skipped..." );
return false;
}
return true;
};

View File

@ -1,57 +1,67 @@
/**
* Minify JavaScript using SWC.
*/
"use strict";
module.exports = ( grunt ) => {
const swc = require( "@swc/core" );
const fs = require( "fs" );
const path = require( "path" );
const processForDist = require( "./dist" );
const getTimestamp = require( "./lib/getTimestamp" );
grunt.registerMultiTask(
"minify",
"Minify JavaScript using SWC",
async function() {
const done = this.async();
const options = this.options();
const sourceMapFilename = options.sourceMap && options.sourceMap.filename;
const sourceMapOverrides = options.sourceMap && options.sourceMap.overrides || {};
const rjs = /\.js$/;
await Promise.all( this.files.map( async( { src, dest } ) => {
if ( src.length !== 1 ) {
grunt.fatal( "The minify task requires a single source per destination" );
}
module.exports = async function minify( { filename, dir, esm } ) {
const contents = await fs.promises.readFile( path.join( dir, filename ), "utf8" );
const version = /jQuery JavaScript Library ([^\n]+)/.exec( contents )[ 1 ];
const { code, map: incompleteMap } = await swc.minify(
grunt.file.read( src[ 0 ] ),
contents,
{
...options.swc,
compress: {
ecma: esm ? 2015 : 5,
hoist_funs: false,
loops: false
},
format: {
ecma: esm ? 2015 : 5,
asciiOnly: true,
comments: false,
preamble: `/*! jQuery ${version}` +
" | (c) OpenJS Foundation and other contributors" +
" | jquery.org/license */\n"
},
mangle: true,
inlineSourcesContent: false,
sourceMap: sourceMapFilename ?
{
filename: sourceMapFilename
} :
false
sourceMap: true
}
);
// Can't seem to get SWC to not use CRLF on Windows, so replace them with LF.
grunt.file.write( dest, code.replace( /\r\n/g, "\n" ) );
const minFilename = filename.replace( rjs, ".min.js" );
const mapFilename = filename.replace( rjs, ".min.map" );
if ( sourceMapFilename ) {
// Apply map overrides if needed. See the task config description
// for more details.
const mapObject = {
// The map's `files` & `sources` property are set incorrectly, fix
// them via overrides from the task config.
// See https://github.com/swc-project/swc/issues/7588#issuecomment-1624345254
const map = JSON.stringify( {
...JSON.parse( incompleteMap ),
...sourceMapOverrides
};
const map = JSON.stringify( mapObject );
file: minFilename,
sources: [ filename ]
} );
grunt.file.write( sourceMapFilename, map );
}
} ) );
await Promise.all( [
fs.promises.writeFile(
path.join( dir, minFilename ),
code
),
fs.promises.writeFile(
path.join( dir, mapFilename ),
map
)
] );
done();
}
);
// Always process files for dist
// Doing it here avoids extra file reads
processForDist( contents, filename );
processForDist( code, minFilename );
processForDist( map, mapFilename );
console.log( `[${getTimestamp()}] ${minFilename} ${version} with ${mapFilename} created.` );
};

View File

@ -1,51 +1,50 @@
"use strict";
module.exports = ( grunt ) => {
const fs = require( "fs" );
const spawnTest = require( "./lib/spawn_test.js" );
const nodeV16OrNewer = !/^v1[0-5]\./.test( process.version );
const util = require( "util" );
const exec = util.promisify( require( "child_process" ).exec );
const verifyNodeVersion = require( "./lib/verifyNodeVersion" );
grunt.registerTask( "node_smoke_tests", function( moduleType, jQueryModuleSpecifier ) {
if (
( moduleType !== "commonjs" && moduleType !== "module" ) ||
!jQueryModuleSpecifier
) {
grunt.fatal( "Use `node_smoke_tests:commonjs:JQUERY` " +
"or `node_smoke_tests:module:JQUERY.\n" +
"JQUERY can be `jquery`, `jquery/slim` or a path to any of them." );
}
const allowedModules = [ "commonjs", "module" ];
if ( !nodeV16OrNewer ) {
grunt.log.writeln( "Old Node.js detected, running the task " +
`"node_smoke_tests:${ moduleType }:${ jQueryModuleSpecifier }" skipped...` );
if ( !verifyNodeVersion() ) {
return;
}
const testsDir = `./test/node_smoke_tests/${ moduleType }`;
const nodeSmokeTests = [];
// Fire up all tests defined in test/node_smoke_tests/*.js in spawned sub-processes.
// All the files under test/node_smoke_tests/*.js are supposed to exit with 0 code
// on success or another one on failure. Spawning in sub-processes is
// important so that the tests & the main process don't interfere with
// each other, e.g. so that they don't share the `require` cache.
fs.readdirSync( testsDir )
.filter( ( testFilePath ) =>
fs.statSync( `${ testsDir }/${ testFilePath }` ).isFile() &&
/\.[cm]?js$/.test( testFilePath )
async function runTests( sourceType, module ) {
if ( !allowedModules.includes( sourceType ) ) {
throw new Error(
`Usage: \`node_smoke_tests [${allowedModules.join( "|" )}]:JQUERY\``
);
}
const dir = `./test/node_smoke_tests/${sourceType}`;
const files = await fs.promises.readdir( dir, { withFileTypes: true } );
const testFiles = files.filter( ( testFilePath ) => testFilePath.isFile() );
await Promise.all(
testFiles.map( ( testFile ) =>
exec( `node "${dir}/${testFile.name}" "${module}"` )
)
.forEach( ( testFilePath ) => {
const taskName = `node_${ testFilePath.replace( /\.[cm]?js$/, "" ) }:${ moduleType }:${ jQueryModuleSpecifier }`;
);
console.log( `Node smoke tests passed for ${sourceType} "${module}".` );
}
grunt.registerTask( taskName, function() {
spawnTest( this.async(), `node "${ testsDir }/${
testFilePath }" ${ jQueryModuleSpecifier }` );
} );
async function runDefaultTests() {
await Promise.all( [
runTests( "commonjs", "jquery" ),
runTests( "commonjs", "jquery/slim" ),
runTests( "commonjs", "./dist/jquery.js" ),
runTests( "commonjs", "./dist/jquery.slim.js" ),
runTests( "module", "jquery" ),
runTests( "module", "jquery/slim" ),
runTests( "module", "./dist-module/jquery.module.js" ),
runTests( "module", "./dist-module/jquery.slim.module.js" )
] );
}
nodeSmokeTests.push( taskName );
} );
grunt.task.run( nodeSmokeTests );
} );
};
runDefaultTests();

42
build/tasks/npmcopy.js Normal file
View File

@ -0,0 +1,42 @@
"use strict";
const fs = require( "fs" );
const path = require( "path" );
const projectDir = path.resolve( __dirname, "..", ".." );
const files = {
"bootstrap/bootstrap.css": "bootstrap/dist/css/bootstrap.css",
"bootstrap/bootstrap.min.css": "bootstrap/dist/css/bootstrap.min.css",
"bootstrap/bootstrap.min.css.map": "bootstrap/dist/css/bootstrap.min.css.map",
"core-js-bundle/core-js-bundle.js": "core-js-bundle/minified.js",
"core-js-bundle/LICENSE": "core-js-bundle/LICENSE",
"npo/npo.js": "native-promise-only/lib/npo.src.js",
"qunit/qunit.js": "qunit/qunit/qunit.js",
"qunit/qunit.css": "qunit/qunit/qunit.css",
"qunit/LICENSE.txt": "qunit/LICENSE.txt",
"requirejs/require.js": "requirejs/require.js",
"sinon/sinon.js": "sinon/pkg/sinon.js",
"sinon/LICENSE.txt": "sinon/LICENSE"
};
async function npmcopy() {
await fs.promises.mkdir( path.resolve( projectDir, "external" ), {
recursive: true
} );
for ( const [ dest, source ] of Object.entries( files ) ) {
const from = path.resolve( projectDir, "node_modules", source );
const to = path.resolve( projectDir, "external", dest );
const toDir = path.dirname( to );
await fs.promises.mkdir( toDir, { recursive: true } );
await fs.promises.copyFile( from, to );
console.log( `${source}${dest}` );
}
}
npmcopy();

View File

@ -1,27 +1,32 @@
"use strict";
module.exports = grunt => {
const timeout = 2000;
const spawnTest = require( "./lib/spawn_test.js" );
const { spawn } = require( "child_process" );
const verifyNodeVersion = require( "./lib/verifyNodeVersion" );
const path = require( "path" );
const os = require( "os" );
grunt.registerTask( "promises_aplus_tests",
[ "promises_aplus_tests:deferred", "promises_aplus_tests:when" ] );
if ( !verifyNodeVersion() ) {
return;
}
grunt.registerTask( "promises_aplus_tests:deferred", function() {
spawnTest( this.async(),
"\"" + __dirname + "/../../node_modules/.bin/promises-aplus-tests\"" +
" test/promises_aplus_adapters/deferred.cjs" +
" --reporter dot" +
" --timeout " + timeout
const command = path.resolve(
__dirname,
`../../node_modules/.bin/promises-aplus-tests${os.platform() === "win32" ? ".cmd" : ""}`
);
const args = [ "--reporter", "dot", "--timeout", "2000" ];
const tests = [
"test/promises_aplus_adapters/deferred.cjs",
"test/promises_aplus_adapters/when.cjs"
];
async function runTests() {
tests.forEach( ( test ) => {
spawn(
command,
[ test ].concat( args ),
{ stdio: "inherit" }
);
} );
}
grunt.registerTask( "promises_aplus_tests:when", function() {
spawnTest( this.async(),
"\"" + __dirname + "/../../node_modules/.bin/promises-aplus-tests\"" +
" test/promises_aplus_adapters/when.cjs" +
" --reporter dot" +
" --timeout " + timeout
);
} );
};
runTests();

View File

@ -0,0 +1,17 @@
"use strict";
const fs = require( "fs" );
async function generateFixture() {
const fixture = await fs.promises.readFile( "./test/data/qunit-fixture.html", "utf8" );
await fs.promises.writeFile(
"./test/data/qunit-fixture.js",
"// Generated by build/tasks/qunit-fixture.js\n" +
"QUnit.config.fixture = " +
JSON.stringify( fixture.replace( /\r\n/g, "\n" ) ) +
";\n"
);
console.log( "Updated ./test/data/qunit-fixture.js" );
}
generateFixture();

View File

@ -1,22 +0,0 @@
"use strict";
var fs = require( "fs" );
module.exports = function( grunt ) {
grunt.registerTask( "qunit_fixture", function() {
var dest = "./test/data/qunit-fixture.js";
fs.writeFileSync(
dest,
"// Generated by build/tasks/qunit_fixture.js\n" +
"QUnit.config.fixture = " +
JSON.stringify(
fs.readFileSync(
"./test/data/qunit-fixture.html",
"utf8"
).toString().replace( /\r\n/g, "\n" )
) +
";\n"
);
grunt.log.ok( "Updated " + dest + "." );
} );
};

View File

@ -1,45 +0,0 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
},
"globals": {
"define": false,
"Symbol": false
},
"overrides": [
{
"files": "jquery{,.slim}.module.min.js",
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
}
},
{
"files": "jquery{,.slim}.module.js",
"extends": "../.eslintrc-browser.json",
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
},
"rules": {
// That is okay for the built version
"no-multiple-empty-lines": "off",
// When custom compilation is used, the version string
// can get large. Accept that in the built version.
"max-len": "off",
"one-var": "off"
}
}
]
}

41
dist/.eslintrc.json vendored
View File

@ -1,41 +0,0 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 5,
"sourceType": "script"
},
"globals": {
"define": false,
"module": true,
"Symbol": false
},
"overrides": [
{
"files": "jquery{,.slim}.min.js",
"parserOptions": {
"ecmaVersion": 5,
"sourceType": "script"
}
},
{
"files": "jquery{,.slim}.js",
"extends": "../.eslintrc-browser.json",
"rules": {
// That is okay for the built version
"no-multiple-empty-lines": "off",
// When custom compilation is used, the version string
// can get large. Accept that in the built version.
"max-len": "off",
"one-var": "off"
}
}
]
}

305
eslint.config.js Normal file
View File

@ -0,0 +1,305 @@
import jqueryConfig from "eslint-config-jquery";
import importPlugin from "eslint-plugin-import";
import globals from "globals";
export default [
{
// Only global ignores will bypass the parser
// and avoid JS parsing errors
// See https://github.com/eslint/eslint/discussions/17412
ignores: [
"external",
"test/data/json_obj.js"
]
},
{
files: [
"eslint.config.js",
"Gruntfile.cjs",
"test/node_smoke_tests/commonjs/**",
"test/node_smoke_tests/module/**",
"test/promises_aplus_adapters/**",
"test/middleware-mockserver.cjs"
],
languageOptions: {
globals: {
...globals.node
}
},
rules: {
...jqueryConfig.rules,
strict: [ "error", "global" ]
}
},
// Source
{
files: [ "src/**" ],
plugins: {
import: importPlugin
},
languageOptions: {
// The browser env is not enabled on purpose so that code takes
// all browser-only globals from window instead of assuming
// they're available as globals. This makes it possible to use
// jQuery with tools like jsdom which provide a custom window
// implementation.
globals: {
window: false
}
},
rules: {
...jqueryConfig.rules,
"import/extensions": [ "error", "always" ],
"import/no-cycle": "error",
// TODO: Enable this rule when eslint-plugin-import supports
// it when using flat config.
// See https://github.com/import-js/eslint-plugin-import/issues/2556
// "import/no-unused-modules": [
// "error",
// {
// unusedExports: true,
// // When run via WebStorm, the root path against which these paths
// // are resolved is the path where this ESLint config file lies,
// // i.e. `src`. When run via the command line, it's usually the root
// // folder of the jQuery repository. This pattern intends to catch both.
// // Note that we cannot specify two patterns here:
// // [ "src/*.js", "*.js" ]
// // as they're analyzed individually and the rule crashes if a pattern
// // cannot be matched.
// ignoreExports: [ "{src/,}*.js" ]
// }
// ],
indent: [
"error",
"tab",
{
outerIIFEBody: 0
}
],
"one-var": [ "error", { var: "always" } ],
strict: [ "error", "function" ]
}
},
{
files: [ "src/wrapper.js" ],
languageOptions: {
sourceType: "script",
globals: {
jQuery: false,
module: true
}
},
rules: {
"no-unused-vars": "off",
indent: [
"error",
"tab",
{
// This makes it so code within the wrapper is not indented.
ignoredNodes: [
"Program > ExpressionStatement > CallExpression > :last-child > *"
]
}
]
}
},
{
files: [ "src/wrapper-esm.js" ],
languageOptions: {
globals: {
jQuery: false
}
},
rules: {
"no-unused-vars": "off",
indent: [
"error",
"tab",
{
// This makes it so code within the wrapper is not indented.
ignoredNodes: [
"Program > FunctionDeclaration > *"
]
}
]
}
},
{
files: [ "src/exports/amd.js" ],
languageOptions: {
globals: {
define: false
}
}
},
// Tests
{
files: [
"test/**"
],
ignores: [
"test/data/jquery-1.9.1.js",
"test/data/badcall.js",
"test/data/badjson.js",
"test/data/support/csp.js",
"test/data/support/getComputedSupport.js",
"test/data/core/jquery-iterability-transpiled.js"
],
languageOptions: {
globals: {
...globals.browser,
require: false,
Promise: false,
Symbol: false,
trustedTypes: false,
QUnit: false,
ajaxTest: false,
testIframe: false,
createDashboardXML: false,
createWithFriesXML: false,
createXMLFragment: false,
includesModule: false,
moduleTeardown: false,
url: false,
q: false,
jQuery: true,
sinon: true,
amdDefined: true,
fireNative: true,
Globals: true,
hasPHP: true,
isLocal: true,
supportjQuery: true,
originaljQuery: true,
$: true,
original$: true,
baseURL: true,
externalHost: true
}
},
rules: {
...jqueryConfig.rules,
strict: [ "error", "function" ],
// See https://github.com/eslint/eslint/issues/2342
"no-unused-vars": "off",
// Too many errors
"max-len": "off",
camelcase: "off",
"one-var": "off"
}
},
{
files: [
"test/data/testrunner.js",
"test/data/core/jquery-iterability-transpiled-es6.js"
],
languageOptions: {
sourceType: "script"
}
},
{
files: [
"test/unit/deferred.js"
],
rules: {
// Deferred tests set strict mode for certain tests
strict: "off"
}
},
{
files: [
"test/node_smoke_tests/commonjs/**",
"test/node_smoke_tests/module/**",
"test/promises_aplus_adapters/**",
"test/middleware-mockserver.cjs"
],
languageOptions: {
globals: {
...globals.node,
...globals.es2021
}
},
rules: {
strict: [ "error", "global" ]
}
},
{
files: [
"build/**",
"test/data/testinit.js",
"test/data/testinit-jsdom.js"
],
languageOptions: {
globals: {
...globals.node,
...globals.es2021
}
},
rules: {
...jqueryConfig.rules,
strict: [ "error", "global" ]
}
},
{
files: [
"build/**/*.js",
"test/data/testinit.js",
"test/data/testinit-jsdom.js"
],
languageOptions: {
sourceType: "commonjs"
}
},
{
files: [
"dist/jquery.js",
"dist/jquery.slim.js",
"dist-module/jquery.module.js",
"dist-module/jquery.slim.module.js"
],
languageOptions: {
globals: {
...globals.browser,
...globals.es2021,
define: false,
module: false,
Symbol: false
}
},
rules: {
...jqueryConfig.rules,
// That is okay for the built version
"no-multiple-empty-lines": "off",
// When custom compilation is used, the version string
// can get large. Accept that in the built version.
"max-len": "off",
"one-var": "off"
}
}
];

8188
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,31 @@
"./src/*.js": "./src/*.js"
},
"main": "dist/jquery.js",
"scripts": {
"babel:tests": "babel test/data/core/jquery-iterability-transpiled-es6.js --out-file test/data/core/jquery-iterability-transpiled.js",
"build": "node ./build/command.js",
"build:all": "node -e \"require('./build/tasks/build.js').buildDefaultFiles()\"",
"build:main": "node -e \"require('./build/tasks/build.js').build()\"",
"jenkins": "npm run test:browserless",
"lint:dev": "eslint --cache .",
"lint:json": "jsonlint --quiet package.json",
"lint": "concurrently -r \"npm:lint:dev\" \"npm:lint:json\"",
"npmcopy": "node build/tasks/npmcopy.js",
"prepare": "husky install",
"pretest": "npm run qunit-fixture && npm run babel:tests && npm run npmcopy",
"qunit-fixture": "node build/tasks/qunit-fixture.js",
"start": "node -e \"require('./build/tasks/build.js').buildDefaultFiles({ watch: true })\"",
"test:browserless": "npm run test:node_smoke_tests && npm run test:promises_aplus && npm run test:jsdom",
"test:browser": "npm run build:all && grunt karma:main",
"test:esmodules": "npm run build:main && grunt karma:esmodules",
"test:jsdom": "npm run build:main && grunt test:jsdom",
"test:no-deprecated": "npm run build -- -e deprecated && grunt karma:main",
"test:selector-native": "npm run build -- -e selector && grunt karma:main",
"test:slim": "npm run build -- --slim && grunt karma:main",
"test:node_smoke_tests": "npm run build:all && node build/tasks/node_smoke_tests.js",
"test:promises_aplus": "npm run build:main && node build/tasks/promises_aplus_tests.js",
"test": "npm run build:all && npm run lint && npm run test:browserless && npm run test:browser && npm run test:esmodules && npm run test:slim && npm run test:no-deprecated && npm run test:selector-native"
},
"homepage": "https://jquery.com",
"author": {
"name": "OpenJS Foundation and other contributors",
@ -50,28 +75,27 @@
},
"license": "MIT",
"devDependencies": {
"@babel/cli": "7.22.9",
"@babel/core": "7.10.5",
"@babel/plugin-transform-for-of": "7.10.4",
"@swc/core": "1.3.66",
"@prantlf/jsonlint": "14.0.3",
"@swc/core": "1.3.78",
"bootstrap": "5.3.0",
"chalk": "5.3.0",
"colors": "1.4.0",
"commitplease": "3.2.0",
"concurrently": "8.2.0",
"core-js-bundle": "3.6.5",
"eslint-config-jquery": "3.0.0",
"eslint-plugin-import": "2.25.2",
"eslint": "8.44.0",
"eslint-config-jquery": "3.0.1",
"eslint-plugin-import": "2.27.5",
"express": "4.18.2",
"globals": "13.20.0",
"grunt": "1.5.3",
"grunt-babel": "8.0.0",
"grunt-cli": "1.4.3",
"grunt-compare-size": "0.4.2",
"grunt-contrib-watch": "1.1.0",
"grunt-eslint": "24.0.0",
"grunt-git-authors": "3.2.0",
"grunt-jsonlint": "2.1.2",
"grunt-karma": "4.0.2",
"grunt-newer": "1.3.0",
"grunt-npmcopy": "0.2.0",
"gzip-js": "0.3.2",
"husky": "4.2.5",
"husky": "8.0.3",
"jsdom": "19.0.0",
"karma": "6.4.1",
"karma-browserstack-launcher": "1.6.0",
@ -94,25 +118,8 @@
"rollup": "2.21.0",
"sinon": "7.3.1",
"strip-json-comments": "3.1.1",
"testswarm": "1.1.2"
},
"scripts": {
"build": "npm install && npm run build-all-variants",
"build-all-variants": "grunt custom:slim --esm --filename=jquery.slim.module.js && grunt custom --esm --filename=jquery.module.js && grunt custom:slim --filename=jquery.slim.js && grunt custom",
"start": "grunt watch",
"test:browserless": "grunt && npm run test:node_smoke_tests && grunt test:slow",
"test:browser": "grunt && grunt karma:main",
"test:esmodules": "grunt && grunt karma:esmodules",
"test:no-deprecated": "grunt test:prepare && grunt custom:-deprecated && grunt karma:main",
"test:selector-native": "grunt test:prepare && grunt custom:-selector && grunt karma:main",
"test:slim": "grunt test:prepare && grunt custom:slim && grunt karma:main",
"test:node_smoke_tests:full-module": "grunt node_smoke_tests:module:./dist-module/jquery.module.js && grunt node_smoke_tests:module:jquery",
"test:node_smoke_tests:full-commonjs": "grunt node_smoke_tests:commonjs:./dist/jquery.js && grunt node_smoke_tests:commonjs:jquery",
"test:node_smoke_tests:slim-module": "grunt node_smoke_tests:module:./dist-module/jquery.slim.module.js && grunt node_smoke_tests:module:jquery/slim",
"test:node_smoke_tests:slim-commonjs": "grunt node_smoke_tests:commonjs:./dist/jquery.slim.js && grunt node_smoke_tests:commonjs:jquery/slim",
"test:node_smoke_tests": "npm run test:node_smoke_tests:full-module && npm run test:node_smoke_tests:slim-module && npm run test:node_smoke_tests:full-commonjs && npm run test:node_smoke_tests:slim-commonjs",
"test": "npm run test:browserless && npm run test:slim && npm run test:no-deprecated && npm run test:selector-native && grunt && grunt test:slow && grunt karma:main && grunt karma:esmodules",
"jenkins": "npm run test:browserless"
"testswarm": "1.1.2",
"yargs": "17.7.2"
},
"commitplease": {
"nohook": true,
@ -143,11 +150,5 @@
],
"markerPattern": "^((clos|fix|resolv)(e[sd]|ing))|^(refs?)",
"ticketPattern": "^((Closes|Fixes) ([a-zA-Z]{2,}-)[0-9]+)|^(Refs? [^#])"
},
"husky": {
"hooks": {
"commit-msg": "commitplease .git/COMMIT_EDITMSG",
"pre-commit": "grunt lint:newer qunit_fixture"
}
}
}

View File

@ -1,102 +0,0 @@
{
"root": true,
"extends": "../.eslintrc-browser.json",
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
},
"plugins": [ "import" ],
"rules": {
"import/extensions": [ "error", "always" ],
"import/no-cycle": "error",
"import/no-unused-modules": [ "error", {
"unusedExports": true,
// When run via WebStorm, the root path against which these paths
// are resolved is the path where this ESLint config file lies,
// i.e. `src`. When run via the command line, it's usually the root
// folder of the jQuery repository. This pattern intends to catch both.
// Note that we cannot specify two patterns here:
// [ "src/*.js", "*.js" ]
// as they're analyzed individually and the rule crashes if a pattern
// cannot be matched.
"ignoreExports": [ "{src/,}*.js" ]
} ],
"indent": [ "error", "tab", {
"outerIIFEBody": 0
} ]
},
"overrides": [
{
"files": "wrapper.js",
"parserOptions": {
"ecmaVersion": 5,
"sourceType": "script"
},
"rules": {
"no-unused-vars": "off",
"indent": [ "error", "tab", {
// Unlike other codes, "wrapper.js" is implemented in UMD.
// So it required a specific exception for jQuery's UMD
// Code Style. This makes that indentation check is not
// performed for 1 depth of outer FunctionExpressions
"ignoredNodes": [
"Program > ExpressionStatement > CallExpression > :last-child > *"
]
} ]
},
"globals": {
"jQuery": false,
"module": true
}
},
{
"files": "wrapper-esm.js",
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "module"
},
"globals": {
"jQuery": false
},
"rules": {
"no-unused-vars": "off",
"indent": [ "error", "tab", {
// Unlike other codes, "wrapper.js" is implemented in UMD.
// So it required a specific exception for jQuery's UMD
// Code Style. This makes that indentation check is not
// performed for 1 depth of outer FunctionExpressions
"ignoredNodes": [
"Program > FunctionDeclaration > *"
]
} ],
"import/no-unused-modules": "off"
}
},
{
"files": "exports/amd.js",
"globals": {
"define": false
}
},
{
"files": "core.js",
"globals": {
// Defining Symbol globally would create a danger of using
// it unguarded in another place, it seems safer to define
// it only for this module.
"Symbol": false
}
}
]
}

View File

@ -1,89 +0,0 @@
{
"root": true,
"extends": "../.eslintrc-browser.json",
"env": {
// In source the browser env is not enabled but unit tests rely on them
// too much and we don't run them in non-browser environments anyway.
"browser": true
},
"globals": {
"require": false,
"Promise": false,
"Symbol": false,
"trustedTypes": false,
"QUnit": false,
"ajaxTest": false,
"testIframe": false,
"createDashboardXML": false,
"createWithFriesXML": false,
"createXMLFragment": false,
"includesModule": false,
"moduleTeardown": false,
"url": false,
"q": false,
"jQuery": true,
"sinon": true,
"amdDefined": true,
"fireNative": true,
"Globals": true,
"hasPHP": true,
"isLocal": true,
"supportjQuery": true,
"originaljQuery": true,
"$": true,
"original$": true,
"baseURL": true,
"externalHost": true
},
"rules": {
// See https://github.com/eslint/eslint/issues/2342
"no-unused-vars": "off",
// Too many errors
"max-len": "off",
"brace-style": "off",
"key-spacing": "off",
"camelcase": "off",
"one-var": "off",
"strict": "off",
// Not really too many - waiting for autofix features for these rules
"lines-around-comment": "off",
"dot-notation": "off"
},
"overrides": [
{
"files": [
"middleware-mockserver.js"
],
"extends": "../.eslintrc-node.json"
},
{
"files": [
"data/core/jquery-iterability-transpiled-es6.js",
"data/testinit-jsdom.js"
],
"parserOptions": {
"ecmaVersion": 2015
}
},
{
"files": [
"jquery.js",
"data/testinit.js"
],
"parserOptions": {
"ecmaVersion": 2020
}
}
]
}

View File

@ -0,0 +1,5 @@
{
"plugins": ["@babel/transform-for-of"],
"retainLines": true,
"sourceMaps": "inline"
}

View File

@ -37,10 +37,10 @@ window.original$ = this.$ = "replaced";
* @example url("mock.php?foo=bar")
* @result "data/mock.php?foo=bar&10538358345554"
*/
function url( value ) {
this.url = function( value ) {
return baseURL + value + ( /\?/.test( value ) ? "&" : "?" ) +
new Date().getTime() + "" + parseInt( Math.random() * 100000, 10 );
}
};
// We only run basic tests in jsdom so we don't need to repeat the logic
// from the regular testinit.js

View File

@ -1,5 +1,4 @@
/* eslint no-multi-str: "off" */
"use strict";
var FILEPATH = "/test/data/testinit.js",
@ -16,18 +15,17 @@ var FILEPATH = "/test/data/testinit.js",
baseURL = parentUrl + "test/data/",
supportjQuery = this.jQuery,
// see RFC 2606
externalHost = "example.com",
// NOTE: keep it in sync with build/tasks/lib/slim-build-flags.js
slimBuildFlags = [
"-ajax",
"-callbacks",
"-deferred",
"-effects",
"-queue"
excludedFromSlim = [
"ajax",
"callbacks",
"deferred",
"effects",
"queue"
];
// see RFC 2606
this.externalHost = "example.com";
this.hasPHP = true;
this.isLocal = window.location.protocol === "file:";
@ -241,7 +239,7 @@ this.ajaxTest = function( title, expect, options, wrapper ) {
completed = true;
delete ajaxTest.abort;
assert.ok( false, "aborted " + reason );
jQuery.each( requests, function( i, request ) {
jQuery.each( requests, function( _i, request ) {
request.abort();
} );
}
@ -319,16 +317,16 @@ this.includesModule = function( moduleName ) {
var excludedModulesPart, excludedModules;
// A short-cut for the slim build, e.g. "4.0.0-pre slim"
if ( jQuery.fn.jquery.indexOf( " slim" ) > -1 ) {
// A short-cut for the slim build, e.g. "4.0.0-pre+slim"
if ( jQuery.fn.jquery.indexOf( "+slim" ) > -1 ) {
// The module is included if it does NOT exist on the list
// of modules excluded in the slim build
return slimBuildFlags.indexOf( "-" + moduleName ) === -1;
return excludedFromSlim.indexOf( moduleName ) === -1;
}
// example version for `grunt custom:-deprecated`:
// "4.0.0-pre -deprecated,-deprecated/ajax-event-alias,-deprecated/event"
// example version for `npm run build -- -e deprecated`:
// "v4.0.0-pre+14dc9347 -deprecated,-deprecated/ajax-event-alias,-deprecated/event"
excludedModulesPart = jQuery.fn.jquery
// Take the flags out of the version string.
@ -393,6 +391,7 @@ this.loadTests = function() {
var i = 0,
tests = [
// A special module with basic tests, meant for not fully
// supported environments like jsdom. We run it everywhere,
// though, to make sure tests are not broken.

View File

@ -1,5 +1,4 @@
( function() {
"use strict";
// Store the old count so that we only assert on tests that have actually leaked,

View File

@ -67,16 +67,25 @@ const mocks = {
resp.end( "<root><element /></root>" );
},
script: function( req, resp ) {
const headers = {};
if ( req.query.header === "ecma" ) {
resp.writeHead( 200, { "content-type": "application/ecmascript" } );
headers[ "content-type" ] = "application/ecmascript";
} else if ( "header" in req.query ) {
resp.writeHead( 200, { "content-type": "text/javascript" } );
headers[ "content-type" ] = "text/javascript";
} else {
resp.writeHead( 200, { "content-type": "text/html" } );
headers[ "content-type" ] = "text/html";
}
if ( req.query.cors ) {
resp.writeHead( 200, { "access-control-allow-origin": "*" } );
headers[ "access-control-allow-origin" ] = "*";
}
if ( resp.set ) {
resp.set( headers );
} else {
for ( const key in headers ) {
resp.writeHead( 200, { [ key ]: headers[ key ] } );
}
}
if ( req.query.callback ) {
@ -165,14 +174,25 @@ const mocks = {
}
},
headers: function( req, resp ) {
resp.writeHead( 200, {
const headers = {
"Sample-Header": "Hello World",
"Empty-Header": "",
"Sample-Header2": "Hello World 2",
"List-Header": "Item 1",
"list-header": "Item 2",
"constructor": "prototype collision (constructor)"
} );
};
// Use resp.append in express to
// avoid overwriting List-Header
if ( resp.append ) {
for ( const key in headers ) {
resp.append( key, headers[ key ] );
}
} else {
resp.writeHead( 200, headers );
}
req.query.keys.split( "|" ).forEach( function( key ) {
if ( key.toLowerCase() in req.headers ) {
resp.write( `${ key }: ${ req.headers[ key.toLowerCase() ] }\n` );
@ -354,7 +374,7 @@ function MockserverMiddlewareFactory() {
parsed: parsed
} );
if ( /^test\/data\/mock.php\//.test( path ) ) {
if ( /^\/?test\/data\/mock.php\/?/.test( path ) ) {
// Support REST-like Apache PathInfo
path = "test\/data\/mock.php";
@ -365,6 +385,7 @@ function MockserverMiddlewareFactory() {
return;
}
// console.log( "Mock handling", req.method, parsed.href );
handlers[ path ]( subReq, resp, next );
};
}

View File

@ -1,13 +0,0 @@
{
"root": true,
"extends": "../../../.eslintrc-node.json",
"parserOptions": {
"ecmaVersion": 2015,
"sourceType": "script"
},
"env": {
"es2022": true
}
}

View File

@ -1,13 +0,0 @@
{
"root": true,
"extends": "../../../.eslintrc-node.json",
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"env": {
"es2022": true
}
}

View File

@ -5,6 +5,11 @@ const dirname = path.dirname( fileURLToPath( import.meta.url ) );
const ROOT_DIR = path.resolve( dirname, "..", "..", "..", ".." );
// import does not work with Windows-style paths
function ensureUnixPath( path ) {
return path.replace( /^[a-z]:/i, "" ).replace( /\\+/g, "/" );
}
// If `jQueryModuleSpecifier` is a real relative path, make it absolute
// to make sure it resolves to the same file inside utils from
// a subdirectory. Otherwise, leave it as-is as we may be testing `exports`
@ -16,6 +21,6 @@ export const getJQueryModuleSpecifier = () => {
}
return jQueryModuleInputSpecifier.startsWith( "." ) ?
path.resolve( ROOT_DIR, jQueryModuleInputSpecifier ) :
ensureUnixPath( path.resolve( ROOT_DIR, jQueryModuleInputSpecifier ) ) :
jQueryModuleInputSpecifier;
};

View File

@ -1,5 +0,0 @@
{
"root": true,
"extends": "../../.eslintrc-node.json"
}

21
test/server.js Normal file
View File

@ -0,0 +1,21 @@
import express from "express";
import mockServer from "./middleware-mockserver.cjs";
import fs from "fs";
const nameHTML = fs.readFileSync( "./test/data/name.html", "utf8" );
const app = express();
app.use( mockServer() );
app.post( "/test/data/name.html", function( _req, res ) {
res.send( nameHTML );
} );
app.use( "/dist", express.static( "dist" ) );
app.use( "/src", express.static( "src" ) );
app.use( "/test", express.static( "test" ) );
app.use( "/external", express.static( "external" ) );
app.listen( 3000, function() {
console.log( "Server is running on port 3000" );
} );

View File

@ -455,6 +455,7 @@ QUnit.module( "ajax", {
},
cache: false,
beforeSend: function( xhr, settings ) {
// Clear the cache-buster param value
var url = settings.url.replace( /_=[^&#]+/, "_=" );
assert.equal( url, baseURL + "name.html?abc&devo=hat&_=#brownies",
@ -873,8 +874,8 @@ QUnit.module( "ajax", {
success: function( data ) {
assert.ok( data.match( /^html text/ ), "Check content for datatype html" );
jQuery( "#ap" ).html( data );
assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated for datatype html" );
assert.strictEqual( window[ "testBar" ], "bar", "Check if script src was evaluated for datatype html" );
assert.strictEqual( window.testFoo, "foo", "Check if script was evaluated for datatype html" );
assert.strictEqual( window.testBar, "bar", "Check if script src was evaluated for datatype html" );
}
};
} );
@ -884,6 +885,7 @@ QUnit.module( "ajax", {
return {
dataType: "jsonp",
url: url( "mock.php?action=errorWithScript" ),
// error is the significant assertion
error: function( xhr ) {
var expected = { "status": 404, "msg": "Not Found" };
@ -912,6 +914,7 @@ QUnit.module( "ajax", {
complete: function() {
jQuery.globalEval = globalEval;
},
// error is the significant assertion
error: function( xhr ) {
assert.strictEqual( xhr.status, 404, testMsg );
@ -1171,8 +1174,8 @@ QUnit.module( "ajax", {
Globals.register( "functionToCleanUp" );
Globals.register( "XXX" );
Globals.register( "jsonpResults" );
window[ "jsonpResults" ] = function( data ) {
assert.ok( data[ "data" ], "JSON results returned (GET, custom callback function)" );
window.jsonpResults = function( data ) {
assert.ok( data.data, "JSON results returned (GET, custom callback function)" );
};
},
requests: [ {
@ -1181,7 +1184,7 @@ QUnit.module( "ajax", {
crossDomain: crossDomain,
jsonp: "callback",
success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (GET, data obj callback)" );
assert.ok( data.data, "JSON results returned (GET, data obj callback)" );
}
}, {
url: baseURL + "mock.php?action=jsonp",
@ -1190,7 +1193,7 @@ QUnit.module( "ajax", {
jsonpCallback: "jsonpResults",
success: function( data ) {
assert.strictEqual(
typeof window[ "jsonpResults" ],
typeof window.jsonpResults,
"function",
"should not rewrite original function"
);
@ -1202,8 +1205,8 @@ QUnit.module( "ajax", {
crossDomain: crossDomain,
jsonpCallback: "functionToCleanUp",
success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (GET, custom callback name to be cleaned up)" );
assert.strictEqual( window[ "functionToCleanUp" ], true, "Callback was removed (GET, custom callback name to be cleaned up)" );
assert.ok( data.data, "JSON results returned (GET, custom callback name to be cleaned up)" );
assert.strictEqual( window.functionToCleanUp, true, "Callback was removed (GET, custom callback name to be cleaned up)" );
var xhr;
jQuery.ajax( {
url: baseURL + "mock.php?action=jsonp",
@ -1217,7 +1220,7 @@ QUnit.module( "ajax", {
} );
xhr.fail( function() {
assert.ok( true, "Ajax error JSON (GET, custom callback name to be cleaned up)" );
assert.strictEqual( window[ "functionToCleanUp" ], true, "Callback was removed after early abort (GET, custom callback name to be cleaned up)" );
assert.strictEqual( window.functionToCleanUp, true, "Callback was removed after early abort (GET, custom callback name to be cleaned up)" );
} );
}
}, {
@ -1230,7 +1233,7 @@ QUnit.module( "ajax", {
assert.ok( /action=jsonp&callback=XXX&_=\d+$/.test( this.url ), "The URL wasn't messed with (GET, custom callback name with no url manipulation)" );
},
success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (GET, custom callback name with no url manipulation)" );
assert.ok( data.data, "JSON results returned (GET, custom callback name with no url manipulation)" );
}
} ]
};
@ -1267,7 +1270,7 @@ QUnit.module( "ajax", {
dataType: "jsonp",
crossDomain: crossDomain,
success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (POST, no callback)" );
assert.ok( data.data, "JSON results returned (POST, no callback)" );
}
},
{
@ -1277,7 +1280,7 @@ QUnit.module( "ajax", {
dataType: "jsonp",
crossDomain: crossDomain,
success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (POST, data callback)" );
assert.ok( data.data, "JSON results returned (POST, data callback)" );
}
},
{
@ -1287,7 +1290,7 @@ QUnit.module( "ajax", {
dataType: "jsonp",
crossDomain: crossDomain,
success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (POST, data obj callback)" );
assert.ok( data.data, "JSON results returned (POST, data obj callback)" );
}
}
];
@ -1461,7 +1464,7 @@ QUnit.module( "ajax", {
url: url( "mock.php?action=testbar" ),
dataType: "script",
success: function() {
assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (GET, no callback)" );
assert.strictEqual( window.testBar, "bar", "Script results returned (GET, no callback)" );
}
};
} );
@ -1478,7 +1481,7 @@ QUnit.module( "ajax", {
type: "POST",
dataType: "script",
success: function( data, status ) {
assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (POST, no callback)" );
assert.strictEqual( window.testBar, "bar", "Script results returned (POST, no callback)" );
assert.strictEqual( status, "success", "Script results returned (POST, no callback)" );
}
};
@ -1492,7 +1495,7 @@ QUnit.module( "ajax", {
url: url( "mock.php?action=testbar" ),
dataType: "script",
success: function() {
assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (GET, no callback)" );
assert.strictEqual( window.testBar, "bar", "Script results returned (GET, no callback)" );
}
};
} );
@ -1518,10 +1521,10 @@ QUnit.module( "ajax", {
},
success: function( json ) {
assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" );
assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
}
},
{
@ -1538,10 +1541,10 @@ QUnit.module( "ajax", {
],
success: function( json ) {
assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" );
assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
}
}
];
@ -1562,10 +1565,10 @@ QUnit.module( "ajax", {
assert.strictEqual( typeof text, "string", "json wasn't auto-determined" );
var json = JSON.parse( text );
assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" );
assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
}
},
{
@ -1587,10 +1590,10 @@ QUnit.module( "ajax", {
assert.strictEqual( typeof text, "string", "json wasn't auto-determined" );
var json = JSON.parse( text );
assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" );
assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
}
}
];
@ -1816,6 +1819,7 @@ QUnit.module( "ajax", {
var ifModifiedNow = new Date();
jQuery.each(
/* jQuery.each arguments start */
{
" (cache)": true,
@ -1870,6 +1874,7 @@ QUnit.module( "ajax", {
}
);
}
/* jQuery.each arguments end */
);
@ -1940,6 +1945,7 @@ QUnit.module( "ajax", {
}
jQuery.each(
/* jQuery.each arguments start */
{
"name.html": true,
@ -2014,6 +2020,7 @@ QUnit.module( "ajax", {
} );
}
/* jQuery.each arguments end*/
);
} );
@ -2815,10 +2822,10 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
},
function( json ) {
assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" );
assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
done();
}
);
@ -2828,9 +2835,9 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
assert.expect( 2 );
var done = assert.async();
jQuery.getJSON( url( "mock.php?action=json" ), function( json ) {
if ( json && json[ "data" ] ) {
assert.strictEqual( json[ "data" ][ "lang" ], "en", "Check JSON: lang" );
assert.strictEqual( json[ "data" ].length, 25, "Check JSON: length" );
if ( json && json.data ) {
assert.strictEqual( json.data.lang, "en", "Check JSON: lang" );
assert.strictEqual( json.data.length, 25, "Check JSON: length" );
done();
}
} );
@ -2870,7 +2877,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
Globals.register( "testBar" );
jQuery.getScript( url( "mock.php?action=testbar" ), function() {
assert.strictEqual( window[ "testBar" ], "bar", "Check if script was evaluated" );
assert.strictEqual( window.testBar, "bar", "Check if script was evaluated" );
done();
} );
}
@ -2901,7 +2908,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
jQuery.getScript( {
url: url( "mock.php?action=testbar" ),
success: function() {
assert.strictEqual( window[ "testBar" ], "bar", "Check if script was evaluated" );
assert.strictEqual( window.testBar, "bar", "Check if script was evaluated" );
done();
}
} );
@ -3003,7 +3010,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
assert.expect( 7 );
var done = assert.async();
var verifyEvaluation = function() {
assert.strictEqual( window[ "testBar" ], "bar", "Check if script src was evaluated after load" );
assert.strictEqual( window.testBar, "bar", "Check if script src was evaluated after load" );
assert.strictEqual( jQuery( "#ap" ).html(), "bar", "Check if script evaluation has modified DOM" );
done();
};
@ -3014,7 +3021,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
jQuery( "#first" ).load( url( "mock.php?action=testHTML&baseURL=" + baseURL ), function() {
assert.ok( jQuery( "#first" ).html().match( /^html text/ ), "Check content after loading html" );
assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" );
assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated after load" );
assert.strictEqual( window.testFoo, "foo", "Check if script was evaluated after load" );
setTimeout( verifyEvaluation, 600 );
} );
} );
@ -3026,7 +3033,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
jQuery( "#first" ).load( url( "test2.html" ), function() {
assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" );
assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated after load" );
assert.strictEqual( window.testFoo, "foo", "Check if script was evaluated after load" );
done();
} );
} );

View File

@ -239,7 +239,7 @@ QUnit.test( "attr(Hash)", function( assert ) {
assert.equal(
jQuery( "#text1" ).attr( {
"value": function() {
return this[ "id" ];
return this.id;
} } ).attr( "value" ),
"text1",
"Set attribute to computed value #1"
@ -394,11 +394,11 @@ QUnit.test( "attr(String, Object)", function( assert ) {
table = jQuery( "#table" ).append( "<tr><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr>" );
td = table.find( "td" ).eq( 0 );
td.attr( "rowspan", "2" );
assert.equal( td[ 0 ][ "rowSpan" ], 2, "Check rowspan is correctly set" );
assert.equal( td[ 0 ].rowSpan, 2, "Check rowspan is correctly set" );
td.attr( "colspan", "2" );
assert.equal( td[ 0 ][ "colSpan" ], 2, "Check colspan is correctly set" );
assert.equal( td[ 0 ].colSpan, 2, "Check colspan is correctly set" );
table.attr( "cellspacing", "2" );
assert.equal( table[ 0 ][ "cellSpacing" ], "2", "Check cellspacing is correctly set" );
assert.equal( table[ 0 ].cellSpacing, "2", "Check cellspacing is correctly set" );
assert.equal( jQuery( "#area1" ).attr( "value" ), undefined, "Value attribute is distinct from value property." );
@ -456,6 +456,7 @@ QUnit.test( "attr(String, Object)", function( assert ) {
$radio = jQuery( "<input>", {
"value": "sup",
// Use uppercase here to ensure the type
// attrHook is still used
"TYPE": "radio"
@ -701,9 +702,9 @@ QUnit.test( "prop(String, Object) on null/undefined", function( assert ) {
$body = jQuery( body );
assert.ok( $body.prop( "nextSibling" ) === null, "Make sure a null expando returns null" );
body[ "foo" ] = "bar";
body.foo = "bar";
assert.equal( $body.prop( "foo" ), "bar", "Make sure the expando is preferred over the dom attribute" );
body[ "foo" ] = undefined;
body.foo = undefined;
assert.ok( $body.prop( "foo" ) === undefined, "Make sure the expando is preferred over the dom attribute, even if undefined" );
select = document.createElement( "select" );
@ -848,7 +849,7 @@ QUnit.test( "removeProp(String)", function( assert ) {
obj = {};
assert.strictEqual(
jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[ 0 ][ "nonexisting" ],
jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[ 0 ].nonexisting,
undefined,
"removeprop works correctly on DOM element nodes"
);
@ -856,12 +857,12 @@ QUnit.test( "removeProp(String)", function( assert ) {
jQuery.each( [ document, obj ], function( i, ele ) {
var $ele = jQuery( ele );
$ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" );
assert.strictEqual( ele[ "nonexisting" ], undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." );
assert.strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." );
} );
jQuery.each( [ commentNode, textNode, attributeNode ], function( i, ele ) {
var $ele = jQuery( ele );
$ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" );
assert.strictEqual( ele[ "nonexisting" ], undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." );
assert.strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug trac-7500)." );
} );
} );
@ -1116,7 +1117,7 @@ QUnit.test( "val(select) after form.reset() (Bug trac-2551)", function( assert )
jQuery( "#kkk" ).val( "gf" );
document[ "kk" ].reset();
document.kk.reset();
assert.equal( jQuery( "#kkk" )[ 0 ].value, "cf", "Check value of select after form reset." );
assert.equal( jQuery( "#kkk" ).val(), "cf", "Check value of select after form reset." );

View File

@ -19,6 +19,7 @@ var output,
outputA = addToOutput( "A" ),
outputB = addToOutput( "B" ),
outputC = addToOutput( "C" ),
/* eslint-disable key-spacing */
tests = {
"": "XABC X XABCABCC X XBB X XABA X XX",
"once": "XABC X X X X X XABA X XX",
@ -227,7 +228,9 @@ jQuery.each( tests, function( strFlags, resultString ) {
// Return false
output = "X";
cblist = jQuery.Callbacks( flags );
cblist.add( outputA, function() { return false; }, outputB );
cblist.add( outputA, function() {
return false;
}, outputB );
cblist.add( outputA );
cblist.fire();
assert.strictEqual( output, results.shift(), "Callback returning false" );
@ -269,7 +272,7 @@ QUnit.test( "jQuery.Callbacks( options ) - options are copied", function( assert
fn = function() {
assert.ok( !( count++ ), "called once" );
};
options[ "unique" ] = false;
options.unique = false;
cb.add( fn, fn );
cb.fire();
} );
@ -364,7 +367,9 @@ QUnit.test( "jQuery.Callbacks() - disabled callback doesn't fire (gh-1790)", fun
var cb = jQuery.Callbacks(),
fired = false,
shot = function() { fired = true; };
shot = function() {
fired = true;
};
cb.disable();
cb.empty();
@ -379,8 +384,12 @@ QUnit.test( "jQuery.Callbacks() - list with memory stays locked (gh-3469)", func
var cb = jQuery.Callbacks( "memory" ),
fired = 0,
count1 = function() { fired += 1; },
count2 = function() { fired += 10; };
count1 = function() {
fired += 1;
},
count2 = function() {
fired += 10;
};
cb.add( count1 );
cb.fire();

View File

@ -38,19 +38,21 @@ QUnit.test( "jQuery()", function( assert ) {
// few here but beware of modular builds where these methods may be excluded.
if ( includesModule( "deprecated" ) ) {
expected++;
attrObj[ "click" ] = function() { assert.ok( exec, "Click executed." ); };
attrObj.click = function() {
assert.ok( exec, "Click executed." );
};
}
if ( includesModule( "dimensions" ) ) {
expected++;
attrObj[ "width" ] = 10;
attrObj.width = 10;
}
if ( includesModule( "offset" ) ) {
expected++;
attrObj[ "offset" ] = { "top": 1, "left": 1 };
attrObj.offset = { "top": 1, "left": 1 };
}
if ( includesModule( "css" ) ) {
expected += 2;
attrObj[ "css" ] = { "paddingLeft": 1, "paddingRight": 1 };
attrObj.css = { "paddingLeft": 1, "paddingRight": 1 };
}
if ( includesModule( "attributes" ) ) {
expected++;
@ -216,17 +218,17 @@ QUnit.test( "noConflict", function( assert ) {
var $$ = jQuery;
assert.strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" );
assert.strictEqual( window[ "jQuery" ], $$, "Make sure jQuery wasn't touched." );
assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." );
assert.strictEqual( window.jQuery, $$, "Make sure jQuery wasn't touched." );
assert.strictEqual( window.$, original$, "Make sure $ was reverted." );
jQuery = $ = $$;
assert.strictEqual( jQuery.noConflict( true ), $$, "noConflict returned the jQuery object" );
assert.strictEqual( window[ "jQuery" ], originaljQuery, "Make sure jQuery was reverted." );
assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." );
assert.strictEqual( window.jQuery, originaljQuery, "Make sure jQuery was reverted." );
assert.strictEqual( window.$, original$, "Make sure $ was reverted." );
assert.ok( $$().pushStack( [] ), "Make sure that jQuery still works." );
window[ "jQuery" ] = jQuery = $$;
window.jQuery = jQuery = $$;
} );
QUnit.test( "isPlainObject", function( assert ) {
@ -274,7 +276,7 @@ QUnit.test( "isPlainObject", function( assert ) {
// Makes the function a little more realistic
// (and harder to detect, incidentally)
fn.prototype[ "someMethod" ] = function() {};
fn.prototype.someMethod = function() {};
// Again, instantiated objects shouldn't be matched
assert.ok( !jQuery.isPlainObject( new fn() ), "new fn" );
@ -431,10 +433,10 @@ QUnit.test( "XSS via location.hash", function( assert ) {
var done = assert.async();
assert.expect( 1 );
jQuery[ "_check9521" ] = function( x ) {
jQuery._check9521 = function( x ) {
assert.ok( x, "script called from #id-like selector with inline handler" );
jQuery( "#check9521" ).remove();
delete jQuery[ "_check9521" ];
delete jQuery._check9521;
done();
};
try {
@ -442,7 +444,7 @@ QUnit.test( "XSS via location.hash", function( assert ) {
// This throws an error because it's processed like an id
jQuery( "#<img id='check9521' src='no-such-.gif' onerror='jQuery._check9521(false)'>" ).appendTo( "#qunit-fixture" );
} catch ( err ) {
jQuery[ "_check9521" ]( true );
jQuery._check9521( true );
}
} );
@ -451,12 +453,12 @@ QUnit.test( "jQuery('html')", function( assert ) {
var s, div, j;
jQuery[ "foo" ] = false;
jQuery.foo = false;
s = jQuery( "<script>jQuery.foo='test';</script>" )[ 0 ];
assert.ok( s, "Creating a script" );
assert.ok( !jQuery[ "foo" ], "Make sure the script wasn't executed prematurely" );
assert.ok( !jQuery.foo, "Make sure the script wasn't executed prematurely" );
jQuery( "body" ).append( "<script>jQuery.foo='test';</script>" );
assert.ok( jQuery[ "foo" ], "Executing a script's contents in the right context" );
assert.ok( jQuery.foo, "Executing a script's contents in the right context" );
// Test multi-line HTML
div = jQuery( "<div>\r\nsome text\n<p>some p</p>\nmore text\r\n</div>" )[ 0 ];
@ -634,7 +636,9 @@ QUnit.test( "each(Function)", function( assert ) {
var div, pass, i;
div = jQuery( "div" );
div.each( function() {this.foo = "zoo";} );
div.each( function() {
this.foo = "zoo";
} );
pass = true;
for ( i = 0; i < div.length; i++ ) {
if ( div.get( i ).foo !== "zoo" ) {
@ -740,8 +744,12 @@ QUnit.test( "jQuery.map", function( assert ) {
result = {
Zero: function() {},
One: function( a ) { a = a; },
Two: function( a, b ) { a = a; b = b; }
One: function( a ) {
a = a;
},
Two: function( a, b ) {
a = a; b = b;
}
};
callback = function( v, k ) {
assert.equal( k, "foo", label + "-argument function treated like object" );
@ -999,25 +1007,25 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
assert.deepEqual( options, optionsCopy, "Check if not modified: options must not be modified" );
jQuery.extend( true, deep1, deep2 );
assert.deepEqual( deep1[ "foo" ], deepmerged[ "foo" ], "Check if foo: settings must be extended" );
assert.deepEqual( deep2[ "foo" ], deep2copy[ "foo" ], "Check if not deep2: options must not be modified" );
assert.equal( deep1[ "foo2" ], document, "Make sure that a deep clone was not attempted on the document" );
assert.deepEqual( deep1.foo, deepmerged.foo, "Check if foo: settings must be extended" );
assert.deepEqual( deep2.foo, deep2copy.foo, "Check if not deep2: options must not be modified" );
assert.equal( deep1.foo2, document, "Make sure that a deep clone was not attempted on the document" );
assert.ok( jQuery.extend( true, {}, nestedarray )[ "arr" ] !== arr, "Deep extend of object must clone child array" );
assert.ok( jQuery.extend( true, {}, nestedarray ).arr !== arr, "Deep extend of object must clone child array" );
// trac-5991
assert.ok( Array.isArray( jQuery.extend( true, { "arr": {} }, nestedarray )[ "arr" ] ), "Cloned array have to be an Array" );
assert.ok( jQuery.isPlainObject( jQuery.extend( true, { "arr": arr }, { "arr": {} } )[ "arr" ] ), "Cloned object have to be an plain object" );
assert.ok( Array.isArray( jQuery.extend( true, { "arr": {} }, nestedarray ).arr ), "Cloned array have to be an Array" );
assert.ok( jQuery.isPlainObject( jQuery.extend( true, { "arr": arr }, { "arr": {} } ).arr ), "Cloned object have to be an plain object" );
empty = {};
optionsWithLength = { "foo": { "length": -1 } };
jQuery.extend( true, empty, optionsWithLength );
assert.deepEqual( empty[ "foo" ], optionsWithLength[ "foo" ], "The length property must copy correctly" );
assert.deepEqual( empty.foo, optionsWithLength.foo, "The length property must copy correctly" );
empty = {};
optionsWithDate = { "foo": { "date": new Date() } };
jQuery.extend( true, empty, optionsWithDate );
assert.deepEqual( empty[ "foo" ], optionsWithDate[ "foo" ], "Dates copy correctly" );
assert.deepEqual( empty.foo, optionsWithDate.foo, "Dates copy correctly" );
/** @constructor */
myKlass = function() {};
@ -1025,13 +1033,13 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
optionsWithCustomObject = { "foo": { "date": customObject } };
empty = {};
jQuery.extend( true, empty, optionsWithCustomObject );
assert.ok( empty[ "foo" ] && empty[ "foo" ][ "date" ] === customObject, "Custom objects copy correctly (no methods)" );
assert.ok( empty.foo && empty.foo.date === customObject, "Custom objects copy correctly (no methods)" );
// Makes the class a little more realistic
myKlass.prototype = { "someMethod": function() {} };
empty = {};
jQuery.extend( true, empty, optionsWithCustomObject );
assert.ok( empty[ "foo" ] && empty[ "foo" ][ "date" ] === customObject, "Custom objects copy correctly" );
assert.ok( empty.foo && empty.foo.date === customObject, "Custom objects copy correctly" );
MyNumber = Number;
@ -1039,13 +1047,13 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
assert.ok( parseInt( ret.foo, 10 ) === 5, "Wrapped numbers copy correctly" );
nullUndef = jQuery.extend( {}, options, { "xnumber2": null } );
assert.ok( nullUndef[ "xnumber2" ] === null, "Check to make sure null values are copied" );
assert.ok( nullUndef.xnumber2 === null, "Check to make sure null values are copied" );
nullUndef = jQuery.extend( {}, options, { "xnumber2": undefined } );
assert.ok( nullUndef[ "xnumber2" ] === options[ "xnumber2" ], "Check to make sure undefined values are not copied" );
assert.ok( nullUndef.xnumber2 === options.xnumber2, "Check to make sure undefined values are not copied" );
nullUndef = jQuery.extend( {}, options, { "xnumber0": null } );
assert.ok( nullUndef[ "xnumber0" ] === null, "Check to make sure null values are inserted" );
assert.ok( nullUndef.xnumber0 === null, "Check to make sure null values are inserted" );
target = {};
recursive = { foo: target, bar: 5 };
@ -1172,8 +1180,12 @@ QUnit.test( "jQuery.each(Object,Function)", function( assert ) {
seen = {
Zero: function() {},
One: function( a ) { a = a; },
Two: function( a, b ) { a = a; b = b; }
One: function( a ) {
a = a;
},
Two: function( a, b ) {
a = a; b = b;
}
};
callback = function( k ) {
assert.equal( k, "foo", label + "-argument function treated like object" );
@ -1282,7 +1294,9 @@ QUnit.test( "jQuery.makeArray", function( assert ) {
assert.equal( jQuery.makeArray( document.getElementsByName( "PWD" ) ).slice( 0, 1 )[ 0 ].name, "PWD", "Pass makeArray a nodelist" );
assert.equal( ( function() { return jQuery.makeArray( arguments ); } )( 1, 2 ).join( "" ), "12", "Pass makeArray an arguments array" );
assert.equal( ( function() {
return jQuery.makeArray( arguments );
} )( 1, 2 ).join( "" ), "12", "Pass makeArray an arguments array" );
assert.equal( jQuery.makeArray( [ 1, 2, 3 ] ).join( "" ), "123", "Pass makeArray a real array" );
@ -1301,7 +1315,9 @@ QUnit.test( "jQuery.makeArray", function( assert ) {
assert.ok( !!jQuery.makeArray( document.documentElement.childNodes ).slice( 0, 1 )[ 0 ].nodeName, "Pass makeArray a childNodes array" );
// function, is tricky as it has length
assert.equal( jQuery.makeArray( function() { return 1;} )[ 0 ](), 1, "Pass makeArray a function" );
assert.equal( jQuery.makeArray( function() {
return 1;
} )[ 0 ](), 1, "Pass makeArray a function" );
//window, also has length
assert.equal( jQuery.makeArray( window )[ 0 ], window, "Pass makeArray the window" );

View File

@ -321,8 +321,7 @@ QUnit.test( "css(String, Object)", function( assert ) {
success = true;
try {
jQuery( "#foo" ).css( "backgroundColor", "rgba(0, 0, 0, 0.1)" );
}
catch ( e ) {
} catch ( e ) {
success = false;
}
assert.ok( success, "Setting RGBA values does not throw Error (trac-5509)" );
@ -1203,6 +1202,7 @@ QUnit.test( "Do not append px (trac-9548, trac-12990, gh-2792)", function( asser
$div.css( "animation-iteration-count", 2 );
if ( $div.css( "animation-iteration-count" ) !== undefined ) {
// if $div.css( "animation-iteration-count" ) return "1",
// it actually return the default value of animation-iteration-count
assert.equal( $div.css( "animation-iteration-count" ), 2, "Do not append px to 'animation-iteration-count'" );
@ -1714,7 +1714,7 @@ QUnit.test( "Do not throw on frame elements from css method (trac-15098)", funct
if ( transformName ) {
assert.equal( elemStyle[ transformName ], transformVal, "setting properly-prefixed transform" );
}
assert.equal( elemStyle[ "undefined" ], undefined, "Nothing writes to node.style.undefined" );
assert.equal( elemStyle.undefined, undefined, "Nothing writes to node.style.undefined" );
} );
QUnit.test( "Don't detect fake set properties on a node when caching the prefixed version", function( assert ) {
@ -1829,6 +1829,7 @@ QUnit.testUnlessIE( "Don't append px to CSS vars", function( assert ) {
// Support: IE 11+
// This test requires Grid to be *not supported* to work.
if ( QUnit.isIE ) {
// Make sure explicitly provided IE vendor prefix (`-ms-`) is not converted
// to a non-working `Ms` prefix in JavaScript.
QUnit.test( "IE vendor prefixes are not mangled", function( assert ) {

View File

@ -74,14 +74,14 @@ function dataTests( elem, assert ) {
assert.strictEqual( jQuery.hasData( elem ), false, "jQuery.hasData agrees no data exists even when an empty data obj exists" );
dataObj[ "foo" ] = "bar";
dataObj.foo = "bar";
assert.equal( jQuery.data( elem, "foo" ), "bar", "Data is readable by jQuery.data when set directly on a returned data object" );
assert.strictEqual( jQuery.hasData( elem ), true, "jQuery.hasData agrees data exists when data exists" );
jQuery.data( elem, "foo", "baz" );
assert.equal( jQuery.data( elem, "foo" ), "baz", "Data can be changed by jQuery.data" );
assert.equal( dataObj[ "foo" ], "baz", "Changes made through jQuery.data propagate to referenced data object" );
assert.equal( dataObj.foo, "baz", "Changes made through jQuery.data propagate to referenced data object" );
jQuery.data( elem, "foo", undefined );
assert.equal( jQuery.data( elem, "foo" ), "baz", "Data is not unset by passing undefined to jQuery.data" );
@ -431,8 +431,8 @@ QUnit.test( ".data(Object)", function( assert ) {
jqobj.data( "test", "unset" );
jqobj.data( { "test": "in", "test2": "in2" } );
assert.equal( jQuery.data( obj )[ "test" ], "in", "Verify setting an object on an object extends the data object" );
assert.equal( obj[ "test2" ], undefined, "Verify setting an object on an object does not extend the object" );
assert.equal( jQuery.data( obj ).test, "in", "Verify setting an object on an object extends the data object" );
assert.equal( obj.test2, undefined, "Verify setting an object on an object does not extend the object" );
// manually clean up detached elements
div.remove();
@ -521,12 +521,12 @@ QUnit.test( ".data should follow html5 specification regarding camel casing", fu
var div = jQuery( "<div id='myObject' data-w-t-f='ftw' data-big-a-little-a='bouncing-b' data-foo='a' data-foo-bar='b' data-foo-bar-baz='c'></div>" )
.prependTo( "body" );
assert.equal( div.data()[ "wTF" ], "ftw", "Verify single letter data-* key" );
assert.equal( div.data()[ "bigALittleA" ], "bouncing-b", "Verify single letter mixed data-* key" );
assert.equal( div.data().wTF, "ftw", "Verify single letter data-* key" );
assert.equal( div.data().bigALittleA, "bouncing-b", "Verify single letter mixed data-* key" );
assert.equal( div.data()[ "foo" ], "a", "Verify single word data-* key" );
assert.equal( div.data()[ "fooBar" ], "b", "Verify multiple word data-* key" );
assert.equal( div.data()[ "fooBarBaz" ], "c", "Verify multiple word data-* key" );
assert.equal( div.data().foo, "a", "Verify single word data-* key" );
assert.equal( div.data().fooBar, "b", "Verify multiple word data-* key" );
assert.equal( div.data().fooBarBaz, "c", "Verify multiple word data-* key" );
assert.equal( div.data( "foo" ), "a", "Verify single word data-* key" );
assert.equal( div.data( "fooBar" ), "b", "Verify multiple word data-* key" );

View File

@ -108,7 +108,9 @@ QUnit.test( "jQuery.Deferred.then - filtering (done)", function( assert ) {
piped = defer.then( function( a, b ) {
return a * b;
} ),
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 3 ), function() {
return assert.async();
} );
piped.done( function( result ) {
value3 = result;
@ -145,7 +147,9 @@ QUnit.test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
piped = defer.then( null, function( a, b ) {
return a * b;
} ),
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 3 ), function() {
return assert.async();
} );
piped.done( function( result ) {
value3 = result;
@ -181,7 +185,9 @@ QUnit.test( "jQuery.Deferred.catch", function( assert ) {
piped = defer.catch( function( a, b ) {
return a * b;
} ),
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 3 ), function() {
return assert.async();
} );
piped.done( function( result ) {
value3 = result;
@ -218,7 +224,9 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - filtering (fail)", function( ass
piped = defer.pipe( null, function( a, b ) {
return a * b;
} ),
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 3 ), function() {
return assert.async();
} );
piped.fail( function( result ) {
value3 = result;
@ -416,7 +424,9 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
var defer, piped, defer2, piped2,
context = { custom: true },
done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 5 ), function() {
return assert.async();
} );
jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
assert.strictEqual( this, context, "custom context received by .then handler" );
@ -475,7 +485,9 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
var defer, piped, defer2, piped2,
context = { custom: true },
done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 5 ), function() {
return assert.async();
} );
jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
assert.strictEqual( this, context, "custom context received by .pipe handler" );
@ -642,7 +654,9 @@ QUnit.test( "jQuery.Deferred - 1.x/2.x compatibility", function( assert ) {
var context = { id: "callback context" },
thenable = jQuery.Deferred().resolve( "thenable fulfillment" ).promise(),
done = jQuery.map( new Array( 8 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 8 ), function() {
return assert.async();
} );
thenable.unwrapped = false;
@ -693,7 +707,9 @@ QUnit.test( "jQuery.Deferred.then - progress and thenables", function( assert )
var trigger = jQuery.Deferred().notify(),
expectedProgress = [ "baz", "baz" ],
done = jQuery.map( new Array( 2 ), function() { return assert.async(); } ),
done = jQuery.map( new Array( 2 ), function() {
return assert.async();
} ),
failer = function( evt ) {
return function() {
assert.ok( false, "no unexpected " + evt );
@ -718,7 +734,9 @@ QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) {
assert.expect( 7 );
var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/,
done = jQuery.map( new Array( 3 ), function() { return assert.async(); } );
done = jQuery.map( new Array( 3 ), function() {
return assert.async();
} );
notifiedResolved.progress( function( v ) {
assert.strictEqual( v, "foo", "progress value" );
@ -798,7 +816,9 @@ QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert
assert.expect( 44 );
var defaultContext = ( function getDefaultContext() { return this; } )(),
var defaultContext = ( function getDefaultContext() {
return this;
} )(),
done = assert.async( 20 );
@ -914,7 +934,9 @@ QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
},
numCases = Object.keys( willSucceed ).length + Object.keys( willError ).length,
defaultContext = ( function getDefaultContext() { return this; } )(),
defaultContext = ( function getDefaultContext() {
return this;
} )(),
done = assert.async( numCases * 2 );
@ -992,7 +1014,9 @@ QUnit.test( "jQuery.when(a, b) - like Promise.all", function( assert ) {
rejectedStandardPromise: true
},
defaultContext = ( function getDefaultContext() { return this; } )(),
defaultContext = ( function getDefaultContext() {
return this;
} )(),
done = assert.async( 98 );

View File

@ -46,8 +46,12 @@ QUnit.test( "hover() mouseenter mouseleave", function( assert ) {
assert.expect( 1 );
var times = 0,
handler1 = function() { ++times; },
handler2 = function() { ++times; };
handler1 = function() {
++times;
},
handler2 = function() {
++times;
};
jQuery( "#firstp" )
.hover( handler1, handler2 )

52
test/unit/effects.js vendored
View File

@ -8,8 +8,12 @@ if ( !includesModule( "effects" ) ) {
var fxInterval = 13,
oldRaf = window.requestAnimationFrame,
hideOptions = {
inline: function() { jQuery.style( this, "display", "none" ); },
cascade: function() { this.className = "hidden"; }
inline: function() {
jQuery.style( this, "display", "none" );
},
cascade: function() {
this.className = "hidden";
}
};
QUnit.module( "effects", {
@ -948,15 +952,33 @@ jQuery.each( {
num = 0;
// TODO: uncrowd this
if ( t_h === "show" ) { num++; }
if ( t_w === "show" ) { num++; }
if ( t_w === "hide" || t_w === "show" ) { num++; }
if ( t_h === "hide" || t_h === "show" ) { num++; }
if ( t_o === "hide" || t_o === "show" ) { num++; }
if ( t_w === "hide" ) { num++; }
if ( t_o.constructor === Number ) { num += 2; }
if ( t_w.constructor === Number ) { num += 2; }
if ( t_h.constructor === Number ) { num += 2; }
if ( t_h === "show" ) {
num++;
}
if ( t_w === "show" ) {
num++;
}
if ( t_w === "hide" || t_w === "show" ) {
num++;
}
if ( t_h === "hide" || t_h === "show" ) {
num++;
}
if ( t_o === "hide" || t_o === "show" ) {
num++;
}
if ( t_w === "hide" ) {
num++;
}
if ( t_o.constructor === Number ) {
num += 2;
}
if ( t_w.constructor === Number ) {
num += 2;
}
if ( t_h.constructor === Number ) {
num += 2;
}
assert.expect( num );
@ -2457,7 +2479,9 @@ QUnit.test( "Show/hide/toggle and display: inline", function( assert ) {
.hide().show( N ),
$el.clone().data( { call: "hide+toggle", done: "inline" } ).appendTo( fixture )
.hide().toggle( N )
], function( $clone ) { return $clone[ 0 ]; } );
], function( $clone ) {
return $clone[ 0 ];
} );
// Animations not allowed to complete
interrupted = jQuery.map( [
@ -2465,7 +2489,9 @@ QUnit.test( "Show/hide/toggle and display: inline", function( assert ) {
$el.clone().data( { call: "toggle+stop" } ).appendTo( fixture ).toggle( N ),
$el.clone().data( { call: "hide+show+stop" } ).appendTo( fixture ).hide().show( N ),
$el.clone().data( { call: "hide+toggle+stop" } ).appendTo( fixture ).hide().toggle( N )
], function( $clone ) { return $clone[ 0 ]; } );
], function( $clone ) {
return $clone[ 0 ];
} );
// All elements should be inline-block during the animation
clock.tick( N / 2 );

View File

@ -224,12 +224,12 @@ QUnit.test( "on(), namespace with special add", function( assert ) {
div.on( "test.a", { x: 1 }, function( e ) {
assert.ok( !!e.xyz, "Make sure that the data is getting passed through." );
assert.equal( e.data[ "x" ], 1, "Make sure data is attached properly." );
assert.equal( e.data.x, 1, "Make sure data is attached properly." );
} );
div.on( "test.b", { x: 2 }, function( e ) {
assert.ok( !!e.xyz, "Make sure that the data is getting passed through." );
assert.equal( e.data[ "x" ], 2, "Make sure data is attached properly." );
assert.equal( e.data.x, 2, "Make sure data is attached properly." );
} );
// Should trigger 5
@ -553,7 +553,9 @@ QUnit.test( "on(), namespaced events, cloned events", function( assert ) {
} ).trigger( "tester" );
// Make sure events stick with appendTo'd elements (which are cloned) trac-2027
jQuery( "<a href='#fail' class='test'>test</a>" ).on( "click", function() { return false; } ).appendTo( "#qunit-fixture" );
jQuery( "<a href='#fail' class='test'>test</a>" ).on( "click", function() {
return false;
} ).appendTo( "#qunit-fixture" );
assert.ok( jQuery( "a.test" ).eq( 0 ).triggerHandler( "click" ) === false, "Handler is bound to appendTo'd elements" );
} );
@ -704,7 +706,9 @@ QUnit.test( "on(name, false), off(name, false)", function( assert ) {
assert.expect( 3 );
var main = 0;
jQuery( "#qunit-fixture" ).on( "click", function() { main++; } );
jQuery( "#qunit-fixture" ).on( "click", function() {
main++;
} );
jQuery( "#ap" ).trigger( "click" );
assert.equal( main, 1, "Verify that the trigger happened correctly." );
@ -727,7 +731,9 @@ QUnit.test( "on(name, selector, false), off(name, selector, false)", function( a
var main = 0;
jQuery( "#qunit-fixture" ).on( "click", "#ap", function() { main++; } );
jQuery( "#qunit-fixture" ).on( "click", "#ap", function() {
main++;
} );
jQuery( "#ap" ).trigger( "click" );
assert.equal( main, 1, "Verify that the trigger happened correctly." );
@ -766,7 +772,7 @@ QUnit.test( "on()/trigger()/off() on plain object", function( assert ) {
events = jQuery._data( obj, "events" );
assert.ok( events, "Object has events bound." );
assert.equal( obj[ "events" ], undefined, "Events object on plain objects is not events" );
assert.equal( obj.events, undefined, "Events object on plain objects is not events" );
assert.equal( obj.test, undefined, "Make sure that test event is not on the plain object." );
assert.equal( obj.handle, undefined, "Make sure that the event handler is not on the plain object." );
@ -785,7 +791,7 @@ QUnit.test( "on()/trigger()/off() on plain object", function( assert ) {
assert.equal( obj && obj[ jQuery.expando ] &&
obj[ jQuery.expando ][ jQuery.expando ] &&
obj[ jQuery.expando ][ jQuery.expando ][ "events" ], undefined, "Make sure events object is removed" );
obj[ jQuery.expando ][ jQuery.expando ].events, undefined, "Make sure events object is removed" );
} );
QUnit.test( "off(type)", function( assert ) {
@ -938,12 +944,26 @@ QUnit.test( "trigger() bubbling", function( assert ) {
var win = 0, doc = 0, html = 0, body = 0, main = 0, ap = 0;
jQuery( window ).on( "click", function() { win++; } );
jQuery( document ).on( "click", function( e ) { if ( e.target !== document ) { doc++; } } );
jQuery( "html" ).on( "click", function() { html++; } );
jQuery( "body" ).on( "click", function() { body++; } );
jQuery( "#qunit-fixture" ).on( "click", function() { main++; } );
jQuery( "#ap" ).on( "click", function() { ap++; return false; } );
jQuery( window ).on( "click", function() {
win++;
} );
jQuery( document ).on( "click", function( e ) {
if ( e.target !== document ) {
doc++;
}
} );
jQuery( "html" ).on( "click", function() {
html++;
} );
jQuery( "body" ).on( "click", function() {
body++;
} );
jQuery( "#qunit-fixture" ).on( "click", function() {
main++;
} );
jQuery( "#ap" ).on( "click", function() {
ap++; return false;
} );
jQuery( "html" ).trigger( "click" );
assert.equal( win, 1, "HTML bubble" );
@ -1159,7 +1179,9 @@ QUnit.test( "trigger(eventObject, [data], [fn])", function( assert ) {
event.stopPropagation();
assert.equal( event.isPropagationStopped(), true, "Verify isPropagationStopped" );
event.isPropagationStopped = function() { return false; };
event.isPropagationStopped = function() {
return false;
};
event.stopImmediatePropagation();
assert.equal( event.isPropagationStopped(), true, "Verify isPropagationStopped" );
assert.equal( event.isImmediatePropagationStopped(), true, "Verify isPropagationStopped" );
@ -1394,7 +1416,9 @@ QUnit.test( "Submit event can be stopped (trac-11049)", function( assert ) {
return false;
} )
.find( "#myform input[type=submit]" )
.each( function() { this.click(); } )
.each( function() {
this.click();
} )
.end()
.on( "submit", function() {
assert.ok( false, "submit bubbled on second handler" );
@ -1506,10 +1530,18 @@ QUnit.test( ".on()/.off()", function( assert ) {
var event, clicked, hash, called, livec, lived, livee,
submit = 0, div = 0, livea = 0, liveb = 0;
jQuery( "#body" ).on( "submit", "#qunit-fixture div", function() { submit++; return false; } );
jQuery( "#body" ).on( "click", "#qunit-fixture div", function() { div++; } );
jQuery( "#body" ).on( "click", "div#nothiddendiv", function() { livea++; } );
jQuery( "#body" ).on( "click", "div#nothiddendivchild", function() { liveb++; } );
jQuery( "#body" ).on( "submit", "#qunit-fixture div", function() {
submit++; return false;
} );
jQuery( "#body" ).on( "click", "#qunit-fixture div", function() {
div++;
} );
jQuery( "#body" ).on( "click", "div#nothiddendiv", function() {
livea++;
} );
jQuery( "#body" ).on( "click", "div#nothiddendivchild", function() {
liveb++;
} );
// Nothing should trigger on the body
jQuery( "body" ).trigger( "click" );
@ -1569,7 +1601,9 @@ QUnit.test( ".on()/.off()", function( assert ) {
// Make sure that stopPropagation doesn't stop live events
submit = 0; div = 0; livea = 0; liveb = 0;
jQuery( "#body" ).on( "click", "div#nothiddendivchild", function( e ) { liveb++; e.stopPropagation(); } );
jQuery( "#body" ).on( "click", "div#nothiddendivchild", function( e ) {
liveb++; e.stopPropagation();
} );
jQuery( "div#nothiddendivchild" ).trigger( "click" );
assert.equal( submit, 0, "stopPropagation Click on inner div" );
assert.equal( div, 1, "stopPropagation Click on inner div" );
@ -1591,7 +1625,9 @@ QUnit.test( ".on()/.off()", function( assert ) {
// Test binding with a different context
clicked = 0;
jQuery( "#qunit-fixture" ).on( "click", "#foo", function() { clicked++; } );
jQuery( "#qunit-fixture" ).on( "click", "#foo", function() {
clicked++;
} );
jQuery( "#qunit-fixture div" ).trigger( "click" );
jQuery( "#foo" ).trigger( "click" );
jQuery( "#qunit-fixture" ).trigger( "click" );
@ -1635,14 +1671,18 @@ QUnit.test( ".on()/.off()", function( assert ) {
jQuery( "#body" ).off( "click", "#foo" );
// Verify that return false prevents default action
jQuery( "#body" ).on( "click", "#anchor2", function() { return false; } );
jQuery( "#body" ).on( "click", "#anchor2", function() {
return false;
} );
hash = window.location.hash;
jQuery( "#anchor2" ).trigger( "click" );
assert.equal( window.location.hash, hash, "return false worked" );
jQuery( "#body" ).off( "click", "#anchor2" );
// Verify that .preventDefault() prevents default action
jQuery( "#body" ).on( "click", "#anchor2", function( e ) { e.preventDefault(); } );
jQuery( "#body" ).on( "click", "#anchor2", function( e ) {
e.preventDefault();
} );
hash = window.location.hash;
jQuery( "#anchor2" ).trigger( "click" );
assert.equal( window.location.hash, hash, "e.preventDefault() worked" );
@ -1650,7 +1690,9 @@ QUnit.test( ".on()/.off()", function( assert ) {
// Test binding the same handler to multiple points
called = 0;
function callback() { called++; return false; }
function callback() {
called++; return false;
}
jQuery( "#body" ).on( "click", "#nothiddendiv", callback );
jQuery( "#body" ).on( "click", "#anchor2", callback );
@ -1696,8 +1738,14 @@ QUnit.test( ".on()/.off()", function( assert ) {
livec = 0;
jQuery( "#nothiddendivchild" ).html( "<span></span>" );
jQuery( "#body" ).on( "click", "#nothiddendivchild", function() { jQuery( "#nothiddendivchild" ).html( "" ); } );
jQuery( "#body" ).on( "click", "#nothiddendivchild", function( e ) { if ( e.target ) {livec++;} } );
jQuery( "#body" ).on( "click", "#nothiddendivchild", function() {
jQuery( "#nothiddendivchild" ).html( "" );
} );
jQuery( "#body" ).on( "click", "#nothiddendivchild", function( e ) {
if ( e.target ) {
livec++;
}
} );
jQuery( "#nothiddendiv span" ).trigger( "click" );
assert.equal( jQuery( "#nothiddendiv span" ).length, 0, "Verify that first handler occurred and modified the DOM." );
@ -1712,16 +1760,26 @@ QUnit.test( ".on()/.off()", function( assert ) {
livee = 0;
// bind one pair in one order
jQuery( "#body" ).on( "click", "span#liveSpan1 a", function() { lived++; return false; } );
jQuery( "#body" ).on( "click", "span#liveSpan1", function() { livee++; } );
jQuery( "#body" ).on( "click", "span#liveSpan1 a", function() {
lived++;
return false;
} );
jQuery( "#body" ).on( "click", "span#liveSpan1", function() {
livee++;
} );
jQuery( "span#liveSpan1 a" ).trigger( "click" );
assert.equal( lived, 1, "Verify that only one first handler occurred." );
assert.equal( livee, 0, "Verify that second handler doesn't." );
// and one pair in inverse
jQuery( "#body" ).on( "click", "span#liveSpan2", function() { livee++; } );
jQuery( "#body" ).on( "click", "span#liveSpan2 a", function() { lived++; return false; } );
jQuery( "#body" ).on( "click", "span#liveSpan2", function() {
livee++;
} );
jQuery( "#body" ).on( "click", "span#liveSpan2 a", function() {
lived++;
return false;
} );
lived = 0;
livee = 0;
@ -1747,11 +1805,17 @@ QUnit.test( ".on()/.off()", function( assert ) {
// Work with deep selectors
livee = 0;
function clickB() { livee++; }
function clickB() {
livee++;
}
jQuery( "#body" ).on( "click", "#nothiddendiv div", function() { livee++; } );
jQuery( "#body" ).on( "click", "#nothiddendiv div", function() {
livee++;
} );
jQuery( "#body" ).on( "click", "#nothiddendiv div", clickB );
jQuery( "#body" ).on( "mouseover", "#nothiddendiv div", function() { livee++; } );
jQuery( "#body" ).on( "mouseover", "#nothiddendiv div", function() {
livee++;
} );
assert.equal( livee, 0, "No clicks, deep selector." );
@ -1825,6 +1889,7 @@ QUnit.test( "events with type matching an Object.prototype property, cloned elem
elem.appendTo( "#qunit-fixture" );
try {
// Make sure the original element has some event data.
elem.on( "click", function() {} );
@ -1859,6 +1924,7 @@ QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "delegated event with delegate
.off( "click" );
if ( QUnit.jQuerySelectorsPos ) {
// Positional selector (trac-11315)
markup.find( "ul" ).eq( 0 )
.on( "click", ">li>a", function() {
@ -1954,8 +2020,12 @@ QUnit.test( "off all bound delegated events", function( assert ) {
clicks = 0,
div = jQuery( "#body" );
div.on( "click submit", "div#nothiddendivchild", function() { count++; } );
div.on( "click", function() { clicks++; } );
div.on( "click submit", "div#nothiddendivchild", function() {
count++;
} );
div.on( "click", function() {
clicks++;
} );
div.off( undefined, "**" );
jQuery( "div#nothiddendivchild" ).trigger( "click" );
@ -1974,7 +2044,9 @@ QUnit.test( "on with multiple delegated events", function( assert ) {
var count = 0,
div = jQuery( "#body" );
div.on( "click submit", "div#nothiddendivchild", function() { count++; } );
div.on( "click submit", "div#nothiddendivchild", function() {
count++;
} );
jQuery( "div#nothiddendivchild" ).trigger( "click" );
jQuery( "div#nothiddendivchild" ).trigger( "submit" );
@ -2329,7 +2401,7 @@ QUnit.test( ".on and .off", function( assert ) {
// We should have removed all the event handlers ... kinda hacky way to check this
data = jQuery.data[ jQuery( "#onandoff" )[ 0 ].expando ] || {};
assert.equal( data[ "events" ], undefined, "no events left" );
assert.equal( data.events, undefined, "no events left" );
$onandoff.remove();
} );
@ -2403,7 +2475,7 @@ QUnit.test( "special on name mapping", function( assert ) {
.trigger( "gutfeeling" ) // This one should not
.remove();
delete jQuery.event.special[ "gutfeeling" ];
delete jQuery.event.special.gutfeeling;
} );
QUnit.test( ".on and .off, selective mixed removal (trac-10705)", function( assert ) {
@ -2564,7 +2636,9 @@ QUnit.test( "addProp extensions", function( assert ) {
fireNative( $fixture[ 0 ], "click" );
$fixture.off( "click" );
jQuery.event.addProp( "testProperty", function() { return 42; } );
jQuery.event.addProp( "testProperty", function() {
return 42;
} );
// Trigger a native click and ensure the property is set
$fixture.on( "click", function( event ) {
@ -3393,6 +3467,7 @@ QUnit.test( "focus change during a focus handler (gh-4382)", function( assert )
} );
jQuery( document ).on( "focusin.focusTests", function( ev ) {
// Support: IE 11+
// In IE focus is async so focusin on document is fired multiple times,
// for each of the elements. In other browsers it's fired just once, for

View File

@ -1325,9 +1325,13 @@ QUnit.test( "Empty replaceWith (trac-13401; trac-13596; gh-2204)", function( ass
jQuery.each( tests, function( label, input ) {
$el.html( "<a></a>" ).children().replaceWith( input );
assert.strictEqual( $el.html(), "", "replaceWith(" + label + ")" );
$el.html( "<b></b>" ).children().replaceWith( function() { return input; } );
$el.html( "<b></b>" ).children().replaceWith( function() {
return input;
} );
assert.strictEqual( $el.html(), "", "replaceWith(function returning " + label + ")" );
$el.html( "<i></i>" ).children().replaceWith( function( i ) { return input; } );
$el.html( "<i></i>" ).children().replaceWith( function( i ) {
return input;
} );
assert.strictEqual( $el.html(), "", "replaceWith(other function returning " + label + ")" );
$el.html( "<p></p>" ).children().replaceWith( function( i ) {
return i ?
@ -1567,7 +1571,9 @@ QUnit.test( "clone(form element) (Bug trac-3879, trac-6655)", function( assert )
element = jQuery( "<select><option>Foo</option><option value='selected' selected>Bar</option></select>" );
assert.equal( element.clone().find( "option" ).filter( function() { return this.selected; } ).val(), "selected", "Selected option cloned correctly" );
assert.equal( element.clone().find( "option" ).filter( function() {
return this.selected;
} ).val(), "selected", "Selected option cloned correctly" );
element = jQuery( "<input type='checkbox' value='foo'>" ).attr( "checked", "checked" );
clone = element.clone();
@ -2217,8 +2223,7 @@ QUnit.test( "domManip plain-text caching (trac-6779)", function( assert ) {
for ( i = 0; i < bad.length; i++ ) {
try {
$f.append( bad[ i ] );
}
catch ( e ) {}
} catch ( e ) {}
}
assert.equal( $f.text(), bad.join( "" ), "Cached strings that match Object properties" );
$f.remove();

View File

@ -592,6 +592,7 @@ QUnit.test( "chaining", function( assert ) {
// parent is not offsetParent); offset includes parent offset+border+padding
// static: same as relative
for ( ; parent; parent = expectations[ parent.parent ] ) {
// position:fixed
if ( props.style === "fixed" ) {
break;

View File

@ -88,7 +88,9 @@ QUnit.test( "queue() passes in the next item in the queue as a parameter to fx q
div.queue( function( next ) {
assert.equal( ++counter, 1, "Dequeueing" );
setTimeout( function() { next(); }, 500 );
setTimeout( function() {
next();
}, 500 );
} ).queue( function( next ) {
assert.equal( ++counter, 2, "Next was called" );
next();
@ -187,7 +189,9 @@ QUnit.test( "clearQueue() clears the fx queue", function( assert ) {
div.queue( function( next ) {
counter++;
var self = this;
setTimeout( function() { jQuery( self ).clearQueue(); next(); }, 50 );
setTimeout( function() {
jQuery( self ).clearQueue(); next();
}, 50 );
} ).queue( function() {
counter++;
} );

View File

@ -35,8 +35,8 @@ QUnit.test( "empty", function( assert ) {
QUnit.test( "star", function( assert ) {
assert.expect( 2 );
var good, i;
var all = jQuery( "*" );
var good, i,
all = jQuery( "*" );
assert.ok( all.length >= 30, "Select all" );
good = true;
@ -51,8 +51,8 @@ QUnit.test( "star", function( assert ) {
QUnit.test( "element", function( assert ) {
assert.expect( 37 );
var i, lengthtest, siblingTest, html;
var fixture = document.getElementById( "qunit-fixture" );
var i, lengthtest, siblingTest, html,
fixture = document.getElementById( "qunit-fixture" );
assert.deepEqual( jQuery( "p", fixture ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a Node context." );
assert.deepEqual( jQuery( "p", "#qunit-fixture" ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a selector context." );
@ -848,6 +848,7 @@ QUnit.test( "pseudo - nth-child", function( assert ) {
"Seeded nth-child"
);
} else {
// Support: Chrome 75+, Firefox 67+
// Some browsers mark disconnected elements as matching `:nth-child(n)`
// so let's skip the test.
@ -901,6 +902,7 @@ QUnit.test( "pseudo - nth-last-child", function( assert ) {
"Seeded nth-last-child"
);
} else {
// Support: Chrome 75+, Firefox 67+
// Some browsers mark disconnected elements as matching `:nth-last-child(n)`
// so let's skip the test.
@ -1037,6 +1039,7 @@ QUnit.test( "pseudo - misc", function( assert ) {
tmp = document.createElement( "div" );
tmp.id = "tmp_input";
tmp.innerHTML = "<span>Hello I am focusable.</span>";
// Setting tabIndex should make the element focusable
// https://html.spec.whatwg.org/#the-tabindex-attribute
document.body.appendChild( tmp );
@ -1093,6 +1096,7 @@ QUnit.test( "pseudo - misc", function( assert ) {
);
if ( QUnit.jQuerySelectors ) {
// Tokenization edge cases
assert.t( "Sequential pseudos", "#qunit-fixture p:has(:contains(mark)):has(code)", [ "ap" ] );
assert.t( "Sequential pseudos", "#qunit-fixture p:has(:contains(mark)):has(code):contains(This link)", [ "ap" ] );
@ -1158,6 +1162,7 @@ QUnit.test( "pseudo - :not", function( assert ) {
assert.t( ":not() failing interior", "#qunit-fixture p:not(div#blargh)", [ "firstp", "ap", "sndp", "en", "sap", "first" ] );
assert.t( ":not() failing interior", "#qunit-fixture p:not(p#blargh)", [ "firstp", "ap", "sndp", "en", "sap", "first" ] );
} else {
// Support: IE 11+
// IE doesn't support `:not(complex selector)`.
assert.ok( "skip", ":not(complex selector) not supported in selector-native" );
@ -1178,6 +1183,7 @@ QUnit.test( "pseudo - :not", function( assert ) {
assert.t( ":not Multiple", "p:not(p,a)", [] );
assert.t( ":not Multiple", "p:not(a,p,b)", [] );
} else {
// Support: IE 11+
// IE doesn't support `:not(complex selector)`.
assert.ok( "skip", ":not(complex selector) not supported in selector-native" );
@ -1207,6 +1213,7 @@ QUnit.test( "pseudo - :not", function( assert ) {
if ( QUnit.jQuerySelectors || !QUnit.isIE ) {
assert.t( ":not() Multiple Class", "#foo a:not(.blog.link)", [ "yahoo", "anchor2" ] );
} else {
// Support: IE 11+
// IE doesn't support `:not(complex selector)`.
assert.ok( "skip", ":not(complex selector) not supported in selector-native" );
@ -1439,8 +1446,7 @@ QUnit.test( "pseudo - a:(dis|en)abled", function( assert ) {
assert.expect( 2 );
var enabled, disabled,
container = jQuery( "<div></div>" ),
anchor = jQuery( "<a href='#'>Link</a>" );
container = jQuery( "<div></div>" );
container.appendTo( "#qunit-fixture" );
@ -1754,8 +1760,8 @@ QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "disconnected nodes", function
QUnit.testUnlessIE( "Shadow DOM nodes supported as root", function( assert ) {
assert.expect( 2 );
var shadowHost = jQuery( "<div></div>" ).appendTo( "#qunit-fixture" )[ 0 ];
var shadowRoot = shadowHost.attachShadow( { mode: "open" } );
var shadowHost = jQuery( "<div></div>" ).appendTo( "#qunit-fixture" )[ 0 ],
shadowRoot = shadowHost.attachShadow( { mode: "open" } );
shadowRoot.innerHTML = "<div class='vagabond'><p></p></div>";
assert.equal( jQuery( shadowRoot ).find( ".vagabond" ).length, 1,
@ -1767,7 +1773,7 @@ QUnit.testUnlessIE( "Shadow DOM nodes supported as root", function( assert ) {
testIframe(
"attributes - jQuery.attr",
"selector/html5_selector.html",
function( assert, jQuery, window, document ) {
function( assert, jQuery, _window, document ) {
assert.expect( 38 );
/**
@ -1840,7 +1846,7 @@ testIframe(
// Enumerated attributes (these are not boolean content attributes)
jQuery.expandedEach = jQuery.each;
jQuery.expandedEach( [ "draggable", "contenteditable", "aria-disabled" ], function( i, val ) {
jQuery.expandedEach( [ "draggable", "contenteditable", "aria-disabled" ], function( _i, val ) {
t( "Enumerated attribute", "[" + val + "]", [ "div1" ] );
} );
t( "Enumerated attribute", "[spellcheck]", [ "span1" ] );
@ -1984,7 +1990,7 @@ QUnit.test( "uniqueSort()", function( assert ) {
testIframe(
"jQuery.uniqueSort works cross-window (trac-14381)",
"selector/mixed_sort.html",
function( assert, jQuery, window, document, actual, expected ) {
function( assert, _jQuery, _window, _document, actual, expected ) {
assert.expect( 1 );
assert.deepEqual( actual, expected, "Mixed array was sorted correctly" );
@ -2313,7 +2319,7 @@ QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "jQuery.find.select with pre-c
"#qunit-fixture #first",
"ol#listWithTabIndex > li[tabindex]",
"#liveSpan1"
], function( i, selector ) {
], function( _i, selector ) {
var compiled = jQuery.find.compile( selector );
assert.equal( jQuery.find.select( compiled, document ).length,
1, "Should match using a compiled selector function" );

View File

@ -64,7 +64,9 @@ QUnit.test( "jQuery.param()", function( assert ) {
params = { "param1": null };
assert.equal( jQuery.param( params ), "param1=", "Make sure that null params aren't traversed." );
params = { "param1": function() {}, "param2": function() { return null; } };
params = { "param1": function() {}, "param2": function() {
return null;
} };
assert.equal( jQuery.param( params, false ), "param1=&param2=", "object with function property that returns null value" );
params = { "test": { "length": 3, "foo": "bar" } };

View File

@ -310,7 +310,9 @@ QUnit.test( "filter(Function)", function( assert ) {
return !jQuery( "a", this ).length;
} ).get(), q( "sndp", "first" ), "filter(Function)" );
assert.deepEqual( jQuery( "#qunit-fixture p" ).filter( function( i, elem ) { return !jQuery( "a", elem ).length; } ).get(), q( "sndp", "first" ), "filter(Function) using arg" );
assert.deepEqual( jQuery( "#qunit-fixture p" ).filter( function( i, elem ) {
return !jQuery( "a", elem ).length;
} ).get(), q( "sndp", "first" ), "filter(Function) using arg" );
} );
QUnit.test( "filter(Element)", function( assert ) {
@ -511,7 +513,9 @@ QUnit.test( "not(Element)", function( assert ) {
QUnit.test( "not(Function)", function( assert ) {
assert.expect( 1 );
assert.deepEqual( jQuery( "#qunit-fixture p" ).not( function() { return jQuery( "a", this ).length; } ).get(), q( "sndp", "first" ), "not(Function)" );
assert.deepEqual( jQuery( "#qunit-fixture p" ).not( function() {
return jQuery( "a", this ).length;
} ).get(), q( "sndp", "first" ), "not(Function)" );
} );
QUnit.test( "not(Array)", function( assert ) {