diff --git a/.github/workflows/browserstack-dispatch.yml b/.github/workflows/browserstack-dispatch.yml index cac0311b40..7941255ac4 100644 --- a/.github/workflows/browserstack-dispatch.yml +++ b/.github/workflows/browserstack-dispatch.yml @@ -43,9 +43,9 @@ jobs: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 20 @@ -59,4 +59,6 @@ jobs: run: npm run pretest - name: Run tests - run: npm run test:unit -- -v --browserstack ${{ inputs.browser }} -m ${{ inputs.module }} + run: npm run test:unit -- \ + -v --browserstack ${{ inputs.browser }} \ + -f module=${{ inputs.module }} diff --git a/.github/workflows/browserstack.yml b/.github/workflows/browserstack.yml index 19ccebf8cd..47ff7634bd 100644 --- a/.github/workflows/browserstack.yml +++ b/.github/workflows/browserstack.yml @@ -12,7 +12,7 @@ jobs: env: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - NODE_VERSION: 20.x + NODE_VERSION: 22.x name: ${{ matrix.BROWSER }} concurrency: group: ${{ github.workflow }}-${{ matrix.BROWSER }} @@ -23,7 +23,13 @@ jobs: BROWSER: - 'IE_11' - 'Safari_latest' - - 'Safari_latest-1' + # JTR doesn't take into account the jump from Safari 18 to 26, + # so we need to specify versions explicitly. Also, while BrowserStack + # already added macOS Tahoe with Safari 26, it's not a stable release + # yet, so we need to test on Safari 17 as well. + # See https://github.com/jquery/jquery-test-runner/issues/17 + - 'Safari_18' + - 'Safari_17' - 'Chrome_latest' - 'Chrome_latest-1' - 'Opera_latest' @@ -31,21 +37,20 @@ jobs: - 'Edge_latest-1' - 'Firefox_latest' - 'Firefox_latest-1' - - 'Firefox_115' + - '__iOS_18' - '__iOS_17' - '__iOS_16' - - '__iOS_15' steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} @@ -62,4 +67,7 @@ jobs: run: npm run pretest - name: Run tests - run: npm run test:unit -- -v --browserstack "${{ matrix.BROWSER }}" --run-id ${{ github.run_id }} --isolate --retries 3 --hard-retries 1 + run: | + npm run test:unit -- -v -c jtr-isolate.yml \ + --browserstack "${{ matrix.BROWSER }}" \ + --run-id ${{ github.run_id }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6d49424e15..8c2f79b356 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -33,7 +33,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -41,7 +41,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -55,4 +55,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 diff --git a/.github/workflows/filestash.yml b/.github/workflows/filestash.yml index 7e306a6e5f..6653f1ef1d 100644 --- a/.github/workflows/filestash.yml +++ b/.github/workflows/filestash.yml @@ -16,18 +16,18 @@ jobs: if: ${{ github.repository == 'jquery/jquery' }} environment: filestash env: - NODE_VERSION: 20.x + NODE_VERSION: 22.x steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 22b683a212..cd2bb3f45f 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,82 +16,127 @@ jobs: fail-fast: false matrix: NAME: ["Node"] - NODE_VERSION: [18.x, 20.x] + NODE_VERSION: [18.x, 20.x, 22.x, 23.x] NPM_SCRIPT: ["test:browserless"] include: - NAME: "Node" - NODE_VERSION: "20.x" + NODE_VERSION: "22.x" NPM_SCRIPT: "lint" - NAME: "Chrome/Firefox" - NODE_VERSION: "20.x" + NODE_VERSION: "22.x" NPM_SCRIPT: "test:browser" - NAME: "Chrome" - NODE_VERSION: "20.x" + NODE_VERSION: "22.x" NPM_SCRIPT: "test:slim" - NAME: "Chrome" - NODE_VERSION: "20.x" + NODE_VERSION: "22.x" NPM_SCRIPT: "test:no-deprecated" - NAME: "Chrome" - NODE_VERSION: "20.x" + NODE_VERSION: "22.x" NPM_SCRIPT: "test:selector-native" - NAME: "Chrome" - NODE_VERSION: "20.x" + NODE_VERSION: "22.x" NPM_SCRIPT: "test:esm" - - NAME: "Firefox ESR" - NODE_VERSION: "20.x" + - NAME: "Firefox ESR (new)" + NODE_VERSION: "22.x" + NPM_SCRIPT: "test:firefox" + - NAME: "Firefox ESR (old)" + NODE_VERSION: "22.x" NPM_SCRIPT: "test:firefox" steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Use Node.js ${{ matrix.NODE_VERSION }} - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ matrix.NODE_VERSION }} - name: Cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ matrix.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-${{ matrix.NODE_VERSION }}-npm-lock- - - name: Install firefox ESR + - name: Set download URL for Firefox ESR (old) + run: | + echo "FIREFOX_SOURCE_URL=https://download.mozilla.org/?product=firefox-esr-latest-ssl&lang=en-US&os=linux64" >> "$GITHUB_ENV" + if: contains(matrix.NAME, 'Firefox ESR (old)') + + - name: Set download URL for Firefox ESR (new) run: | - export FIREFOX_SOURCE_URL='https://download.mozilla.org/?product=firefox-esr-latest-ssl&lang=en-US&os=linux64' - wget --no-verbose $FIREFOX_SOURCE_URL -O - | tar -jx -C ${HOME} + echo "FIREFOX_SOURCE_URL=https://download.mozilla.org/?product=firefox-esr-next-latest-ssl&lang=en-US&os=linux64" >> "$GITHUB_ENV" + if: contains(matrix.NAME, 'Firefox ESR (new)') + + - name: Install Firefox ESR + run: | + # Support: Firefox <135 only + # Older Firefox used to be compressed using bzip2, newer using xz. Try + # to uncompress using xz, fall back to bzip2 if that fails. + # Note: this will download the old Firefox ESR twice, but it will still work + # when the current ESR version starts to use xz with no changes to the code. + wget --no-verbose "$FIREFOX_SOURCE_URL" -O - | tar -Jx -C "$HOME" || \ + wget --no-verbose "$FIREFOX_SOURCE_URL" -O - | tar -jx -C "$HOME" + echo "PATH=${HOME}/firefox:$PATH" >> "$GITHUB_ENV" + echo "FIREFOX_BIN=${HOME}/firefox/firefox" >> "$GITHUB_ENV" if: contains(matrix.NAME, 'Firefox ESR') - name: Install dependencies run: npm install - - name: Build All for Linting + - name: Build all for linting run: npm run build:all if: contains(matrix.NPM_SCRIPT, 'lint') - name: Run tests - run: | - export PATH=${HOME}/firefox:$PATH - export FIREFOX_BIN=${HOME}/firefox/firefox - npm run ${{ matrix.NPM_SCRIPT }} + run: npm run ${{ matrix.NPM_SCRIPT }} + + ie: + runs-on: windows-latest + env: + NODE_VERSION: 22.x + name: test:ie - IE + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Cache + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock- + + - name: Install dependencies + run: npm install + + - name: Run tests in Edge in IE mode + run: npm run test:ie safari: runs-on: macos-latest env: - NODE_VERSION: 20.x + NODE_VERSION: 22.x name: test:safari - Safari steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ env.NODE_VERSION }} - name: Cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-npm-lock-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/verify-release.yml b/.github/workflows/verify-release.yml index cd43efac2c..ab696541f5 100644 --- a/.github/workflows/verify-release.yml +++ b/.github/workflows/verify-release.yml @@ -1,16 +1,17 @@ name: Reproducible Builds on: - # On tags push: + # On tags tags: - '*' # Or manually workflow_dispatch: inputs: version: - description: 'Version to verify (>= 4.0.0-beta.2)' + description: 'Version to verify (>= 4.0.0-rc.1)' required: false + jobs: run: name: Verify release @@ -18,16 +19,19 @@ jobs: # skip on forks if: ${{ github.repository == 'jquery/jquery' }} env: - NODE_VERSION: 20.x + NODE_VERSION: 22.x steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ env.NODE_VERSION }} + - name: Install dependencies + run: npm ci + - run: npm run release:verify env: VERSION: ${{ github.event.inputs.version || github.ref_name }} diff --git a/.gitignore b/.gitignore index b3cc97d999..2b984efe70 100644 --- a/.gitignore +++ b/.gitignore @@ -31,8 +31,8 @@ npm-debug.log* /test/data/qunit-fixture.js # Release artifacts -changelog.* -contributors.* +changelog.html +contributors.html # Ignore BrowserStack testing files local.log diff --git a/.husky/commit-msg b/.husky/commit-msg index 69cff3820a..199b8aa3ea 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1,2 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" npx commitplease .git/COMMIT_EDITMSG diff --git a/.husky/pre-commit b/.husky/pre-commit index 5e1db84ec8..f9543cf3cc 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,3 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" npm run lint npm run qunit-fixture diff --git a/.release-it.cjs b/.release-it.cjs index 1de0de5486..42eb979b08 100644 --- a/.release-it.cjs +++ b/.release-it.cjs @@ -11,23 +11,33 @@ module.exports = { hooks: { "before:init": "bash ./build/release/pre-release.sh", "after:version:bump": - "sed -i 's/main\\/AUTHORS.txt/${version}\\/AUTHORS.txt/' package.json", + "sed -i '' -e 's|main/AUTHORS.txt|${version}/AUTHORS.txt|' package.json", "after:bump": "cross-env VERSION=${version} npm run build:all", "before:git:release": "git add -f dist/ dist-module/ changelog.md", - "after:release": `bash ./build/release/post-release.sh \${version} ${ blogURL }` + "after:release": "echo 'Run the following to complete the release:' && " + + `echo './build/release/post-release.sh $\{version} ${ blogURL }'` }, git: { - changelog: "npm run release:changelog -- ${from} ${to}", + + // Use the node script directly to avoid an npm script + // command log entry in the GH release notes + changelog: "node build/release/changelog.js ${from} ${to}", commitMessage: "Release: ${version}", getLatestTagFromAllRefs: true, + pushRepo: "git@github.com:jquery/jquery.git", requireBranch: "main", requireCleanWorkingDir: true }, github: { + pushRepo: "git@github.com:jquery/jquery.git", release: true, tokenRef: "JQUERY_GITHUB_TOKEN" }, npm: { - publish: true + + // We're publishing from a dist folder generated in the post-release + // step, so we also need to publish by ourselves; release-it would + // do it too early. + publish: false } }; diff --git a/AUTHORS.txt b/AUTHORS.txt index 1ef119e026..73d14981cd 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -373,3 +373,5 @@ Gabriela Gutierrez Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> J.Son Liam James +ac-mmi <79802170+ac-mmi@users.noreply.github.com> +neogy-akash diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c9d7faa12..45396af7d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,7 @@ Note: This is the code development repository for *jQuery Core* only. Before ope * jQuery Core API documentation issues can be filed [at the API repo](https://github.com/jquery/api.jquery.com/issues). * Bugs or suggestions for other jQuery organization projects should be filed in [their respective repos](https://github.com/jquery/). + ## Getting Involved [API design principles](https://github.com/jquery/jquery/wiki/API-design-guidelines) @@ -20,19 +21,19 @@ More information on how to contribute to this and other jQuery organization proj When opening a pull request, you'll be asked to sign our Contributor License Agreement. Both the Corporate and Individual agreements can be [previewed on GitHub](https://github.com/openjs-foundation/easycla). -If you're looking for some good issues to start with, [here are some issues labeled "help wanted" or "patch welcome"](https://github.com/jquery/jquery/issues?q=is%3Aissue+label%3A%22help+wanted%22%2C%22Patch+Welcome%22). +If you're looking for some good issues to start with, [here are some issues labeled "help wanted" or "patch welcome"](https://github.com/jquery/jquery/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22%2C%22Patch+Welcome%22). ## Questions and Discussion -### Forum and IRC +### Looking for help? jQuery is so popular that many developers have knowledge of its capabilities and limitations. Most questions about using jQuery can be answered on popular forums such as [Stack Overflow](https://stackoverflow.com). Please start there when you have questions, even if you think you've found a bug. -The jQuery Core team watches the [jQuery Development Forum](https://forum.jquery.com/developing-jquery-core). If you have longer posts or questions that can't be answered in places such as Stack Overflow, please feel free to post them there. If you think you've found a bug, please [file it in the bug tracker](#how-to-report-bugs). The Core team can be found in the [#jquery-dev](https://webchat.freenode.net/?channels=jquery-dev) IRC channel on irc.freenode.net. +The jQuery Core team watches [jQuery GitHub Discussions](https://github.com/jquery/jquery/discussions). If you have longer posts or questions that can't be answered in places such as Stack Overflow, please feel free to post them there. If you think you've found a bug, please [file it in the bug tracker](#how-to-report-bugs). The Core team can be found in the [#jquery/dev](https://matrix.to/#/#jquery_dev:gitter.im) Matrix channel on gitter.im. ### Weekly Status Meetings -The jQuery Core team has a weekly meeting to discuss the progress of current work. The meeting is held in the [#jquery-meeting](https://webchat.freenode.net/?channels=jquery-meeting) IRC channel on irc.freenode.net at [Noon EST](https://www.timeanddate.com/worldclock/fixedtime.html?month=1&day=17&year=2011&hour=12&min=0&sec=0&p1=43) on Mondays. +The jQuery Core team has a weekly meeting to discuss the progress of current work. The meeting is held in the [#jquery/meeting](hhttps://matrix.to/#/#jquery_meeting:gitter.im) Matrix channel on gitter.im at [Noon EST](https://www.timeanddate.com/worldclock/fixedtime.html?month=10&day=7&year=2024&hour=12&min=0&sec=0&p1=43) on Mondays. [jQuery Core Meeting Notes](https://meetings.jquery.org/category/core/) @@ -43,7 +44,7 @@ The jQuery Core team has a weekly meeting to discuss the progress of current wor Most bugs reported to our bug tracker are actually bugs in user code, not in jQuery code. Keep in mind that just because your code throws an error inside of jQuery, this does *not* mean the bug is a jQuery bug. -Ask for help first in the [Using jQuery Forum](https://forum.jquery.com/using-jquery) or another discussion forum like [Stack Overflow](https://stackoverflow.com/). You will get much quicker support, and you will help avoid tying up the jQuery team with invalid bug reports. +Ask for help first on a discussion forum like [Stack Overflow](https://stackoverflow.com/). You will get much quicker support, and you will help avoid tying up the jQuery team with invalid bug reports. ### Disable browser extensions @@ -55,7 +56,7 @@ Bugs in old versions of jQuery may have already been fixed. In order to avoid re ### Simplify the test case -When experiencing a problem, [reduce your code](https://webkit.org/quality/reduction.html) to the bare minimum required to reproduce the issue. This makes it *much* easier to isolate and fix the offending code. Bugs reported without reduced test cases take on average 9001% longer to fix than bugs that are submitted with them, so you really should try to do this if at all possible. +When experiencing a problem, [reduce your code](https://webkit.org/test-case-reduction/) to the bare minimum required to reproduce the issue. This makes it *much* easier to isolate and fix the offending code. Bugs reported without reduced test cases take on average 9001% longer to fix than bugs that are submitted with them, so you really should try to do this if at all possible. ### Search for related or duplicate issues @@ -68,45 +69,45 @@ We *love* when people contribute back to the project by patching the bugs they f ### Build a Local Copy of jQuery -Create a fork of the jQuery repo on github at https://github.com/jquery/jquery +Create a fork of the jQuery repo on GitHub at https://github.com/jquery/jquery -Clone your jQuery fork to work locally +Clone your jQuery fork to work locally: ```bash $ git clone git@github.com:username/jquery.git ``` -Change directory to the newly created dir jquery/ +Change directory to the newly created dir `jquery/`: ```bash $ cd jquery ``` -Add the jQuery main as a remote. I label mine "upstream" +Add the jQuery `main` as a remote. I label mine `upstream`: ```bash -$ git remote add upstream git://github.com/jquery/jquery.git +$ git remote add upstream git@github.com:jquery/jquery.git ``` -Get in the habit of pulling in the "upstream" main to stay up to date as jQuery receives new commits +Get in the habit of pulling in the "upstream" main to stay up to date as jQuery receives new commits: ```bash $ git pull upstream main ``` -Install the necessary dependencies +Install the necessary dependencies: ```bash $ npm install ``` -Build all jQuery files +Build all jQuery files: ```bash $ npm run build:all ``` -Start a test server +Start a test server: ```bash $ npm run test:server @@ -118,7 +119,7 @@ Success! You just built and tested jQuery! ### Test Suite Tips... -During the process of writing your patch, you will run the test suite MANY times. You can speed up the process by narrowing the running test suite down to the module you are testing by either double clicking the title of the test or appending it to the url. The following examples assume you're working on a local repo, hosted on your localhost server. +During the process of writing your patch, you will run the test suite MANY times. You can speed up the process by narrowing the running test suite down to the module you are testing by either double-clicking the title of the test or appending it to the url. The following examples assume you're working on a local repo, hosted on your localhost server. Example: @@ -130,10 +131,10 @@ This will only run the "css" module tests. This will significantly speed up your #### Change the test server port -The default port for the test server is 3000. You can change the port by setting the `PORT` environment variable. +The default port for the test server is 3000. You can change the port by setting the `--port` option. ```bash -$ PORT=3001 npm run test:server +$ npm run test:server -- --port 8000 ``` #### Loading changes on the test page @@ -164,7 +165,7 @@ Make sure jQuery is built (`npm run build:all`) and run the tests: $ npm run test:unit ``` -This will run each module in its own browser instance and report the results in the terminal. +This will run all tests and report the results in the terminal. View the full help for the test suite for more info on running the tests from the command line: @@ -172,6 +173,26 @@ View the full help for the test suite for more info on running the tests from th $ npm run test:unit -- --help ``` +#### Running a single module + +All test modules run by default. Run a single module by specifying the module in a "flag": + +```bash +$ npm run test:unit -- --flag module=css +``` + +Or, run multiple modules with multiple flags (`-f` is shorthand for `--flag`): + +```bash +$ npm run test:unit -- -f module=css -f module=effects +``` + +Anything passed to the `--flag` option is passed as query parameters on the QUnit test page. For instance, run tests with unminified code with the `dev` flag: + +```bash +$ npm run test:unit -- -f dev +``` + ### Repo organization The jQuery source is organized with ECMAScript modules and then compiled into one file at build time. diff --git a/README.md b/README.md index 36d6fb15c0..3aefeb7fa8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,27 @@ -[jQuery](https://jquery.com/) — New Wave JavaScript -================================================== +# [jQuery](https://jquery.com/) — New Wave JavaScript Meetings are currently held on the [matrix.org platform](https://matrix.to/#/#jquery_meeting:gitter.im). Meeting minutes can be found at [meetings.jquery.org](https://meetings.jquery.org/category/core/). -Contribution Guides --------------------------------------- +The latest version of jQuery is available at [https://jquery.com/download/](https://jquery.com/download/). + +## Version support + +| Version | Branch | Status | +| ------- | ---------- | -------- | +| 4.x | main | Beta | +| 3.x | 3.x-stable | Active | +| 2.x | 2.x-stable | Inactive | +| 1.x | 1.x-stable | Inactive | + +Once 4.0.0 final is released, the 3.x branch will continue to receive updates for a limited time. The 2.x and 1.x branches are no longer supported. + +Commercial support for inactive versions is available from [HeroDevs](https://herodevs.com/nes). + +Learn more about our [version support](https://jquery.com/support/). + +## Contribution Guides In the spirit of open source software development, jQuery always encourages community code contribution. To help you get started and before you jump into writing code, be sure to read these important contribution guidelines thoroughly: @@ -20,16 +35,12 @@ GitHub issues/PRs are usually referenced via `gh-NUMBER`, where `NUMBER` is the jQuery has used a different bug tracker - based on Trac - in the past, available under [bugs.jquery.com](https://bugs.jquery.com/). It is being kept in read only mode so that referring to past discussions is possible. When jQuery source references one of those issues, it uses the pattern `trac-NUMBER`, where `NUMBER` is the numerical ID of the issue. You can find such an issue under `https://bugs.jquery.com/ticket/NUMBER`. - -Environments in which to use jQuery --------------------------------------- +## Environments in which to use jQuery - [Browser support](https://jquery.com/browser-support/) - jQuery also supports Node, browser extensions, and other non-browser environments. - -What you need to build your own jQuery --------------------------------------- +## What you need to build your own jQuery To build jQuery, you need to have the latest Node.js/npm and git 1.7 or later. Earlier versions might work, but are not supported. @@ -41,9 +52,7 @@ and `brew install node` to install Node.js. Linux/BSD users should use their appropriate package managers to install git and Node.js, or build from source if you swing that way. Easy-peasy. - -How to build your own jQuery ----------------------------- +## 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). @@ -94,7 +103,7 @@ Some example modules that can be excluded or included are: - **ajax/script**: The `" - ) - ); - } ); - - // Bind the reporter - app.post( - "/api/report", - bodyParser.json( { limit: "50mb" } ), - async( req, res ) => { - if ( report ) { - const response = await report( req.body ); - if ( response ) { - res.json( response ); - return; - } - } - res.sendStatus( 204 ); - } - ); - - // Handle errors from the body parser - app.use( bodyParserErrorHandler() ); - - // Hook up mock server - app.use( mockServer() ); - - // Serve static files - app.post( "/test/data/name.html", ( _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" ) ); - - return app; -} diff --git a/test/runner/flags/browsers.js b/test/runner/flags/browsers.js deleted file mode 100644 index c15d7085ed..0000000000 --- a/test/runner/flags/browsers.js +++ /dev/null @@ -1,25 +0,0 @@ -// This list is static, so no requests are required -// in the command help menu. - -import { getBrowsers } from "../browserstack/api.js"; - -export const browsers = [ - "chrome", - "ie", - "firefox", - "edge", - "safari", - "opera", - "yandex", - "IE Mobile", - "Android Browser", - "Mobile Safari", - "jsdom" -]; - -// A function that can be used to update the above list. -export async function getAvailableBrowsers() { - const browsers = await getBrowsers( { flat: true } ); - const available = [ ...new Set( browsers.map( ( { browser } ) => browser ) ) ]; - return available.concat( "jsdom" ); -} diff --git a/test/runner/flags/modules.js b/test/runner/flags/modules.js deleted file mode 100644 index 53f9a933a0..0000000000 --- a/test/runner/flags/modules.js +++ /dev/null @@ -1,24 +0,0 @@ -export const modules = [ - "basic", - - "ajax", - "animation", - "attributes", - "callbacks", - "core", - "css", - "data", - "deferred", - "deprecated", - "dimensions", - "effects", - "event", - "manipulation", - "offset", - "queue", - "selector", - "serialize", - "support", - "traversing", - "tween" -]; diff --git a/test/runner/jsdom/createWindow.js b/test/runner/jsdom/createWindow.js deleted file mode 100644 index de6c63ffa6..0000000000 --- a/test/runner/jsdom/createWindow.js +++ /dev/null @@ -1,21 +0,0 @@ -import jsdom from "jsdom"; - -const { JSDOM } = jsdom; - -export default async function createWindow( { reportId, url, verbose } ) { - const virtualConsole = new jsdom.VirtualConsole(); - virtualConsole.sendTo( console ); - virtualConsole.removeAllListeners( "clear" ); - - const { window } = await JSDOM.fromURL( url, { - resources: "usable", - runScripts: "dangerously", - virtualConsole - } ); - - if ( verbose ) { - console.log( `JSDOM window created (${ reportId })` ); - } - - return window; -} diff --git a/test/runner/lib/buildTestUrl.js b/test/runner/lib/buildTestUrl.js deleted file mode 100644 index 6e0f1a9b02..0000000000 --- a/test/runner/lib/buildTestUrl.js +++ /dev/null @@ -1,29 +0,0 @@ -import { generateModuleId } from "./generateHash.js"; - -export function buildTestUrl( modules, { browserstack, esm, jsdom, port, reportId } ) { - if ( !port ) { - throw new Error( "No port specified." ); - } - - const query = new URLSearchParams(); - for ( const module of modules ) { - query.append( "moduleId", generateModuleId( module ) ); - } - - if ( esm ) { - query.append( "esmodules", "true" ); - } - - if ( jsdom ) { - query.append( "jsdom", "true" ); - } - - if ( reportId ) { - query.append( "reportId", reportId ); - } - - // BrowserStack supplies a custom domain for local testing, - // which is especially necessary for iOS testing. - const host = browserstack ? "bs-local.com" : "localhost"; - return `http://${ host }:${ port }/test/?${ query }`; -} diff --git a/test/runner/lib/generateHash.js b/test/runner/lib/generateHash.js deleted file mode 100644 index dbbd4b4761..0000000000 --- a/test/runner/lib/generateHash.js +++ /dev/null @@ -1,50 +0,0 @@ -import crypto from "node:crypto"; - -export function generateHash( string ) { - const hash = crypto.createHash( "md5" ); - hash.update( string ); - - // QUnit hashes are 8 characters long - // We use 10 characters to be more visually distinct - return hash.digest( "hex" ).slice( 0, 10 ); -} - -/** - * A copy of the generate hash function from QUnit, - * used to generate a hash for the module name. - * - * QUnit errors on passing multiple modules to the - * module query parameter. We need to know - * the hash for each module before loading QUnit - * in order to pass multiple moduleId parameters instead. - */ -export function generateModuleId( module, browser ) { - - // QUnit normally hashes the test name, but - // we've repurposed this function to generate - // report IDs for module/browser combinations. - // We still use it without the browser parameter - // to get the same module IDs as QUnit to pass - // multiple ahead-of-time in the query string. - const str = module + "\x1C" + browser; - let hash = 0; - - for ( let i = 0; i < str.length; i++ ) { - hash = ( hash << 5 ) - hash + str.charCodeAt( i ); - hash |= 0; - } - - let hex = ( 0x100000000 + hash ).toString( 16 ); - if ( hex.length < 8 ) { - hex = "0000000" + hex; - } - - return hex.slice( -8 ); -} - -export function printModuleHashes( modules ) { - console.log( "Module hashes:" ); - modules.forEach( ( module ) => { - console.log( ` ${ module }: ${ generateModuleId( module ) }` ); - } ); -} diff --git a/test/runner/lib/getBrowserString.js b/test/runner/lib/getBrowserString.js deleted file mode 100644 index 413a605004..0000000000 --- a/test/runner/lib/getBrowserString.js +++ /dev/null @@ -1,49 +0,0 @@ -const browserMap = { - chrome: "Chrome", - edge: "Edge", - firefox: "Firefox", - ie: "IE", - jsdom: "JSDOM", - opera: "Opera", - safari: "Safari" -}; - -export function browserSupportsHeadless( browser ) { - browser = browser.toLowerCase(); - return ( - browser === "chrome" || - browser === "firefox" || - browser === "edge" - ); -} - -export function getBrowserString( - { - browser, - browser_version: browserVersion, - device, - os, - os_version: osVersion - }, - headless -) { - browser = browser.toLowerCase(); - browser = browserMap[ browser ] || browser; - let str = browser; - if ( browserVersion ) { - str += ` ${ browserVersion }`; - } - if ( device ) { - str += ` for ${ device }`; - } - if ( os ) { - str += ` on ${ os }`; - } - if ( osVersion ) { - str += ` ${ osVersion }`; - } - if ( headless && browserSupportsHeadless( browser ) ) { - str += " (headless)"; - } - return str; -} diff --git a/test/runner/lib/prettyMs.js b/test/runner/lib/prettyMs.js deleted file mode 100644 index 99bae2b353..0000000000 --- a/test/runner/lib/prettyMs.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Pretty print a time in milliseconds. - */ -export function prettyMs( time ) { - const minutes = Math.floor( time / 60000 ); - const seconds = Math.floor( time / 1000 ); - const ms = Math.floor( time % 1000 ); - - let prettyTime = `${ ms }ms`; - if ( seconds > 0 ) { - prettyTime = `${ seconds }s ${ prettyTime }`; - } - if ( minutes > 0 ) { - prettyTime = `${ minutes }m ${ prettyTime }`; - } - - return prettyTime; -} diff --git a/test/runner/listeners.js b/test/runner/listeners.js deleted file mode 100644 index 61a98e7ce8..0000000000 --- a/test/runner/listeners.js +++ /dev/null @@ -1,110 +0,0 @@ -( function() { - - "use strict"; - - // Get the report ID from the URL. - var match = location.search.match( /reportId=([^&]+)/ ); - if ( !match ) { - return; - } - var id = match[ 1 ]; - - // Adopted from https://github.com/douglascrockford/JSON-js - // Support: IE 11+ - // Using the replacer argument of JSON.stringify in IE has issues - // TODO: Replace this with a circular replacer + JSON.stringify + WeakSet - function decycle( object ) { - var objects = []; - - // The derez function recurses through the object, producing the deep copy. - function derez( value ) { - if ( - typeof value === "object" && - value !== null && - !( value instanceof Boolean ) && - !( value instanceof Date ) && - !( value instanceof Number ) && - !( value instanceof RegExp ) && - !( value instanceof String ) - ) { - - // Return a string early for elements - if ( value.nodeType ) { - return value.toString(); - } - - if ( objects.indexOf( value ) > -1 ) { - return; - } - - objects.push( value ); - - if ( Array.isArray( value ) ) { - - // If it is an array, replicate the array. - return value.map( derez ); - } else { - - // If it is an object, replicate the object. - var nu = Object.create( null ); - Object.keys( value ).forEach( function( name ) { - nu[ name ] = derez( value[ name ] ); - } ); - return nu; - } - } - - // Serialize Symbols as string representations so they are - // sent over the wire after being stringified. - if ( typeof value === "symbol" ) { - - // We can *describe* unique symbols, but note that their identity - // (e.g., `Symbol() !== Symbol()`) is lost - var ctor = Symbol.keyFor( value ) !== undefined ? "Symbol.for" : "Symbol"; - return ctor + "(" + JSON.stringify( value.description ) + ")"; - } - - return value; - } - return derez( object ); - } - - function send( type, data ) { - var json = JSON.stringify( { - id: id, - type: type, - data: data ? decycle( data ) : undefined - } ); - var request = new XMLHttpRequest(); - request.open( "POST", "/api/report", true ); - request.setRequestHeader( "Content-Type", "application/json" ); - request.send( json ); - return request; - } - - // Send acknowledgement to the server. - send( "ack" ); - - QUnit.on( "testEnd", function( data ) { - send( "testEnd", data ); - } ); - - QUnit.on( "runEnd", function( data ) { - - // Reduce the payload size. - // childSuites is large and unused. - data.childSuites = undefined; - - var request = send( "runEnd", data ); - request.onload = function() { - if ( request.status === 200 && request.responseText ) { - try { - var json = JSON.parse( request.responseText ); - window.location = json.url; - } catch ( e ) { - console.error( e ); - } - } - }; - } ); -} )(); diff --git a/test/runner/queue.js b/test/runner/queue.js deleted file mode 100644 index 843d5672f8..0000000000 --- a/test/runner/queue.js +++ /dev/null @@ -1,119 +0,0 @@ -import chalk from "chalk"; -import { getBrowserString } from "./lib/getBrowserString.js"; -import { - checkLastTouches, - createBrowserWorker, - restartBrowser, - setBrowserWorkerUrl -} from "./browsers.js"; - -const TEST_POLL_TIMEOUT = 1000; - -const queue = []; - -export function getNextBrowserTest( reportId ) { - const index = queue.findIndex( ( test ) => test.id === reportId ); - if ( index === -1 ) { - return; - } - - // Remove the completed test from the queue - const previousTest = queue[ index ]; - queue.splice( index, 1 ); - - // Find the next test for the same browser - for ( const test of queue.slice( index ) ) { - if ( test.fullBrowser === previousTest.fullBrowser ) { - - // Set the URL for our tracking - setBrowserWorkerUrl( test.browser, test.url ); - test.running = true; - - // Return the URL for the next test. - // listeners.js will use this to set the browser URL. - return { url: test.url }; - } - } -} - -export function retryTest( reportId, maxRetries ) { - if ( !maxRetries ) { - return; - } - const test = queue.find( ( test ) => test.id === reportId ); - if ( test ) { - test.retries++; - if ( test.retries <= maxRetries ) { - console.log( - `\nRetrying test ${ reportId } for ${ chalk.yellow( - test.options.modules.join( ", " ) - ) }...${ test.retries }` - ); - return test; - } - } -} - -export async function hardRetryTest( reportId, maxHardRetries ) { - if ( !maxHardRetries ) { - return false; - } - const test = queue.find( ( test ) => test.id === reportId ); - if ( test ) { - test.hardRetries++; - if ( test.hardRetries <= maxHardRetries ) { - console.log( - `\nHard retrying test ${ reportId } for ${ chalk.yellow( - test.options.modules.join( ", " ) - ) }...${ test.hardRetries }` - ); - await restartBrowser( test.browser ); - return true; - } - } - return false; -} - -export function addRun( url, browser, options ) { - queue.push( { - browser, - fullBrowser: getBrowserString( browser ), - hardRetries: 0, - id: options.reportId, - url, - options, - retries: 0, - running: false - } ); -} - -export async function runAll() { - return new Promise( async( resolve, reject ) => { - while ( queue.length ) { - try { - await checkLastTouches(); - } catch ( error ) { - reject( error ); - } - - // Run one test URL per browser at a time - const browsersTaken = []; - for ( const test of queue ) { - if ( browsersTaken.indexOf( test.fullBrowser ) > -1 ) { - continue; - } - browsersTaken.push( test.fullBrowser ); - if ( !test.running ) { - test.running = true; - try { - await createBrowserWorker( test.url, test.browser, test.options ); - } catch ( error ) { - reject( error ); - } - } - } - await new Promise( ( resolve ) => setTimeout( resolve, TEST_POLL_TIMEOUT ) ); - } - resolve(); - } ); -} diff --git a/test/runner/reporter.js b/test/runner/reporter.js deleted file mode 100644 index e79059648f..0000000000 --- a/test/runner/reporter.js +++ /dev/null @@ -1,132 +0,0 @@ -import chalk from "chalk"; -import { getBrowserString } from "./lib/getBrowserString.js"; -import { prettyMs } from "./lib/prettyMs.js"; -import * as Diff from "diff"; - -function serializeForDiff( value ) { - - // Use naive serialization for everything except types with confusable values - if ( typeof value === "string" ) { - return JSON.stringify( value ); - } - if ( typeof value === "bigint" ) { - return `${ value }n`; - } - return `${ value }`; -} - -export function reportTest( test, reportId, { browser, headless } ) { - if ( test.status === "passed" ) { - - // Write to console without newlines - process.stdout.write( "." ); - return; - } - - let message = `${ chalk.bold( `${ test.suiteName }: ${ test.name }` ) }`; - message += `\nTest ${ test.status } on ${ chalk.yellow( - getBrowserString( browser, headless ) - ) } (${ chalk.bold( reportId ) }).`; - - // test.assertions only contains passed assertions; - // test.errors contains all failed asssertions - if ( test.errors.length ) { - for ( const error of test.errors ) { - message += "\n"; - if ( error.message ) { - message += `\n${ error.message }`; - } - message += `\n${ chalk.gray( error.stack ) }`; - - // Show expected and actual values - // if either is defined and non-null. - // error.actual is set to null for failed - // assert.expect() assertions, so skip those as well. - // This should be fine because error.expected would - // have to also be null for this to be skipped. - if ( error.expected != null || error.actual != null ) { - message += `\nexpected: ${ chalk.red( JSON.stringify( error.expected ) ) }`; - message += `\nactual: ${ chalk.green( JSON.stringify( error.actual ) ) }`; - let diff; - - if ( Array.isArray( error.expected ) && Array.isArray( error.actual ) ) { - - // Diff arrays - diff = Diff.diffArrays( error.expected, error.actual ); - } else if ( - typeof error.expected === "object" && - typeof error.actual === "object" - ) { - - // Diff objects - diff = Diff.diffJson( error.expected, error.actual ); - } else if ( - typeof error.expected === "number" && - typeof error.actual === "number" - ) { - - // Diff numbers directly - const value = error.actual - error.expected; - if ( value > 0 ) { - diff = [ { added: true, value: `+${ value }` } ]; - } else { - diff = [ { removed: true, value: `${ value }` } ]; - } - } else if ( - typeof error.expected === "string" && - typeof error.actual === "string" - ) { - - // Diff the characters of strings - diff = Diff.diffChars( error.expected, error.actual ); - } else { - - // Diff everything else as words - diff = Diff.diffWords( - serializeForDiff( error.expected ), - serializeForDiff( error.actual ) - ); - } - - if ( diff ) { - message += "\n"; - message += diff - .map( ( part ) => { - if ( part.added ) { - return chalk.green( part.value ); - } - if ( part.removed ) { - return chalk.red( part.value ); - } - return chalk.gray( part.value ); - } ) - .join( "" ); - } - } - } - } - - console.log( `\n\n${ message }` ); - - // Only return failed messages - if ( test.status === "failed" ) { - return message; - } -} - -export function reportEnd( result, reportId, { browser, headless, modules } ) { - const fullBrowser = getBrowserString( browser, headless ); - console.log( - `\n\nTests finished in ${ prettyMs( result.runtime ) } ` + - `for ${ chalk.yellow( modules.join( "," ) ) } ` + - `in ${ chalk.yellow( fullBrowser ) } (${ chalk.bold( reportId ) })...` - ); - console.log( - ( result.status !== "passed" ? - `${ chalk.red( result.testCounts.failed ) } failed. ` : - "" ) + - `${ chalk.green( result.testCounts.total ) } passed. ` + - `${ chalk.gray( result.testCounts.skipped ) } skipped.` - ); - return result.testCounts; -} diff --git a/test/runner/run.js b/test/runner/run.js deleted file mode 100644 index 27845547c8..0000000000 --- a/test/runner/run.js +++ /dev/null @@ -1,336 +0,0 @@ -import chalk from "chalk"; -import { asyncExitHook, gracefulExit } from "exit-hook"; -import { getLatestBrowser } from "./browserstack/api.js"; -import { buildBrowserFromString } from "./browserstack/buildBrowserFromString.js"; -import { localTunnel } from "./browserstack/local.js"; -import { reportEnd, reportTest } from "./reporter.js"; -import { createTestServer } from "./createTestServer.js"; -import { buildTestUrl } from "./lib/buildTestUrl.js"; -import { generateHash, printModuleHashes } from "./lib/generateHash.js"; -import { getBrowserString } from "./lib/getBrowserString.js"; -import { modules as allModules } from "./flags/modules.js"; -import { cleanupAllBrowsers, touchBrowser } from "./browsers.js"; -import { - addRun, - getNextBrowserTest, - hardRetryTest, - retryTest, - runAll -} from "./queue.js"; - -const EXIT_HOOK_WAIT_TIMEOUT = 60 * 1000; - -/** - * Run modules in parallel in different browser instances. - */ -export async function run( { - browser: browserNames = [], - browserstack, - concurrency, - debug, - esm, - hardRetries, - headless, - isolate, - module: modules = [], - retries = 0, - runId, - verbose -} ) { - if ( !browserNames.length ) { - browserNames = [ "chrome" ]; - } - if ( !modules.length ) { - modules = allModules; - } - if ( headless && debug ) { - throw new Error( - "Cannot run in headless mode and debug mode at the same time." - ); - } - - if ( verbose ) { - console.log( browserstack ? "Running in BrowserStack." : "Running locally." ); - } - - const errorMessages = []; - const pendingErrors = {}; - - // Convert browser names to browser objects - let browsers = browserNames.map( ( b ) => ( { browser: b } ) ); - const tunnelId = generateHash( - `${ Date.now() }-${ modules.join( ":" ) }-${ ( browserstack || [] ) - .concat( browserNames ) - .join( ":" ) }` - ); - - // A unique identifier for this run - if ( !runId ) { - runId = tunnelId; - } - - // Create the test app and - // hook it up to the reporter - const reports = Object.create( null ); - const app = await createTestServer( async( message ) => { - switch ( message.type ) { - case "testEnd": { - const reportId = message.id; - const report = reports[ reportId ]; - touchBrowser( report.browser ); - const errors = reportTest( message.data, reportId, report ); - pendingErrors[ reportId ] ??= Object.create( null ); - if ( errors ) { - pendingErrors[ reportId ][ message.data.name ] = errors; - } else { - const existing = pendingErrors[ reportId ][ message.data.name ]; - - // Show a message for flakey tests - if ( existing ) { - console.log(); - console.warn( - chalk.italic( - chalk.gray( existing.replace( "Test failed", "Test flakey" ) ) - ) - ); - console.log(); - delete pendingErrors[ reportId ][ message.data.name ]; - } - } - break; - } - case "runEnd": { - const reportId = message.id; - const report = reports[ reportId ]; - touchBrowser( report.browser ); - const { failed, total } = reportEnd( - message.data, - message.id, - reports[ reportId ] - ); - report.total = total; - - // Handle failure - if ( failed ) { - const retry = retryTest( reportId, retries ); - - // Retry if retryTest returns a test - if ( retry ) { - return retry; - } - - // Return early if hardRetryTest returns true - if ( await hardRetryTest( reportId, hardRetries ) ) { - return; - } - errorMessages.push( ...Object.values( pendingErrors[ reportId ] ) ); - } - - // Run the next test - return getNextBrowserTest( reportId ); - } - case "ack": { - const report = reports[ message.id ]; - touchBrowser( report.browser ); - break; - } - default: - console.warn( "Received unknown message type:", message.type ); - } - } ); - - // Start up local test server - let server; - let port; - await new Promise( ( resolve ) => { - - // Pass 0 to choose a random, unused port - server = app.listen( 0, () => { - port = server.address().port; - resolve(); - } ); - } ); - - if ( !server || !port ) { - throw new Error( "Server not started." ); - } - - if ( verbose ) { - console.log( `Server started on port ${ port }.` ); - } - - function stopServer() { - return new Promise( ( resolve ) => { - server.close( () => { - if ( verbose ) { - console.log( "Server stopped." ); - } - resolve(); - } ); - } ); - } - - async function cleanup() { - console.log( "Cleaning up..." ); - - await cleanupAllBrowsers( { verbose } ); - - if ( tunnel ) { - await tunnel.stop(); - if ( verbose ) { - console.log( "Stopped BrowserStackLocal." ); - } - } - } - - asyncExitHook( - async() => { - await cleanup(); - await stopServer(); - }, - { wait: EXIT_HOOK_WAIT_TIMEOUT } - ); - - // Start up BrowserStackLocal - let tunnel; - if ( browserstack ) { - if ( headless ) { - console.warn( - chalk.italic( - "BrowserStack does not support headless mode. Running in normal mode." - ) - ); - headless = false; - } - - // Convert browserstack to browser objects. - // If browserstack is an empty array, fall back - // to the browsers array. - if ( browserstack.length ) { - browsers = browserstack.map( ( b ) => { - if ( !b ) { - return browsers[ 0 ]; - } - return buildBrowserFromString( b ); - } ); - } - - // Fill out browser defaults - browsers = await Promise.all( - browsers.map( async( browser ) => { - - // Avoid undici connect timeout errors - await new Promise( ( resolve ) => setTimeout( resolve, 100 ) ); - - const latestMatch = await getLatestBrowser( browser ); - if ( !latestMatch ) { - console.error( - chalk.red( `Browser not found: ${ getBrowserString( browser ) }.` ) - ); - gracefulExit( 1 ); - } - return latestMatch; - } ) - ); - - tunnel = await localTunnel( tunnelId ); - if ( verbose ) { - console.log( "Started BrowserStackLocal." ); - - printModuleHashes( modules ); - } - } - - function queueRun( modules, browser ) { - const fullBrowser = getBrowserString( browser, headless ); - const reportId = generateHash( `${ modules.join( ":" ) } ${ fullBrowser }` ); - reports[ reportId ] = { browser, headless, modules }; - - const url = buildTestUrl( modules, { - browserstack, - esm, - jsdom: browser.browser === "jsdom", - port, - reportId - } ); - - const options = { - browserstack, - concurrency, - debug, - headless, - modules, - reportId, - runId, - tunnelId, - verbose - }; - - addRun( url, browser, options ); - } - - for ( const browser of browsers ) { - if ( isolate ) { - for ( const module of modules ) { - queueRun( [ module ], browser ); - } - } else { - queueRun( modules, browser ); - } - } - - try { - console.log( `Starting Run ${ runId }...` ); - await runAll(); - } catch ( error ) { - console.error( error ); - if ( !debug ) { - gracefulExit( 1 ); - } - } finally { - console.log(); - if ( errorMessages.length === 0 ) { - let stop = false; - for ( const report of Object.values( reports ) ) { - if ( !report.total ) { - stop = true; - console.error( - chalk.red( - `No tests were run for ${ report.modules.join( - ", " - ) } in ${ getBrowserString( report.browser ) }` - ) - ); - } - } - if ( stop ) { - return gracefulExit( 1 ); - } - console.log( chalk.green( "All tests passed!" ) ); - - if ( !debug || browserstack ) { - gracefulExit( 0 ); - } - } else { - console.error( chalk.red( `${ errorMessages.length } tests failed.` ) ); - console.log( - errorMessages.map( ( error, i ) => `\n${ i + 1 }. ${ error }` ).join( "\n" ) - ); - - if ( debug ) { - console.log(); - if ( browserstack ) { - console.log( "Leaving browsers with failures open for debugging." ); - console.log( - "View running sessions at https://automate.browserstack.com/dashboard/v2/" - ); - } else { - console.log( "Leaving browsers open for debugging." ); - } - console.log( "Press Ctrl+C to exit." ); - } else { - gracefulExit( 1 ); - } - } - } -} diff --git a/test/runner/selenium/createDriver.js b/test/runner/selenium/createDriver.js deleted file mode 100644 index 095c12214d..0000000000 --- a/test/runner/selenium/createDriver.js +++ /dev/null @@ -1,84 +0,0 @@ -import { Builder, Capabilities, logging } from "selenium-webdriver"; -import Chrome from "selenium-webdriver/chrome.js"; -import Edge from "selenium-webdriver/edge.js"; -import Firefox from "selenium-webdriver/firefox.js"; -import { browserSupportsHeadless } from "../lib/getBrowserString.js"; - -// Set script timeout to 10min -const DRIVER_SCRIPT_TIMEOUT = 1000 * 60 * 10; - -export default async function createDriver( { browserName, headless, url, verbose } ) { - const capabilities = Capabilities[ browserName ](); - const prefs = new logging.Preferences(); - prefs.setLevel( logging.Type.BROWSER, logging.Level.ALL ); - capabilities.setLoggingPrefs( prefs ); - - let driver = new Builder().withCapabilities( capabilities ); - - const chromeOptions = new Chrome.Options(); - chromeOptions.addArguments( "--enable-chrome-browser-cloud-management" ); - - // Alter the chrome binary path if - // the CHROME_BIN environment variable is set - if ( process.env.CHROME_BIN ) { - if ( verbose ) { - console.log( `Setting chrome binary to ${ process.env.CHROME_BIN }` ); - } - chromeOptions.setChromeBinaryPath( process.env.CHROME_BIN ); - } - - const firefoxOptions = new Firefox.Options(); - - if ( process.env.FIREFOX_BIN ) { - if ( verbose ) { - console.log( `Setting firefox binary to ${ process.env.FIREFOX_BIN }` ); - } - - firefoxOptions.setBinary( process.env.FIREFOX_BIN ); - } - - const edgeOptions = new Edge.Options(); - edgeOptions.addArguments( "--enable-chrome-browser-cloud-management" ); - - // Alter the edge binary path if - // the EDGE_BIN environment variable is set - if ( process.env.EDGE_BIN ) { - if ( verbose ) { - console.log( `Setting edge binary to ${ process.env.EDGE_BIN }` ); - } - edgeOptions.setEdgeChromiumBinaryPath( process.env.EDGE_BIN ); - } - - if ( headless ) { - chromeOptions.addArguments( "--headless=new" ); - firefoxOptions.addArguments( "--headless" ); - edgeOptions.addArguments( "--headless=new" ); - if ( !browserSupportsHeadless( browserName ) ) { - console.log( - `Headless mode is not supported for ${ browserName }.` + - "Running in normal mode instead." - ); - } - } - - driver = await driver - .setChromeOptions( chromeOptions ) - .setFirefoxOptions( firefoxOptions ) - .setEdgeOptions( edgeOptions ) - .build(); - - if ( verbose ) { - const driverCapabilities = await driver.getCapabilities(); - const name = driverCapabilities.getBrowserName(); - const version = driverCapabilities.getBrowserVersion(); - console.log( `\nDriver created for ${ name } ${ version }` ); - } - - // Increase script timeout to 10min - await driver.manage().setTimeouts( { script: DRIVER_SCRIPT_TIMEOUT } ); - - // Set the first URL for the browser - await driver.get( url ); - - return driver; -} diff --git a/test/runner/server.js b/test/runner/server.js deleted file mode 100644 index 09fe0da4c6..0000000000 --- a/test/runner/server.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createTestServer } from "./createTestServer.js"; - -const port = process.env.PORT || 3000; - -async function runServer() { - const app = await createTestServer(); - - app.listen( { port, host: "0.0.0.0" }, function() { - console.log( `Open tests at http://localhost:${ port }/test/` ); - } ); -} - -runServer(); diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 53dc9c5d22..4f843f2e53 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -2782,7 +2782,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re } ); } ); - QUnit.test( "jQuery.get( String, null-ish, String ) - dataType with null callback (gh-4989)", + QUnit.test( "jQuery.get( String, null, String ) - dataType with null callback (gh-4989)", function( assert ) { assert.expect( 2 ); var done = assert.async( 2 ); @@ -2802,6 +2802,37 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re } ); } ); + QUnit.test( "jQuery.get( String, null-ish, null-ish, String ) - dataType with null/undefined data & callback", + function( assert ) { + assert.expect( 8 ); + var done = assert.async( 8 ); + + [ + { data: null, success: null }, + { data: null, success: undefined }, + { data: undefined, success: null }, + { data: undefined, success: undefined } + ].forEach( function( options ) { + var data = options.data, + success = options.success; + jQuery.get( url( "mock.php?action=json&header" ), data, success, "json" ) + .then( function( json ) { + assert.deepEqual( json, { data: { lang: "en", length: 25 } }, + "`dataType: \"json\"` applied with `" + data + "` data & `" + + success + "` success callback" ); + done(); + } ); + + jQuery.get( url( "mock.php?action=json&header" ), data, success, "text" ) + .then( function( text ) { + assert.strictEqual( text, "{\"data\":{\"lang\":\"en\",\"length\":25}}", + "`dataType: \"text\"` applied with `" + data + "` data & `" + + success + "` success callback" ); + done(); + } ); + } ); + } ); + //----------- jQuery.getJSON() QUnit.test( "jQuery.getJSON( String, Hash, Function ) - JSON array", function( assert ) { diff --git a/test/unit/attributes.js b/test/unit/attributes.js index b02e99e875..5ace087b96 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -63,11 +63,11 @@ QUnit.test( "attr(String)", function( assert ) { assert.equal( jQuery( "#text1" ).attr( "type" ), "text", "Check for type attribute" ); assert.equal( jQuery( "#radio1" ).attr( "type" ), "radio", "Check for type attribute" ); assert.equal( jQuery( "#check1" ).attr( "type" ), "checkbox", "Check for type attribute" ); - assert.equal( jQuery( "#simon1" ).attr( "rel" ), "bookmark", "Check for rel attribute" ); + assert.equal( jQuery( "#john1" ).attr( "rel" ), "bookmark", "Check for rel attribute" ); assert.equal( jQuery( "#google" ).attr( "title" ), "Google!", "Check for title attribute" ); - assert.equal( jQuery( "#mark" ).attr( "hreflang" ), "en", "Check for hreflang attribute" ); + assert.equal( jQuery( "#mozilla" ).attr( "hreflang" ), "en", "Check for hreflang attribute" ); assert.equal( jQuery( "#en" ).attr( "lang" ), "en", "Check for lang attribute" ); - assert.equal( jQuery( "#simon" ).attr( "class" ), "blog link", "Check for class attribute" ); + assert.equal( jQuery( "#timmy" ).attr( "class" ), "blog link", "Check for class attribute" ); assert.equal( jQuery( "#name" ).attr( "name" ), "name", "Check for name attribute" ); assert.equal( jQuery( "#text1" ).attr( "name" ), "action", "Check for name attribute" ); assert.ok( jQuery( "#form" ).attr( "action" ).indexOf( "formaction" ) >= 0, "Check for action attribute" ); @@ -479,6 +479,24 @@ QUnit.test( "attr(String, Object)", function( assert ) { assert.equal( jQuery( "#name" ).attr( "nonexisting", undefined ).attr( "nonexisting" ), undefined, ".attr('attribute', undefined) does not create attribute (trac-5571)" ); } ); +QUnit.test( "attr( previously-boolean-attr, non-boolean-value)", function( assert ) { + assert.expect( 3 ); + + var div = jQuery( "
" ).appendTo( "#qunit-fixture" ); + + div.attr( "hidden", "foo" ); + assert.strictEqual( div.attr( "hidden" ), "foo", + "Values not normalized for previously-boolean hidden attribute" ); + + div.attr( "hidden", "until-found" ); + assert.strictEqual( div.attr( "hidden" ), "until-found", + "`until-found` value preserved for hidden attribute" ); + + div.attr( "hiDdeN", "uNtil-fOund" ); + assert.strictEqual( div.attr( "hidden" ), "uNtil-fOund", + "`uNtil-fOund` different casing preserved" ); +} ); + QUnit.test( "attr(non-ASCII)", function( assert ) { assert.expect( 2 ); diff --git a/test/unit/core.js b/test/unit/core.js index aa1f637dd3..2bd73fa9bf 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -589,14 +589,14 @@ QUnit.test( "inArray()", function( assert ) { p: q( "firstp", "sap", "ap", "first" ), em: q( "siblingnext", "siblingfirst" ), div: q( "qunit-testrunner-toolbar", "nothiddendiv", "nothiddendivchild", "foo" ), - a: q( "mark", "groups", "google", "simon1" ), + a: q( "mozilla", "groups", "google", "john1" ), empty: [] }, tests = { p: { elem: jQuery( "#ap" )[ 0 ], index: 2 }, em: { elem: jQuery( "#siblingfirst" )[ 0 ], index: 1 }, div: { elem: jQuery( "#nothiddendiv" )[ 0 ], index: 1 }, - a: { elem: jQuery( "#simon1" )[ 0 ], index: 3 } + a: { elem: jQuery( "#john1" )[ 0 ], index: 3 } }, falseTests = { p: jQuery( "#liveSpan1" )[ 0 ], @@ -654,13 +654,13 @@ QUnit.test( "slice()", function( assert ) { var $links = jQuery( "#ap a" ); assert.deepEqual( $links.slice( 1, 2 ).get(), q( "groups" ), "slice(1,2)" ); - assert.deepEqual( $links.slice( 1 ).get(), q( "groups", "anchor1", "mark" ), "slice(1)" ); + assert.deepEqual( $links.slice( 1 ).get(), q( "groups", "anchor1", "mozilla" ), "slice(1)" ); assert.deepEqual( $links.slice( 0, 3 ).get(), q( "google", "groups", "anchor1" ), "slice(0,3)" ); - assert.deepEqual( $links.slice( -1 ).get(), q( "mark" ), "slice(-1)" ); + assert.deepEqual( $links.slice( -1 ).get(), q( "mozilla" ), "slice(-1)" ); assert.deepEqual( $links.eq( 1 ).get(), q( "groups" ), "eq(1)" ); assert.deepEqual( $links.eq( "2" ).get(), q( "anchor1" ), "eq('2')" ); - assert.deepEqual( $links.eq( -1 ).get(), q( "mark" ), "eq(-1)" ); + assert.deepEqual( $links.eq( -1 ).get(), q( "mozilla" ), "eq(-1)" ); } ); QUnit.test( "first()/last()", function( assert ) { @@ -669,7 +669,7 @@ QUnit.test( "first()/last()", function( assert ) { var $links = jQuery( "#ap a" ), $none = jQuery( "asdf" ); assert.deepEqual( $links.first().get(), q( "google" ), "first()" ); - assert.deepEqual( $links.last().get(), q( "mark" ), "last()" ); + assert.deepEqual( $links.last().get(), q( "mozilla" ), "last()" ); assert.deepEqual( $none.first().get(), [], "first() none" ); assert.deepEqual( $none.last().get(), [], "last() none" ); @@ -681,7 +681,7 @@ QUnit.test( "even()/odd()", function( assert ) { var $links = jQuery( "#ap a" ), $none = jQuery( "asdf" ); assert.deepEqual( $links.even().get(), q( "google", "anchor1" ), "even()" ); - assert.deepEqual( $links.odd().get(), q( "groups", "mark" ), "odd()" ); + assert.deepEqual( $links.odd().get(), q( "groups", "mozilla" ), "odd()" ); assert.deepEqual( $none.even().get(), [], "even() none" ); assert.deepEqual( $none.odd().get(), [], "odd() none" ); @@ -694,7 +694,7 @@ QUnit.test( "map()", function( assert ) { jQuery( "#ap" ).map( function() { return jQuery( this ).find( "a" ).get(); } ).get(), - q( "google", "groups", "anchor1", "mark" ), + q( "google", "groups", "anchor1", "mozilla" ), "Array Map" ); diff --git a/test/unit/css.js b/test/unit/css.js index d872e52bbe..8a96fb3578 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -1379,7 +1379,7 @@ testIframe( function( assert, jQuery, window, document, widthBeforeSet, widthAfterSet ) { assert.expect( 2 ); - // Support: Firefox 126+ + // Support: Firefox 126 - 135+ // Newer Firefox implements CSS zoom in a way it affects // those values slightly. assert.ok( /^100(?:|\.0\d*)px$/.test( widthBeforeSet ), "elem.css('width') works correctly with browser zoom" ); @@ -1705,7 +1705,7 @@ QUnit.test( "Do not throw on frame elements from css method (trac-15098)", funct ( function() { var vendorPrefixes = [ "Webkit", "Moz", "ms" ]; - QUnit.test( "Don't default to a cached previously used wrong prefixed name (gh-2015)", function( assert ) { + QUnit.test( "Don't default to a previously used wrong prefixed name (gh-2015)", function( assert ) { // Note: this test needs a property we know is only supported in a prefixed version // by at least one of our main supported browsers. This may get out of date so let's @@ -1759,18 +1759,32 @@ QUnit.test( "Do not throw on frame elements from css method (trac-15098)", funct 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 ) { - assert.expect( 1 ); +} )(); - var elem = jQuery( "
" ), - style = elem[ 0 ].style; - style.MozFakeProperty = "old value"; - elem.css( "fakeProperty", "new value" ); +QUnit.test( "Don't update existing unsupported prefixed properties", function( assert ) { + assert.expect( 1 ); - assert.equal( style.MozFakeProperty, "old value", "Fake prefixed property is not cached" ); - } ); + var elem = jQuery( "
" ), + style = elem[ 0 ].style; + style.MozFakeProperty = "old value"; + elem.css( "fakeProperty", "new value" ); -} )(); + assert.equal( style.MozFakeProperty, "old value", "Fake prefixed property is not set" ); +} ); + +QUnit.test( "Don't set fake prefixed properties when a regular one is missing", function( assert ) { + assert.expect( 5 ); + + var elem = jQuery( "
" ), + style = elem[ 0 ].style; + elem.css( "fakeProperty", "fake value" ); + + assert.strictEqual( style.fakeProperty, "fake value", "Fake unprefixed property is set" ); + assert.strictEqual( style.webkitFakeProperty, undefined, "Fake prefixed property is not set (webkit)" ); + assert.strictEqual( style.WebkitFakeProperty, undefined, "Fake prefixed property is not set (Webkit)" ); + assert.strictEqual( style.MozFakeProperty, undefined, "Fake prefixed property is not set (Moz)" ); + assert.strictEqual( style.msFakeProperty, undefined, "Fake prefixed property is not set (ms)" ); +} ); // IE doesn't support CSS variables. QUnit.testUnlessIE( "css(--customProperty)", function( assert ) { @@ -1798,14 +1812,9 @@ QUnit.testUnlessIE( "css(--customProperty)", function( assert ) { var div = jQuery( "
" ).appendTo( "#qunit-fixture" ), $elem = jQuery( "
" ).addClass( "test__customProperties" ) - .appendTo( "#qunit-fixture" ), - webkitOrBlink = /webkit\b/i.test( navigator.userAgent ), - expected = 20; + .appendTo( "#qunit-fixture" ); - if ( webkitOrBlink ) { - expected -= 2; - } - assert.expect( expected ); + assert.expect( 20 ); div.css( "--color", "blue" ); assert.equal( div.css( "--color" ), "blue", "Modified CSS custom property using string" ); @@ -1834,13 +1843,15 @@ QUnit.testUnlessIE( "css(--customProperty)", function( assert ) { assert.equal( $elem.css( "--prop5" ), "val5", "Multiple Following whitespace trimmed" ); assert.equal( $elem.css( "--prop6" ), "val6", "Preceding and Following whitespace trimmed" ); assert.equal( $elem.css( "--prop7" ), "val7", "Multiple preceding and following whitespace trimmed" ); + assert.equal( $elem.css( "--prop8" ), "\"val8\"", "Works with double quotes" ); - // Support: Chrome <=49 - 73+, Safari <=9.1 - 12.1+ - // Chrome treats single quotes as double ones. - // Safari treats double quotes as single ones. - if ( !webkitOrBlink ) { - assert.equal( $elem.css( "--prop8" ), "\"val8\"", "Works with double quotes" ); + // Support: Safari <=9.1 - 18.1+ + // Safari converts single quotes to double ones. + if ( !/\bapplewebkit\/605\.1\.15\b/i.test( navigator.userAgent ) ) { assert.equal( $elem.css( "--prop9" ), "'val9'", "Works with single quotes" ); + } else { + assert.equal( $elem.css( "--prop9" ).replace( /"/g, "'" ), "'val9'", + "Works with single quotes, but they may be changed to double ones" ); } assert.equal( $elem.css( "--prop10" ), "val10", "Multiple preceding and following escaped unicode whitespace trimmed" ); diff --git a/test/unit/deprecated.js b/test/unit/deprecated.js index 7958ca450e..dd9aaee9ce 100644 --- a/test/unit/deprecated.js +++ b/test/unit/deprecated.js @@ -93,10 +93,10 @@ QUnit.test( "trigger() shortcuts", function( assert ) { assert.equal( counter, 1, "Check that click, triggers onclick event handler also" ); clickCounter = 0; - jQuery( "#simon1" )[ 0 ].onclick = function() { + jQuery( "#john1" )[ 0 ].onclick = function() { clickCounter++; }; - jQuery( "#simon1" ).click(); + jQuery( "#john1" ).click(); assert.equal( clickCounter, 1, "Check that click, triggers onclick event handler on an a tag also" ); } ); @@ -205,4 +205,17 @@ QUnit.test( "jQuery.proxy", function( assert ) { cb.call( thisObject, "arg3" ); } ); +if ( includesModule( "selector" ) ) { + QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( + "jQuery.expr[ \":\" ], jQuery.expr.filters", + function( assert ) { + assert.expect( 2 ); + + assert.strictEqual( jQuery.expr[ ":" ], jQuery.expr.pseudos, + "jQuery.expr[ \":\" ] is an alias of jQuery.expr.pseudos" ); + assert.strictEqual( jQuery.expr.filters, jQuery.expr.pseudos, + "jQuery.expr.filters is an alias of jQuery.expr.pseudos" ); + } ); +} + } diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js index 6b0c9c7982..3a8b988f17 100644 --- a/test/unit/dimensions.js +++ b/test/unit/dimensions.js @@ -345,40 +345,94 @@ QUnit.test( "child of a hidden elem (or unconnected node) has accurate inner/out $divNormal.remove(); } ); -QUnit.test( "getting dimensions shouldn't modify runtimeStyle see trac-9233", function( assert ) { - assert.expect( 1 ); +QUnit.test( "hidden element with dimensions from a stylesheet", function( assert ) { + assert.expect( 2 ); - var $div = jQuery( "
" ).appendTo( "#qunit-fixture" ), - div = $div.get( 0 ), - runtimeStyle = div.runtimeStyle; + var div = jQuery( "" + + "
" + + " " + + "
" + + "" ) + .appendTo( "#qunit-fixture" ); - if ( runtimeStyle ) { - div.runtimeStyle.marginLeft = "12em"; - div.runtimeStyle.left = "11em"; - } + assert.strictEqual( div.width(), 111, "width of a hidden element" ); + assert.strictEqual( div.height(), 123, "height of a hidden element" ); +} ); + +QUnit.test( "hidden element with implicit content-based dimensions", function( assert ) { + assert.expect( 2 ); - $div.outerWidth( true ); + var container = jQuery( "" + - if ( runtimeStyle ) { - assert.equal( div.runtimeStyle.left, "11em", "getting dimensions modifies runtimeStyle, see trac-9233" ); - } else { - assert.ok( true, "this browser doesn't support runtimeStyle, see trac-9233" ); - } + // font-size affects the child dimensions implicitly + "
" + + "
" + + "
" + + "
" + + "
" + + "" ), + div = container.children().first(); - $div.remove(); + container.appendTo( "#qunit-fixture" ); + + assert.strictEqual( div.width(), 60, "width of a hidden element" ); + assert.strictEqual( div.height(), 40, "height of a hidden element" ); } ); QUnit.test( "table dimensions", function( assert ) { - assert.expect( 2 ); - - var table = jQuery( "
a
a
" ).appendTo( "#qunit-fixture" ), + assert.expect( 3 ); + + var table = jQuery( "" + + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + "
ab
ab
" + ).appendTo( "#qunit-fixture" ), tdElem = table.find( "td" ).first(), - colElem = table.find( "col" ).first().width( 300 ); + colElem = table.find( "col" ).first(), + doubleColElem = table.find( ".col-double" ); + + table.find( "td" ).css( { margin: 0, padding: 0, border: 0 } ); - table.find( "td" ).css( { "margin": 0, "padding": 0 } ); + colElem.width( 300 ); + + table.find( ".td-a-1" ).width( 200 ); + table.find( ".td-b-1" ).width( 400 ); assert.equal( tdElem.width(), tdElem.width(), "width() doesn't alter dimension values of empty cells, see trac-11293" ); - assert.equal( colElem.width(), 300, "col elements have width(), see trac-12243" ); + assert.equal( colElem.width(), 300, "col elements have width(), (trac-12243)" ); + + // Support: IE 11+ + // In IE, `` computed width is `"auto"` unless `width` is set + // explicitly via CSS so measurements there remain incorrect. Because of + // the lack of a proper workaround, we accept this limitation. + // To make IE pass the test, set the width explicitly. + if ( QUnit.isIE ) { + doubleColElem.width( 600 ); + } + + assert.equal( doubleColElem.width(), 600, + "col with span measured correctly (gh-5628)" ); } ); QUnit.test( "SVG dimensions (basic content-box)", function( assert ) { @@ -691,7 +745,7 @@ QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) { .appendTo( "#qunit-fixture" ), // Workarounds for IE kill fractional output here. - fraction = document.documentMode ? 0 : 0.5, + fraction = QUnit.isIE ? 0 : 0.5, borderWidth = 1, padding = 2, size = 100 + fraction, diff --git a/test/unit/effects.js b/test/unit/effects.js index 2ba9ee6860..cc0526f1df 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -1708,7 +1708,7 @@ QUnit.test( "hide, fadeOut and slideUp called on element width height and width QUnit.test( "hide should not leave hidden inline elements visible (trac-14848)", function( assert ) { assert.expect( 2 ); - var el = jQuery( "#simon1" ); + var el = jQuery( "#john1" ); el.hide( 1, function() { assert.equal( el.css( "display" ), "none", "hidden" ); diff --git a/test/unit/event.js b/test/unit/event.js index 89d48c420a..dac53ed93d 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -1438,7 +1438,7 @@ QUnit.test( "Submit event can be stopped (trac-11049)", function( assert ) { form.remove(); } ); -// Support: iOS <=7 - 12+ +// Support: iOS <=7 - 18+ // iOS has the window.onbeforeunload field but doesn't support the beforeunload // handler making it impossible to feature-detect the support. QUnit[ /(ipad|iphone|ipod)/i.test( navigator.userAgent ) ? "skip" : "test" ]( @@ -2707,7 +2707,7 @@ testIframe( // IE does propagate the event to the parent document. In this test // we mainly care about the inner element so we'll just skip this one // assertion in IE. - if ( !document.documentMode ) { + if ( !QUnit.isIE ) { assert.ok( false, "fired a focusin event in the parent document" ); } } ); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index b91d72b298..f4941b8904 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -34,8 +34,8 @@ QUnit.test( "text()", function( assert ) { var expected, frag, $newLineTest, doc; - expected = "This link has class=\"blog\": Simon Willison's Weblog"; - assert.equal( jQuery( "#sap" ).text(), expected, "Check for merged text of more then one element." ); + expected = "This link has class=\"blog\": Timmy Willison's Weblog"; + assert.equal( jQuery( "#sap" ).text(), expected, "Check for merged text of more than one element." ); // Check serialization of text values assert.equal( jQuery( document.createTextNode( "foo" ) ).text(), "foo", "Text node was retrieved from .text()." ); @@ -110,7 +110,7 @@ QUnit.test( "text(Function) with incoming value", function( assert ) { assert.expect( 2 ); - var old = "This link has class=\"blog\": Simon Willison's Weblog"; + var old = "This link has class=\"blog\": Timmy Willison's Weblog"; jQuery( "#sap" ).text( function( i, val ) { assert.equal( val, old, "Make sure the incoming value is correct." ); @@ -123,7 +123,7 @@ QUnit.test( "text(Function) with incoming value", function( assert ) { function testAppendForObject( valueObj, isFragment, assert ) { var $base, type = isFragment ? " (DocumentFragment)" : " (Element)", - text = "This link has class=\"blog\": Simon Willison's Weblog", + text = "This link has class=\"blog\": Timmy Willison's Weblog", el = document.getElementById( "sap" ).cloneNode( true ), first = document.getElementById( "first" ), yahoo = document.getElementById( "yahoo" ); @@ -357,7 +357,7 @@ QUnit.test( "append(Function) returns String", function( assert ) { QUnit.test( "append(Function) returns Element", function( assert ) { assert.expect( 2 ); - var expected = "This link has class=\"blog\": Simon Willison's WeblogTry them out:", + var expected = "This link has class=\"blog\": Timmy Willison's WeblogTry them out:", old = jQuery( "#sap" ).html(); jQuery( "#sap" ).append( function( i, val ) { @@ -370,7 +370,7 @@ QUnit.test( "append(Function) returns Element", function( assert ) { QUnit.test( "append(Function) returns Array", function( assert ) { assert.expect( 2 ); - var expected = "This link has class=\"blog\": Simon Willison's WeblogTry them out:Yahoo", + var expected = "This link has class=\"blog\": Timmy Willison's WeblogTry them out:Yahoo", old = jQuery( "#sap" ).html(); jQuery( "#sap" ).append( function( i, val ) { @@ -383,7 +383,7 @@ QUnit.test( "append(Function) returns Array", function( assert ) { QUnit.test( "append(Function) returns jQuery", function( assert ) { assert.expect( 2 ); - var expected = "This link has class=\"blog\": Simon Willison's WeblogYahooTry them out:", + var expected = "This link has class=\"blog\": Timmy Willison's WeblogYahooTry them out:", old = jQuery( "#sap" ).html(); jQuery( "#sap" ).append( function( i, val ) { @@ -616,11 +616,11 @@ QUnit.test( "appendTo(Element|Array)", function( assert ) { assert.expect( 2 ); - var expected = "This link has class=\"blog\": Simon Willison's WeblogTry them out:"; + var expected = "This link has class=\"blog\": Timmy Willison's WeblogTry them out:"; jQuery( document.getElementById( "first" ) ).appendTo( "#sap" ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for appending of element" ); - expected = "This link has class=\"blog\": Simon Willison's WeblogTry them out:Yahoo"; + expected = "This link has class=\"blog\": Timmy Willison's WeblogTry them out:Yahoo"; jQuery( [ document.getElementById( "first" ), document.getElementById( "yahoo" ) ] ).appendTo( "#sap" ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for appending of array of elements" ); @@ -633,7 +633,7 @@ QUnit.test( "appendTo(jQuery)", function( assert ) { var expected, num, div; assert.ok( jQuery( document.createElement( "script" ) ).appendTo( "body" ).length, "Make sure a disconnected script can be appended." ); - expected = "This link has class=\"blog\": Simon Willison's WeblogYahooTry them out:"; + expected = "This link has class=\"blog\": Timmy Willison's WeblogYahooTry them out:"; jQuery( "#yahoo, #first" ).appendTo( "#sap" ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for appending of jQuery object" ); @@ -686,7 +686,7 @@ QUnit.test( "prepend(Element)", function( assert ) { assert.expect( 1 ); var expected; - expected = "Try them out:This link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:This link has class=\"blog\": Timmy Willison's Weblog"; jQuery( "#sap" ).prepend( document.getElementById( "first" ) ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of element" ); } ); @@ -696,7 +696,7 @@ QUnit.test( "prepend(Array)", function( assert ) { assert.expect( 1 ); var expected; - expected = "Try them out:YahooThis link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:YahooThis link has class=\"blog\": Timmy Willison's Weblog"; jQuery( "#sap" ).prepend( [ document.getElementById( "first" ), document.getElementById( "yahoo" ) ] ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of array of elements" ); } ); @@ -706,7 +706,7 @@ QUnit.test( "prepend(jQuery)", function( assert ) { assert.expect( 1 ); var expected; - expected = "YahooTry them out:This link has class=\"blog\": Simon Willison's Weblog"; + expected = "YahooTry them out:This link has class=\"blog\": Timmy Willison's Weblog"; jQuery( "#sap" ).prepend( jQuery( "#yahoo, #first" ) ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of jQuery object" ); } ); @@ -716,7 +716,7 @@ QUnit.test( "prepend(Array)", function( assert ) { assert.expect( 1 ); var expected; - expected = "Try them out:GoogleYahooThis link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:GoogleYahooThis link has class=\"blog\": Timmy Willison's Weblog"; jQuery( "#sap" ).prepend( [ jQuery( "#first" ), jQuery( "#yahoo, #google" ) ] ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of array of jQuery objects" ); } ); @@ -749,7 +749,7 @@ QUnit.test( "prepend(Function) with incoming value -- Element", function( assert assert.expect( 2 ); var old, expected; - expected = "Try them out:This link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:This link has class=\"blog\": Timmy Willison's Weblog"; old = jQuery( "#sap" ).html(); jQuery( "#sap" ).prepend( function( i, val ) { @@ -765,7 +765,7 @@ QUnit.test( "prepend(Function) with incoming value -- Array", function( assert.expect( 2 ); var old, expected; - expected = "Try them out:YahooThis link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:YahooThis link has class=\"blog\": Timmy Willison's Weblog"; old = jQuery( "#sap" ).html(); jQuery( "#sap" ).prepend( function( i, val ) { @@ -781,7 +781,7 @@ QUnit.test( "prepend(Function) with incoming value -- jQuery", function( assert assert.expect( 2 ); var old, expected; - expected = "YahooTry them out:This link has class=\"blog\": Simon Willison's Weblog"; + expected = "YahooTry them out:This link has class=\"blog\": Timmy Willison's Weblog"; old = jQuery( "#sap" ).html(); jQuery( "#sap" ).prepend( function( i, val ) { @@ -811,7 +811,7 @@ QUnit.test( "prependTo(Element)", function( assert ) { var expected; - expected = "Try them out:This link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:This link has class=\"blog\": Timmy Willison's Weblog"; jQuery( document.getElementById( "first" ) ).prependTo( "#sap" ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of element" ); } ); @@ -822,7 +822,7 @@ QUnit.test( "prependTo(Array)", function( assert ) { var expected; - expected = "Try them out:YahooThis link has class=\"blog\": Simon Willison's Weblog"; + expected = "Try them out:YahooThis link has class=\"blog\": Timmy Willison's Weblog"; jQuery( [ document.getElementById( "first" ), document.getElementById( "yahoo" ) ] ).prependTo( "#sap" ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of array of elements" ); } ); @@ -833,7 +833,7 @@ QUnit.test( "prependTo(jQuery)", function( assert ) { var expected; - expected = "YahooTry them out:This link has class=\"blog\": Simon Willison's Weblog"; + expected = "YahooTry them out:This link has class=\"blog\": Timmy Willison's Weblog"; jQuery( "#yahoo, #first" ).prependTo( "#sap" ); assert.equal( jQuery( "#sap" ).text(), expected, "Check for prepending of jQuery object" ); } ); @@ -875,8 +875,8 @@ QUnit.test( "before(Array)", function( assert ) { assert.expect( 1 ); var expected; - expected = "This is a normal link: Try them out:diveintomarkYahoo"; - jQuery( "#yahoo" ).before( manipulationBareObj( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ) ); + expected = "This is a normal link: Try them out:mozillaYahoo"; + jQuery( "#yahoo" ).before( manipulationBareObj( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of elements before" ); } ); @@ -885,8 +885,8 @@ QUnit.test( "before(jQuery)", function( assert ) { assert.expect( 1 ); var expected; - expected = "This is a normal link: diveintomarkTry them out:Yahoo"; - jQuery( "#yahoo" ).before( manipulationBareObj( jQuery( "#mark, #first" ) ) ); + expected = "This is a normal link: mozillaTry them out:Yahoo"; + jQuery( "#yahoo" ).before( manipulationBareObj( jQuery( "#mozilla, #first" ) ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert jQuery before" ); } ); @@ -895,8 +895,8 @@ QUnit.test( "before(Array)", function( assert ) { assert.expect( 1 ); var expected; - expected = "This is a normal link: Try them out:GooglediveintomarkYahoo"; - jQuery( "#yahoo" ).before( manipulationBareObj( [ jQuery( "#first" ), jQuery( "#mark, #google" ) ] ) ); + expected = "This is a normal link: Try them out:GooglemozillaYahoo"; + jQuery( "#yahoo" ).before( manipulationBareObj( [ jQuery( "#first" ), jQuery( "#mozilla, #google" ) ] ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of jQuery objects before" ); } ); @@ -927,8 +927,8 @@ QUnit.test( "before(Function) -- Returns Array", function( assert ) { assert.expect( 1 ); var expected; - expected = "This is a normal link: Try them out:diveintomarkYahoo"; - jQuery( "#yahoo" ).before( manipulationFunctionReturningObj( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ) ); + expected = "This is a normal link: Try them out:mozillaYahoo"; + jQuery( "#yahoo" ).before( manipulationFunctionReturningObj( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of elements before" ); } ); @@ -937,8 +937,8 @@ QUnit.test( "before(Function) -- Returns jQuery", function( assert ) { assert.expect( 1 ); var expected; - expected = "This is a normal link: diveintomarkTry them out:Yahoo"; - jQuery( "#yahoo" ).before( manipulationFunctionReturningObj( jQuery( "#mark, #first" ) ) ); + expected = "This is a normal link: mozillaTry them out:Yahoo"; + jQuery( "#yahoo" ).before( manipulationFunctionReturningObj( jQuery( "#mozilla, #first" ) ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert jQuery before" ); } ); @@ -947,8 +947,8 @@ QUnit.test( "before(Function) -- Returns Array", function( assert ) { assert.expect( 1 ); var expected; - expected = "This is a normal link: Try them out:GooglediveintomarkYahoo"; - jQuery( "#yahoo" ).before( manipulationFunctionReturningObj( [ jQuery( "#first" ), jQuery( "#mark, #google" ) ] ) ); + expected = "This is a normal link: Try them out:GooglemozillaYahoo"; + jQuery( "#yahoo" ).before( manipulationFunctionReturningObj( [ jQuery( "#first" ), jQuery( "#mozilla, #google" ) ] ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of jQuery objects before" ); } ); @@ -1042,8 +1042,8 @@ QUnit.test( "insertBefore(Array)", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: Try them out:diveintomarkYahoo"; - jQuery( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ).insertBefore( "#yahoo" ); + var expected = "This is a normal link: Try them out:mozillaYahoo"; + jQuery( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ).insertBefore( "#yahoo" ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of elements before" ); } ); @@ -1051,8 +1051,8 @@ QUnit.test( "insertBefore(jQuery)", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: diveintomarkTry them out:Yahoo"; - jQuery( "#mark, #first" ).insertBefore( "#yahoo" ); + var expected = "This is a normal link: mozillaTry them out:Yahoo"; + jQuery( "#mozilla, #first" ).insertBefore( "#yahoo" ); assert.equal( jQuery( "#en" ).text(), expected, "Insert jQuery before" ); } ); @@ -1078,8 +1078,8 @@ QUnit.test( ".after(Array)", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: YahooTry them out:diveintomark"; - jQuery( "#yahoo" ).after( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ); + var expected = "This is a normal link: YahooTry them out:mozilla"; + jQuery( "#yahoo" ).after( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of elements after" ); } ); @@ -1087,8 +1087,8 @@ QUnit.test( ".after(jQuery)", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: YahooTry them out:Googlediveintomark"; - jQuery( "#yahoo" ).after( [ jQuery( "#first" ), jQuery( "#mark, #google" ) ] ); + var expected = "This is a normal link: YahooTry them out:Googlemozilla"; + jQuery( "#yahoo" ).after( [ jQuery( "#first" ), jQuery( "#mozilla, #google" ) ] ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of jQuery objects after" ); } ); @@ -1116,9 +1116,9 @@ QUnit.test( ".after(Function) returns Array", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: YahooTry them out:diveintomark", + var expected = "This is a normal link: YahooTry them out:mozilla", val = manipulationFunctionReturningObj; - jQuery( "#yahoo" ).after( val( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ) ); + jQuery( "#yahoo" ).after( val( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of elements after" ); } ); @@ -1126,9 +1126,9 @@ QUnit.test( ".after(Function) returns jQuery", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: YahooTry them out:Googlediveintomark", + var expected = "This is a normal link: YahooTry them out:Googlemozilla", val = manipulationFunctionReturningObj; - jQuery( "#yahoo" ).after( val( [ jQuery( "#first" ), jQuery( "#mark, #google" ) ] ) ); + jQuery( "#yahoo" ).after( val( [ jQuery( "#first" ), jQuery( "#mozilla, #google" ) ] ) ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of jQuery objects after" ); } ); @@ -1163,8 +1163,8 @@ QUnit.test( "insertAfter(Array)", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: YahooTry them out:diveintomark"; - jQuery( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ).insertAfter( "#yahoo" ); + var expected = "This is a normal link: YahooTry them out:mozilla"; + jQuery( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ).insertAfter( "#yahoo" ); assert.equal( jQuery( "#en" ).text(), expected, "Insert array of elements after" ); } ); @@ -1172,8 +1172,8 @@ QUnit.test( "insertAfter(jQuery)", function( assert ) { assert.expect( 1 ); - var expected = "This is a normal link: YahoodiveintomarkTry them out:"; - jQuery( "#mark, #first" ).insertAfter( "#yahoo" ); + var expected = "This is a normal link: YahoomozillaTry them out:"; + jQuery( "#mozilla, #first" ).insertAfter( "#yahoo" ); assert.equal( jQuery( "#en" ).text(), expected, "Insert jQuery after" ); } ); @@ -1202,17 +1202,17 @@ function testReplaceWith( val, assert ) { assert.strictEqual( jQuery( "#yahoo" )[ 0 ].nextSibling, jQuery( "#baz" )[ 0 ].previousSibling, "Argument order preserved" ); assert.deepEqual( jQuery( "#bar" ).get(), [], "Verify that original element is gone, after multiple arguments" ); - jQuery( "#google" ).replaceWith( val( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ) ); - assert.deepEqual( jQuery( "#mark, #first" ).get(), q( "first", "mark" ), "Replace element with array of elements" ); + jQuery( "#google" ).replaceWith( val( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ) ); + assert.deepEqual( jQuery( "#mozilla, #first" ).get(), q( "first", "mozilla" ), "Replace element with array of elements" ); assert.ok( !jQuery( "#google" )[ 0 ], "Verify that original element is gone, after array of elements" ); - jQuery( "#groups" ).replaceWith( val( jQuery( "#mark, #first" ) ) ); - assert.deepEqual( jQuery( "#mark, #first" ).get(), q( "first", "mark" ), "Replace element with jQuery collection" ); + jQuery( "#groups" ).replaceWith( val( jQuery( "#mozilla, #first" ) ) ); + assert.deepEqual( jQuery( "#mozilla, #first" ).get(), q( "first", "mozilla" ), "Replace element with jQuery collection" ); assert.ok( !jQuery( "#groups" )[ 0 ], "Verify that original element is gone, after jQuery collection" ); - jQuery( "#mark, #first" ).replaceWith( val( "" ) ); + jQuery( "#mozilla, #first" ).replaceWith( val( "" ) ); assert.equal( jQuery( "#qunit-fixture .replacement" ).length, 4, "Replace multiple elements (trac-12449)" ); - assert.deepEqual( jQuery( "#mark, #first" ).get(), [], "Verify that original elements are gone, after replace multiple" ); + assert.deepEqual( jQuery( "#mozilla, #first" ).get(), [], "Verify that original elements are gone, after replace multiple" ); tmp = jQuery( "content" )[ 0 ]; jQuery( "#anchor1" ).contents().replaceWith( val( tmp ) ); @@ -1367,9 +1367,9 @@ QUnit.test( "replaceAll(Array)", function( assert ) { assert.expect( 3 ); - jQuery( [ document.getElementById( "first" ), document.getElementById( "mark" ) ] ).replaceAll( "#yahoo" ); + jQuery( [ document.getElementById( "first" ), document.getElementById( "mozilla" ) ] ).replaceAll( "#yahoo" ); assert.ok( jQuery( "#first" )[ 0 ], "Replace element with array of elements" ); - assert.ok( jQuery( "#mark" )[ 0 ], "Replace element with array of elements" ); + assert.ok( jQuery( "#mozilla" )[ 0 ], "Replace element with array of elements" ); assert.ok( !jQuery( "#yahoo" )[ 0 ], "Verify that original element is gone, after array of elements" ); } ); @@ -1377,9 +1377,9 @@ QUnit.test( "replaceAll(jQuery)", function( assert ) { assert.expect( 3 ); - jQuery( "#mark, #first" ).replaceAll( "#yahoo" ); + jQuery( "#mozilla, #first" ).replaceAll( "#yahoo" ); assert.ok( jQuery( "#first" )[ 0 ], "Replace element with set of elements" ); - assert.ok( jQuery( "#mark" )[ 0 ], "Replace element with set of elements" ); + assert.ok( jQuery( "#mozilla" )[ 0 ], "Replace element with set of elements" ); assert.ok( !jQuery( "#yahoo" )[ 0 ], "Verify that original element is gone, after set of elements" ); } ); @@ -3020,8 +3020,7 @@ QUnit.test( "Sanitized HTML doesn't get unsanitized", function( assert ) { var container, counter = 0, - oldIos = /iphone os (?:8|9|10|11|12)_/i.test( navigator.userAgent ), - assertCount = oldIos ? 12 : 13, + assertCount = 13, done = assert.async( assertCount ); assert.expect( assertCount ); @@ -3065,12 +3064,7 @@ QUnit.test( "Sanitized HTML doesn't get unsanitized", function( assert ) { test( "