diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..65096ce0b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# NOTE: Prettier reads EditorConfig settings, so be careful adjusting settings here and assuming they'll only affect your editor ;). + +# top-most EditorConfig file +root = true + +[*.yml] +indent_size = 2 + +[*.{js,jsx,ts,tsx}] +indent_size = 2 \ No newline at end of file diff --git a/.eslintignore b/.eslintignore index f7ed8b5f9..2eeecaed5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,5 @@ +playwright.config.ts + **/node_modules/ src/**/__mocks__/**/* dist/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd097a194..15c555e31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,79 +104,6 @@ jobs: run: az storage blob upload -c '$web' -f ./preview/config.json --account-name cosmosexplorerpreview --name "${{github.event.pull_request.head.sha || github.sha}}/config.json" --account-key="${PREVIEW_STORAGE_KEY}" --overwrite true env: PREVIEW_STORAGE_KEY: ${{ secrets.PREVIEW_STORAGE_KEY }} - endtoendemulator: - name: "End To End Emulator Tests" - # Temporarily disabled. This test needs to be rewritten in playwright - if: false - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - name: Use Node.js 18.x - uses: actions/setup-node@v4 - with: - node-version: 18.x - - uses: southpolesteve/cosmos-emulator-github-action@v1 - - name: End to End Tests - run: | - npm ci - npm start & - npm run wait-for-server - npx jest -c ./jest.config.e2e.js --detectOpenHandles test/sql/container.spec.ts - shell: bash - env: - DATA_EXPLORER_ENDPOINT: "https://localhost:1234/explorer.html?platform=Emulator" - PLATFORM: "Emulator" - NODE_TLS_REJECT_UNAUTHORIZED: 0 - - uses: actions/upload-artifact@v3 - if: failure() - with: - name: screenshots - path: failed-* - endtoend: - name: "E2E" - runs-on: ubuntu-latest - env: - NODE_TLS_REJECT_UNAUTHORIZED: 0 - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - strategy: - fail-fast: false - matrix: - test-file: - - ./test/cassandra/container.spec.ts - - ./test/graph/container.spec.ts - - ./test/sql/container.spec.ts - - ./test/mongo/container.spec.ts - - ./test/mongo/container32.spec.ts - - ./test/selfServe/selfServeExample.spec.ts - - ./test/sql/resourceToken.spec.ts - - ./test/tables/container.spec.ts - steps: - - uses: actions/checkout@v4 - - - name: "Az CLI login" - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - - name: Use Node.js 18.x - uses: actions/setup-node@v4 - with: - node-version: 18.x - - run: npm ci - - run: npm start & - - run: npm run wait-for-server - - name: ${{ matrix['test-file'] }} - run: | - # Run tests up to three times - for i in $(seq 1 3); do npx jest -c ./jest.config.playwright.js ${{ matrix['test-file'] }} && s=0 && break || s=$? && sleep 1; done; (exit $s) - shell: bash - - uses: actions/upload-artifact@v3 - if: failure() - with: - name: screenshots - path: screenshots/ nuget: name: Publish Nuget if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/') @@ -226,3 +153,70 @@ jobs: name: packages with: path: "*.nupkg" + + playwright-tests: + name: "Run Playwright Tests (Shard ${{ matrix.shardIndex }} of ${{ matrix.shardTotal }})" + runs-on: ubuntu-latest + env: + NODE_TLS_REJECT_UNAUTHORIZED: 0 + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + strategy: + fail-fast: false + matrix: + shardIndex: [1, 2, 3, 4, 5, 6, 7, 8] + shardTotal: [8] + steps: + - uses: actions/checkout@v4 + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + - name: Use Node.js 18.x + uses: actions/setup-node@v4 + with: + node-version: 18.x + - run: npm ci + - run: npx playwright install --with-deps + - name: Run test shard ${{ matrix['shardIndex'] }} of ${{ matrix['shardTotal']}} + run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + - name: Upload blob report to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: blob-report-${{ matrix.shardIndex }} + path: blob-report + retention-days: 1 + + merge-playwright-reports: + name: "Merge Playwright Reports" + # Merge reports after playwright-tests, even if some shards have failed + if: ${{ !cancelled() }} + needs: [playwright-tests] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v4 + with: + path: all-blob-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge into HTML Report + run: npx playwright merge-reports --reporter html ./all-blob-reports + + - name: Upload HTML report + uses: actions/upload-artifact@v4 + with: + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 14 \ No newline at end of file diff --git a/.gitignore b/.gitignore index be016240b..3617cf905 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,8 @@ Contracts/* .env failure.png screenshots/* -GettingStarted-ignore*.ipynb \ No newline at end of file +GettingStarted-ignore*.ipynb +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/jest-playwright.config.js b/jest-playwright.config.js deleted file mode 100644 index d66e8f221..000000000 --- a/jest-playwright.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const isCI = require("is-ci"); - -module.exports = { - exitOnPageError: false, - launchOptions: { - headless: isCI, - slowMo: 10, - timeout: 60000, - }, - contextOptions: { - ignoreHTTPSErrors: true, - }, -}; diff --git a/jest.config.playwright.js b/jest.config.playwright.js deleted file mode 100644 index c452a5368..000000000 --- a/jest.config.playwright.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - preset: "jest-playwright-preset", - testMatch: ["/test/**/*.spec.[jt]s?(x)"], - setupFiles: ["dotenv/config"], - testEnvironment: "./test/playwrightEnv.js", - setupFilesAfterEnv: ["expect-playwright"], -}; diff --git a/less/Common/Constants.less b/less/Common/Constants.less index 5b80d67f8..d79c3e4e0 100644 --- a/less/Common/Constants.less +++ b/less/Common/Constants.less @@ -130,6 +130,7 @@ @ActiveTabWidth: 141px; @TabsHeight: 30px; @TabsWidth: 140px; +@ContentWrapper: 111px; @StatusIconContainerSize: 18px; @LoadingErrorIconSize: 14px; @ErrorIconContainer: 16px; diff --git a/less/documentDB.less b/less/documentDB.less index 05ad22760..bf5f87e3e 100644 --- a/less/documentDB.less +++ b/less/documentDB.less @@ -2366,9 +2366,9 @@ a:link { .tabsManagerContainer { height: 100%; flex-grow: 1; - overflow: hidden; + display: flex; + flex-direction: column; min-height: 300px; - overflow-y: scroll; } .tabs { @@ -2671,7 +2671,7 @@ a:link { width: @ActiveTabWidth; } -.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .tabNavText { +.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .contentWrapper > .tabNavText { font-weight: bolder; border-bottom: 2px solid rgba(0, 120, 212, 1); } @@ -2707,67 +2707,71 @@ a:link { width: @TabsWidth; border-right: @ButtonBorderWidth solid @BaseMedium; white-space: nowrap; + .contentWrapper { + .flex-display(); + width: @ContentWrapper; - .statusIconContainer { - width: @StatusIconContainerSize; - height: @StatusIconContainerSize; - margin-left: @SmallSpace; - display: inline-flex; + .statusIconContainer { + width: @StatusIconContainerSize; + height: @StatusIconContainerSize; + margin-left: @SmallSpace; + display: inline-flex; - .errorIconContainer { - width: @ErrorIconContainer; - height: @ErrorIconContainer; - margin-top: 1px; + .errorIconContainer { + width: @ErrorIconContainer; + height: @ErrorIconContainer; + margin-top: 1px; - .errorIcon { - width: @ErrorIconWidth; + .errorIcon { + width: @ErrorIconWidth; + height: @LoadingErrorIconSize; + background-image: url(../images/error_no_outline.svg); + background-repeat: no-repeat; + background-position: center; + background-size: 3px; + display: block; + margin: 1px 0px 0px 6px; + } + } + + .errorIconContainer.actionsEnabled { + &:hover { + .hover(); + } + + &:focus { + .focus(); + } + + &:active { + .active(); + } + } + + .errorIconContainer[tabindex]:active { + outline: none; + } + + .loadingIcon { + width: @LoadingErrorIconSize; height: @LoadingErrorIconSize; - background-image: url(../images/error_no_outline.svg); - background-repeat: no-repeat; - background-position: center; - background-size: 3px; - display: block; - margin: 1px 0px 0px 6px; + margin: 0px 0px @SmallSpace @SmallSpace; } } - .errorIconContainer.actionsEnabled { - &:hover { - .hover(); - } - - &:focus { - .focus(); - } - - &:active { - .active(); - } + .tabNavText { + margin-left: @SmallSpace; + margin-right: 2px; + color: @BaseDark; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + flex-grow: 1; } - - .errorIconContainer[tabindex]:active { - outline: none; - } - - .loadingIcon { - width: @LoadingErrorIconSize; - height: @LoadingErrorIconSize; - margin: 0px 0px @SmallSpace @SmallSpace; - } - } - - .tabNavText { - margin-left: @SmallSpace; - margin-right: 2px; - color: @BaseDark; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - flex-grow: 1; } .tabIconSection { - width: 30px; + width: 29px; position: relative; padding-top: 2px; diff --git a/less/documentDBFabric.less b/less/documentDBFabric.less index be4e5c17e..ea4001780 100644 --- a/less/documentDBFabric.less +++ b/less/documentDBFabric.less @@ -75,7 +75,7 @@ a:focus { border-bottom: 2px solid @FabricAccentMedium; } -.nav-tabs>li.active>.tabNavContentContainer>.tab_Content>.tabNavText { +.nav-tabs>li.active>.tabNavContentContainer>.tab_Content>.contentWrapper>.tabNavText { border-bottom: 0px none transparent; } @@ -93,9 +93,11 @@ a:focus { width: calc(@TabsWidth - (@SmallSpace * 2)); padding-bottom: @SmallSpace; - .statusIconContainer { - margin-left: 0px; - } + .contentWrapper { + .statusIconContainer { + margin-left: 0px; + } + } .tabIconSection { .cancelButton { diff --git a/package-lock.json b/package-lock.json index 31e3d37db..5a872d3fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -122,6 +122,7 @@ "@babel/preset-env": "7.9.0", "@babel/preset-react": "7.9.4", "@babel/preset-typescript": "7.9.0", + "@playwright/test": "1.44.0", "@testing-library/react": "11.2.3", "@types/applicationinsights-js": "1.0.7", "@types/codemirror": "0.0.56", @@ -166,7 +167,6 @@ "eslint-plugin-no-null": "1.0.2", "eslint-plugin-prefer-arrow": "1.2.3", "eslint-plugin-react-hooks": "4.6.0", - "expect-playwright": "0.3.3", "fast-glob": "3.2.5", "fs-extra": "7.0.0", "html-inline-css-webpack-plugin": "1.11.2", @@ -175,7 +175,6 @@ "html-webpack-plugin": "5.5.3", "jest": "26.6.3", "jest-canvas-mock": "2.3.1", - "jest-playwright-preset": "1.5.1", "jest-react-hooks-shallow": "1.5.1", "jest-trx-results-processor": "0.0.7", "less": "3.8.1", @@ -184,7 +183,6 @@ "mini-css-extract-plugin": "2.1.0", "monaco-editor-webpack-plugin": "7.1.0", "node-fetch": "2.6.7", - "playwright": "1.13.0", "prettier": "3.0.3", "process": "0.11.10", "querystring-es3": "0.2.1", @@ -10210,6 +10208,53 @@ "@phosphor/virtualdom": "^1.2.0" } }, + "node_modules/@playwright/test": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz", + "integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==", + "dev": true, + "dependencies": { + "playwright": "1.44.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@playwright/test/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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/@playwright/test/node_modules/playwright": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz", + "integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==", + "dev": true, + "dependencies": { + "playwright-core": "1.44.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.23", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz", @@ -11806,6 +11851,7 @@ "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", "dev": true, + "peer": true, "dependencies": { "@hapi/hoek": "^9.0.0" } @@ -11814,13 +11860,15 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@sinclair/typebox": { "version": "0.27.8", @@ -13580,15 +13628,6 @@ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" }, - "node_modules/@types/wait-on": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@types/wait-on/-/wait-on-5.3.4.tgz", - "integrity": "sha512-EBsPjFMrFlMbbUFf9D1Fp+PAB2TwmUn7a3YtHyD9RLuTIk1jDd8SxXVAoez2Ciy+8Jsceo2MYEYZzJ/DvorOKw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -13611,16 +13650,6 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, - "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, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/youtube-player": { "version": "5.5.6", "resolved": "https://registry.npmjs.org/@types/youtube-player/-/youtube-player-5.5.6.tgz", @@ -14314,19 +14343,6 @@ "node": ">= 6.0.0" } }, - "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, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/airbnb-prop-types": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz", @@ -14553,18 +14569,6 @@ "dev": true, "peer": true }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/applicationinsights": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.8.0.tgz", @@ -14584,12 +14588,6 @@ "optional": true, "peer": true }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -15667,15 +15665,6 @@ "ieee754": "^1.1.4" } }, - "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, - "engines": { - "node": "*" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -15714,57 +15703,6 @@ "node": ">=0.10.0" } }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/caching-transform/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caching-transform/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/caching-transform/node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -16210,15 +16148,6 @@ "node": ">=0.10.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, - "engines": { - "node": ">=6" - } - }, "node_modules/clean-webpack-plugin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", @@ -17285,19 +17214,6 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, - "node_modules/cwd": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", - "integrity": "sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA==", - "dev": true, - "dependencies": { - "find-pkg": "^0.1.2", - "fs-exists-sync": "^0.1.0" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/d3": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", @@ -18083,12 +17999,6 @@ "node": ">=8" } }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, "node_modules/deep-diff": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", @@ -18191,21 +18101,6 @@ "node": ">=10.17.0" } }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -19167,12 +19062,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, "node_modules/es6-templates": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", @@ -20079,15 +19968,6 @@ "node": ">= 10.14.2" } }, - "node_modules/expect-playwright": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/expect-playwright/-/expect-playwright-0.3.3.tgz", - "integrity": "sha512-uoeyx2D5LawJdziMdweOp6cnZzFOOPT9VvPG6gOh6YC7N9pU0k2KpVlRiz/Vc/fFBiGUNNeJq2Aq+9GJ65Nfrw==", - "dev": true, - "peerDependencies": { - "playwright-core": "^1.9.1" - } - }, "node_modules/expect/node_modules/@jest/types": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", @@ -20350,26 +20230,6 @@ "node": ">=0.10.0" } }, - "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, - "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", @@ -20558,15 +20418,6 @@ "bser": "2.1.1" } }, - "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, - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -20685,124 +20536,6 @@ "node": ">=6" } }, - "node_modules/find-file-up": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", - "integrity": "sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A==", - "dev": true, - "dependencies": { - "fs-exists-sync": "^0.1.0", - "resolve-dir": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-pkg": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz", - "integrity": "sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw==", - "dev": true, - "dependencies": { - "find-file-up": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-process": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/find-process/-/find-process-1.4.7.tgz", - "integrity": "sha512-/U4CYp1214Xrp3u3Fqr9yNynUrr5Le4y0SsJh2lMDDSbpwYSz3M2SMWQC+wqcx79cN8PQtHQIL8KnuY9M66fdg==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "commander": "^5.1.0", - "debug": "^4.1.1" - }, - "bin": { - "find-process": "bin/find-process.js" - } - }, - "node_modules/find-process/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/find-process/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, - "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/find-process/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/find-process/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 - }, - "node_modules/find-process/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/find-process/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-process/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -20968,19 +20701,6 @@ "node": ">=0.10.0" } }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -21284,41 +21004,12 @@ "node": ">= 0.6" } }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "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" - } - ] - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "optional": true }, - "node_modules/fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "integrity": "sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fs-extra": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", @@ -21967,31 +21658,6 @@ "node": ">=0.10.0" } }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/hasher": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hasher/-/hasher-1.2.0.tgz", @@ -22106,18 +21772,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -23701,18 +23355,6 @@ "node": ">=6" } }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-instrument": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", @@ -23738,44 +23380,6 @@ "semver": "bin/semver.js" } }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", - "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo/node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -24070,217 +23674,6 @@ "node": ">=8" } }, - "node_modules/jest-circus": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-26.6.3.tgz", - "integrity": "sha512-ACrpWZGcQMpbv13XbzRzpytEJlilP/Su0JtNCi5r/xLpOUhnaIJr8leYYpLEMgPFURZISEHrnnpmB54Q/UziPw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "stack-utils": "^2.0.2", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-circus/node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "15.0.19", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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, - "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/jest-circus/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/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 - }, - "node_modules/jest-circus/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jest-circus/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-circus/node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/jest-circus/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/jest-cli": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", @@ -26622,38 +26015,6 @@ "node": ">=8" } }, - "node_modules/jest-playwright-preset": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/jest-playwright-preset/-/jest-playwright-preset-1.5.1.tgz", - "integrity": "sha512-zsFAe61V72vSLkd1fCcf7YbHmbdAB82SLBdUuCUF43aODIojshQEDF88KdWL9P+4JQ+DvEABT+6sFX4sY0rR2w==", - "dev": true, - "dependencies": { - "expect-playwright": "^0.3.3", - "jest-circus": "^26.6.3", - "jest-environment-node": "^26.6.2", - "jest-process-manager": "^0.2.9", - "jest-runner": "^26.6.3", - "nyc": "^15.1.0", - "playwright-core": ">=1.2.0", - "rimraf": "^3.0.2", - "uuid": "^8.3.2" - } - }, - "node_modules/jest-playwright-preset/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", @@ -26670,113 +26031,6 @@ } } }, - "node_modules/jest-process-manager": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/jest-process-manager/-/jest-process-manager-0.2.9.tgz", - "integrity": "sha512-IKVdOSz1NLwKg9HTeyEDn63waMvKK6wcS+tCarGquNIktlXt4zAW2cfJ9vAA/xBcidWYKOPXHvy1l1N8qfw3Ww==", - "dev": true, - "dependencies": { - "@types/wait-on": "^5.2.0", - "chalk": "^4.1.0", - "cwd": "^0.10.0", - "exit": "^0.1.2", - "find-process": "^1.4.4", - "prompts": "^2.4.0", - "signal-exit": "^3.0.3", - "spawnd": "^4.4.0", - "tree-kill": "^1.2.2", - "wait-on": "^5.2.1" - } - }, - "node_modules/jest-process-manager/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-process-manager/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, - "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/jest-process-manager/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-process-manager/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 - }, - "node_modules/jest-process-manager/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-process-manager/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-process-manager/node_modules/wait-on": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz", - "integrity": "sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==", - "dev": true, - "dependencies": { - "axios": "^0.21.1", - "joi": "^17.3.0", - "lodash": "^4.17.21", - "minimist": "^1.2.5", - "rxjs": "^6.6.3" - }, - "bin": { - "wait-on": "bin/wait-on" - }, - "engines": { - "node": ">=8.9.0" - } - }, "node_modules/jest-react-hooks-shallow": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/jest-react-hooks-shallow/-/jest-react-hooks-shallow-1.5.1.tgz", @@ -28713,6 +27967,7 @@ "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", "dev": true, + "peer": true, "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -28721,12 +27976,6 @@ "@sideway/pinpoint": "^2.0.0" } }, - "node_modules/jpeg-js": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", - "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", - "dev": true - }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -32465,18 +31714,6 @@ "dev": true, "optional": true }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -32591,205 +31828,6 @@ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/nyc/node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/nyc/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/nyc/node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -33198,15 +32236,6 @@ "node": ">=8" } }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/os-name": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", @@ -33308,21 +32337,6 @@ "node": ">=6" } }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", @@ -33374,15 +32388,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-srcset": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", @@ -33718,12 +32723,6 @@ "node": ">=8" } }, - "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 - }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -33803,39 +32802,10 @@ "node": ">=8" } }, - "node_modules/playwright": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.13.0.tgz", - "integrity": "sha512-GA5OyEeKx1v/pRcANmYncCT67Y7Y4N5zLRU5E690dn/Id10sooR5hQZmCDYsjXlutZb/1q0R3sITALnvhEjCjg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "commander": "^6.1.0", - "debug": "^4.1.1", - "extract-zip": "^2.0.1", - "https-proxy-agent": "^5.0.0", - "jpeg-js": "^0.4.2", - "mime": "^2.4.6", - "pngjs": "^5.0.0", - "progress": "^2.0.3", - "proper-lockfile": "^4.1.1", - "proxy-from-env": "^1.1.0", - "rimraf": "^3.0.2", - "stack-utils": "^2.0.3", - "ws": "^7.4.6", - "yazl": "^2.5.1" - }, - "bin": { - "playwright": "lib/cli/cli.js" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/playwright-core": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz", - "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz", + "integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -33844,42 +32814,6 @@ "node": ">=16" } }, - "node_modules/playwright/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/playwright/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/playwright/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/plotly.js-cartesian-dist-min": { "version": "1.52.3", "resolved": "https://registry.npmjs.org/plotly.js-cartesian-dist-min/-/plotly.js-cartesian-dist-min-1.52.3.tgz", @@ -33895,15 +32829,6 @@ "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/polygon-offset": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/polygon-offset/-/polygon-offset-0.3.1.tgz", @@ -34315,27 +33240,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -34384,26 +33288,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/proper-lockfile/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/property-information": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", @@ -34438,12 +33322,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -35806,18 +34684,6 @@ "node": ">= 0.10" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/remark-parse": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", @@ -36153,80 +35019,6 @@ "node": ">=8" } }, - "node_modules/resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "integrity": "sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA==", - "dev": true, - "dependencies": { - "expand-tilde": "^1.2.2", - "global-modules": "^0.2.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "integrity": "sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==", - "dev": true, - "dependencies": { - "os-homedir": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "integrity": "sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA==", - "dev": true, - "dependencies": { - "global-prefix": "^0.1.4", - "is-windows": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", - "integrity": "sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw==", - "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.0", - "ini": "^1.3.4", - "is-windows": "^0.2.0", - "which": "^1.2.12" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -37397,59 +36189,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/spawn-wrap/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/spawn-wrap/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/spawnd": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-4.4.0.tgz", - "integrity": "sha512-jLPOfB6QOEgMOQY15Z6+lwZEhH3F5ncXxIaZ7WHPIapwNNLyjrs61okj3VJ3K6tmP5TZ6cO0VAu9rEY4MD4YQg==", - "dev": true, - "dependencies": { - "exit": "^0.1.2", - "signal-exit": "^3.0.2", - "tree-kill": "^1.2.2", - "wait-port": "^0.2.7" - } - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -38589,15 +37328,6 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, "node_modules/trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -39789,29 +38519,6 @@ "node": ">=8.9.0" } }, - "node_modules/wait-port": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-0.2.14.tgz", - "integrity": "sha512-kIzjWcr6ykl7WFbZd0TMae8xovwqcqbx6FM9l+7agOgUByhzdjfzZBPK2CPufldTOMxbUivss//Sh9MFawmPRQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "commander": "^3.0.2", - "debug": "^4.1.1" - }, - "bin": { - "wait-port": "bin/wait-port.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wait-port/node_modules/commander": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", - "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", - "dev": true - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -40951,25 +39658,6 @@ "node": ">=8" } }, - "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, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3" - } - }, "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 d27273af9..abec04d59 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "@babel/preset-env": "7.9.0", "@babel/preset-react": "7.9.4", "@babel/preset-typescript": "7.9.0", + "@playwright/test": "1.44.0", "@testing-library/react": "11.2.3", "@types/applicationinsights-js": "1.0.7", "@types/codemirror": "0.0.56", @@ -161,7 +162,6 @@ "eslint-plugin-no-null": "1.0.2", "eslint-plugin-prefer-arrow": "1.2.3", "eslint-plugin-react-hooks": "4.6.0", - "expect-playwright": "0.3.3", "fast-glob": "3.2.5", "fs-extra": "7.0.0", "html-inline-css-webpack-plugin": "1.11.2", @@ -170,7 +170,6 @@ "html-webpack-plugin": "5.5.3", "jest": "26.6.3", "jest-canvas-mock": "2.3.1", - "jest-playwright-preset": "1.5.1", "jest-react-hooks-shallow": "1.5.1", "jest-trx-results-processor": "0.0.7", "less": "3.8.1", @@ -179,7 +178,6 @@ "mini-css-extract-plugin": "2.1.0", "monaco-editor-webpack-plugin": "7.1.0", "node-fetch": "2.6.7", - "playwright": "1.13.0", "prettier": "3.0.3", "process": "0.11.10", "querystring-es3": "0.2.1", @@ -211,6 +209,7 @@ "test": "rimraf coverage && jest", "test:debug": "jest --runInBand", "test:e2e": "jest -c ./jest.config.playwright.js --detectOpenHandles", + "test:file": "jest --coverage=false", "watch": "npm run start", "wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/", "build:ase": "gulp build:ase", @@ -246,4 +245,4 @@ "printWidth": 120, "endOfLine": "auto" } -} \ No newline at end of file +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 000000000..074e7e5f5 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,53 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: 'test', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 3 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: process.env.CI ? 'blob' : 'html', + timeout: 10 * 60 * 1000, + use: { + actionTimeout: 5 * 60 * 1000, + trace: 'off', + video: 'off', + screenshot: 'on', + testIdAttribute: 'data-test', + contextOptions: { + ignoreHTTPSErrors: true, + }, + }, + + expect: { + timeout: 5 * 60 * 1000, + }, + + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + + webServer: { + command: 'npm run start', + url: 'https://127.0.0.1:1234/_ready', + timeout: 120 * 1000, + ignoreHTTPSErrors: true, + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index d56f5087a..ff121f3a5 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -255,6 +255,7 @@ export class HttpHeaders { public static partitionKey: string = "x-ms-documentdb-partitionkey"; public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput"; public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot"; + public static xAPIKey: string = "X-API-Key"; } export class ContentType { diff --git a/src/ConfigContext.ts b/src/ConfigContext.ts index 061f25286..564e905ba 100644 --- a/src/ConfigContext.ts +++ b/src/ConfigContext.ts @@ -42,6 +42,10 @@ export interface ConfigContext { ARM_API_VERSION: string; GRAPH_ENDPOINT: string; GRAPH_API_VERSION: string; + // This is the endpoint to get offering Ids to be used to fetch prices. Refer to this doc: https://learn.microsoft.com/en-us/rest/api/marketplacecatalog/dataplane/skus/list?view=rest-marketplacecatalog-dataplane-2023-05-01-preview&tabs=HTTP + CATALOG_ENDPOINT: string; + CATALOG_API_VERSION: string; + CATALOG_API_KEY: string; ARCADIA_ENDPOINT: string; ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string; BACKEND_ENDPOINT?: string; @@ -93,6 +97,9 @@ let configContext: Readonly = { ARM_API_VERSION: "2016-06-01", GRAPH_ENDPOINT: "https://graph.microsoft.com", GRAPH_API_VERSION: "1.6", + CATALOG_ENDPOINT: "https://catalogapi.azure.com/", + CATALOG_API_VERSION: "2023-05-01-preview", + CATALOG_API_KEY: "", ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net", ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net", GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1189306 @@ -192,6 +199,9 @@ if (process.env.NODE_ENV === "development") { updateConfigContext({ PROXY_PATH: "/proxy", EMULATOR_ENDPOINT: "https://localhost:8081", + PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac, + MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac, + CASSANDRA_PROXY_ENDPOINT: CassandraProxyEndpoints.Mpac, }); } diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index af69452f1..04afc10bb 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -425,6 +425,7 @@ export interface SelfServeFrameInputs { authorizationToken: string; csmEndpoint: string; flights?: readonly string[]; + catalogAPIKey: string; } export class MonacoEditorSettings { diff --git a/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx b/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx index 207642d84..80da43414 100644 --- a/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx +++ b/src/Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent.tsx @@ -1,4 +1,4 @@ -import { Icon, Label, Stack } from "@fluentui/react"; +import { DirectionalHint, Icon, Label, Stack, TooltipHost } from "@fluentui/react"; import * as React from "react"; import { NormalizedEventKey } from "../../../Common/Constants"; import { accordionStackTokens } from "../Settings/SettingsRenderUtils"; @@ -8,6 +8,7 @@ export interface CollapsibleSectionProps { isExpandedByDefault: boolean; onExpand?: () => void; children: JSX.Element; + tooltipContent?: string | JSX.Element | JSX.Element[]; } export interface CollapsibleSectionState { @@ -55,6 +56,19 @@ export class CollapsibleSectionComponent extends React.Component + {this.props.tooltipContent && ( + + + + )} {this.state.isExpanded && this.props.children} diff --git a/src/Explorer/Controls/CommandButton/CommandButtonComponent.tsx b/src/Explorer/Controls/CommandButton/CommandButtonComponent.tsx index 6337f947e..b6e847d54 100644 --- a/src/Explorer/Controls/CommandButton/CommandButtonComponent.tsx +++ b/src/Explorer/Controls/CommandButton/CommandButtonComponent.tsx @@ -31,7 +31,7 @@ export interface CommandButtonComponentProps { /** * Click handler for command button click */ - onCommandClick: (e: React.SyntheticEvent | KeyboardEvent) => void; + onCommandClick?: (e: React.SyntheticEvent | KeyboardEvent) => void; /** * Label for the button diff --git a/src/Explorer/Controls/ThroughputInput/ThroughputInput.less b/src/Explorer/Controls/ThroughputInput/ThroughputInput.less index ae7e873fa..059b210f6 100644 --- a/src/Explorer/Controls/ThroughputInput/ThroughputInput.less +++ b/src/Explorer/Controls/ThroughputInput/ThroughputInput.less @@ -20,6 +20,10 @@ outline-offset: 2px; } +.outlineNone{ + outline: none !important; +} + .copyQuery:focus::after, .deleteQuery:focus::after { outline: none !important; diff --git a/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx b/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx index 07b105642..2001700ad 100644 --- a/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx +++ b/src/Explorer/Controls/ThroughputInput/ThroughputInput.tsx @@ -223,6 +223,7 @@ export const ThroughputInput: FunctionComponent = ({ Estimate your required RU/s with{" "} = ({ Estimate your required RU/s with  - + capacity calculator . diff --git a/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap b/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap index 391d88845..eb21939c9 100644 --- a/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap +++ b/src/Explorer/Controls/ThroughputInput/__snapshots__/ThroughputInput.test.tsx.snap @@ -733,11 +733,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = ` ) => this.onNodeClick(event, node)} onKeyPress={(event: React.KeyboardEvent) => this.onNodeKeyPress(event, node)} @@ -174,9 +175,9 @@ export class LegacyTreeNodeComponent extends React.Component< >
{this.renderCollapseExpandIcon(node)} {node.iconSrc && } @@ -264,7 +265,7 @@ export class LegacyTreeNodeComponent extends React.Component< onMenuDismissed: (contextualMenu?: IContextualMenuProps) => this.setState({ isMenuShowing: false }), contextualMenuItemAs: (props: IContextualMenuItemProps) => (
e.target.dispatchEvent(LegacyTreeNodeComponent.createClickEvent())} > diff --git a/src/Explorer/Controls/TreeComponent/TreeNodeComponent.test.tsx b/src/Explorer/Controls/TreeComponent/TreeNodeComponent.test.tsx index 602edcb2a..e0cdf5700 100644 --- a/src/Explorer/Controls/TreeComponent/TreeNodeComponent.test.tsx +++ b/src/Explorer/Controls/TreeComponent/TreeNodeComponent.test.tsx @@ -124,6 +124,20 @@ describe("TreeNodeComponent", () => { expect(component).toMatchSnapshot(); }); + it("renders a node as expandable if it has empty, but defined, children array", () => { + const node = generateTestNode("root", { + isLoading: true, + children: [ + generateTestNode("child1", { + children: [], + }), + generateTestNode("child2"), + ], + }); + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + it("does not render children if the node is loading", () => { const node = generateTestNode("root", { isLoading: true, diff --git a/src/Explorer/Controls/TreeComponent/TreeNodeComponent.tsx b/src/Explorer/Controls/TreeComponent/TreeNodeComponent.tsx index 1717530ec..9ec2c904a 100644 --- a/src/Explorer/Controls/TreeComponent/TreeNodeComponent.tsx +++ b/src/Explorer/Controls/TreeComponent/TreeNodeComponent.tsx @@ -100,7 +100,8 @@ export const TreeNodeComponent: React.FC = ({ return unsortedChildren; }; - const isBranch = node.children?.length > 0; + // A branch node is any node with a defined children array, even if the array is empty. + const isBranch = !!node.children; const onOpenChange = useCallback( (_: TreeOpenChangeEvent, data: TreeOpenChangeData) => { @@ -146,9 +147,9 @@ export const TreeNodeComponent: React.FC = ({ const treeItem = (
`; +exports[`TreeNodeComponent renders a node as expandable if it has empty, but defined, children array 1`] = ` + + + } + style={ + Object { + "backgroundColor": undefined, + } + } + > + rootLabel + + +`; + exports[`TreeNodeComponent renders a node with a menu 1`] = ` | React.KeyboardEvent) => { - btn.onCommandClick(ev); - let copilotEnabled = false; - if (useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotUserDBEnabled) { - copilotEnabled = useQueryCopilot.getState().copilotEnabledforExecution; - } - TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label, copilotEnabled }); - }, + onClick: btn.onCommandClick + ? (ev?: React.MouseEvent | React.KeyboardEvent) => { + btn.onCommandClick(ev); + let copilotEnabled = false; + if (useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotUserDBEnabled) { + copilotEnabled = useQueryCopilot.getState().copilotEnabledforExecution; + } + TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label, copilotEnabled }); + } + : undefined, key: `${btn.commandButtonLabel}${index}`, text: label, - "data-test": label, title: btn.tooltipText, name: label, disabled: btn.disabled, ariaLabel: btn.ariaLabel, + "data-test": `CommandBar/Button:${label}`, buttonStyles: { root: { backgroundColor: backgroundColor, diff --git a/src/Explorer/Panes/AddCollectionPanel.tsx b/src/Explorer/Panes/AddCollectionPanel.tsx index c4fa38ec4..f208720cf 100644 --- a/src/Explorer/Panes/AddCollectionPanel.tsx +++ b/src/Explorer/Panes/AddCollectionPanel.tsx @@ -21,7 +21,7 @@ import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils"; import { configContext, Platform } from "ConfigContext"; import * as DataModels from "Contracts/DataModels"; import { SubscriptionType } from "Contracts/SubscriptionType"; -import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; +import { AddVectorEmbeddingPolicyForm } from "Explorer/Panes/VectorSearchPanel/AddVectorEmbeddingPolicyForm"; import { useSidePanel } from "hooks/useSidePanel"; import { useTeachingBubble } from "hooks/useTeachingBubble"; import React from "react"; @@ -82,22 +82,6 @@ export const AllPropertiesIndexed: DataModels.IndexingPolicy = { excludedPaths: [], }; -const DefaultDatabaseVectorIndex: DataModels.IndexingPolicy = { - indexingMode: "consistent", - automatic: true, - includedPaths: [ - { - path: "/*", - }, - ], - excludedPaths: [ - { - path: '/"_etag"/?', - }, - ], - vectorIndexes: [], -}; - export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = { vectorEmbeddings: [], }; @@ -122,8 +106,9 @@ export interface AddCollectionPanelState { isExecuting: boolean; isThroughputCapExceeded: boolean; teachingBubbleStep: number; - vectorIndexingPolicy: string; - vectorEmbeddingPolicy: string; + vectorIndexingPolicy: DataModels.VectorIndex[]; + vectorEmbeddingPolicy: DataModels.VectorEmbedding[]; + vectorPolicyValidated: boolean; } export class AddCollectionPanel extends React.Component { @@ -159,8 +144,9 @@ export class AddCollectionPanel extends React.Component - { - this.scrollToSection("collapsibleVectorPolicySectionContent"); - }} - > - - - Learn more - - this.setVectorIndexingPolicy(newIndexingPolicy)} - /> - - { this.scrollToSection("collapsibleVectorPolicySectionContent"); }} + tooltipContent={this.getContainerVectorPolicyTooltipContent()} > - - Learn more - - - this.setVectorEmbeddingPolicy(newVectorEmbeddingPolicy) - } - /> + + { + this.setState({ vectorEmbeddingPolicy, vectorIndexingPolicy, vectorPolicyValidated }); + }} + /> + @@ -1159,13 +1113,13 @@ export class AddCollectionPanel extends React.Component + Describe any properties in your data that contain vectors, so that they can be made available for similarity + queries.{" "} + + Learn more + + + ); + } + private shouldShowCollectionThroughputInput(): boolean { if (isServerlessAccount()) { return false; @@ -1370,20 +1336,9 @@ export class AddCollectionPanel extends React.Component Enter CQL command to create the table.{" "} - + Learn More diff --git a/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap b/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap index 8add09097..66d7da606 100644 --- a/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap +++ b/src/Explorer/Panes/DeleteCollectionConfirmationPane/__snapshots__/DeleteCollectionConfirmationPane.test.tsx.snap @@ -379,6 +379,7 @@ exports[`Delete Collection Confirmation Pane submit() should call delete collect > => { if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) { setFormError( - `Input database name "${databaseInput}" does not match the selected database "${selectedDatabase.id()}"`, + `Input ${getDatabaseName()} name "${databaseInput}" does not match the selected ${getDatabaseName()} "${selectedDatabase.id()}"`, ); - logConsoleError(`Error while deleting collection ${selectedDatabase && selectedDatabase.id()}`); + logConsoleError(`Error while deleting ${getDatabaseName()} ${selectedDatabase && selectedDatabase.id()}`); logConsoleError( - `Input database name "${databaseInput}" does not match the selected database "${selectedDatabase.id()}"`, + `Input ${getDatabaseName()} name "${databaseInput}" does not match the selected ${getDatabaseName()} "${selectedDatabase.id()}"`, ); return; } @@ -123,17 +124,18 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent {!formError && }
* - Confirm by typing the database id + Confirm by typing the {getDatabaseName()} id { @@ -149,7 +151,7 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent - What is the reason why you are deleting this database? + What is the reason why you are deleting this {getDatabaseName()}? = ( = ({ LocalStorageUtility.getEntryBoolean(StorageKey.QueryTimeoutEnabled), ); const [queryTimeout, setQueryTimeout] = useState(LocalStorageUtility.getEntryNumber(StorageKey.QueryTimeout)); + const [defaultQueryResultsView, setDefaultQueryResultsView] = useState( + getDefaultQueryResultsView(), + ); const [automaticallyCancelQueryAfterTimeout, setAutomaticallyCancelQueryAfterTimeout] = useState( LocalStorageUtility.getEntryBoolean(StorageKey.AutomaticallyCancelQueryAfterTimeout), ); @@ -125,6 +130,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism); LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString()); LocalStorageUtility.setEntryString(StorageKey.CopilotSampleDBEnabled, copilotSampleDBEnabled.toString()); + LocalStorageUtility.setEntryString(StorageKey.DefaultQueryResultsView, defaultQueryResultsView); if (shouldShowGraphAutoVizOption) { LocalStorageUtility.setEntryBoolean( @@ -201,6 +207,11 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ { key: Constants.PriorityLevel.High, text: "High" }, ]; + const defaultQueryResultsViewOptionList: IChoiceGroupOption[] = [ + { key: SplitterDirection.Vertical, text: "Vertical" }, + { key: SplitterDirection.Horizontal, text: "Horizontal" }, + ]; + const handleOnPriorityLevelOptionChange = ( ev: React.FormEvent, option: IChoiceGroupOption, @@ -238,6 +249,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ } }; + const handleOnDefaultQueryResultsViewChange = ( + ev: React.MouseEvent, + option: IChoiceGroupOption, + ): void => { + setDefaultQueryResultsView(option.key as SplitterDirection); + }; + const handleOnQueryRetryAttemptsSpinButtonChange = (ev: React.MouseEvent, newValue?: string): void => { const retryAttempts = Number(newValue); if (!isNaN(retryAttempts)) { @@ -442,6 +460,25 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ )}
+
+
+
+ + Default Query Results View + + Select the default view to use when displaying query results. +
+
+ +
+
+
)}
diff --git a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap index 6f4ac0c06..f887a53dd 100644 --- a/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap +++ b/src/Explorer/Panes/SettingsPane/__snapshots__/SettingsPane.test.tsx.snap @@ -205,6 +205,67 @@ exports[`Settings Pane should render Default properly 1`] = `
+
+
+
+ + Default Query Results View + + + Select the default view to use when displaying query results. + +
+
+ +
+
+
diff --git a/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap b/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap index e8553fa4e..4e569f93e 100644 --- a/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap +++ b/src/Explorer/Panes/StringInputPane/__snapshots__/StringInputPane.test.tsx.snap @@ -688,6 +688,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = ` > { + let component: RenderResult; + + beforeEach(() => { + component = render( + , + ); + }); + + test("renders correctly", () => { + expect(screen.getByText("Vector embedding 1")).toBeInTheDocument(); + expect(screen.getByPlaceholderText("/vector1")).toBeInTheDocument(); + }); + + test("calls onVectorEmbeddingChange on adding a new vector embedding", () => { + fireEvent.click(screen.getByText("Add vector embedding")); + expect(mockOnVectorEmbeddingChange).toHaveBeenCalled(); + }); + + test("calls onDelete when delete button is clicked", async () => { + const deleteButton = component.container.querySelector("#delete-vector-policy-1"); + fireEvent.click(deleteButton); + expect(mockOnVectorEmbeddingChange).toHaveBeenCalled(); + expect(screen.queryByText("Vector embedding 1")).toBeNull(); + }); + + test("calls onVectorEmbeddingPathChange on input change", () => { + fireEvent.change(screen.getByPlaceholderText("/vector1"), { target: { value: "/newPath" } }); + expect(mockOnVectorEmbeddingChange).toHaveBeenCalled(); + }); + + test("validates input correctly", async () => { + fireEvent.change(screen.getByPlaceholderText("/vector1"), { target: { value: "" } }); + await waitFor(() => expect(screen.getByText("Vector embedding path should not be empty")).toBeInTheDocument(), { + timeout: 1500, + }); + await waitFor( + () => + expect( + screen.getByText("Vector embedding dimension must be greater than 0 and less than or equal 4096"), + ).toBeInTheDocument(), + { + timeout: 1500, + }, + ); + fireEvent.change(component.container.querySelector("#vector-policy-dimension-1"), { target: { value: "4096" } }); + fireEvent.change(screen.getByPlaceholderText("/vector1"), { target: { value: "/vector1" } }); + await waitFor(() => expect(screen.queryByText("Vector embedding path should not be empty")).toBeNull(), { + timeout: 1500, + }); + await waitFor( + () => expect(screen.queryByText("Maximum allowed dimension for flat index is 505")).toBeInTheDocument(), + { + timeout: 1500, + }, + ); + }); + + test("duplicate vector path is not allowed", async () => { + fireEvent.click(screen.getByText("Add vector embedding")); + fireEvent.change(component.container.querySelector("#vector-policy-path-2"), { target: { value: "/vector1" } }); + await waitFor(() => expect(screen.queryByText("Vector embedding path is already defined")).toBeNull(), { + timeout: 1500, + }); + }); +}); diff --git a/src/Explorer/Panes/VectorSearchPanel/AddVectorEmbeddingPolicyForm.tsx b/src/Explorer/Panes/VectorSearchPanel/AddVectorEmbeddingPolicyForm.tsx new file mode 100644 index 000000000..a5012cad5 --- /dev/null +++ b/src/Explorer/Panes/VectorSearchPanel/AddVectorEmbeddingPolicyForm.tsx @@ -0,0 +1,300 @@ +import { + DefaultButton, + Dropdown, + IDropdownOption, + IStyleFunctionOrObject, + ITextFieldStyleProps, + ITextFieldStyles, + IconButton, + Label, + Stack, + TextField, +} from "@fluentui/react"; +import { VectorEmbedding, VectorIndex } from "Contracts/DataModels"; +import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent"; +import { + getDataTypeOptions, + getDistanceFunctionOptions, + getIndexTypeOptions, +} from "Explorer/Panes/VectorSearchPanel/VectorSearchUtils"; +import React, { FunctionComponent, useState } from "react"; + +export interface IAddVectorEmbeddingPolicyFormProps { + vectorEmbedding: VectorEmbedding[]; + vectorIndex: VectorIndex[]; + onVectorEmbeddingChange: ( + vectorEmbeddings: VectorEmbedding[], + vectorIndexingPolicies: VectorIndex[], + validationPassed: boolean, + ) => void; +} + +export interface VectorEmbeddingPolicyData { + path: string; + dataType: VectorEmbedding["dataType"]; + distanceFunction: VectorEmbedding["distanceFunction"]; + dimensions: number; + indexType: VectorIndex["type"] | "none"; + pathError: string; + dimensionsError: string; +} + +type VectorEmbeddingPolicyProperty = "dataType" | "distanceFunction" | "indexType"; + +const textFieldStyles: IStyleFunctionOrObject = { + fieldGroup: { + height: 27, + }, + field: { + fontSize: 12, + padding: "0 8px", + }, +}; + +const dropdownStyles = { + title: { + height: 27, + lineHeight: "24px", + fontSize: 12, + }, + dropdown: { + height: 27, + lineHeight: "24px", + }, + dropdownItem: { + fontSize: 12, + }, +}; + +export const AddVectorEmbeddingPolicyForm: FunctionComponent = ({ + vectorEmbedding, + vectorIndex, + onVectorEmbeddingChange, +}): JSX.Element => { + const onVectorEmbeddingPathError = (path: string, index?: number): string => { + let error = ""; + if (!path) { + error = "Vector embedding path should not be empty"; + } + if ( + index >= 0 && + vectorEmbeddingPolicyData?.find( + (vectorEmbedding: VectorEmbeddingPolicyData, dataIndex: number) => + dataIndex !== index && vectorEmbedding.path === path, + ) + ) { + error = "Vector embedding path is already defined"; + } + return error; + }; + + const onVectorEmbeddingDimensionError = (dimension: number, indexType: VectorIndex["type"] | "none"): string => { + let error = ""; + if (dimension <= 0 || dimension > 4096) { + error = "Vector embedding dimension must be greater than 0 and less than or equal 4096"; + } + if (indexType === "flat" && dimension > 505) { + error = "Maximum allowed dimension for flat index is 505"; + } + return error; + }; + + const initializeData = (vectorEmbedding: VectorEmbedding[], vectorIndex: VectorIndex[]) => { + const mergedData: VectorEmbeddingPolicyData[] = []; + vectorEmbedding.forEach((embedding) => { + const matchingIndex = vectorIndex.find((index) => index.path === embedding.path); + mergedData.push({ + ...embedding, + indexType: matchingIndex?.type || "none", + pathError: onVectorEmbeddingPathError(embedding.path), + dimensionsError: onVectorEmbeddingDimensionError(embedding.dimensions, matchingIndex?.type || "none"), + }); + }); + return mergedData; + }; + + const [vectorEmbeddingPolicyData, setVectorEmbeddingPolicyData] = useState( + initializeData(vectorEmbedding, vectorIndex), + ); + + React.useEffect(() => { + propagateData(); + }, [vectorEmbeddingPolicyData]); + + const propagateData = () => { + const vectorEmbeddings: VectorEmbedding[] = vectorEmbeddingPolicyData.map((policy: VectorEmbeddingPolicyData) => ({ + dataType: policy.dataType, + dimensions: policy.dimensions, + distanceFunction: policy.distanceFunction, + path: policy.path, + })); + const vectorIndexingPolicies: VectorIndex[] = vectorEmbeddingPolicyData + .filter((policy: VectorEmbeddingPolicyData) => policy.indexType !== "none") + .map( + (policy) => + ({ + path: policy.path, + type: policy.indexType, + }) as VectorIndex, + ); + const validationPassed = vectorEmbeddingPolicyData.every( + (policy: VectorEmbeddingPolicyData) => policy.pathError === "" && policy.dimensionsError === "", + ); + onVectorEmbeddingChange(vectorEmbeddings, vectorIndexingPolicies, validationPassed); + }; + + const onVectorEmbeddingPathChange = (index: number, event: React.ChangeEvent) => { + const value = event.target.value.trim(); + const vectorEmbeddings = [...vectorEmbeddingPolicyData]; + if (!vectorEmbeddings[index]?.path && !value.startsWith("/")) { + vectorEmbeddings[index].path = "/" + value; + } else { + vectorEmbeddings[index].path = value; + } + const error = onVectorEmbeddingPathError(value, index); + vectorEmbeddings[index].pathError = error; + setVectorEmbeddingPolicyData(vectorEmbeddings); + }; + + const onVectorEmbeddingDimensionsChange = (index: number, event: React.ChangeEvent) => { + const value = parseInt(event.target.value.trim()) || 0; + const vectorEmbeddings = [...vectorEmbeddingPolicyData]; + const vectorEmbedding = vectorEmbeddings[index]; + vectorEmbeddings[index].dimensions = value; + const error = onVectorEmbeddingDimensionError(value, vectorEmbedding.indexType); + vectorEmbeddings[index].dimensionsError = error; + setVectorEmbeddingPolicyData(vectorEmbeddings); + }; + + const onVectorEmbeddingIndexTypeChange = (index: number, option: IDropdownOption): void => { + const vectorEmbeddings = [...vectorEmbeddingPolicyData]; + const vectorEmbedding = vectorEmbeddings[index]; + vectorEmbeddings[index].indexType = option.key as never; + const error = onVectorEmbeddingDimensionError(vectorEmbedding.dimensions, vectorEmbedding.indexType); + vectorEmbeddings[index].dimensionsError = error; + setVectorEmbeddingPolicyData(vectorEmbeddings); + }; + + const onVectorEmbeddingPolicyChange = ( + index: number, + option: IDropdownOption, + property: VectorEmbeddingPolicyProperty, + ): void => { + const vectorEmbeddings = [...vectorEmbeddingPolicyData]; + vectorEmbeddings[index][property] = option.key as never; + setVectorEmbeddingPolicyData(vectorEmbeddings); + }; + + const onAdd = () => { + setVectorEmbeddingPolicyData([ + ...vectorEmbeddingPolicyData, + { + path: "", + dataType: "float32", + distanceFunction: "euclidean", + dimensions: 0, + indexType: "none", + pathError: onVectorEmbeddingPathError(""), + dimensionsError: onVectorEmbeddingDimensionError(0, "none"), + }, + ]); + }; + + const onDelete = (index: number) => { + const vectorEmbeddings = vectorEmbeddingPolicyData.filter((_uniqueKey, j) => index !== j); + setVectorEmbeddingPolicyData(vectorEmbeddings); + }; + + return ( + + {vectorEmbeddingPolicyData.length > 0 && + vectorEmbeddingPolicyData.map((vectorEmbeddingPolicy: VectorEmbeddingPolicyData, index: number) => ( + + + + + + ) => onVectorEmbeddingPathChange(index, event)} + value={vectorEmbeddingPolicy.path || ""} + errorMessage={vectorEmbeddingPolicy.pathError} + /> + + + + , option: IDropdownOption) => + onVectorEmbeddingPolicyChange(index, option, "dataType") + } + > + + + + , option: IDropdownOption) => + onVectorEmbeddingPolicyChange(index, option, "distanceFunction") + } + > + + + + ) => + onVectorEmbeddingDimensionsChange(index, event) + } + errorMessage={vectorEmbeddingPolicy.dimensionsError} + /> + + + + , option: IDropdownOption) => + onVectorEmbeddingIndexTypeChange(index, option) + } + > + + + onDelete(index)} + /> + + + ))} + + Add vector embedding + + + ); +}; diff --git a/src/Explorer/Panes/VectorSearchPanel/VectorSearchUtils.ts b/src/Explorer/Panes/VectorSearchPanel/VectorSearchUtils.ts new file mode 100644 index 000000000..0f5547aa7 --- /dev/null +++ b/src/Explorer/Panes/VectorSearchPanel/VectorSearchUtils.ts @@ -0,0 +1,16 @@ +import { IDropdownOption } from "@fluentui/react"; + +const dataTypes = ["float32", "uint8", "int8"]; +const distanceFunctions = ["euclidean", "cosine", "dotproduct"]; +const indexTypes = ["none", "flat", "diskANN", "quantizedFlat"]; + +export const getDataTypeOptions = (): IDropdownOption[] => createDropdownOptionsFromLiterals(dataTypes); +export const getDistanceFunctionOptions = (): IDropdownOption[] => createDropdownOptionsFromLiterals(distanceFunctions); +export const getIndexTypeOptions = (): IDropdownOption[] => createDropdownOptionsFromLiterals(indexTypes); + +function createDropdownOptionsFromLiterals(literals: T[]): IDropdownOption[] { + return literals.map((value) => ({ + key: value, + text: value, + })); +} diff --git a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap index 6e32d62c0..c6a239ebf 100644 --- a/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap +++ b/src/Explorer/Panes/__snapshots__/DeleteDatabaseConfirmationPanel.test.tsx.snap @@ -361,12 +361,15 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = ` - Confirm by typing the database id + Confirm by typing the + Database + id - What is the reason why you are deleting this database? + What is the reason why you are deleting this + Database + ?