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 trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[package.json] [*.{json,yml}]
indent_style = space indent_style = space
indent_size = 2 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: | run: |
export FIREFOX_SOURCE_URL='https://download.mozilla.org/?product=firefox-esr-latest&lang=en-US&os=linux64' 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} 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 - name: Install dependencies
run: | run: npm install
npm install
- name: Install Playwright dependencies - name: Install Playwright dependencies
run: npx playwright-webkit install-deps 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 - name: Run tests
env: env:

7
.gitignore vendored
View File

@ -8,19 +8,18 @@
.bower.json .bower.json
.sizecache.json .sizecache.json
yarn.lock yarn.lock
package-lock.json .eslintcache
npm-debug.log* 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/*
!/dist/.eslintrc.json
!/dist/package.json !/dist/package.json
# Ignore everything in the `dist-module` folder except for the ESLint config, # Ignore everything in the `dist-module` folder except for the ESLint config,
# package.json & Node module wrapper files # package.json & Node module wrapper files
/dist-module/* /dist-module/*
!/dist-module/.eslintrc.json
!/dist-module/package.json !/dist-module/package.json
!/dist-module/jquery.node-module-wrapper.js !/dist-module/jquery.node-module-wrapper.js
!/dist-module/jquery.node-module-wrapper.slim.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 .eslintignore
.eslintrc.json eslint.config.js
/.editorconfig /.editorconfig
/.gitattributes /.gitattributes

View File

@ -1,20 +1,6 @@
"use strict"; "use strict";
module.exports = function( grunt ) { 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 nodeV16OrNewer = !/^v1[0-5]\./.test( process.version );
const nodeV17OrNewer = !/^v1[0-6]\./.test( process.version ); const nodeV17OrNewer = !/^v1[0-6]\./.test( process.version );
const customBrowsers = process.env.BROWSERS && process.env.BROWSERS.split( "," ); const customBrowsers = process.env.BROWSERS && process.env.BROWSERS.split( "," );
@ -34,137 +20,8 @@ module.exports = function( grunt ) {
grunt.option( "filename", "jquery.js" ); 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( { grunt.initConfig( {
pkg: grunt.file.readJSON( "package.json" ), 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: { testswarm: {
tests: [ tests: [
@ -230,9 +87,14 @@ module.exports = function( grunt ) {
"external/npo/npo.js", "external/npo/npo.js",
"external/requirejs/require.js", "external/requirejs/require.js",
"test/data/testinit.js", "test/data/testinit.js",
"test/jquery.js", "test/jquery.js",
{
pattern: "external/**",
included: false,
served: true,
nocache: true
},
{ {
pattern: "dist/jquery.*", pattern: "dist/jquery.*",
included: false, included: false,
@ -246,7 +108,6 @@ module.exports = function( grunt ) {
served: true, served: true,
nocache: true nocache: true
}, },
{ pattern: "external/**", included: false, served: true },
{ {
pattern: "test/**/*.@(js|css|jpg|html|xml|svg)", pattern: "test/**/*.@(js|css|jpg|html|xml|svg)",
included: false, included: false,
@ -328,108 +189,27 @@ module.exports = function( grunt ) {
browsers: [ "IE" ], browsers: [ "IE" ],
singleRun: false 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 // Load grunt tasks from NPM packages
require( "load-grunt-tasks" )( grunt, { require( "load-grunt-tasks" )( grunt, {
pattern: nodeV16OrNewer ? [ "grunt-*" ] : [ "grunt-*", "!grunt-eslint" ] pattern: [ "grunt-*" ]
} ); } );
// Integrate jQuery specific tasks // Integrate jQuery specific tasks
grunt.loadTasks( "build/tasks" ); grunt.loadTasks( "build/grunt-tasks" );
grunt.registerTask( "print_old_node_message", ( ...args ) => { grunt.registerTask( "print_old_node_message", ( ...args ) => {
var task = args.join( ":" ); var task = args.join( ":" );
grunt.log.writeln( "Old Node.js detected, running the task \"" + task + "\" skipped..." ); 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.registerTask( "print_jsdom_message", () => {
grunt.log.writeln( "Node.js 17 or newer detected, skipping jsdom tests..." ); grunt.log.writeln( "Node.js 17 or newer detected, skipping jsdom tests..." );
} ); } );
grunt.registerTask( "lint", [ grunt.registerTask( "test:jsdom", [
"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" ),
// Support: Node.js 17+ // Support: Node.js 17+
// jsdom fails to connect to the Karma server in Node 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" ) nodeV17OrNewer ? "print_jsdom_message" : runIfNewNode( "karma:jsdom" )
] ); ] );
grunt.registerTask( "test:prepare", [
"npmcopy",
"qunit_fixture",
"babel:tests"
] );
grunt.registerTask( "test", [ grunt.registerTask( "test", [
"test:prepare", "test:jsdom"
"test:fast",
"test:slow"
] );
grunt.registerTask( "dev", [
"build:*:*",
runIfNewNode( "newer:eslint:dev" ),
"newer:minify",
"dist:*",
"qunit_fixture",
"compare_size"
] ); ] );
grunt.registerTask( "default", [ grunt.registerTask( "default", [
runIfNewNode( "eslint:dev" ), "test"
"build-all-variants",
"test:prepare",
runIfNewNode( "eslint:dist" ),
"test:fast",
"compare_size"
] ); ] );
}; };

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). 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 ```bash
cd jquery && npm run build cd jquery
``` npm install
The built version of jQuery will be put in the `dist/` subdirectory, along with the minified copy and associated map file. npm run build
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
``` ```
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: The built version of jQuery will be placed in the `dist/` directory, along with a minified copy and associated map file.
```
grunt ## 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: 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.
```
grunt -help `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 ### Modules
Special builds can be created that exclude subsets of jQuery functionality. 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.
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.
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 or included are:
Some example modules that can be excluded are:
- **ajax**: All AJAX functionality: `$.ajax()`, `$.get()`, `$.post()`, `$.ajaxSetup()`, `.load()`, transports, and ajax event shorthands such as `.ajaxStart()`. - **ajax**: All AJAX functionality: `$.ajax()`, `$.get()`, `$.post()`, `$.ajaxSetup()`, `.load()`, transports, and ajax event shorthands such as `.ajaxStart()`.
- **ajax/xhr**: The XMLHTTPRequest AJAX transport only. - **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. - **offset**: The `.offset()`, `.position()`, `.offsetParent()`, `.scrollLeft()`, and `.scrollTop()` methods.
- **wrap**: The `.wrap()`, `.wrapAll()`, `.wrapInner()`, and `.unwrap()` 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. - **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/global**: Exclude the attachment of global jQuery variables ($ and jQuery) to the window.
- **exports/amd**: Exclude the AMD definition. - **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 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.
- **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.
*Note*: Excluding the full `selector` module will also exclude all jQuery selector extensions (such as `effects/animatedSelector` and `css/hiddenVisibleSelectors`). *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 ##### 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 ```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 ```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 ```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 ##### 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: 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 ```bash
grunt custom --esm npm run build -- --filename=jquery.module.js --esm
```
The default is `script` but you can also pass it explicitly via `--no-esm`:
```bash
grunt custom --no-esm
``` ```
#### Custom Build Examples #### Custom Build Examples
To create a custom build, first check out the version: 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.
```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.
Exclude all **ajax** functionality: Exclude all **ajax** functionality:
```bash ```bash
grunt custom:-ajax npm run build -- --exclude=ajax
``` ```
Excluding **css** removes modules depending on CSS: **effects**, **offset**, **dimensions**. Excluding **css** removes modules depending on CSS: **effects**, **offset**, **dimensions**.
```bash ```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 ```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 ```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 Running the Unit Tests
-------------------------------------- --------------------------------------
@ -197,10 +188,10 @@ Make sure you have the necessary dependencies:
npm install 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 ```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 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"; "use strict";
module.exports = function( grunt ) { const fs = require( "fs" );
const fs = require( "fs" ); const path = require( "path" );
const path = require( "path" ); const util = require( "util" );
const rollup = require( "rollup" ); const exec = util.promisify( require( "child_process" ).exec );
const slimBuildFlags = require( "./lib/slim-build-flags" ); const rollup = require( "rollup" );
const rollupFileOverrides = require( "./lib/rollup-plugin-file-overrides" ); const excludedFromSlim = require( "./lib/slim-exclude" );
const srcFolder = path.resolve( `${ __dirname }/../../src` ); const rollupFileOverrides = require( "./lib/rollup-plugin-file-overrides" );
const read = function( fileName ) { const pkg = require( "../../package.json" );
return grunt.file.read( `${ srcFolder }/${ fileName }` ); 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 inputFileName = "jquery.js"; const minimum = [ "core" ];
const inputRollupOptions = {
input: `${ srcFolder }/${ inputFileName }`
};
function getOutputRollupOptions( { // Exclude specified modules if the module matching the key is removed
esm = false const removeWith = {
} = {} ) { ajax: [ "manipulation/_evalUrl", "deprecated/ajax-event-alias" ],
const wrapperFileName = `wrapper${ esm ? "-esm" : "" }.js`; 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" ]
};
async function read( filename ) {
return fs.promises.readFile( path.join( srcFolder, filename ), "utf8" );
}
// 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 // Catch `// @CODE` and subsequent comment lines event if they don't start
// in the first column. // in the first column.
const wrapper = read( wrapperFileName ) const wrapper = wrapperSource.split(
.split( /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/ ); /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/
);
return { return {
@ -39,276 +88,60 @@ module.exports = function( grunt ) {
// generate any extra wrappers so there's nothing for us to remove. // generate any extra wrappers so there's nothing for us to remove.
format: "esm", format: "esm",
intro: `${ wrapper[ 0 ].replace( /\n*$/, "" ) }`, intro: wrapper[ 0 ].replace( /\n*$/, "" ),
outro: wrapper[ 1 ].replace( /^\n*/, "" ) outro: wrapper[ 1 ].replace( /^\n*/, "" )
}; };
} }
const fileOverrides = new Map(); const fileOverrides = new Map();
function getOverride( filePath ) { function setOverride( filePath, source ) {
return fileOverrides.get( path.resolve( filePath ) );
}
function setOverride( filePath, source ) {
// We want normalized paths in overrides as they will be matched // We want normalized paths in overrides as they will be matched
// against normalized paths in the file overrides Rollup plugin. // against normalized paths in the file overrides Rollup plugin.
fileOverrides.set( path.resolve( filePath ), source ); fileOverrides.set( path.resolve( filePath ), source );
}
function unique( array ) {
return [ ...new Set( array ) ];
}
async function checkExclude( exclude, include ) {
const included = [ ...include ];
const excluded = [ ...exclude ];
for ( const module of exclude ) {
if ( minimum.indexOf( module ) !== -1 ) {
throw new Error( `Module \"${module}\" is a minimum requirement.` );
} }
grunt.registerMultiTask( // Exclude all files in the dir of the same name
"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;
}
}
/**
* 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;
}
// 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
// These are the removable dependencies // These are the removable dependencies
// It's fine if the directory is not there // It's fine if the directory is not there
try {
// `selector` is a special case as we don't just remove // `selector` is a special case as we don't just remove
// the module, but we replace it with `selector-native` // 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" ) { if ( module !== "selector" ) {
excludeList( const files = await readdirRecursive( module );
fs.readdirSync( `${ srcFolder }/${ module }` ), excluded.push( ...files );
module
);
} }
} catch ( e ) {
grunt.verbose.writeln( e );
}
}
additional = removeWith[ module ];
// Check removeWith list // Check removeWith list
const additional = removeWith[ module ];
if ( additional ) { if ( additional ) {
excludeList( additional.remove || additional ); const [ additionalExcluded, additionalIncluded ] = await checkExclude(
if ( additional.include ) { additional.remove || additional,
included.push( ...additional.include ); additional.include || []
grunt.log.writeln( "+" + additional.include );
}
}
} 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 );
}
// 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" ) );
// 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() {};" );
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 );
} else {
grunt.log.writeln( "AMD name now anonymous" );
}
// Remove the comma for anonymous defines
setOverride( `${ srcFolder }/exports/amd.js`,
read( "exports/amd.js" )
.replace( /(\s*)"jquery"(,\s*)/,
amdName ? "$1\"" + amdName + "\"$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 ) {
version += " -" + excluded.join( ",-" );
}
if ( excluded.length ) {
// 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 );
// Replace excluded modules with empty sources.
for ( const module of excluded ) {
setOverride(
`${ srcFolder }/${ module }.js`,
// The `selector` module is not removed, but replaced
// with `selector-native`.
module === "selector" ? read( "selector-native.js" ) : ""
); );
excluded.push( ...additionalExcluded );
included.push( ...additionalIncluded );
} }
} }
// Turn off opt-in if necessary return [ unique( excluded ), unique( included ) ];
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,
plugins: [ rollupFileOverrides( fileOverrides ) ]
} );
const outputRollupOptions =
getOutputRollupOptions( { esm } );
const { output: [ { code } ] } = await bundle.generate( outputRollupOptions );
async function writeCompiled( { code, dir, filename, version } ) {
const compiledContents = code const compiledContents = code
// Embed Version // Embed Version
@ -316,40 +149,213 @@ module.exports = function( grunt ) {
// Embed Date // Embed Date
// yyyy-mm-ddThh:mmZ // yyyy-mm-ddThh:mmZ
.replace( .replace( /@DATE/g, new Date().toISOString().replace( /:\d+\.\d+Z$/, "Z" ) );
/@DATE/g,
( new Date() ).toISOString() await fs.promises.writeFile( path.join( dir, filename ), compiledContents );
.replace( /:\d+\.\d+Z$/, "Z" ) 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
); );
grunt.file.write( name, compiledContents ); // Replace exports/global with a noop noConflict
grunt.log.ok( `File '${ name }' created.` ); if ( excluded.includes( "exports/global" ) ) {
done(); const index = excluded.indexOf( "exports/global" );
} catch ( err ) { setOverride(
done( err ); `${srcFolder}/exports/global.js`,
"import { jQuery } from \"../core.js\";\n\n" +
"jQuery.noConflict = function() {};"
);
excluded.splice( index, 1 );
}
// Set a desired AMD name.
if ( amd != null ) {
if ( amd ) {
console.log( "Naming jQuery with AMD name: " + amd );
} else {
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
/(\s*)"jquery"(,\s*)/,
amd ? `$1\"${amd}\"$2` : " "
)
);
}
// Append excluded modules to version.
// Skip adding exclusions for slim builds.
// Don't worry about semver syntax for these.
if ( !pureSlim && excluded.length ) {
version += " -" + excluded.join( ",-" );
}
// Append extra included modules to version.
if ( !pureSlim && included.length ) {
version += " +" + included.join( ",+" );
}
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 ) {
setOverride(
`${srcFolder}/${module}.js`,
// The `selector` module is not removed, but replaced
// with `selector-native`.
module === "selector" ? await read( "selector-native.js" ) : ""
);
}
const bundle = await rollup.rollup( {
...inputOptions,
plugins: [ rollupFileOverrides( fileOverrides ) ]
} );
const outputOptions = await getOutputRollupOptions( { esm } );
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 watcher.on( "event", async( event ) => {
// Translation example switch ( event.code ) {
// case "ERROR":
// grunt custom:+ajax,-dimensions,-effects,-offset console.error( event.error );
// break;
// Becomes: case "BUNDLE_END":
// const {
// grunt build:*:*:+ajax:-dimensions:-effects:-offset output: [ { code } ]
// } = await event.result.generate( outputOptions );
// 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( ":" ) :
"";
grunt.log.writeln( "Creating custom build...\n" ); await writeCompiled( {
grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "minify", "dist" ] ); 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"; "use strict";
module.exports = function( grunt ) { // Process files for distribution.
const fs = require( "fs" ); module.exports = async function processForDist( text, filename ) {
const filename = grunt.option( "filename" ); if ( !text ) {
const distFolder = grunt.option( "dist-folder" ); throw new Error( "text required for processForDist" );
const distPaths = [ }
`${ distFolder }/${ filename }`,
`${ distFolder }/${ filename.replace( ".js", ".min.js" ) }`,
`${ distFolder }/${ filename.replace( ".js", ".min.map" ) }`
];
// Process files for distribution if ( !filename ) {
grunt.registerTask( "dist", function() { throw new Error( "filename required for processForDist" );
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" );
// Ensure files use only \n for line endings, not \r\n // Ensure files use only \n for line endings, not \r\n
if ( /\x0d\x0a/.test( text ) ) { if ( /\x0d\x0a/.test( text ) ) {
grunt.log.writeln( filename + ": Incorrect line endings (\\r\\n)" ); throw new Error( filename + ": Incorrect line endings (\\r\\n)" );
nonascii = true;
} }
// Ensure only ASCII chars so script tags don't need a charset attribute // Ensure only ASCII chars so script tags don't need a charset attribute
if ( text.length !== Buffer.byteLength( text, "utf8" ) ) { if ( text.length !== Buffer.byteLength( text, "utf8" ) ) {
grunt.log.writeln( filename + ": Non-ASCII characters detected:" ); let message = filename + ": Non-ASCII characters detected:\n";
for ( i = 0; i < text.length; i++ ) { for ( let i = 0; i < text.length; i++ ) {
c = text.charCodeAt( i ); const c = text.charCodeAt( i );
if ( c > 127 ) { if ( c > 127 ) {
grunt.log.writeln( "- position " + i + ": " + c ); message += "- position " + i + ": " + c + "\n";
grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) ); message += "==> " + text.substring( i - 20, i + 20 );
break; 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 // NOTE: keep it in sync with test/data/testinit.js
module.exports = [ module.exports = [
"-ajax", "ajax",
"-callbacks", "callbacks",
"-deferred", "deferred",
"-effects", "effects",
"-queue" "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"; "use strict";
module.exports = ( grunt ) => { const swc = require( "@swc/core" );
const swc = require( "@swc/core" ); const fs = require( "fs" );
const path = require( "path" );
const processForDist = require( "./dist" );
const getTimestamp = require( "./lib/getTimestamp" );
grunt.registerMultiTask( const rjs = /\.js$/;
"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 || {};
await Promise.all( this.files.map( async( { src, dest } ) => { module.exports = async function minify( { filename, dir, esm } ) {
if ( src.length !== 1 ) { const contents = await fs.promises.readFile( path.join( dir, filename ), "utf8" );
grunt.fatal( "The minify task requires a single source per destination" ); const version = /jQuery JavaScript Library ([^\n]+)/.exec( contents )[ 1 ];
}
const { code, map: incompleteMap } = await swc.minify( 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, inlineSourcesContent: false,
sourceMap: sourceMapFilename ? sourceMap: true
{
filename: sourceMapFilename
} :
false
} }
); );
// Can't seem to get SWC to not use CRLF on Windows, so replace them with LF. const minFilename = filename.replace( rjs, ".min.js" );
grunt.file.write( dest, code.replace( /\r\n/g, "\n" ) ); const mapFilename = filename.replace( rjs, ".min.map" );
if ( sourceMapFilename ) { // The map's `files` & `sources` property are set incorrectly, fix
// them via overrides from the task config.
// Apply map overrides if needed. See the task config description // See https://github.com/swc-project/swc/issues/7588#issuecomment-1624345254
// for more details. const map = JSON.stringify( {
const mapObject = {
...JSON.parse( incompleteMap ), ...JSON.parse( incompleteMap ),
...sourceMapOverrides file: minFilename,
}; sources: [ filename ]
const map = JSON.stringify( mapObject ); } );
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"; "use strict";
module.exports = ( grunt ) => { const fs = require( "fs" );
const fs = require( "fs" ); const util = require( "util" );
const spawnTest = require( "./lib/spawn_test.js" ); const exec = util.promisify( require( "child_process" ).exec );
const nodeV16OrNewer = !/^v1[0-5]\./.test( process.version ); const verifyNodeVersion = require( "./lib/verifyNodeVersion" );
grunt.registerTask( "node_smoke_tests", function( moduleType, jQueryModuleSpecifier ) { const allowedModules = [ "commonjs", "module" ];
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." );
}
if ( !nodeV16OrNewer ) { if ( !verifyNodeVersion() ) {
grunt.log.writeln( "Old Node.js detected, running the task " +
`"node_smoke_tests:${ moduleType }:${ jQueryModuleSpecifier }" skipped...` );
return; return;
}
// 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.
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 testsDir = `./test/node_smoke_tests/${ moduleType }`; const files = await fs.promises.readdir( dir, { withFileTypes: true } );
const nodeSmokeTests = []; const testFiles = files.filter( ( testFilePath ) => testFilePath.isFile() );
await Promise.all(
// Fire up all tests defined in test/node_smoke_tests/*.js in spawned sub-processes. testFiles.map( ( testFile ) =>
// All the files under test/node_smoke_tests/*.js are supposed to exit with 0 code exec( `node "${dir}/${testFile.name}" "${module}"` )
// 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 )
) )
.forEach( ( testFilePath ) => { );
const taskName = `node_${ testFilePath.replace( /\.[cm]?js$/, "" ) }:${ moduleType }:${ jQueryModuleSpecifier }`; console.log( `Node smoke tests passed for ${sourceType} "${module}".` );
}
grunt.registerTask( taskName, function() { async function runDefaultTests() {
spawnTest( this.async(), `node "${ testsDir }/${ await Promise.all( [
testFilePath }" ${ jQueryModuleSpecifier }` ); 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 ); runDefaultTests();
} );
grunt.task.run( nodeSmokeTests );
} );
};

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

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" "./src/*.js": "./src/*.js"
}, },
"main": "dist/jquery.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", "homepage": "https://jquery.com",
"author": { "author": {
"name": "OpenJS Foundation and other contributors", "name": "OpenJS Foundation and other contributors",
@ -50,28 +75,27 @@
}, },
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@babel/cli": "7.22.9",
"@babel/core": "7.10.5", "@babel/core": "7.10.5",
"@babel/plugin-transform-for-of": "7.10.4", "@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", "bootstrap": "5.3.0",
"chalk": "5.3.0",
"colors": "1.4.0", "colors": "1.4.0",
"commitplease": "3.2.0", "commitplease": "3.2.0",
"concurrently": "8.2.0",
"core-js-bundle": "3.6.5", "core-js-bundle": "3.6.5",
"eslint-config-jquery": "3.0.0", "eslint": "8.44.0",
"eslint-plugin-import": "2.25.2", "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": "1.5.3",
"grunt-babel": "8.0.0",
"grunt-cli": "1.4.3", "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-git-authors": "3.2.0",
"grunt-jsonlint": "2.1.2",
"grunt-karma": "4.0.2", "grunt-karma": "4.0.2",
"grunt-newer": "1.3.0", "husky": "8.0.3",
"grunt-npmcopy": "0.2.0",
"gzip-js": "0.3.2",
"husky": "4.2.5",
"jsdom": "19.0.0", "jsdom": "19.0.0",
"karma": "6.4.1", "karma": "6.4.1",
"karma-browserstack-launcher": "1.6.0", "karma-browserstack-launcher": "1.6.0",
@ -94,25 +118,8 @@
"rollup": "2.21.0", "rollup": "2.21.0",
"sinon": "7.3.1", "sinon": "7.3.1",
"strip-json-comments": "3.1.1", "strip-json-comments": "3.1.1",
"testswarm": "1.1.2" "testswarm": "1.1.2",
}, "yargs": "17.7.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"
}, },
"commitplease": { "commitplease": {
"nohook": true, "nohook": true,
@ -143,11 +150,5 @@
], ],
"markerPattern": "^((clos|fix|resolv)(e[sd]|ing))|^(refs?)", "markerPattern": "^((clos|fix|resolv)(e[sd]|ing))|^(refs?)",
"ticketPattern": "^((Closes|Fixes) ([a-zA-Z]{2,}-)[0-9]+)|^(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

@ -5,16 +5,16 @@
<link rel="stylesheet" href="../../../external/bootstrap/bootstrap.min.css" class="stylesheet"> <link rel="stylesheet" href="../../../external/bootstrap/bootstrap.min.css" class="stylesheet">
</head> </head>
<body> <body>
<div> <div>
<script src="../../jquery.js"></script> <script src="../../jquery.js"></script>
<script src="../iframeTest.js"></script> <script src="../iframeTest.js"></script>
<script src="getComputedSupport.js"></script> <script src="getComputedSupport.js"></script>
</div> </div>
<script> <script>
startIframeTest( startIframeTest(
getComputedStyle( document.body ), getComputedStyle( document.body ),
getComputedSupport( jQuery.support ) getComputedSupport( jQuery.support )
); );
</script> </script>
</body> </body>
</html> </html>

View File

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

View File

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

View File

@ -1,5 +1,4 @@
( function() { ( function() {
"use strict"; "use strict";
// Store the old count so that we only assert on tests that have actually leaked, // 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>" ); resp.end( "<root><element /></root>" );
}, },
script: function( req, resp ) { script: function( req, resp ) {
const headers = {};
if ( req.query.header === "ecma" ) { if ( req.query.header === "ecma" ) {
resp.writeHead( 200, { "content-type": "application/ecmascript" } ); headers[ "content-type" ] = "application/ecmascript";
} else if ( "header" in req.query ) { } else if ( "header" in req.query ) {
resp.writeHead( 200, { "content-type": "text/javascript" } ); headers[ "content-type" ] = "text/javascript";
} else { } else {
resp.writeHead( 200, { "content-type": "text/html" } ); headers[ "content-type" ] = "text/html";
} }
if ( req.query.cors ) { 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 ) { if ( req.query.callback ) {
@ -165,14 +174,25 @@ const mocks = {
} }
}, },
headers: function( req, resp ) { headers: function( req, resp ) {
resp.writeHead( 200, { const headers = {
"Sample-Header": "Hello World", "Sample-Header": "Hello World",
"Empty-Header": "", "Empty-Header": "",
"Sample-Header2": "Hello World 2", "Sample-Header2": "Hello World 2",
"List-Header": "Item 1", "List-Header": "Item 1",
"list-header": "Item 2", "list-header": "Item 2",
"constructor": "prototype collision (constructor)" "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 ) { req.query.keys.split( "|" ).forEach( function( key ) {
if ( key.toLowerCase() in req.headers ) { if ( key.toLowerCase() in req.headers ) {
resp.write( `${ key }: ${ req.headers[ key.toLowerCase() ] }\n` ); resp.write( `${ key }: ${ req.headers[ key.toLowerCase() ] }\n` );
@ -354,7 +374,7 @@ function MockserverMiddlewareFactory() {
parsed: parsed parsed: parsed
} ); } );
if ( /^test\/data\/mock.php\//.test( path ) ) { if ( /^\/?test\/data\/mock.php\/?/.test( path ) ) {
// Support REST-like Apache PathInfo // Support REST-like Apache PathInfo
path = "test\/data\/mock.php"; path = "test\/data\/mock.php";
@ -365,6 +385,7 @@ function MockserverMiddlewareFactory() {
return; return;
} }
// console.log( "Mock handling", req.method, parsed.href );
handlers[ path ]( subReq, resp, next ); 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, "..", "..", "..", ".." ); 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 // If `jQueryModuleSpecifier` is a real relative path, make it absolute
// to make sure it resolves to the same file inside utils from // 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` // a subdirectory. Otherwise, leave it as-is as we may be testing `exports`
@ -16,6 +21,6 @@ export const getJQueryModuleSpecifier = () => {
} }
return jQueryModuleInputSpecifier.startsWith( "." ) ? return jQueryModuleInputSpecifier.startsWith( "." ) ?
path.resolve( ROOT_DIR, jQueryModuleInputSpecifier ) : ensureUnixPath( path.resolve( ROOT_DIR, jQueryModuleInputSpecifier ) ) :
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, cache: false,
beforeSend: function( xhr, settings ) { beforeSend: function( xhr, settings ) {
// Clear the cache-buster param value // Clear the cache-buster param value
var url = settings.url.replace( /_=[^&#]+/, "_=" ); var url = settings.url.replace( /_=[^&#]+/, "_=" );
assert.equal( url, baseURL + "name.html?abc&devo=hat&_=#brownies", assert.equal( url, baseURL + "name.html?abc&devo=hat&_=#brownies",
@ -873,8 +874,8 @@ QUnit.module( "ajax", {
success: function( data ) { success: function( data ) {
assert.ok( data.match( /^html text/ ), "Check content for datatype html" ); assert.ok( data.match( /^html text/ ), "Check content for datatype html" );
jQuery( "#ap" ).html( data ); jQuery( "#ap" ).html( data );
assert.strictEqual( window[ "testFoo" ], "foo", "Check if script 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" ); assert.strictEqual( window.testBar, "bar", "Check if script src was evaluated for datatype html" );
} }
}; };
} ); } );
@ -884,6 +885,7 @@ QUnit.module( "ajax", {
return { return {
dataType: "jsonp", dataType: "jsonp",
url: url( "mock.php?action=errorWithScript" ), url: url( "mock.php?action=errorWithScript" ),
// error is the significant assertion // error is the significant assertion
error: function( xhr ) { error: function( xhr ) {
var expected = { "status": 404, "msg": "Not Found" }; var expected = { "status": 404, "msg": "Not Found" };
@ -912,6 +914,7 @@ QUnit.module( "ajax", {
complete: function() { complete: function() {
jQuery.globalEval = globalEval; jQuery.globalEval = globalEval;
}, },
// error is the significant assertion // error is the significant assertion
error: function( xhr ) { error: function( xhr ) {
assert.strictEqual( xhr.status, 404, testMsg ); assert.strictEqual( xhr.status, 404, testMsg );
@ -1171,8 +1174,8 @@ QUnit.module( "ajax", {
Globals.register( "functionToCleanUp" ); Globals.register( "functionToCleanUp" );
Globals.register( "XXX" ); Globals.register( "XXX" );
Globals.register( "jsonpResults" ); Globals.register( "jsonpResults" );
window[ "jsonpResults" ] = function( data ) { window.jsonpResults = function( data ) {
assert.ok( data[ "data" ], "JSON results returned (GET, custom callback function)" ); assert.ok( data.data, "JSON results returned (GET, custom callback function)" );
}; };
}, },
requests: [ { requests: [ {
@ -1181,7 +1184,7 @@ QUnit.module( "ajax", {
crossDomain: crossDomain, crossDomain: crossDomain,
jsonp: "callback", jsonp: "callback",
success: function( data ) { 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", url: baseURL + "mock.php?action=jsonp",
@ -1190,7 +1193,7 @@ QUnit.module( "ajax", {
jsonpCallback: "jsonpResults", jsonpCallback: "jsonpResults",
success: function( data ) { success: function( data ) {
assert.strictEqual( assert.strictEqual(
typeof window[ "jsonpResults" ], typeof window.jsonpResults,
"function", "function",
"should not rewrite original function" "should not rewrite original function"
); );
@ -1202,8 +1205,8 @@ QUnit.module( "ajax", {
crossDomain: crossDomain, crossDomain: crossDomain,
jsonpCallback: "functionToCleanUp", jsonpCallback: "functionToCleanUp",
success: function( data ) { success: function( data ) {
assert.ok( data[ "data" ], "JSON results returned (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)" ); assert.strictEqual( window.functionToCleanUp, true, "Callback was removed (GET, custom callback name to be cleaned up)" );
var xhr; var xhr;
jQuery.ajax( { jQuery.ajax( {
url: baseURL + "mock.php?action=jsonp", url: baseURL + "mock.php?action=jsonp",
@ -1217,7 +1220,7 @@ QUnit.module( "ajax", {
} ); } );
xhr.fail( function() { xhr.fail( function() {
assert.ok( true, "Ajax error JSON (GET, custom callback name to be cleaned up)" ); 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)" ); 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 ) { 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", dataType: "jsonp",
crossDomain: crossDomain, crossDomain: crossDomain,
success: function( data ) { 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", dataType: "jsonp",
crossDomain: crossDomain, crossDomain: crossDomain,
success: function( data ) { 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", dataType: "jsonp",
crossDomain: crossDomain, crossDomain: crossDomain,
success: function( data ) { 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" ), url: url( "mock.php?action=testbar" ),
dataType: "script", dataType: "script",
success: function() { 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", type: "POST",
dataType: "script", dataType: "script",
success: function( data, status ) { 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)" ); assert.strictEqual( status, "success", "Script results returned (POST, no callback)" );
} }
}; };
@ -1492,7 +1495,7 @@ QUnit.module( "ajax", {
url: url( "mock.php?action=testbar" ), url: url( "mock.php?action=testbar" ),
dataType: "script", dataType: "script",
success: function() { 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 ) { success: function( json ) {
assert.ok( json.length >= 2, "Check length" ); assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
} }
}, },
{ {
@ -1538,10 +1541,10 @@ QUnit.module( "ajax", {
], ],
success: function( json ) { success: function( json ) {
assert.ok( json.length >= 2, "Check length" ); assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); 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" ); assert.strictEqual( typeof text, "string", "json wasn't auto-determined" );
var json = JSON.parse( text ); var json = JSON.parse( text );
assert.ok( json.length >= 2, "Check length" ); assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); 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" ); assert.strictEqual( typeof text, "string", "json wasn't auto-determined" );
var json = JSON.parse( text ); var json = JSON.parse( text );
assert.ok( json.length >= 2, "Check length" ); assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
} }
} }
]; ];
@ -1816,6 +1819,7 @@ QUnit.module( "ajax", {
var ifModifiedNow = new Date(); var ifModifiedNow = new Date();
jQuery.each( jQuery.each(
/* jQuery.each arguments start */ /* jQuery.each arguments start */
{ {
" (cache)": true, " (cache)": true,
@ -1870,6 +1874,7 @@ QUnit.module( "ajax", {
} }
); );
} }
/* jQuery.each arguments end */ /* jQuery.each arguments end */
); );
@ -1940,6 +1945,7 @@ QUnit.module( "ajax", {
} }
jQuery.each( jQuery.each(
/* jQuery.each arguments start */ /* jQuery.each arguments start */
{ {
"name.html": true, "name.html": true,
@ -2014,6 +2020,7 @@ QUnit.module( "ajax", {
} ); } );
} }
/* jQuery.each arguments end*/ /* jQuery.each arguments end*/
); );
} ); } );
@ -2815,10 +2822,10 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
}, },
function( json ) { function( json ) {
assert.ok( json.length >= 2, "Check length" ); assert.ok( json.length >= 2, "Check length" );
assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); assert.strictEqual( json[ 0 ].name, "John", "Check JSON: first, name" );
assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); assert.strictEqual( json[ 0 ].age, 21, "Check JSON: first, age" );
assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); assert.strictEqual( json[ 1 ].name, "Peter", "Check JSON: second, name" );
assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); assert.strictEqual( json[ 1 ].age, 25, "Check JSON: second, age" );
done(); done();
} }
); );
@ -2828,9 +2835,9 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
assert.expect( 2 ); assert.expect( 2 );
var done = assert.async(); var done = assert.async();
jQuery.getJSON( url( "mock.php?action=json" ), function( json ) { jQuery.getJSON( url( "mock.php?action=json" ), function( json ) {
if ( json && json[ "data" ] ) { if ( json && json.data ) {
assert.strictEqual( json[ "data" ][ "lang" ], "en", "Check JSON: lang" ); assert.strictEqual( json.data.lang, "en", "Check JSON: lang" );
assert.strictEqual( json[ "data" ].length, 25, "Check JSON: length" ); assert.strictEqual( json.data.length, 25, "Check JSON: length" );
done(); done();
} }
} ); } );
@ -2870,7 +2877,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
Globals.register( "testBar" ); Globals.register( "testBar" );
jQuery.getScript( url( "mock.php?action=testbar" ), function() { 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(); done();
} ); } );
} }
@ -2901,7 +2908,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
jQuery.getScript( { jQuery.getScript( {
url: url( "mock.php?action=testbar" ), url: url( "mock.php?action=testbar" ),
success: function() { success: function() {
assert.strictEqual( window[ "testBar" ], "bar", "Check if script was evaluated" ); assert.strictEqual( window.testBar, "bar", "Check if script was evaluated" );
done(); done();
} }
} ); } );
@ -3003,7 +3010,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
assert.expect( 7 ); assert.expect( 7 );
var done = assert.async(); var done = assert.async();
var verifyEvaluation = function() { 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" ); assert.strictEqual( jQuery( "#ap" ).html(), "bar", "Check if script evaluation has modified DOM" );
done(); 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() { 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.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( 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 ); setTimeout( verifyEvaluation, 600 );
} ); } );
} ); } );
@ -3026,7 +3033,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
jQuery( "#first" ).load( url( "test2.html" ), function() { jQuery( "#first" ).load( url( "test2.html" ), function() {
assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" ); 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(); done();
} ); } );
} ); } );

View File

@ -239,7 +239,7 @@ QUnit.test( "attr(Hash)", function( assert ) {
assert.equal( assert.equal(
jQuery( "#text1" ).attr( { jQuery( "#text1" ).attr( {
"value": function() { "value": function() {
return this[ "id" ]; return this.id;
} } ).attr( "value" ), } } ).attr( "value" ),
"text1", "text1",
"Set attribute to computed value #1" "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>" ); 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 = table.find( "td" ).eq( 0 );
td.attr( "rowspan", "2" ); 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" ); 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" ); 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." ); 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>", { $radio = jQuery( "<input>", {
"value": "sup", "value": "sup",
// Use uppercase here to ensure the type // Use uppercase here to ensure the type
// attrHook is still used // attrHook is still used
"TYPE": "radio" "TYPE": "radio"
@ -701,9 +702,9 @@ QUnit.test( "prop(String, Object) on null/undefined", function( assert ) {
$body = jQuery( body ); $body = jQuery( body );
assert.ok( $body.prop( "nextSibling" ) === null, "Make sure a null expando returns null" ); 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" ); 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" ); assert.ok( $body.prop( "foo" ) === undefined, "Make sure the expando is preferred over the dom attribute, even if undefined" );
select = document.createElement( "select" ); select = document.createElement( "select" );
@ -848,7 +849,7 @@ QUnit.test( "removeProp(String)", function( assert ) {
obj = {}; obj = {};
assert.strictEqual( assert.strictEqual(
jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[ 0 ][ "nonexisting" ], jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[ 0 ].nonexisting,
undefined, undefined,
"removeprop works correctly on DOM element nodes" "removeprop works correctly on DOM element nodes"
); );
@ -856,12 +857,12 @@ QUnit.test( "removeProp(String)", function( assert ) {
jQuery.each( [ document, obj ], function( i, ele ) { jQuery.each( [ document, obj ], function( i, ele ) {
var $ele = jQuery( ele ); var $ele = jQuery( ele );
$ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" ); $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 ) { jQuery.each( [ commentNode, textNode, attributeNode ], function( i, ele ) {
var $ele = jQuery( ele ); var $ele = jQuery( ele );
$ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" ); $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" ); 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" )[ 0 ].value, "cf", "Check value of select after form reset." );
assert.equal( jQuery( "#kkk" ).val(), "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" ), outputA = addToOutput( "A" ),
outputB = addToOutput( "B" ), outputB = addToOutput( "B" ),
outputC = addToOutput( "C" ), outputC = addToOutput( "C" ),
/* eslint-disable key-spacing */
tests = { tests = {
"": "XABC X XABCABCC X XBB X XABA X XX", "": "XABC X XABCABCC X XBB X XABA X XX",
"once": "XABC X X X X 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 // Return false
output = "X"; output = "X";
cblist = jQuery.Callbacks( flags ); cblist = jQuery.Callbacks( flags );
cblist.add( outputA, function() { return false; }, outputB ); cblist.add( outputA, function() {
return false;
}, outputB );
cblist.add( outputA ); cblist.add( outputA );
cblist.fire(); cblist.fire();
assert.strictEqual( output, results.shift(), "Callback returning false" ); assert.strictEqual( output, results.shift(), "Callback returning false" );
@ -269,7 +272,7 @@ QUnit.test( "jQuery.Callbacks( options ) - options are copied", function( assert
fn = function() { fn = function() {
assert.ok( !( count++ ), "called once" ); assert.ok( !( count++ ), "called once" );
}; };
options[ "unique" ] = false; options.unique = false;
cb.add( fn, fn ); cb.add( fn, fn );
cb.fire(); cb.fire();
} ); } );
@ -364,7 +367,9 @@ QUnit.test( "jQuery.Callbacks() - disabled callback doesn't fire (gh-1790)", fun
var cb = jQuery.Callbacks(), var cb = jQuery.Callbacks(),
fired = false, fired = false,
shot = function() { fired = true; }; shot = function() {
fired = true;
};
cb.disable(); cb.disable();
cb.empty(); cb.empty();
@ -379,8 +384,12 @@ QUnit.test( "jQuery.Callbacks() - list with memory stays locked (gh-3469)", func
var cb = jQuery.Callbacks( "memory" ), var cb = jQuery.Callbacks( "memory" ),
fired = 0, fired = 0,
count1 = function() { fired += 1; }, count1 = function() {
count2 = function() { fired += 10; }; fired += 1;
},
count2 = function() {
fired += 10;
};
cb.add( count1 ); cb.add( count1 );
cb.fire(); 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. // few here but beware of modular builds where these methods may be excluded.
if ( includesModule( "deprecated" ) ) { if ( includesModule( "deprecated" ) ) {
expected++; expected++;
attrObj[ "click" ] = function() { assert.ok( exec, "Click executed." ); }; attrObj.click = function() {
assert.ok( exec, "Click executed." );
};
} }
if ( includesModule( "dimensions" ) ) { if ( includesModule( "dimensions" ) ) {
expected++; expected++;
attrObj[ "width" ] = 10; attrObj.width = 10;
} }
if ( includesModule( "offset" ) ) { if ( includesModule( "offset" ) ) {
expected++; expected++;
attrObj[ "offset" ] = { "top": 1, "left": 1 }; attrObj.offset = { "top": 1, "left": 1 };
} }
if ( includesModule( "css" ) ) { if ( includesModule( "css" ) ) {
expected += 2; expected += 2;
attrObj[ "css" ] = { "paddingLeft": 1, "paddingRight": 1 }; attrObj.css = { "paddingLeft": 1, "paddingRight": 1 };
} }
if ( includesModule( "attributes" ) ) { if ( includesModule( "attributes" ) ) {
expected++; expected++;
@ -216,17 +218,17 @@ QUnit.test( "noConflict", function( assert ) {
var $$ = jQuery; var $$ = jQuery;
assert.strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" ); assert.strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" );
assert.strictEqual( window[ "jQuery" ], $$, "Make sure jQuery wasn't touched." ); assert.strictEqual( window.jQuery, $$, "Make sure jQuery wasn't touched." );
assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." ); assert.strictEqual( window.$, original$, "Make sure $ was reverted." );
jQuery = $ = $$; jQuery = $ = $$;
assert.strictEqual( jQuery.noConflict( true ), $$, "noConflict returned the jQuery object" ); assert.strictEqual( jQuery.noConflict( true ), $$, "noConflict returned the jQuery object" );
assert.strictEqual( window[ "jQuery" ], originaljQuery, "Make sure jQuery was reverted." ); assert.strictEqual( window.jQuery, originaljQuery, "Make sure jQuery was reverted." );
assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." ); assert.strictEqual( window.$, original$, "Make sure $ was reverted." );
assert.ok( $$().pushStack( [] ), "Make sure that jQuery still works." ); assert.ok( $$().pushStack( [] ), "Make sure that jQuery still works." );
window[ "jQuery" ] = jQuery = $$; window.jQuery = jQuery = $$;
} ); } );
QUnit.test( "isPlainObject", function( assert ) { QUnit.test( "isPlainObject", function( assert ) {
@ -274,7 +276,7 @@ QUnit.test( "isPlainObject", function( assert ) {
// Makes the function a little more realistic // Makes the function a little more realistic
// (and harder to detect, incidentally) // (and harder to detect, incidentally)
fn.prototype[ "someMethod" ] = function() {}; fn.prototype.someMethod = function() {};
// Again, instantiated objects shouldn't be matched // Again, instantiated objects shouldn't be matched
assert.ok( !jQuery.isPlainObject( new fn() ), "new fn" ); assert.ok( !jQuery.isPlainObject( new fn() ), "new fn" );
@ -431,10 +433,10 @@ QUnit.test( "XSS via location.hash", function( assert ) {
var done = assert.async(); var done = assert.async();
assert.expect( 1 ); assert.expect( 1 );
jQuery[ "_check9521" ] = function( x ) { jQuery._check9521 = function( x ) {
assert.ok( x, "script called from #id-like selector with inline handler" ); assert.ok( x, "script called from #id-like selector with inline handler" );
jQuery( "#check9521" ).remove(); jQuery( "#check9521" ).remove();
delete jQuery[ "_check9521" ]; delete jQuery._check9521;
done(); done();
}; };
try { try {
@ -442,7 +444,7 @@ QUnit.test( "XSS via location.hash", function( assert ) {
// This throws an error because it's processed like an id // 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" ); jQuery( "#<img id='check9521' src='no-such-.gif' onerror='jQuery._check9521(false)'>" ).appendTo( "#qunit-fixture" );
} catch ( err ) { } catch ( err ) {
jQuery[ "_check9521" ]( true ); jQuery._check9521( true );
} }
} ); } );
@ -451,12 +453,12 @@ QUnit.test( "jQuery('html')", function( assert ) {
var s, div, j; var s, div, j;
jQuery[ "foo" ] = false; jQuery.foo = false;
s = jQuery( "<script>jQuery.foo='test';</script>" )[ 0 ]; s = jQuery( "<script>jQuery.foo='test';</script>" )[ 0 ];
assert.ok( s, "Creating a script" ); 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>" ); 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 // Test multi-line HTML
div = jQuery( "<div>\r\nsome text\n<p>some p</p>\nmore text\r\n</div>" )[ 0 ]; 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; var div, pass, i;
div = jQuery( "div" ); div = jQuery( "div" );
div.each( function() {this.foo = "zoo";} ); div.each( function() {
this.foo = "zoo";
} );
pass = true; pass = true;
for ( i = 0; i < div.length; i++ ) { for ( i = 0; i < div.length; i++ ) {
if ( div.get( i ).foo !== "zoo" ) { if ( div.get( i ).foo !== "zoo" ) {
@ -740,8 +744,12 @@ QUnit.test( "jQuery.map", function( assert ) {
result = { result = {
Zero: function() {}, Zero: function() {},
One: function( a ) { a = a; }, One: function( a ) {
Two: function( a, b ) { a = a; b = b; } a = a;
},
Two: function( a, b ) {
a = a; b = b;
}
}; };
callback = function( v, k ) { callback = function( v, k ) {
assert.equal( k, "foo", label + "-argument function treated like object" ); 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" ); assert.deepEqual( options, optionsCopy, "Check if not modified: options must not be modified" );
jQuery.extend( true, deep1, deep2 ); jQuery.extend( true, deep1, deep2 );
assert.deepEqual( deep1[ "foo" ], deepmerged[ "foo" ], "Check if foo: settings must be extended" ); 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.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.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 // trac-5991
assert.ok( Array.isArray( jQuery.extend( true, { "arr": {} }, nestedarray )[ "arr" ] ), "Cloned array have to be an Array" ); 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( jQuery.isPlainObject( jQuery.extend( true, { "arr": arr }, { "arr": {} } ).arr ), "Cloned object have to be an plain object" );
empty = {}; empty = {};
optionsWithLength = { "foo": { "length": -1 } }; optionsWithLength = { "foo": { "length": -1 } };
jQuery.extend( true, empty, optionsWithLength ); 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 = {}; empty = {};
optionsWithDate = { "foo": { "date": new Date() } }; optionsWithDate = { "foo": { "date": new Date() } };
jQuery.extend( true, empty, optionsWithDate ); jQuery.extend( true, empty, optionsWithDate );
assert.deepEqual( empty[ "foo" ], optionsWithDate[ "foo" ], "Dates copy correctly" ); assert.deepEqual( empty.foo, optionsWithDate.foo, "Dates copy correctly" );
/** @constructor */ /** @constructor */
myKlass = function() {}; myKlass = function() {};
@ -1025,13 +1033,13 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
optionsWithCustomObject = { "foo": { "date": customObject } }; optionsWithCustomObject = { "foo": { "date": customObject } };
empty = {}; empty = {};
jQuery.extend( true, empty, optionsWithCustomObject ); 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 // Makes the class a little more realistic
myKlass.prototype = { "someMethod": function() {} }; myKlass.prototype = { "someMethod": function() {} };
empty = {}; empty = {};
jQuery.extend( true, empty, optionsWithCustomObject ); 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; MyNumber = Number;
@ -1039,18 +1047,18 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
assert.ok( parseInt( ret.foo, 10 ) === 5, "Wrapped numbers copy correctly" ); assert.ok( parseInt( ret.foo, 10 ) === 5, "Wrapped numbers copy correctly" );
nullUndef = jQuery.extend( {}, options, { "xnumber2": null } ); 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 } ); 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 } ); 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 = {}; target = {};
recursive = { foo:target, bar:5 }; recursive = { foo: target, bar: 5 };
jQuery.extend( true, target, recursive ); jQuery.extend( true, target, recursive );
assert.deepEqual( target, { bar:5 }, "Check to make sure a recursive obj doesn't go never-ending loop by not copying it over" ); assert.deepEqual( target, { bar: 5 }, "Check to make sure a recursive obj doesn't go never-ending loop by not copying it over" );
ret = jQuery.extend( true, { foo: [] }, { foo: [ 0 ] } ); // 1907 ret = jQuery.extend( true, { foo: [] }, { foo: [ 0 ] } ); // 1907
assert.equal( ret.foo.length, 1, "Check to make sure a value with coercion 'false' copies over when necessary to fix trac-1907" ); assert.equal( ret.foo.length, 1, "Check to make sure a value with coercion 'false' copies over when necessary to fix trac-1907" );
@ -1058,11 +1066,11 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
ret = jQuery.extend( true, { foo: "1,2,3" }, { foo: [ 1, 2, 3 ] } ); ret = jQuery.extend( true, { foo: "1,2,3" }, { foo: [ 1, 2, 3 ] } );
assert.ok( typeof ret.foo !== "string", "Check to make sure values equal with coercion (but not actually equal) overwrite correctly" ); assert.ok( typeof ret.foo !== "string", "Check to make sure values equal with coercion (but not actually equal) overwrite correctly" );
ret = jQuery.extend( true, { foo:"bar" }, { foo:null } ); ret = jQuery.extend( true, { foo: "bar" }, { foo: null } );
assert.ok( typeof ret.foo !== "undefined", "Make sure a null value doesn't crash with deep extend, for trac-1908" ); assert.ok( typeof ret.foo !== "undefined", "Make sure a null value doesn't crash with deep extend, for trac-1908" );
obj = { foo:null }; obj = { foo: null };
jQuery.extend( true, obj, { foo:"notnull" } ); jQuery.extend( true, obj, { foo: "notnull" } );
assert.equal( obj.foo, "notnull", "Make sure a null value can be overwritten" ); assert.equal( obj.foo, "notnull", "Make sure a null value can be overwritten" );
function func() {} function func() {}
@ -1172,8 +1180,12 @@ QUnit.test( "jQuery.each(Object,Function)", function( assert ) {
seen = { seen = {
Zero: function() {}, Zero: function() {},
One: function( a ) { a = a; }, One: function( a ) {
Two: function( a, b ) { a = a; b = b; } a = a;
},
Two: function( a, b ) {
a = a; b = b;
}
}; };
callback = function( k ) { callback = function( k ) {
assert.equal( k, "foo", label + "-argument function treated like object" ); 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( 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" ); assert.equal( jQuery.makeArray( [ 1, 2, 3 ] ).join( "" ), "123", "Pass makeArray a real array" );
@ -1296,12 +1310,14 @@ QUnit.test( "jQuery.makeArray", function( assert ) {
assert.equal( jQuery.makeArray( document.createElement( "div" ) )[ 0 ].nodeName.toUpperCase(), "DIV", "Pass makeArray a single node" ); assert.equal( jQuery.makeArray( document.createElement( "div" ) )[ 0 ].nodeName.toUpperCase(), "DIV", "Pass makeArray a single node" );
assert.equal( jQuery.makeArray( { length:2, 0:"a", 1:"b" } ).join( "" ), "ab", "Pass makeArray an array like map (with length)" ); assert.equal( jQuery.makeArray( { length: 2, 0: "a", 1: "b" } ).join( "" ), "ab", "Pass makeArray an array like map (with length)" );
assert.ok( !!jQuery.makeArray( document.documentElement.childNodes ).slice( 0, 1 )[ 0 ].nodeName, "Pass makeArray a childNodes array" ); assert.ok( !!jQuery.makeArray( document.documentElement.childNodes ).slice( 0, 1 )[ 0 ].nodeName, "Pass makeArray a childNodes array" );
// function, is tricky as it has length // 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 //window, also has length
assert.equal( jQuery.makeArray( window )[ 0 ], window, "Pass makeArray the window" ); assert.equal( jQuery.makeArray( window )[ 0 ], window, "Pass makeArray the window" );
@ -1327,7 +1343,7 @@ QUnit.test( "jQuery.isEmptyObject", function( assert ) {
assert.expect( 2 ); assert.expect( 2 );
assert.equal( true, jQuery.isEmptyObject( {} ), "isEmptyObject on empty object literal" ); assert.equal( true, jQuery.isEmptyObject( {} ), "isEmptyObject on empty object literal" );
assert.equal( false, jQuery.isEmptyObject( { a:1 } ), "isEmptyObject on non-empty object literal" ); assert.equal( false, jQuery.isEmptyObject( { a: 1 } ), "isEmptyObject on non-empty object literal" );
// What about this ? // What about this ?
// equal(true, jQuery.isEmptyObject(null), "isEmptyObject on null" ); // equal(true, jQuery.isEmptyObject(null), "isEmptyObject on null" );

View File

@ -38,7 +38,7 @@ QUnit.test( "css(String|Hash)", function( assert ) {
width = parseFloat( jQuery( "#nothiddendiv" ).css( "width" ) ); width = parseFloat( jQuery( "#nothiddendiv" ).css( "width" ) );
height = parseFloat( jQuery( "#nothiddendiv" ).css( "height" ) ); height = parseFloat( jQuery( "#nothiddendiv" ).css( "height" ) );
jQuery( "#nothiddendiv" ).css( { "overflow":"hidden", "width": -1, "height": -1 } ); jQuery( "#nothiddendiv" ).css( { "overflow": "hidden", "width": -1, "height": -1 } );
assert.equal( parseFloat( jQuery( "#nothiddendiv" ).css( "width" ) ), 0, "Test negative width set to 0" ); assert.equal( parseFloat( jQuery( "#nothiddendiv" ).css( "width" ) ), 0, "Test negative width set to 0" );
assert.equal( parseFloat( jQuery( "#nothiddendiv" ).css( "height" ) ), 0, "Test negative height set to 0" ); assert.equal( parseFloat( jQuery( "#nothiddendiv" ).css( "height" ) ), 0, "Test negative height set to 0" );
@ -321,8 +321,7 @@ QUnit.test( "css(String, Object)", function( assert ) {
success = true; success = true;
try { try {
jQuery( "#foo" ).css( "backgroundColor", "rgba(0, 0, 0, 0.1)" ); jQuery( "#foo" ).css( "backgroundColor", "rgba(0, 0, 0, 0.1)" );
} } catch ( e ) {
catch ( e ) {
success = false; success = false;
} }
assert.ok( success, "Setting RGBA values does not throw Error (trac-5509)" ); 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 ); $div.css( "animation-iteration-count", 2 );
if ( $div.css( "animation-iteration-count" ) !== undefined ) { if ( $div.css( "animation-iteration-count" ) !== undefined ) {
// if $div.css( "animation-iteration-count" ) return "1", // if $div.css( "animation-iteration-count" ) return "1",
// it actually return the default value of animation-iteration-count // 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'" ); 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 ) { if ( transformName ) {
assert.equal( elemStyle[ transformName ], transformVal, "setting properly-prefixed transform" ); 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 ) { 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+ // Support: IE 11+
// This test requires Grid to be *not supported* to work. // This test requires Grid to be *not supported* to work.
if ( QUnit.isIE ) { if ( QUnit.isIE ) {
// Make sure explicitly provided IE vendor prefix (`-ms-`) is not converted // Make sure explicitly provided IE vendor prefix (`-ms-`) is not converted
// to a non-working `Ms` prefix in JavaScript. // to a non-working `Ms` prefix in JavaScript.
QUnit.test( "IE vendor prefixes are not mangled", function( assert ) { 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" ); 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.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" ); assert.strictEqual( jQuery.hasData( elem ), true, "jQuery.hasData agrees data exists when data exists" );
jQuery.data( elem, "foo", "baz" ); jQuery.data( elem, "foo", "baz" );
assert.equal( jQuery.data( elem, "foo" ), "baz", "Data can be changed by jQuery.data" ); 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 ); jQuery.data( elem, "foo", undefined );
assert.equal( jQuery.data( elem, "foo" ), "baz", "Data is not unset by passing undefined to jQuery.data" ); assert.equal( jQuery.data( elem, "foo" ), "baz", "Data is not unset by passing undefined to jQuery.data" );
@ -398,7 +398,7 @@ QUnit.test( "data-* attributes", function( assert ) {
break; break;
case 2: case 2:
assert.equal( jQuery( elem ).data( "zoooo" ), "bar", "Check zoooo property" ); assert.equal( jQuery( elem ).data( "zoooo" ), "bar", "Check zoooo property" );
assert.deepEqual( jQuery( elem ).data( "bar" ), { "test":"baz" }, "Check bar property" ); assert.deepEqual( jQuery( elem ).data( "bar" ), { "test": "baz" }, "Check bar property" );
break; break;
case 3: case 3:
assert.equal( jQuery( elem ).data( "number" ), true, "Check number property" ); assert.equal( jQuery( elem ).data( "number" ), true, "Check number property" );
@ -426,13 +426,13 @@ QUnit.test( ".data(Object)", function( assert ) {
assert.equal( div.data( "test" ), "in", "Verify setting an object in data" ); assert.equal( div.data( "test" ), "in", "Verify setting an object in data" );
assert.equal( div.data( "test2" ), "in2", "Verify setting an object in data" ); assert.equal( div.data( "test2" ), "in2", "Verify setting an object in data" );
obj = { test:"unset" }; obj = { test: "unset" };
jqobj = jQuery( obj ); jqobj = jQuery( obj );
jqobj.data( "test", "unset" ); jqobj.data( "test", "unset" );
jqobj.data( { "test": "in", "test2": "in2" } ); 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( 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( obj.test2, undefined, "Verify setting an object on an object does not extend the object" );
// manually clean up detached elements // manually clean up detached elements
div.remove(); 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>" ) 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" ); .prependTo( "body" );
assert.equal( div.data()[ "wTF" ], "ftw", "Verify single letter 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().bigALittleA, "bouncing-b", "Verify single letter mixed data-* key" );
assert.equal( div.data()[ "foo" ], "a", "Verify single 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().fooBar, "b", "Verify multiple word data-* key" );
assert.equal( div.data()[ "fooBarBaz" ], "c", "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( "foo" ), "a", "Verify single word data-* key" );
assert.equal( div.data( "fooBar" ), "b", "Verify multiple 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 ) { piped = defer.then( function( a, b ) {
return 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 ) { piped.done( function( result ) {
value3 = result; value3 = result;
@ -145,7 +147,9 @@ QUnit.test( "jQuery.Deferred.then - filtering (fail)", function( assert ) {
piped = defer.then( null, function( a, b ) { piped = defer.then( null, function( a, b ) {
return 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 ) { piped.done( function( result ) {
value3 = result; value3 = result;
@ -181,7 +185,9 @@ QUnit.test( "jQuery.Deferred.catch", function( assert ) {
piped = defer.catch( function( a, b ) { piped = defer.catch( function( a, b ) {
return 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 ) { piped.done( function( result ) {
value3 = 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 ) { piped = defer.pipe( null, function( a, b ) {
return 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 ) { piped.fail( function( result ) {
value3 = result; value3 = result;
@ -416,7 +424,9 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
var defer, piped, defer2, piped2, var defer, piped, defer2, piped2,
context = { custom: true }, 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 ) { jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
assert.strictEqual( this, context, "custom context received by .then handler" ); 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, var defer, piped, defer2, piped2,
context = { custom: true }, 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 ) { jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
assert.strictEqual( this, context, "custom context received by .pipe handler" ); 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" }, var context = { id: "callback context" },
thenable = jQuery.Deferred().resolve( "thenable fulfillment" ).promise(), 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; thenable.unwrapped = false;
@ -693,7 +707,9 @@ QUnit.test( "jQuery.Deferred.then - progress and thenables", function( assert )
var trigger = jQuery.Deferred().notify(), var trigger = jQuery.Deferred().notify(),
expectedProgress = [ "baz", "baz" ], 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 ) { failer = function( evt ) {
return function() { return function() {
assert.ok( false, "no unexpected " + evt ); assert.ok( false, "no unexpected " + evt );
@ -718,7 +734,9 @@ QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) {
assert.expect( 7 ); assert.expect( 7 );
var notifiedResolved = jQuery.Deferred().notify( "foo" )/*xxx .resolve( "bar" )*/, 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 ) { notifiedResolved.progress( function( v ) {
assert.strictEqual( v, "foo", "progress value" ); assert.strictEqual( v, "foo", "progress value" );
@ -798,7 +816,9 @@ QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert
assert.expect( 44 ); assert.expect( 44 );
var defaultContext = ( function getDefaultContext() { return this; } )(), var defaultContext = ( function getDefaultContext() {
return this;
} )(),
done = assert.async( 20 ); 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, numCases = Object.keys( willSucceed ).length + Object.keys( willError ).length,
defaultContext = ( function getDefaultContext() { return this; } )(), defaultContext = ( function getDefaultContext() {
return this;
} )(),
done = assert.async( numCases * 2 ); done = assert.async( numCases * 2 );
@ -992,7 +1014,9 @@ QUnit.test( "jQuery.when(a, b) - like Promise.all", function( assert ) {
rejectedStandardPromise: true rejectedStandardPromise: true
}, },
defaultContext = ( function getDefaultContext() { return this; } )(), defaultContext = ( function getDefaultContext() {
return this;
} )(),
done = assert.async( 98 ); done = assert.async( 98 );

View File

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

62
test/unit/effects.js vendored
View File

@ -8,8 +8,12 @@ if ( !includesModule( "effects" ) ) {
var fxInterval = 13, var fxInterval = 13,
oldRaf = window.requestAnimationFrame, oldRaf = window.requestAnimationFrame,
hideOptions = { hideOptions = {
inline: function() { jQuery.style( this, "display", "none" ); }, inline: function() {
cascade: function() { this.className = "hidden"; } jQuery.style( this, "display", "none" );
},
cascade: function() {
this.className = "hidden";
}
}; };
QUnit.module( "effects", { QUnit.module( "effects", {
@ -540,12 +544,12 @@ QUnit.test( "animate duration 0", function( assert ) {
assert.expect( 11 ); assert.expect( 11 );
var $elem, var $elem,
$elems = jQuery( [ { a:0 }, { a:0 } ] ), $elems = jQuery( [ { a: 0 }, { a: 0 } ] ),
counter = 0; counter = 0;
assert.equal( jQuery.timers.length, 0, "Make sure no animation was running from another test" ); assert.equal( jQuery.timers.length, 0, "Make sure no animation was running from another test" );
$elems.eq( 0 ).animate( { a:1 }, 0, function() { $elems.eq( 0 ).animate( { a: 1 }, 0, function() {
assert.ok( true, "Animate a simple property." ); assert.ok( true, "Animate a simple property." );
counter++; counter++;
} ); } );
@ -555,18 +559,18 @@ QUnit.test( "animate duration 0", function( assert ) {
assert.equal( counter, 1, "One synchronic animations" ); assert.equal( counter, 1, "One synchronic animations" );
$elems.animate( { a:2 }, 0, function() { $elems.animate( { a: 2 }, 0, function() {
assert.ok( true, "Animate a second simple property." ); assert.ok( true, "Animate a second simple property." );
counter++; counter++;
} ); } );
assert.equal( counter, 3, "Multiple synchronic animations" ); assert.equal( counter, 3, "Multiple synchronic animations" );
$elems.eq( 0 ).animate( { a:3 }, 0, function() { $elems.eq( 0 ).animate( { a: 3 }, 0, function() {
assert.ok( true, "Animate a third simple property." ); assert.ok( true, "Animate a third simple property." );
counter++; counter++;
} ); } );
$elems.eq( 1 ).animate( { a:3 }, fxInterval * 20, function() { $elems.eq( 1 ).animate( { a: 3 }, fxInterval * 20, function() {
counter++; counter++;
// Failed until [6115] // Failed until [6115]
@ -948,15 +952,33 @@ jQuery.each( {
num = 0; num = 0;
// TODO: uncrowd this // TODO: uncrowd this
if ( t_h === "show" ) { num++; } if ( t_h === "show" ) {
if ( t_w === "show" ) { num++; } num++;
if ( t_w === "hide" || t_w === "show" ) { num++; } }
if ( t_h === "hide" || t_h === "show" ) { num++; } if ( t_w === "show" ) {
if ( t_o === "hide" || t_o === "show" ) { num++; } num++;
if ( t_w === "hide" ) { num++; } }
if ( t_o.constructor === Number ) { num += 2; } if ( t_w === "hide" || t_w === "show" ) {
if ( t_w.constructor === Number ) { num += 2; } num++;
if ( t_h.constructor === Number ) { num += 2; } }
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 ); assert.expect( num );
@ -2457,7 +2479,9 @@ QUnit.test( "Show/hide/toggle and display: inline", function( assert ) {
.hide().show( N ), .hide().show( N ),
$el.clone().data( { call: "hide+toggle", done: "inline" } ).appendTo( fixture ) $el.clone().data( { call: "hide+toggle", done: "inline" } ).appendTo( fixture )
.hide().toggle( N ) .hide().toggle( N )
], function( $clone ) { return $clone[ 0 ]; } ); ], function( $clone ) {
return $clone[ 0 ];
} );
// Animations not allowed to complete // Animations not allowed to complete
interrupted = jQuery.map( [ 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: "toggle+stop" } ).appendTo( fixture ).toggle( N ),
$el.clone().data( { call: "hide+show+stop" } ).appendTo( fixture ).hide().show( 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 ) $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 // All elements should be inline-block during the animation
clock.tick( N / 2 ); 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 ) { div.on( "test.a", { x: 1 }, function( e ) {
assert.ok( !!e.xyz, "Make sure that the data is getting passed through." ); 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 ) { div.on( "test.b", { x: 2 }, function( e ) {
assert.ok( !!e.xyz, "Make sure that the data is getting passed through." ); 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 // Should trigger 5
@ -293,14 +293,14 @@ QUnit.test( "on/one/off(Object)", function( assert ) {
// Regular bind // Regular bind
.on( { .on( {
"click":handler, "click": handler,
"mouseover":handler "mouseover": handler
} ) } )
// Bind with data // Bind with data
.one( { .one( {
"click":handlerWithData, "click": handlerWithData,
"mouseover":handlerWithData "mouseover": handlerWithData
}, 2 ); }, 2 );
trigger(); trigger();
@ -313,8 +313,8 @@ QUnit.test( "on/one/off(Object)", function( assert ) {
assert.equal( mouseoverCounter, 4, "on(Object)" ); assert.equal( mouseoverCounter, 4, "on(Object)" );
jQuery( "#firstp" ).off( { jQuery( "#firstp" ).off( {
"click":handler, "click": handler,
"mouseover":handler "mouseover": handler
} ); } );
trigger(); trigger();
@ -553,7 +553,9 @@ QUnit.test( "on(), namespaced events, cloned events", function( assert ) {
} ).trigger( "tester" ); } ).trigger( "tester" );
// Make sure events stick with appendTo'd elements (which are cloned) trac-2027 // 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" ); 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 ); assert.expect( 3 );
var main = 0; var main = 0;
jQuery( "#qunit-fixture" ).on( "click", function() { main++; } ); jQuery( "#qunit-fixture" ).on( "click", function() {
main++;
} );
jQuery( "#ap" ).trigger( "click" ); jQuery( "#ap" ).trigger( "click" );
assert.equal( main, 1, "Verify that the trigger happened correctly." ); 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; var main = 0;
jQuery( "#qunit-fixture" ).on( "click", "#ap", function() { main++; } ); jQuery( "#qunit-fixture" ).on( "click", "#ap", function() {
main++;
} );
jQuery( "#ap" ).trigger( "click" ); jQuery( "#ap" ).trigger( "click" );
assert.equal( main, 1, "Verify that the trigger happened correctly." ); 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" ); events = jQuery._data( obj, "events" );
assert.ok( events, "Object has events bound." ); 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.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." ); 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 ] && assert.equal( obj && obj[ jQuery.expando ] &&
obj[ jQuery.expando ][ 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 ) { 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; var win = 0, doc = 0, html = 0, body = 0, main = 0, ap = 0;
jQuery( window ).on( "click", function() { win++; } ); jQuery( window ).on( "click", function() {
jQuery( document ).on( "click", function( e ) { if ( e.target !== document ) { doc++; } } ); win++;
jQuery( "html" ).on( "click", function() { html++; } ); } );
jQuery( "body" ).on( "click", function() { body++; } ); jQuery( document ).on( "click", function( e ) {
jQuery( "#qunit-fixture" ).on( "click", function() { main++; } ); if ( e.target !== document ) {
jQuery( "#ap" ).on( "click", function() { ap++; return false; } ); 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" ); jQuery( "html" ).trigger( "click" );
assert.equal( win, 1, "HTML bubble" ); assert.equal( win, 1, "HTML bubble" );
@ -1159,7 +1179,9 @@ QUnit.test( "trigger(eventObject, [data], [fn])", function( assert ) {
event.stopPropagation(); event.stopPropagation();
assert.equal( event.isPropagationStopped(), true, "Verify isPropagationStopped" ); assert.equal( event.isPropagationStopped(), true, "Verify isPropagationStopped" );
event.isPropagationStopped = function() { return false; }; event.isPropagationStopped = function() {
return false;
};
event.stopImmediatePropagation(); event.stopImmediatePropagation();
assert.equal( event.isPropagationStopped(), true, "Verify isPropagationStopped" ); assert.equal( event.isPropagationStopped(), true, "Verify isPropagationStopped" );
assert.equal( event.isImmediatePropagationStopped(), 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; return false;
} ) } )
.find( "#myform input[type=submit]" ) .find( "#myform input[type=submit]" )
.each( function() { this.click(); } ) .each( function() {
this.click();
} )
.end() .end()
.on( "submit", function() { .on( "submit", function() {
assert.ok( false, "submit bubbled on second handler" ); 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, var event, clicked, hash, called, livec, lived, livee,
submit = 0, div = 0, livea = 0, liveb = 0; submit = 0, div = 0, livea = 0, liveb = 0;
jQuery( "#body" ).on( "submit", "#qunit-fixture div", function() { submit++; return false; } ); jQuery( "#body" ).on( "submit", "#qunit-fixture div", function() {
jQuery( "#body" ).on( "click", "#qunit-fixture div", function() { div++; } ); submit++; return false;
jQuery( "#body" ).on( "click", "div#nothiddendiv", function() { livea++; } ); } );
jQuery( "#body" ).on( "click", "div#nothiddendivchild", function() { liveb++; } ); 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 // Nothing should trigger on the body
jQuery( "body" ).trigger( "click" ); jQuery( "body" ).trigger( "click" );
@ -1569,7 +1601,9 @@ QUnit.test( ".on()/.off()", function( assert ) {
// Make sure that stopPropagation doesn't stop live events // Make sure that stopPropagation doesn't stop live events
submit = 0; div = 0; livea = 0; liveb = 0; 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" ); jQuery( "div#nothiddendivchild" ).trigger( "click" );
assert.equal( submit, 0, "stopPropagation Click on inner div" ); assert.equal( submit, 0, "stopPropagation Click on inner div" );
assert.equal( div, 1, "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 // Test binding with a different context
clicked = 0; 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( "#qunit-fixture div" ).trigger( "click" );
jQuery( "#foo" ).trigger( "click" ); jQuery( "#foo" ).trigger( "click" );
jQuery( "#qunit-fixture" ).trigger( "click" ); jQuery( "#qunit-fixture" ).trigger( "click" );
@ -1635,14 +1671,18 @@ QUnit.test( ".on()/.off()", function( assert ) {
jQuery( "#body" ).off( "click", "#foo" ); jQuery( "#body" ).off( "click", "#foo" );
// Verify that return false prevents default action // 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; hash = window.location.hash;
jQuery( "#anchor2" ).trigger( "click" ); jQuery( "#anchor2" ).trigger( "click" );
assert.equal( window.location.hash, hash, "return false worked" ); assert.equal( window.location.hash, hash, "return false worked" );
jQuery( "#body" ).off( "click", "#anchor2" ); jQuery( "#body" ).off( "click", "#anchor2" );
// Verify that .preventDefault() prevents default action // 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; hash = window.location.hash;
jQuery( "#anchor2" ).trigger( "click" ); jQuery( "#anchor2" ).trigger( "click" );
assert.equal( window.location.hash, hash, "e.preventDefault() worked" ); 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 // Test binding the same handler to multiple points
called = 0; called = 0;
function callback() { called++; return false; } function callback() {
called++; return false;
}
jQuery( "#body" ).on( "click", "#nothiddendiv", callback ); jQuery( "#body" ).on( "click", "#nothiddendiv", callback );
jQuery( "#body" ).on( "click", "#anchor2", callback ); jQuery( "#body" ).on( "click", "#anchor2", callback );
@ -1696,8 +1738,14 @@ QUnit.test( ".on()/.off()", function( assert ) {
livec = 0; livec = 0;
jQuery( "#nothiddendivchild" ).html( "<span></span>" ); jQuery( "#nothiddendivchild" ).html( "<span></span>" );
jQuery( "#body" ).on( "click", "#nothiddendivchild", function() { jQuery( "#nothiddendivchild" ).html( "" ); } ); jQuery( "#body" ).on( "click", "#nothiddendivchild", function() {
jQuery( "#body" ).on( "click", "#nothiddendivchild", function( e ) { if ( e.target ) {livec++;} } ); jQuery( "#nothiddendivchild" ).html( "" );
} );
jQuery( "#body" ).on( "click", "#nothiddendivchild", function( e ) {
if ( e.target ) {
livec++;
}
} );
jQuery( "#nothiddendiv span" ).trigger( "click" ); jQuery( "#nothiddendiv span" ).trigger( "click" );
assert.equal( jQuery( "#nothiddendiv span" ).length, 0, "Verify that first handler occurred and modified the DOM." ); 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; livee = 0;
// bind one pair in one order // bind one pair in one order
jQuery( "#body" ).on( "click", "span#liveSpan1 a", function() { lived++; return false; } ); jQuery( "#body" ).on( "click", "span#liveSpan1 a", function() {
jQuery( "#body" ).on( "click", "span#liveSpan1", function() { livee++; } ); lived++;
return false;
} );
jQuery( "#body" ).on( "click", "span#liveSpan1", function() {
livee++;
} );
jQuery( "span#liveSpan1 a" ).trigger( "click" ); jQuery( "span#liveSpan1 a" ).trigger( "click" );
assert.equal( lived, 1, "Verify that only one first handler occurred." ); assert.equal( lived, 1, "Verify that only one first handler occurred." );
assert.equal( livee, 0, "Verify that second handler doesn't." ); assert.equal( livee, 0, "Verify that second handler doesn't." );
// and one pair in inverse // and one pair in inverse
jQuery( "#body" ).on( "click", "span#liveSpan2", function() { livee++; } ); jQuery( "#body" ).on( "click", "span#liveSpan2", function() {
jQuery( "#body" ).on( "click", "span#liveSpan2 a", function() { lived++; return false; } ); livee++;
} );
jQuery( "#body" ).on( "click", "span#liveSpan2 a", function() {
lived++;
return false;
} );
lived = 0; lived = 0;
livee = 0; livee = 0;
@ -1747,11 +1805,17 @@ QUnit.test( ".on()/.off()", function( assert ) {
// Work with deep selectors // Work with deep selectors
livee = 0; 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( "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." ); 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" ); elem.appendTo( "#qunit-fixture" );
try { try {
// Make sure the original element has some event data. // Make sure the original element has some event data.
elem.on( "click", function() {} ); elem.on( "click", function() {} );
@ -1859,6 +1924,7 @@ QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "delegated event with delegate
.off( "click" ); .off( "click" );
if ( QUnit.jQuerySelectorsPos ) { if ( QUnit.jQuerySelectorsPos ) {
// Positional selector (trac-11315) // Positional selector (trac-11315)
markup.find( "ul" ).eq( 0 ) markup.find( "ul" ).eq( 0 )
.on( "click", ">li>a", function() { .on( "click", ">li>a", function() {
@ -1954,8 +2020,12 @@ QUnit.test( "off all bound delegated events", function( assert ) {
clicks = 0, clicks = 0,
div = jQuery( "#body" ); div = jQuery( "#body" );
div.on( "click submit", "div#nothiddendivchild", function() { count++; } ); div.on( "click submit", "div#nothiddendivchild", function() {
div.on( "click", function() { clicks++; } ); count++;
} );
div.on( "click", function() {
clicks++;
} );
div.off( undefined, "**" ); div.off( undefined, "**" );
jQuery( "div#nothiddendivchild" ).trigger( "click" ); jQuery( "div#nothiddendivchild" ).trigger( "click" );
@ -1974,7 +2044,9 @@ QUnit.test( "on with multiple delegated events", function( assert ) {
var count = 0, var count = 0,
div = jQuery( "#body" ); 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( "click" );
jQuery( "div#nothiddendivchild" ).trigger( "submit" ); 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 // We should have removed all the event handlers ... kinda hacky way to check this
data = jQuery.data[ jQuery( "#onandoff" )[ 0 ].expando ] || {}; 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(); $onandoff.remove();
} ); } );
@ -2403,7 +2475,7 @@ QUnit.test( "special on name mapping", function( assert ) {
.trigger( "gutfeeling" ) // This one should not .trigger( "gutfeeling" ) // This one should not
.remove(); .remove();
delete jQuery.event.special[ "gutfeeling" ]; delete jQuery.event.special.gutfeeling;
} ); } );
QUnit.test( ".on and .off, selective mixed removal (trac-10705)", function( assert ) { 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" ); fireNative( $fixture[ 0 ], "click" );
$fixture.off( "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 // Trigger a native click and ensure the property is set
$fixture.on( "click", function( event ) { $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 ) { jQuery( document ).on( "focusin.focusTests", function( ev ) {
// Support: IE 11+ // Support: IE 11+
// In IE focus is async so focusin on document is fired multiple times, // 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 // 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 ) { jQuery.each( tests, function( label, input ) {
$el.html( "<a></a>" ).children().replaceWith( input ); $el.html( "<a></a>" ).children().replaceWith( input );
assert.strictEqual( $el.html(), "", "replaceWith(" + label + ")" ); 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 + ")" ); 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 + ")" ); assert.strictEqual( $el.html(), "", "replaceWith(other function returning " + label + ")" );
$el.html( "<p></p>" ).children().replaceWith( function( i ) { $el.html( "<p></p>" ).children().replaceWith( function( i ) {
return 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>" ); 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" ); element = jQuery( "<input type='checkbox' value='foo'>" ).attr( "checked", "checked" );
clone = element.clone(); clone = element.clone();
@ -2217,8 +2223,7 @@ QUnit.test( "domManip plain-text caching (trac-6779)", function( assert ) {
for ( i = 0; i < bad.length; i++ ) { for ( i = 0; i < bad.length; i++ ) {
try { try {
$f.append( bad[ i ] ); $f.append( bad[ i ] );
} } catch ( e ) {}
catch ( e ) {}
} }
assert.equal( $f.text(), bad.join( "" ), "Cached strings that match Object properties" ); assert.equal( $f.text(), bad.join( "" ), "Cached strings that match Object properties" );
$f.remove(); $f.remove();

View File

@ -592,6 +592,7 @@ QUnit.test( "chaining", function( assert ) {
// parent is not offsetParent); offset includes parent offset+border+padding // parent is not offsetParent); offset includes parent offset+border+padding
// static: same as relative // static: same as relative
for ( ; parent; parent = expectations[ parent.parent ] ) { for ( ; parent; parent = expectations[ parent.parent ] ) {
// position:fixed // position:fixed
if ( props.style === "fixed" ) { if ( props.style === "fixed" ) {
break; 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 ) { div.queue( function( next ) {
assert.equal( ++counter, 1, "Dequeueing" ); assert.equal( ++counter, 1, "Dequeueing" );
setTimeout( function() { next(); }, 500 ); setTimeout( function() {
next();
}, 500 );
} ).queue( function( next ) { } ).queue( function( next ) {
assert.equal( ++counter, 2, "Next was called" ); assert.equal( ++counter, 2, "Next was called" );
next(); next();
@ -187,7 +189,9 @@ QUnit.test( "clearQueue() clears the fx queue", function( assert ) {
div.queue( function( next ) { div.queue( function( next ) {
counter++; counter++;
var self = this; var self = this;
setTimeout( function() { jQuery( self ).clearQueue(); next(); }, 50 ); setTimeout( function() {
jQuery( self ).clearQueue(); next();
}, 50 );
} ).queue( function() { } ).queue( function() {
counter++; counter++;
} ); } );

View File

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

View File

@ -5,10 +5,10 @@ QUnit.test( "jQuery.param()", function( assert ) {
var params; var params;
params = { "foo":"bar", "baz":42, "quux":"All your base are belong to us" }; params = { "foo": "bar", "baz": 42, "quux": "All your base are belong to us" };
assert.equal( jQuery.param( params ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" ); assert.equal( jQuery.param( params ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" );
params = { "string":"foo", "null":null, "undefined":undefined }; params = { "string": "foo", "null": null, "undefined": undefined };
assert.equal( jQuery.param( params ), "string=foo&null=&undefined=", "handle nulls and undefineds properly" ); assert.equal( jQuery.param( params ), "string=foo&null=&undefined=", "handle nulls and undefineds properly" );
params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" }; params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" };
@ -23,13 +23,13 @@ QUnit.test( "jQuery.param()", function( assert ) {
params = { "foo": { "bar": "baz", "beep": 42, "quux": "All your base are belong to us" } }; params = { "foo": { "bar": "baz", "beep": 42, "quux": "All your base are belong to us" } };
assert.equal( jQuery.param( params ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" ); assert.equal( jQuery.param( params ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" );
params = { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" }; params = { a: [ 1, 2 ], b: { c: 3, d: [ 4, 5 ], e: { x: [ 6 ], y: 7, z: [ 8, 9 ] }, f: true, g: false, h: undefined }, i: [ 10, 11 ], j: true, k: false, l: [ undefined, 0 ], m: "cowboy hat?" };
assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure" ); assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure" );
params = { "a": [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { "b": [ 7, [ 8, 9 ], [ { "c": 10, "d": 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { "e": { "f": { "g": [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] }; params = { "a": [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { "b": [ 7, [ 8, 9 ], [ { "c": 10, "d": 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { "e": { "f": { "g": [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] };
assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=0&a[1][]=1&a[1][]=2&a[2][]=3&a[2][1][]=4&a[2][1][]=5&a[2][2][]=6&a[3][b][]=7&a[3][b][1][]=8&a[3][b][1][]=9&a[3][b][2][0][c]=10&a[3][b][2][0][d]=11&a[3][b][3][0][]=12&a[3][b][4][0][0][]=13&a[3][b][5][e][f][g][]=14&a[3][b][5][e][f][g][1][]=15&a[3][b][]=16&a[]=17", "nested arrays" ); assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=0&a[1][]=1&a[1][]=2&a[2][]=3&a[2][1][]=4&a[2][1][]=5&a[2][2][]=6&a[3][b][]=7&a[3][b][1][]=8&a[3][b][1][]=9&a[3][b][2][0][c]=10&a[3][b][2][0][d]=11&a[3][b][3][0][]=12&a[3][b][4][0][0][]=13&a[3][b][5][e][f][g][]=14&a[3][b][5][e][f][g][1][]=15&a[3][b][]=16&a[]=17", "nested arrays" );
params = { "a":[ 1, 2 ], "b":{ "c":3, "d":[ 4, 5 ], "e":{ "x":[ 6 ], "y":7, "z":[ 8, 9 ] }, "f":true, "g":false, "h":undefined }, "i":[ 10, 11 ], "j":true, "k":false, "l":[ undefined, 0 ], "m":"cowboy hat?" }; params = { "a": [ 1, 2 ], "b": { "c": 3, "d": [ 4, 5 ], "e": { "x": [ 6 ], "y": 7, "z": [ 8, 9 ] }, "f": true, "g": false, "h": undefined }, "i": [ 10, 11 ], "j": true, "k": false, "l": [ undefined, 0 ], "m": "cowboy hat?" };
assert.equal( jQuery.param( params, true ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure, forced traditional" ); assert.equal( jQuery.param( params, true ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure, forced traditional" );
assert.equal( decodeURIComponent( jQuery.param( { "a": [ 1, 2, 3 ], "b[]": [ 4, 5, 6 ], "c[d]": [ 7, 8, 9 ], "e": { "f": [ 10 ], "g": [ 11, 12 ], "h": 13 } } ) ), "a[]=1&a[]=2&a[]=3&b[]=4&b[]=5&b[]=6&c[d][]=7&c[d][]=8&c[d][]=9&e[f][]=10&e[g][]=11&e[g][]=12&e[h]=13", "Make sure params are not double-encoded." ); assert.equal( decodeURIComponent( jQuery.param( { "a": [ 1, 2, 3 ], "b[]": [ 4, 5, 6 ], "c[d]": [ 7, 8, 9 ], "e": { "f": [ 10 ], "g": [ 11, 12 ], "h": 13 } } ) ), "a[]=1&a[]=2&a[]=3&b[]=4&b[]=5&b[]=6&c[d][]=7&c[d][]=8&c[d][]=9&e[f][]=10&e[g][]=11&e[g][]=12&e[h]=13", "Make sure params are not double-encoded." );
@ -37,7 +37,7 @@ QUnit.test( "jQuery.param()", function( assert ) {
// trac-7945 // trac-7945
assert.equal( jQuery.param( { "jquery": "1.4.2" } ), "jquery=1.4.2", "Check that object with a jQuery property get serialized correctly" ); assert.equal( jQuery.param( { "jquery": "1.4.2" } ), "jquery=1.4.2", "Check that object with a jQuery property get serialized correctly" );
params = { "foo":"bar", "baz":42, "quux":"All your base are belong to us" }; params = { "foo": "bar", "baz": 42, "quux": "All your base are belong to us" };
assert.equal( jQuery.param( params, true ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" ); assert.equal( jQuery.param( params, true ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" );
params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" }; params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" };
@ -46,25 +46,27 @@ QUnit.test( "jQuery.param()", function( assert ) {
params = { "foo": [ "a", "b", "c" ] }; params = { "foo": [ "a", "b", "c" ] };
assert.equal( jQuery.param( params, true ), "foo=a&foo=b&foo=c", "with array of strings" ); assert.equal( jQuery.param( params, true ), "foo=a&foo=b&foo=c", "with array of strings" );
params = { "foo[]":[ "baz", 42, "All your base are belong to us" ] }; params = { "foo[]": [ "baz", 42, "All your base are belong to us" ] };
assert.equal( jQuery.param( params, true ), "foo%5B%5D=baz&foo%5B%5D=42&foo%5B%5D=All%20your%20base%20are%20belong%20to%20us", "more array" ); assert.equal( jQuery.param( params, true ), "foo%5B%5D=baz&foo%5B%5D=42&foo%5B%5D=All%20your%20base%20are%20belong%20to%20us", "more array" );
params = { "foo[bar]":"baz", "foo[beep]":42, "foo[quux]":"All your base are belong to us" }; params = { "foo[bar]": "baz", "foo[beep]": 42, "foo[quux]": "All your base are belong to us" };
assert.equal( jQuery.param( params, true ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" ); assert.equal( jQuery.param( params, true ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" );
params = { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" }; params = { a: [ 1, 2 ], b: { c: 3, d: [ 4, 5 ], e: { x: [ 6 ], y: 7, z: [ 8, 9 ] }, f: true, g: false, h: undefined }, i: [ 10, 11 ], j: true, k: false, l: [ undefined, 0 ], m: "cowboy hat?" };
assert.equal( jQuery.param( params, true ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure" ); assert.equal( jQuery.param( params, true ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure" );
params = { "a": [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { "b": [ 7, [ 8, 9 ], [ { "c": 10, d: 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { "e": { "f": { "g": [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] }; params = { "a": [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { "b": [ 7, [ 8, 9 ], [ { "c": 10, d: 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { "e": { "f": { "g": [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] };
assert.equal( jQuery.param( params, true ), "a=0&a=1%2C2&a=3%2C4%2C5%2C6&a=%5Bobject%20Object%5D&a=17", "nested arrays (not possible when traditional == true)" ); assert.equal( jQuery.param( params, true ), "a=0&a=1%2C2&a=3%2C4%2C5%2C6&a=%5Bobject%20Object%5D&a=17", "nested arrays (not possible when traditional == true)" );
params = { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" }; params = { a: [ 1, 2 ], b: { c: 3, d: [ 4, 5 ], e: { x: [ 6 ], y: 7, z: [ 8, 9 ] }, f: true, g: false, h: undefined }, i: [ 10, 11 ], j: true, k: false, l: [ undefined, 0 ], m: "cowboy hat?" };
assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure, forced not traditional" ); assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure, forced not traditional" );
params = { "param1": null }; params = { "param1": null };
assert.equal( jQuery.param( params ), "param1=", "Make sure that null params aren't traversed." ); 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" ); assert.equal( jQuery.param( params, false ), "param1=&param2=", "object with function property that returns null value" );
params = { "test": { "length": 3, "foo": "bar" } }; params = { "test": { "length": 3, "foo": "bar" } };

View File

@ -310,7 +310,9 @@ QUnit.test( "filter(Function)", function( assert ) {
return !jQuery( "a", this ).length; return !jQuery( "a", this ).length;
} ).get(), q( "sndp", "first" ), "filter(Function)" ); } ).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 ) { QUnit.test( "filter(Element)", function( assert ) {
@ -511,7 +513,9 @@ QUnit.test( "not(Element)", function( assert ) {
QUnit.test( "not(Function)", function( assert ) { QUnit.test( "not(Function)", function( assert ) {
assert.expect( 1 ); 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 ) { QUnit.test( "not(Array)", function( assert ) {