diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 22df10b9..ea47ffd7 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -10,18 +10,21 @@ on: workflow_call: jobs: - test: - name: Run unit tests + lint: + name: Run linter runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - name: "☁️ checkout repository" + uses: actions/checkout@v4 - - uses: pnpm/action-setup@v3 + - name: "🔧 setup pnpm" + uses: pnpm/action-setup@v3 with: version: 9 - - uses: actions/setup-node@v4 + - name: "🔧 setup node" + uses: actions/setup-node@v4 with: node-version: 20 cache: "pnpm" @@ -29,24 +32,22 @@ jobs: - name: "📦 install dependencies" run: pnpm install - - name: "🔍 run tests" - run: pnpm test + - name: "🔍 lint code" + run: pnpm lint - lint: - name: Run linter + test: + needs: lint + name: Run unit tests runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 60 steps: - - name: "☁️ checkout repository" - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: "🔧 setup pnpm" - uses: pnpm/action-setup@v3 + - uses: pnpm/action-setup@v3 with: version: 9 - - name: "🔧 setup node" - uses: actions/setup-node@v4 + - uses: actions/setup-node@v4 with: node-version: 20 cache: "pnpm" @@ -54,5 +55,8 @@ jobs: - name: "📦 install dependencies" run: pnpm install - - name: "🔍 lint code" - run: pnpm lint + - name: "🎭 install playwright" + run: pnpx playwright install --with-deps --only-shell + + - name: "🔍 run tests" + run: pnpm test diff --git a/.gitignore b/.gitignore index 1cd62e35..862d03e0 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,9 @@ out-tsc # Vitepress docs/.vitepress/dist docs/.vitepress/cache + +# Playwright +test-results/ +playwright-report/ +blob-report/ +playwright/.cache/ diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 90d4cd31..5ba81ca2 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,3 +1,3 @@ -import vitepressConfig from '../vitepress.config'; +import vitepressConfig from "../vitepress.config.ts"; export default vitepressConfig; diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index d541d744..93c45f8e 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,7 +1,7 @@ -import DefaultTheme from "vitepress/theme"; +import defaultTheme from "vitepress/theme"; import "../../../packages/theme/src/index.css"; import "./custom.css"; export default { - extends: DefaultTheme, + extends: defaultTheme, }; diff --git a/eslint.config.js b/eslint.config.js index 495dfe34..ee5da04c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,11 +1,9 @@ import jsLint from "@eslint/js"; import commentsPlugin from "eslint-plugin-eslint-comments"; import importPlugin from "eslint-plugin-import"; -import jestPlugin from "eslint-plugin-jest"; -import jestDomPlugin from "eslint-plugin-jest-dom"; import litPlugin from "eslint-plugin-lit"; +import playwrightPlugin from "eslint-plugin-playwright"; import prettierRecommendedConfig from "eslint-plugin-prettier/recommended"; -import testingLibraryPlugin from "eslint-plugin-testing-library"; import wcPlugin from "eslint-plugin-wc"; import { config, configs as tsLintConfigs } from "typescript-eslint"; @@ -20,6 +18,9 @@ export default config( { files: ["*.ts", "*.tsx"], }, + { + ignores: ["dist", "node_modules", "docs/.vitepress/cache"], + }, { languageOptions: { parserOptions: { @@ -47,15 +48,7 @@ export default config( }, { files: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"], - extends: [ - jestDomPlugin.configs["flat/recommended"], - testingLibraryPlugin.configs["flat/react"], - testingLibraryPlugin.configs["flat/dom"], - jestPlugin.configs["flat/recommended"], - ], - rules: { - "jest/prefer-importing-jest-globals": "error", - }, + extends: [playwrightPlugin.configs["flat/recommended"]], }, { rules: { diff --git a/internals/test-helpers/create-promise-resolvers.ts b/internals/test-helpers/create-promise-resolvers.ts new file mode 100644 index 00000000..b4fd49c3 --- /dev/null +++ b/internals/test-helpers/create-promise-resolvers.ts @@ -0,0 +1,33 @@ +const createPromiseResolvers = () => { + const noop = () => void 0; + + let _resolve: (value: T | PromiseLike) => void = noop; + let _reject: (reason?: unknown) => void = noop; + + const createPromise = () => + new Promise((res, rej) => { + _resolve = res; + _reject = rej; + }); + + let _promise = createPromise(); + + const renew = () => { + _promise = createPromise(); + }; + + return { + get promise() { + return _promise; + }, + get resolve() { + return _resolve; + }, + get reject() { + return _reject; + }, + renew, + }; +}; + +export default createPromiseResolvers; diff --git a/internals/test-helpers/forEachLocator.ts b/internals/test-helpers/forEachLocator.ts new file mode 100644 index 00000000..8a2314e7 --- /dev/null +++ b/internals/test-helpers/forEachLocator.ts @@ -0,0 +1,10 @@ +import { type Locator } from "@playwright/test"; + +export const forEachLocator = async ( + locators: Locator[], + callback: (locator: Locator, index: number) => void | Promise, +) => { + for (const [index, locator] of locators.entries()) { + await callback(locator, index); + } +}; diff --git a/internals/test-helpers/handles.ts b/internals/test-helpers/handles.ts new file mode 100644 index 00000000..c7e77ea5 --- /dev/null +++ b/internals/test-helpers/handles.ts @@ -0,0 +1,9 @@ +import type { Page } from "@playwright/test"; + +export const windowHandle = (page: Page) => page.evaluateHandle(() => window); + +export const documentHandle = (page: Page) => + page.evaluateHandle(() => document); + +export const bodyHandle = (page: Page) => + page.evaluateHandle(() => document.body); diff --git a/internals/test-helpers/index.ts b/internals/test-helpers/index.ts index d0682557..37848462 100644 --- a/internals/test-helpers/index.ts +++ b/internals/test-helpers/index.ts @@ -1,3 +1,28 @@ -export { act, render } from "@testing-library/react"; -export { default as userEvent } from "@testing-library/user-event"; -export * from "shadow-dom-testing-library"; +import { test as base } from "@playwright/test"; + +const test = base.extend({ + page: async ({ page }, use) => { + await page.goto("/test"); + await use(page); + }, +}); + +const { afterAll, afterEach, beforeAll, beforeEach, describe, expect, step } = + test; + +export { default as createPromiseResolvers } from "./create-promise-resolvers.ts"; +export * from "./forEachLocator.ts"; +export * from "./handles.ts"; +export * from "./mock/index.ts"; +export * from "./render.ts"; + +export { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + step, + test, +}; diff --git a/internals/test-helpers/mock/constants.ts b/internals/test-helpers/mock/constants.ts new file mode 100644 index 00000000..e35b8f2a --- /dev/null +++ b/internals/test-helpers/mock/constants.ts @@ -0,0 +1,2 @@ +export const globalMockReferenceKey = "__global_mock_ref__"; +export const mockNamespaces = ["events"] as const; diff --git a/internals/test-helpers/mock/events-mock.ts b/internals/test-helpers/mock/events-mock.ts new file mode 100644 index 00000000..e5928c73 --- /dev/null +++ b/internals/test-helpers/mock/events-mock.ts @@ -0,0 +1,159 @@ +import type { Locator, Page } from "@playwright/test"; +import { globalMockReferenceKey } from "./constants.ts"; +import type { MockFn } from "./types.ts"; + +export type EventsMockState = Record>; + +const attachMockedEvent = async ( + locator: Locator, + eventName: string, + mockFn: MockFn, +) => { + await locator.evaluateHandle( + (element, [gmrk, eventName, mockFn]) => { + if (!element) { + throw new Error( + "Expected an element with the specified locator to be found on the page.", + ); + } + + const globalMock = window[gmrk]; + + if (!globalMock) { + throw new Error( + [ + "Global mock object not found.", + "Ensure that the global mock object is properly initialized.", + ].join(" "), + ); + } + + if (!globalMock.events) { + throw new Error( + [ + "Global events mock object not found.", + "Ensure that the global events mock object is properly initialized.", + ].join(" "), + ); + } + + const listeners = globalMock.events[eventName] ?? []; + const abortController = new AbortController(); + + globalMock.events[eventName] = listeners; + + element.addEventListener( + eventName, + (...args) => { + mockFn.called = true; + mockFn.callCount += 1; + mockFn.calls.push(args); + }, + { signal: abortController.signal }, + ); + + globalMock.events[eventName].push([mockFn, abortController]); + globalMock.mockFns.push(mockFn); + }, + [globalMockReferenceKey, eventName, mockFn] as const, + ); +}; + +const detachMockedEvent = async ( + locator: Locator, + eventName: string, + mockFn: MockFn, +) => { + await locator.evaluateHandle( + (element, [gmrk, eventName, mockFn]) => { + if (!element) { + throw new Error( + [ + "Element not found.", + "Expected an element with the specified locator to be present on the page.", + ].join(" "), + ); + } + + const globalMock = window[gmrk]; + + if (!globalMock) { + throw new Error( + [ + "Global mock object not found.", + "Ensure that the global mock object is properly initialized.", + ].join(" "), + ); + } + + if (!globalMock.events) { + throw new Error( + [ + "Global events mock object not found.", + "Ensure that the global events mock object is properly initialized.", + ].join(" "), + ); + } + + const listeners = globalMock.events[eventName]; + + if (!listeners) { + throw new Error( + [ + "Event list not found.", + `No event listener found for the event name "${eventName}".`, + ].join(" "), + ); + } + + const entityIdx = listeners.findIndex( + mockEntity => mockEntity[0] === mockFn, + ); + + if (entityIdx === -1) { + throw new Error( + [ + "Mock function not found.", + "The specified mock function is not registered in the event set.", + ].join(" "), + ); + } + + const entity = listeners[entityIdx]!; + + entity[1].abort(); + listeners.splice(entityIdx, 1); + + const idx = globalMock.mockFns.findIndex(fn => fn === mockFn); + + if (idx === -1) return; + + globalMock.mockFns.splice(idx, 1); + }, + [globalMockReferenceKey, eventName, mockFn] as const, + ); +}; + +export const initNamespace = async (page: Page) => { + await page.evaluate(gmrk => { + const globalMock = window[gmrk as typeof globalMockReferenceKey]; + + if (!globalMock) { + throw new Error( + [ + "Global mock object not found.", + "Ensure that the global mock object is properly initialized.", + ].join(" "), + ); + } + + globalMock.events = {} as EventsMockState; + }, globalMockReferenceKey); +}; + +export const setup = () => { + return { + attachMockedEvent, + detachMockedEvent, + }; +}; diff --git a/internals/test-helpers/mock/fn-mock.ts b/internals/test-helpers/mock/fn-mock.ts new file mode 100644 index 00000000..8255171e --- /dev/null +++ b/internals/test-helpers/mock/fn-mock.ts @@ -0,0 +1,34 @@ +import type { Page } from "@playwright/test"; +import { expect } from "../index.ts"; +import type { MockFn } from "./types"; +import { evaluateGlobalMock } from "./utils.ts"; + +const createResultMatcher = + (page: Page, mockRef: MockFn) => + async (expectedResult: Partial>) => { + const globalMockRef = await evaluateGlobalMock(page); + const mock = globalMockRef.mockFns.find(fn => fn.__id__ === mockRef.__id__); + + if (!mock) { + throw new Error( + `Expected valid mock fn. Received ${JSON.stringify(mockRef, null, 2)}`, + ); + } + + expect(mock).toEqual(expect.objectContaining(expectedResult)); + }; + +let idCounter = 0; + +const genId = () => idCounter++; + +export const setup = (page: Page) => () => { + const ref: MockFn = { + called: false, + callCount: 0, + calls: [], + __id__: genId(), + }; + + return { ref, matchResult: createResultMatcher(page, ref) }; +}; diff --git a/internals/test-helpers/mock/index.ts b/internals/test-helpers/mock/index.ts new file mode 100644 index 00000000..a639e9e3 --- /dev/null +++ b/internals/test-helpers/mock/index.ts @@ -0,0 +1,2 @@ +export { disposeMocks, setupMocks } from "./setup.ts"; +export type * from "./types.ts"; diff --git a/internals/test-helpers/mock/setup.ts b/internals/test-helpers/mock/setup.ts new file mode 100644 index 00000000..836682d6 --- /dev/null +++ b/internals/test-helpers/mock/setup.ts @@ -0,0 +1,39 @@ +import type { Page } from "@playwright/test"; +import { globalMockReferenceKey } from "./constants.ts"; +import * as eventsMock from "./events-mock.ts"; +import * as fnMock from "./fn-mock.ts"; +import type { MockFn } from "./types.ts"; + +declare global { + interface Window { + [globalMockReferenceKey]?: { + events?: eventsMock.EventsMockState; + mockFns: MockFn[]; + }; + } +} + +const attachGlobals = async (page: Page) => { + await page.evaluate(gmrk => { + window[gmrk as typeof globalMockReferenceKey] = { mockFns: [] }; + }, globalMockReferenceKey); + + await eventsMock.initNamespace(page); +}; + +const detachGlobals = (page: Page) => { + return page.evaluate(gmrk => { + delete window[gmrk as typeof globalMockReferenceKey]; + }, globalMockReferenceKey); +}; + +export const disposeMocks = (page: Page) => detachGlobals(page); + +export const setupMocks = async (page: Page) => { + await attachGlobals(page); + + return { + events: eventsMock.setup(), + createFakeFn: fnMock.setup(page), + }; +}; diff --git a/internals/test-helpers/mock/types.ts b/internals/test-helpers/mock/types.ts new file mode 100644 index 00000000..17f2c3e6 --- /dev/null +++ b/internals/test-helpers/mock/types.ts @@ -0,0 +1,8 @@ +export type CallDetail = object; + +export type MockFn = { + called: boolean; + callCount: number; + calls: CallDetail[]; + __id__: number; +}; diff --git a/internals/test-helpers/mock/utils.ts b/internals/test-helpers/mock/utils.ts new file mode 100644 index 00000000..acb865e3 --- /dev/null +++ b/internals/test-helpers/mock/utils.ts @@ -0,0 +1,19 @@ +import type { Page } from "@playwright/test"; +import { globalMockReferenceKey } from "./constants.ts"; + +export const evaluateGlobalMock = async (page: Page) => { + return page.evaluate(gmrk => { + const globalMock = window[gmrk as typeof globalMockReferenceKey]; + + if (!globalMock) { + throw new Error( + [ + "Global mock object not found.", + "Ensure that the global mock object is properly initialized.", + ].join(" "), + ); + } + + return globalMock; + }, globalMockReferenceKey); +}; diff --git a/internals/test-helpers/render.ts b/internals/test-helpers/render.ts new file mode 100644 index 00000000..cde0b229 --- /dev/null +++ b/internals/test-helpers/render.ts @@ -0,0 +1,26 @@ +import type { ElementHandle, Locator, Page } from "@playwright/test"; +import { bodyHandle } from "./handles.ts"; + +export const render = async (page: Page, content: string, root?: Locator) => { + let handle: ElementHandle | null = null; + + if (!root) handle = await bodyHandle(page); + else handle = await root.elementHandle(); + + if (!handle) throw new Error("Invalid root."); + + await page.evaluate( + ([element, content]) => { + if (!element) { + throw new Error( + "Expected an element with the specified locator or handle to be found on the page.", + ); + } + + element.innerHTML = content; + }, + [handle, content] as const, + ); + + return handle.dispose(); +}; diff --git a/package.json b/package.json index efb0872f..6b2432cf 100644 --- a/package.json +++ b/package.json @@ -21,14 +21,17 @@ "web-icons:release": "pnpm --filter @tapsioss/web-icons run release", "react-icons:build": "pnpm --filter @tapsioss/react-icons run build", "react-icons:release": "pnpm --filter @tapsioss/react-icons run release", + "packages:test": "pnpm run -r --parallel --filter=!@tapsioss/docs --filter=!@tapsioss/playground test", "packages:dev": "pnpm run -r --parallel --filter=!@tapsioss/docs dev", "packages:build": "pnpm run -r --filter=!@tapsioss/playground --filter=!@tapsioss/docs build", "prepackages:release": "pnpm packages:build", "packages:release": "pnpm run -r --filter=!@tapsioss/playground --filter=!@tapsioss/docs release", - "dev": "pnpm run packages:dev", - "test": "pnpm run -r --parallel test", + "playground:dev": "pnpm --filter @tapsioss/playground run start:dev", + "playground:test": "pnpm --filter @tapsioss/playground run start:test", + "dev": "run-p packages:dev playground:dev", + "test": "pnpm run packages:build && pnpm run packages:test", "lint:ts": "tsc --project tsconfig.json", - "lint:ecma": "eslint packages/*/src/**/* --fix", + "lint:ecma": "eslint --fix", "lint": "run-p lint:*", "fmt": "prettier packages/*/src/**/* --write --fix", "docs:vitepress:dev": "pnpm --filter docs run dev", @@ -43,14 +46,8 @@ "devDependencies": { "@custom-elements-manifest/analyzer": "^0.10.2", "@eslint/js": "^9.1.1", - "@jest/globals": "^29.7.0", "@lit/react": "^1.0.6", - "@open-wc/lit-helpers": "^0.7.0", - "@open-wc/testing": "^4.0.0", - "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", - "@testing-library/user-event": "^14.6.1", + "@playwright/test": "^1.50.1", "@types/eslint__js": "^8.42.3", "@types/mustache": "^4.2.5", "@types/node": "^20.12.10", @@ -66,15 +63,11 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jest": "^28.11.0", - "eslint-plugin-jest-dom": "^5.5.0", "eslint-plugin-lit": "^1.15.0", + "eslint-plugin-playwright": "^2.2.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-testing-library": "^7.1.1", "eslint-plugin-wc": "^2.2.0", "fast-glob": "^3.3.2", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", "lit": "^3.2.1", "mustache": "^4.2.0", "npm-run-all": "^4.1.5", @@ -87,12 +80,9 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "semver": "^7.6.3", - "shadow-dom-testing-library": "^1.11.3", "shx": "^0.3.4", "stream-chain": "^3.3.2", "stream-json": "^1.9.1", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", "tslib": "^2.8.0", "tsx": "^4.19.2", "typescript": "^5.7.3", diff --git a/packages/web-components/jest.config.ts b/packages/web-components/jest.config.ts deleted file mode 100644 index 8491e67c..00000000 --- a/packages/web-components/jest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createJsWithTsEsmPreset, type JestConfigWithTsJest } from "ts-jest"; - -const jestConfig: JestConfigWithTsJest = { - ...createJsWithTsEsmPreset(), - verbose: true, - setupFilesAfterEnv: ["./jest.setup.ts"], - testPathIgnorePatterns: ["/node_modules/", "/dist/"], - testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"], - testEnvironment: "jest-environment-jsdom", - injectGlobals: false, - moduleNameMapper: { - "^(\\.{1,2}/.*)\\.[jt]sx?$": "$1", - }, -}; - -export default jestConfig; diff --git a/packages/web-components/jest.setup.ts b/packages/web-components/jest.setup.ts deleted file mode 100644 index ba7aa37a..00000000 --- a/packages/web-components/jest.setup.ts +++ /dev/null @@ -1 +0,0 @@ -import "@testing-library/jest-dom/jest-globals"; diff --git a/packages/web-components/package.json b/packages/web-components/package.json index 7c438022..72a3589f 100644 --- a/packages/web-components/package.json +++ b/packages/web-components/package.json @@ -18,7 +18,7 @@ "prebuild": "pnpm run clear", "build": "tsc --project ./tsconfig.build.json", "predev": "pnpm run clear", - "test": "NODE_OPTIONS=--experimental-vm-modules jest", + "test": "playwright test", "dev": "tsc --watch --project ./tsconfig.dev.json", "release": "pnpm publish . --tag latest --access public" }, @@ -29,7 +29,6 @@ "tslib": "^2.8.0" }, "devDependencies": { - "@internals/test-helpers": "workspace:*", - "jest": "^29.7.0" + "@internals/test-helpers": "workspace:*" } } diff --git a/packages/web-components/playwright.config.ts b/packages/web-components/playwright.config.ts new file mode 100644 index 00000000..7f819bec --- /dev/null +++ b/packages/web-components/playwright.config.ts @@ -0,0 +1,36 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./src", + fullyParallel: true, + retries: process.env.CI ? 2 : undefined, + forbidOnly: !!process.env.CI, + reporter: process.env.CI ? [["github"]] : [["list"], ["html"]], + projects: [ + { + name: "💻 Desktop", + use: devices["Desktop Chrome"], + }, + // { + // name: "📱 iOS", + // use: devices["iPhone X"], + // }, + { + name: "📱 Android", + use: devices["Galaxy S9+"], + }, + ], + use: { + baseURL: "http://localhost:3000", + permissions: ["clipboard-write", "clipboard-read"], + }, + webServer: { + command: "pnpm --filter @tapsioss/playground run start:test", + reuseExistingServer: !process.env.CI, + url: "http://localhost:3000/test", + gracefulShutdown: { + signal: "SIGTERM", + timeout: 1000, + }, + }, +}); diff --git a/packages/web-components/src/avatar/avatar.test.ts b/packages/web-components/src/avatar/avatar.test.ts new file mode 100644 index 00000000..cf8b173e --- /dev/null +++ b/packages/web-components/src/avatar/avatar.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +const selectors = { + image: 'tapsi-avatar [part="image"]', + placeholder: 'tapsi-avatar [part="placeholder"]', +}; + +describe("🧩 avatar", () => { + test("🧪 should customize placeholder using default slot", async ({ + page, + }) => { + await render( + page, + ` + + + + + `, + ); + + const customPlaceholder = page.getByTestId("custom-placeholder"); + + await expect(customPlaceholder).toBeVisible(); + }); + + test("🧪 should show the placeholder if the `image` property was not set", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const placeholder = page.locator(selectors.placeholder); + const image = page.locator(selectors.image); + + await expect(image).toBeHidden(); + await expect(placeholder).toBeVisible(); + }); + + test("🧪 should show the image if the `image` property was set", async ({ + page, + }) => { + await render( + page, + ` + + + + + `, + ); + + const image = page.locator(selectors.image); + const placeholder = page.locator(selectors.placeholder); + const customPlaceholder = page.getByTestId("custom-placeholder"); + + await expect(image).toBeVisible(); + await expect(placeholder).toBeHidden(); + await expect(customPlaceholder).toBeHidden(); + }); +}); diff --git a/packages/web-components/src/avatar/avatar.test.tsx b/packages/web-components/src/avatar/avatar.test.tsx deleted file mode 100644 index 6902441d..00000000 --- a/packages/web-components/src/avatar/avatar.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { render, screen, userEvent } from "@internals/test-helpers"; -import { describe, expect, it, jest } from "@jest/globals"; - -import "./index.ts"; - -describe("tapsi-avatar", () => { - it("renders", async () => { - // @ts-expect-error Current React does not support custom elements. - render(); - - const el = screen.getByTestId("test"); - - const handleClick = jest.fn(); - - el.addEventListener("click", handleClick); - - await userEvent.click(el); - - expect(handleClick).toHaveBeenCalled(); - - expect(await screen.findByShadowRole("img")).toBeInTheDocument(); - }); -}); diff --git a/packages/web-components/src/badge-wrapper/badge-wrapper.test.ts b/packages/web-components/src/badge-wrapper/badge-wrapper.test.ts new file mode 100644 index 00000000..374ab4be --- /dev/null +++ b/packages/web-components/src/badge-wrapper/badge-wrapper.test.ts @@ -0,0 +1,167 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 badge-wrapper", () => { + test("🧪 should show anchor and badge slots in all anchor shapes", async ({ + page, + }) => { + const anchorShapes = ["rectangle", "circle", "pill"]; + + for (const anchorShape of anchorShapes) { + await render( + page, + ` + + + click + `, + ); + + const badgeSlot = page.getByTestId("test-badge-wrapper-badge-slot"); + const anchorSlot = page.getByTestId("test-badge-wrapper-anchor-slot"); + + await expect(badgeSlot).toBeVisible(); + await expect(anchorSlot).toBeVisible(); + } + }); + + test("🧪 should place badge correctly in `circle` anchor shape", async ({ + page, + }) => { + await render( + page, + ` + + +
+
`, + ); + + const badgeSlot = page.getByTestId("test-badge-wrapper-badge-slot"); + const anchorSlot = page.getByTestId("test-badge-wrapper-anchor-slot"); + + const badgeBoundingBox = await ( + await badgeSlot.elementHandle() + )?.boundingBox(); + + const anchorBoundingBox = await ( + await anchorSlot.elementHandle() + )?.boundingBox(); + + // We calculate the badge offset is component using this formula: + + const expectedBadgeXOffset = (anchorBoundingBox!.width * 14.5) / 100; + const expectedBadgeYOffset = (anchorBoundingBox!.height * 14.5) / 100; + + // When we actually render the components in DOM, the actual coordinates of the badge is not as same as the + // calculated number inside the code, because the browser has some constraints in positioning elements based on + // pixel density. So we define a threshold and expect the difference of actual coordinate and the expected one to be + // less than the threshold. + + const THRESHOLD = 0.0001; + + const actualBadgeYOffset = + badgeBoundingBox!.y + + 0.5 * badgeBoundingBox!.height - + anchorBoundingBox!.y; + + const actualBadgeXOffset = + badgeBoundingBox!.x + + 0.5 * badgeBoundingBox!.width - + anchorBoundingBox!.x; + + const xDiff = actualBadgeXOffset - expectedBadgeXOffset; + const yDiff = actualBadgeYOffset - expectedBadgeYOffset; + + expect(xDiff).toBeLessThan(THRESHOLD); + expect(yDiff).toBeLessThan(THRESHOLD); + }); + + test("🧪 should place badge correctly in `pill` anchor shape", async ({ + page, + }) => { + await render( + page, + ` + + +
+
`, + ); + + const badgeSlot = page.getByTestId("test-badge-wrapper-badge-slot"); + const anchorSlot = page.getByTestId("test-badge-wrapper-anchor-slot"); + + const badgeBoundingBox = await ( + await badgeSlot.elementHandle() + )?.boundingBox(); + + const anchorBoundingBox = await ( + await anchorSlot.elementHandle() + )?.boundingBox(); + + // We calculate the badge offset is component using this formula: + + const expectedBadgeOffset = + anchorBoundingBox!.height * (Math.sqrt(2) / 4) * (Math.sqrt(2) - 1); + + // When we actually render the components in DOM, the actual coordinates of the badge is not as same as the + // calculated number inside the code, because the browser has some constraints in positioning elements based on + // pixel density. So we define a threshold and expect the difference of actual coordinate and the expected one to be + // less than the threshold. + + const THRESHOLD = 0.0001; + + const actualBadgeYOffset = + badgeBoundingBox!.y + + 0.5 * badgeBoundingBox!.height - + anchorBoundingBox!.y; + + const actualBadgeXOffset = + badgeBoundingBox!.x + + 0.5 * badgeBoundingBox!.width - + anchorBoundingBox!.x; + + const xDiff = actualBadgeXOffset - expectedBadgeOffset; + const yDiff = actualBadgeYOffset - expectedBadgeOffset; + + expect(xDiff).toBeLessThan(THRESHOLD); + expect(yDiff).toBeLessThan(THRESHOLD); + }); + + test("🧪 should place badge correctly in `rectangle` anchor shape", async ({ + page, + }) => { + await render( + page, + ` + + +
+
`, + ); + + const badgeSlot = page.getByTestId("test-badge-wrapper-badge-slot"); + const anchorSlot = page.getByTestId("test-badge-wrapper-anchor-slot"); + + const badgeBoundingBox = await ( + await badgeSlot.elementHandle() + )?.boundingBox(); + + const anchorBoundingBox = await ( + await anchorSlot.elementHandle() + )?.boundingBox(); + + const actualBadgeYOffset = + badgeBoundingBox!.y + + 0.5 * badgeBoundingBox!.height - + anchorBoundingBox!.y; + + const actualBadgeXOffset = + badgeBoundingBox!.x + + 0.5 * badgeBoundingBox!.width - + anchorBoundingBox!.x; + + expect(actualBadgeXOffset).toBe(0); + expect(actualBadgeYOffset).toBe(0); + }); +}); diff --git a/packages/web-components/src/badge/badge.test.ts b/packages/web-components/src/badge/badge.test.ts new file mode 100644 index 00000000..01f84b5f --- /dev/null +++ b/packages/web-components/src/badge/badge.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 badge", () => { + test("🧪 should show value when variant is not `dot`", async ({ page }) => { + const badge = page.getByTestId("test-badge"); + + await render( + page, + ``, + ); + await expect(badge.getByText("20")).toBeHidden(); + + await render( + page, + ``, + ); + await expect(badge.getByText("20")).toBeVisible(); + + await render( + page, + ``, + ); + await expect(badge.getByText("20")).toBeVisible(); + }); + + test("🧪 should render icon slot when variant is `pill`", async ({ + page, + }) => { + const iconSlot = ` + + + + `; + + const icon = page.getByTestId("test-badge-icon-slot"); + + await render( + page, + ` + ${iconSlot}`, + ); + await expect(icon).toBeHidden(); + + await render( + page, + ` + ${iconSlot}`, + ); + await expect(icon).toBeHidden(); + + await render( + page, + ` + ${iconSlot}`, + ); + await expect(icon).toBeVisible(); + }); +}); diff --git a/packages/web-components/src/banner/banner.test.ts b/packages/web-components/src/banner/banner.test.ts new file mode 100644 index 00000000..928eb5cc --- /dev/null +++ b/packages/web-components/src/banner/banner.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 banner", () => { + test("🧪 should render banner elements", async ({ page }) => { + await render( + page, + ` + + click + `, + ); + + const heading = page.locator("tapsi-banner [part=heading]"); + const description = page.locator("tapsi-banner [part=description]"); + const action = page.locator("tapsi-banner [part=action]"); + + await expect(heading).toBeVisible(); + await expect(description).toBeVisible(); + await expect(action).toBeVisible(); + }); +}); diff --git a/packages/web-components/src/base-text-input/constants.ts b/packages/web-components/src/base-text-input/constants.ts index 610558aa..9aace919 100644 --- a/packages/web-components/src/base-text-input/constants.ts +++ b/packages/web-components/src/base-text-input/constants.ts @@ -2,3 +2,10 @@ export const Slots = { LEADING_ICON: "leading-icon", TRAILING: "trailing", } as const; + +export const ErrorMessages = { + SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE: [ + "Expected a valid `label` or `labelledby` attribute, received none.", + "If you want to hide the label, provide both `label` and `hide-label` attributes.", + ].join(" "), +} as const; diff --git a/packages/web-components/src/bottom-navigation/bottom-navigation.test.ts b/packages/web-components/src/bottom-navigation/bottom-navigation.test.ts new file mode 100644 index 00000000..d236b026 --- /dev/null +++ b/packages/web-components/src/bottom-navigation/bottom-navigation.test.ts @@ -0,0 +1,338 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + forEachLocator, + render, + setupMocks, + test, +} from "@internals/test-helpers"; +import { type TapsiBottomNavigationItem } from "@tapsioss/web-components/bottom-navigation/index"; + +describe("🧩 bottom-navigation-item", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `activate` event for toggling each item", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + + `, + ); + + const item1 = page.getByTestId("test-bottom-navigation-item-1"); + const item2 = page.getByTestId("test-bottom-navigation-item-2"); + + const mocks = await setupMocks(page); + const handleActivate1 = mocks.createFakeFn(); + const handleActivate2 = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + item1, + "activate", + handleActivate1.ref, + ); + await mocks.events.attachMockedEvent( + item2, + "activate", + handleActivate2.ref, + ); + + await item1.click(); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 0 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await item2.click(); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 1 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + + await item1.click(); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 1 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await item2.click(); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 2 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + }); + + test("🧪 should be able to activate using javascript", async ({ page }) => { + await render( + page, + ` + + آیتم ۱ + + `, + ); + + const item1 = page.getByTestId("test-bottom-navigation-item-1"); + + await expect(item1).toHaveJSProperty("active", false); + + await page.evaluate(() => { + const item = document.getElementById( + "test-bottom-navigation-item-1", + ) as TapsiBottomNavigationItem; + + item.active = true; + }); + + await expect(item1).toHaveJSProperty("active", true); + }); + + test("🧪 should show icon slot", async ({ page }) => { + await render( + page, + ` + + + + + آیتم ۱ + + `, + ); + + const icon = page.getByTestId("test-bottom-navigation-item-icon"); + + await expect(icon).toBeVisible(); + }); +}); + +describe("🧩 bottom-navigation", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `activate` event for toggling each item", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + + `, + ); + + const item1 = page.getByTestId("test-bottom-navigation-item-1"); + const item2 = page.getByTestId("test-bottom-navigation-item-2"); + + const mocks = await setupMocks(page); + const handleActivate1 = mocks.createFakeFn(); + const handleActivate2 = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + item1, + "activate", + handleActivate1.ref, + ); + await mocks.events.attachMockedEvent( + item2, + "activate", + handleActivate2.ref, + ); + + await item1.click(); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 0 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await item2.click(); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 1 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + + await item1.click(); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 1 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await item2.click(); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 2 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + }); + + test("🧪 should be able to change focus using tab and shift+tab", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + + `, + ); + + const item1 = page.getByTestId("test-bottom-navigation-item-1"); + const item2 = page.getByTestId("test-bottom-navigation-item-2"); + + await page.keyboard.press("Tab"); + await expect(item1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(item2).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(item1).toBeFocused(); + }); + + test("🧪 should trigger the `activechange` event by changing the active item using click", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + آیتم ۳ + آیتم ۴ + + `, + ); + + const container = page.getByTestId("test-bottom-navigation"); + const item1 = page.getByTestId("test-bottom-navigation-item-1"); + const item2 = page.getByTestId("test-bottom-navigation-item-2"); + const item3 = page.getByTestId("test-bottom-navigation-item-3"); + const item4 = page.getByTestId("test-bottom-navigation-item-4"); + + const mocks = await setupMocks(page); + const handleActiveChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + container, + "activechange", + handleActiveChange.ref, + ); + + await handleActiveChange.matchResult({ called: false }); + + await item1.click(); + await handleActiveChange.matchResult({ callCount: 1 }); + await expect(item1).toHaveJSProperty("active", true); + await forEachLocator( + [item2, item3, item4], + async item => await expect(item).not.toHaveAttribute("active"), + ); + + await item2.click(); + await handleActiveChange.matchResult({ callCount: 2 }); + await expect(item2).toHaveJSProperty("active", true); + await forEachLocator( + [item1, item3, item4], + async item => await expect(item).not.toHaveAttribute("active"), + ); + + await item3.click(); + await handleActiveChange.matchResult({ callCount: 3 }); + await expect(item3).toHaveJSProperty("active", true); + await forEachLocator( + [item1, item2, item4], + async item => await expect(item).not.toHaveAttribute("active"), + ); + + await item4.click(); + await handleActiveChange.matchResult({ callCount: 4 }); + await expect(item4).toHaveJSProperty("active", true); + await forEachLocator( + [item1, item2, item3], + async item => await expect(item).not.toHaveAttribute("active"), + ); + }); + + test("🧪 should trigger the `activechange` event by changing the active item using keyboard navigation", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + آیتم ۳ + آیتم ۴ + + `, + ); + + const container = page.getByTestId("test-bottom-navigation"); + const item1 = page.getByTestId("test-bottom-navigation-item-1"); + const item2 = page.getByTestId("test-bottom-navigation-item-2"); + const item3 = page.getByTestId("test-bottom-navigation-item-3"); + const item4 = page.getByTestId("test-bottom-navigation-item-4"); + + const mocks = await setupMocks(page); + const handleActiveChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + container, + "activechange", + handleActiveChange.ref, + ); + + await handleActiveChange.matchResult({ called: false }); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Space"); + await handleActiveChange.matchResult({ callCount: 1 }); + await expect(item1).toHaveJSProperty("active", true); + await forEachLocator( + [item2, item3, item4], + async item => await expect(item).not.toHaveAttribute("active"), + ); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Space"); + await handleActiveChange.matchResult({ callCount: 2 }); + await expect(item2).toHaveJSProperty("active", true); + await forEachLocator( + [item1, item3, item4], + async item => await expect(item).not.toHaveAttribute("active"), + ); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Space"); + await handleActiveChange.matchResult({ callCount: 3 }); + await expect(item3).toHaveJSProperty("active", true); + await forEachLocator( + [item1, item2, item4], + async item => await expect(item).not.toHaveAttribute("active"), + ); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Space"); + await handleActiveChange.matchResult({ callCount: 4 }); + await expect(item4).toHaveJSProperty("active", true); + await forEachLocator( + [item1, item2, item3], + async item => await expect(item).not.toHaveAttribute("active"), + ); + }); +}); diff --git a/packages/web-components/src/button-group/button-group.test.ts b/packages/web-components/src/button-group/button-group.test.ts new file mode 100644 index 00000000..4d3e0daa --- /dev/null +++ b/packages/web-components/src/button-group/button-group.test.ts @@ -0,0 +1,70 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 button-group", () => { + test("🧪 should render it's children", async ({ page }) => { + await render( + page, + ` + + دکمه ۱ + دکمه ۲ + `, + ); + + const button1 = page.getByTestId("test-button-1"); + const button2 = page.getByTestId("test-button-2"); + + await expect(button1).toBeVisible(); + await expect(button2).toBeVisible(); + }); + + test("🧪 should work in `horizontal` orientation", async ({ page }) => { + await render( + page, + ` + + دکمه ۱ + دکمه ۲ + `, + ); + + const button1 = page.getByTestId("test-button-1"); + const button2 = page.getByTestId("test-button-2"); + + const button1BoundingBox = await ( + await button1.elementHandle() + )?.boundingBox(); + + const button2BoundingBox = await ( + await button2.elementHandle() + )?.boundingBox(); + + expect(button1BoundingBox!.y).toEqual(button2BoundingBox!.y); + expect(button1BoundingBox!.x).toBeGreaterThan(button2BoundingBox!.x); + }); + + test("🧪 should work in `vertical` orientation", async ({ page }) => { + await render( + page, + ` + + دکمه ۱ + دکمه ۱ + `, + ); + + const button1 = page.getByTestId("test-button-1"); + const button2 = page.getByTestId("test-button-2"); + + const button1BoundingBox = await ( + await button1.elementHandle() + )?.boundingBox(); + + const button2BoundingBox = await ( + await button2.elementHandle() + )?.boundingBox(); + + expect(button1BoundingBox!.x).toEqual(button2BoundingBox!.x); + expect(button1BoundingBox!.y).toBeLessThan(button2BoundingBox!.y); + }); +}); diff --git a/packages/web-components/src/button/icon-button/icon-button.test.ts b/packages/web-components/src/button/icon-button/icon-button.test.ts new file mode 100644 index 00000000..99367722 --- /dev/null +++ b/packages/web-components/src/button/icon-button/icon-button.test.ts @@ -0,0 +1,139 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; + +describe("🧩 icon-button", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `click` event on click", async ({ page }) => { + await render( + page, + `کلیک کنید`, + ); + + const btn = page.getByTestId("test-icon-button"); + + await expect(btn).toBeVisible(); + + const mocks = await setupMocks(page); + const handleClick = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(btn, "click", handleClick.ref); + + await handleClick.matchResult({ called: false }); + + await btn.click(); + await handleClick.matchResult({ callCount: 1 }); + }); + + test("🧪 should trigger `click` event using keyboard interaction", async ({ + page, + }) => { + await render( + page, + `کلیک کنید`, + ); + + const btn = page.getByTestId("test-icon-button"); + + await page.keyboard.press("Tab"); + + await expect(btn).toBeFocused(); + + const mocks = await setupMocks(page); + const handleClick = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(btn, "click", handleClick.ref); + + await handleClick.matchResult({ called: false }); + + await page.keyboard.press("Enter"); + await handleClick.matchResult({ callCount: 1 }); + }); + + test("🧪 should focus buttons using Tab and Shift+Tab", async ({ page }) => { + await render( + page, + ` + کلیک کنید + کلیک کنید + کلیک کنید + کلیک کنید +`, + ); + + const btn1 = page.getByTestId("test-icon-button-1"); + const btn2 = page.getByTestId("test-icon-button-2"); + const btn3 = page.getByTestId("test-icon-button-3"); + const btn4 = page.getByTestId("test-icon-button-4"); + + await page.keyboard.press("Tab"); + await expect(btn1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(btn2).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(btn3).not.toBeFocused(); + await expect(btn4).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(btn3).not.toBeFocused(); + await expect(btn2).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(btn1).toBeFocused(); + }); + + test("🧪 should not trigger `click` event when disabled", async ({ + page, + }) => { + await render( + page, + `کلیک کنید`, + ); + + const btn = page.getByTestId("test-icon-button"); + + await page.keyboard.press("Tab"); + + await expect(btn).not.toBeFocused(); + + const mocks = await setupMocks(page); + const handleClick = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(btn, "click", handleClick.ref); + + await page.keyboard.press("Enter"); + await handleClick.matchResult({ called: false }); + + await btn.click(); + await handleClick.matchResult({ called: false }); + }); + + test('🧪 should show spinner in loading state with `aria-busy="true"', async ({ + page, + }) => { + await render( + page, + ` + + کلیک کنید +`, + ); + + const spinner = page.locator("tapsi-icon-button tapsi-spinner"); + const root = page.getByRole("button"); + + await expect(spinner).toBeVisible(); + await expect(root).toHaveAttribute("aria-busy", "true"); + }); +}); diff --git a/packages/web-components/src/button/standard/button.test.ts b/packages/web-components/src/button/standard/button.test.ts new file mode 100644 index 00000000..02bdaf0b --- /dev/null +++ b/packages/web-components/src/button/standard/button.test.ts @@ -0,0 +1,161 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; + +describe("🧩 button", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `click` event on click", async ({ page }) => { + await render( + page, + `کلیک کنید`, + ); + + const btn = page.getByTestId("test-button"); + + await expect(btn).toBeVisible(); + + const mocks = await setupMocks(page); + const handleClick = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(btn, "click", handleClick.ref); + + await handleClick.matchResult({ called: false }); + + await btn.click(); + await handleClick.matchResult({ callCount: 1 }); + }); + + test("🧪 should trigger `click` event using keyboard interaction", async ({ + page, + }) => { + await render( + page, + `کلیک کنید`, + ); + + const btn = page.getByTestId("test-button"); + + await page.keyboard.press("Tab"); + + await expect(btn).toBeFocused(); + + const mocks = await setupMocks(page); + const handleClick = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(btn, "click", handleClick.ref); + + await handleClick.matchResult({ called: false }); + + await page.keyboard.press("Enter"); + await handleClick.matchResult({ callCount: 1 }); + }); + + test("🧪 should focus buttons using Tab and Shift+Tab", async ({ page }) => { + await render( + page, + ` + کلیک کنید + کلیک کنید + کلیک کنید + کلیک کنید +`, + ); + + const btn1 = page.getByTestId("test-button-1"); + const btn2 = page.getByTestId("test-button-2"); + const btn3 = page.getByTestId("test-button-3"); + const btn4 = page.getByTestId("test-button-4"); + + await page.keyboard.press("Tab"); + await expect(btn1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(btn2).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(btn3).not.toBeFocused(); + await expect(btn4).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(btn3).not.toBeFocused(); + await expect(btn2).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(btn1).toBeFocused(); + }); + + test("🧪 should not trigger `click` event when disabled", async ({ + page, + }) => { + await render( + page, + `کلیک کنید`, + ); + + const btn = page.getByTestId("test-button"); + + await page.keyboard.press("Tab"); + + await expect(btn).not.toBeFocused(); + + const mocks = await setupMocks(page); + const handleClick = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(btn, "click", handleClick.ref); + + await page.keyboard.press("Enter"); + await handleClick.matchResult({ called: false }); + + await btn.click(); + await handleClick.matchResult({ called: false }); + }); + + test("🧪 should render leading and trailing icons", async ({ page }) => { + await render( + page, + ` + + + + + کلیک کنید + + + +`, + ); + + const leading = page.getByTestId("test-button-leading-slot"); + const trailing = page.getByTestId("test-button-trailing-slot"); + + await expect(leading).toBeVisible(); + await expect(trailing).toBeVisible(); + }); + + test('🧪 should show spinner in loading state with `aria-busy="true"', async ({ + page, + }) => { + await render( + page, + ` + + کلیک کنید + `, + ); + + const spinner = page.locator("tapsi-button tapsi-spinner"); + const root = page.getByRole("button"); + + await expect(spinner).toBeVisible(); + await expect(root).toHaveAttribute("aria-busy", "true"); + }); +}); diff --git a/packages/web-components/src/chat-bubble/chat-bubble.test.ts b/packages/web-components/src/chat-bubble/chat-bubble.test.ts new file mode 100644 index 00000000..ebaf26d7 --- /dev/null +++ b/packages/web-components/src/chat-bubble/chat-bubble.test.ts @@ -0,0 +1,155 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; +import { + type States, + STATUS_TO_LOCALE_MAP, +} from "@tapsioss/web-components/chat-bubble/in/constants"; + +describe("🧩 chat-bubble", () => { + describe("🧩 chat-bubble-in", () => { + test("🧪 should show correct text under message based on `status`", async ({ + page, + }) => { + for (const status of ["pending", "seen", "sent"] as Exclude< + States, + "failed" + >[]) { + await render( + page, + ` + + سلام + + `, + ); + + const statusElement = page.locator( + 'tapsi-chat-bubble-in [part="status"] span', + ); + + await expect(statusElement).toHaveText(STATUS_TO_LOCALE_MAP[status]); + } + }); + + test("🧪 should get correct classes based on attributes", async ({ + page, + }) => { + await render( + page, + ` + + سلام + + `, + ); + + const root = page.locator('tapsi-chat-bubble-in [part="root"]'); + + await expect(root).toHaveClass(" root in sent fully-rounded "); + }); + + test("🧪 should have pre content in `failed` status", async ({ page }) => { + await render( + page, + ` + + سلام + + `, + ); + + const preContent = page.locator( + 'tapsi-chat-bubble-in [part="failure-indicator"]', + ); + + await expect(preContent).toBeVisible(); + }); + + test("🧪 should show time correctly", async ({ page }) => { + await render( + page, + ` + + سلام + + `, + ); + + const timestamp = page.locator('tapsi-chat-bubble-in [part="timestamp"]'); + + await expect(timestamp).toHaveText("12:34"); + }); + }); + + describe("🧩 chat-bubble-out", () => { + test("🧪 should show avatar if it was set", async ({ page }) => { + // If no `avatar-src` was set, the avatar should be hidden, + await render( + page, + ` + + سلام + + `, + ); + + const avatar = page.getByRole("img"); + + await expect(avatar).toBeHidden(); + + // ...otherwise, we should be able to see the avatar. + await render( + page, + ` + + سلام + + `, + ); + + await expect(avatar).toBeVisible(); + }); + + test("🧪 should show time correctly", async ({ page }) => { + await render( + page, + ` + + سلام + + `, + ); + + const timestamp = page.locator( + 'tapsi-chat-bubble-out [part="timestamp"]', + ); + + await expect(timestamp).toHaveText("12:34"); + }); + }); +}); diff --git a/packages/web-components/src/checkbox/checkbox.test.ts b/packages/web-components/src/checkbox/checkbox.test.ts new file mode 100644 index 00000000..7493a7f2 --- /dev/null +++ b/packages/web-components/src/checkbox/checkbox.test.ts @@ -0,0 +1,174 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; + +describe("🧩 checkbox", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `change` event on click", async ({ page }) => { + await render( + page, + ``, + ); + + const checkbox = page.getByTestId("test-checkbox"); + + await expect(checkbox).toBeVisible(); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(checkbox, "change", handleChange.ref); + + await handleChange.matchResult({ called: false }); + await expect(checkbox).toHaveJSProperty("checked", false); + + await checkbox.click(); + await handleChange.matchResult({ callCount: 1 }); + await expect(checkbox).toHaveJSProperty("checked", true); + }); + + test("🧪 should trigger `change` event using keyboard interaction", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const checkbox = page.getByTestId("test-checkbox"); + + await page.keyboard.press("Tab"); + + await expect(checkbox).toBeFocused(); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(checkbox, "change", handleChange.ref); + + await handleChange.matchResult({ called: false }); + await expect(checkbox).toHaveJSProperty("checked", false); + + await page.keyboard.press("Space"); + await handleChange.matchResult({ callCount: 1 }); + await expect(checkbox).toHaveJSProperty("checked", true); + }); + + test("🧪 should focus checkboxes using Tab and Shift+Tab", async ({ + page, + }) => { + await render( + page, + ` + + + + +`, + ); + + const checkbox1 = page.getByTestId("test-checkbox-1"); + const checkbox2 = page.getByTestId("test-checkbox-2"); + const checkbox3 = page.getByTestId("test-checkbox-3"); + const checkbox4 = page.getByTestId("test-checkbox-4"); + + await page.keyboard.press("Tab"); + await expect(checkbox1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(checkbox2).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(checkbox3).not.toBeFocused(); + await expect(checkbox4).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(checkbox3).not.toBeFocused(); + await expect(checkbox2).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(checkbox1).toBeFocused(); + }); + + test("🧪 should not trigger `change` event when disabled", async ({ + page, + }) => { + await render( + page, + `انتخاب کنید`, + ); + + const checkbox = page.getByTestId("test-checkbox"); + + await page.keyboard.press("Tab"); + + await expect(checkbox).not.toBeFocused(); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(checkbox, "change", handleChange.ref); + + await page.keyboard.press("Enter"); + await handleChange.matchResult({ called: false }); + + await checkbox.click(); + await handleChange.matchResult({ called: false }); + }); + + test("🧪 should be indeterminate with `indeterminate` attribute", async ({ + page, + }) => { + await render( + page, + `انتخاب کنید`, + ); + + const checkbox = page.getByTestId("test-checkbox"); + + await expect(checkbox).toHaveJSProperty("indeterminate", true); + await expect(checkbox).toHaveJSProperty("checked", false); + }); + + test("🧪 should be checked by default if `checked` attribute was set", async ({ + page, + }) => { + await render( + page, + `انتخاب کنید`, + ); + + const checkbox = page.getByTestId("test-checkbox"); + + await expect(checkbox).toHaveJSProperty("checked", true); + await expect(checkbox).toHaveJSProperty("indeterminate", false); + }); + + test("🧪 should check by clicking on external label", async ({ page }) => { + await render( + page, + ` + + + `, + ); + + const label = page.getByTestId("external-label"); + const input = page.getByTestId("test-checkbox"); + + await expect(input).toHaveJSProperty("checked", false); + + await label.click(); + await expect(input).toBeFocused(); + await expect(input).toHaveJSProperty("checked", true); + }); +}); diff --git a/packages/web-components/src/chip-group/chip-group.test.ts b/packages/web-components/src/chip-group/chip-group.test.ts new file mode 100644 index 00000000..d72fd304 --- /dev/null +++ b/packages/web-components/src/chip-group/chip-group.test.ts @@ -0,0 +1,230 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + forEachLocator, + render, + setupMocks, + test, +} from "@internals/test-helpers"; + +describe("🧩 chip-group", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should be able to change focus using tab and shift+tab", async ({ + page, + }) => { + await render( + page, + ` + + چیپ ۱ + چیپ ۲ + + `, + ); + + const item1 = page.getByTestId("test-chip-1"); + const item2 = page.getByTestId("test-chip-2"); + + await page.keyboard.press("Tab"); + await expect(item1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(item2).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(item1).toBeFocused(); + }); + + test("🧪 should work in single select mode", async ({ page }) => { + await render( + page, + ` + + چیپ ۱ + چیپ ۲ + چیپ ۳ + چیپ ۴ + + `, + ); + + const container = page.getByTestId("test-chip-group"); + const item1 = page.getByTestId("test-chip-1"); + const item2 = page.getByTestId("test-chip-2"); + const item3 = page.getByTestId("test-chip-3"); + const item4 = page.getByTestId("test-chip-4"); + + const mocks = await setupMocks(page); + const handleSelectChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + container, + "selectchange", + handleSelectChange.ref, + ); + + await handleSelectChange.matchResult({ called: false }); + + await item1.click(); + await handleSelectChange.matchResult({ callCount: 1 }); + await expect(item1).toHaveAttribute("selected"); + await forEachLocator([item2, item3, item4], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + + await item2.click(); + await handleSelectChange.matchResult({ callCount: 2 }); + await expect(item2).toHaveAttribute("selected"); + await forEachLocator([item1, item3, item4], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + + await item3.click(); + await handleSelectChange.matchResult({ callCount: 3 }); + await expect(item3).toHaveAttribute("selected"); + await forEachLocator([item1, item2, item4], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + + await item4.click(); + await handleSelectChange.matchResult({ callCount: 4 }); + await expect(item4).toHaveAttribute("selected"); + await forEachLocator([item1, item2, item3], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + }); + + test("🧪 should work in multiple select", async ({ page }) => { + await render( + page, + ` + + چیپ ۱ + چیپ ۲ + چیپ ۳ + چیپ ۴ + + `, + ); + + const item1 = page.getByTestId("test-chip-1"); + const item2 = page.getByTestId("test-chip-2"); + const item3 = page.getByTestId("test-chip-3"); + const item4 = page.getByTestId("test-chip-4"); + + await forEachLocator([item1, item2, item3, item4], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + + await item1.click(); + await expect(item1).toHaveAttribute("selected"); + await forEachLocator([item2, item3, item4], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + + await item2.click(); + await forEachLocator([item1, item2], async chip => { + await expect(chip).toHaveAttribute("selected"); + }); + await forEachLocator([item3, item4], async chip => { + await expect(chip).not.toHaveAttribute("selected"); + }); + + await item3.click(); + await forEachLocator([item1, item2, item3], async chip => { + await expect(chip).toHaveAttribute("selected"); + }); + await expect(item4).not.toHaveAttribute("selected"); + + await item4.click(); + await forEachLocator([item1, item2, item3, item4], async chip => { + await expect(chip).toHaveAttribute("selected"); + }); + }); + + test("🧪 should work with default value", async ({ page }) => { + await render( + page, + ` + + چیپ ۱ + چیپ ۲ + + `, + ); + + const item1 = page.getByTestId("test-chip-1"); + + await expect(item1).toHaveAttribute("selected"); + + await item1.click(); + await expect(item1).not.toHaveAttribute("selected"); + }); + + test("🧪 should work with multiple `cols` in `vertical` orientation", async ({ + page, + }) => { + // we have 6 chips (c1-c6) inside out chip group. + await render( + page, + ` + + c1 + c2 + c3 + c4 + c5 + c6 + + `, + ); + + const item1 = page.getByTestId("test-chip-1"); + const item2 = page.getByTestId("test-chip-2"); + const item3 = page.getByTestId("test-chip-3"); + const item4 = page.getByTestId("test-chip-4"); + const item5 = page.getByTestId("test-chip-5"); + const item6 = page.getByTestId("test-chip-6"); + + const item1BoundingBox = await (await item1.elementHandle())?.boundingBox(); + const item2BoundingBox = await (await item2.elementHandle())?.boundingBox(); + const item3BoundingBox = await (await item3.elementHandle())?.boundingBox(); + const item4BoundingBox = await (await item4.elementHandle())?.boundingBox(); + const item5BoundingBox = await (await item5.elementHandle())?.boundingBox(); + const item6BoundingBox = await (await item6.elementHandle())?.boundingBox(); + + const { x: item1X, y: item1Y } = item1BoundingBox!; + const { x: item2X, y: item2Y } = item2BoundingBox!; + const { x: item3X, y: item3Y } = item3BoundingBox!; + const { x: item4X, y: item4Y } = item4BoundingBox!; + const { x: item5X, y: item5Y } = item5BoundingBox!; + const { x: item6X, y: item6Y } = item6BoundingBox!; + + // We expect c1, c2 and c3 to be in first row... + expect(item1Y).toEqual(item2Y); + expect(item2Y).toEqual(item3Y); + + // ...and c4, c5, and c6 to be in the second row; + expect(item4Y).toEqual(item5Y); + expect(item5Y).toEqual(item6Y); + + // obviously the first row should be on top of the second one. + expect(item1Y).toBeLessThan(item4Y); + + // Now we need to check if the chips are correctly positions based in columns: + expect(item1X).toEqual(item4X); + expect(item2X).toEqual(item5X); + expect(item3X).toEqual(item6X); + }); +}); diff --git a/packages/web-components/src/chip-group/index.ts b/packages/web-components/src/chip-group/index.ts index 98e414be..e03889d7 100644 --- a/packages/web-components/src/chip-group/index.ts +++ b/packages/web-components/src/chip-group/index.ts @@ -15,7 +15,7 @@ export * from "./events.ts"; * * @fires {SelectChangeEvent} selectchange - Fired when the chip selection state changes. (bubbles). * - * @prop {'single' | 'multiple'} [select-mode='single'] - The select mode of the chip group. + * @prop {'single' | 'multiple'} [select-mode='multiple'] - The select mode of the chip group. * @prop {'horizontal' | 'vertical'} [orientation='horizontal'] - The orientation of the chip group. * @prop {string} [label=""] - * Defines a string value that can be used to set a label diff --git a/packages/web-components/src/chip/chip.test.ts b/packages/web-components/src/chip/chip.test.ts new file mode 100644 index 00000000..6fec3847 --- /dev/null +++ b/packages/web-components/src/chip/chip.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 chip", () => { + test("🧪 should toggle with keyboard interaction", async ({ page }) => { + await render( + page, + `چیپ`, + ); + const chip = page.getByTestId("test-chip"); + + await expect(chip).not.toHaveAttribute("selected"); + + await page.keyboard.press("Tab"); + await expect(chip).toBeFocused(); + + await page.keyboard.press("Space"); + await expect(chip).toHaveAttribute("selected"); + + await page.keyboard.press("Space"); + await expect(chip).not.toHaveAttribute("selected"); + }); + + test("🧪 should toggle on click", async ({ page }) => { + await render( + page, + `چیپ`, + ); + const chip = page.getByTestId("test-chip"); + + await expect(chip).not.toHaveAttribute("selected"); + + await chip.click(); + await expect(chip).toHaveAttribute("selected"); + + await chip.click(); + await expect(chip).not.toHaveAttribute("selected"); + }); + + test("🧪 should render leading and trailing icons", async ({ page }) => { + await render( + page, + ` + + + + + چیپ + + + +`, + ); + const leadingSlot = page.getByTestId("test-chip-leading-slot"); + const trailingSlot = page.getByTestId("test-chip-trailing-slot"); + + await expect(leadingSlot).toBeVisible(); + await expect(trailingSlot).toBeVisible(); + }); +}); diff --git a/packages/web-components/src/file-input/__test__/image.jpg b/packages/web-components/src/file-input/__test__/image.jpg new file mode 100644 index 00000000..be660e3e Binary files /dev/null and b/packages/web-components/src/file-input/__test__/image.jpg differ diff --git a/packages/web-components/src/file-input/constants.ts b/packages/web-components/src/file-input/constants.ts index 2fc7bcea..24a152d2 100644 --- a/packages/web-components/src/file-input/constants.ts +++ b/packages/web-components/src/file-input/constants.ts @@ -1,3 +1,20 @@ export const Slots = { PLACEHOLDER_ICON: "placeholder-icon", } as const; + +export const scope = "file-input"; + +export const ErrorMessages = { + SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE: [ + "Expected a valid `label` or `labelledby` attribute, received none.", + "If you want to hide the label, provide both `label` and `hide-label` attributes.", + ].join(" "), + INVALID_LOADING_VALUE: "the `loading` value should be between 0 and 100", + ERROR_AND_LOADING_ATTRIBUTES_AT_THE_SAME_TIME: + "The File input cannot have `error` and `loading` state at the same time.", + INVALID_VALUE: [ + "Failed to set the 'value' property on 'TapsiFileInput':", + "This input element accepts a filename, which may only be", + "programmatically set to the empty string.", + ].join(" "), +} as const; diff --git a/packages/web-components/src/file-input/file-input.test.ts b/packages/web-components/src/file-input/file-input.test.ts new file mode 100644 index 00000000..ee0ca0a3 --- /dev/null +++ b/packages/web-components/src/file-input/file-input.test.ts @@ -0,0 +1,361 @@ +import { + afterEach, + createPromiseResolvers, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; +import * as path from "path"; +import { ErrorMessages, scope } from "./constants.ts"; + +describe("🧩 file-input", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should hide label with `hide-label` attribute", async ({ page }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-file-input label"); + + await expect(label).toBeHidden(); + }); + + test("🧪 should throw error if no valid label was set for the input", async ({ + page, + }) => { + const msgResolver = createPromiseResolvers(); + + page.on("console", msg => { + if ( + msg.type() === "error" && + msg.text().includes(scope) && + msg + .text() + .includes(ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE) + ) { + msgResolver.resolve(msg.text()); + } + }); + + await render( + page, + ``, + ); + + const msg = await msgResolver.promise; + + expect(msg).toBeDefined(); + }); + + test("🧪 should show spinner when attribute `loading` was set to true", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const spinner = page.locator("tapsi-file-input tapsi-spinner"); + + await expect(spinner).toBeVisible(); + }); + + test("🧪 should throw error when both `loading` and `error` attributes are set", async ({ + page, + }) => { + const errors: string[] = []; + + page.on("console", msg => { + if (msg.type() === "error") { + errors.push(msg.text()); + } + }); + + await render( + page, + ``, + ); + + expect(errors[0]).toContain( + ErrorMessages.ERROR_AND_LOADING_ATTRIBUTES_AT_THE_SAME_TIME, + ); + }); + + test("🧪 should not be interactive when disabled", async ({ page }) => { + await render( + page, + ``, + ); + + const textField = page.getByTestId("test-file-input"); + + await expect(textField).not.toBeFocused(); + await textField.click(); + await expect(textField).not.toBeFocused(); + }); + + test("🧪 should show supporting text", async ({ page }) => { + await render( + page, + ``, + ); + + const supportingText = page.locator( + 'tapsi-file-input [part="supporting-text"]', + ); + + await expect(supportingText).toBeVisible(); + }); + + test("🧪 should open file input after clicking on internal label", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-file-input label"); + const fileChooserPromise = page.waitForEvent("filechooser"); + + await label.click(); + + await fileChooserPromise; + + // if we reach here, it means the file input has been opened! + expect(true).toBe(true); + }); + + test("🧪 should open file input after clicking on external label", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + + const label = page.getByTestId("external-label"); + + const fileChooserPromise = page.waitForEvent("filechooser"); + + await label.click(); + + await fileChooserPromise; + + // if we reach here, it means the file input has been opened! + expect(true).toBe(true); + }); + + test("🧪 should be able to choose a single file", async ({ page }) => { + await render( + page, + ``, + ); + + const fileInput = page.getByTestId("test-file-input"); + + // At first, no files are selected. + await expect(fileInput).toHaveJSProperty("files", null); + + const fileChooserPromise = page.waitForEvent("filechooser"); + + await fileInput.click(); + const fileChooser = await fileChooserPromise; + + // if we reach here, it means the file input has been opened! + await fileChooser.setFiles(path.resolve("src", "file-input", "index.ts")); + await expect(fileInput).not.toHaveJSProperty("files", null); + + // We should show to user the file name when it's not an image. + const previewSection = page.locator("tapsi-file-input .text"); + + await expect(previewSection).toHaveText("index.ts"); + }); + + test("🧪 should show image file preview after selection", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const fileInput = page.getByTestId("test-file-input"); + + // At first, no files are selected. + await expect(fileInput).toHaveJSProperty("files", null); + + const fileChooserPromise = page.waitForEvent("filechooser"); + + await fileInput.click(); + const fileChooser = await fileChooserPromise; + + // if we reach here, it means the file input has been opened! + await fileChooser.setFiles( + path.resolve("src", "file-input", "__test__", "image.jpg"), + ); + + // We should show to user the file name when it's not an image. + const previewSection = page.locator("tapsi-file-input img.preview"); + + await expect(previewSection).toBeVisible(); + }); + + test("🧪 should show clear button only when a file is selected", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const fileInput = page.getByTestId("test-file-input"); + const clearButton = page.locator('tapsi-file-input [label="clear"]'); + + // At first, no files are selected and no clear button is visible to user. + await expect(clearButton).toBeHidden(); + + const fileChooserPromise = page.waitForEvent("filechooser"); + + await fileInput.click(); + const fileChooser = await fileChooserPromise; + + await fileChooser.setFiles(path.resolve("src", "file-input", "index.ts")); + + // After selecting files, we expect the clear button shows when a file is chosen + await expect(clearButton).toBeVisible(); + + // Also after clicking on clear button, it should hide. + await clearButton.click(); + await expect(clearButton).toBeHidden(); + }); + + test("🧪 should be able to choose multiple files", async ({ page }) => { + await render( + page, + ``, + ); + + const fileInput = page.getByTestId("test-file-input"); + + const fileChooserPromise = page.waitForEvent("filechooser"); + + await fileInput.click(); + const fileChooser = await fileChooserPromise; + + expect(fileChooser.isMultiple()).toBe(true); + + await fileChooser.setFiles([ + path.resolve("src", "file-input", "index.ts"), + path.resolve("src", "file-input", "file-input.ts"), + ]); + + // We should show to user that 2 files are selected + const previewSection = page.locator("tapsi-file-input .text"); + + await expect(previewSection).toHaveText("۲ فایل انتخاب شده"); + }); + + test("🧪 should not show retry button without `retryable-error` attribute", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const fileInput = page.getByTestId("test-file-input"); + + const retryButton = page.locator("tapsi-file-input .error-action"); + + await expect(retryButton).toBeVisible(); + + const mocks = await setupMocks(page); + const handleRetry = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(fileInput, "retry", handleRetry.ref); + + await handleRetry.matchResult({ called: false }); + await retryButton.click(); + await handleRetry.matchResult({ callCount: 1 }); + }); + + test("🧪 should override placeholder text", async ({ page }) => { + await render( + page, + ``, + ); + + const placeholder = page.locator("tapsi-file-input .placeholder"); + + await expect(placeholder).toHaveText("my placeholder"); + }); + + test("🧪 should hide `placeholder` text in `loading` state", async ({ + page, + }) => { + // placeholder is visible when we are not in `loading` state + await render( + page, + ``, + ); + + const placeholder = page.locator("tapsi-file-input .placeholder"); + + await expect(placeholder).toBeVisible(); + + // ...but it's hidden in `loading` state + await render( + page, + ``, + ); + await expect(placeholder).toBeHidden(); + }); + + test("🧪 should hide `placeholder` text in `error` state", async ({ + page, + }) => { + // placeholder is visible when we are not in `loading` state + await render( + page, + ``, + ); + + const placeholder = page.locator("tapsi-file-input .placeholder"); + + await expect(placeholder).toBeHidden(); + }); + + test("🧪 should retry after error with `retryable-error` attribute", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const fileInput = page.getByTestId("test-file-input"); + + const retryButton = page.locator("tapsi-file-input .error-action"); + + await expect(retryButton).toBeVisible(); + + const mocks = await setupMocks(page); + const handleRetry = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(fileInput, "retry", handleRetry.ref); + + await handleRetry.matchResult({ called: false }); + await retryButton.click(); + await handleRetry.matchResult({ callCount: 1 }); + }); +}); diff --git a/packages/web-components/src/file-input/file-input.ts b/packages/web-components/src/file-input/file-input.ts index f720a0ca..c113fc0e 100644 --- a/packages/web-components/src/file-input/file-input.ts +++ b/packages/web-components/src/file-input/file-input.ts @@ -24,7 +24,7 @@ import { withFormAssociated, withOnReportValidity, } from "../utils/index.ts"; -import { Slots } from "./constants.ts"; +import { ErrorMessages, scope, Slots } from "./constants.ts"; import { RetryEvent } from "./events.ts"; import { clear, error, image } from "./icons.ts"; import { getProgressUiParams, isFileImage, isStringNumber } from "./utils.ts"; @@ -363,16 +363,16 @@ export class FileInput extends BaseClass { private _logErrors() { if (this._isLoading() && this.error) { logger( - "The File input cannot have `error` and `loading` state at the same time.", - "file-input", + ErrorMessages.ERROR_AND_LOADING_ATTRIBUTES_AT_THE_SAME_TIME, + scope, "error", ); } if (!this._hasValidLabel()) { logger( - "Expected a valid `label` or `labelledby` attribute, received none.", - "file-input", + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, + scope, "error", ); } @@ -381,11 +381,7 @@ export class FileInput extends BaseClass { const loading = parseInt(this.loading.toString()); if (!(0 <= loading && loading <= 100)) { - logger( - "the `loading` value should be between 0 and 100", - "file-input", - "error", - ); + logger(ErrorMessages.INVALID_LOADING_VALUE, scope, "error"); } } } @@ -403,15 +399,7 @@ export class FileInput extends BaseClass { @property() public set value(newValue: string) { if (newValue) { - logger( - [ - "Failed to set the 'value' property on 'TapsiFileInput':", - "This input element accepts a filename, which may only be", - "programmatically set to the empty string.", - ].join(" "), - "file-input", - "error", - ); + logger(ErrorMessages.INVALID_VALUE, scope, "error"); return; } @@ -488,7 +476,7 @@ export class FileInput extends BaseClass { if (isFileImage(file.name)) { return html`preview`; diff --git a/packages/web-components/src/modal/modal.test.ts b/packages/web-components/src/modal/modal.test.ts new file mode 100644 index 00000000..8dc43dae --- /dev/null +++ b/packages/web-components/src/modal/modal.test.ts @@ -0,0 +1,243 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 modal", () => { + test("🧪 should show elements based on props and slots", async ({ page }) => { + await render( + page, + ` + + image + + click + click + + + `, + ); + + const modalContainer = page.getByRole("alertdialog"); + const actionBarSlot = page.getByTestId("test-modal-actions-slot"); + const imageSlot = page.getByTestId("test-modal-image-slot"); + const title = page.locator("tapsi-modal [part='title']"); + const description = page.locator("tapsi-modal [part='description']"); + + await expect(modalContainer).toBeVisible(); + await expect(actionBarSlot).toBeVisible(); + await expect(imageSlot).toBeVisible(); + await expect(title).toHaveText("هدینگ"); + await expect(description).toHaveText("دسکریپشن"); + }); + + test("🧪 should trap focus inside modal", async ({ page }) => { + await render( + page, + ` + click + + + click + click + + + `, + ); + + const modalActionButton1 = page.getByTestId("test-modal-action-1"); + const modalActionButton2 = page.getByTestId("test-modal-action-2"); + + await page.keyboard.press("Tab"); + await expect(modalActionButton1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(modalActionButton2).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(modalActionButton1).toBeFocused(); + }); + + test("🧪 should have required attributes for screen readers", async ({ + page, + }) => { + await render( + page, + ` + + + click + click + + + `, + ); + + const modalContainer = page.getByRole("alertdialog"); + const modalOverlay = page.locator("tapsi-modal [part='overlay']"); + + await expect(modalOverlay).toHaveAttribute("aria-hidden", "true"); + await expect(modalContainer).toHaveAttribute("aria-modal", "true"); + + const titleElement = page.locator("tapsi-modal #title"); + const descriptionElement = page.locator("tapsi-modal #description"); + + await expect(titleElement).toHaveText("هدینگ"); + await expect(modalContainer).toHaveAttribute("aria-labelledby", "title"); + + await expect(descriptionElement).toHaveText("دسکریپشن"); + await expect(modalContainer).toHaveAttribute( + "aria-describedby", + "description", + ); + }); + + test("🧪 should close modal using Escape key", async ({ page }) => { + await render( + page, + ` + + + click + click + + + `, + ); + + const modalContainer = page.getByRole("alertdialog"); + + await expect(modalContainer).toBeVisible(); + + await page.keyboard.press("Escape"); + await expect(modalContainer).toBeHidden(); + }); + + test("🧪 should close modal by clicking on overlay", async ({ page }) => { + await render( + page, + ` + + + click + click + + + `, + ); + + const modalContainer = page.getByRole("alertdialog"); + const modalOverlay = page.locator("tapsi-modal [part='overlay']"); + + await expect(modalContainer).toBeVisible(); + + await modalOverlay.click(); + await expect(modalContainer).toBeHidden(); + }); + + test("🧪 should work with multiple modals", async ({ page }) => { + await render( + page, + ` + + + click + click + + + + + click + click + + + + + click + click + + + `, + ); + + const modal1 = page.getByTestId("test-modal-1").getByRole("alertdialog"); + + const modal2 = page.getByTestId("test-modal-2").getByRole("alertdialog"); + const modal2Action1 = page.getByTestId("test-modal-2-action-1"); + const modal2Action2 = page.getByTestId("test-modal-2-action-2"); + + const modal3 = page.getByTestId("test-modal-3").getByRole("alertdialog"); + const modal3Action1 = page.getByTestId("test-modal-3-action-1"); + const modal3Action2 = page.getByTestId("test-modal-3-action-2"); + + // Modal 3 should be on top + await expect(modal3).toBeVisible(); + await expect(modal3Action1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(modal3Action2).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(modal3Action1).toBeFocused(); + + await page.keyboard.press("Escape"); + await expect(modal3).toBeHidden(); + + // After closing modal 3, modal 2 should be on top + await expect(modal2).toBeVisible(); + + await page.keyboard.press("Shift+Tab"); + await expect(modal2Action1).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(modal2Action2).toBeFocused(); + + await page.keyboard.press("Tab"); + await expect(modal2Action1).toBeFocused(); + + await page.keyboard.press("Escape"); + await expect(modal2).toBeHidden(); + + // After closing modal 2, modal 1 should be on top + await expect(modal1).toBeVisible(); + await page.keyboard.press("Escape"); + await expect(modal1).toBeHidden(); + }); +}); diff --git a/packages/web-components/src/notice/notice.test.ts b/packages/web-components/src/notice/notice.test.ts new file mode 100644 index 00000000..28a8d9c7 --- /dev/null +++ b/packages/web-components/src/notice/notice.test.ts @@ -0,0 +1,180 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 notice", () => { + test("🧪 should be visible only if `visible` attribute was set", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const notice = page.getByTestId("test-notice"); + + await expect(notice).toBeVisible(); + + await render( + page, + ` + + `, + ); + await expect(notice).toBeHidden(); + }); + + test("🧪 should has correct screen reader properties based on attributes", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const root = page.locator("tapsi-notice [part='root']"); + + await expect(root).toHaveAttribute("role", "status"); + await expect(root).toHaveAttribute("aria-label", "هدینگ"); + await expect(root).toHaveAttribute("aria-hidden", "true"); + await expect(root).toHaveAttribute("aria-describedby", "توضیحات"); + await render( + page, + ` + + `, + ); + + await expect(root).toHaveAttribute("role", "alert"); + await expect(root).toHaveAttribute("aria-hidden", "false"); + }); + + test("🧪 should be dismissible only with `dismissible` attribute", async ({ + page, + }) => { + const dismissButton = page.locator("tapsi-notice [part='dismiss']"); + + await render( + page, + ` + + `, + ); + await expect(dismissButton).toBeVisible(); + + await render( + page, + ` + + `, + ); + await expect(dismissButton).toBeHidden(); + }); + + test("🧪 should show slots correctly (`action` and `artwork`)", async ({ + page, + }) => { + await render( + page, + ` + + + + + اکشن ۱ + اکشن ۲ + + `, + ); + const artwork = page.getByTestId("test-notice-artwork-slot"); + const action = page.getByTestId("test-notice-action-slot"); + + await expect(artwork).toBeVisible(); + await expect(action).toHaveCount(2); + }); + + test("🧪 should hide artwork if `artwork` attribute value was `none`", async ({ + page, + }) => { + await render( + page, + ` + + + + + + `, + ); + const artwork = page.getByTestId("test-notice-artwork-slot"); + + await expect(artwork).toBeHidden(); + }); + + test("🧪 should hide description and actions in `compact` variant", async ({ + page, + }) => { + await render( + page, + ` + + اکشن ۱ + + `, + ); + const description = page.locator("tapsi-notice [part='description']"); + const action = page.getByTestId("test-notice-action-slot"); + + await expect(description).toBeHidden(); + await expect(action).toBeHidden(); + }); +}); diff --git a/packages/web-components/src/pin-input/constants.ts b/packages/web-components/src/pin-input/constants.ts index eb6d195b..b76e442d 100644 --- a/packages/web-components/src/pin-input/constants.ts +++ b/packages/web-components/src/pin-input/constants.ts @@ -1 +1,8 @@ export const DEFAULT_DISPLAY_VALUE = (v: string) => v; + +export const ErrorMessages = { + SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE: [ + "Expected a valid `label` or `labelledby` attribute, received none.", + "If you want to hide the label, provide both `label` and `hide-label` attributes.", + ].join(" "), +} as const; diff --git a/packages/web-components/src/pin-input/pin-input.test.ts b/packages/web-components/src/pin-input/pin-input.test.ts new file mode 100644 index 00000000..8460fcc2 --- /dev/null +++ b/packages/web-components/src/pin-input/pin-input.test.ts @@ -0,0 +1,253 @@ +import { + describe, + expect, + forEachLocator, + render, + test, +} from "@internals/test-helpers"; +import { type Locator } from "@playwright/test"; +import { ErrorMessages } from "./constants.ts"; + +const validatePinValues = async (pins: Locator[], pattern: string[]) => { + if (pins.length !== pattern.length) { + throw new Error( + `The pattern is does not match to pins count. (Pins count: ${pins.length}, Pattern's length: ${pattern.length})`, + ); + } + + let isValid = true; + + await forEachLocator(pins, async (pin, index) => { + const pinExpectedValue = pattern[index]!; + const handle = await pin.elementHandle(); + const value = (await handle?.inputValue()) ?? ""; + + if (value !== pinExpectedValue) isValid = false; + }); + + return isValid; +}; + +describe("🧩 pin-input", () => { + test("🧪 should change pin counts using `pins` attribute", async ({ + page, + }) => { + for (let pinSize = 1; pinSize <= 10; pinSize++) { + await render( + page, + ``, + ); + + const pins = page.locator("tapsi-pin-input input"); + + await expect(pins).toHaveCount(pinSize); + } + }); + + test("🧪 should fill pin-input from middle while typing from the middle of the pin-input", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const pins = page.locator("tapsi-pin-input input"); + + await pins.nth(2).focus(); + + await page.keyboard.type("1234"); + + expect(await validatePinValues(await pins.all(), ["", "", "1", "2"])).toBe( + true, + ); + }); + + test("🧪 should change pin length using `pinLength` attribute", async ({ + page, + }) => { + const PINS = 3; + const CHAR = "0"; + + for (let pinLength = 1; pinLength <= 4; pinLength++) { + await render( + page, + ``, + ); + + const pinInput = page.getByTestId("test-pin-input"); + const pins = page.locator("tapsi-pin-input input"); + + await page.keyboard.press("Tab"); + await page.keyboard.type(CHAR.repeat(PINS * pinLength)); + await page.keyboard.press("Enter"); + + expect( + await validatePinValues( + await pins.all(), + Array.from({ length: PINS }).map(() => CHAR.repeat(pinLength)), + ), + ).toBe(true); + + await expect(pinInput).toHaveJSProperty( + "value", + CHAR.repeat(PINS * pinLength), + ); + } + }); + + test("🧪 should show supporting text", async ({ page }) => { + await render( + page, + ``, + ); + + const supportingText = page.locator( + 'tapsi-pin-input [part="supporting-text"]', + ); + + await expect(supportingText).toBeVisible(); + }); + + test("🧪 should be focused after clicking on internal label", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-pin-input label"); + const input = page.getByTestId("test-pin-input"); + + await expect(input).not.toBeFocused(); + await label.click(); + await expect(input).toBeFocused(); + }); + + test("🧪 should hide label with `hide-label` attribute", async ({ page }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-pin-input label"); + + await expect(label).toBeHidden(); + }); + + test("🧪 should throw error if no valid label was set for the input", async ({ + page, + }) => { + const errors: string[] = []; + + page.on("console", msg => { + if (msg.type() === "error") { + errors.push(msg.text()); + } + }); + + await render( + page, + ``, + ); + + expect(errors.length).toBeGreaterThan(0); + expect(errors[0]).toContain( + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, + ); + }); + + test("🧪 should be focused after clicking on external label", async ({ + page, + }) => { + await render( + page, + ` + + + `, + ); + + const label = page.getByTestId("external-label"); + const input = page.getByTestId("test-pin-input"); + + await expect(input).not.toBeFocused(); + await label.click(); + await expect(input).toBeFocused(); + }); + + test("🧪 should fill the pin-input from start on paste even when focus is not on first element", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const pins = page.locator("tapsi-pin-input input"); + + await page.evaluate(() => navigator.clipboard.writeText("123")); + + await pins.nth(2).focus(); + + await page.keyboard.press("ControlOrMeta+KeyV"); + + expect( + await validatePinValues(await pins.all(), ["1", "2", "3", "", ""]), + ).toBe(true); + + await render( + page, + ``, + ); + + await page.evaluate(() => navigator.clipboard.writeText("123456789")); + + await pins.nth(2).focus(); + + await page.keyboard.press("ControlOrMeta+KeyV"); + + expect( + await validatePinValues(await pins.all(), ["12", "34", "56", "78", "9"]), + ).toBe(true); + }); + + test("🧪 should ignore trailing of pasted string if the input string is larger that pin input capacity", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const pins = page.locator("tapsi-pin-input input"); + + await page.evaluate(() => navigator.clipboard.writeText("123456789")); + + await page.keyboard.press("Tab"); + + await page.keyboard.press("ControlOrMeta+KeyV"); + + expect( + await validatePinValues(await pins.all(), ["1", "2", "3", "4", "5"]), + ).toBe(true); + + await render( + page, + ``, + ); + + await page.evaluate(() => + navigator.clipboard.writeText("12345678901234567890"), + ); + + await page.keyboard.press("Tab"); + + await page.keyboard.press("ControlOrMeta+KeyV"); + + expect( + await validatePinValues(await pins.all(), ["12", "34", "56", "78", "90"]), + ).toBe(true); + }); +}); diff --git a/packages/web-components/src/pin-input/pin-input.ts b/packages/web-components/src/pin-input/pin-input.ts index 6181bb97..735b3f4f 100644 --- a/packages/web-components/src/pin-input/pin-input.ts +++ b/packages/web-components/src/pin-input/pin-input.ts @@ -25,7 +25,7 @@ import { withOnReportValidity, type Validator, } from "../utils/index.ts"; -import { DEFAULT_DISPLAY_VALUE } from "./constants.ts"; +import { DEFAULT_DISPLAY_VALUE, ErrorMessages } from "./constants.ts"; import { CompleteEvent } from "./events.ts"; import { isAlphaNumeric, isNumeric, stringConverter } from "./utils.ts"; import PinInputValidator from "./Validator.ts"; @@ -151,7 +151,7 @@ export class PinInput extends BaseClass { * The number of each input's length. * Defaults to 1. */ - @property({ type: Number }) + @property({ type: Number, attribute: "pin-length" }) public pinLength = 1; @state() @@ -658,7 +658,7 @@ export class PinInput extends BaseClass { if (!hasValidLabel) { logger( - "Expected a valid `label` or `labelledby` attribute, received none.", + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, "pin-input", "error", ); diff --git a/packages/web-components/src/pinwheel/pinwheel.test.ts b/packages/web-components/src/pinwheel/pinwheel.test.ts new file mode 100644 index 00000000..525d43a0 --- /dev/null +++ b/packages/web-components/src/pinwheel/pinwheel.test.ts @@ -0,0 +1,213 @@ +// import { +// afterEach, +// beforeEach, +// describe, +// disposeMocks, +// expect, +// render, +// setupMocks, +// test, +// } from "@internals/test-helpers"; +// import type { Locator } from "@playwright/test"; +// +// const testIds = { +// banner: "test-pinwheel", +// }; +// +// const expectPinwheelItems = (pinwheelItem: Locator[]) => { +// return { +// toBeSelected: async () => { +// for (const chip of pinwheelItem) { +// await expect(chip).toHaveAttribute("selected"); +// } +// }, +// notToBeSelected: async () => { +// for (const chip of pinwheelItem) { +// await expect(chip).not.toHaveAttribute("selected"); +// } +// }, +// }; +// }; +// +// describe("🧩 pinwheel", () => { +// beforeEach(async ({ page }) => { +// await page.goto("/"); +// }); +// +// afterEach(async ({ page }) => { +// await disposeMocks(page); +// }); +// +// test("🧪should be able to change selected item using click", async ({ +// page, +// }) => { +// await render( +// page, +// ` +// +// آیتم ۱ +// آیتم ۲ +// آیتم ۳ +// +// `, +// ); +// +// const pinwheel = page.getByTestId("test-pinwheel"); +// const item1 = page.getByTestId("test-pinwheel-item-1"); +// const item2 = page.getByTestId("test-pinwheel-item-2"); +// const item3 = page.getByTestId("test-pinwheel-item-3"); +// +// +// +// await expectPinwheelItems([item1]).toBeSelected(); +// await expectPinwheelItems([item2, item3]).notToBeSelected(); +// +// +// await item2.click(); +// await expectPinwheelItems([item2]).toBeSelected(); +// await expectPinwheelItems([item1, item3]).notToBeSelected(); +// }); +// +// test("🧪should be able to change selected item using up and down arrow keys", async ({ +// page, +// }) => { +// await render( +// page, +// ` +// +// آیتم ۱ +// آیتم ۲ +// آیتم ۳ +// +// `, +// ); +// +// const pinwheel = page.getByTestId("test-pinwheel"); +// const item1 = page.getByTestId("test-pinwheel-item-1"); +// const item2 = page.getByTestId("test-pinwheel-item-2"); +// const item3 = page.getByTestId("test-pinwheel-item-3"); +// +// +// +// await expectPinwheelItems([item1]).toBeSelected(); +// await expectPinwheelItems([item2, item3]).notToBeSelected(); +// +// await page.keyboard.press("Tab"); +// await expect(pinwheel).toBeFocused(); +// +// await page.keyboard.press("ArrowDown"); +// await expectPinwheelItems([item2]).toBeSelected(); +// await expectPinwheelItems([item1, item3]).notToBeSelected(); +// +// await page.keyboard.press("ArrowDown"); +// await expectPinwheelItems([item3]).toBeSelected(); +// await expectPinwheelItems([item1, item3]).notToBeSelected(); +// +// await page.keyboard.press("ArrowUp"); +// await expectPinwheelItems([item2]).toBeSelected(); +// await expectPinwheelItems([item1, item3]).notToBeSelected(); +// +// }); +// +// test("🧪 should trigger the `activechange` event by changing the active item using click", async ({ +// page, +// }) => { +// await render( +// page, +// ` +// +// آیتم ۱ +// آیتم ۲ +// آیتم ۳ +// آیتم ۴ +// +// `, +// ); +// +// const container = page.getByTestId("test-pinwheel"); +// const item1 = page.getByTestId("test-pinwheel-item-1"); +// const item2 = page.getByTestId("test-pinwheel-item-2"); +// const item3 = page.getByTestId("test-pinwheel-item-3"); +// const item4 = page.getByTestId("test-pinwheel-item-4"); +// +// const mocks = await setupMocks(page); +// const fn = mocks.createFakeFn(); +// +// await mocks.events.attachMockedEvent(container, "activechange", fn.ref); +// +// await fn.matchResult({ called: false }); +// +// await item1.click(); +// await fn.matchResult({ callCount: 1 }); +// await expectPinwheelItems([item1]).toBeSelected(); +// await expectPinwheelItems([item2, item3, item4]).notToBeSelected(); +// +// await item2.click(); +// await fn.matchResult({ callCount: 2 }); +// await expectPinwheelItems([item2]).toBeSelected(); +// await expectPinwheelItems([item1, item3, item4]).notToBeSelected(); +// +// await item3.click(); +// await fn.matchResult({ callCount: 3 }); +// await expectPinwheelItems([item3]).toBeSelected(); +// await expectPinwheelItems([item1, item2, item4]).notToBeSelected(); +// +// await item4.click(); +// await fn.matchResult({ callCount: 4 }); +// await expectPinwheelItems([item4]).toBeSelected(); +// await expectPinwheelItems([item1, item2, item3]).notToBeSelected(); +// }); +// +// test("🧪 should trigger the `activechange` event by changing the active item using keyboard navigation", async ({ +// page, +// }) => { +// await render( +// page, +// ` +// +// آیتم ۱ +// آیتم ۲ +// آیتم ۳ +// آیتم ۴ +// +// `, +// ); +// +// const container = page.getByTestId("test-pinwheel"); +// const item1 = page.getByTestId("test-pinwheel-item-1"); +// const item2 = page.getByTestId("test-pinwheel-item-2"); +// const item3 = page.getByTestId("test-pinwheel-item-3"); +// const item4 = page.getByTestId("test-pinwheel-item-4"); +// +// const mocks = await setupMocks(page); +// const fn = mocks.createFakeFn(); +// +// await mocks.events.attachMockedEvent(container, "activechange", fn.ref); +// +// await fn.matchResult({ called: false }); +// +// await page.keyboard.press("Tab"); +// await page.keyboard.press("Space"); +// await fn.matchResult({ callCount: 1 }); +// await expectPinwheelItems([item1]).toBeSelected(); +// await expectPinwheelItems([item2, item3, item4]).notToBeSelected(); +// +// await page.keyboard.press("Tab"); +// await page.keyboard.press("Space"); +// await fn.matchResult({ callCount: 2 }); +// await expectPinwheelItems([item2]).toBeSelected(); +// await expectPinwheelItems([item1, item3, item4]).notToBeSelected(); +// +// await page.keyboard.press("Tab"); +// await page.keyboard.press("Space"); +// await fn.matchResult({ callCount: 3 }); +// await expectPinwheelItems([item3]).toBeSelected(); +// await expectPinwheelItems([item1, item2, item4]).notToBeSelected(); +// +// await page.keyboard.press("Tab"); +// await page.keyboard.press("Space"); +// await fn.matchResult({ callCount: 4 }); +// await expectPinwheelItems([item4]).toBeSelected(); +// await expectPinwheelItems([item1, item2, item3]).notToBeSelected(); +// }); +// }); diff --git a/packages/web-components/src/progress-indicator/progress-indicator.test.ts b/packages/web-components/src/progress-indicator/progress-indicator.test.ts new file mode 100644 index 00000000..c02c4414 --- /dev/null +++ b/packages/web-components/src/progress-indicator/progress-indicator.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 progress-indicator", () => { + test("🧪 should show correct steps based on attributes", async ({ page }) => { + await render( + page, + ` + + `, + ); + + const steps = page.locator(`tapsi-progress-indicator [part="root"] .step`); + + await expect(steps).toHaveCount(5); + + for (let i = 0; i < (await steps.count()); i++) { + await expect(steps.nth(i)).toHaveClass(` step ${i < 2 ? "active " : ""}`); + } + }); + + test("🧪 should have required attributes for screen readers", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + + const root = page.locator(`tapsi-progress-indicator [part="root"]`); + + await expect(root).toHaveAttribute("aria-label", "my label"); + await expect(root).toHaveAttribute("role", "progressbar"); + await expect(root).toHaveAttribute("role", "progressbar"); + await expect(root).toHaveAttribute("aria-valuemin", "0"); + await expect(root).toHaveAttribute("aria-valuemax", "5"); + await expect(root).toHaveAttribute("aria-valuenow", "2"); + await expect(root).toHaveAttribute("aria-valuetext", "value text"); + }); +}); diff --git a/packages/web-components/src/radio/radio.test.ts b/packages/web-components/src/radio/radio.test.ts new file mode 100644 index 00000000..03ce1dc8 --- /dev/null +++ b/packages/web-components/src/radio/radio.test.ts @@ -0,0 +1,347 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; + +describe("🧩 radio", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `change` event on click", async ({ page }) => { + await render( + page, + ``, + ); + + const radio = page.getByTestId("test-radio"); + + await expect(radio).toBeVisible(); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(radio, "change", handleChange.ref); + + await handleChange.matchResult({ called: false }); + await expect(radio).toHaveJSProperty("checked", false); + + await radio.click(); + await handleChange.matchResult({ callCount: 1 }); + await expect(radio).toHaveJSProperty("checked", true); + }); + + test("🧪 should trigger `change` event using keyboard interaction", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const radio = page.getByTestId("test-radio"); + + await page.keyboard.press("Tab"); + + await expect(radio).toBeFocused(); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(radio, "change", handleChange.ref); + + await handleChange.matchResult({ called: false }); + await expect(radio).toHaveJSProperty("checked", false); + + await page.keyboard.press("Space"); + await handleChange.matchResult({ callCount: 1 }); + await expect(radio).toHaveJSProperty("checked", true); + }); + + test("🧪 should be interactive using keyboard interaction for multiple radios", async ({ + page, + }) => { + await page.setContent(` +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ `); + + const radio1 = page.getByTestId("test-radio-1"); + const radio2 = page.getByTestId("test-radio-2"); + const radio3 = page.getByTestId("test-radio-3"); + const radio4 = page.getByTestId("test-radio-4"); + + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await page.keyboard.press("Tab"); + await expect(radio1).toBeFocused(); + + await page.keyboard.press("Space"); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + // first we will check of the up and right arrow keys can toggle the radios + + await page.keyboard.press("ArrowDown"); + + // Since the second radio is disabled, we should not be able to focus on it. + await expect(radio2).not.toBeFocused(); + await expect(radio3).toBeFocused(); + + await page.keyboard.press("Space"); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", true); + await expect(radio4).toHaveJSProperty("checked", false); + + await page.keyboard.press("ArrowDown"); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", true); + + await page.keyboard.press("ArrowDown"); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await page.keyboard.press("ArrowUp"); + await page.keyboard.press("ArrowUp"); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", true); + await expect(radio4).toHaveJSProperty("checked", false); + + // the same behaviour should occur using left and right keys + await page.keyboard.press("ArrowRight"); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", true); + + await page.keyboard.press("ArrowRight"); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await page.keyboard.press("ArrowLeft"); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", true); + }); + + test("🧪 should be interactive using click on labels for multiple radios", async ({ + page, + }) => { + await page.setContent(` +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ `); + + const radio1 = page.getByTestId("test-radio-1"); + const radio2 = page.getByTestId("test-radio-2"); + const radio3 = page.getByTestId("test-radio-3"); + const radio4 = page.getByTestId("test-radio-4"); + + const radio1Label = page.getByTestId("test-radio-1-label"); + const radio2Label = page.getByTestId("test-radio-2-label"); + const radio3Label = page.getByTestId("test-radio-3-label"); + const radio4Label = page.getByTestId("test-radio-4-label"); + + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await radio1Label.click(); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + // Since the second radio is disabled, we should not be able to focus on it. + await radio2Label.click(); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await radio3Label.click(); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", true); + await expect(radio4).toHaveJSProperty("checked", false); + + await radio4Label.click(); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", true); + }); + + test("🧪 should be interactive using click for multiple radios", async ({ + page, + }) => { + await page.setContent(` +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ `); + + const radio1 = page.getByTestId("test-radio-1"); + const radio2 = page.getByTestId("test-radio-2"); + const radio3 = page.getByTestId("test-radio-3"); + const radio4 = page.getByTestId("test-radio-4"); + + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await radio1.click(); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + // Since the second radio is disabled, we should not be able to focus on it. + await radio2.click(); + await expect(radio1).toHaveJSProperty("checked", true); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", false); + + await radio3.click(); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", true); + await expect(radio4).toHaveJSProperty("checked", false); + + await radio4.click(); + await expect(radio1).toHaveJSProperty("checked", false); + await expect(radio2).toHaveJSProperty("checked", false); + await expect(radio3).toHaveJSProperty("checked", false); + await expect(radio4).toHaveJSProperty("checked", true); + }); + + test("🧪 should not trigger `change` event when disabled", async ({ + page, + }) => { + await render( + page, + `انتخاب کنید`, + ); + + const radio = page.getByTestId("test-radio"); + + await page.keyboard.press("Tab"); + + await expect(radio).not.toBeFocused(); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(radio, "change", handleChange.ref); + + await page.keyboard.press("Enter"); + await handleChange.matchResult({ called: false }); + + await radio.click(); + await handleChange.matchResult({ called: false }); + }); + + test("🧪 should be checked by default if `checked` attribute was set", async ({ + page, + }) => { + await render( + page, + `انتخاب کنید`, + ); + + const radio = page.getByTestId("test-radio"); + + await expect(radio).toHaveJSProperty("checked", true); + }); + + test("🧪 should be focused after clicking on external label", async ({ + page, + }) => { + await render( + page, + ` + + + `, + ); + + const label = page.getByTestId("external-label"); + const input = page.getByTestId("test-radio"); + + await expect(input).not.toBeFocused(); + await label.click(); + await expect(input).toBeFocused(); + }); +}); diff --git a/packages/web-components/src/rate-slider/rate-slider.test.ts b/packages/web-components/src/rate-slider/rate-slider.test.ts new file mode 100644 index 00000000..433dc438 --- /dev/null +++ b/packages/web-components/src/rate-slider/rate-slider.test.ts @@ -0,0 +1,417 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; +import { type Locator } from "@playwright/test"; +import { type TapsiRateSlider } from "@tapsioss/web-components/rate-slider/index"; + +describe("🧩 rate-slider", () => { + test("🧪 should has required attributes for screen readers", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const root = page.getByRole("slider"); + + await expect(root).toHaveAttribute("aria-label", "my label"); + await expect(root).toHaveAttribute("aria-valuemin", "5"); + await expect(root).toHaveAttribute("aria-valuemax", "10"); + await expect(root).toHaveAttribute("aria-valuenow", "7"); + await expect(root).toHaveAttribute("aria-valuetext", "value text"); + }); + + test("🧪 should not in `disabled` mode", async ({ page }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + await rateSlider.click(); + await expect(rateSlider).toHaveJSProperty("value", "7"); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Home"); + await expect(rateSlider).toHaveJSProperty("value", "7"); + }); + + test("🧪 should work with default value", async ({ page }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + await expect(rateSlider).toHaveJSProperty("value", "7"); + }); + + test("🧪 should set value to min or max if default value is out of range", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + await expect(rateSlider).toHaveJSProperty("value", "10"); + + await render( + page, + ` + + `, + ); + + await expect(rateSlider).toHaveJSProperty("value", "-5"); + }); + + test("🧪 should work using click", async ({ page }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + const stops: Locator[] = await page + .locator("tapsi-rate-slider .stop") + .all(); + + const stop0 = stops[0]!; + const stop1 = stops[1]!; + const stop2 = stops[2]!; + const stop3 = stops[3]!; + const stop4 = stops[4]!; + const stop5 = stops[5]!; + const stop6 = stops[6]!; + const stop7 = stops[7]!; + const stop8 = stops[8]!; + const stop9 = stops[9]!; + const stop10 = stops[10]!; + + await stop10.click(); + await expect(rateSlider).toHaveJSProperty("value", "10"); + + await stop2.click(); + await expect(rateSlider).toHaveJSProperty("value", "2"); + + await stop3.click(); + await expect(rateSlider).toHaveJSProperty("value", "3"); + + await stop1.click(); + await expect(rateSlider).toHaveJSProperty("value", "1"); + + await stop9.click(); + await expect(rateSlider).toHaveJSProperty("value", "9"); + + await stop5.click(); + await expect(rateSlider).toHaveJSProperty("value", "5"); + + await stop4.click(); + await expect(rateSlider).toHaveJSProperty("value", "4"); + + await stop8.click(); + await expect(rateSlider).toHaveJSProperty("value", "8"); + + await stop7.click(); + await expect(rateSlider).toHaveJSProperty("value", "7"); + + await stop6.click(); + await expect(rateSlider).toHaveJSProperty("value", "6"); + + await stop0.click(); + await expect(rateSlider).toHaveJSProperty("value", "0"); + }); + + test("🧪 should work using swipe", async ({ page }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + const stops: Locator[] = await page + .locator("tapsi-rate-slider .stop") + .all(); + + const stop0 = stops[0]!; + const stop1 = stops[1]!; + const stop2 = stops[2]!; + const stop3 = stops[3]!; + const stop4 = stops[4]!; + const stop5 = stops[5]!; + const stop6 = stops[6]!; + const stop7 = stops[7]!; + const stop8 = stops[8]!; + const stop9 = stops[9]!; + const stop10 = stops[10]!; + + await stop0.dragTo(stop10); + await expect(rateSlider).toHaveJSProperty("value", "10"); + + await stop10.dragTo(stop2); + await expect(rateSlider).toHaveJSProperty("value", "2"); + + await stop2.dragTo(stop3); + await expect(rateSlider).toHaveJSProperty("value", "3"); + + await stop3.dragTo(stop1); + await expect(rateSlider).toHaveJSProperty("value", "1"); + + await stop1.dragTo(stop9); + await expect(rateSlider).toHaveJSProperty("value", "9"); + + await stop9.dragTo(stop5); + await expect(rateSlider).toHaveJSProperty("value", "5"); + + await stop5.dragTo(stop4); + await expect(rateSlider).toHaveJSProperty("value", "4"); + + await stop4.dragTo(stop8); + await expect(rateSlider).toHaveJSProperty("value", "8"); + + await stop8.dragTo(stop7); + await expect(rateSlider).toHaveJSProperty("value", "7"); + + await stop7.dragTo(stop6); + await expect(rateSlider).toHaveJSProperty("value", "6"); + + await stop6.dragTo(stop1); + await expect(rateSlider).toHaveJSProperty("value", "1"); + }); + + test("🧪 should set value to max or min while dragging slider outside of the rate-slider", async ({ + page, + }) => { + await render( + page, + ` +
+ outside-right + + outside-left +
+ `, + ); + const leftSideOfTheComponent = page.getByTestId("outside-left"); + const rateSlider = page.getByTestId("test-rate-slider"); + + const stop5 = page.locator("tapsi-rate-slider .stop").nth(5); + + await stop5.dragTo(leftSideOfTheComponent); + await expect(rateSlider).toHaveJSProperty("value", "0"); + }); + + test("🧪 should work using keyboard interaction", async ({ page }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + await expect(rateSlider).toHaveJSProperty("value", "5"); + + await page.keyboard.press("Tab"); + + // Test left and right arrow keys + + await page.keyboard.press("ArrowLeft"); + await expect(rateSlider).toHaveJSProperty("value", "4"); + await page.keyboard.press("ArrowLeft"); + await expect(rateSlider).toHaveJSProperty("value", "3"); + await page.keyboard.press("ArrowLeft"); + await expect(rateSlider).toHaveJSProperty("value", "2"); + await page.keyboard.press("ArrowLeft"); + await expect(rateSlider).toHaveJSProperty("value", "1"); + // should not decrease value if the current value is min + await page.keyboard.press("ArrowLeft"); + await expect(rateSlider).toHaveJSProperty("value", "1"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "2"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "3"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "4"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "5"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "6"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "7"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "8"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "9"); + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "10"); + // should not increase value if the current value is max + await page.keyboard.press("ArrowRight"); + await expect(rateSlider).toHaveJSProperty("value", "10"); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowLeft"); + await expect(rateSlider).toHaveJSProperty("value", "5"); + + // Test up and down arrow keys + await page.keyboard.press("ArrowDown"); + await expect(rateSlider).toHaveJSProperty("value", "4"); + await page.keyboard.press("ArrowDown"); + await expect(rateSlider).toHaveJSProperty("value", "3"); + await page.keyboard.press("ArrowDown"); + await expect(rateSlider).toHaveJSProperty("value", "2"); + await page.keyboard.press("ArrowDown"); + await expect(rateSlider).toHaveJSProperty("value", "1"); + // should not decrease value if the current value is min + await page.keyboard.press("ArrowDown"); + await expect(rateSlider).toHaveJSProperty("value", "1"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "2"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "3"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "4"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "5"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "6"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "7"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "8"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "9"); + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "10"); + // should not increase value if the current value is max + await page.keyboard.press("ArrowUp"); + await expect(rateSlider).toHaveJSProperty("value", "10"); + await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown"); + await page.keyboard.press("ArrowDown"); + await expect(rateSlider).toHaveJSProperty("value", "5"); + + // Test home and end keys + await page.keyboard.press("Home"); + await expect(rateSlider).toHaveJSProperty("value", "1"); + + await page.keyboard.press("End"); + await expect(rateSlider).toHaveJSProperty("value", "10"); + }); + + test("🧪 should work using public `stepUp` and `stepDown` methods", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const rateSlider = page.getByTestId("test-rate-slider"); + + await expect(rateSlider).toHaveJSProperty("value", "5"); + + await page.evaluate(() => { + (document.getElementById("test-rate-slider") as TapsiRateSlider).stepUp(); + }); + + await expect(rateSlider).toHaveJSProperty("value", "6"); + + await page.evaluate(() => { + const slider = document.getElementById( + "test-rate-slider", + ) as TapsiRateSlider; + + slider.stepDown(); + slider.stepDown(); + }); + + await expect(rateSlider).toHaveJSProperty("value", "4"); + }); +}); diff --git a/packages/web-components/src/segmented-view/segmented-view.test.ts b/packages/web-components/src/segmented-view/segmented-view.test.ts new file mode 100644 index 00000000..4dce5625 --- /dev/null +++ b/packages/web-components/src/segmented-view/segmented-view.test.ts @@ -0,0 +1,152 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; + +describe("🧩 segmented-view", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should change active item and trigger events (`activate` and `activechange`) using click", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + + `, + ); + + const container = page.getByTestId("test-segmented-view"); + const item1 = page.getByTestId("test-segmented-view-item-1"); + const item2 = page.getByTestId("test-segmented-view-item-2"); + + const mocks = await setupMocks(page); + const handleActivate1 = mocks.createFakeFn(); + const handleActivate2 = mocks.createFakeFn(); + const handleActiveChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + item1, + "activate", + handleActivate1.ref, + ); + await mocks.events.attachMockedEvent( + item2, + "activate", + handleActivate2.ref, + ); + await mocks.events.attachMockedEvent( + container, + "activechange", + handleActiveChange.ref, + ); + + await handleActiveChange.matchResult({ called: false }); + + await item1.click(); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 0 }); + await handleActiveChange.matchResult({ callCount: 1 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await item2.click(); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 1 }); + await handleActiveChange.matchResult({ callCount: 2 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + + await item1.click(); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 1 }); + await handleActiveChange.matchResult({ callCount: 3 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await item2.click(); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 2 }); + await handleActiveChange.matchResult({ callCount: 4 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + }); + + test("🧪 should change active item and trigger events (`activate` and `activechange`) using keyboard navigation", async ({ + page, + }) => { + await render( + page, + ` + + آیتم ۱ + آیتم ۲ + + `, + ); + + const container = page.getByTestId("test-segmented-view"); + const item1 = page.getByTestId("test-segmented-view-item-1"); + const item2 = page.getByTestId("test-segmented-view-item-2"); + + const mocks = await setupMocks(page); + const handleActivate1 = mocks.createFakeFn(); + const handleActivate2 = mocks.createFakeFn(); + const handleActiveChange = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent( + item1, + "activate", + handleActivate1.ref, + ); + await mocks.events.attachMockedEvent( + item2, + "activate", + handleActivate2.ref, + ); + await mocks.events.attachMockedEvent( + container, + "activechange", + handleActiveChange.ref, + ); + + await handleActiveChange.matchResult({ called: false }); + + await page.keyboard.press("Tab"); + await expect(item1).toBeFocused(); + await page.keyboard.press("Space"); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 0 }); + await handleActiveChange.matchResult({ callCount: 1 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + + await page.keyboard.press("ArrowLeft"); + await expect(item2).toBeFocused(); + await page.keyboard.press("Space"); + await handleActivate1.matchResult({ callCount: 1 }); + await handleActivate2.matchResult({ callCount: 1 }); + await handleActiveChange.matchResult({ callCount: 2 }); + await expect(item2).toHaveJSProperty("active", true); + await expect(item1).toHaveJSProperty("active", false); + + await page.keyboard.press("ArrowRight"); + await expect(item1).toBeFocused(); + await page.keyboard.press("Space"); + await handleActivate1.matchResult({ callCount: 2 }); + await handleActivate2.matchResult({ callCount: 1 }); + await handleActiveChange.matchResult({ callCount: 3 }); + await expect(item1).toHaveJSProperty("active", true); + await expect(item2).toHaveJSProperty("active", false); + }); +}); diff --git a/packages/web-components/src/skeleton/constants.ts b/packages/web-components/src/skeleton/constants.ts index 2913c1da..1cbea684 100644 --- a/packages/web-components/src/skeleton/constants.ts +++ b/packages/web-components/src/skeleton/constants.ts @@ -1,3 +1,8 @@ export const Slots = { DEFAULT: "", } as const; + +export const ErrorMessages = { + SET_RATIO_ONLY_IN_RECTANGULAR_VARIANT: + 'You can only use `ratio` when `variant="rectangular"`.', +} as const; diff --git a/packages/web-components/src/skeleton/skeleton.test.ts b/packages/web-components/src/skeleton/skeleton.test.ts new file mode 100644 index 00000000..d8b67e7f --- /dev/null +++ b/packages/web-components/src/skeleton/skeleton.test.ts @@ -0,0 +1,143 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; +import { ErrorMessages } from "./constants.ts"; + +describe("🧩 skeleton", () => { + test("🧪 should fit to content in `text` variant", async ({ page }) => { + await render( + page, + ` + +
+
+ `, + ); + const skeleton = page.getByTestId("test-skeleton"); + const box = await (await skeleton.elementHandle())?.boundingBox(); + + const { width, height } = box!; + + expect(width).toBe(200); + expect(height).toBe(400); + }); + + test("🧪 should apply correct class to root based on variant", async ({ + page, + }) => { + const variants = ["rectangular", "circular", "pill", "text"]; + + for (const variant of variants) { + await render( + page, + ``, + ); + + const root = page.locator('tapsi-skeleton [part="root"]'); + + await expect(root).toHaveClass(` root ${variant} `); + } + }); + + test("🧪 should get size from `width` and `height` attributes in all variants expect `text`", async ({ + page, + }) => { + const allowedVariants = ["rectangular", "circular", "pill"]; + const skeleton = page.getByTestId("test-skeleton"); + + for (const variant of allowedVariants) { + await render( + page, + ` + + `, + ); + + const boundingBox = await (await skeleton.elementHandle())?.boundingBox(); + + const { width, height } = boundingBox!; + + expect(width).toBe(200); + expect(height).toBe(300); + } + + // it should not be the same for `text` variant + + await render( + page, + ` + + `, + ); + + const boundingBox = await (await skeleton.elementHandle())?.boundingBox(); + + const { width, height } = boundingBox!; + + expect(width).not.toBe(200); + expect(height).not.toBe(300); + }); + + test("🧪 should set the ratio of width to the height based on `ratio` attribute in `rectangular` variant", async ({ + page, + }) => { + const width = 300; + const inputRatioValues = [0.5, 1, 2, 5]; + const skeleton = page.getByTestId("test-skeleton"); + + for (const ratio of inputRatioValues) { + await render( + page, + ` + + `, + ); + + const boundingBox = await (await skeleton.elementHandle())?.boundingBox(); + + const { height } = boundingBox!; + + expect(height).toBe(width / ratio); + } + + const errors: string[] = []; + + page.on("console", msg => { + if (msg.type() === "error") { + errors.push(msg.text()); + } + }); + + await render( + page, + ` + + `, + ); + + expect(errors[0]).toContain( + ErrorMessages.SET_RATIO_ONLY_IN_RECTANGULAR_VARIANT, + ); + }); +}); diff --git a/packages/web-components/src/skeleton/skeleton.ts b/packages/web-components/src/skeleton/skeleton.ts index ec1194f2..91f714ac 100644 --- a/packages/web-components/src/skeleton/skeleton.ts +++ b/packages/web-components/src/skeleton/skeleton.ts @@ -3,6 +3,7 @@ import { property } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; import { styleMap, type StyleInfo } from "lit/directives/style-map.js"; import { logger } from "../utils/index.ts"; +import { ErrorMessages } from "./constants.ts"; export class Skeleton extends LitElement { /** @@ -45,7 +46,7 @@ export class Skeleton extends LitElement { styleInfo.paddingTop = `${100 / ratio}%`; } else if (this.ratio && this.variant !== "rectangular") { logger( - 'You can only use `ratio` when `variant="rectangular"`.', + ErrorMessages.SET_RATIO_ONLY_IN_RECTANGULAR_VARIANT, "skeleton", "error", ); diff --git a/packages/web-components/src/spinner/spinner.test.ts b/packages/web-components/src/spinner/spinner.test.ts new file mode 100644 index 00000000..e07a3c69 --- /dev/null +++ b/packages/web-components/src/spinner/spinner.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 spinner", () => { + test("🧪 should be hidden for screen readers", async ({ page }) => { + await render( + page, + ` + + `, + ); + const root = page.locator("tapsi-spinner svg"); + + await expect(root).toHaveAttribute("aria-hidden", "true"); + }); + + test("🧪 should get absolute size from `size` attribute", async ({ + page, + }) => { + const size = 200; + + await render( + page, + ` + + `, + ); + const spinner = page.getByTestId("test-spinner"); + + const boundingBox = await (await spinner.elementHandle())?.boundingBox(); + const { width, height } = boundingBox!; + + expect(width).toBe(size); + expect(height).toBe(size); + }); + + test("🧪 should get size of the parent element if the size was `auto`", async ({ + page, + }) => { + const size = 200; + + await render( + page, + ` +
+ +
+ `, + ); + const spinner = page.getByTestId("test-spinner"); + + const boundingBox = await (await spinner.elementHandle())?.boundingBox(); + const { width, height } = boundingBox!; + + expect(width).toBe(size); + expect(height).toBe(size); + }); +}); diff --git a/packages/web-components/src/stepper/stepper.test.ts b/packages/web-components/src/stepper/stepper.test.ts new file mode 100644 index 00000000..6f27f835 --- /dev/null +++ b/packages/web-components/src/stepper/stepper.test.ts @@ -0,0 +1,322 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; +import { type Page } from "@playwright/test"; +import { type TapsiStepper } from "@tapsioss/web-components/stepper"; + +const getStepperElements = (page: Page) => { + const stepper = page.getByTestId("test-stepper"); + const root = page.getByRole("spinbutton"); + const decreaseButton = page.getByLabel("Decrease value"); + const increaseButton = page.getByLabel("Increase value"); + + return { + root, + stepper, + decreaseButton, + increaseButton, + }; +}; + +describe("🧩 stepper", () => { + test("🧪 should has required attributes for screen readers", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const { root } = getStepperElements(page); + + await expect(root).toHaveAttribute("aria-label", "my label"); + await expect(root).toHaveAttribute("aria-valuemin", "5"); + await expect(root).toHaveAttribute("aria-valuemax", "10"); + await expect(root).toHaveAttribute("aria-valuenow", "7"); + await expect(root).toHaveAttribute("aria-valuetext", "value text"); + }); + + test("🧪 should not in `disabled` mode", async ({ page }) => { + await render( + page, + ` + + `, + ); + const { stepper, decreaseButton, increaseButton } = + getStepperElements(page); + + await expect(decreaseButton).toBeDisabled(); + // eslint-disable-next-line playwright/no-force-option + await decreaseButton.click({ force: true }); + await expect(stepper).toHaveJSProperty("value", "7"); + + await expect(increaseButton).toBeDisabled(); + // eslint-disable-next-line playwright/no-force-option + await increaseButton.click({ force: true }); + await expect(stepper).toHaveJSProperty("value", "7"); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Home"); + await expect(stepper).toHaveJSProperty("value", "7"); + }); + + test("🧪 should work with default value", async ({ page }) => { + await render( + page, + ` + + `, + ); + const { stepper } = getStepperElements(page); + + await expect(stepper).toHaveJSProperty("value", "7"); + }); + + test("🧪 should set value to min/max if default value is out of range", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const { stepper } = getStepperElements(page); + + await expect(stepper).toHaveJSProperty("value", "10"); + + await render( + page, + ` + + `, + ); + + await expect(stepper).toHaveJSProperty("value", "-5"); + }); + + test("🧪 should work using keyboard interaction", async ({ page }) => { + await render( + page, + ` + + `, + ); + const { stepper } = getStepperElements(page); + + await expect(stepper).toHaveJSProperty("value", "0"); + + await page.keyboard.press("Tab"); + + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-1"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-2"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-3"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-4"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-5"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-6"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-7"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-8"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-9"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-10"); + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "-10"); + + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-9"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-8"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-7"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-6"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-5"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-4"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-3"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-2"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "-1"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "0"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "1"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "2"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "3"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "4"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "5"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "6"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "7"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "8"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "9"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "10"); + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "10"); + + await page.keyboard.press("Home"); + await expect(stepper).toHaveJSProperty("value", "-10"); + await page.keyboard.press("End"); + await expect(stepper).toHaveJSProperty("value", "10"); + }); + + test("🧪 should work using public `stepUp` and `stepDown` methods", async ({ + page, + }) => { + await render( + page, + ` + + `, + ); + const { stepper } = getStepperElements(page); + + await expect(stepper).toHaveJSProperty("value", "5"); + + await page.evaluate(() => { + (document.getElementById("test-stepper") as TapsiStepper).stepUp(); + }); + + await expect(stepper).toHaveJSProperty("value", "6"); + + await page.evaluate(() => { + const slider = document.getElementById("test-stepper") as TapsiStepper; + + slider.stepDown(); + slider.stepDown(); + }); + + await expect(stepper).toHaveJSProperty("value", "4"); + }); + + test("🧪 should work with custom step", async ({ page }) => { + await render( + page, + ` + + `, + ); + const { stepper } = getStepperElements(page); + + await expect(stepper).toHaveJSProperty("value", "0"); + + await page.keyboard.press("Tab"); + + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "0"); + + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "5"); + + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "10"); + + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "15"); + + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "10"); + + await page.keyboard.press("End"); + await expect(stepper).toHaveJSProperty("value", "100"); + + await page.keyboard.press("ArrowUp"); + await expect(stepper).toHaveJSProperty("value", "100"); + + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "95"); + + await page.keyboard.press("ArrowDown"); + await expect(stepper).toHaveJSProperty("value", "90"); + + await page.keyboard.press("Home"); + await expect(stepper).toHaveJSProperty("value", "0"); + + await page.evaluate(() => { + const stepper = document.getElementById("test-stepper") as TapsiStepper; + + stepper.stepUp(); + stepper.stepUp(); + }); + await expect(stepper).toHaveJSProperty("value", "10"); + }); +}); diff --git a/packages/web-components/src/switch/constants.ts b/packages/web-components/src/switch/constants.ts new file mode 100644 index 00000000..b7b30e77 --- /dev/null +++ b/packages/web-components/src/switch/constants.ts @@ -0,0 +1,4 @@ +export const ErrorMessages = { + SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE: + "Expected a valid `label` or `labelledby` attribute, received none.", +} as const; diff --git a/packages/web-components/src/switch/switch.test.ts b/packages/web-components/src/switch/switch.test.ts new file mode 100644 index 00000000..fb580e8b --- /dev/null +++ b/packages/web-components/src/switch/switch.test.ts @@ -0,0 +1,110 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; +import { ErrorMessages } from "./constants.ts"; + +describe("🧩 switch", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should get default value", async ({ page }) => { + await render( + page, + '', + ); + + const switchEl = page.getByTestId("test-switch"); + + await expect(switchEl).toHaveJSProperty("selected", true); + }); + + test("🧪 should trigger `change` and `input` event on toggling the switch", async ({ + page, + }) => { + await render( + page, + '', + ); + + const switchEl = page.getByTestId("test-switch"); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + const handleInput = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(switchEl, "change", handleChange.ref); + await mocks.events.attachMockedEvent(switchEl, "input", handleInput.ref); + + await expect(switchEl).toHaveJSProperty("selected", false); + + await handleChange.matchResult({ called: false }); + await handleInput.matchResult({ called: false }); + + await switchEl.click(); + await handleChange.matchResult({ callCount: 1 }); + await handleInput.matchResult({ callCount: 1 }); + await expect(switchEl).toHaveJSProperty("selected", true); + + await page.keyboard.press("Space"); + await handleChange.matchResult({ callCount: 2 }); + await handleInput.matchResult({ callCount: 2 }); + await expect(switchEl).toHaveJSProperty("selected", false); + + await page.keyboard.press("Enter"); + await handleChange.matchResult({ callCount: 3 }); + await handleInput.matchResult({ callCount: 3 }); + await expect(switchEl).toHaveJSProperty("selected", true); + }); + + test("🧪 should hide label with `hide-label` attribute", async ({ page }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-switch label"); + + await expect(label).toBeHidden(); + }); + + test("🧪 should throw error if no valid label was set for the input", async ({ + page, + }) => { + const errors: string[] = []; + + page.on("console", msg => { + if (msg.type() === "error") { + errors.push(msg.text()); + } + }); + + await render( + page, + ``, + ); + + expect(errors[0]).toContain( + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, + ); + }); + + test("🧪 should not be interactive when disabled", async ({ page }) => { + await render( + page, + ``, + ); + + const textField = page.getByTestId("test-switch"); + + await expect(textField).not.toBeFocused(); + await textField.click(); + await expect(textField).not.toBeFocused(); + }); +}); diff --git a/packages/web-components/src/switch/switch.ts b/packages/web-components/src/switch/switch.ts index c88b6b81..e1c69276 100644 --- a/packages/web-components/src/switch/switch.ts +++ b/packages/web-components/src/switch/switch.ts @@ -13,6 +13,7 @@ import { redispatchEvent, waitAMicrotask, } from "../utils/index.ts"; +import { ErrorMessages } from "./constants.ts"; export class Switch extends BaseInput { /** @@ -135,7 +136,7 @@ export class Switch extends BaseInput { protected override renderControl() { if (!this.hasValidLabel()) { logger( - "Expected a valid `label` or `labelledby` attribute, received none.", + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, "switch", "error", ); diff --git a/packages/web-components/src/text-area/text-area.ts b/packages/web-components/src/text-area/text-area.ts index 699ba583..722a07d8 100644 --- a/packages/web-components/src/text-area/text-area.ts +++ b/packages/web-components/src/text-area/text-area.ts @@ -46,7 +46,7 @@ export class TextArea extends BaseTextInput { logger( [ "Expected a valid `label` or `labelledby` attribute, received none.", - "If you want to hide the label, provide both `label` and `hidelabel` attributes.", + "If you want to hide the label, provide both `label` and `hide-label` attributes.", ].join(" "), "text-area", "error", diff --git a/packages/web-components/src/text-field/text-field.test.ts b/packages/web-components/src/text-field/text-field.test.ts new file mode 100644 index 00000000..5858df66 --- /dev/null +++ b/packages/web-components/src/text-field/text-field.test.ts @@ -0,0 +1,222 @@ +import { + afterEach, + describe, + disposeMocks, + expect, + render, + setupMocks, + test, +} from "@internals/test-helpers"; +import { type TapsiTextField } from "@tapsioss/web-components/text-field/index"; +import { ErrorMessages } from "../base-text-input/constants.ts"; + +describe("🧩 text-field", () => { + afterEach(async ({ page }) => { + await disposeMocks(page); + }); + + test("🧪 should trigger `input` and `change` events while typing", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const textField = page.getByTestId("test-text-field"); + + const mocks = await setupMocks(page); + const handleChange = mocks.createFakeFn(); + const handleInput = mocks.createFakeFn(); + + await mocks.events.attachMockedEvent(textField, "change", handleChange.ref); + await mocks.events.attachMockedEvent(textField, "input", handleInput.ref); + + await textField.click(); + await expect(textField).toBeFocused(); + + // We want ot type "test" in text field, but we type the last letter wrong. (4 input event triggers) + await page.keyboard.type("tesy"); + await handleChange.matchResult({ callCount: 0 }); + await handleInput.matchResult({ callCount: 4 }); + + // Now we should fix the last letter! so we press backspace and type "t" (2 input event triggers) + await page.keyboard.press("Backspace"); + await handleInput.matchResult({ callCount: 5 }); + + await page.keyboard.type("t"); + await handleInput.matchResult({ callCount: 6 }); + + await page.keyboard.press("Enter"); + await handleChange.matchResult({ callCount: 1 }); + await handleInput.matchResult({ callCount: 6 }); + await expect(textField).toHaveJSProperty("value", "test"); + + // Now we want to replace the text to another one. We will select all the text and type another string, for + // example: hello. this should trigger 5 more input events + + await page.keyboard.press("Meta+A"); + await page.keyboard.type("hello"); + await page.keyboard.press("Enter"); + await handleChange.matchResult({ callCount: 2 }); + await handleInput.matchResult({ callCount: 11 }); + }); + + test("🧪 should hide label with `hide-label` attribute", async ({ page }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-text-field label"); + + await expect(label).toBeHidden(); + }); + + test("🧪 should throw error if no valid label was set for the input", async ({ + page, + }) => { + const errors: string[] = []; + + page.on("console", msg => { + if (msg.type() === "error") { + errors.push(msg.text()); + } + }); + + await render( + page, + ``, + ); + + expect(errors[0]).toContain( + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, + ); + }); + + test("🧪 should render slots", async ({ page }) => { + await render( + page, + ` + + + + + click + + `, + ); + + const leadingIcon = page.getByTestId("leading-icon"); + const trailing = page.getByTestId("trailing"); + + await expect(leadingIcon).toBeVisible(); + await expect(trailing).toBeVisible(); + }); + + test("🧪 should not be interactive when disabled", async ({ page }) => { + await render( + page, + ``, + ); + + const textField = page.getByTestId("test-text-field"); + + await expect(textField).not.toBeFocused(); + await textField.click(); + await expect(textField).not.toBeFocused(); + }); + + test("🧪 should show supporting text", async ({ page }) => { + await render( + page, + ``, + ); + + const supportingText = page.locator( + 'tapsi-text-field [part="supporting-text"]', + ); + + await expect(supportingText).toBeVisible(); + }); + + test("🧪 should set and get `valueAsDate`", async ({ page }) => { + await render( + page, + ``, + ); + + const textField = page.getByTestId("test-text-field"); + + const date = new Date("2023-12-25"); + + await textField.evaluate((el: TapsiTextField, date: Date) => { + el.valueAsDate = date; + }, date); + await expect(textField).toHaveJSProperty("valueAsDate", date); + }); + + test("🧪 should be able to change the value in `number` type using public methods (`stepUp` and `stepDown`) and keyboard with custom step", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const textField = page.getByTestId("test-text-field"); + + await textField.evaluate((el: TapsiTextField) => { + el.stepDown(2); + }); + await expect(textField).toHaveJSProperty("valueAsNumber", 6); + + await textField.evaluate((el: TapsiTextField) => { + el.stepUp(4); + }); + await expect(textField).toHaveJSProperty("valueAsNumber", 14); + + await page.keyboard.press("Tab"); + await page.keyboard.press("ArrowDown"); + await expect(textField).toHaveJSProperty("valueAsNumber", 12); + + await page.keyboard.press("ArrowUp"); + await page.keyboard.press("ArrowUp"); + await expect(textField).toHaveJSProperty("valueAsNumber", 16); + }); + + test("🧪 should be focused after clicking on internal label", async ({ + page, + }) => { + await render( + page, + ``, + ); + + const label = page.locator("tapsi-text-field label"); + const input = page.getByTestId("test-text-field"); + + await expect(input).not.toBeFocused(); + await label.click(); + await expect(input).toBeFocused(); + }); + + test("🧪 should be focused after clicking on external label", async ({ + page, + }) => { + await render( + page, + ` + + + `, + ); + + const label = page.getByTestId("external-label"); + const input = page.getByTestId("test-text-field"); + + await expect(input).not.toBeFocused(); + await label.click(); + await expect(input).toBeFocused(); + }); +}); diff --git a/packages/web-components/src/text-field/text-field.ts b/packages/web-components/src/text-field/text-field.ts index 039efad2..c7fad244 100644 --- a/packages/web-components/src/text-field/text-field.ts +++ b/packages/web-components/src/text-field/text-field.ts @@ -1,6 +1,7 @@ import { html, nothing } from "lit"; import { property } from "lit/decorators.js"; import { live } from "lit/directives/live.js"; +import { ErrorMessages } from "../base-text-input/constants.ts"; import BaseTextInput from "../base-text-input/index.ts"; import { createValidator, logger, type Validator } from "../utils/index.ts"; import TextFieldValidator from "./Validator.ts"; @@ -170,10 +171,7 @@ export class TextField extends BaseTextInput { protected override renderInput() { if (!this.hasValidLabel()) { logger( - [ - "Expected a valid `label` or `labelledby` attribute, received none.", - "If you want to hide the label, provide both `label` and `hidelabel` attributes.", - ].join(" "), + ErrorMessages.SET_VALID_LABEL_OR_LABELLEDBY_ATTRIBUTE, "text-field", "error", ); diff --git a/packages/web-components/src/tooltip/tooltip.test.ts b/packages/web-components/src/tooltip/tooltip.test.ts new file mode 100644 index 00000000..ab08c158 --- /dev/null +++ b/packages/web-components/src/tooltip/tooltip.test.ts @@ -0,0 +1,108 @@ +import { describe, expect, render, test } from "@internals/test-helpers"; + +describe("🧩 tooltip", () => { + test("🧪 should be visible to user", async ({ page }) => { + await render( + page, + ` + button + tooltip + `, + ); + + const anchor = page.getByTestId("test-anchor"); + const tooltip = page.getByTestId("test-tooltip").getByRole("tooltip"); + + await expect(anchor).toBeVisible(); + await expect(tooltip).toBeVisible(); + }); + + test("🧪 should be dismissible only with `dismissible` attribute", async ({ + page, + }) => { + const dismissButton = page.getByTestId("test-tooltip").getByRole("button"); + const tooltip = page.getByTestId("test-tooltip").getByRole("tooltip"); + + await render( + page, + ` + button + tooltip + `, + ); + await expect(dismissButton).toBeHidden(); + + await render( + page, + ` + button + tooltip + `, + ); + await expect(dismissButton).toBeVisible(); + + await dismissButton.click(); + await expect(tooltip).toBeHidden(); + }); + + test("🧪 should show tooltip on hover based on value of `no-hover-activation` attribute", async ({ + page, + }) => { + const tooltip = page.getByTestId("test-tooltip").getByRole("tooltip"); + const anchor = page.getByTestId("test-anchor"); + + await render( + page, + ` + button + tooltip + `, + ); + await expect(tooltip).toBeHidden(); + + await anchor.hover(); + await expect(tooltip).toBeVisible(); + + await render( + page, + ` + button + tooltip + `, + ); + await expect(tooltip).toBeHidden(); + + await anchor.hover(); + await expect(tooltip).toBeHidden(); + }); + + test("🧪 should hide tooltip on pressing Escape based on value of `no-escape-deactivation` attribute", async ({ + page, + }) => { + const tooltip = page.getByTestId("test-tooltip").getByRole("tooltip"); + + await render( + page, + ` + button + tooltip + `, + ); + await expect(tooltip).toBeVisible(); + + await page.keyboard.press("Escape"); + await expect(tooltip).toBeHidden(); + + await render( + page, + ` + button + tooltip + `, + ); + await expect(tooltip).toBeVisible(); + + await page.keyboard.press("Escape"); + await expect(tooltip).toBeVisible(); + }); +}); diff --git a/packages/web-icons/src/base-icon.ts b/packages/web-icons/src/base-icon.ts index b4d13847..6a5370cc 100644 --- a/packages/web-icons/src/base-icon.ts +++ b/packages/web-icons/src/base-icon.ts @@ -30,9 +30,9 @@ class BaseIcon extends HTMLElement { this.attachShadow({ mode: "open" }); } - public declare viewbox: string; - public declare title: string; - public declare size: Size; + declare public viewbox: string; + declare public title: string; + declare public size: Size; public static get observedAttributes() { return ["viewbox", "title", "size"]; diff --git a/playground/index.html b/playground/index.html index 1abdf184..c631860c 100644 --- a/playground/index.html +++ b/playground/index.html @@ -24,10 +24,6 @@ href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100..900&display=swap" rel="stylesheet" /> - + [Test] Playground + + + diff --git a/playground/vite.config.ts b/playground/vite.config.ts index 9997a6f8..e073d206 100644 --- a/playground/vite.config.ts +++ b/playground/vite.config.ts @@ -1,7 +1,19 @@ import react from "@vitejs/plugin-react"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + export default defineConfig({ plugins: [tsconfigPaths(), react()], + build: { + rollupOptions: { + input: { + test: path.resolve(__dirname, "test.html"), + dev: path.resolve(__dirname, "index.html"), + }, + }, + }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index efe1820d..15a3a952 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,30 +14,12 @@ importers: '@eslint/js': specifier: ^9.1.1 version: 9.19.0 - '@jest/globals': - specifier: ^29.7.0 - version: 29.7.0 '@lit/react': specifier: ^1.0.6 version: 1.0.7(@types/react@18.3.18) - '@open-wc/lit-helpers': - specifier: ^0.7.0 - version: 0.7.0(lit@3.2.1) - '@open-wc/testing': - specifier: ^4.0.0 - version: 4.0.0 - '@testing-library/dom': - specifier: ^10.4.0 - version: 10.4.0 - '@testing-library/jest-dom': - specifier: ^6.6.3 - version: 6.6.3 - '@testing-library/react': - specifier: ^16.2.0 - version: 16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@testing-library/user-event': - specifier: ^14.6.1 - version: 14.6.1(@testing-library/dom@10.4.0) + '@playwright/test': + specifier: ^1.50.1 + version: 1.50.1 '@types/eslint__js': specifier: ^8.42.3 version: 8.42.3 @@ -83,33 +65,21 @@ importers: eslint-plugin-import: specifier: ^2.31.0 version: 2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.19.0) - eslint-plugin-jest: - specifier: ^28.11.0 - version: 28.11.0(@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(jest@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)))(typescript@5.7.3) - eslint-plugin-jest-dom: - specifier: ^5.5.0 - version: 5.5.0(@testing-library/dom@10.4.0)(eslint@9.19.0) eslint-plugin-lit: specifier: ^1.15.0 version: 1.15.0(eslint@9.19.0) + eslint-plugin-playwright: + specifier: ^2.2.0 + version: 2.2.0(eslint@9.19.0) eslint-plugin-prettier: specifier: ^5.2.1 version: 5.2.3(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.19.0))(eslint@9.19.0)(prettier@3.4.2) - eslint-plugin-testing-library: - specifier: ^7.1.1 - version: 7.1.1(eslint@9.19.0)(typescript@5.7.3) eslint-plugin-wc: specifier: ^2.2.0 version: 2.2.0(eslint@9.19.0) fast-glob: specifier: ^3.3.2 version: 3.3.3 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - jest-environment-jsdom: - specifier: ^29.7.0 - version: 29.7.0 lit: specifier: ^3.2.1 version: 3.2.1 @@ -146,9 +116,6 @@ importers: semver: specifier: ^7.6.3 version: 7.6.3 - shadow-dom-testing-library: - specifier: ^1.11.3 - version: 1.11.3(@testing-library/dom@10.4.0) shx: specifier: ^0.3.4 version: 0.3.4 @@ -158,12 +125,6 @@ importers: stream-json: specifier: ^1.9.1 version: 1.9.1 - ts-jest: - specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.7))(jest@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)))(typescript@5.7.3) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@20.17.16)(typescript@5.7.3) tslib: specifier: ^2.8.0 version: 2.8.1 @@ -253,9 +214,6 @@ importers: '@internals/test-helpers': specifier: workspace:* version: link:../../internals/test-helpers - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) packages/web-icons: dependencies: @@ -286,9 +244,6 @@ importers: packages: - '@adobe/css-tools@4.4.1': - resolution: {integrity: sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==} - '@algolia/autocomplete-core@1.17.7': resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} @@ -420,97 +375,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.26.0': - resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.25.9': - resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.25.9': resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} engines: {node: '>=6.9.0'} @@ -523,10 +387,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.7': - resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} - engines: {node: '>=6.9.0'} - '@babel/template@7.25.9': resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} @@ -539,13 +399,6 @@ packages: resolution: {integrity: sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - '@custom-elements-manifest/analyzer@0.10.4': resolution: {integrity: sha512-hse8o20Jd82BwWank29/J9OC4PmSTwUoEmll3LEjDF3WLY/Lc8g3TUYSib/3GARCS8Q5myT2RPqEWfRa+6bkIg==} hasBin: true @@ -892,9 +745,6 @@ packages: resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@esm-bundle/chai@4.3.4-fix.0': - resolution: {integrity: sha512-26SKdM4uvDWlY8/OOOxSB1AqQWeBosCX3wRYUZO7enTAj03CtVxIiCimYVG2WpULcyV51qapK4qTovwkUr5Mlw==} - '@floating-ui/core@1.6.9': resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} @@ -907,9 +757,6 @@ packages: '@github/catalyst@1.7.0': resolution: {integrity: sha512-qOAxrDdRZz9+v4y2WoAfh11rpRY/x4FRofPNmJyZFzAjubtzE3sCa/tAycWWufmQGoYiwwzL/qJBBgyg7avxPw==} - '@hapi/bourne@3.0.0': - resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==} - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -936,80 +783,6 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/core@29.7.0': - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -1028,9 +801,6 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@lit-labs/ssr-dom-shim@1.3.0': resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} @@ -1058,30 +828,15 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@open-wc/dedupe-mixin@1.4.0': - resolution: {integrity: sha512-Sj7gKl1TLcDbF7B6KUhtvr+1UCxdhMbNY5KxdU5IfMFWqL8oy1ZeAcCANjoB1TL0AJTcPmcCFsCbHf8X2jGDUA==} - - '@open-wc/lit-helpers@0.7.0': - resolution: {integrity: sha512-4NBlx5ve0EvZplCRJbESm0MdMbRCw16alP2y76KAAAwzmFFXXrUj5hFwhw55+sSg5qaRRx6sY+s7usKgnNo3TQ==} - peerDependencies: - lit: ^2.0.0 || ^3.0.0 - - '@open-wc/scoped-elements@3.0.5': - resolution: {integrity: sha512-q4U+hFTQQRyorJILOpmBm6PY2hgjCnQe214nXJNjbJMQ9EvT55oyZ7C8BY5aFYJkytUyBoawlMpZt4F2xjdzHw==} - - '@open-wc/semantic-dom-diff@0.20.1': - resolution: {integrity: sha512-mPF/RPT2TU7Dw41LEDdaeP6eyTOWBD4z0+AHP4/d0SbgcfJZVRymlIB6DQmtz0fd2CImIS9kszaMmwMt92HBPA==} - - '@open-wc/testing-helpers@3.0.1': - resolution: {integrity: sha512-hyNysSatbgT2FNxHJsS3rGKcLEo6+HwDFu1UQL6jcSQUabp/tj3PyX7UnXL3H5YGv0lJArdYLSnvjLnjn3O2fw==} - - '@open-wc/testing@4.0.0': - resolution: {integrity: sha512-KI70O0CJEpBWs3jrTju4BFCy7V/d4tFfYWkg8pMzncsDhD7TYNHLw5cy+s1FHXIgVFetnMDhPpwlKIPvtTQW7w==} - '@pkgr/core@0.1.1': resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@playwright/test@1.50.1': + resolution: {integrity: sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==} + engines: {node: '>=18'} + hasBin: true + '@rollup/rollup-android-arm-eabi@4.32.0': resolution: {integrity: sha512-G2fUQQANtBPsNwiVFg4zKiPQyjVKZCUdQUol53R8E71J7AsheRMV/Yv/nB8giOcOVqP7//eB5xPqieBYZe9bGg==} cpu: [arm] @@ -1204,73 +959,10 @@ packages: '@shikijs/vscode-textmate@10.0.1': resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - - '@testing-library/dom@10.4.0': - resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} - engines: {node: '>=18'} - - '@testing-library/jest-dom@6.6.3': - resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - - '@testing-library/react@16.2.0': - resolution: {integrity: sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==} - engines: {node: '>=18'} - peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@testing-library/user-event@14.6.1': - resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} - engines: {node: '>=12', npm: '>=6'} - peerDependencies: - '@testing-library/dom': '>=7.21.4' - - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@types/accepts@1.3.7': - resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} - - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - - '@types/babel__code-frame@7.0.6': - resolution: {integrity: sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1283,36 +975,9 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/body-scroll-lock@3.1.2': resolution: {integrity: sha512-ELhtuphE/YbhEcpBf/rIV9Tl3/O0A0gpCVD+oYFSS8bWstHFJUgA4nNw1ZakVlRC38XaQEIsBogUZKWIPBvpfQ==} - '@types/chai-dom@1.11.3': - resolution: {integrity: sha512-EUEZI7uID4ewzxnU7DJXtyvykhQuwe+etJ1wwOiJyQRTH/ifMWKX+ghiXkxCUvNJ6IQDodf0JXhuP6zZcy2qXQ==} - - '@types/chai@4.3.20': - resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} - - '@types/co-body@6.1.3': - resolution: {integrity: sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/content-disposition@0.5.8': - resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} - - '@types/convert-source-map@2.0.3': - resolution: {integrity: sha512-ag0BfJLZf6CQz8VIuRIEYQ5Ggwk/82uvTQf27RcpyDNbY0Vw49LIPqAxk5tqYfrCs9xDaIMvl4aj7ZopnYL8bA==} - - '@types/cookies@0.9.0': - resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} - - '@types/debounce@1.2.4': - resolution: {integrity: sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw==} - '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} @@ -1322,51 +987,15 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/express-serve-static-core@5.0.5': - resolution: {integrity: sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA==} - - '@types/express@5.0.0': - resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==} - - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/http-assert@1.5.6': - resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} - - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jsdom@20.0.1': - resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/keygrip@1.0.6': - resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} - - '@types/koa-compose@3.2.8': - resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} - - '@types/koa@2.15.0': - resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} - '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -1379,30 +1008,18 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/mustache@4.2.5': resolution: {integrity: sha512-PLwiVvTBg59tGFL/8VpcGvqOu3L4OuveNvPi0EYbWchRdEVP++yRUXJPFl+CApKEq13017/4Nf7aQ5lTtHUNsA==} '@types/node@20.17.16': resolution: {integrity: sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==} - '@types/parse5@6.0.3': - resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} - '@types/postcss-import@14.0.3': resolution: {integrity: sha512-raZhRVTf6Vw5+QbmQ7LOHSDML71A5rj4+EqDzAbrZPfxfoGzFxMHRCq16VlddGIZpHELw0BG4G0YE2ANkdZiIQ==} '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - '@types/qs@6.9.18': - resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-dom@18.3.5': resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} peerDependencies: @@ -1414,33 +1031,12 @@ packages: '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - - '@types/sinon-chai@3.2.12': - resolution: {integrity: sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ==} - - '@types/sinon@17.0.3': - resolution: {integrity: sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==} - - '@types/sinonjs__fake-timers@8.1.5': - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/stream-chain@2.1.0': resolution: {integrity: sha512-guDyAl6s/CAzXUOWpGK2bHvdiopLIwpGu8v10+lb9hnQOyo4oj/ZUQFOvqFjKGsE3wJP1fpIesCcMvbXuWsqOg==} '@types/stream-json@1.7.8': resolution: {integrity: sha512-MU1OB1eFLcYWd1LjwKXrxdoPtXSRzRmAnnxs4Js/ayB5O/NvHraWwuOaqMWIebpYwM6khFlsJOHEhI9xK/ab4Q==} - '@types/tough-cookie@4.0.5': - resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -1450,15 +1046,6 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - '@types/ws@7.4.7': - resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.21.0': resolution: {integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1616,59 +1203,20 @@ packages: '@vueuse/shared@12.5.0': resolution: {integrity: sha512-vMpcL1lStUU6O+kdj6YdHDixh0odjPAUM15uJ9f7MY781jcYkIwFA4iv2EfoIPO6vBmvutI1HxxAwmf0cx5ISQ==} - '@web/browser-logs@0.4.1': - resolution: {integrity: sha512-ypmMG+72ERm+LvP+loj9A64MTXvWMXHUOu773cPO4L1SV/VWg6xA9Pv7vkvkXQX+ItJtCJt+KQ+U6ui2HhSFUw==} - engines: {node: '>=18.0.0'} - '@web/config-loader@0.1.3': resolution: {integrity: sha512-XVKH79pk4d3EHRhofete8eAnqto1e8mCRAqPV00KLNFzCWSe8sWmLnqKCqkPNARC6nksMaGrATnA5sPDRllMpQ==} engines: {node: '>=10.0.0'} - '@web/dev-server-core@0.7.5': - resolution: {integrity: sha512-Da65zsiN6iZPMRuj4Oa6YPwvsmZmo5gtPWhW2lx3GTUf5CAEapjVpZVlUXnKPL7M7zRuk72jSsIl8lo+XpTCtw==} - engines: {node: '>=18.0.0'} - - '@web/parse5-utils@2.1.0': - resolution: {integrity: sha512-GzfK5disEJ6wEjoPwx8AVNwUe9gYIiwc+x//QYxYDAFKUp4Xb1OJAGLc2l2gVrSQmtPGLKrTRcW90Hv4pEq1qA==} - engines: {node: '>=18.0.0'} - - '@web/test-runner-commands@0.9.0': - resolution: {integrity: sha512-zeLI6QdH0jzzJMDV5O42Pd8WLJtYqovgdt0JdytgHc0d1EpzXDsc7NTCJSImboc2NcayIsWAvvGGeRF69SMMYg==} - engines: {node: '>=18.0.0'} - - '@web/test-runner-core@0.13.4': - resolution: {integrity: sha512-84E1025aUSjvZU1j17eCTwV7m5Zg3cZHErV3+CaJM9JPCesZwLraIa0ONIQ9w4KLgcDgJFw9UnJ0LbFf42h6tg==} - engines: {node: '>=18.0.0'} - - abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - deprecated: Use your platform's native atob() and btoa() methods instead - - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - - acorn-globals@7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1676,10 +1224,6 @@ packages: resolution: {integrity: sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==} engines: {node: '>= 14.0.0'} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1692,30 +1236,13 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - array-back@3.1.0: resolution: {integrity: sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==} engines: {node: '>=6'} @@ -1752,53 +1279,14 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.10.2: - resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} - engines: {node: '>=4'} - - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - babel-preset-current-node-syntax@1.1.0: - resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} - peerDependencies: - '@babel/core': ^7.0.0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1827,24 +1315,6 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - cache-content-type@1.0.1: - resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} - engines: {node: '>= 6.0.0'} - call-bind-apply-helpers@1.0.1: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} @@ -1861,39 +1331,20 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - caniuse-lite@1.0.30001695: resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai-a11y-axe@1.5.0: - resolution: {integrity: sha512-V/Vg/zJDr9aIkaHJ2KQu7lGTQQm5ZOH4u1k5iTMvIXuSVlSuUo0jcSpSqf9wUn9zl6oQXa4e4E0cqH18KOgKlQ==} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} - chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -1908,40 +1359,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - clone@2.1.2: - resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} - engines: {node: '>=0.8'} - - co-body@6.2.0: - resolution: {integrity: sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==} - engines: {node: '>=8.0.0'} - - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -1955,10 +1376,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -1973,33 +1390,13 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookies@0.9.1: - resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} - engines: {node: '>= 0.8'} - copy-anything@3.0.5: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} - create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-spawn@6.0.6: resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} engines: {node: '>=4.8'} @@ -2008,24 +1405,11 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true - cssom@0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - - cssom@0.5.0: - resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} - - cssstyle@2.3.0: - resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} - engines: {node: '>=8'} - csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -2035,10 +1419,6 @@ packages: custom-elements-manifest@2.1.0: resolution: {integrity: sha512-4TU+YhBQpCGYWonsZVTOPx6aYJXenOiSRT7TNGvDB7ipa4SZSJKed1DYXG77XKL9JFZ86sDSDVkwgv1mqw3V3A==} - data-urls@3.0.2: - resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} - engines: {node: '>=12'} - data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2071,58 +1451,17 @@ packages: supports-color: optional: true - decimal.js@10.5.0: - resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} - - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - - deep-equal@1.0.1: - resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - default-gateway@6.0.3: - resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} - engines: {node: '>= 10'} - define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - - depd@1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} - engines: {node: '>= 0.6'} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dependency-graph@0.11.0: resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} engines: {node: '>= 0.6.0'} @@ -2131,25 +1470,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2158,46 +1481,19 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - - dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - - domexception@4.0.0: - resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} - engines: {node: '>=12'} - deprecated: Use your platform's native DOMException instead - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - electron-to-chromium@1.5.88: resolution: {integrity: sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==} - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - emoji-regex-xs@1.0.0: resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - enhanced-resolve@5.18.0: resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} engines: {node: '>=10.13.0'} @@ -2209,9 +1505,6 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - errorstacks@2.4.1: - resolution: {integrity: sha512-jE4i0SMYevwu/xxAuzhly/KTwtj0xDhbzB6m1xPImxTkw8wcCbgarOQPfCVMi5JKVyW7in29pNJCCJrry3Ynnw==} - es-abstract@1.23.9: resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} @@ -2227,9 +1520,6 @@ packages: es-module-lexer@0.9.3: resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} - es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2259,26 +1549,14 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true @@ -2338,35 +1616,18 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-jest-dom@5.5.0: - resolution: {integrity: sha512-CRlXfchTr7EgC3tDI7MGHY6QjdJU5Vv2RPaeeGtkXUHnKZf04kgzMPIJUXt4qKCvYWVVIEo9ut9Oq1vgXAykEA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6', yarn: '>=1'} - peerDependencies: - '@testing-library/dom': ^8.0.0 || ^9.0.0 || ^10.0.0 - eslint: ^6.8.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - peerDependenciesMeta: - '@testing-library/dom': - optional: true - - eslint-plugin-jest@28.11.0: - resolution: {integrity: sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig==} - engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} - peerDependencies: - '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - jest: '*' - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - jest: - optional: true - eslint-plugin-lit@1.15.0: resolution: {integrity: sha512-Yhr2MYNz6Ln8megKcX503aVZQln8wsywCG49g0heiJ/Qr5UjkE4pGr4Usez2anNcc7NvlvHbQWMYwWcgH3XRKA==} engines: {node: '>= 12'} peerDependencies: eslint: '>= 5' + eslint-plugin-playwright@2.2.0: + resolution: {integrity: sha512-qSQpAw7RcSzE3zPp8FMGkthaCWovHZ/BsXtpmnGax9vQLIovlh1bsZHEa2+j2lv9DWhnyeLM/qZmp7ffQZfQvg==} + engines: {node: '>=16.6.0'} + peerDependencies: + eslint: '>=8.40.0' + eslint-plugin-prettier@5.2.3: resolution: {integrity: sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2381,12 +1642,6 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-testing-library@7.1.1: - resolution: {integrity: sha512-nszC833aZPwB6tik1nMkbFqmtgIXTT0sfJEYs0zMBKMlkQ4to2079yUV96SvmLh00ovSBJI4pgcBC1TiIP8mXg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: ^9.14.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - eslint-plugin-wc@2.2.0: resolution: {integrity: sha512-kjPp+aXz23fOl0JZJOJS+6adwhEv98KjZ2FJqWpc4vtmk4Oenz/JJmmNZrGSARgtyR0BLIF/kVWC6GSlHA+5MA==} peerDependencies: @@ -2418,11 +1673,6 @@ packages: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -2442,22 +1692,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2477,16 +1711,10 @@ packages: fastq@1.18.0: resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2495,10 +1723,6 @@ packages: resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} engines: {node: '>=4.0.0'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -2517,14 +1741,6 @@ packages: resolution: {integrity: sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==} engines: {node: '>= 0.4'} - form-data@4.0.1: - resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} - engines: {node: '>= 6'} - - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -2532,6 +1748,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2559,10 +1780,6 @@ packages: resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} engines: {node: '>= 0.4'} - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -2571,10 +1788,6 @@ packages: resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} engines: {node: '>=12'} - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -2598,6 +1811,10 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2610,10 +1827,6 @@ packages: resolution: {integrity: sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==} engines: {node: '>=10'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - globby@14.0.2: resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} @@ -2674,52 +1887,9 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - html-encoding-sniffer@3.0.0: - resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} - engines: {node: '>=12'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - http-assert@1.5.0: - resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} - engines: {node: '>= 0.8'} - - http-errors@1.6.3: - resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} - engines: {node: '>= 0.6'} - - http-errors@1.8.1: - resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} - engines: {node: '>= 0.6'} - - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - - http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2728,37 +1898,17 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - inflation@2.1.0: - resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==} - engines: {node: '>= 0.8.0'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.3: - resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - internal-ip@6.2.0: - resolution: {integrity: sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==} - engines: {node: '>=10'} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -2767,14 +1917,6 @@ packages: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} - ip-regex@4.3.0: - resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} - engines: {node: '>=8'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2817,11 +1959,6 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2834,10 +1971,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - is-generator-function@1.1.0: resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} @@ -2846,10 +1979,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-ip@3.1.0: - resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==} - engines: {node: '>=8'} - is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -2877,10 +2006,6 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -2912,210 +2037,22 @@ packages: resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} engines: {node: '>=12.13'} - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isbinaryfile@5.0.4: - resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} - engines: {node: '>= 18.0.0'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} - engines: {node: '>=10'} - hasBin: true - - jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-jsdom@29.7.0: - resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - js-levenshtein-esm@1.2.0: resolution: {integrity: sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsdom@20.0.3: - resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} - engines: {node: '>=14'} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3127,9 +2064,6 @@ packages: json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -3148,43 +2082,9 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - keygrip@1.1.0: - resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} - engines: {node: '>= 0.6'} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - - koa-compose@4.1.0: - resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} - - koa-convert@2.0.0: - resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} - engines: {node: '>= 10'} - - koa-etag@4.0.0: - resolution: {integrity: sha512-1cSdezCkBWlyuB9l6c/IFoe1ANCDdPBxkDkRiaIup40xpUub6U/wwRXoKBZw/O5BifX9OlqAjYnDyzM6+l+TAg==} - - koa-send@5.0.1: - resolution: {integrity: sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==} - engines: {node: '>= 8'} - - koa-static@5.0.0: - resolution: {integrity: sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==} - engines: {node: '>= 7.6.0'} - - koa@2.15.3: - resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==} - engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} - - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3193,9 +2093,6 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lit-element@4.1.1: resolution: {integrity: sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==} @@ -3209,10 +2106,6 @@ packages: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3220,19 +2113,9 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-update@4.0.0: - resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} - engines: {node: '>=10'} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -3240,27 +2123,9 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@8.0.5: - resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} - engines: {node: '>=16.14'} - - lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} @@ -3271,17 +2136,10 @@ packages: mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3305,29 +2163,9 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3341,11 +2179,6 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3353,9 +2186,6 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true - nanocolors@0.2.13: - resolution: {integrity: sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==} - nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3364,16 +2194,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -3389,13 +2212,6 @@ packages: engines: {node: '>= 4'} hasBin: true - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - nwsapi@2.2.16: - resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} - object-inspect@1.13.3: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} @@ -3420,27 +2236,12 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - oniguruma-to-es@2.3.0: resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==} - only@0.0.2: - resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} - - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -3449,38 +2250,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - p-event@4.2.0: - resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} - engines: {node: '>=8'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3489,23 +2266,12 @@ packages: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - parse5@7.2.1: - resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3560,13 +2326,15 @@ packages: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} + playwright-core@1.50.1: + resolution: {integrity: sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==} + engines: {node: '>=18'} + hasBin: true - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + playwright@1.50.1: + resolution: {integrity: sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==} + engines: {node: '>=18'} + hasBin: true possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} @@ -3649,60 +2417,25 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-hrtime@1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - property-information@6.5.0: resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -3722,25 +2455,14 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.1.1: - resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} - engines: {node: '>= 14.18.0'} - rechoir@0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regex-recursion@5.1.1: resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==} @@ -3762,41 +2484,18 @@ packages: resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} engines: {node: '>=0.10.5'} - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve-path@1.4.0: - resolution: {integrity: sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==} - engines: {node: '>= 0.8'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} hasBin: true - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -3816,9 +2515,6 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -3827,13 +2523,6 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -3865,18 +2554,6 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - setprototypeof@1.1.0: - resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shadow-dom-testing-library@1.11.3: - resolution: {integrity: sha512-+XWK0Ds5eoDmejMFQbyPt5xPaBCd/JodAaZTzRa4/sZMhcO/wokGKMHeQYA3eiiZvtHs0MoJzU0ttfrWtUvbQA==} - engines: {node: '>= 14', npm: '>= 7'} - peerDependencies: - '@testing-library/dom': '>= 8' - shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -3926,12 +2603,6 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3940,25 +2611,10 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -3978,24 +2634,9 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - - statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - stream-chain@2.2.5: resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} @@ -4005,10 +2646,6 @@ packages: stream-json@1.9.1: resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4040,18 +2677,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -4068,17 +2693,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.9.2: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -4090,32 +2708,13 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - thenby@1.3.4: resolution: {integrity: sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} - - tr46@3.0.0: - resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} - engines: {node: '>=12'} - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -4125,44 +2724,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-jest@29.2.5: - resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} - engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/transform': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - tsconfck@3.1.4: resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==} engines: {node: ^18 || >=20} @@ -4179,10 +2740,6 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsscmp@1.0.6: - resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} - engines: {node: '>=0.6.x'} - tsx@4.19.2: resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} engines: {node: '>=18.0.0'} @@ -4192,18 +2749,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -4267,18 +2816,10 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - update-browserslist-db@1.1.2: resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true @@ -4288,26 +2829,12 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} @@ -4373,29 +2900,6 @@ packages: typescript: optional: true - w3c-xmlserializer@4.0.0: - resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} - engines: {node: '>=14'} - - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-encoding@2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} - engines: {node: '>=12'} - - whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - - whatwg-url@11.0.0: - resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} - engines: {node: '>=12'} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -4425,10 +2929,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4436,41 +2936,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - - xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4491,14 +2956,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - ylru@1.4.0: - resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==} - engines: {node: '>= 4.0.0'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -4508,8 +2965,6 @@ packages: snapshots: - '@adobe/css-tools@4.4.1': {} - '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': dependencies: '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) @@ -4697,91 +3152,6 @@ snapshots: dependencies: '@babel/types': 7.26.7 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.7)': - dependencies: - '@babel/core': 7.26.7 - '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.7)': dependencies: '@babel/core': 7.26.7 @@ -4792,10 +3162,6 @@ snapshots: '@babel/core': 7.26.7 '@babel/helper-plugin-utils': 7.26.5 - '@babel/runtime@7.26.7': - dependencies: - regenerator-runtime: 0.14.1 - '@babel/template@7.25.9': dependencies: '@babel/code-frame': 7.26.2 @@ -4819,12 +3185,6 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@bcoe/v8-coverage@0.2.3': {} - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - '@custom-elements-manifest/analyzer@0.10.4': dependencies: '@custom-elements-manifest/find-dependencies': 0.0.5 @@ -5052,10 +3412,6 @@ snapshots: '@eslint/core': 0.10.0 levn: 0.4.1 - '@esm-bundle/chai@4.3.4-fix.0': - dependencies: - '@types/chai': 4.3.20 - '@floating-ui/core@1.6.9': dependencies: '@floating-ui/utils': 0.2.9 @@ -5069,8 +3425,6 @@ snapshots: '@github/catalyst@1.7.0': {} - '@hapi/bourne@3.0.0': {} - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -5090,264 +3444,52 @@ snapshots: '@iconify/types@2.0.0': {} - '@istanbuljs/load-nyc-config@1.1.0': + '@jridgewell/gen-mapping@0.3.8': dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 - '@istanbuljs/schema@0.1.3': {} + '@jridgewell/resolve-uri@3.1.2': {} - '@jest/console@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 + '@jridgewell/set-array@1.2.1': {} - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node + '@jridgewell/sourcemap-codec@1.5.0': {} - '@jest/environment@29.7.0': + '@jridgewell/trace-mapping@0.3.25': dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - jest-mock: 29.7.0 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 - '@jest/expect-utils@29.7.0': + '@lit-labs/ssr-dom-shim@1.3.0': {} + + '@lit/react@1.0.7(@types/react@18.3.18)': dependencies: - jest-get-type: 29.6.3 + '@types/react': 18.3.18 - '@jest/expect@29.7.0': + '@lit/reactive-element@2.0.4': dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color + '@lit-labs/ssr-dom-shim': 1.3.0 - '@jest/fake-timers@29.7.0': + '@nodelib/fs.scandir@2.1.5': dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.17.16 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 - '@jest/globals@29.7.0': + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.18.0 + + '@nolyfill/is-core-module@1.0.39': {} + + '@pkgr/core@0.1.1': {} - '@jest/reporters@29.7.0': + '@playwright/test@1.50.1': dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.17.16 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.7 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.26.7 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 20.17.16 - '@types/yargs': 17.0.33 - chalk: 4.1.2 - - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@lit-labs/ssr-dom-shim@1.3.0': {} - - '@lit/react@1.0.7(@types/react@18.3.18)': - dependencies: - '@types/react': 18.3.18 - - '@lit/reactive-element@2.0.4': - dependencies: - '@lit-labs/ssr-dom-shim': 1.3.0 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.18.0 - - '@nolyfill/is-core-module@1.0.39': {} - - '@open-wc/dedupe-mixin@1.4.0': {} - - '@open-wc/lit-helpers@0.7.0(lit@3.2.1)': - dependencies: - lit: 3.2.1 - - '@open-wc/scoped-elements@3.0.5': - dependencies: - '@open-wc/dedupe-mixin': 1.4.0 - lit: 3.2.1 - - '@open-wc/semantic-dom-diff@0.20.1': - dependencies: - '@types/chai': 4.3.20 - '@web/test-runner-commands': 0.9.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@open-wc/testing-helpers@3.0.1': - dependencies: - '@open-wc/scoped-elements': 3.0.5 - lit: 3.2.1 - lit-html: 3.2.1 - - '@open-wc/testing@4.0.0': - dependencies: - '@esm-bundle/chai': 4.3.4-fix.0 - '@open-wc/semantic-dom-diff': 0.20.1 - '@open-wc/testing-helpers': 3.0.1 - '@types/chai-dom': 1.11.3 - '@types/sinon-chai': 3.2.12 - chai-a11y-axe: 1.5.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@pkgr/core@0.1.1': {} + playwright: 1.50.1 '@rollup/rollup-android-arm-eabi@4.32.0': optional: true @@ -5448,71 +3590,8 @@ snapshots: '@shikijs/vscode-textmate@10.0.1': {} - '@sinclair/typebox@0.27.8': {} - '@sindresorhus/merge-streams@2.3.0': {} - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - - '@testing-library/dom@10.4.0': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/runtime': 7.26.7 - '@types/aria-query': 5.0.4 - aria-query: 5.3.0 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - - '@testing-library/jest-dom@6.6.3': - dependencies: - '@adobe/css-tools': 4.4.1 - aria-query: 5.3.2 - chalk: 3.0.0 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - lodash: 4.17.21 - redent: 3.0.0 - - '@testing-library/react@16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.26.7 - '@testing-library/dom': 10.4.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - optionalDependencies: - '@types/react': 18.3.18 - '@types/react-dom': 18.3.5(@types/react@18.3.18) - - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': - dependencies: - '@testing-library/dom': 10.4.0 - - '@tootallnate/once@2.0.0': {} - - '@tsconfig/node10@1.0.11': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@types/accepts@1.3.7': - dependencies: - '@types/node': 20.17.16 - - '@types/aria-query@5.0.4': {} - - '@types/babel__code-frame@7.0.6': {} - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.26.7 @@ -5534,41 +3613,8 @@ snapshots: dependencies: '@babel/types': 7.26.7 - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 20.17.16 - '@types/body-scroll-lock@3.1.2': {} - '@types/chai-dom@1.11.3': - dependencies: - '@types/chai': 4.3.20 - - '@types/chai@4.3.20': {} - - '@types/co-body@6.1.3': - dependencies: - '@types/node': 20.17.16 - '@types/qs': 6.9.18 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 20.17.16 - - '@types/content-disposition@0.5.8': {} - - '@types/convert-source-map@2.0.3': {} - - '@types/cookies@0.9.0': - dependencies: - '@types/connect': 3.4.38 - '@types/express': 5.0.0 - '@types/keygrip': 1.0.6 - '@types/node': 20.17.16 - - '@types/debounce@1.2.4': {} - '@types/eslint@9.6.1': dependencies: '@types/estree': 1.0.6 @@ -5580,69 +3626,14 @@ snapshots: '@types/estree@1.0.6': {} - '@types/express-serve-static-core@5.0.5': - dependencies: - '@types/node': 20.17.16 - '@types/qs': 6.9.18 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express@5.0.0': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 5.0.5 - '@types/qs': 6.9.18 - '@types/serve-static': 1.15.7 - - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 20.17.16 - '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 - '@types/http-assert@1.5.6': {} - - '@types/http-errors@2.0.4': {} - - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jsdom@20.0.1': - dependencies: - '@types/node': 20.17.16 - '@types/tough-cookie': 4.0.5 - parse5: 7.2.1 - '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} - '@types/keygrip@1.0.6': {} - - '@types/koa-compose@3.2.8': - dependencies: - '@types/koa': 2.15.0 - - '@types/koa@2.15.0': - dependencies: - '@types/accepts': 1.3.7 - '@types/content-disposition': 0.5.8 - '@types/cookies': 0.9.0 - '@types/http-assert': 1.5.6 - '@types/http-errors': 2.0.4 - '@types/keygrip': 1.0.6 - '@types/koa-compose': 3.2.8 - '@types/node': 20.17.16 - '@types/linkify-it@5.0.0': {} '@types/markdown-it@14.1.2': @@ -5656,26 +3647,18 @@ snapshots: '@types/mdurl@2.0.0': {} - '@types/mime@1.3.5': {} - '@types/mustache@4.2.5': {} '@types/node@20.17.16': dependencies: undici-types: 6.19.8 - '@types/parse5@6.0.3': {} - '@types/postcss-import@14.0.3': dependencies: postcss: 8.5.1 '@types/prop-types@15.7.14': {} - '@types/qs@6.9.18': {} - - '@types/range-parser@1.2.7': {} - '@types/react-dom@18.3.5(@types/react@18.3.18)': dependencies: '@types/react': 18.3.18 @@ -5687,30 +3670,6 @@ snapshots: '@types/semver@7.5.8': {} - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 20.17.16 - - '@types/serve-static@1.15.7': - dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 20.17.16 - '@types/send': 0.17.4 - - '@types/sinon-chai@3.2.12': - dependencies: - '@types/chai': 4.3.20 - '@types/sinon': 17.0.3 - - '@types/sinon@17.0.3': - dependencies: - '@types/sinonjs__fake-timers': 8.1.5 - - '@types/sinonjs__fake-timers@8.1.5': {} - - '@types/stack-utils@2.0.3': {} - '@types/stream-chain@2.1.0': dependencies: '@types/node': 20.17.16 @@ -5720,24 +3679,12 @@ snapshots: '@types/node': 20.17.16 '@types/stream-chain': 2.1.0 - '@types/tough-cookie@4.0.5': {} - '@types/trusted-types@2.0.7': {} '@types/unist@3.0.3': {} '@types/web-bluetooth@0.0.20': {} - '@types/ws@7.4.7': - dependencies: - '@types/node': 20.17.16 - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.33': - dependencies: - '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -5938,114 +3885,16 @@ snapshots: transitivePeerDependencies: - typescript - '@web/browser-logs@0.4.1': - dependencies: - errorstacks: 2.4.1 - '@web/config-loader@0.1.3': dependencies: semver: 7.6.3 - '@web/dev-server-core@0.7.5': - dependencies: - '@types/koa': 2.15.0 - '@types/ws': 7.4.7 - '@web/parse5-utils': 2.1.0 - chokidar: 4.0.3 - clone: 2.1.2 - es-module-lexer: 1.6.0 - get-stream: 6.0.1 - is-stream: 2.0.1 - isbinaryfile: 5.0.4 - koa: 2.15.3 - koa-etag: 4.0.0 - koa-send: 5.0.1 - koa-static: 5.0.0 - lru-cache: 8.0.5 - mime-types: 2.1.35 - parse5: 6.0.1 - picomatch: 2.3.1 - ws: 7.5.10 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@web/parse5-utils@2.1.0': - dependencies: - '@types/parse5': 6.0.3 - parse5: 6.0.1 - - '@web/test-runner-commands@0.9.0': - dependencies: - '@web/test-runner-core': 0.13.4 - mkdirp: 1.0.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@web/test-runner-core@0.13.4': - dependencies: - '@babel/code-frame': 7.26.2 - '@types/babel__code-frame': 7.0.6 - '@types/co-body': 6.1.3 - '@types/convert-source-map': 2.0.3 - '@types/debounce': 1.2.4 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@web/browser-logs': 0.4.1 - '@web/dev-server-core': 0.7.5 - chokidar: 4.0.3 - cli-cursor: 3.1.0 - co-body: 6.2.0 - convert-source-map: 2.0.0 - debounce: 1.2.1 - dependency-graph: 0.11.0 - globby: 11.1.0 - internal-ip: 6.2.0 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.1.7 - log-update: 4.0.0 - nanocolors: 0.2.13 - nanoid: 3.3.8 - open: 8.4.2 - picomatch: 2.3.1 - source-map: 0.7.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - abab@2.0.6: {} - - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - - acorn-globals@7.0.1: - dependencies: - acorn: 8.14.0 - acorn-walk: 8.3.4 - acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 - acorn-walk@8.3.4: - dependencies: - acorn: 8.14.0 - acorn@8.14.0: {} - agent-base@6.0.2: - dependencies: - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -6069,10 +3918,6 @@ snapshots: '@algolia/requester-fetch': 5.20.0 '@algolia/requester-node-http': 5.20.0 - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} ansi-styles@3.2.1: @@ -6083,27 +3928,13 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 - - aria-query@5.3.2: {} - array-back@3.1.0: {} array-back@6.2.2: {} @@ -6157,78 +3988,15 @@ snapshots: get-intrinsic: 1.2.7 is-array-buffer: 3.0.5 - astral-regex@2.0.0: {} - async-function@1.0.0: {} - async@3.2.6: {} - - asynckit@0.4.0: {} - available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - axe-core@4.10.2: {} + balanced-match@1.0.2: {} - babel-jest@29.7.0(@babel/core@7.26.7): - dependencies: - '@babel/core': 7.26.7 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.26.7) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.26.5 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.7 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 - - babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.7): - dependencies: - '@babel/core': 7.26.7 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.7) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.7) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.7) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.7) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.7) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.7) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.7) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.7) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.7) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.7) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.7) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.7) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.7) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.7) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.7) - - babel-preset-jest@29.6.3(@babel/core@7.26.7): - dependencies: - '@babel/core': 7.26.7 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.7) - - balanced-match@1.0.2: {} - - binary-extensions@2.3.0: {} + binary-extensions@2.3.0: {} birpc@0.2.19: {} @@ -6254,23 +4022,6 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.2(browserslist@4.24.4) - bs-logger@0.2.6: - dependencies: - fast-json-stable-stringify: 2.1.0 - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - - buffer-from@1.1.2: {} - - bytes@3.1.2: {} - - cache-content-type@1.0.1: - dependencies: - mime-types: 2.1.35 - ylru: 1.4.0 - call-bind-apply-helpers@1.0.1: dependencies: es-errors: 1.3.0 @@ -6290,36 +4041,21 @@ snapshots: callsites@3.1.0: {} - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - caniuse-lite@1.0.30001695: {} ccount@2.0.1: {} - chai-a11y-axe@1.5.0: - dependencies: - axe-core: 4.10.2 - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - chalk@3.0.0: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - char-regex@1.0.2: {} - character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -6348,38 +4084,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.3: - dependencies: - readdirp: 4.1.1 - - ci-info@3.9.0: {} - - cjs-module-lexer@1.4.3: {} - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - clone@2.1.2: {} - - co-body@6.2.0: - dependencies: - '@hapi/bourne': 3.0.0 - inflation: 2.1.0 - qs: 6.14.0 - raw-body: 2.5.2 - type-is: 1.6.18 - - co@4.6.0: {} - - collect-v8-coverage@1.0.2: {} - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -6392,10 +4102,6 @@ snapshots: color-name@1.1.4: {} - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - comma-separated-tokens@2.0.3: {} command-line-args@5.1.2: @@ -6409,40 +4115,12 @@ snapshots: concat-map@0.0.1: {} - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - - content-type@1.0.5: {} - convert-source-map@2.0.0: {} - cookies@0.9.1: - dependencies: - depd: 2.0.0 - keygrip: 1.1.0 - copy-anything@3.0.5: dependencies: is-what: 4.1.16 - create-jest@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - create-require@1.1.1: {} - cross-spawn@6.0.6: dependencies: nice-try: 1.0.5 @@ -6457,30 +4135,14 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css.escape@1.5.1: {} - cssesc@3.0.0: {} - cssom@0.3.8: {} - - cssom@0.5.0: {} - - cssstyle@2.3.0: - dependencies: - cssom: 0.3.8 - csstype@3.1.3: {} custom-elements-manifest@1.0.0: {} custom-elements-manifest@2.1.0: {} - data-urls@3.0.2: - dependencies: - abab: 2.0.6 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - data-view-buffer@1.0.2: dependencies: call-bound: 1.0.3 @@ -6509,58 +4171,28 @@ snapshots: dependencies: ms: 2.1.3 - decimal.js@10.5.0: {} - - dedent@1.5.3: {} - - deep-equal@1.0.1: {} - deep-is@0.1.4: {} - deepmerge@4.3.1: {} - - default-gateway@6.0.3: - dependencies: - execa: 5.1.1 - define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - define-lazy-prop@2.0.0: {} - define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - delayed-stream@1.0.0: {} - - delegates@1.0.0: {} - - depd@1.1.2: {} - - depd@2.0.0: {} - dependency-graph@0.11.0: {} dequal@2.0.3: {} - destroy@1.2.0: {} - - detect-newline@3.1.0: {} - devlop@1.1.0: dependencies: dequal: 2.0.3 - diff-sequences@29.6.3: {} - - diff@4.0.2: {} - dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -6569,36 +4201,18 @@ snapshots: dependencies: esutils: 2.0.3 - dom-accessibility-api@0.5.16: {} - - dom-accessibility-api@0.6.3: {} - - domexception@4.0.0: - dependencies: - webidl-conversions: 7.0.0 - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - ee-first@1.1.1: {} - - ejs@3.1.10: - dependencies: - jake: 10.9.2 - electron-to-chromium@1.5.88: {} - emittery@0.13.1: {} - emoji-regex-xs@1.0.0: {} emoji-regex@8.0.0: {} - encodeurl@1.0.2: {} - enhanced-resolve@5.18.0: dependencies: graceful-fs: 4.2.11 @@ -6610,8 +4224,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - errorstacks@2.4.1: {} - es-abstract@1.23.9: dependencies: array-buffer-byte-length: 1.0.2 @@ -6672,8 +4284,6 @@ snapshots: es-module-lexer@0.9.3: {} - es-module-lexer@1.6.0: {} - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -6750,22 +4360,10 @@ snapshots: escalade@3.2.0: {} - escape-html@1.0.3: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - eslint-config-prettier@9.1.0(eslint@9.19.0): dependencies: eslint: 9.19.0 @@ -6840,25 +4438,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest-dom@5.5.0(@testing-library/dom@10.4.0)(eslint@9.19.0): - dependencies: - '@babel/runtime': 7.26.7 - eslint: 9.19.0 - requireindex: 1.2.0 - optionalDependencies: - '@testing-library/dom': 10.4.0 - - eslint-plugin-jest@28.11.0(@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(jest@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)))(typescript@5.7.3): - dependencies: - '@typescript-eslint/utils': 8.21.0(eslint@9.19.0)(typescript@5.7.3) - eslint: 9.19.0 - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.19.0)(typescript@5.7.3))(eslint@9.19.0)(typescript@5.7.3) - jest: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-lit@1.15.0(eslint@9.19.0): dependencies: eslint: 9.19.0 @@ -6866,6 +4445,11 @@ snapshots: parse5-htmlparser2-tree-adapter: 6.0.1 requireindex: 1.2.0 + eslint-plugin-playwright@2.2.0(eslint@9.19.0): + dependencies: + eslint: 9.19.0 + globals: 13.24.0 + eslint-plugin-prettier@5.2.3(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.19.0))(eslint@9.19.0)(prettier@3.4.2): dependencies: eslint: 9.19.0 @@ -6876,15 +4460,6 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 9.1.0(eslint@9.19.0) - eslint-plugin-testing-library@7.1.1(eslint@9.19.0)(typescript@5.7.3): - dependencies: - '@typescript-eslint/scope-manager': 8.21.0 - '@typescript-eslint/utils': 8.21.0(eslint@9.19.0)(typescript@5.7.3) - eslint: 9.19.0 - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-wc@2.2.0(eslint@9.19.0): dependencies: eslint: 9.19.0 @@ -6945,8 +4520,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 4.2.0 - esprima@4.0.1: {} - esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -6961,30 +4534,6 @@ snapshots: esutils@2.0.3: {} - etag@1.8.1: {} - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - exit@0.1.2: {} - - expect@29.7.0: - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -7005,18 +4554,10 @@ snapshots: dependencies: reusify: 1.0.4 - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -7025,11 +4566,6 @@ snapshots: dependencies: array-back: 3.1.0 - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -7050,14 +4586,6 @@ snapshots: dependencies: is-callable: 1.2.7 - form-data@4.0.1: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - fresh@0.5.2: {} - fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -7066,6 +4594,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -7099,8 +4630,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-package-type@0.1.0: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -7108,8 +4637,6 @@ snapshots: get-stdin@9.0.0: {} - get-stream@6.0.1: {} - get-symbol-description@1.1.0: dependencies: call-bound: 1.0.3 @@ -7139,6 +4666,10 @@ snapshots: globals@11.12.0: {} + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + globals@14.0.0: {} globalthis@1.0.4: @@ -7155,15 +4686,6 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - globby@14.0.2: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -7227,67 +4749,8 @@ snapshots: hosted-git-info@2.8.9: {} - html-encoding-sniffer@3.0.0: - dependencies: - whatwg-encoding: 2.0.0 - - html-escaper@2.0.2: {} - html-void-elements@3.0.0: {} - http-assert@1.5.0: - dependencies: - deep-equal: 1.0.1 - http-errors: 1.8.1 - - http-errors@1.6.3: - dependencies: - depd: 1.1.2 - inherits: 2.0.3 - setprototypeof: 1.1.0 - statuses: 1.5.0 - - http-errors@1.8.1: - dependencies: - depd: 1.1.2 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 1.5.0 - toidentifier: 1.0.1 - - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - http-proxy-agent@5.0.0: - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - - human-signals@2.1.0: {} - - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - ignore@5.3.2: {} import-fresh@3.3.0: @@ -7295,33 +4758,15 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - imurmurhash@0.1.4: {} - indent-string@4.0.0: {} - - inflation@2.1.0: {} - inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - inherits@2.0.3: {} - inherits@2.0.4: {} - internal-ip@6.2.0: - dependencies: - default-gateway: 6.0.3 - ipaddr.js: 1.9.1 - is-ip: 3.1.0 - p-event: 4.2.0 - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -7330,10 +4775,6 @@ snapshots: interpret@1.4.0: {} - ip-regex@4.3.0: {} - - ipaddr.js@1.9.1: {} - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -7384,8 +4825,6 @@ snapshots: call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-docker@2.2.1: {} - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -7394,8 +4833,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-fn@2.1.0: {} - is-generator-function@1.1.0: dependencies: call-bound: 1.0.3 @@ -7407,10 +4844,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-ip@3.1.0: - dependencies: - ip-regex: 4.3.0 - is-map@2.0.3: {} is-number-object@1.1.1: @@ -7435,476 +4868,56 @@ snapshots: dependencies: call-bound: 1.0.3 - is-stream@2.0.1: {} - is-string@1.1.1: dependencies: call-bound: 1.0.3 has-tostringtag: 1.0.2 - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.3 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.18 - - is-valid-element-name@1.0.0: - dependencies: - is-potential-custom-element-name: 1.0.1 - - is-weakmap@2.0.2: {} - - is-weakref@1.1.0: - dependencies: - call-bound: 1.0.3 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.3 - get-intrinsic: 1.2.7 - - is-what@4.1.16: {} - - is-wsl@2.2.0: - dependencies: - is-docker: 2.2.1 - - isarray@2.0.5: {} - - isbinaryfile@5.0.4: {} - - isexe@2.0.0: {} - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.26.7 - '@babel/parser': 7.26.7 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.26.7 - '@babel/parser': 7.26.7 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@4.0.1: - dependencies: - debug: 4.4.0 - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - jake@10.9.2: - dependencies: - async: 3.2.6 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - - jest-changed-files@29.7.0: - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - - jest-circus@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.3 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest-config@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)): - dependencies: - '@babel/core': 7.26.7 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.7) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 20.17.16 - ts-node: 10.9.2(@types/node@20.17.16)(typescript@5.7.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-docblock@29.7.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - - jest-environment-jsdom@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/jsdom': 20.0.1 - '@types/node': 20.17.16 - jest-mock: 29.7.0 - jest-util: 29.7.0 - jsdom: 20.0.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - jest-environment-node@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 20.17.16 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-matcher-utils@29.7.0: - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-message-util@29.7.0: - dependencies: - '@babel/code-frame': 7.26.2 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - jest-util: 29.7.0 - - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 - - jest-regex-util@29.6.3: {} - - jest-resolve-dependencies@29.7.0: - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - jest-resolve@29.7.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.10 - resolve.exports: 2.0.3 - slash: 3.0.0 - - jest-runner@29.7.0: - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - jest-runtime@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - chalk: 4.1.2 - cjs-module-lexer: 1.4.3 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.26.7 - '@babel/generator': 7.26.5 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.7) - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.7) - '@babel/types': 7.26.7 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.7) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - - jest-util@29.7.0: + is-symbol@1.1.1: dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 + call-bound: 1.0.3 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 - jest-validate@29.7.0: + is-typed-array@1.1.15: dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 + which-typed-array: 1.1.18 - jest-watcher@29.7.0: + is-valid-element-name@1.0.0: dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.16 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 + is-potential-custom-element-name: 1.0.1 + + is-weakmap@2.0.2: {} - jest-worker@29.7.0: + is-weakref@1.1.0: dependencies: - '@types/node': 20.17.16 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 + call-bound: 1.0.3 - jest@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)): + is-weakset@2.0.4: dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node + call-bound: 1.0.3 + get-intrinsic: 1.2.7 + + is-what@4.1.16: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} js-levenshtein-esm@1.2.0: {} js-tokens@4.0.0: {} - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.0: dependencies: argparse: 2.0.1 - jsdom@20.0.3: - dependencies: - abab: 2.0.6 - acorn: 8.14.0 - acorn-globals: 7.0.1 - cssom: 0.5.0 - cssstyle: 2.3.0 - data-urls: 3.0.2 - decimal.js: 10.5.0 - domexception: 4.0.0 - escodegen: 2.1.0 - form-data: 4.0.1 - html-encoding-sniffer: 3.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.16 - parse5: 7.2.1 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.4 - w3c-xmlserializer: 4.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - ws: 8.18.0 - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - jsesc@3.1.0: {} json-buffer@3.0.1: {} json-parse-better-errors@1.0.2: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -7921,72 +4934,10 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - keygrip@1.1.0: - dependencies: - tsscmp: 1.0.6 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - kleur@3.0.3: {} - - koa-compose@4.1.0: {} - - koa-convert@2.0.0: - dependencies: - co: 4.6.0 - koa-compose: 4.1.0 - - koa-etag@4.0.0: - dependencies: - etag: 1.8.1 - - koa-send@5.0.1: - dependencies: - debug: 4.4.0 - http-errors: 1.8.1 - resolve-path: 1.4.0 - transitivePeerDependencies: - - supports-color - - koa-static@5.0.0: - dependencies: - debug: 3.2.7 - koa-send: 5.0.1 - transitivePeerDependencies: - - supports-color - - koa@2.15.3: - dependencies: - accepts: 1.3.8 - cache-content-type: 1.0.1 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookies: 0.9.1 - debug: 4.4.0 - delegates: 1.0.0 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - fresh: 0.5.2 - http-assert: 1.5.0 - http-errors: 1.8.1 - is-generator-function: 1.1.0 - koa-compose: 4.1.0 - koa-convert: 2.0.0 - on-finished: 2.4.1 - only: 0.0.2 - parseurl: 1.3.3 - statuses: 1.5.0 - type-is: 1.6.18 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - leven@3.1.0: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -7994,8 +4945,6 @@ snapshots: lilconfig@3.1.3: {} - lines-and-columns@1.2.4: {} - lit-element@4.1.1: dependencies: '@lit-labs/ssr-dom-shim': 1.3.0 @@ -8019,29 +4968,14 @@ snapshots: pify: 3.0.0 strip-bom: 3.0.0 - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.camelcase@4.3.0: {} - lodash.memoize@4.1.2: {} - lodash.merge@4.6.2: {} - lodash@4.17.21: {} - - log-update@4.0.0: - dependencies: - ansi-escapes: 4.3.2 - cli-cursor: 3.1.0 - slice-ansi: 4.0.0 - wrap-ansi: 6.2.0 - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -8050,24 +4984,10 @@ snapshots: dependencies: yallist: 3.1.1 - lru-cache@8.0.5: {} - - lz-string@1.5.0: {} - magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - make-dir@4.0.0: - dependencies: - semver: 7.6.3 - - make-error@1.3.6: {} - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - mark.js@8.11.1: {} math-intrinsics@1.1.0: {} @@ -8084,12 +5004,8 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 - media-typer@0.3.0: {} - memorystream@0.3.1: {} - merge-stream@2.0.0: {} - merge2@1.4.1: {} micromark-util-character@2.1.1: @@ -8114,24 +5030,10 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mimic-fn@2.1.0: {} - - min-indent@1.0.1: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -8142,24 +5044,16 @@ snapshots: mitt@3.0.1: {} - mkdirp@1.0.4: {} - ms@2.1.3: {} mustache@4.2.0: {} - nanocolors@0.2.13: {} - nanoid@3.3.8: {} natural-compare@1.4.0: {} - negotiator@0.6.3: {} - nice-try@1.0.5: {} - node-int64@0.4.0: {} - node-releases@2.0.19: {} normalize-package-data@2.5.0: @@ -8183,12 +5077,6 @@ snapshots: shell-quote: 1.8.2 string.prototype.padend: 3.1.6 - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - nwsapi@2.2.16: {} - object-inspect@1.13.3: {} object-keys@1.1.1: {} @@ -8222,32 +5110,16 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - oniguruma-to-es@2.3.0: dependencies: emoji-regex-xs: 1.0.0 regex: 5.1.1 regex-recursion: 5.1.1 - only@0.0.2: {} - - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -8263,34 +5135,14 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - p-event@4.2.0: - dependencies: - p-timeout: 3.2.0 - - p-finally@1.0.0: {} - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - p-try@2.2.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -8300,25 +5152,12 @@ snapshots: error-ex: 1.3.2 json-parse-better-errors: 1.0.2 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.26.2 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse5-htmlparser2-tree-adapter@6.0.1: dependencies: parse5: 6.0.1 parse5@6.0.1: {} - parse5@7.2.1: - dependencies: - entities: 4.5.0 - - parseurl@1.3.3: {} - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -8349,11 +5188,13 @@ snapshots: pify@3.0.0: {} - pirates@4.0.6: {} + playwright-core@1.50.1: {} - pkg-dir@4.2.0: + playwright@1.50.1: dependencies: - find-up: 4.1.0 + playwright-core: 1.50.1 + optionalDependencies: + fsevents: 2.3.2 possible-typed-array-names@1.0.0: {} @@ -8430,60 +5271,20 @@ snapshots: prettier@3.4.2: {} - pretty-format@27.5.1: - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 - - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - pretty-hrtime@1.0.3: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - property-information@6.5.0: {} - psl@1.15.0: - dependencies: - punycode: 2.3.1 - punycode@2.3.1: {} - pure-rand@6.1.0: {} - - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - querystringify@2.2.0: {} - queue-microtask@1.2.3: {} - raw-body@2.5.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 - react-is@17.0.2: {} - - react-is@18.3.1: {} - react-refresh@0.14.2: {} react@18.3.1: @@ -8504,17 +5305,10 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.1.1: {} - rechoir@0.6.2: dependencies: resolve: 1.22.10 - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -8526,8 +5320,6 @@ snapshots: get-proto: 1.0.1 which-builtin-type: 1.2.1 - regenerator-runtime@0.14.1: {} - regex-recursion@5.1.1: dependencies: regex: 5.1.1 @@ -8552,36 +5344,16 @@ snapshots: requireindex@1.2.0: {} - requires-port@1.0.0: {} - - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - resolve-from@4.0.0: {} - resolve-from@5.0.0: {} - - resolve-path@1.4.0: - dependencies: - http-errors: 1.6.3 - path-is-absolute: 1.0.1 - resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.3: {} - resolve@1.22.10: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - reusify@1.0.4: {} rfdc@1.4.1: {} @@ -8623,8 +5395,6 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 - safe-buffer@5.2.1: {} - safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -8636,12 +5406,6 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safer-buffer@2.1.2: {} - - saxes@6.0.0: - dependencies: - xmlchars: 2.2.0 - scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -8676,14 +5440,6 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 - setprototypeof@1.1.0: {} - - setprototypeof@1.2.0: {} - - shadow-dom-testing-library@1.11.3(@testing-library/dom@10.4.0): - dependencies: - '@testing-library/dom': 10.4.0 - shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -8748,31 +5504,12 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - signal-exit@3.0.7: {} - - sisteransi@1.0.5: {} - slash@3.0.0: {} slash@5.1.0: {} - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - source-map-js@1.2.1: {} - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - source-map@0.7.4: {} - space-separated-tokens@2.0.2: {} spdx-correct@3.2.0: @@ -8791,18 +5528,8 @@ snapshots: speakingurl@14.0.1: {} - sprintf-js@1.0.3: {} - stable-hash@0.0.4: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - - statuses@1.5.0: {} - - statuses@2.0.1: {} - stream-chain@2.2.5: {} stream-chain@3.4.0: {} @@ -8811,11 +5538,6 @@ snapshots: dependencies: stream-chain: 2.2.5 - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -8863,14 +5585,6 @@ snapshots: strip-bom@3.0.0: {} - strip-bom@4.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - strip-json-comments@3.1.1: {} superjson@2.2.2: @@ -8885,14 +5599,8 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - symbol-tree@3.2.4: {} - synckit@0.9.2: dependencies: '@pkgr/core': 0.1.1 @@ -8902,76 +5610,18 @@ snapshots: tapable@2.2.1: {} - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - thenby@1.3.4: {} - tmpl@1.0.5: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - toidentifier@1.0.1: {} - - tough-cookie@4.1.4: - dependencies: - psl: 1.15.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 - - tr46@3.0.0: - dependencies: - punycode: 2.3.1 - trim-lines@3.0.1: {} ts-api-utils@2.0.0(typescript@5.7.3): dependencies: typescript: 5.7.3 - ts-jest@29.2.5(@babel/core@7.26.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.7))(jest@29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)))(typescript@5.7.3): - dependencies: - bs-logger: 0.2.6 - ejs: 3.1.10 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.17.16)(ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3)) - jest-util: 29.7.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.6.3 - typescript: 5.7.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.26.7 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.7) - - ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.17.16 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.7.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - tsconfck@3.1.4(typescript@5.7.3): optionalDependencies: typescript: 5.7.3 @@ -8985,8 +5635,6 @@ snapshots: tslib@2.8.1: {} - tsscmp@1.0.6: {} - tsx@4.19.2: dependencies: esbuild: 0.23.1 @@ -8998,14 +5646,7 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-detect@4.0.8: {} - - type-fest@0.21.3: {} - - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 + type-fest@0.20.2: {} typed-array-buffer@1.0.3: dependencies: @@ -9090,12 +5731,8 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 - universalify@0.2.0: {} - universalify@2.0.1: {} - unpipe@1.0.0: {} - update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: browserslist: 4.24.4 @@ -9106,28 +5743,13 @@ snapshots: dependencies: punycode: 2.3.1 - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - util-deprecate@1.0.2: {} - v8-compile-cache-lib@3.0.1: {} - - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 - validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vary@1.1.2: {} - vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 @@ -9217,27 +5839,6 @@ snapshots: optionalDependencies: typescript: 5.7.3 - w3c-xmlserializer@4.0.0: - dependencies: - xml-name-validator: 4.0.0 - - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - - webidl-conversions@7.0.0: {} - - whatwg-encoding@2.0.0: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@3.0.0: {} - - whatwg-url@11.0.0: - dependencies: - tr46: 3.0.0 - webidl-conversions: 7.0.0 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -9288,12 +5889,6 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -9302,19 +5897,6 @@ snapshots: wrappy@1.0.2: {} - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - - ws@7.5.10: {} - - ws@8.18.0: {} - - xml-name-validator@4.0.0: {} - - xmlchars@2.2.0: {} - y18n@5.0.8: {} yallist@3.1.1: {} @@ -9333,10 +5915,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - ylru@1.4.0: {} - - yn@3.1.1: {} - yocto-queue@0.1.0: {} zwitch@2.0.4: {} diff --git a/tsconfig.json b/tsconfig.json index 88416472..4f9bbc21 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -59,9 +59,9 @@ "@tapsioss/theme": [ "./packages/theme/dist/index.css", "./packages/theme/src/index.css" - ], + ] } }, - "include": ["**/*.ts", "**/*.tsx"], + "include": ["**/*.ts", "**/*.tsx", "docs/.vitepress/**/*.ts"], "exclude": ["node_modules", "dist"] }