From 2c2cca3eb21cc704d999f1e6a56e85890d281536 Mon Sep 17 00:00:00 2001 From: cmorten Date: Sat, 6 Jan 2024 15:58:18 +0000 Subject: [PATCH] test: add a test for `React.useId()` use-case * Introducing a fragment introduced edge-case which wasn't covered, so extra null guard added * For some reason linter/ts complaining so some fixups for that as well * Upped timeouts as my old laptop was struggling to run tests in time! --- .vscode/settings.json | 3 + jest.config.js | 2 +- package.json | 5 +- src/getLiveSpokenPhrase.ts | 16 ++++-- src/test/int/ariaLive.int.test.ts | 14 ++--- src/test/int/ariaLive2.int.test.ts | 4 +- src/test/int/ariaRelevant.int.test.ts | 64 +++++++++++++++++++-- src/test/int/ariaRelevant.js | 29 +++++----- src/test/int/liveRegionRoles.int.test.ts | 18 +++--- src/test/int/reactUseId.int.test.tsx | 62 ++++++++++++++++++++ tsconfig.json | 4 +- yarn.lock | 73 +++++++++++++++++++++++- 12 files changed, 248 insertions(+), 46 deletions(-) create mode 100644 src/test/int/reactUseId.int.test.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 3b65b55..22b1918 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,8 @@ }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/jest.config.js b/jest.config.js index 5f8fbb9..b6c0eb5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,7 +3,7 @@ module.exports = { preset: "ts-jest", testEnvironment: "jsdom", roots: ["src"], - collectCoverageFrom: ["**/*.ts"], + collectCoverageFrom: ["**/*.ts", "**/*.tsx"], coveragePathIgnorePatterns: [], coverageThreshold: { global: { diff --git a/package.json b/package.json index 62bf066..78e4ecf 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "ci": "yarn clean && yarn lint && yarn test:coverage && yarn build", "clean": "rimraf lib", "compile": "tsc", - "lint": "eslint . --ext .ts --cache", + "lint": "eslint src --ext .ts --cache", "lint:fix": "yarn lint --fix", "test": "jest", "test:coverage": "yarn test --coverage", @@ -38,6 +38,7 @@ }, "devDependencies": { "@testing-library/jest-dom": "^6.1.4", + "@testing-library/react": "^14.1.2", "@types/jest": "^29.5.8", "@types/node": "^20.9.0", "@typescript-eslint/eslint-plugin": "^6.10.0", @@ -46,6 +47,8 @@ "eslint-config-prettier": "^9.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", "rimraf": "^5.0.5", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", diff --git a/src/getLiveSpokenPhrase.ts b/src/getLiveSpokenPhrase.ts index ed838cd..bf6f912 100644 --- a/src/getLiveSpokenPhrase.ts +++ b/src/getLiveSpokenPhrase.ts @@ -58,7 +58,10 @@ function getSpokenPhraseForNode(node: Node) { return ( getAccessibleName(node) || getAccessibleValue(node) || - sanitizeString(node.textContent) + // `node.textContent` is only `null` if the `node` is a `document` or a + // `doctype`. We don't consider either. + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sanitizeString(node.textContent!) ); } @@ -219,8 +222,11 @@ function getLiveRegionAttributes( } if (typeof relevant === "undefined" && target.hasAttribute("aria-relevant")) { + // The `target.hasAttribute("aria-relevant")` check is sufficient to guard + // against the `target.getAttribute("aria-relevant")` being null. + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion relevant = target - .getAttribute("aria-relevant") + .getAttribute("aria-relevant")! .split(" ") .filter( (token) => !!RELEVANT_VALUES.includes(token as Relevant) @@ -244,7 +250,9 @@ function getLiveRegionAttributes( }; } - if (target === container) { + const targetAncestor = target.parentElement; + + if (target === container || targetAncestor === null) { return { atomic: atomic ?? DEFAULT_ATOMIC, live: live ?? DEFAULT_LIVE, @@ -253,8 +261,6 @@ function getLiveRegionAttributes( }; } - const targetAncestor = target.parentElement; - return getLiveRegionAttributes( { container, target: targetAncestor }, { diff --git a/src/test/int/ariaLive.int.test.ts b/src/test/int/ariaLive.int.test.ts index 688165d..ff0bd14 100644 --- a/src/test/int/ariaLive.int.test.ts +++ b/src/test/int/ariaLive.int.test.ts @@ -27,7 +27,7 @@ describe("Aria Live", () => { expect(document.querySelector("#target-2")?.textContent).toBe( "I am now populated aria-live=polite" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -52,7 +52,7 @@ describe("Aria Live", () => { "Existing Content updated aria-live=polite" ); }, - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -100,7 +100,7 @@ describe("Aria Live", () => { expect(document.querySelector("#target-3")?.textContent).toBe( "I am now populated aria-live=assertive" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -125,7 +125,7 @@ describe("Aria Live", () => { "Existing Content updated aria-live=assertive" ); }, - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -175,7 +175,7 @@ describe("Aria Live", () => { expect(document.querySelector("#target-1")?.textContent).toBe( "I am now populated aria-live=off" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -197,7 +197,7 @@ describe("Aria Live", () => { "Existing Content updated aria-live=off" ); }, - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -221,7 +221,7 @@ describe("Aria Live", () => { document.querySelector("#target-fully-defined")?.textContent ).toBe("I am now populated"); }, - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ diff --git a/src/test/int/ariaLive2.int.test.ts b/src/test/int/ariaLive2.int.test.ts index 458323b..4812ca6 100644 --- a/src/test/int/ariaLive2.int.test.ts +++ b/src/test/int/ariaLive2.int.test.ts @@ -24,7 +24,7 @@ describe("Aria Live - Content Editable", () => { expect(document.querySelector("#target")?.textContent).toBe( "Edit this text! And announce!" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -72,7 +72,7 @@ describe("Aria Live - All Attributes", () => { await waitFor( () => expect(document.querySelector("#target")?.textContent).toBe("Updated"), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ diff --git a/src/test/int/ariaRelevant.int.test.ts b/src/test/int/ariaRelevant.int.test.ts index bd1e464..57acd4f 100644 --- a/src/test/int/ariaRelevant.int.test.ts +++ b/src/test/int/ariaRelevant.int.test.ts @@ -14,6 +14,60 @@ describe("Aria Relevant", () => { teardown(); }); + it("should handle no value - applied to the div element", async () => { + await virtual.start({ container: document.body }); + + // Navigate to aria-live=polite button + const button = screen.getByRole("button", { + name: "Test aria-relevant missing", + }); + + button.focus(); + + await virtual.act(); + + await waitFor(() => + expect( + document.querySelector("#target-0 [data-change]")?.textContent + ).toBe("Content changed") + ); + + expect(await virtual.spokenPhraseLog()).toEqual([ + "document", + "button, Test aria-relevant missing", + "assertive: DOM was added", + "assertive: Content changed", + ]); + + await virtual.stop(); + }); + + it("should handle an empty value - applied to the div element", async () => { + await virtual.start({ container: document.body }); + + // Navigate to aria-live=polite button + const button = screen.getByRole("button", { + name: "Test aria-relevant empty", + }); + + button.focus(); + + await virtual.act(); + + await waitFor(() => + expect( + document.querySelector("#target-1 [data-change]")?.textContent + ).toBe("Content changed") + ); + + expect(await virtual.spokenPhraseLog()).toEqual([ + "document", + "button, Test aria-relevant empty", + ]); + + await virtual.stop(); + }); + it("should convey the 'additions' value - applied to the div element", async () => { await virtual.start({ container: document.body }); @@ -28,7 +82,7 @@ describe("Aria Relevant", () => { await waitFor(() => expect( - document.querySelector("#target-0 [data-change]")?.textContent + document.querySelector("#target-2 [data-change]")?.textContent ).toBe("Content changed") ); @@ -55,7 +109,7 @@ describe("Aria Relevant", () => { await waitFor(() => expect( - document.querySelector("#target-4 [data-change]")?.textContent + document.querySelector("#target-6 [data-change]")?.textContent ).toBe("Content changed") ); @@ -83,7 +137,7 @@ describe("Aria Relevant", () => { await waitFor(() => expect( - document.querySelector("#target-3 [data-change]")?.textContent + document.querySelector("#target-5 [data-change]")?.textContent ).toBe("Content changed") ); @@ -112,7 +166,7 @@ describe("Aria Relevant", () => { await waitFor(() => expect( - document.querySelector("#target-1 [data-change]")?.textContent + document.querySelector("#target-3 [data-change]")?.textContent ).toBe("Content changed") ); @@ -139,7 +193,7 @@ describe("Aria Relevant", () => { await waitFor(() => expect( - document.querySelector("#target-2 [data-change]")?.textContent + document.querySelector("#target-4 [data-change]")?.textContent ).toBe("Content changed") ); diff --git a/src/test/int/ariaRelevant.js b/src/test/int/ariaRelevant.js index ea8a7e2..3f65f54 100644 --- a/src/test/int/ariaRelevant.js +++ b/src/test/int/ariaRelevant.js @@ -14,25 +14,28 @@ function test(index) { function setupAriaRelevant() { document.body.innerHTML = `
`; - ["additions", "removals", "text", "all", "additions text"].forEach(function ( - attr, - index - ) { - const content = document.querySelector("#content"); - const div = document.createElement("div"); - - div.innerHTML = ` + [null, "", "additions", "removals", "text", "all", "additions text"].forEach( + function (attr, index) { + const content = document.querySelector("#content"); + const div = document.createElement("div"); + + div.innerHTML = `

aria-relevant=${attr}

-
+
DOM was removed
- + `; - content.appendChild(div); - document.querySelector(`#trigger-${index}`).onclick = () => test(index); - }); + content.appendChild(div); + document.querySelector(`#trigger-${index}`).onclick = () => test(index); + } + ); return () => { document.body.innerHTML = ""; diff --git a/src/test/int/liveRegionRoles.int.test.ts b/src/test/int/liveRegionRoles.int.test.ts index 28e0362..8fb9c27 100644 --- a/src/test/int/liveRegionRoles.int.test.ts +++ b/src/test/int/liveRegionRoles.int.test.ts @@ -27,7 +27,7 @@ describe("Live Region Roles", () => { expect(document.querySelector("#target-alert")?.textContent).toBe( "Initial Content Populated role=alert" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -51,7 +51,7 @@ describe("Live Region Roles", () => { expect( document.querySelector("#target-alert-non-atomic")?.textContent ).toBe("Initial Content Populated role=alert"), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -73,7 +73,7 @@ describe("Live Region Roles", () => { expect(document.querySelector("#target-log")?.textContent).toBe( "Initial Content Populated role=log" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -95,7 +95,7 @@ describe("Live Region Roles", () => { expect(document.querySelector("#target-marquee")?.textContent).toBe( "Initial Content Populated role=marquee" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -116,7 +116,7 @@ describe("Live Region Roles", () => { expect(document.querySelector("#target-status")?.textContent).toBe( "Initial Content Populated role=status" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -140,7 +140,7 @@ describe("Live Region Roles", () => { expect( document.querySelector("#target-status-non-atomic")?.textContent ).toBe("Initial Content Populated role=status"), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -162,7 +162,7 @@ describe("Live Region Roles", () => { expect(document.querySelector("#target-timer")?.textContent).toBe( "Initial Content Populated role=timer" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -183,7 +183,7 @@ describe("Live Region Roles", () => { expect(document.querySelector("#target-alertdialog")?.textContent).toBe( "Initial Content Populated role=alertdialog" ), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ @@ -207,7 +207,7 @@ describe("Live Region Roles", () => { expect( document.querySelector("#target-alertdialog-non-atomic")?.textContent ).toBe("Initial Content Populated role=alertdialog"), - { timeout: 5000 } + { timeout: 7000 } ); expect(await virtual.spokenPhraseLog()).toEqual([ diff --git a/src/test/int/reactUseId.int.test.tsx b/src/test/int/reactUseId.int.test.tsx new file mode 100644 index 0000000..b29702a --- /dev/null +++ b/src/test/int/reactUseId.int.test.tsx @@ -0,0 +1,62 @@ +import React, { useId } from "react"; +import { render } from "@testing-library/react"; +import { virtual } from "../.."; + +const ComponentWithReactUseId = () => { + const childId = useId(); + + return ( + <> + + + + + ); +}; + +describe("React.useId()", () => { + afterEach(async () => { + document.body.innerHTML = ""; + + try { + await virtual.stop(); + } catch { + // swallow + } + }); + + test("should handle ids generated by React.useId() hooks", async () => { + const { container } = render(); + + await virtual.start({ container }); + + for (let i = 0; i <= 14; i++) { + await virtual.next(); + } + + expect(await virtual.spokenPhraseLog()).toEqual([ + "list", + "listitem", + "Fruit", + "list", + "listitem", + "Apples", + "end of listitem", + "listitem", + "Bananas", + "end of listitem", + "end of list", + "end of listitem", + "listitem", + "Vegetables", + "end of listitem", + "end of list", + ]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 743cfb8..ad29085 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,10 +4,12 @@ "target": "esnext", "module": "commonjs", "moduleResolution": "node", + "esModuleInterop": true, + "jsx": "react", "declaration": true, "rootDir": "./src", "outDir": "./lib" }, - "include": ["src/**/*.ts", "src/test/jest.setup.ts"], + "include": ["src/**/*.tsx?", "src/test/jest.setup.ts"], "exclude": ["node_modules", "lib"] } diff --git a/yarn.lock b/yarn.lock index ae59cd8..080e9a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -839,7 +839,7 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@testing-library/dom@^9.3.3": +"@testing-library/dom@^9.0.0", "@testing-library/dom@^9.3.3": version "9.3.3" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.3.tgz#108c23a5b0ef51121c26ae92eb3179416b0434f5" integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== @@ -867,6 +867,15 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/react@^14.1.2": + version "14.1.2" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.1.2.tgz#a2b9e9ee87721ec9ed2d7cfc51cc04e474537c32" + integrity sha512-z4p7DVBTPjKM5qDZ0t5ZjzkpSNb+fZy1u6bzO7kk8oeGagpPCAtgh4cx1syrfp7a+QWkM021jGqjJaxJJnXAZg== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" + "@testing-library/user-event@^14.5.1": version "14.5.1" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" @@ -1000,6 +1009,32 @@ dependencies: undici-types "~5.26.4" +"@types/prop-types@*": + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + +"@types/react-dom@^18.0.0": + version "18.2.18" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" + integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.2.47" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.47.tgz#85074b27ab563df01fbc3f68dc64bf7050b0af40" + integrity sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + "@types/semver@^7.5.0": version "7.5.4" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff" @@ -1595,6 +1630,11 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + data-urls@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" @@ -3031,7 +3071,7 @@ jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -3168,6 +3208,13 @@ lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3565,6 +3612,14 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -3575,6 +3630,13 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + "readable-stream@>=1.0.33-1 <1.1.0-0": version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -3717,6 +3779,13 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + semver@^6.0.0, semver@^6.3.0: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"