diff --git a/.eslintrc.json b/.eslintrc.json index 115eb698..6a27cb6a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,8 +11,7 @@ "env": { "es6": true, "node": true, - "browser": true, - "cypress/globals": true + "browser": true }, "parserOptions": { "sourceType": "module", @@ -65,6 +64,7 @@ } ], "lines-between-class-members": "off", + "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-namespace": "off", "react/jsx-filename-extension": [0], "import/extensions": [ @@ -100,16 +100,6 @@ } ] } - }, - { - "files": ["cypress/**"], - "plugins": ["cypress"], - "rules": { - "no-unused-vars": "warn" - }, - "env": { - "cypress/globals": true - } } ], "settings": { diff --git a/.github/workflows/browsers.yml b/.github/workflows/browsers.yml deleted file mode 100644 index 4b2fe776..00000000 --- a/.github/workflows/browsers.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: Browsers - -on: - pull_request: - paths: - - 'src/**' - - 'package-lock.json' - - '.browserslistrc' - - '.babelrc' - - 'webpack.config.*' - - 'public/index.html' - - '.github/actions-scripts/__snapshots__/**' - - '.github/workflows/browsers.yml' - -jobs: - selenium: - strategy: - fail-fast: false - matrix: - os: [windows-latest, macos-latest] - browser: [edge, firefox, safari, chrome] - exclude: - - os: windows-latest - browser: safari - - os: macos-latest - browser: edge - - os: macos-latest - browser: chrome - # Safari workaround is not working in Catalina - - browser: safari - - runs-on: ${{ matrix.os }} - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Cache node modules - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.OS }}-build-${{ matrix.browser }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - - run: | - npm ci --no-audit - npm run build - env: - CYPRESS_INSTALL_BINARY: 0 - HUSKY_SKIP_INSTALL: true - - # install drivers - - name: Enable Safari Driver - run: | - # Workaround for `sudo safardriver --enable` not working: - # https://github.com/web-platform-tests/wpt/issues/19845 - # https://github.com/web-platform-tests/wpt/blob/master/tools/ci/azure/install_safari.yml - mkdir -p ~/Library/WebDriver/ - curl https://raw.githubusercontent.com/web-platform-tests/wpt/master/tools/ci/azure/com.apple.Safari.plist -o ~/Library/WebDriver/com.apple.Safari.plist - defaults write com.apple.Safari WebKitJavaScriptCanOpenWindowsAutomatically 1 - # sudo safaridriver --enable - if: matrix.browser == 'safari' - - - run: | - brew install --cask firefox - brew install geckodriver - if: matrix.browser == 'firefox' && matrix.os == 'macos-latest' - - - run: echo "$env:GeckoWebDriver" >> $GITHUB_PATH - if: matrix.browser == 'firefox' && matrix.os == 'windows-latest' - - - run: echo "$env:EdgeWebDriver" >> $GITHUB_PATH - if: matrix.browser == 'edge' && matrix.os == 'windows-latest' - - - run: echo "$env:ChromeWebDriver" >> $GITHUB_PATH - if: matrix.browser == 'chrome' && matrix.os == 'windows-latest' - - - run: npm i --no-optional --no-audit selenium-webdriver pixelmatch@5.3.0 pngjs - - run: node .github/actions-scripts/selenium.cjs - env: - BROWSER: ${{ matrix.browser }} - PORT: 0 - NODE_ENV: production # prevent watching - - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: screenshot-${{ matrix.browser }}-${{ matrix.os }} - path: screenshot - - puppeteer: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - name: Cache node modules - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.OS }}-build-puppeteer - restore-keys: | - ${{ runner.OS }}-build-puppeteer - - run: | - npm ci --no-audit - npm run build - env: - CYPRESS_INSTALL_BINARY: 0 - HUSKY_SKIP_INSTALL: true - - run: npm i --no-optional --no-audit puppeteer@23.0.1 pixelmatch@5.3.0 pngjs@6.0.0 - - run: node .github/actions-scripts/puppeteer.cjs - env: - PORT: 0 - NODE_ENV: production # prevent watching - - - uses: actions/upload-artifact@v4 - if: failure() - with: - name: screenshot-puppeteer-darwin - path: screenshot diff --git a/.github/workflows/bundlesize.yml b/.github/workflows/bundlesize.yml index d718931b..e5d901af 100644 --- a/.github/workflows/bundlesize.yml +++ b/.github/workflows/bundlesize.yml @@ -25,7 +25,6 @@ jobs: npm ci npm run build env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true # we don't need to build here, as even minized assets expected to be commited diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 455b534f..c69e71b6 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -3,8 +3,8 @@ name: Deploy Pages on: release: types: [published] - workflow_dispatch: - + workflow_dispatch: + jobs: deploy-gh-pages: runs-on: ubuntu-latest @@ -22,7 +22,6 @@ jobs: npm run build rm -rf public/test env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - name: Deploy uses: peaceiris/actions-gh-pages@v3 diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index bec38f47..18ee93fc 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -17,7 +17,6 @@ jobs: registry-url: https://registry.npmjs.org/ - run: npm ci env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - run: npm publish env: diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml deleted file mode 100644 index 1da01bce..00000000 --- a/.github/workflows/e2e-tests.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: End-to-end tests - -on: - pull_request: - paths: - - 'src/**' - - 'package-lock.json' - - '.browserslistrc' - - '.babelrc' - - 'webpack.config.*' - - 'public/test/**' - - 'cypress/**' - - '.github/workflows/e2e-tests.yml' - -jobs: - test-e2e: - runs-on: ubuntu-latest - env: - CI: true - TERM: xterm-256color - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Cache node modules - uses: actions/cache@v2 - with: - path: ~/.npm - key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - - name: Get Cypress info - id: cypress-info - run: | - echo ::set-output name=version::$(jq -r .devDependencies.cypress ./package.json) - echo ::set-output name=cache::$(npm exec cypress cache path) - env: - CYPRESS_INSTALL_BINARY: 0 - - name: Cache Cypress cache - uses: actions/cache@v2 - with: - path: ${{ steps.cypress-info.outputs.cache }} - key: ${{ runner.OS }}-cypress-${{ steps.cypress-info.outputs.version }} - restore-keys: | - ${{ runner.OS }}-cypress-${{ steps.cypress-info.outputs.version }} - - - name: Install dependencies - run: npm ci --no-audit - env: - HUSKY_SKIP_INSTALL: true - - - name: run Cypress (with or without recording) - # if we have ran out of free Cypress recordings, run Cypress with recording switched off - run: npm exec -- run-p --race start cypress:ci || npm exec -- run-p --race start cypress:run - env: - NODE_ENV: production # prevent watching - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - DEBUG: commit-info,cypress:server:record - # https://docs.cypress.io/guides/guides/continuous-integration.html#Environment-variables - COMMIT_INFO_BRANCH: ${{ github.head_ref }} - COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }} - COMMIT_INFO_SHA: ${{ github.event.after }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cd4d4d8f..f263e5de 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,7 +23,6 @@ jobs: - name: Install dependencies run: npm ci env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - name: run eslint diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..c8589879 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,59 @@ +name: Playwright Tests +on: + push: + branches: [ playwright ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest, ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Cache node modules + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.OS }}-build + restore-keys: | + ${{ runner.OS }}-build- + ${{ runner.OS }}- + - name: Install dependencies + run: npm ci --no-audit + + - name: Get installed Playwright version + id: playwright-version + run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package-lock.json').dependencies['@playwright/test'].version)")" >> $GITHUB_ENV + - name: Cache playwright binaries + uses: actions/cache@v3 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }} + - name: Install Playwright Browsers + run: npx playwright install --with-deps + if: steps.playwright-cache.outputs.cache-hit != 'true' + - run: npx playwright install-deps + if: steps.playwright-cache.outputs.cache-hit != 'true' + + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: screenshot-${{ matrix.os }} + path: screenshot + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 013d2c07..7b57f2b4 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -22,7 +22,6 @@ jobs: - name: Install dependencies run: npm ci --no-audit env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - run: npm run build diff --git a/.gitignore b/.gitignore index 7ae0f10e..91d3a6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,7 @@ tests/reports tests/results .nyc_output coverage -cypress/videos -cypress/screenshots +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 6dba3119..7592f253 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,35 +12,5 @@ "webpack://Choices/*": "${workspaceFolder}/*" } }, - { - "type": "node", - "request": "launch", - "name": "Cypress Current File", - "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/cypress", - "windows": { - "runtimeExecutable": "${workspaceFolder}\\node_modules\\.bin\\cypress.cmd" - }, - "runtimeArgs": [ - "run", - "--headed", - "--no-exit", - "--browser=electron", - "--port", - "9898", - "--spec" - ], - "protocol": "legacy", - "port": 9898, - "program": "${file}", - "console": "integratedTerminal", - "preLaunchTask": "buildAndWatch", - "internalConsoleOptions": "openOnSessionStart", - "timeout": 999999999999999, - "autoAttachChildProcesses": false, - "env": { - "NODE_ENV": "test" - // "DEBUG": "cypress:*" - } - } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 3b7eada7..5a669ecb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -38,11 +38,6 @@ "npm.fetchOnlinePackageInfo": true, "eslint.packageManager": "npm", "json.schemas": [ - // Cypress related settings - https://docs.cypress.io/guides/tooling/intelligent-code-completion.html#Features-1 - { - "fileMatch": ["cypress.json"], - "url": "https://on.cypress.io/cypress.schema.json" - }, // Husky config file { "fileMatch": [".huskyrc"], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5014b08f..aa3cc208 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -78,10 +78,5 @@ "script": "test:unit", "group": "test" }, - { - "type": "npm", - "script": "cypress:open", - "isBackground": true - } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 97833752..fd5b8cfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ * Restructure end-to-end tests so html/script blocks are co-located to improve debugability * Enable `@typescript-eslint/explicit-function-return-type` eslint rule +### Chore +* Switch e2e tests from `puppeteer`/`selenium`/`cypress` `playwright` + ## [11.0.0-rc6] (2024-08-12) ### Features diff --git a/README.md b/README.md index 558890e2..c27f2540 100644 --- a/README.md +++ b/README.md @@ -1229,17 +1229,27 @@ To setup a local environment: clone this repo, navigate into its directory in a `npm install` +### playwright + +e2e (End-to-end) tests are implemented using playwright, which requires installing likely with OS support. + +`npx playwright install` +`npx playwright install-deps ` + +For JetBrain IDE's the `Test automation` plugin is recommended: +https://www.jetbrains.com/help/phpstorm/playwright.html + ### NPM tasks | Task | Usage | -| ------------------------- | ------------------------------------------------------------ | +|---------------------------|--------------------------------------------------------------| | `npm run start` | Fire up local server for development | | `npm run test:unit` | Run sequence of tests once | | `npm run test:unit:watch` | Fire up test server and re-test on file change | | `npm run test:e2e` | Run sequence of e2e tests (with local server) | | `npm run test` | Run both unit and e2e tests | -| `npm run cypress:open` | Run Cypress e2e tests (GUI) | -| `npm run cypress:run` | Run Cypress e2e tests (CLI) | +| `npm run playwright:gui` | Run Playwright e2e tests (GUI) | +| `npm run playwright:cli` | Run Playwright e2e tests (CLI) | | `npm run js:build` | Compile Choices to an uglified JavaScript file | | `npm run css:watch` | Watch SCSS files for changes. On a change, run build process | | `npm run css:build` | Compile, minify and prefix SCSS files to CSS | diff --git a/cypress/cypress.config.ts b/cypress/cypress.config.ts deleted file mode 100644 index 43bb0078..00000000 --- a/cypress/cypress.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from 'cypress'; - -export default defineConfig({ - video: false, - projectId: 'n7g5qp', - e2e: { - baseUrl: 'http://localhost:3001/test', - specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}', - }, -}); diff --git a/cypress/e2e/select-multiple.spec.ts b/cypress/e2e/select-multiple.spec.ts deleted file mode 100644 index 3f95cac4..00000000 --- a/cypress/e2e/select-multiple.spec.ts +++ /dev/null @@ -1,1014 +0,0 @@ -describe('Choices - select multiple', () => { - beforeEach(() => { - cy.visit('/select-multiple', { - onBeforeLoad(win) { - cy.stub(win.console, 'warn').as('consoleWarn'); - }, - }); - }); - - describe('scenarios', () => { - describe('basic setup', () => { - beforeEach(() => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .focus(); - }); - - describe('focusing on text input', () => { - it('displays a dropdown of choices', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 4) - .each(($choice, index) => { - expect($choice.text().trim()).to.equal(`Choice ${index + 1}`); - }); - }); - - describe('pressing escape', () => { - beforeEach(() => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .wait(200) // Otherwise these tests are flaky - .type('{esc}'); - }); - - it('closes the dropdown', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('not.be.visible'); - }); - - describe('typing more into the input', () => { - it('re-opens the dropdown', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('test'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible'); - }); - }); - }); - }); - - describe('selecting choices', () => { - describe('selecting a single choice', () => { - let selectedChoiceText; - - beforeEach(() => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .then(($choice) => { - selectedChoiceText = $choice.text().trim(); - }) - .click(); - }); - - it('allows selecting choices from dropdown', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($item) => { - expect($item).to.contain(selectedChoiceText); - }); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input[hidden]') - .should(($select) => { - expect($select.val()).to.contain(selectedChoiceText); - }); - }); - - it('removes selected choice from dropdown list', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .each(($choice) => { - expect($choice.text().trim()).to.not.equal(selectedChoiceText); - }); - }); - }); - - describe('selecting all available choices', () => { - beforeEach(() => { - for (let index = 0; index <= 4; index++) { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .click(); - } - }); - - it('displays "no choices to choose" prompt', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal('No choices to choose from'); - }); - }); - }); - }); - - describe('removing choices', () => { - let removedChoiceText; - - beforeEach(() => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .last() - .then(($choice) => { - removedChoiceText = $choice.text().trim(); - }) - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('{backspace}'); - }); - - describe('on backspace', () => { - it('removes last choice', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--multiple') - .children() - .should('have.length', 0); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input[hidden]') - .should(($select) => { - const val = $select.val() || []; - expect(val).to.not.contain(removedChoiceText); - }); - }); - }); - }); - - describe('searching choices', () => { - describe('on input', () => { - describe('searching by label', () => { - it('displays choices filtered by inputted value', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('item 2'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 2'); - }); - }); - }); - - describe('searching by value', () => { - it('displays choices filtered by inputted value', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('find me'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 3'); - }); - }); - }); - - describe('no results found', () => { - it('displays "no results found" prompt', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('faergge'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal('No results found'); - }); - }); - }); - }); - }); - - describe('disabling', () => { - describe('on disable', () => { - it('disables the search input', () => { - cy.get('[data-test-hook=basic]') - .find('button.disable') - .focus() - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - }); - }); - - describe('enabling', () => { - describe('on enable', () => { - it('enables the search input', () => { - cy.get('[data-test-hook=basic]') - .find('button.enable') - .focus() - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .should('not.be.disabled'); - }); - }); - }); - }); - - describe('remove button', () => { - /* - { - removeItemButton: true, - }; - */ - beforeEach(() => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--dropdown .choices__list') - .children() - .last() - .click(); - }); - - describe('on click', () => { - it('removes respective choice', () => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--multiple .choices__item') - .last() - .find('.choices__button') - .focus() - .click(); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--multiple') - .children() - .should('have.length', 0); - }); - }); - }); - - describe('disabled choice', () => { - describe('selecting a disabled choice', () => { - beforeEach(() => { - cy.get('[data-test-hook=disabled-choice]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=disabled-choice]') - .find('.choices__list--dropdown .choices__item--disabled') - .click(); - }); - - it('does not select choice', () => { - cy.get('[data-test-hook=disabled-choice]') - .find('.choices__list--multiple .choices__item') - .should('have.length', 0); - }); - - it('keeps choice dropdown open', () => { - cy.get('[data-test-hook=disabled-choice]') - .find('.choices__list--dropdown') - .should('be.visible'); - }); - }); - }); - - describe('adding items disabled', () => { - /* - { - addItems: false, - } - */ - it('disables the search input', () => { - cy.get('[data-test-hook=add-items-disabled]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - - describe('on click', () => { - it('does not open choice dropdown', () => { - cy.get('[data-test-hook=add-items-disabled]') - .find('.choices') - .click() - .find('.choices__list--dropdown') - .should('not.have.class', 'is-active'); - }); - }); - }); - - describe('disabled via attribute', () => { - it('disables the search input', () => { - cy.get('[data-test-hook=disabled-via-attr]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - - describe('on click', () => { - it('does not open choice dropdown', () => { - cy.get('[data-test-hook=disabled-via-attr]') - .find('.choices') - .click() - .find('.choices__list--dropdown') - .should('not.have.class', 'is-active'); - }); - }); - }); - - describe('selection limit', () => { - /* - { - maxItemCount: 5, - } - */ - const selectionLimit = 5; - - beforeEach(() => { - for (let index = 0; index < selectionLimit; index++) { - cy.get('[data-test-hook=selection-limit]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=selection-limit]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .click(); - } - }); - - it('displays "limit reached" prompt', () => { - cy.get('[data-test-hook=selection-limit]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal( - `Only ${selectionLimit} values can be added`, - ); - }); - }); - }); - - describe('prepend/append', () => { - /* - { - prependValue: 'before-', - appendValue: '-after', - } - */ - let selectedChoiceText; - - beforeEach(() => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--dropdown .choices__list') - .children() - .last() - .then(($choice) => { - selectedChoiceText = $choice.text().trim(); - }) - .click(); - }); - - it('prepends and appends value to inputted value', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.data('value')).to.equal( - `before-${selectedChoiceText}-after`, - ); - }); - }); - - it('displays just the inputted value to the user', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.text()).to.not.contain( - `before-${selectedChoiceText}-after`, - ); - expect($choice.text()).to.contain(selectedChoiceText); - }); - }); - }); - - describe('render choice limit', () => { - /* - { - renderChoiceLimit: 1, - } - */ - it('only displays given number of choices in the dropdown', () => { - cy.get('[data-test-hook=render-choice-limit]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 1); - }); - }); - - describe('search floor', () => { - /* - { - searchFloor: 10, - } - */ - describe('on input', () => { - describe('search floor not reached', () => { - it('displays choices not filtered by inputted value', () => { - const searchTerm = 'item 2'; - - cy.get('[data-test-hook=search-floor]') - .find('.choices__input--cloned') - .type(searchTerm); - - cy.get('[data-test-hook=search-floor]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.not.contain(searchTerm); - }); - }); - }); - - describe('search floor reached', () => { - it('displays choices filtered by inputted value', () => { - const searchTerm = 'Choice 2'; - - cy.get('[data-test-hook=search-floor]') - .find('.choices__input--cloned') - .type(searchTerm); - - cy.get('[data-test-hook=search-floor]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.contain(searchTerm); - }); - }); - }); - }); - }); - - describe('placeholder via empty option value', () => { - describe('when no value has been inputted', () => { - it('displays a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__input--cloned') - .should('have.attr', 'placeholder', 'I am a placeholder'); - }); - }); - - describe('when a value has been inputted', () => { - it('does not display a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__input--cloned') - .type('test') - .should('not.have.value', 'I am a placeholder'); - }); - }); - }); - - describe('placeholder via option attribute', () => { - describe('when no value has been inputted', () => { - it('displays a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__input--cloned') - .should('have.attr', 'placeholder', 'I am a placeholder'); - }); - }); - - describe('when a value has been inputted', () => { - it('does not display a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__input--cloned') - .type('test') - .should('not.have.value', 'I am a placeholder'); - }); - }); - }); - - describe('remote data', () => { - beforeEach(() => { - cy.reload(true); - }); - - describe('when loading data', () => { - it('shows a loading message as a placeholder', () => { - cy.get('[data-test-hook=remote-data]') - .find('.choices__input--cloned') - .should('have.attr', 'placeholder', 'Loading...'); - }); - - describe('on click', () => { - it('does not open choice dropdown', () => { - cy.get('[data-test-hook=remote-data]') - .find('.choices') - .click() - .find('.choices__list--dropdown') - .should('not.have.class', 'is-active'); - }); - }); - }); - - describe('when data has loaded', () => { - describe('opening the dropdown', () => { - it('displays the loaded data', () => { - cy.wait(1000); - cy.get('[data-test-hook=remote-data]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 50) - .each(($choice, index) => { - expect($choice.text().trim()).to.equal(`Label ${index + 1}`); - expect($choice.data('value')).to.equal(`Value ${index + 1}`); - }); - }); - }); - }); - }); - - describe('dropdown scrolling', () => { - let choicesCount; - - beforeEach(() => { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .choices__item') - .then(($choices) => { - choicesCount = $choices.length; - }); - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .focus(); - }); - - it('highlights first choice on dropdown open', () => { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .is-highlighted') - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 1'); - }); - }); - - it('scrolls to next choice on down arrow', () => { - for (let index = 1; index <= choicesCount; index++) { - cy.wait(100); - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .is-highlighted') - .invoke('text') - .then((text) => { - expect(text.trim()).to.equal(`Choice ${index}`); - }); - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .type('{downarrow}'); - } - }); - - it('scrolls up to previous choice on up arrow', () => { - // scroll to last choice - for (let index = 0; index < choicesCount; index++) { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .type('{downarrow}'); - } - - // scroll up to first choice - for (let index = choicesCount; index > 0; index--) { - cy.wait(100); // allow for dropdown animation to finish - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .is-highlighted') - .invoke('text') - .then((text) => { - expect(text.trim()).to.equal(`Choice ${index}`); - }); - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .type('{uparrow}'); - } - }); - }); - - describe('choice groups', () => { - const choicesInGroup = 3; - let groupValue; - - beforeEach(() => { - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__group') - .first() - .then(($group) => { - groupValue = $group.text().trim(); - }); - }); - - describe('selecting all choices in group', () => { - it('removes group from dropdown', () => { - for (let index = 0; index < choicesInGroup; index++) { - cy.get('[data-test-hook=groups]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__item') - .first() - .click(); - } - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__group') - .first() - .should(($group) => { - expect($group.text().trim()).to.not.equal(groupValue); - }); - }); - }); - - describe('deselecting all choices in group', () => { - beforeEach(() => { - for (let index = 0; index < choicesInGroup; index++) { - cy.get('[data-test-hook=groups]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__item') - .first() - .click(); - } - }); - - it('shows group in dropdown', () => { - for (let index = 0; index < choicesInGroup; index++) { - cy.get('[data-test-hook=groups]') - .find('.choices__input--cloned') - .focus() - .type('{backspace}'); - } - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__group') - .first() - .should(($group) => { - expect($group.text().trim()).to.equal(groupValue); - }); - }); - }); - }); - - describe('some options with a group and some without', () => { - it('Shows all options, whether they are in a group or not', () => { - cy.get('[data-test-hook=mixed-groups]') - .find('.choices__input--cloned') - .focus(); - cy.get('[data-test-hook=mixed-groups]') - .find('.choices__list--dropdown .choices__item') - .should(($choices) => { - expect($choices.length).to.equal(3); - }); - }); - }); - - describe('custom properties', () => { - beforeEach(() => { - cy.get('[data-test-hook=custom-properties]') - .find('.choices__input--cloned') - .focus(); - }); - - describe('on input', () => { - it('filters choices based on custom property', () => { - const data = [ - { - country: 'Germany', - city: 'Berlin', - }, - { - country: 'United Kingdom', - city: 'London', - }, - { - country: 'Portugal', - city: 'Lisbon', - }, - ]; - - data.forEach(({ country, city }) => { - cy.get('[data-test-hook=custom-properties]') - .find('.choices__input--cloned') - .type(country); - - cy.get('[data-test-hook=custom-properties]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal(city); - }); - - cy.get('[data-test-hook=custom-properties]') - .find('.choices__input--cloned') - .type('{selectall}{del}'); - }); - }); - }); - }); - - describe('custom properties via HTML', () => { - beforeEach(() => { - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices') - .click(); - }); - - describe('on input', () => { - it('filters choices based on a string custom property', () => { - const data = [ - { - searchText: 'fantastic', - label: 'Label Three', - }, - ]; - - data.forEach(({ searchText, label }) => { - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type(searchText); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal(label); - }); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type('{selectall}{del}'); - }); - }); - - it('filters choices based on a JSON custom property', () => { - const data = [ - { - searchText: 'foo', - label: 'Label Four', - }, - ]; - - data.forEach(({ searchText, label }) => { - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type(searchText); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal(label); - }); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type('{selectall}{del}'); - }); - }); - }); - }); - - describe('non-string values', () => { - beforeEach(() => { - cy.get('[data-test-hook=non-string-values]').find('.choices').click(); - }); - - it('displays expected amount of choices in dropdown', () => { - cy.get('[data-test-hook=non-string-values]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 4); - }); - - it('allows selecting choices from dropdown', () => { - let $selectedChoice; - cy.get('[data-test-hook=non-string-values]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .then(($choice) => { - $selectedChoice = $choice; - }) - .click(); - - cy.get('[data-test-hook=non-string-values]') - .find('.choices__list--single .choices__item') - .last() - .should(($item) => { - expect($item.text().trim()).to.equal($selectedChoice.text().trim()); - }); - }); - }); - - describe('within form', () => { - describe('selecting choice', () => { - describe('on enter key', () => { - it('selects choice', () => { - cy.get('[data-test-hook=within-form] form').then(($form) => { - $form.submit(() => { - // this will fail the test if the form submits - throw new Error('Form submitted'); - }); - }); - - cy.get('[data-test-hook=within-form]') - .find('.choices__input--cloned') - .click() - .type('{enter}'); - - cy.get('[data-test-hook=within-form]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($item) => { - expect($item).to.contain('Choice 1'); - }); - }); - }); - }); - }); - - describe('dynamically setting choice by value', () => { - const dynamicallySelectedChoiceValue = 'Choice 2'; - - it('selects choice', () => { - cy.get('[data-test-hook=set-choice-by-value]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.text().trim()).to.equal( - dynamicallySelectedChoiceValue, - ); - }); - }); - - it('removes choice from dropdown list', () => { - cy.get('[data-test-hook=set-choice-by-value]') - .find('.choices__list--dropdown .choices__list') - .children() - .each(($choice) => { - expect($choice.text().trim()).to.not.equal( - dynamicallySelectedChoiceValue, - ); - }); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=set-choice-by-value]') - .find('.choices__input[hidden]') - .should(($select) => { - const val = $select.val() || []; - expect(val).to.contain(dynamicallySelectedChoiceValue); - }); - }); - }); - - describe('searching by label only', () => { - it('gets zero results when searching by value', () => { - cy.get('[data-test-hook=search-by-label]') - .find('.choices__input--cloned') - .type('value1'); - - cy.get('[data-test-hook=search-by-label]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('No results found'); - }); - }); - - it('gets a result when searching by label', () => { - cy.get('[data-test-hook=search-by-label]') - .find('.choices__input--cloned') - .type('label1'); - - cy.get('[data-test-hook=search-by-label]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('label1'); - }); - }); - }); - - describe('allow html', () => { - describe('set to true', () => { - it('does not show as text when selected', () => { - cy.get('[data-test-hook=allowhtml-true]') - .find('.choices__list--multiple .choices__item') - - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 1'); - }); - }); - - it('does not show html as text in dropdown', () => { - cy.get('[data-test-hook=allowhtml-true]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 2'); - }); - }); - }); - - describe('set to false', () => { - it('shows html as text when selected', () => { - cy.get('[data-test-hook=allowhtml-false]') - .find('.choices__list--multiple .choices__item') - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 1'); - }); - }); - - it('shows html as text', () => { - cy.get('[data-test-hook=allowhtml-false]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 2'); - }); - }); - }); - - describe('adding user-created choices', () => { - it('allows the user to add choices', () => { - const newChoice = 'New Choice'; - - cy.get('[data-test-hook=add-choices]') - .find('.choices__input--cloned') - .type(newChoice) - .type('{enter}'); - - cy.get('[data-test-hook=add-choices]') - .find('.choices__list--multiple') - .last() - .should($el => { - expect($el).to.contain(newChoice); - }); - }); - }); - }); - }); -}); diff --git a/cypress/e2e/select-one.spec.ts b/cypress/e2e/select-one.spec.ts deleted file mode 100644 index dedd20e0..00000000 --- a/cypress/e2e/select-one.spec.ts +++ /dev/null @@ -1,1151 +0,0 @@ -describe('Choices - select one', () => { - beforeEach(() => { - cy.visit('/select-one', { - onBeforeLoad(win) { - cy.stub(win.console, 'warn').as('consoleWarn'); - }, - }); - }); - - describe('scenarios', () => { - describe('basic', () => { - describe('focusing on container', () => { - describe('pressing enter key', () => { - it('toggles the dropdown', () => { - cy.get('[data-test-hook=basic]') - .find('.choices') - .focus() - .type('{enter}'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible'); - - cy.get('[data-test-hook=basic]') - .find('.choices') - .focus() - .type('{enter}'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('not.be.visible'); - }); - }); - - describe('pressing an alpha-numeric key', () => { - it('opens the dropdown and the input value', () => { - const inputValue = 'test'; - - cy.get('[data-test-hook=basic]') - .find('.choices') - .focus() - .type(inputValue); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible'); - - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .should('have.value', inputValue); - }); - }); - }); - - describe('selecting choices', () => { - beforeEach(() => { - // open dropdown - cy.get('[data-test-hook=basic]').find('.choices').click(); - }); - - const selectedChoiceText = 'Choice 1'; - - it('allows selecting choices from dropdown', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--single .choices__item') - .last() - .should(($item) => { - expect($item).to.contain(selectedChoiceText); - }); - }); - - it('does not remove selected choice from dropdown list', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($item) => { - expect($item).to.contain(selectedChoiceText); - }); - }); - }); - - describe('searching choices', () => { - beforeEach(() => { - // open dropdown - cy.get('[data-test-hook=basic]').find('.choices').click(); - }); - - describe('on input', () => { - describe('searching by label', () => { - it('displays choices filtered by inputted value', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('item 2'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 2'); - }); - }); - }); - - describe('searching by value', () => { - it('displays choices filtered by inputted value', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('find me'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 3'); - }); - }); - }); - - describe('no results found', () => { - it('displays "no results found" prompt', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type('faergge'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal('No results found'); - }); - }); - }); - }); - }); - - describe('disabling', () => { - describe('on disable', () => { - it('disables the search input', () => { - cy.get('[data-test-hook=basic]') - .find('button.disable') - .focus() - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - }); - }); - - describe('enabling', () => { - describe('on enable', () => { - it('enables the search input', () => { - cy.get('[data-test-hook=basic]') - .find('button.enable') - .focus() - .click(); - - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .should('not.be.disabled'); - }); - }); - }); - }); - - describe('remove button', () => { - /* - { - removeItemButton: true, - } - */ - beforeEach(() => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--dropdown .choices__list') - .children() - .last() - .click(); - }); - - describe('remove button', () => { - describe('on click', () => { - let removedChoiceText; - - beforeEach(() => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--single .choices__item') - .last() - .then(($choice) => { - removedChoiceText = $choice.text().trim(); - }) - .click(); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--single .choices__item') - .last() - .find('.choices__button') - .focus() - .click(); - }); - - it('removes selected choice', () => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--single') - .children() - .should('have.length', 0); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__input[hidden]') - .should(($select) => { - const val = $select.val() || []; - - expect(val).to.not.contain(removedChoiceText); - }); - }); - }); - }); - }); - - describe('disabled choice', () => { - describe('selecting a disabled choice', () => { - let selectedChoiceText; - - beforeEach(() => { - cy.get('[data-test-hook=disabled-choice]').click(); - - cy.get('[data-test-hook=disabled-choice]') - .find('.choices__list--dropdown .choices__item--disabled') - .then(($choice) => { - selectedChoiceText = $choice.text().trim(); - }) - .click(); - }); - - it('does not change selected choice', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--single .choices__item') - .last() - .should(($choice) => { - expect($choice.text()).to.not.contain(selectedChoiceText); - }); - }); - - it('closes the dropdown list', () => { - cy.get('[data-test-hook=disabled-choice]') - .find('.choices__list--dropdown') - .should('not.be.visible'); - }); - }); - }); - - describe('adding items disabled', () => { - /* - { - addItems: false, - } - */ - it('disables the search input', () => { - cy.get('[data-test-hook=add-items-disabled]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - - describe('on click', () => { - it('does not open choice dropdown', () => { - cy.get('[data-test-hook=add-items-disabled]') - .find('.choices') - .click(); - - cy.get('[data-test-hook=add-items-disabled]') - .find('.choices__list--dropdown') - .should('not.have.class', 'is-active'); - }); - }); - }); - - describe('disabled via attribute', () => { - it('disables the search input', () => { - cy.get('[data-test-hook=disabled-via-attr]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - - describe('on click', () => { - it('does not open choice dropdown', () => { - cy.get('[data-test-hook=disabled-via-attr]').find('.choices').click(); - - cy.get('[data-test-hook=disabled-via-attr]') - .find('.choices__list--dropdown') - .should('not.have.class', 'is-active'); - }); - }); - }); - - describe('prepend/append', () => { - /* - { - prependValue: 'before-', - appendValue: '-after', - }; - */ - - let selectedChoiceText; - - beforeEach(() => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--dropdown .choices__list') - .children() - .last() - .then(($choice) => { - selectedChoiceText = $choice.text().trim(); - }) - .click(); - }); - - it('prepends and appends value to inputted value', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--single .choices__item') - .last() - .should(($choice) => { - expect($choice.data('value')).to.equal( - `before-${selectedChoiceText}-after`, - ); - }); - }); - - it('displays just the inputted value to the user', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--single .choices__item') - .last() - .should(($choice) => { - expect($choice.text()).to.not.contain( - `before-${selectedChoiceText}-after`, - ); - expect($choice.text()).to.contain(selectedChoiceText); - }); - }); - }); - - describe('render choice limit', () => { - /* - { - renderChoiceLimit: 1 - } - */ - - it('only displays given number of choices in the dropdown', () => { - cy.get('[data-test-hook=render-choice-limit]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 1); - }); - }); - - describe('search disabled', () => { - /* - { - searchEnabled: false - } - */ - const selectedChoiceText = 'Choice 3'; - - beforeEach(() => { - cy.get('[data-test-hook=search-disabled]').find('.choices').click(); - }); - - it('does not display a search input', () => { - cy.get('[data-test-hook=search-disabled]') - .find('.choices__input--cloned') - .should('not.exist'); - }); - - it('allows selecting choices from dropdown', () => { - cy.get('[data-test-hook=search-disabled]') - .find('.choices__list--dropdown .choices__list') - .children() - .last() - .click(); - - cy.get('[data-test-hook=search-disabled]') - .find('.choices__list--single .choices__item') - .last() - .should(($item) => { - expect($item).to.contain(selectedChoiceText); - }); - }); - }); - - describe('search floor', () => { - /* - { - searchFloor: 10, - }; - */ - - describe('on input', () => { - beforeEach(() => { - cy.get('[data-test-hook=search-floor]') - .find('.choices__input--cloned') - .focus(); - }); - - describe('search floor not reached', () => { - it('displays choices not filtered by inputted value', () => { - const searchTerm = 'item 2'; - - cy.get('[data-test-hook=search-floor]') - .find('.choices__input--cloned') - .type(searchTerm); - - cy.get('[data-test-hook=search-floor]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.not.contain(searchTerm); - }); - }); - }); - - describe('search floor reached', () => { - it('displays choices filtered by inputted value', () => { - const searchTerm = 'Choice 2'; - - cy.get('[data-test-hook=search-floor]') - .find('.choices__input--cloned') - .type(searchTerm); - - cy.get('[data-test-hook=search-floor]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.contain(searchTerm); - }); - }); - }); - }); - }); - - describe('placeholder via empty option value', () => { - describe('when no choice has been selected', () => { - it('displays a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__list--single') - .children() - .first() - .should('have.class', 'choices__placeholder') - .and(($placeholder) => { - expect($placeholder).to.contain('I am a placeholder'); - }); - }); - }); - - describe('when a choice has been selected', () => { - it('does not display a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .click(); - - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__input--cloned') - .should('not.have.value', 'I am a placeholder'); - }); - }); - - describe('when choice list is open', () => { - it('displays the placeholder choice first', () => { - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=placeholder-via-option-value]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should('have.class', 'choices__placeholder') - .should('have.text', 'I am a placeholder'); - }); - }); - }); - - describe('placeholder via option attribute', () => { - describe('when no choice has been selected', () => { - it('displays a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__list--single') - .children() - .first() - .should('have.class', 'choices__placeholder') - .and(($placeholder) => { - expect($placeholder).to.contain('I am a placeholder'); - }); - }); - }); - - describe('when a choice has been selected', () => { - it('does not display a placeholder', () => { - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .click(); - - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__input--cloned') - .should('not.have.value', 'I am a placeholder'); - }); - }); - - describe('when choice list is open', () => { - it('displays the placeholder choice first', () => { - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=placeholder-via-option-attr]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should('have.class', 'choices__placeholder') - .should('have.text', 'I am a placeholder'); - }); - }); - }); - - describe('remote data', () => { - beforeEach(() => { - cy.reload(true); - }); - - describe('when loading data', () => { - it('shows a loading message as a placeholder', () => { - cy.get('[data-test-hook=remote-data]') - .find('.choices__list--single') - .children() - .should('have.length', 1) - .first() - .should('have.class', 'choices__placeholder') - .and(($placeholder) => { - expect($placeholder).to.contain('Loading...'); - }); - }); - - describe('on click', () => { - it('does not open choice dropdown', () => { - cy.get('[data-test-hook=remote-data]') - .find('.choices') - .click() - .find('.choices__list--dropdown') - .should('not.have.class', 'is-active'); - }); - }); - }); - - describe('when data has loaded', () => { - describe('opening the dropdown', () => { - it('displays the loaded data', () => { - cy.wait(1000); - cy.get('[data-test-hook=remote-data]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 51) // 50 choices + 1 placeholder choice - .each(($choice, index) => { - if (index === 0) { - expect($choice.text().trim()).to.equal('I am a placeholder'); - } else { - expect($choice.text().trim()).to.equal(`Label ${index}`); - expect($choice.data('value')).to.equal(`Value ${index}`); - } - }); - }); - }); - }); - }); - - describe('scrolling dropdown', () => { - let choicesCount; - - beforeEach(() => { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .choices__item') - .then(($choices) => { - choicesCount = $choices.length; - }); - - cy.get('[data-test-hook=scrolling-dropdown]').find('.choices').click(); - }); - - it('highlights first choice on dropdown open', () => { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .is-highlighted') - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 1'); - }); - }); - - it('scrolls to next choice on down arrow', () => { - for (let index = 0; index < choicesCount; index++) { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .is-highlighted') - .should(($choice) => { - expect($choice.text().trim()).to.equal(`Choice ${index + 1}`); - }); - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .type('{downarrow}'); - } - }); - - it('scrolls up to previous choice on up arrow', () => { - // scroll to last choice - for (let index = 0; index < choicesCount; index++) { - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .type('{downarrow}'); - } - - // scroll up to first choice - for (let index = choicesCount; index > 0; index--) { - cy.wait(100); // allow for dropdown animation to finish - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__list--dropdown .choices__list .is-highlighted') - .should(($choice) => { - expect($choice.text().trim()).to.equal(`Choice ${index}`); - }); - - cy.get('[data-test-hook=scrolling-dropdown]') - .find('.choices__input--cloned') - .type('{uparrow}'); - } - }); - }); - - describe('choice groups', () => { - const choicesInGroup = 3; - let groupValue; - - beforeEach(() => { - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__group') - .first() - .then(($group) => { - groupValue = $group.text().trim(); - }); - }); - - describe('selecting all choices in group', () => { - it('removes group from dropdown', () => { - for (let index = 0; index < choicesInGroup; index++) { - cy.get('[data-test-hook=groups]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__item') - .first() - .click(); - } - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__group') - .first() - .should(($group) => { - expect($group.text().trim()).to.not.equal(groupValue); - }); - }); - }); - - describe('deselecting all choices in group', () => { - beforeEach(() => { - for (let index = 0; index < choicesInGroup; index++) { - cy.get('[data-test-hook=groups]') - .find('.choices__input--cloned') - .focus(); - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__item') - .first() - .click(); - } - }); - - it('shows group in dropdown', () => { - for (let index = 0; index < choicesInGroup; index++) { - cy.get('[data-test-hook=groups]') - .find('.choices__input--cloned') - .focus() - .type('{backspace}'); - } - - cy.get('[data-test-hook=groups]') - .find('.choices__list--dropdown .choices__list .choices__group') - .first() - .should(($group) => { - expect($group.text().trim()).to.equal(groupValue); - }); - }); - }); - }); - - describe('parent/child', () => { - describe('selecting "Parent choice 2"', () => { - it('enables the child Choices instance', () => { - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(1) - .should('have.class', 'is-disabled'); - - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(0) - .click(); - - cy.get('[data-test-hook=parent-child]') - .find('.choices__list--dropdown .choices__list') - .children() - .eq(1) - .click(); - - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(1) - .should('not.have.class', 'is-disabled'); - }); - }); - - describe('changing selection from "Parent choice 2" to something else', () => { - it('disables the child Choices instance', () => { - // open parent instance and select second choice - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(0) - .click() - .find('.choices__list--dropdown .choices__list') - .children() - .eq(1) - .click(); - - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(1) - .should('not.have.class', 'is-disabled'); - - // open parent instance and select third choice - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(0) - .click() - .find('.choices__list--dropdown .choices__list') - .children() - .eq(2) - .click(); - - cy.get('[data-test-hook=parent-child]') - .find('.choices') - .eq(1) - .should('have.class', 'is-disabled'); - }); - }); - }); - - describe('custom properties', () => { - beforeEach(() => { - cy.get('[data-test-hook=custom-properties]').find('.choices').click(); - }); - - describe('on input', () => { - it('filters choices based on custom property', () => { - const data = [ - { - country: 'Germany', - city: 'Berlin', - }, - { - country: 'United Kingdom', - city: 'London', - }, - { - country: 'Portugal', - city: 'Lisbon', - }, - ]; - - data.forEach(({ country, city }) => { - cy.get('[data-test-hook=custom-properties]') - .find('.choices__input--cloned') - .type(country); - - cy.get('[data-test-hook=custom-properties]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal(city); - }); - - cy.get('[data-test-hook=custom-properties]') - .find('.choices__input--cloned') - .type('{selectall}{del}'); - }); - }); - }); - }); - - describe('custom properties via HTML', () => { - beforeEach(() => { - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices') - .click(); - }); - - describe('on input', () => { - it('filters choices based on a string custom property', () => { - const data = [ - { - searchText: 'fantastic', - label: 'Label Three', - }, - ]; - - data.forEach(({ searchText, label }) => { - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type(searchText); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal(label); - }); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type('{selectall}{del}'); - }); - }); - - it('filters choices based on a JSON custom property', () => { - const data = [ - { - searchText: 'foo', - label: 'Label Four', - }, - ]; - - data.forEach(({ searchText, label }) => { - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type(searchText); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal(label); - }); - - cy.get('[data-test-hook=custom-properties-html]') - .find('.choices__input--cloned') - .type('{selectall}{del}'); - }); - }); - }); - }); - - describe('non-string values', () => { - beforeEach(() => { - cy.get('[data-test-hook=non-string-values]').find('.choices').click(); - }); - - it('displays expected amount of choices in dropdown', () => { - cy.get('[data-test-hook=non-string-values]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 4); - }); - - it('allows selecting choices from dropdown', () => { - let $selectedChoice; - cy.get('[data-test-hook=non-string-values]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .then(($choice) => { - $selectedChoice = $choice; - }) - .click(); - - cy.get('[data-test-hook=non-string-values]') - .find('.choices__list--single .choices__item') - .last() - .should(($item) => { - expect($item.text().trim()).to.equal($selectedChoice.text().trim()); - }); - }); - }); - - describe('within form', () => { - describe('selecting choice', () => { - describe('on enter key', () => { - it('does not submit form', () => { - cy.get('[data-test-hook=within-form] form').then(($form) => { - $form.submit(() => { - // this will fail the test if the form submits - throw new Error('Form submitted'); - }); - }); - - cy.get('[data-test-hook=within-form]') - .find('.choices') - .click() - .find('.choices__input--cloned') - .type('{enter}'); - - cy.get('[data-test-hook=within-form]').find('.choices').click(); - - cy.get('[data-test-hook=within-form]') - .find('.choices__list--single .choices__item') - .last() - .should(($item) => { - expect($item).to.contain('Choice 1'); - }); - }); - }); - }); - }); - - describe('dynamically setting choice by value', () => { - const dynamicallySelectedChoiceValue = 'Choice 2'; - - it('selects choice', () => { - cy.get('[data-test-hook=set-choice-by-value]') - .find('.choices__list--single .choices__item') - .last() - .should(($choice) => { - expect($choice.text().trim()).to.equal( - dynamicallySelectedChoiceValue, - ); - }); - }); - - it('does not remove choice from dropdown list', () => { - cy.get('[data-test-hook=set-choice-by-value]') - .find('.choices__list--dropdown .choices__list') - .then(($choicesList) => { - expect($choicesList).to.contain(dynamicallySelectedChoiceValue); - }); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=set-choice-by-value]') - .find('.choices__input[hidden]') - .should(($select) => { - const val = $select.val() || []; - expect(val).to.contain(dynamicallySelectedChoiceValue); - }); - }); - }); - - describe('searching by label only', () => { - beforeEach(() => { - cy.get('[data-test-hook=search-by-label]').find('.choices').click(); - }); - - it('gets zero results when searching by value', () => { - cy.get('[data-test-hook=search-by-label]') - .find('.choices__input--cloned') - .type('value1'); - - cy.get('[data-test-hook=search-by-label]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('No results found'); - }); - }); - - it('gets a result when searching by label', () => { - cy.get('[data-test-hook=search-by-label]') - .find('.choices__input--cloned') - .type('label1'); - - cy.get('[data-test-hook=search-by-label]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('label1'); - }); - }); - }); - - describe('disabling first choice via options', () => { - beforeEach(() => { - cy.get('[data-test-hook=disabled-first-choice-via-options]') - .find('.choices') - .click(); - }); - - let disabledValue; - - it('disables the first choice', () => { - cy.get('[data-test-hook=disabled-first-choice-via-options]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should('have.class', 'choices__item--disabled') - .then(($choice) => { - disabledValue = $choice.val(); - }); - }); - - it('selects the first enabled choice', () => { - cy.get('[data-test-hook=disabled-first-choice-via-options]') - .find('.choices__input[hidden]') - .then(($option) => { - expect($option.text().trim()).to.not.equal(disabledValue); - }); - - cy.get('[data-test-hook=disabled-first-choice-via-options]') - .find('.choices__item.choices__item--selectable') - .first() - .should(($choice) => { - expect($choice.text().trim()).to.not.equal(disabledValue); - }); - }); - }); - - describe('allow html', () => { - describe('set to true', () => { - it('does not show html as text', () => { - cy.get('[data-test-hook=allowhtml-true]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 1'); - }); - }); - }); - - describe('set to false', () => { - it('shows html as text', () => { - cy.get('[data-test-hook=allowhtml-false]') - .find('.choices__list--dropdown .choices__list') - .children() - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Choice 1'); - }); - }); - }); - }); - - describe('re-initialising a choices instance', () => { - it('preserves the choices list', () => { - cy.get('[data-test-hook=new-destroy-init]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 3); - - cy.get('[data-test-hook=new-destroy-init]') - .find('button.destroy') - .click(); - cy.get('[data-test-hook=new-destroy-init]').find('button.init').click(); - - cy.get('[data-test-hook=new-destroy-init]') - .find('.choices__list--dropdown .choices__list') - .children() - .should('have.length', 3); - }); - }); - - describe('destroying the choices instance', () => { - it('preserves the original select element', () => { - cy.get('[data-test-hook=new-destroy-init]') - .find('button.destroy') - .click(); - - cy.get('[data-test-hook=new-destroy-init]') - .find('select') - .children() - .should('have.length', 3); - }); - }); - - describe('adding user-created choices', () => { - beforeEach(() => { - cy.get('[data-test-hook=add-choices]').find('.choices').click(); - }); - - it('allows the user to add choices', () => { - const newChoice = 'New Choice'; - - cy.get('[data-test-hook=add-choices]') - .find('.choices__input--cloned') - .type(newChoice) - .type('{enter}'); - - cy.get('[data-test-hook=add-choices]') - .find('.choices__list--single .choices__item') - .should(($el) => { - expect($el).to.contain(newChoice); - }); - }); - }); - }); -}); diff --git a/cypress/e2e/text.spec.ts b/cypress/e2e/text.spec.ts deleted file mode 100644 index 98eb6247..00000000 --- a/cypress/e2e/text.spec.ts +++ /dev/null @@ -1,413 +0,0 @@ -describe('Choices - text element', () => { - beforeEach(() => { - cy.visit('/text', { - onBeforeLoad(win) { - cy.stub(win.console, 'warn').as('consoleWarn'); - }, - }); - }); - - describe('scenarios', () => { - const textInput = 'testing'; - - describe('basic', () => { - describe('adding items', () => { - it('allows me to input items', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type(textInput) - .type('{enter}'); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($el) => { - expect($el).to.contain(textInput); - }); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type(textInput) - .type('{enter}'); - - cy.get('[data-test-hook=basic]') - .find('.choices__input[hidden]') - .should('have.value', textInput); - }); - - describe('inputting data', () => { - it('shows a dropdown prompt', () => { - cy.get('[data-test-hook=basic]') - .find('.choices__input--cloned') - .type(textInput); - - cy.get('[data-test-hook=basic]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal( - `Press Enter to add "${textInput}"`, - ); - }); - }); - }); - }); - }); - - describe('editing items', () => { - beforeEach(() => { - for (let index = 0; index < 3; index++) { - cy.get('[data-test-hook=edit-items]') - .find('.choices__input--cloned') - .type(textInput) - .type('{enter}'); - } - }); - - describe('on back space', () => { - it('allows me to change my entry', () => { - cy.get('[data-test-hook=edit-items]') - .find('.choices__input--cloned') - .type('{backspace}') - .type('-edited') - .type('{enter}'); - - cy.get('[data-test-hook=edit-items]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.data('value')).to.equal(`${textInput}-edited`); - }); - }); - }); - - describe('on cmd+a', () => { - beforeEach(() => { - cy.get('[data-test-hook=edit-items]') - .find('.choices__input--cloned') - .type('{cmd}a'); - }); - - it('highlights all items', () => { - cy.get('[data-test-hook=edit-items]') - .find('.choices__list--multiple .choices__item') - .each(($choice) => { - expect($choice.hasClass('is-highlighted')).to.equal(true); - }); - }); - - describe('on backspace', () => { - it('clears all inputted values', () => { - // two backspaces are needed as Cypress has an issue where - // it will also insert an 'a' character into the text input - cy.get('[data-test-hook=edit-items]') - .find('.choices__input--cloned') - .type('{backspace}{backspace}'); - - cy.get('[data-test-hook=edit-items]') - .find('.choices__list--multiple .choices__item') - .should('have.length', 0); - }); - }); - }); - }); - - describe('remove button', () => { - beforeEach(() => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__input--cloned') - .type(`${textInput}`) - .type('{enter}'); - }); - - describe('on click', () => { - it('removes respective choice', () => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--multiple') - .children() - .should(($items) => { - expect($items.length).to.equal(1); - }); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--multiple .choices__item') - .last() - .find('.choices__button') - .focus() - .click(); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--multiple .choices__item') - .should(($items) => { - expect($items.length).to.equal(0); - }); - }); - - it('updates the value of the original input', () => { - cy.get('[data-test-hook=remove-button]') - .find('.choices__list--multiple .choices__item') - .last() - .find('.choices__button') - .focus() - .click(); - - cy.get('[data-test-hook=remove-button]') - .find('.choices__input[hidden]') - .then(($input) => { - expect($input.val()).to.not.contain(textInput); - }); - }); - }); - }); - - describe('unique values only', () => { - describe('unique values', () => { - beforeEach(() => { - cy.get('[data-test-hook=unique-values]') - .find('.choices__input--cloned') - .type(`${textInput}`) - .type('{enter}') - .type(`${textInput}`) - .type('{enter}'); - }); - - it('only allows me to input unique values', () => { - cy.get('[data-test-hook=unique-values]') - .find('.choices__list--multiple') - .first() - .children() - .should(($items) => { - expect($items.length).to.equal(1); - }); - }); - - describe('inputting a non-unique value', () => { - it('displays dropdown prompt', () => { - cy.get('[data-test-hook=unique-values]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal( - 'Only unique values can be added', - ); - }); - }); - }); - }); - }); - - describe('input limit', () => { - const inputLimit = 5; - beforeEach(() => { - for (let index = 0; index < inputLimit + 1; index++) { - cy.get('[data-test-hook=input-limit]') - .find('.choices__input--cloned') - .type(`${textInput} + ${index}`) - .type('{enter}'); - } - }); - - it('does not let me input more than 5 choices', () => { - cy.get('[data-test-hook=input-limit]') - .find('.choices__list--multiple') - .first() - .children() - .should(($items) => { - expect($items.length).to.equal(inputLimit); - }); - }); - - describe('reaching input limit', () => { - it('displays dropdown prompt', () => { - cy.get('[data-test-hook=input-limit]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal( - `Only ${inputLimit} values can be added`, - ); - }); - }); - }); - }); - - describe('add item filter', () => { - describe('inputting a value that satisfies the filter', () => { - const input = 'joe@bloggs.com'; - - it('allows me to add choice', () => { - cy.get('[data-test-hook=add-item-filter]') - .find('.choices__input--cloned') - .type(input) - .type('{enter}'); - - cy.get('[data-test-hook=add-item-filter]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.text().trim()).to.equal(input); - }); - }); - }); - - describe('inputting a value that does not satisfy the regex', () => { - it('displays dropdown prompt', () => { - cy.get('[data-test-hook=add-item-filter]') - .find('.choices__input--cloned') - .type(`this is not an email address`) - .type('{enter}'); - - cy.get('[data-test-hook=add-item-filter]') - .find('.choices__list--dropdown') - .should('be.visible') - .should(($dropdown) => { - const dropdownText = $dropdown.text().trim(); - expect(dropdownText).to.equal( - 'Only values matching specific conditions can be added', - ); - }); - }); - }); - }); - - describe('prepend/append', () => { - beforeEach(() => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__input--cloned') - .type(textInput) - .type('{enter}'); - }); - - it('prepends and appends value to inputted value', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.data('value')).to.equal(`before-${textInput}-after`); - }); - }); - - it('displays just the inputted value to the user', () => { - cy.get('[data-test-hook=prepend-append]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.text()).to.not.contain(`before-${textInput}-after`); - expect($choice.text()).to.contain(textInput); - }); - }); - }); - - describe('adding items disabled', () => { - it('does not allow me to input data', () => { - cy.get('[data-test-hook=adding-items-disabled]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - }); - - describe('disabled via attribute', () => { - it('does not allow me to input data', () => { - cy.get('[data-test-hook=disabled-via-attr]') - .find('.choices__input--cloned') - .should('be.disabled'); - }); - }); - - describe('pre-populated choices', () => { - it('pre-populates choices', () => { - cy.get('[data-test-hook=prepopulated]') - .find('.choices__list--multiple .choices__item') - .should(($choices) => { - expect($choices.length).to.equal(2); - }); - - cy.get('[data-test-hook=prepopulated]') - .find('.choices__list--multiple .choices__item') - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Josh Johnson'); - }); - - cy.get('[data-test-hook=prepopulated]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Joe Bloggs'); - }); - }); - }); - - describe('placeholder', () => { - /* - { - placeholder: true, - placeholderValue: 'I am a placeholder', - } - */ - describe('when no value has been inputted', () => { - it('displays a placeholder', () => { - cy.get('[data-test-hook=placeholder]') - .find('.choices__input--cloned') - .should('have.attr', 'placeholder', 'I am a placeholder'); - }); - }); - }); - - describe('allow html', () => { - describe('set to true', () => { - it('does not show html as text', () => { - cy.get('[data-test-hook=allowhtml-true]') - .find('.choices__list--multiple .choices__item') - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Mason Rogers'); - }); - }); - }); - - describe('set to false', () => { - it('shows html as text', () => { - cy.get('[data-test-hook=allowhtml-false]') - .find('.choices__list--multiple .choices__item') - .first() - .should(($choice) => { - expect($choice.text().trim()).to.equal('Mason Rogers'); - }); - }); - }); - }); - - describe('within form', () => { - describe('inputting item', () => { - describe('on enter key', () => { - it('does not submit form', () => { - cy.get('[data-test-hook=within-form] form').then(($form) => { - $form.submit(() => { - // this will fail the test if the form submits - throw new Error('Form submitted'); - }); - }); - - cy.get('[data-test-hook=within-form]') - .find('.choices__input--cloned') - .type(textInput) - .type('{enter}'); - - cy.get('[data-test-hook=within-form]') - .find('.choices__list--multiple .choices__item') - .last() - .should(($el) => { - expect($el).to.contain(textInput); - }); - }); - }); - }); - }); - }); -}); diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index da18d935..00000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index c1f5a772..00000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js deleted file mode 100644 index 37a498fb..00000000 --- a/cypress/support/e2e.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands'; - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/cypress/tsconfig.json b/e2e/tsconfig.json similarity index 67% rename from cypress/tsconfig.json rename to e2e/tsconfig.json index 1e24a936..833c6bb1 100644 --- a/cypress/tsconfig.json +++ b/e2e/tsconfig.json @@ -3,20 +3,21 @@ "module": "es6", "lib": ["es2017", "dom"], "target": "es5", - "moduleResolution": "node", + "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "esModuleInterop": true, "strict": false, "noImplicitAny": false, - "declaration": true, "allowJs": true, "noUnusedLocals": true, "noUnusedParameters": true, "strictNullChecks": true, "newLine": "lf", - "types": ["cypress", "node"], - "declarationDir": "../public/types/cypress" + "declaration": false, + "declarationMap": false, + "types": [], }, - "include": ["."] -} \ No newline at end of file + "include": [".", "../src"], + "exclude": ["**/node_modules", "**/public"] +} diff --git a/jsconfig.json b/jsconfig.json index d5c3a53e..d4f0681f 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -3,7 +3,7 @@ "checkJs": true, "target": "es2020", "lib": ["esnext", "dom"], - "types": ["cypress"], + "types": [], "strict": true, "moduleResolution": "node", /* Additional Checks */ diff --git a/package-lock.json b/package-lock.json index 4e6db4ee..3e40e1c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "choices.js", - "version": "11.0.0-rc5", + "version": "11.0.0-rc6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "choices.js", - "version": "11.0.0-rc5", + "version": "11.0.0-rc6", "license": "MIT", "dependencies": { "fuse.js": "^7.0.0" @@ -17,6 +17,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/preset-env": "^7.25.3", "@babel/preset-typescript": "^7.24.7", + "@playwright/test": "^1.46.0", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-eslint": "^9.0.5", "@rollup/plugin-node-resolve": "^15.2.3", @@ -24,6 +25,7 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/chai": "^4.3.17", + "@types/node": "^22.2.0", "@types/sinon": "^17.0.3", "@types/sinon-chai": "^3.2.12", "@vitest/coverage-v8": "^2.0.5", @@ -31,13 +33,11 @@ "bundlesize": "^0.18.2", "chai": "^5.1.1", "csso-cli": "^4.0.2", - "cypress": "^13.13.2", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-compat": "6.0.0", - "eslint-plugin-cypress": "^3.4.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-sort-class-members": "^1.20.0", @@ -1951,17 +1951,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@csstools/css-parser-algorithms": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz", @@ -2052,72 +2041,6 @@ "postcss-selector-parser": "^6.0.13" } }, - "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "6.10.4", - "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@cypress/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - } - }, - "node_modules/@cypress/xvfb/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/@dual-bundle/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -3114,6 +3037,22 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@playwright/test": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.0.tgz", + "integrity": "sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.46.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/plugin-babel": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", @@ -3616,12 +3555,11 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", - "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz", + "integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "undici-types": "~6.13.0" } @@ -3661,24 +3599,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/sizzle": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", @@ -4082,20 +4002,6 @@ "node": ">= 14" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4123,22 +4029,6 @@ "node": ">=6" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4183,27 +4073,6 @@ "dev": true, "license": "ISC" }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", @@ -4370,26 +4239,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -4420,13 +4269,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true, - "license": "MIT" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4434,16 +4276,6 @@ "dev": true, "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -4521,23 +4353,6 @@ "queue-microtask": "^1.1.2" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.1.tgz", - "integrity": "sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA==", - "dev": true, - "license": "MIT" - }, "node_modules/axios": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", @@ -4620,16 +4435,6 @@ ], "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4670,20 +4475,6 @@ "node": ">= 6" } }, - "node_modules/blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -4874,16 +4665,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4955,16 +4736,6 @@ "node": ">=8" } }, - "node_modules/cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -5042,13 +4813,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/chai": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", @@ -5091,16 +4855,6 @@ "node": ">= 16" } }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -5140,22 +4894,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/clap": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/clap/-/clap-3.1.1.tgz", @@ -5170,62 +4908,6 @@ "npm": ">=7.0.0" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -5305,16 +4987,6 @@ "node": ">= 6" } }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5526,196 +5198,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cypress": { - "version": "13.13.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.13.2.tgz", - "integrity": "sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@cypress/request": "^3.0.1", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.7.1", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.1", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.5.3", - "supports-color": "^8.1.1", - "tmp": "~0.2.3", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" - }, - "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" - } - }, - "node_modules/cypress/node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/cypress/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cypress/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cypress/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cypress/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/cypress/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress/node_modules/proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cypress/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cypress/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -5800,13 +5282,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dayjs": { - "version": "1.11.12", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz", - "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==", - "dev": true, - "license": "MIT" - }, "node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -6025,17 +5500,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6087,20 +5551,6 @@ "once": "^1.4.0" } }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -6556,48 +6006,6 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-cypress": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.4.0.tgz", - "integrity": "sha512-Rrrr3Ri6wHqzrRr+TyUV7bDS4UnMMrFY1R1PP2F7XdGfe9txDC6lQEshyoNOWqGoPkbbeDm1x1XPc/adxemsnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "globals": "^13.20.0" - }, - "peerDependencies": { - "eslint": ">=7" - } - }, - "node_modules/eslint-plugin-cypress/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-cypress/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-plugin-import": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", @@ -7051,13 +6459,6 @@ "node": ">= 0.6" } }, - "node_modules/eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true, - "license": "MIT" - }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", @@ -7065,43 +6466,6 @@ "dev": true, "license": "MIT" }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.2.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -7254,44 +6618,6 @@ "dev": true, "license": "MIT" }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, "node_modules/fast-content-type-parse": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", @@ -7466,16 +6792,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/femtocolor": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/femtocolor/-/femtocolor-2.0.3.tgz", @@ -7483,22 +6799,6 @@ "dev": true, "license": "MIT" }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -7697,16 +6997,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -7763,22 +7053,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -8014,22 +7288,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -8048,26 +7306,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "async": "^3.2.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/github-build": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/github-build/-/github-build-1.2.4.tgz", @@ -8156,22 +7394,6 @@ "node": "*" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -8502,21 +7724,6 @@ "node": ">= 14" } }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/https-proxy-agent": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", @@ -8531,16 +7738,6 @@ "node": ">= 14" } }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.12.0" - } - }, "node_modules/husky": { "version": "9.1.4", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", @@ -8655,16 +7852,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -8684,16 +7871,6 @@ "dev": true, "license": "ISC" }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -8815,19 +7992,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", @@ -8919,23 +8083,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -9042,19 +8189,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -9103,26 +8237,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -9150,13 +8264,6 @@ "dev": true, "license": "ISC" }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "license": "MIT" - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -9314,13 +8421,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "license": "MIT" - }, "node_modules/jsdom": { "version": "24.1.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.1.tgz", @@ -9396,13 +8496,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9417,13 +8510,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -9450,22 +8536,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "node_modules/just-extend": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", @@ -9500,16 +8570,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "> 0.8" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10044,34 +9104,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -10114,13 +9146,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -10149,13 +9174,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -10163,187 +9181,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-update/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/loupe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", @@ -10554,16 +9391,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -10997,19 +9824,6 @@ "which": "bin/which" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", @@ -11183,22 +9997,6 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -11217,13 +10015,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true, - "license": "MIT" - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -11256,22 +10047,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -11434,20 +10209,6 @@ "node": ">= 14.16" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -11517,6 +10278,53 @@ "dev": true, "license": "MIT" }, + "node_modules/playwright": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz", + "integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.46.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz", + "integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -11873,19 +10681,6 @@ "node": ">=6.0.0" } }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -11944,16 +10739,6 @@ "node": ">=4" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -12024,22 +10809,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -12337,16 +11106,6 @@ "jsesc": "bin/jsesc" } }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "throttleit": "^1.0.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12402,20 +11161,6 @@ "node": ">=4" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ret": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", @@ -12554,16 +11299,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-array-concat": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", @@ -13129,57 +11864,6 @@ "node": ">=6" } }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -13272,32 +11956,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -13496,16 +12154,6 @@ "node": ">=4" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -14218,23 +12866,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/throttleit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", - "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, "node_modules/time-zone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-2.0.0.tgz", @@ -14295,16 +12926,6 @@ "node": ">=14.0.0" } }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -14446,13 +13067,6 @@ "node": "*" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -14476,19 +13090,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -14862,8 +13463,7 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -14942,16 +13542,6 @@ "node": ">= 0.8" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -15021,16 +13611,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -15052,28 +13632,6 @@ "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT" - }, "node_modules/vite": { "version": "5.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", @@ -15763,17 +14321,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 38d277bf..82a4e6ef 100644 --- a/package.json +++ b/package.json @@ -30,17 +30,16 @@ "start": "run-p js:watch css:watch", "build": "run-p js:build css:build", "lint": "run-p lint:js lint:scss", - "lint:js": "eslint src/scripts test/scripts", + "lint:js": "eslint src/scripts test/scripts e2e", "lint:scss": "stylelint src/**/*.scss", "bundlesize": "bundlesize", - "cypress:run": "cypress run --config-file cypress/cypress.config.ts --browser chromium", - "cypress:open": "cypress open --config-file cypress/cypress.config.ts ", - "cypress:ci": "cypress run --config-file cypress/cypress.config.ts --browser chromium --record --group $GITHUB_REF --ci-build-id $GITHUB_SHA", + "playwright:cli": "playwright test", + "playwright:gui": "playwright test --ui", "test": "run-s test:unit test:e2e", "test:unit": "vitest --config test/vitest.config.ts run", "test:unit:watch": "npm run test:unit -- --watch --inspect=5556", "test:unit:coverage": "vitest --config test/vitest.config.ts run --coverage", - "test:e2e": "run-p --race start cypress:run", + "test:e2e": "run-s playwright:cli", "js:watch": "rollup -w --bundleConfigAsCjs -c scripts/rollup.config.mjs --environment TARGET:. --environment OUTPUT_TYPES:umd --environment WATCH_HOST:localhost", "js:build": "rollup --bundleConfigAsCjs -c scripts/rollup.config.mjs --environment WITH_D_TS_FILES:1 && mv public/assets/scripts/src public/types/", "js:build-dev": "rollup --bundleConfigAsCjs -c scripts/rollup.config.mjs --environment TARGET:. --environment OUTPUT_TYPES:umd", @@ -83,6 +82,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/preset-env": "^7.25.3", "@babel/preset-typescript": "^7.24.7", + "@playwright/test": "^1.46.0", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-eslint": "^9.0.5", "@rollup/plugin-node-resolve": "^15.2.3", @@ -90,6 +90,7 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", "@types/chai": "^4.3.17", + "@types/node": "^22.2.0", "@types/sinon": "^17.0.3", "@types/sinon-chai": "^3.2.12", "@vitest/coverage-v8": "^2.0.5", @@ -97,13 +98,11 @@ "bundlesize": "^0.18.2", "chai": "^5.1.1", "csso-cli": "^4.0.2", - "cypress": "^13.13.2", "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^18.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-compat": "6.0.0", - "eslint-plugin-cypress": "^3.4.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-sort-class-members": "^1.20.0", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..9872068b --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,82 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: process.env.CI ? 'dot' : 'list', + timeout: 1000, + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://127.0.0.1:3001/', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + testIdAttribute: 'data-test-hook', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, +/* + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, +*/ + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + //webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3001', + // reuseExistingServer: !process.env.CI, + //}, +}); diff --git a/public/index.html b/public/index.html index ba032bb2..ec303523 100644 --- a/public/index.html +++ b/public/index.html @@ -942,7 +942,6 @@ -