From fec13956ee44f39828ec51dbcb09382edd0ec6c5 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:16:36 -0500 Subject: [PATCH] Move ember-concurrency-specific-tests to the ember-concurrency@v2 test-app config eh eh eh eh eh --- .github/workflows/ci-build.yml | 7 +- addon/package.json | 4 +- pnpm-lock.yaml | 153 +++--- test-apps/base-tests/package.json | 11 +- .../base-tests/tests/unit/wait-for-test.ts | 310 +----------- test-apps/ember-concurrency-v2/.eslintrc.js | 12 +- .../ember-concurrency-v2/config/ember-try.js | 117 +++++ test-apps/ember-concurrency-v2/package.json | 17 +- .../tests/helpers/index.ts | 43 -- .../tests/integration/.gitkeep | 0 .../ember-concurrency-v2/tests/unit/.gitkeep | 0 .../tests/unit/utils/mock-stable-error.ts | 26 ++ .../tests/unit/wait-for-test.ts | 442 ++++++++++++++++++ test-apps/ember-concurrency-v3/package.json | 4 +- test-apps/ember-fetch-v8/package.json | 4 +- 15 files changed, 726 insertions(+), 424 deletions(-) create mode 100644 test-apps/ember-concurrency-v2/config/ember-try.js delete mode 100644 test-apps/ember-concurrency-v2/tests/helpers/index.ts delete mode 100644 test-apps/ember-concurrency-v2/tests/integration/.gitkeep delete mode 100644 test-apps/ember-concurrency-v2/tests/unit/.gitkeep create mode 100644 test-apps/ember-concurrency-v2/tests/unit/utils/mock-stable-error.ts create mode 100644 test-apps/ember-concurrency-v2/tests/unit/wait-for-test.ts diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 416d9962..54403afd 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -35,7 +35,7 @@ jobs: try-scenarios: timeout-minutes: 10 - name: "Try: ${{ matrix.ember-try-scenario }}" + name: "Try: ${{ matrix.ember-try-scenario }} @ ${{ matrix.app }}" runs-on: ubuntu-latest @@ -44,6 +44,9 @@ jobs: strategy: fail-fast: false matrix: + app: + - test-apps/base-tests + - test-apps/ember-concurrency-v2 ember-try-scenario: - ember-lts-3.16 - ember-lts-3.20 @@ -61,4 +64,4 @@ jobs: node-version: 18.18.1 - name: test run: node_modules/.bin/ember try:one ${{ matrix.ember-try-scenario }} --skip-cleanup - working-directory: test-apps/base-tests + working-directory: ${{ matrix.app }} diff --git a/addon/package.json b/addon/package.json index d7fa711e..1d60f7b7 100644 --- a/addon/package.json +++ b/addon/package.json @@ -45,7 +45,7 @@ "@types/ember-test-helpers": "^1.0.10", "@types/ember-testing-helpers": "^0.0.4", "@types/ember__test-helpers": "^2.6.1", - "@types/qunit": "^2.11.2", + "@types/qunit": "^2.19.9", "@types/rsvp": "^4.0.4", "@typescript-eslint/eslint-plugin": "^4.29.2", "@typescript-eslint/parser": "^4.30.0", @@ -74,7 +74,7 @@ "loader.js": "^4.7.0", "npm-run-all": "^4.1.5", "prettier": "^2.5.1", - "qunit": "^2.16.0", + "qunit": "^2.20.0", "release-it": "^14.14.3", "release-it-lerna-changelog": "^3.1.0", "typescript": "~4.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 981cc57f..7422ad28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,8 +58,8 @@ importers: specifier: ^2.6.1 version: 2.6.1(@babel/core@7.23.5) '@types/qunit': - specifier: ^2.11.2 - version: 2.11.3 + specifier: ^2.19.9 + version: 2.19.9 '@types/rsvp': specifier: ^4.0.4 version: 4.0.4 @@ -145,8 +145,8 @@ importers: specifier: ^2.5.1 version: 2.6.1 qunit: - specifier: ^2.16.0 - version: 2.18.1 + specifier: ^2.20.0 + version: 2.20.0 release-it: specifier: ^14.14.3 version: 14.14.3 @@ -161,6 +161,10 @@ importers: version: 5.88.2 test-apps/base-tests: + dependencies: + '@ember/test-waiters': + specifier: workspace:* + version: link:../../addon devDependencies: '@babel/core': specifier: ^7.22.20 @@ -268,8 +272,8 @@ importers: specifier: ^4.0.3 version: 4.0.5(@babel/core@7.23.2) '@types/qunit': - specifier: ^2.19.6 - version: 2.19.7 + specifier: ^2.19.9 + version: 2.19.9 '@types/rsvp': specifier: ^4.0.4 version: 4.0.4 @@ -282,9 +286,6 @@ importers: broccoli-asset-rev: specifier: ^3.0.0 version: 3.0.0 - co: - specifier: ^4.6.0 - version: 4.6.0 concurrently: specifier: ^8.2.1 version: 8.2.1 @@ -318,15 +319,6 @@ importers: ember-cli-terser: specifier: ^4.0.2 version: 4.0.2 - ember-concurrency: - specifier: ^2.1.2 - version: 2.2.1(@babel/core@7.23.2) - ember-concurrency-decorators: - specifier: ^2.0.3 - version: 2.0.3(@babel/core@7.23.2) - ember-concurrency-ts: - specifier: ^0.3.1 - version: 0.3.1(ember-concurrency@2.2.1) ember-data: specifier: ~5.3.0 version: 5.3.0(@babel/core@7.23.2)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(@glint/template@1.2.1)(ember-source@5.3.0) @@ -388,7 +380,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 qunit: - specifier: ^2.19.4 + specifier: ^2.20.0 version: 2.20.0 qunit-dom: specifier: ^3.0.0 @@ -413,6 +405,10 @@ importers: version: 5.88.2 test-apps/ember-concurrency-v2: + dependencies: + '@ember/test-waiters': + specifier: workspace:* + version: link:../../addon devDependencies: '@babel/core': specifier: ^7.22.20 @@ -520,8 +516,8 @@ importers: specifier: ^4.0.3 version: 4.0.5(@babel/core@7.23.2) '@types/qunit': - specifier: ^2.19.6 - version: 2.19.7 + specifier: ^2.19.9 + version: 2.19.9 '@types/rsvp': specifier: ^4.0.4 version: 4.0.4 @@ -534,6 +530,9 @@ importers: broccoli-asset-rev: specifier: ^3.0.0 version: 3.0.0 + co: + specifier: ^4.6.0 + version: 4.6.0 concurrently: specifier: ^8.2.1 version: 8.2.1 @@ -567,6 +566,15 @@ importers: ember-cli-terser: specifier: ^4.0.2 version: 4.0.2 + ember-concurrency: + specifier: ^2.1.2 + version: 2.3.7(@babel/core@7.23.2) + ember-concurrency-decorators: + specifier: ^2.0.3 + version: 2.0.3(@babel/core@7.23.2) + ember-concurrency-ts: + specifier: ^0.3.1 + version: 0.3.1(ember-concurrency@2.3.7) ember-data: specifier: ~5.3.0 version: 5.3.0(@babel/core@7.23.2)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(@glint/template@1.2.1)(ember-source@5.3.0) @@ -583,17 +591,23 @@ importers: specifier: ^8.0.0 version: 8.0.0 ember-qunit: - specifier: ^8.0.1 - version: 8.0.1(@ember/test-helpers@3.2.0)(@glint/template@1.2.1)(ember-source@5.3.0)(qunit@2.20.0) + specifier: ^8.0.2 + version: 8.0.2(@ember/test-helpers@3.2.0)(@glint/template@1.2.1)(ember-source@5.3.0)(qunit@2.20.0) ember-resolver: specifier: ^11.0.1 version: 11.0.1(ember-source@5.3.0) ember-source: specifier: ~5.3.0 version: 5.3.0(@babel/core@7.23.2)(@glimmer/component@1.1.2)(@glint/template@1.2.1)(rsvp@4.8.5)(webpack@5.88.2) + ember-source-channel-url: + specifier: ^3.0.0 + version: 3.0.0 ember-template-lint: specifier: ^5.11.2 version: 5.11.2 + ember-try: + specifier: ^3.0.0 + version: 3.0.0 ember-welcome-page: specifier: ^7.0.2 version: 7.0.2 @@ -622,11 +636,11 @@ importers: specifier: ^3.0.3 version: 3.0.3 qunit: - specifier: ^2.19.4 + specifier: ^2.20.0 version: 2.20.0 qunit-dom: - specifier: ^2.0.0 - version: 2.0.0 + specifier: ^3.0.0 + version: 3.0.0 stylelint: specifier: ^15.10.3 version: 15.11.0(typescript@5.2.2) @@ -754,8 +768,8 @@ importers: specifier: ^4.0.3 version: 4.0.5(@babel/core@7.23.2) '@types/qunit': - specifier: ^2.19.6 - version: 2.19.7 + specifier: ^2.19.9 + version: 2.19.9 '@types/rsvp': specifier: ^4.0.4 version: 4.0.4 @@ -856,7 +870,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 qunit: - specifier: ^2.19.4 + specifier: ^2.20.0 version: 2.20.0 qunit-dom: specifier: ^2.0.0 @@ -988,8 +1002,8 @@ importers: specifier: ^4.0.3 version: 4.0.5(@babel/core@7.23.2) '@types/qunit': - specifier: ^2.19.6 - version: 2.19.7 + specifier: ^2.19.9 + version: 2.19.9 '@types/rsvp': specifier: ^4.0.4 version: 4.0.4 @@ -1090,7 +1104,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 qunit: - specifier: ^2.19.4 + specifier: ^2.20.0 version: 2.20.0 qunit-dom: specifier: ^2.0.0 @@ -5913,7 +5927,7 @@ packages: resolution: {integrity: sha512-5z/h8KzTUGa9D04Vtd0/q39GTmLppqzD/XBoc9G3fudDige4P6tiV6LB7BIzDM5IsC1rffQtxcphPjnxlgJQWg==} dependencies: '@types/ember': 4.0.8(@babel/core@7.23.2) - '@types/ember__object': 4.0.9(@babel/core@7.23.5) + '@types/ember__object': 4.0.9(@babel/core@7.23.2) transitivePeerDependencies: - '@babel/core' - supports-color @@ -5940,7 +5954,7 @@ packages: resolution: {integrity: sha512-BrdaubCMGivKvMgcW8GzouWsrrz56nEQMr7iR7hUYfyS47u4fJrB8zzoYAIGhwWth0l8stEqfUMo5U7kIOuy1w==} dependencies: '@types/ember': 4.0.8(@babel/core@7.23.2) - '@types/ember__object': 4.0.9(@babel/core@7.23.5) + '@types/ember__object': 4.0.9(@babel/core@7.23.2) transitivePeerDependencies: - '@babel/core' - supports-color @@ -6353,12 +6367,8 @@ packages: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} dev: true - /@types/qunit@2.11.3: - resolution: {integrity: sha512-UF/4jDehcpRlMXzKXi2Z59Id48/uYlMeUDhYdjFrVVwy30Eud/e60Ok3yVjSPlHprafj3B315uFTrF6eqQTeSw==} - dev: true - - /@types/qunit@2.19.7: - resolution: {integrity: sha512-Vf1+zHCOhMyDqZqM6zlB++6n5mkMe1+pWH1l3fzbzakQ2VImMNeAKSQD++RAjpGTqPUio8Tre2a6kmq6O1tK/A==} + /@types/qunit@2.19.9: + resolution: {integrity: sha512-Ym6B8Ewt1R1b0EgSqISSrAKp2Pg5MNgKK3/d2Fbls3PN7kNnucVSGaRx9Prxeo78HfQofYJMWDWLPlfAuwx7IQ==} dev: true /@types/range-parser@1.2.4: @@ -10999,6 +11009,20 @@ packages: - supports-color dev: true + /ember-compatibility-helpers@1.2.7(@babel/core@7.23.2): + resolution: {integrity: sha512-BtkjulweiXo9c3yVWrtexw2dTmBrvavD/xixNC6TKOBdrixUwU+6nuOO9dufDWsMxoid7MvtmDpzc9+mE8PdaA==} + engines: {node: 10.* || >= 12.*} + dependencies: + babel-plugin-debug-macros: 0.2.0(@babel/core@7.23.2) + ember-cli-version-checker: 5.1.2 + find-up: 5.0.0 + fs-extra: 9.1.0 + semver: 5.7.2 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + /ember-concurrency-decorators@2.0.3(@babel/core@7.23.2): resolution: {integrity: sha512-r6O34YKI/slyYapVsuOPnmaKC4AsmBSwvgcadbdy+jHNj+mnryXPkm+3hhhRnFdlsKUKdEuXvl43lhjhYRLhhA==} engines: {node: 10.* || >= 12} @@ -11038,17 +11062,16 @@ packages: - supports-color dev: true - /ember-concurrency@2.2.1(@babel/core@7.23.2): - resolution: {integrity: sha512-a4283Yq+jimxqoD5YaxQu7cXePHKqkNQfsT4fs0nYTz5PYbUd6wzUtelp6k8R1JTNPwDdxyVvUgu7yYoC8Sk5A==} - engines: {node: 10.* || 12.* || 14.* || >= 16} + /ember-concurrency-ts@0.3.1(ember-concurrency@2.3.7): + resolution: {integrity: sha512-lE9uqPgK1Y9PN/0BJ5zE2a+h95izRCn6FCyt7qVV3012TlblTynsBaoUuAbN1T3KfzFsrJaXwsxzRbDjEde2Sw==} + engines: {node: 10.* || >= 12} + peerDependencies: + ember-concurrency: ^1.2.1 || ^2.0.0-rc.1 dependencies: - '@glimmer/tracking': 1.1.2 ember-cli-babel: 7.26.11 - ember-cli-htmlbars: 5.7.2 - ember-compatibility-helpers: 1.2.6(@babel/core@7.23.2) - ember-destroyable-polyfill: 2.0.3(@babel/core@7.23.2) + ember-cli-htmlbars: 4.5.0 + ember-concurrency: 2.3.7(@babel/core@7.23.2) transitivePeerDependencies: - - '@babel/core' - supports-color dev: true @@ -11066,6 +11089,23 @@ packages: - supports-color dev: true + /ember-concurrency@2.3.7(@babel/core@7.23.2): + resolution: {integrity: sha512-sz6sTIXN/CuLb5wdpauFa+rWXuvXXSnSHS4kuNzU5GSMDX1pLBWSuovoUk61FUe6CYRqBmT1/UushObwBGickQ==} + engines: {node: 10.* || 12.* || 14.* || >= 16} + dependencies: + '@babel/helper-plugin-utils': 7.22.5 + '@babel/types': 7.23.5 + '@glimmer/tracking': 1.1.2 + ember-cli-babel: 7.26.11 + ember-cli-babel-plugin-helpers: 1.1.1 + ember-cli-htmlbars: 5.7.2 + ember-compatibility-helpers: 1.2.7(@babel/core@7.23.2) + ember-destroyable-polyfill: 2.0.3(@babel/core@7.23.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + /ember-data@5.3.0(@babel/core@7.23.2)(@ember/string@3.1.1)(@glimmer/tracking@1.1.2)(@glint/template@1.2.1)(ember-source@5.3.0): resolution: {integrity: sha512-ca8udUa2SrWyYxPckYc89Fdv/9pCG3X360zHvlGxtB4C87o3dWp6sle98tP9G1TjximKhrU/PMrqpdhJ8rOGtA==} engines: {node: 16.* || >= 18.*} @@ -11122,7 +11162,7 @@ packages: dependencies: ember-cli-babel: 7.26.11 ember-cli-version-checker: 5.1.2 - ember-compatibility-helpers: 1.2.6(@babel/core@7.23.2) + ember-compatibility-helpers: 1.2.7(@babel/core@7.23.2) transitivePeerDependencies: - '@babel/core' - supports-color @@ -16351,16 +16391,6 @@ packages: resolution: {integrity: sha512-rqGZ9ZgOvtOY+Ph2ElCJ4pRIHouMZmI3DWK35Xr3x3c17/gIwOHHyKAQk6XetqhqepS6p+oXvK6Bx1uZu5jJuw==} dev: true - /qunit@2.18.1: - resolution: {integrity: sha512-A2Adgr/DeMQOJZFVllyQi2wiGJVVXGSRRwMe39fNfuuftUYHHpGRTWUhBa8wNblunCAOUCt+1uFcg1L7NaxQTA==} - engines: {node: '>=10'} - hasBin: true - dependencies: - commander: 7.2.0 - node-watch: 0.7.3 - tiny-glob: 0.2.9 - dev: true - /qunit@2.20.0: resolution: {integrity: sha512-N8Fp1J55waE+QG1KwX2LOyqulZUToRrrPBqDOfYfuAMkEglFL15uwvmH1P4Tq/omQ/mGbBI8PEB3PhIfvUb+jg==} engines: {node: '>=10'} @@ -17144,6 +17174,11 @@ packages: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true diff --git a/test-apps/base-tests/package.json b/test-apps/base-tests/package.json index 6679828c..e1d78be4 100644 --- a/test-apps/base-tests/package.json +++ b/test-apps/base-tests/package.json @@ -27,6 +27,9 @@ "test:ember-compatibility": "ember try:each", "test:prod": "ember test --environment=production" }, + "dependencies": { + "@ember/test-waiters": "workspace:*" + }, "devDependencies": { "@babel/core": "^7.22.20", "@ember/optional-features": "^2.0.0", @@ -63,16 +66,12 @@ "@types/ember__template": "^4.0.2", "@types/ember__test": "^4.0.2", "@types/ember__utils": "^4.0.3", - "@types/qunit": "^2.19.6", + "@types/qunit": "^2.19.9", "@types/rsvp": "^4.0.4", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "broccoli-asset-rev": "^3.0.0", - "co": "^4.6.0", "concurrently": "^8.2.1", - "ember-concurrency": "^2.1.2", - "ember-concurrency-decorators": "^2.0.3", - "ember-concurrency-ts": "^0.3.1", "ember-auto-import": "^2.6.3", "ember-cli": "~5.3.0", "ember-cli-app-version": "^6.0.1", @@ -103,7 +102,7 @@ "eslint-plugin-qunit": "^8.0.0", "loader.js": "^4.7.0", "prettier": "^3.0.3", - "qunit": "^2.19.4", + "qunit": "^2.20.0", "qunit-dom": "^3.0.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", diff --git a/test-apps/base-tests/tests/unit/wait-for-test.ts b/test-apps/base-tests/tests/unit/wait-for-test.ts index 320d1ed7..6ede6367 100644 --- a/test-apps/base-tests/tests/unit/wait-for-test.ts +++ b/test-apps/base-tests/tests/unit/wait-for-test.ts @@ -2,23 +2,19 @@ import MockStableError, { overrideError, resetError, } from './utils/mock-stable-error'; +// @ember/test-waiters is still a v1 addon and is too weird +// to have in-repo types working correctly. +// @ts-ignore import { _reset, getPendingWaiterState, waitFor } from '@ember/test-waiters'; import { module, test } from 'qunit'; -import EmberObject, { get } from '@ember/object'; +import EmberObject from '@ember/object'; import { DEBUG } from '@glimmer/env'; import RSVP from 'rsvp'; -import { task as taskFn, TaskGenerator, didCancel } from 'ember-concurrency'; -// type resolution is not working correctly due to usage of forked -// (non-published) ember-concurrency-decorators remove this ts-ignore when -// migrating back to mainline off the fork -// @ts-ignore -import { task as taskDec } from 'ember-concurrency-decorators'; -import { perform } from 'ember-concurrency-ts'; +// @ember/test-waiters is still a v1 addon and is too weird +// to have in-repo types working correctly. // @ts-ignore -import co from 'co'; - import { PromiseType, Thenable } from '@ember/test-waiters/types'; interface PromiseClassType { @@ -93,71 +89,7 @@ if (DEBUG) { }, ); - const generatorTestModules = [ - (function () { - const EmberObjectThing = EmberObject.extend({ - doStuffTask: taskFn( - waitFor(function* doTaskStuff(...args: any) { - yield new Promise((resolve) => { - setTimeout(resolve, 10); - }); - return args.reverse(); - }), - ), - doAsyncStuff(...args: any) { - // @ts-ignore - return this.doStuffTask.perform(...args); - }, - - throwingTask: taskFn( - waitFor(function* taskThrow() { - yield new Promise((resolve) => { - setTimeout(resolve, 10); - }); - throw new Error('doh!'); - }), - ), - asyncThrow() { - // @ts-ignore - return this.throwingTask.perform(); - }, - }); - - class NativeThing { - @taskDec - @waitFor - *doStuffTask(...args: any): TaskGenerator { - yield new Promise((resolve) => { - setTimeout(resolve, 10); - }); - return args.reverse(); - } - doAsyncStuff(...args: any) { - return perform(this.doStuffTask, ...args); - } - - @taskDec - @waitFor - *throwingTask() { - yield new Promise((resolve) => { - setTimeout(resolve, 10); - }); - throw new Error('doh!'); - } - asyncThrow(...args: any) { - return perform(this.throwingTask, ...args); - } - } - return { - name: 'Generator', - waiterName: '@ember/test-waiters:generator-waiter', - EmberObjectThing, - NativeThing, - }; - })(), - ]; - - const testModules = [...promiseTestModules, ...generatorTestModules]; + const testModules = [...promiseTestModules]; testModules.forEach( ({ name, waiterName, EmberObjectThing, NativeThing }) => { @@ -239,234 +171,6 @@ if (DEBUG) { }, ); - module('waitFor ember-concurrency interop', function () { - class Deferred { - promise: Promise; - resolve: Function = () => null; - - constructor() { - this.promise = new Promise((res) => (this.resolve = res)); - } - } - - class NativeThing { - iterations: Array = []; - _continue?: Function; - - @taskDec - @waitFor - *doStuffTask(): TaskGenerator { - for (let i = 0; i < 3; i += 1) { - const continuation = new Deferred(); - if (this._continue !== undefined) { - throw new Error('pending continue, cannot proceed'); - } - const completion = new Deferred(); - let complete = false; - - this._continue = () => { - if (complete === true) { - throw new Error( - 'Cannot call continue twice on a single iteration', - ); - } else { - complete = true; - } - - continuation.resolve(); - return completion.promise; - }; - - try { - yield continuation.promise; - } finally { - this._continue = undefined; - completion.resolve(); - } - this.iterations.push(i); - } - return 'done'; - } - - get continue() { - if (this._continue === undefined) { - throw new Error('Cannot call continue twice on a single iteration'); - } else { - try { - return this._continue; - } finally { - // detect invalid usage. Specifically, our test (as written - // currently) would be in-error if any given continue() is invoked more - // then once - this._continue = undefined; - } - } - } - - @taskDec - *parentTask(): TaskGenerator { - // @ts-ignore - return yield this.doStuffTask.perform(); - } - - @taskDec - @waitFor - *wrappedParentTask(): TaskGenerator { - // @ts-ignore - return yield this.doStuffTask.perform(); - } - } - - test('tasks with multiple yields work', async function (assert) { - const thing = new NativeThing(); - const task = perform(thing.doStuffTask); - - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - assert.deepEqual(thing.iterations, [0]); - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - assert.deepEqual(thing.iterations, [0, 1]); - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - await task; - - assert.deepEqual(thing.iterations, [0, 1, 2]); - assert.deepEqual(getPendingWaiterState().pending, 0); - }); - - interface CancellationDef { - desc: string; - taskName: 'doStuffTask' | 'parentTask' | 'wrappedParentTask'; - } - - const cancellationCases: CancellationDef[] = [ - { desc: 'direct', taskName: 'doStuffTask' }, - { desc: 'parent', taskName: 'parentTask' }, - { desc: 'wrapped parent', taskName: 'wrappedParentTask' }, - ]; - - cancellationCases.forEach(({ desc, taskName }) => { - test(`${desc} task cancellation works`, async function (assert) { - const thing = new NativeThing(); - - const instance = perform(get(thing, taskName)); - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - assert.deepEqual(thing.iterations, [0]); - assert.deepEqual(getPendingWaiterState().pending, 1); - - instance.cancel(); - try { - await instance; - assert.ok(false); - } catch (e) { - assert.ok(didCancel(e)); - assert.deepEqual(getPendingWaiterState().pending, 0); - } - }); - }); - }); - - module('waitFor co interop', function () { - function coDec( - _target: object, - _key: string, - descriptor: PropertyDescriptor, - ): PropertyDescriptor { - descriptor.value = co.wrap(descriptor.value); - return descriptor; - } - - class Deferred { - promise: Promise; - resolve: Function = () => null; - - constructor() { - this.promise = new Promise((res) => (this.resolve = res)); - } - } - - class NativeThing { - iterations: Array = []; - _continue?: Function; - - @coDec - @waitFor - *doStuffCo(): TaskGenerator { - for (let i = 0; i < 3; i += 1) { - const continuation = new Deferred(); - if (this._continue !== undefined) { - throw new Error('pending continue, cannot proceed'); - } - const completion = new Deferred(); - let continued = false; - - this._continue = () => { - if (continued === true) { - throw new Error( - 'Cannot call continue twice on a single iteration', - ); - } else { - continued = true; - } - - continuation.resolve(); - return completion.promise; - }; - - try { - yield continuation.promise; - } finally { - completion.resolve(); - this._continue = undefined; - } - this.iterations.push(i); - } - return 'done'; - } - - get continue() { - if (this._continue === undefined) { - throw new Error('Cannot call continue twice on a single iteration'); - } else { - try { - return this._continue; - } finally { - // detect invalid usage. Specifically, our test (as written - // currently) would be in-error if any given continue() is invoked more - // then once - this._continue = undefined; - } - } - } - } - - test('it works', async function (assert) { - const thing = new NativeThing(); - - thing.doStuffCo(); - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - assert.deepEqual(thing.iterations, [0]); - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - assert.deepEqual(thing.iterations, [0, 1]); - assert.deepEqual(getPendingWaiterState().pending, 1); - - await thing.continue(); - - assert.deepEqual(thing.iterations, [0, 1, 2]); - assert.deepEqual(getPendingWaiterState().pending, 0); - }); - }); - test('types', async function (assert) { assert.expect(0); diff --git a/test-apps/ember-concurrency-v2/.eslintrc.js b/test-apps/ember-concurrency-v2/.eslintrc.js index 121ac75c..63a17669 100644 --- a/test-apps/ember-concurrency-v2/.eslintrc.js +++ b/test-apps/ember-concurrency-v2/.eslintrc.js @@ -48,8 +48,18 @@ module.exports = { }, { // test files - files: ['tests/**/*-test.{js,ts}'], + files: ['tests/**/*.{js,ts}'], extends: ['plugin:qunit/recommended'], + rules: { + // Disabled rules when migrating to v2 addon / updated app + 'ember/no-classic-classes': 'off', + 'qunit/no-assert-equal': 'off', + 'qunit/no-identical-names': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-empty-function': 'off', + }, }, ], }; diff --git a/test-apps/ember-concurrency-v2/config/ember-try.js b/test-apps/ember-concurrency-v2/config/ember-try.js new file mode 100644 index 00000000..c0f21723 --- /dev/null +++ b/test-apps/ember-concurrency-v2/config/ember-try.js @@ -0,0 +1,117 @@ +'use strict'; + +const getChannelURL = require('ember-source-channel-url'); + +function embroider(label, deps) { + return [ + { + name: `${label}-embroider-safe`, + npm: { + devDependencies: { + ...deps, + }, + }, + env: { + EMBROIDER_TEST_SETUP_OPTIONS: 'safe', + }, + }, + { + name: `${label}-embroider-optimized`, + npm: { + devDependencies: { + ...deps, + }, + }, + env: { + EMBROIDER_TEST_SETUP_OPTIONS: 'optimized', + }, + }, + ]; +} + +const v3Embroider = embroider('3.x', { + '@embroider/core': `^3.4.2`, + '@embroider/webpack': `^3.2.1`, + '@embroider/compat': `^3.4.0`, + '@embroider/test-setup': `^3.0.3`, +}); + +module.exports = async function () { + return { + usePnpm: true, + scenarios: [ + { + name: 'ember-lts-3.16', + npm: { + devDependencies: { + '@ember/test-helpers': '^2.0.0', + 'ember-source': '~3.16.0', + 'ember-cli': '^4.10.0', + }, + }, + }, + { + name: 'ember-lts-3.20', + npm: { + devDependencies: { + '@ember/test-helpers': '^2.0.0', + 'ember-source': '~3.20.0', + 'ember-cli': '^4.10.0', + }, + }, + }, + { + name: 'ember-lts-3.24', + npm: { + devDependencies: { + '@ember/test-helpers': '^2.0.0', + 'ember-source': '~3.24.0', + 'ember-cli': '^4.10.0', + }, + }, + }, + + { + name: 'ember-release', + npm: { + dependencies: { + 'ember-auto-import': '^2.2.0', + webpack: '^5.0.0', + }, + devDependencies: { + 'ember-source': await getChannelURL('release'), + }, + }, + }, + { + name: 'ember-beta', + npm: { + dependencies: { + 'ember-auto-import': '^2.2.0', + webpack: '^5.0.0', + }, + devDependencies: { + '@types/ember__owner': '^4.0.3', + 'ember-resolver': '^10.0.0', + 'ember-source': await getChannelURL('beta'), + }, + }, + }, + { + name: 'ember-canary', + npm: { + dependencies: { + 'ember-auto-import': '^2.2.0', + webpack: '^5.0.0', + }, + devDependencies: { + '@types/ember__owner': '^4.0.3', + 'ember-resolver': '^10.0.0', + 'ember-source': await getChannelURL('canary'), + }, + }, + }, + ...v3Embroider, + ], + }; +}; diff --git a/test-apps/ember-concurrency-v2/package.json b/test-apps/ember-concurrency-v2/package.json index 38567db5..65887809 100644 --- a/test-apps/ember-concurrency-v2/package.json +++ b/test-apps/ember-concurrency-v2/package.json @@ -25,6 +25,9 @@ "test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"", "test:ember": "ember test" }, + "dependencies": { + "@ember/test-waiters": "workspace:*" + }, "devDependencies": { "@babel/core": "^7.22.20", "@ember/optional-features": "^2.0.0", @@ -61,11 +64,12 @@ "@types/ember__template": "^4.0.2", "@types/ember__test": "^4.0.2", "@types/ember__utils": "^4.0.3", - "@types/qunit": "^2.19.6", + "@types/qunit": "^2.19.9", "@types/rsvp": "^4.0.4", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "broccoli-asset-rev": "^3.0.0", + "co": "^4.6.0", "concurrently": "^8.2.1", "ember-auto-import": "^2.6.3", "ember-cli": "~5.3.0", @@ -77,15 +81,20 @@ "ember-cli-inject-live-reload": "^2.1.0", "ember-cli-sri": "^2.1.1", "ember-cli-terser": "^4.0.2", + "ember-concurrency": "^2.1.2", + "ember-concurrency-decorators": "^2.0.3", + "ember-concurrency-ts": "^0.3.1", "ember-data": "~5.3.0", "ember-fetch": "^8.1.2", "ember-load-initializers": "^2.1.2", "ember-modifier": "^4.1.0", "ember-page-title": "^8.0.0", - "ember-qunit": "^8.0.1", + "ember-qunit": "^8.0.2", "ember-resolver": "^11.0.1", "ember-source": "~5.3.0", + "ember-source-channel-url": "^3.0.0", "ember-template-lint": "^5.11.2", + "ember-try": "^3.0.0", "ember-welcome-page": "^7.0.2", "eslint": "^8.49.0", "eslint-config-prettier": "^9.0.0", @@ -95,8 +104,8 @@ "eslint-plugin-qunit": "^8.0.0", "loader.js": "^4.7.0", "prettier": "^3.0.3", - "qunit": "^2.19.4", - "qunit-dom": "^2.0.0", + "qunit": "^2.20.0", + "qunit-dom": "^3.0.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", diff --git a/test-apps/ember-concurrency-v2/tests/helpers/index.ts b/test-apps/ember-concurrency-v2/tests/helpers/index.ts deleted file mode 100644 index 0a16e0be..00000000 --- a/test-apps/ember-concurrency-v2/tests/helpers/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - setupApplicationTest as upstreamSetupApplicationTest, - setupRenderingTest as upstreamSetupRenderingTest, - setupTest as upstreamSetupTest, - SetupTestOptions, -} from 'ember-qunit'; - -// This file exists to provide wrappers around ember-qunit's -// test setup functions. This way, you can easily extend the setup that is -// needed per test type. - -function setupApplicationTest(hooks: NestedHooks, options?: SetupTestOptions) { - upstreamSetupApplicationTest(hooks, options); - - // Additional setup for application tests can be done here. - // - // For example, if you need an authenticated session for each - // application test, you could do: - // - // hooks.beforeEach(async function () { - // await authenticateSession(); // ember-simple-auth - // }); - // - // This is also a good place to call test setup functions coming - // from other addons: - // - // setupIntl(hooks); // ember-intl - // setupMirage(hooks); // ember-cli-mirage -} - -function setupRenderingTest(hooks: NestedHooks, options?: SetupTestOptions) { - upstreamSetupRenderingTest(hooks, options); - - // Additional setup for rendering tests can be done here. -} - -function setupTest(hooks: NestedHooks, options?: SetupTestOptions) { - upstreamSetupTest(hooks, options); - - // Additional setup for unit tests can be done here. -} - -export { setupApplicationTest, setupRenderingTest, setupTest }; diff --git a/test-apps/ember-concurrency-v2/tests/integration/.gitkeep b/test-apps/ember-concurrency-v2/tests/integration/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test-apps/ember-concurrency-v2/tests/unit/.gitkeep b/test-apps/ember-concurrency-v2/tests/unit/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test-apps/ember-concurrency-v2/tests/unit/utils/mock-stable-error.ts b/test-apps/ember-concurrency-v2/tests/unit/utils/mock-stable-error.ts new file mode 100644 index 00000000..744d196f --- /dev/null +++ b/test-apps/ember-concurrency-v2/tests/unit/utils/mock-stable-error.ts @@ -0,0 +1,26 @@ +/* eslint-disable no-global-assign */ +/* eslint-disable no-unused-vars */ + +const ERROR = Error; + +// @ts-ignore +Error = ERROR; + +export function overrideError(_Error: any) { + // @ts-ignore + Error = _Error; +} +export function resetError() { + // @ts-ignore + Error = ERROR; +} +export default class MockStableError { + message: string; + // @ts-ignore + constructor(message: string) { + this.message = message; + } + get stack() { + return 'STACK'; + } +} diff --git a/test-apps/ember-concurrency-v2/tests/unit/wait-for-test.ts b/test-apps/ember-concurrency-v2/tests/unit/wait-for-test.ts new file mode 100644 index 00000000..c87e2246 --- /dev/null +++ b/test-apps/ember-concurrency-v2/tests/unit/wait-for-test.ts @@ -0,0 +1,442 @@ +import MockStableError, { + overrideError, + resetError, +} from './utils/mock-stable-error'; +// @ember/test-waiters is still a v1 addon and is too weird +// to have in-repo types working correctly. +// @ts-ignore +import { _reset, getPendingWaiterState, waitFor } from '@ember/test-waiters'; +import { module, test } from 'qunit'; + +import EmberObject, { get } from '@ember/object'; +import { DEBUG } from '@glimmer/env'; + +import { task as taskFn, TaskGenerator, didCancel } from 'ember-concurrency'; +// type resolution is not working correctly due to usage of forked +// (non-published) ember-concurrency-decorators remove this ts-ignore when +// migrating back to mainline off the fork +// @ts-ignore +import { task as taskDec } from 'ember-concurrency-decorators'; +import { perform } from 'ember-concurrency-ts'; +// @ts-ignore +import co from 'co'; + +// @ember/test-waiters is still a v1 addon and is too weird +// to have in-repo types working correctly. +// @ts-ignore +import { PromiseType, Thenable } from '@ember/test-waiters/types'; + +interface ModeDef { + name: string; + createPromise: Function; + createThrowingPromise: Function; +} + +if (DEBUG) { + module('wait-for', function (hooks) { + hooks.afterEach(function () { + _reset(); + resetError(); + }); + const generatorTestModules = [ + (function () { + const EmberObjectThing = EmberObject.extend({ + doStuffTask: taskFn( + waitFor(function* doTaskStuff(...args: any) { + yield new Promise((resolve) => { + setTimeout(resolve, 10); + }); + return args.reverse(); + }), + ), + doAsyncStuff(...args: any) { + // @ts-ignore + return this.doStuffTask.perform(...args); + }, + + throwingTask: taskFn( + waitFor(function* taskThrow() { + yield new Promise((resolve) => { + setTimeout(resolve, 10); + }); + throw new Error('doh!'); + }), + ), + asyncThrow() { + // @ts-ignore + return this.throwingTask.perform(); + }, + }); + + class NativeThing { + @taskDec + @waitFor + *doStuffTask(...args: any): TaskGenerator { + yield new Promise((resolve) => { + setTimeout(resolve, 10); + }); + return args.reverse(); + } + doAsyncStuff(...args: any) { + return perform(this.doStuffTask, ...args); + } + + @taskDec + @waitFor + *throwingTask() { + yield new Promise((resolve) => { + setTimeout(resolve, 10); + }); + throw new Error('doh!'); + } + asyncThrow(...args: any) { + return perform(this.throwingTask, ...args); + } + } + return { + name: 'Generator', + waiterName: '@ember/test-waiters:generator-waiter', + EmberObjectThing, + NativeThing, + }; + })(), + ]; + + const testModules = [...generatorTestModules]; + + testModules.forEach( + ({ name, waiterName, EmberObjectThing, NativeThing }) => { + module(name, function () { + const invocationType = [ + { + name: 'class function', + createPromise(...args: any[]) { + return EmberObjectThing.create().doAsyncStuff(...args); + }, + createThrowingPromise() { + return EmberObjectThing.create().asyncThrow(); + }, + }, + { + name: 'decorator', + createPromise(...args: any[]) { + return new NativeThing().doAsyncStuff(...args); + }, + createThrowingPromise() { + return new NativeThing().asyncThrow(); + }, + }, + ]; + + invocationType.forEach( + ({ name, createPromise, createThrowingPromise }: ModeDef) => { + module(name, function () { + test('waitFor wraps and registers a waiter', async function (assert) { + overrideError(MockStableError); + + const promise = createPromise(); + + assert.deepEqual(getPendingWaiterState(), { + pending: 1, + waiters: { + [waiterName]: [ + { + label: undefined, + stack: 'STACK', + }, + ], + }, + }); + + await ( + promise as unknown as Thenable> + ).then(() => { + assert.deepEqual(getPendingWaiterState(), { + pending: 0, + waiters: {}, + }); + }); + }); + + test('waitFor handles arguments and return value', async function (assert) { + overrideError(MockStableError); + + const ret = await createPromise(1, 'foo'); + assert.deepEqual(ret, ['foo', 1]); + }); + + test('waitFor transitions waiter to not pending even if promise throws when thenable wrapped', async function (assert) { + const promise = createThrowingPromise(); + + try { + await promise; + } catch (e) { + assert.deepEqual(getPendingWaiterState(), { + pending: 0, + waiters: {}, + }); + } + }); + }); + }, + ); + }); + }, + ); + + module('waitFor ember-concurrency interop', function () { + class Deferred { + promise: Promise; + resolve: Function = () => null; + + constructor() { + this.promise = new Promise((res) => (this.resolve = res)); + } + } + + class NativeThing { + iterations: Array = []; + _continue?: Function; + + @taskDec + @waitFor + *doStuffTask(): TaskGenerator { + for (let i = 0; i < 3; i += 1) { + const continuation = new Deferred(); + if (this._continue !== undefined) { + throw new Error('pending continue, cannot proceed'); + } + const completion = new Deferred(); + let complete = false; + + this._continue = () => { + if (complete === true) { + throw new Error( + 'Cannot call continue twice on a single iteration', + ); + } else { + complete = true; + } + + continuation.resolve(); + return completion.promise; + }; + + try { + yield continuation.promise; + } finally { + this._continue = undefined; + completion.resolve(); + } + this.iterations.push(i); + } + return 'done'; + } + + get continue() { + if (this._continue === undefined) { + throw new Error('Cannot call continue twice on a single iteration'); + } else { + try { + return this._continue; + } finally { + // detect invalid usage. Specifically, our test (as written + // currently) would be in-error if any given continue() is invoked more + // then once + this._continue = undefined; + } + } + } + + @taskDec + *parentTask(): TaskGenerator { + // @ts-ignore + return yield this.doStuffTask.perform(); + } + + @taskDec + @waitFor + *wrappedParentTask(): TaskGenerator { + // @ts-ignore + return yield this.doStuffTask.perform(); + } + } + + test('tasks with multiple yields work', async function (assert) { + const thing = new NativeThing(); + const task = perform(thing.doStuffTask); + + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + assert.deepEqual(thing.iterations, [0]); + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + assert.deepEqual(thing.iterations, [0, 1]); + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + await task; + + assert.deepEqual(thing.iterations, [0, 1, 2]); + assert.deepEqual(getPendingWaiterState().pending, 0); + }); + + interface CancellationDef { + desc: string; + taskName: 'doStuffTask' | 'parentTask' | 'wrappedParentTask'; + } + + const cancellationCases: CancellationDef[] = [ + { desc: 'direct', taskName: 'doStuffTask' }, + { desc: 'parent', taskName: 'parentTask' }, + { desc: 'wrapped parent', taskName: 'wrappedParentTask' }, + ]; + + cancellationCases.forEach(({ desc, taskName }) => { + test(`${desc} task cancellation works`, async function (assert) { + const thing = new NativeThing(); + + const instance = perform(get(thing, taskName)); + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + assert.deepEqual(thing.iterations, [0]); + assert.deepEqual(getPendingWaiterState().pending, 1); + + instance.cancel(); + try { + await instance; + assert.ok(false); + } catch (e) { + assert.ok(didCancel(e)); + assert.deepEqual(getPendingWaiterState().pending, 0); + } + }); + }); + }); + + module('waitFor co interop', function () { + function coDec( + _target: object, + _key: string, + descriptor: PropertyDescriptor, + ): PropertyDescriptor { + descriptor.value = co.wrap(descriptor.value); + return descriptor; + } + + class Deferred { + promise: Promise; + resolve: Function = () => null; + + constructor() { + this.promise = new Promise((res) => (this.resolve = res)); + } + } + + class NativeThing { + iterations: Array = []; + _continue?: Function; + + @coDec + @waitFor + *doStuffCo(): TaskGenerator { + for (let i = 0; i < 3; i += 1) { + const continuation = new Deferred(); + if (this._continue !== undefined) { + throw new Error('pending continue, cannot proceed'); + } + const completion = new Deferred(); + let continued = false; + + this._continue = () => { + if (continued === true) { + throw new Error( + 'Cannot call continue twice on a single iteration', + ); + } else { + continued = true; + } + + continuation.resolve(); + return completion.promise; + }; + + try { + yield continuation.promise; + } finally { + completion.resolve(); + this._continue = undefined; + } + this.iterations.push(i); + } + return 'done'; + } + + get continue() { + if (this._continue === undefined) { + throw new Error('Cannot call continue twice on a single iteration'); + } else { + try { + return this._continue; + } finally { + // detect invalid usage. Specifically, our test (as written + // currently) would be in-error if any given continue() is invoked more + // then once + this._continue = undefined; + } + } + } + } + + test('it works', async function (assert) { + const thing = new NativeThing(); + + thing.doStuffCo(); + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + assert.deepEqual(thing.iterations, [0]); + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + assert.deepEqual(thing.iterations, [0, 1]); + assert.deepEqual(getPendingWaiterState().pending, 1); + + await thing.continue(); + + assert.deepEqual(thing.iterations, [0, 1, 2]); + assert.deepEqual(getPendingWaiterState().pending, 0); + }); + }); + + test('types', async function (assert) { + assert.expect(0); + + async function asyncFn(a: string, b: string) { + return `${a}${b}`; + } + function* genFn(a: string, b: string) { + yield `${a}${b}`; + return `${a}${b}`; + } + + function asyncNoop(fn: typeof asyncFn) { + return fn; + } + function genNoop(fn: typeof genFn) { + return fn; + } + + asyncNoop(waitFor(asyncFn)); + genNoop(waitFor(genFn)); + + // @ts-expect-error wrong argument types + waitFor(asyncFn)(1, 2); + // @ts-expect-error wrong argument types + waitFor(genFn)(1, 2); + }); + }); +} diff --git a/test-apps/ember-concurrency-v3/package.json b/test-apps/ember-concurrency-v3/package.json index 638d1429..0ed74717 100644 --- a/test-apps/ember-concurrency-v3/package.json +++ b/test-apps/ember-concurrency-v3/package.json @@ -61,7 +61,7 @@ "@types/ember__template": "^4.0.2", "@types/ember__test": "^4.0.2", "@types/ember__utils": "^4.0.3", - "@types/qunit": "^2.19.6", + "@types/qunit": "^2.19.9", "@types/rsvp": "^4.0.4", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", @@ -95,7 +95,7 @@ "eslint-plugin-qunit": "^8.0.0", "loader.js": "^4.7.0", "prettier": "^3.0.3", - "qunit": "^2.19.4", + "qunit": "^2.20.0", "qunit-dom": "^2.0.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", diff --git a/test-apps/ember-fetch-v8/package.json b/test-apps/ember-fetch-v8/package.json index 6dde63b7..d1b73e4e 100644 --- a/test-apps/ember-fetch-v8/package.json +++ b/test-apps/ember-fetch-v8/package.json @@ -61,7 +61,7 @@ "@types/ember__template": "^4.0.2", "@types/ember__test": "^4.0.2", "@types/ember__utils": "^4.0.3", - "@types/qunit": "^2.19.6", + "@types/qunit": "^2.19.9", "@types/rsvp": "^4.0.4", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", @@ -95,7 +95,7 @@ "eslint-plugin-qunit": "^8.0.0", "loader.js": "^4.7.0", "prettier": "^3.0.3", - "qunit": "^2.19.4", + "qunit": "^2.20.0", "qunit-dom": "^2.0.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0",