From f566c600e2df8287618b59add025800387a02794 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 29 Jul 2022 15:03:25 +0100 Subject: [PATCH] Port remaining Puppeteer tests over to Cypress (#9104) * Port remaining Puppeteer tests over to Cypress * Remove puppeteer support files * Fix lifecycle matrixclientpeg setup race condition * Alternative solution to the lifecycle problem * Dismiss the notifications toast --- .github/workflows/end-to-end-tests.yaml | 57 - cypress/e2e/toasts/analytics-toast.ts | 98 ++ package.json | 1 - scripts/ci/prepare-end-to-end-tests.sh | 27 - scripts/ci/run-end-to-end-tests.sh | 19 - src/Lifecycle.ts | 2 +- src/components/structures/MatrixChat.tsx | 11 +- test/end-to-end-tests/.gitignore | 7 - test/end-to-end-tests/README.md | 49 - test/end-to-end-tests/TODO.md | 8 - test/end-to-end-tests/Windows.md | 45 - test/end-to-end-tests/element/.gitignore | 2 - .../element/config-template/config.json | 28 - .../element/install-webserver.sh | 18 - test/end-to-end-tests/element/install.sh | 16 - test/end-to-end-tests/element/start.sh | 54 - test/end-to-end-tests/element/stop.sh | 22 - test/end-to-end-tests/has-custom-app.js | 24 - test/end-to-end-tests/install.sh | 7 - test/end-to-end-tests/package.json | 24 - .../end-to-end-tests/pick-synapse-log-file.js | 26 - test/end-to-end-tests/run.sh | 44 - test/end-to-end-tests/src/@types/global.d.ts | 24 - test/end-to-end-tests/src/logbuffer.ts | 34 - test/end-to-end-tests/src/logger.ts | 62 - test/end-to-end-tests/src/scenario.ts | 40 - test/end-to-end-tests/src/scenarios/README.md | 1 - test/end-to-end-tests/src/scenarios/toast.ts | 42 - test/end-to-end-tests/src/session.ts | 153 --- test/end-to-end-tests/src/usecases/README.md | 2 - .../src/usecases/accept-invite.ts | 39 - .../src/usecases/create-room.ts | 88 -- test/end-to-end-tests/src/usecases/dialog.ts | 47 - test/end-to-end-tests/src/usecases/invite.ts | 43 - test/end-to-end-tests/src/usecases/join.ts | 34 - test/end-to-end-tests/src/usecases/login.ts | 91 -- .../src/usecases/memberlist.ts | 46 - .../src/usecases/rightpanel.ts | 74 -- .../src/usecases/room-settings.ts | 240 ---- .../end-to-end-tests/src/usecases/security.ts | 43 - .../src/usecases/select-room.ts | 35 - .../src/usecases/send-message.ts | 36 - .../src/usecases/send-sticker.ts | 85 -- .../end-to-end-tests/src/usecases/settings.ts | 30 - test/end-to-end-tests/src/usecases/signup.ts | 93 -- .../end-to-end-tests/src/usecases/timeline.ts | 89 -- test/end-to-end-tests/src/usecases/toasts.ts | 47 - test/end-to-end-tests/src/usecases/verify.ts | 115 -- test/end-to-end-tests/src/util.ts | 97 -- test/end-to-end-tests/start.ts | 149 --- test/end-to-end-tests/synapse/.gitignore | 2 - .../config-templates/consent/homeserver.yaml | 1141 ----------------- .../consent/res/templates/privacy/en/1.0.html | 23 - .../res/templates/privacy/en/success.html | 9 - test/end-to-end-tests/synapse/getcfg.sh | 17 - test/end-to-end-tests/synapse/install.sh | 43 - test/end-to-end-tests/synapse/start.sh | 9 - test/end-to-end-tests/synapse/stop.sh | 8 - test/end-to-end-tests/tsconfig.json | 23 - test/end-to-end-tests/yarn.lock | 886 ------------- 60 files changed, 105 insertions(+), 4524 deletions(-) delete mode 100644 .github/workflows/end-to-end-tests.yaml create mode 100644 cypress/e2e/toasts/analytics-toast.ts delete mode 100755 scripts/ci/prepare-end-to-end-tests.sh delete mode 100755 scripts/ci/run-end-to-end-tests.sh delete mode 100644 test/end-to-end-tests/.gitignore delete mode 100644 test/end-to-end-tests/README.md delete mode 100644 test/end-to-end-tests/TODO.md delete mode 100644 test/end-to-end-tests/Windows.md delete mode 100644 test/end-to-end-tests/element/.gitignore delete mode 100644 test/end-to-end-tests/element/config-template/config.json delete mode 100755 test/end-to-end-tests/element/install-webserver.sh delete mode 100755 test/end-to-end-tests/element/install.sh delete mode 100755 test/end-to-end-tests/element/start.sh delete mode 100755 test/end-to-end-tests/element/stop.sh delete mode 100644 test/end-to-end-tests/has-custom-app.js delete mode 100755 test/end-to-end-tests/install.sh delete mode 100644 test/end-to-end-tests/package.json delete mode 100644 test/end-to-end-tests/pick-synapse-log-file.js delete mode 100755 test/end-to-end-tests/run.sh delete mode 100644 test/end-to-end-tests/src/@types/global.d.ts delete mode 100644 test/end-to-end-tests/src/logbuffer.ts delete mode 100644 test/end-to-end-tests/src/logger.ts delete mode 100644 test/end-to-end-tests/src/scenario.ts delete mode 100644 test/end-to-end-tests/src/scenarios/README.md delete mode 100644 test/end-to-end-tests/src/scenarios/toast.ts delete mode 100644 test/end-to-end-tests/src/session.ts delete mode 100644 test/end-to-end-tests/src/usecases/README.md delete mode 100644 test/end-to-end-tests/src/usecases/accept-invite.ts delete mode 100644 test/end-to-end-tests/src/usecases/create-room.ts delete mode 100644 test/end-to-end-tests/src/usecases/dialog.ts delete mode 100644 test/end-to-end-tests/src/usecases/invite.ts delete mode 100644 test/end-to-end-tests/src/usecases/join.ts delete mode 100644 test/end-to-end-tests/src/usecases/login.ts delete mode 100644 test/end-to-end-tests/src/usecases/memberlist.ts delete mode 100644 test/end-to-end-tests/src/usecases/rightpanel.ts delete mode 100644 test/end-to-end-tests/src/usecases/room-settings.ts delete mode 100644 test/end-to-end-tests/src/usecases/security.ts delete mode 100644 test/end-to-end-tests/src/usecases/select-room.ts delete mode 100644 test/end-to-end-tests/src/usecases/send-message.ts delete mode 100644 test/end-to-end-tests/src/usecases/send-sticker.ts delete mode 100644 test/end-to-end-tests/src/usecases/settings.ts delete mode 100644 test/end-to-end-tests/src/usecases/signup.ts delete mode 100644 test/end-to-end-tests/src/usecases/timeline.ts delete mode 100644 test/end-to-end-tests/src/usecases/toasts.ts delete mode 100644 test/end-to-end-tests/src/usecases/verify.ts delete mode 100644 test/end-to-end-tests/src/util.ts delete mode 100644 test/end-to-end-tests/start.ts delete mode 100644 test/end-to-end-tests/synapse/.gitignore delete mode 100644 test/end-to-end-tests/synapse/config-templates/consent/homeserver.yaml delete mode 100644 test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/1.0.html delete mode 100644 test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/success.html delete mode 100755 test/end-to-end-tests/synapse/getcfg.sh delete mode 100755 test/end-to-end-tests/synapse/install.sh delete mode 100755 test/end-to-end-tests/synapse/start.sh delete mode 100755 test/end-to-end-tests/synapse/stop.sh delete mode 100644 test/end-to-end-tests/tsconfig.json delete mode 100644 test/end-to-end-tests/yarn.lock diff --git a/.github/workflows/end-to-end-tests.yaml b/.github/workflows/end-to-end-tests.yaml deleted file mode 100644 index 48abcfaf81..0000000000 --- a/.github/workflows/end-to-end-tests.yaml +++ /dev/null @@ -1,57 +0,0 @@ -name: End-to-end Tests -on: - # These tests won't work for non-develop branches at the moment as they - # won't pull in the right versions of other repos, so they're only enabled - # on develop. - push: - branches: [ develop, master ] - pull_request: - branches: [ develop ] - repository_dispatch: - types: [ upstream-sdk-notify ] -env: - # These must be set for fetchdep.sh to get the right branch - REPOSITORY: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} -jobs: - end-to-end: - runs-on: ubuntu-latest - container: vectorim/element-web-ci-e2etests-env:latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - uses: actions/setup-node@v3 - with: - cache: 'yarn' - - - name: Prepare End-to-End tests - run: ./scripts/ci/prepare-end-to-end-tests.sh - - - name: Run End-to-End tests - run: ./scripts/ci/run-end-to-end-tests.sh - - - name: Archive logs - uses: actions/upload-artifact@v2 - if: ${{ always() }} - with: - path: | - test/end-to-end-tests/logs/**/* - test/end-to-end-tests/synapse/installations/consent/homeserver.log - retention-days: 14 - - - name: Store benchmark result - if: github.ref == 'refs/heads/develop' && github.repository == 'matrix-org/matrix-react-sdk' - uses: matrix-org/github-action-benchmark@jsperfentry-1 - with: - tool: 'jsperformanceentry' - output-file-path: test/end-to-end-tests/performance-entries.json - # This is the default dashboard path. It's included here anyway to - # make the difference from the Cypress variant in - # `element-build-and-test.yaml` more obvious. - # The dashboard is available at https://matrix-org.github.io/matrix-react-sdk/dev/bench/ - benchmark-data-dir-path: dev/bench - fail-on-alert: false - comment-on-alert: false - github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} - auto-push: ${{ github.ref == 'refs/heads/develop' }} diff --git a/cypress/e2e/toasts/analytics-toast.ts b/cypress/e2e/toasts/analytics-toast.ts new file mode 100644 index 0000000000..547e46bf68 --- /dev/null +++ b/cypress/e2e/toasts/analytics-toast.ts @@ -0,0 +1,98 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import { SynapseInstance } from "../../plugins/synapsedocker"; +import Chainable = Cypress.Chainable; + +function assertNoToasts(): void { + cy.get(".mx_Toast_toast").should("not.exist"); +} + +function getToast(expectedTitle: string): Chainable { + return cy.get(".mx_Toast_toast").contains("h2", expectedTitle).should("exist").closest(".mx_Toast_toast"); +} + +function acceptToast(expectedTitle: string): void { + getToast(expectedTitle).within(() => { + cy.get(".mx_Toast_buttons .mx_AccessibleButton_kind_primary").click(); + }); +} + +function rejectToast(expectedTitle: string): void { + getToast(expectedTitle).within(() => { + cy.get(".mx_Toast_buttons .mx_AccessibleButton_kind_danger_outline").click(); + }); +} + +describe("Analytics Toast", () => { + let synapse: SynapseInstance; + + afterEach(() => { + cy.stopSynapse(synapse); + }); + + it("should not show an analytics toast if config has nothing about posthog", () => { + cy.intercept("/config.json?cachebuster=*", req => { + req.continue(res => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { posthog, ...body } = res.body; + res.send(200, body); + }); + }); + + cy.startSynapse("default").then(data => { + synapse = data; + cy.initTestUser(synapse, "Tod"); + }); + + rejectToast("Notifications"); + assertNoToasts(); + }); + + describe("with posthog enabled", () => { + beforeEach(() => { + cy.intercept("/config.json?cachebuster=*", req => { + req.continue(res => { + res.send(200, { + ...res.body, + posthog: { + project_api_key: "foo", + api_host: "bar", + }, + }); + }); + }); + + cy.startSynapse("default").then(data => { + synapse = data; + cy.initTestUser(synapse, "Tod"); + rejectToast("Notifications"); + }); + }); + + it("should show an analytics toast which can be accepted", () => { + acceptToast("Help improve Element"); + assertNoToasts(); + }); + + it("should show an analytics toast which can be rejected", () => { + rejectToast("Help improve Element"); + assertNoToasts(); + }); + }); +}); diff --git a/package.json b/package.json index ff48e4e800..0ac7006add 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "test": "jest", "test:cypress": "cypress run", "test:cypress:open": "cypress open", - "test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080", "coverage": "yarn test --coverage" }, "dependencies": { diff --git a/scripts/ci/prepare-end-to-end-tests.sh b/scripts/ci/prepare-end-to-end-tests.sh deleted file mode 100755 index 147e1f6445..0000000000 --- a/scripts/ci/prepare-end-to-end-tests.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -ev - -handle_error() { - EXIT_CODE=$? - exit $EXIT_CODE -} - -trap 'handle_error' ERR - -echo "--- Building Element" -scripts/ci/layered.sh -cd element-web -element_web_dir=`pwd` -CI_PACKAGE=true yarn build -cd .. -# prepare end to end tests -pushd test/end-to-end-tests -ln -s $element_web_dir element/element-web -# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh -# CHROME_PATH=$(which google-chrome-stable) ./run.sh -echo "--- Install synapse & other dependencies" -./install.sh -# install static webserver to server symlinked local copy of element -./element/install-webserver.sh -popd diff --git a/scripts/ci/run-end-to-end-tests.sh b/scripts/ci/run-end-to-end-tests.sh deleted file mode 100755 index 3c99391fc7..0000000000 --- a/scripts/ci/run-end-to-end-tests.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -ev - -handle_error() { - EXIT_CODE=$? - exit $EXIT_CODE -} - -trap 'handle_error' ERR - -# run end to end tests -pushd test/end-to-end-tests -rm -r logs || true -mkdir logs -echo "--- Running end-to-end tests" -TESTS_STARTED=1 -./run.sh --no-sandbox --log-directory logs/ -popd diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 915bc85dd7..df9dec9f5e 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -635,8 +635,8 @@ async function doSetLoggedIn( } dis.fire(Action.OnLoggedIn); - await startMatrixClient(/*startSyncing=*/!softLogout); + return client; } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index db0d84e3d0..41be62e1e6 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1326,11 +1326,6 @@ export default class MatrixChat extends React.PureComponent { this.showScreenAfterLogin(); } - // Will be moved to a pre-login flow as well - if (PosthogAnalytics.instance.isEnabled() && SettingsStore.isLevelSupported(SettingLevel.ACCOUNT)) { - this.initPosthogAnalyticsToast(); - } - if (SdkConfig.get("mobile_guide_toast")) { // The toast contains further logic to detect mobile platforms, // check if it has been dismissed before, etc. @@ -1646,6 +1641,12 @@ export default class MatrixChat extends React.PureComponent { // we implement more settings. cli.setGlobalErrorOnUnknownDevices(false); } + + // Cannot be done in OnLoggedIn as at that point the AccountSettingsHandler doesn't yet have a client + // Will be moved to a pre-login flow as well + if (PosthogAnalytics.instance.isEnabled() && SettingsStore.isLevelSupported(SettingLevel.ACCOUNT)) { + this.initPosthogAnalyticsToast(); + } } public showScreen(screen: string, params?: {[key: string]: any}) { diff --git a/test/end-to-end-tests/.gitignore b/test/end-to-end-tests/.gitignore deleted file mode 100644 index a604178f5f..0000000000 --- a/test/end-to-end-tests/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules -*.png -element/env -performance-entries.json -lib -logs -homeserver.log diff --git a/test/end-to-end-tests/README.md b/test/end-to-end-tests/README.md deleted file mode 100644 index dc5022b405..0000000000 --- a/test/end-to-end-tests/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Matrix React SDK End-to-End tests - -This directory contains tests for matrix-react-sdk. The tests fire up a headless Chrome and simulate user interaction (end-to-end). Note that end-to-end has little to do with the end-to-end encryption Matrix supports, just that we test the full stack, going from user interaction to expected DOM in the browser. - -## Setup - -Run `./install.sh`. This will: - - install Synapse, fetches the develop branch at the moment. If anything fails here, please refer to the Synapse README to see if you're missing one of the prerequisites. - - install Element Web, this fetches the develop branch at the moment. - - install dependencies (will download copy of Chrome) - -## Running the tests - -Run tests with `./run.sh`. - -### Debug tests locally. - -`./run.sh` will run the tests against the Element copy present in `element/element-web` served by a static Python HTTP server. You can symlink your `element-web` develop copy here but that doesn't work well with Webpack recompiling. You can run the test runner directly and specify parameters to get more insight into a failure or run the tests against your local Webpack server. - -``` -./synapse/stop.sh && \ -./synapse/start.sh && \ -node start.js -``` -It's important to always stop and start Synapse before each run of the tests to clear the in-memory SQLite database it uses, as the tests assume a blank slate. - -start.js accepts these parameters (and more, see `node start.js --help`) that can help running the tests locally: - - - `--app-url ` don't use the Element Web copy and static server provided by the tests, but use a running server like the Webpack watch server to run the tests against. - - `--slow-mo` type at a human speed, useful with `--windowed`. - - `--throttle-cpu ` throttle cpu in the browser by the given factor. Useful to reproduce failures because of insufficient timeouts happening on the slower CI server. - - `--windowed` run the tests in an actual browser window Try to limit interacting with the windows while the tests are running. Hovering over the window tends to fail the tests, dragging the title bar should be fine though. - - `--dev-tools` open the devtools in the browser window, only applies if `--windowed` is set as well. - -For god level debug (e.g. for debugging slow tests): - -`env DEBUG="puppeteer:*" ./test/end-to-end-tests/run.sh --app-url http://localhost:8080 --log-directory `pwd`/logs --dev-tools --windowed` 2>&1 | cat - -(piping everything through cat means you get proper timestamps on the debugging, and the chromiums hang around at the end) - -Developer Guide -=============== - -Please follow the standard Matrix contributor's guide: -https://github.com/matrix-org/synapse/tree/master/CONTRIBUTING.rst - -Please follow the Matrix JS/React code style as per: -https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md - diff --git a/test/end-to-end-tests/TODO.md b/test/end-to-end-tests/TODO.md deleted file mode 100644 index f5ccebcf77..0000000000 --- a/test/end-to-end-tests/TODO.md +++ /dev/null @@ -1,8 +0,0 @@ - - - join a peekable room by directory - - join a peekable room by invite - - join a non-peekable room by directory - - join a non-peekable room by invite - - leave a room and check we move to the "next" room - - get kicked from a room and check that we show the correct message - - get banned " diff --git a/test/end-to-end-tests/Windows.md b/test/end-to-end-tests/Windows.md deleted file mode 100644 index 4fbf42d160..0000000000 --- a/test/end-to-end-tests/Windows.md +++ /dev/null @@ -1,45 +0,0 @@ -# Running the end-to-end tests on Windows - -Windows is not the best platform to run the tests on, but if you have to, enable Windows Subsystem for Linux (WSL) -and start following these steps to get going: - -1. Navigate to your working directory (`cd /mnt/c/users/travisr/whatever/matrix-react-sdk` for example). -2. Run `sudo apt-get install unzip python3 virtualenv dos2unix` -3. Run `dos2unix ./test/end-to-end-tests/*.sh ./test/end-to-end-tests/synapse/*.sh ./test/end-to-end-tests/element/*.sh` -4. Install NodeJS for ubuntu: - ```bash - curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - - sudo apt-get update - sudo apt-get install nodejs - ``` -5. Run `yarn link` and `yarn install` for all layers from WSL if you haven't already. If you want to switch back to - your Windows host after your tests then you'll need to re-run `yarn install` (and possibly `yarn link`) there too. - Though, do note that you can access `http://localhost:8080` in your Windows-based browser when running webpack in - the WSL environment (it does *not* work the other way around, annoyingly). -6. In WSL, run `yarn start` at the element-web layer to get things going. -7. While that builds... Run: - ```bash - sudo apt-get install x11-apps - wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb - sudo dpkg -i google-chrome-stable_current_amd64.deb - sudo apt -f install - ``` -8. Run: - ```bash - cd ./test/end-to-end-tests - ./synapse/install.sh - ./install.sh - ./run.sh --app-url http://localhost:8080 --log-directory ./logs - ``` - -Note that using `yarn test:e2e` probably won't work for you. You might also have to use the config.json from the -`element/config-template` directory in order to actually succeed at the tests. - -Also note that you'll have to use `--no-sandbox` otherwise Chrome will complain that there's no sandbox available. You -could probably fix this with enough effort, or you could run a headless Chrome in the WSL container without a sandbox. - - -Reference material that isn't fully represented in the steps above (but snippets have been borrowed): -* https://virtualizationreview.com/articles/2017/02/08/graphical-programs-on-windows-subsystem-on-linux.aspx -* https://gist.github.com/drexler/d70ab957f964dbef1153d46bd853c775 -* https://docs.microsoft.com/en-us/windows/wsl/networking#accessing-windows-networking-apps-from-linux-host-ip diff --git a/test/end-to-end-tests/element/.gitignore b/test/end-to-end-tests/element/.gitignore deleted file mode 100644 index 57fac2072f..0000000000 --- a/test/end-to-end-tests/element/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -element-web -element.pid diff --git a/test/end-to-end-tests/element/config-template/config.json b/test/end-to-end-tests/element/config-template/config.json deleted file mode 100644 index 66e1295af3..0000000000 --- a/test/end-to-end-tests/element/config-template/config.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "default_hs_url": "http://localhost:5005", - "default_is_url": "https://vector.im", - "disable_custom_urls": false, - "disable_guests": false, - "disable_login_language_selector": false, - "disable_3pid_login": false, - "brand": "Element", - "integrations_ui_url": "https://scalar.vector.im/", - "integrations_rest_url": "https://scalar.vector.im/api", - "bug_report_endpoint_url": "https://element.io/bugreports/submit", - "showLabsSettings": true, - "default_federate": true, - "welcomePageUrl": "home.html", - "default_theme": "light", - "roomDirectory": { - "servers": [ - "localhost:5005" - ] - }, - "posthog": { - "projectApiKey": "not-a-real-api-key", - "apiHost": "http://localhost:5005" - }, - "enable_presence_by_hs_url": { - "https://matrix.org": false - } -} diff --git a/test/end-to-end-tests/element/install-webserver.sh b/test/end-to-end-tests/element/install-webserver.sh deleted file mode 100755 index ffc57c60c9..0000000000 --- a/test/end-to-end-tests/element/install-webserver.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -e - -BASE_DIR=$(cd $(dirname $0) && pwd) -cd $BASE_DIR -# Install ComplexHttpServer (a drop in replacement for Python's SimpleHttpServer -# but with support for multiple threads) into a virtualenv. -( - virtualenv -p python3 env - source env/bin/activate - - pip install --upgrade pip - pip install --upgrade setuptools - - pip install ComplexHttpServer - - deactivate -) diff --git a/test/end-to-end-tests/element/install.sh b/test/end-to-end-tests/element/install.sh deleted file mode 100755 index e1df709c68..0000000000 --- a/test/end-to-end-tests/element/install.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e -ELEMENT_BRANCH=develop - -if [ -d $BASE_DIR/element-web ]; then - echo "Element is already installed" - exit -fi - -curl -L https://github.com/vector-im/element-web/archive/${ELEMENT_BRANCH}.zip --output element.zip -unzip -q element.zip -rm element.zip -mv element-web-${ELEMENT_BRANCH} element-web -cd element-web -yarn install --pure-lockfile -yarn run build diff --git a/test/end-to-end-tests/element/start.sh b/test/end-to-end-tests/element/start.sh deleted file mode 100755 index b344f91a19..0000000000 --- a/test/end-to-end-tests/element/start.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -set -e - -PORT=5000 -BASE_DIR=$(cd $(dirname $0) && pwd) -PIDFILE=$BASE_DIR/element.pid -CONFIG_BACKUP=config.e2etests_backup.json - -if [ -f $PIDFILE ]; then - exit -fi - -cd $BASE_DIR/ -echo -n "Starting Element on http://localhost:$PORT ... " -pushd element-web/webapp/ > /dev/null - -# backup config file before we copy template -if [ -f config.json ]; then - mv config.json $CONFIG_BACKUP -fi -cp $BASE_DIR/config-template/config.json . - -LOGFILE=$(mktemp) -# run web server in the background, showing output on error -( - source $BASE_DIR/env/bin/activate - python -m ComplexHTTPServer $PORT > $LOGFILE 2>&1 & - PID=$! - echo $PID > $PIDFILE - # wait so subshell does not exit - # otherwise sleep below would not work - wait $PID; RESULT=$? - - # NOT expected SIGTERM (128 + 15) - # from stop.sh? - if [ $RESULT -ne 143 ]; then - echo "Failed" - cat $LOGFILE - rm $PIDFILE 2> /dev/null - fi - rm $LOGFILE - exit $RESULT -)& -# to be able to return the exit code for immediate errors (like address already in use) -# we wait for a short amount of time in the background and exit when the first -# child process exits -sleep 0.5 & -# wait the first child process to exit (python or sleep) -wait -n; RESULT=$? -# return exit code of first child to exit -if [ $RESULT -eq 0 ]; then - echo "Running" -fi -exit $RESULT diff --git a/test/end-to-end-tests/element/stop.sh b/test/end-to-end-tests/element/stop.sh deleted file mode 100755 index e39f0077eb..0000000000 --- a/test/end-to-end-tests/element/stop.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -e - -BASE_DIR=$(cd $(dirname $0) && pwd) -PIDFILE=element.pid -CONFIG_BACKUP=config.e2etests_backup.json - -cd $BASE_DIR - -if [ -f $PIDFILE ]; then - echo "Stopping Element server ..." - PID=$(cat $PIDFILE) - rm $PIDFILE - kill $PID - - # revert config file - cd element-web/webapp - rm config.json - if [ -f $CONFIG_BACKUP ]; then - mv $CONFIG_BACKUP config.json - fi -fi diff --git a/test/end-to-end-tests/has-custom-app.js b/test/end-to-end-tests/has-custom-app.js deleted file mode 100644 index 43784b6852..0000000000 --- a/test/end-to-end-tests/has-custom-app.js +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// used from run.sh as getopts doesn't support long parameters -const idx = process.argv.indexOf("--app-url"); -let hasAppUrl = false; -if (idx !== -1) { - const value = process.argv[idx + 1]; - hasAppUrl = !!value; -} -process.stdout.write(hasAppUrl ? "1" : "0"); diff --git a/test/end-to-end-tests/install.sh b/test/end-to-end-tests/install.sh deleted file mode 100755 index b2254a4ea7..0000000000 --- a/test/end-to-end-tests/install.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# run with PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true sh install.sh if chrome is already installed -set -e -./synapse/install.sh -# local testing doesn't need a Element fetched from master, -# so not installing that by default -yarn install --pure-lockfile diff --git a/test/end-to-end-tests/package.json b/test/end-to-end-tests/package.json deleted file mode 100644 index 2f7cfd4b6f..0000000000 --- a/test/end-to-end-tests/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "e2e-tests", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "tsc -p ./tsconfig.json" - }, - "author": "", - "license": "ISC", - "dependencies": { - "cheerio": "^1.0.0-rc.2", - "commander": "^9", - "puppeteer": "13.4.1", - "request": "^2.88.0", - "request-promise-native": "^1.0.7", - "uuid": "^3.3.2" - }, - "devDependencies": { - "@types/puppeteer": "^5.4.4", - "typescript": "^4.5.3" - } -} diff --git a/test/end-to-end-tests/pick-synapse-log-file.js b/test/end-to-end-tests/pick-synapse-log-file.js deleted file mode 100644 index aaefd0dfd2..0000000000 --- a/test/end-to-end-tests/pick-synapse-log-file.js +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -const path = require('path'); - -// used from run.sh as getopts doesn't support long parameters -const idx = process.argv.indexOf("--log-directory"); -if (idx !== -1) { - const value = process.argv[idx + 1]; - process.stdout.write(path.join(path.resolve(value), 'homeserver.log')); -} else { - process.stdout.write(path.join(process.cwd(), 'homeserver.log')); -} diff --git a/test/end-to-end-tests/run.sh b/test/end-to-end-tests/run.sh deleted file mode 100755 index f60a2cb30d..0000000000 --- a/test/end-to-end-tests/run.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -set -e - -BASE_DIR=$(cd $(dirname $0) && pwd) -pushd $BASE_DIR - -if [ ! -d "synapse/installations" ] || [ ! -d "node_modules" ]; then -echo "Please first run $BASE_DIR/install.sh" - exit 1 -fi - -has_custom_app=$(node has-custom-app.js $@) -synapse_log_file=$(node pick-synapse-log-file.js $@) -touch $synapse_log_file - -if [ ! -d "element/element-web" ] && [ $has_custom_app -ne "1" ]; then - echo "Please provide an instance of Element to test against by passing --app-url or running $BASE_DIR/element/install.sh" - exit 1 -fi - -stop_servers() { - if [ $has_custom_app -ne "1" ]; then - ./element/stop.sh - fi - ./synapse/stop.sh -} - -handle_error() { - EXIT_CODE=$? - echo "Tests fell over with a non-zero exit code: stopping servers" - stop_servers - exit $EXIT_CODE -} - -trap 'handle_error' ERR - -LOGFILE=$synapse_log_file ./synapse/start.sh -reg_secret=`./synapse/getcfg.sh registration_shared_secret` -if [ $has_custom_app -ne "1" ]; then - ./element/start.sh -fi -yarn build -node lib/start.js --registration-shared-secret=$reg_secret $@ -stop_servers diff --git a/test/end-to-end-tests/src/@types/global.d.ts b/test/end-to-end-tests/src/@types/global.d.ts deleted file mode 100644 index 6966996f1e..0000000000 --- a/test/end-to-end-tests/src/@types/global.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2021 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import "matrix-react-sdk/src/@types/global"; // load matrix-react-sdk's type extensions first - -declare global { - interface Window { - mxPerformanceMonitor: any; - mxPerformanceEntryNames: any; - } -} diff --git a/test/end-to-end-tests/src/logbuffer.ts b/test/end-to-end-tests/src/logbuffer.ts deleted file mode 100644 index 441bd0bdce..0000000000 --- a/test/end-to-end-tests/src/logbuffer.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { Page, PageEventObject } from "puppeteer"; - -export class LogBuffer[1]>[0]> { - buffer: string; - - constructor( - page: Page, - eventName: keyof PageEventObject, - eventMapper: (arg: EventMapperArg) => Promise, - initialValue = "", - ) { - this.buffer = initialValue; - page.on(eventName, (arg: EventMapperArg) => { - eventMapper(arg).then((r) => this.buffer += r); - }); - } -} diff --git a/test/end-to-end-tests/src/logger.ts b/test/end-to-end-tests/src/logger.ts deleted file mode 100644 index 194cd3761f..0000000000 --- a/test/end-to-end-tests/src/logger.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -export class Logger { - private indent = 0; - private muted = false; - - constructor(readonly username: string) {} - - public startGroup(description: string): Logger { - if (!this.muted) { - const indent = " ".repeat(this.indent * 2); - console.log(`${new Date().toISOString()} ${indent} * ${this.username} ${description}:`); - } - this.indent += 1; - return this; - } - - public endGroup(): Logger { - this.indent -= 1; - return this; - } - - public step(description: string): Logger { - if (!this.muted) { - const indent = " ".repeat(this.indent * 2); - process.stdout.write(`${new Date().toISOString()} ${indent} * ${this.username} ${description} ... `); - } - return this; - } - - public done(status = "done"): Logger { - if (!this.muted) { - process.stdout.write(status + "\n"); - } - return this; - } - - public mute(): Logger { - this.muted = true; - return this; - } - - public unmute(): Logger { - this.muted = false; - return this; - } -} diff --git a/test/end-to-end-tests/src/scenario.ts b/test/end-to-end-tests/src/scenario.ts deleted file mode 100644 index f7479d17cb..0000000000 --- a/test/end-to-end-tests/src/scenario.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { signup } from './usecases/signup'; -import { toastScenarios } from './scenarios/toast'; -import { ElementSession } from "./session"; - -export async function scenario(createSession: (s: string) => Promise): Promise { - let firstUser = true; - async function createUser(username: string) { - const session = await createSession(username); - if (firstUser) { - // only show browser version for first browser opened - console.log(`running tests on ${await session.browser.version()} ...`); - firstUser = false; - } - // ported to cyprus (registration test) - await signup(session, session.username, 'testsarefun!!!', session.hsUrl); - return session; - } - - const alice = await createUser("alice"); - const bob = await createUser("bob"); - - await toastScenarios(alice, bob); -} diff --git a/test/end-to-end-tests/src/scenarios/README.md b/test/end-to-end-tests/src/scenarios/README.md deleted file mode 100644 index 4eabc8f9ef..0000000000 --- a/test/end-to-end-tests/src/scenarios/README.md +++ /dev/null @@ -1 +0,0 @@ -scenarios contains the high-level playbook for the test suite diff --git a/test/end-to-end-tests/src/scenarios/toast.ts b/test/end-to-end-tests/src/scenarios/toast.ts deleted file mode 100644 index bf7dbe809b..0000000000 --- a/test/end-to-end-tests/src/scenarios/toast.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { assertNoToasts, acceptToast, rejectToast } from "../usecases/toasts"; -import { ElementSession } from "../session"; - -export async function toastScenarios(alice: ElementSession, bob: ElementSession): Promise { - console.log(" checking and clearing toasts:"); - - alice.log.startGroup(`clears toasts`); - alice.log.step(`accepts analytics toast`); - await acceptToast(alice, "Help improve Element"); - alice.log.done(); - - alice.log.step(`checks no remaining toasts`); - await assertNoToasts(alice); - alice.log.done(); - alice.log.endGroup(); - - bob.log.startGroup(`clears toasts`); - bob.log.step(`reject analytics toast`); - await rejectToast(bob, "Help improve Element"); - bob.log.done(); - - bob.log.step(`checks no remaining toasts`); - await assertNoToasts(bob); - bob.log.done(); - bob.log.endGroup(); -} diff --git a/test/end-to-end-tests/src/session.ts b/test/end-to-end-tests/src/session.ts deleted file mode 100644 index 9f8d67bae9..0000000000 --- a/test/end-to-end-tests/src/session.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import * as puppeteer from 'puppeteer'; - -import { Logger } from './logger'; -import { LogBuffer } from './logbuffer'; -import { delay, serializeLog } from './util'; - -const DEFAULT_TIMEOUT = 20000; - -export class ElementSession { - readonly consoleLog: LogBuffer; - readonly networkLog: LogBuffer; - readonly log: Logger; - - constructor(readonly browser: puppeteer.Browser, readonly page: puppeteer.Page, readonly username: string, - readonly elementServer: string, readonly hsUrl: string) { - this.consoleLog = new LogBuffer(page, "console", - async (msg: puppeteer.ConsoleMessage) => `${await serializeLog(msg)}\n`); - this.networkLog = new LogBuffer(page, - "requestfinished", async (req: puppeteer.HTTPRequest) => { - const type = req.resourceType(); - const response = await req.response(); - return new Date().toISOString() + - ` ${type} ${response?.status() ?? ''} ${req.method()} ${req.url()} \n`; - }); - this.log = new Logger(this.username); - } - - public static async create(username: string, puppeteerOptions: Parameters[0], - elementServer: string, hsUrl: string, throttleCpuFactor = 1): Promise { - const browser = await puppeteer.launch(puppeteerOptions); - const page = await browser.newPage(); - await page.setViewport({ - width: 1280, - height: 800, - }); - if (throttleCpuFactor !== 1) { - const client = await page.target().createCDPSession(); - console.log("throttling cpu by a factor of", throttleCpuFactor); - await client.send('Emulation.setCPUThrottlingRate', { rate: throttleCpuFactor }); - } - return new ElementSession(browser, page, username, elementServer, hsUrl); - } - - public async tryGetInnertext(selector: string): Promise { - const field = await this.page.$(selector); - if (field != null) { - const textHandle = await field.getProperty('innerText'); - return await textHandle.jsonValue(); - } - return null; - } - - public async getElementProperty(handle: puppeteer.ElementHandle, property: string): Promise { - const propHandle = await handle.getProperty(property); - return await propHandle.jsonValue(); - } - - public innerText(field: puppeteer.ElementHandle): Promise { - return this.getElementProperty(field, 'innerText'); - } - - public isChecked(field: puppeteer.ElementHandle): Promise { - return this.getElementProperty(field, 'checked'); - } - - public consoleLogs(): string { - return this.consoleLog.buffer; - } - - public networkLogs(): string { - return this.networkLog.buffer; - } - - public async replaceInputText(input: puppeteer.ElementHandle, text: string): Promise { - // click 3 times to select all text - await input.click({ clickCount: 3 }); - // waiting here solves not having selected all the text by the 3x click above, - // presumably because of the Field label animation. - await this.delay(300); - // then remove it with backspace - await input.press('Backspace'); - // and type the new text - await input.type(text); - } - - public query( - selector: string, - timeout: number = DEFAULT_TIMEOUT, - hidden = false, - ): Promise { - return this.page.waitForSelector(selector, { visible: true, timeout, hidden }); - } - - public queryWithoutWaiting(selector: string): Promise { - return this.page.$(selector); - } - - public async queryAll(selector: string): Promise { - const timeout = DEFAULT_TIMEOUT; - await this.query(selector, timeout); - return await this.page.$$(selector); - } - - public async waitNoSpinner(): Promise { - await this.page.waitForSelector(".mx_Spinner", { hidden: true }); - } - - public goto(url: string): Promise { - return this.page.goto(url); - } - - public url(path: string): string { - return this.elementServer + path; - } - - public delay(ms: number) { - return delay(ms); - } - - public async close(): Promise { - return this.browser.close(); - } - - public async poll(callback: () => Promise, interval = 100): Promise { - const timeout = DEFAULT_TIMEOUT; - let waited = 0; - while (waited < timeout) { - await this.delay(interval); - waited += interval; - if (await callback()) { - return true; - } - } - return false; - } -} diff --git a/test/end-to-end-tests/src/usecases/README.md b/test/end-to-end-tests/src/usecases/README.md deleted file mode 100644 index daa990e15c..0000000000 --- a/test/end-to-end-tests/src/usecases/README.md +++ /dev/null @@ -1,2 +0,0 @@ -use cases contains the detailed DOM interactions to perform a given use case, may also do some assertions. -use cases are often used in multiple scenarios. diff --git a/test/end-to-end-tests/src/usecases/accept-invite.ts b/test/end-to-end-tests/src/usecases/accept-invite.ts deleted file mode 100644 index 727497dad6..0000000000 --- a/test/end-to-end-tests/src/usecases/accept-invite.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { findSublist } from "./create-room"; -import { ElementSession } from "../session"; - -export async function acceptInvite(session: ElementSession, name: string): Promise { - session.log.step(`accepts "${name}" invite`); - const inviteSublist = await findSublist(session, "invites"); - const invitesHandles = await inviteSublist.$$(".mx_RoomTile_title"); - const invitesWithText = await Promise.all(invitesHandles.map(async (inviteHandle) => { - const text = await session.innerText(inviteHandle); - return { inviteHandle, text }; - })); - const inviteHandle = invitesWithText.find(({ inviteHandle, text }) => { - return text.trim() === name; - }).inviteHandle; - - await inviteHandle.click(); - - const acceptInvitationLink = await session.query(".mx_RoomPreviewBar_Invite .mx_AccessibleButton_kind_primary"); - await acceptInvitationLink.click(); - - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/create-room.ts b/test/end-to-end-tests/src/usecases/create-room.ts deleted file mode 100644 index d0a492f8c5..0000000000 --- a/test/end-to-end-tests/src/usecases/create-room.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import * as puppeteer from "puppeteer"; - -import { measureStart, measureStop } from '../util'; -import { ElementSession } from "../session"; - -export async function openRoomDirectory(session: ElementSession): Promise { - const roomDirectoryButton = await session.query('.mx_LeftPanel_exploreButton'); - await roomDirectoryButton.click(); -} - -export async function findSublist(session: ElementSession, name: string): Promise { - return await session.query(`.mx_RoomSublist[aria-label="${name}" i]`); -} - -export async function createRoom(session: ElementSession, roomName: string, encrypted = false): Promise { - session.log.step(`creates room "${roomName}"`); - - const roomsSublist = await findSublist(session, "rooms"); - const addRoomButton = await roomsSublist.$(".mx_RoomSublist_auxButton"); - await addRoomButton.click(); - - const createRoomButton = await session.query('.mx_AccessibleButton[aria-label="New room"]'); - await createRoomButton.click(); - - const roomNameInput = await session.query('.mx_CreateRoomDialog_name input'); - await session.replaceInputText(roomNameInput, roomName); - - if (!encrypted) { - const encryptionToggle = await session.query('.mx_CreateRoomDialog_e2eSwitch .mx_ToggleSwitch'); - await encryptionToggle.click(); - } - - const createButton = await session.query('.mx_Dialog_primary'); - await createButton.click(); - - await session.query('.mx_MessageComposer'); - session.log.done(); -} - -export async function createDm(session: ElementSession, invitees: string[]): Promise { - session.log.step(`creates DM with ${JSON.stringify(invitees)}`); - - await measureStart(session, "mx_CreateDM"); - - const dmsSublist = await findSublist(session, "people"); - const startChatButton = await dmsSublist.$(".mx_RoomSublist_auxButton"); - await startChatButton.click(); - - const inviteesEditor = await session.query('.mx_InviteDialog_editor input'); - for (const target of invitees) { - await session.replaceInputText(inviteesEditor, target); - await session.delay(1000); // give it a moment to figure out a suggestion - // find the suggestion and accept it - const suggestions = await session.queryAll('.mx_InviteDialog_tile_nameStack_userId'); - const suggestionTexts = await Promise.all(suggestions.map(s => session.innerText(s))); - const suggestionIndex = suggestionTexts.indexOf(target); - if (suggestionIndex === -1) { - throw new Error(`failed to find a suggestion in the DM dialog to invite ${target} with`); - } - await suggestions[suggestionIndex].click(); - } - - // press the go button and hope for the best - const goButton = await session.query('.mx_InviteDialog_goButton'); - await goButton.click(); - - await session.query('.mx_MessageComposer'); - session.log.done(); - - await measureStop(session, "mx_CreateDM"); -} diff --git a/test/end-to-end-tests/src/usecases/dialog.ts b/test/end-to-end-tests/src/usecases/dialog.ts deleted file mode 100644 index aee6620ff9..0000000000 --- a/test/end-to-end-tests/src/usecases/dialog.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; - -import { ElementSession } from "../session"; - -export async function assertDialog(session: ElementSession, expectedTitle: string): Promise { - const titleElement = await session.query(".mx_Dialog .mx_Dialog_title"); - const dialogHeader = await session.innerText(titleElement); - assert.equal(dialogHeader, expectedTitle); -} - -export async function acceptDialog(session: ElementSession, expectedTitle: string): Promise { - const foundDialog = await acceptDialogMaybe(session, expectedTitle); - if (!foundDialog) { - throw new Error("could not find a dialog"); - } -} - -export async function acceptDialogMaybe(session: ElementSession, expectedTitle: string): Promise { - let primaryButton = null; - try { - primaryButton = await session.query(".mx_Dialog .mx_Dialog_primary"); - } catch (err) { - return false; - } - if (expectedTitle) { - await assertDialog(session, expectedTitle); - } - await primaryButton.click(); - return true; -} diff --git a/test/end-to-end-tests/src/usecases/invite.ts b/test/end-to-end-tests/src/usecases/invite.ts deleted file mode 100644 index 9df9f38669..0000000000 --- a/test/end-to-end-tests/src/usecases/invite.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { ElementSession } from "../session"; - -export async function invite(session: ElementSession, userId: string): Promise { - session.log.step(`invites "${userId}" to room`); - await session.delay(1000); - const memberPanelButton = await session.query(".mx_RightPanel_membersButton"); - try { - await session.query(".mx_RightPanel_headerButton_highlight", 500); - // Right panel is open - toggle it to ensure it's the member list - // Sometimes our tests have this opened to MemberInfo - await memberPanelButton.click(); - await memberPanelButton.click(); - } catch (e) { - // Member list is closed - open it - await memberPanelButton.click(); - } - const inviteButton = await session.query(".mx_MemberList_invite"); - await inviteButton.click(); - const inviteTextArea = await session.query(".mx_InviteDialog_editor input"); - await inviteTextArea.type(userId); - const selectUserItem = await session.query(".mx_InviteDialog_tile--room"); - await selectUserItem.click(); - const confirmButton = await session.query(".mx_InviteDialog_goButton"); - await confirmButton.click(); - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/join.ts b/test/end-to-end-tests/src/usecases/join.ts deleted file mode 100644 index 93ce9957f0..0000000000 --- a/test/end-to-end-tests/src/usecases/join.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { openRoomDirectory } from './create-room'; -import { measureStart, measureStop } from '../util'; -import { ElementSession } from "../session"; - -export async function join(session: ElementSession, roomName: string): Promise { - session.log.step(`joins room "${roomName}"`); - await measureStart(session, "mx_JoinRoom"); - await openRoomDirectory(session); - const roomInput = await session.query('.mx_DirectorySearchBox input'); - await session.replaceInputText(roomInput, roomName); - - const joinFirstLink = await session.query('.mx_RoomDirectory_table .mx_RoomDirectory_join .mx_AccessibleButton'); - await joinFirstLink.click(); - await session.query('.mx_MessageComposer'); - await measureStop(session, "mx_JoinRoom"); - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/login.ts b/test/end-to-end-tests/src/usecases/login.ts deleted file mode 100644 index a7943bb595..0000000000 --- a/test/end-to-end-tests/src/usecases/login.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; - -import { ElementSession } from "../session"; - -export async function login( - session: ElementSession, - username: string, password: string, - homeserver: string, -): Promise { - session.log.startGroup("logs in"); - session.log.step("Navigates to login page"); - - const navPromise = session.page.waitForNavigation(); - await session.goto(session.url('/#/login')); - await navPromise; - session.log.done(); - - // for reasons I still don't fully understand, this seems to be flakey - // such that when it's trying to click on 'mx_ServerPicker_change', - // it ends up clicking instead on the dropdown for username / email / phone. - // Waiting for the serverpicker to appear before proceeding seems to make - // it reliable... - await session.query('.mx_ServerPicker'); - - // wait until no spinners visible - await session.waitNoSpinner(); - - // change the homeserver by clicking the advanced section - if (homeserver) { - session.log.step("Clicks button to change homeserver"); - const changeButton = await session.query('.mx_ServerPicker_change'); - await changeButton.click(); - session.log.done(); - - session.log.step("Enters homeserver"); - const hsInputField = await session.query('.mx_ServerPickerDialog_otherHomeserver'); - await session.replaceInputText(hsInputField, homeserver); - session.log.done(); - - session.log.step("Clicks next"); - const nextButton = await session.query('.mx_ServerPickerDialog_continue'); - // accept homeserver - await nextButton.click(); - session.log.done(); - } - // Delay required because of local race condition on macOS - // Where the form is not query-able despite being present in the DOM - await session.delay(100); - - session.log.step("Fills in login form"); - //fill out form - const usernameField = await session.query("#mx_LoginForm_username"); - const passwordField = await session.query("#mx_LoginForm_password"); - await session.replaceInputText(usernameField, username); - await session.replaceInputText(passwordField, password); - session.log.done(); - - session.log.step("Clicks login"); - const loginButton = await session.query('.mx_Login_submit'); - await loginButton.focus(); - //check no errors - const errorText = await session.tryGetInnertext('.mx_Login_error'); - assert.strictEqual(errorText, null); - //submit form - //await page.screenshot({path: "beforesubmit.png", fullPage: true}); - await loginButton.click(); - session.log.done(); - - const foundHomeUrl = await session.poll(async () => { - const url = session.page.url(); - return url === session.url('/#/home'); - }); - assert(foundHomeUrl); - session.log.endGroup(); -} diff --git a/test/end-to-end-tests/src/usecases/memberlist.ts b/test/end-to-end-tests/src/usecases/memberlist.ts deleted file mode 100644 index 9daea8b1cb..0000000000 --- a/test/end-to-end-tests/src/usecases/memberlist.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { ElementHandle } from "puppeteer"; - -import { openRoomSummaryCard } from "./rightpanel"; -import { ElementSession } from "../session"; - -export async function openMemberInfo(session: ElementSession, name: String): Promise { - const membersAndNames = await getMembersInMemberlist(session); - const matchingLabel = membersAndNames.filter((m) => { - return m.displayName === name; - }).map((m) => m.label)[0]; - await matchingLabel.click(); -} - -interface MemberName { - label: ElementHandle; - displayName: string; -} - -export async function getMembersInMemberlist(session: ElementSession): Promise { - await openRoomSummaryCard(session); - const memberPanelButton = await session.query(".mx_RoomSummaryCard_icon_people"); - // We are back at the room summary card - await memberPanelButton.click(); - - const memberNameElements = await session.queryAll(".mx_MemberList .mx_EntityTile_name"); - return Promise.all(memberNameElements.map(async (el) => { - return { label: el, displayName: await session.innerText(el) }; - })); -} diff --git a/test/end-to-end-tests/src/usecases/rightpanel.ts b/test/end-to-end-tests/src/usecases/rightpanel.ts deleted file mode 100644 index 83417ccb1a..0000000000 --- a/test/end-to-end-tests/src/usecases/rightpanel.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { ElementSession } from "../session"; - -export async function closeRoomRightPanel(session: ElementSession): Promise { - const button = await session.query(".mx_BaseCard_close"); - await button.click(); -} - -export async function openThreadListPanel(session: ElementSession): Promise { - await session.query('.mx_RoomHeader .mx_AccessibleButton[aria-label="Threads"]'); - const button = await session.queryWithoutWaiting('.mx_RoomHeader .mx_AccessibleButton[aria-label="Threads"]' + - ':not(.mx_RightPanel_headerButton_highlight)'); - await button?.click(); -} - -export async function assertThreadListHasUnreadIndicator(session: ElementSession): Promise { - await session.query('.mx_RoomHeader .mx_AccessibleButton[aria-label="Threads"] ' + - '.mx_RightPanel_headerButton_unreadIndicator'); -} - -export async function clickLatestThreadInThreadListPanel(session: ElementSession): Promise { - const threads = await session.queryAll(".mx_ThreadPanel .mx_EventTile"); - await threads[threads.length - 1].click(); -} - -export async function openRoomRightPanel(session: ElementSession): Promise { - // block until we have a roomSummaryButton - const roomSummaryButton = await session.query('.mx_RoomHeader .mx_AccessibleButton[aria-label="Room Info"]'); - // check if it's highlighted - const highlightedRoomSummaryButton = await session.queryWithoutWaiting( - '.mx_RoomHeader .mx_RightPanel_headerButton_highlight[aria-label="Room Info"]', - ); - if (!highlightedRoomSummaryButton) { - // If the room summary is not yet open, open it - await roomSummaryButton.click(); - } -} - -export async function goBackToRoomSummaryCard(session: ElementSession): Promise { - for (let i = 0; i < 5; i++) { - try { - const backButton = await session.query(".mx_BaseCard_back", 500); - // Right panel is open to the wrong thing - go back up to the Room Summary Card - // Sometimes our tests have this opened to MemberInfo - await backButton.click(); - } catch (e) { - // explicitly check for TimeoutError as this sometimes returned - // `Error: Node is detached from document` due to a re-render race or similar - if (e.name === "TimeoutError") { - break; // stop trying to go further back - } - } - } -} - -export async function openRoomSummaryCard(session: ElementSession) { - await openRoomRightPanel(session); - await goBackToRoomSummaryCard(session); -} diff --git a/test/end-to-end-tests/src/usecases/room-settings.ts b/test/end-to-end-tests/src/usecases/room-settings.ts deleted file mode 100644 index 11fe69e46f..0000000000 --- a/test/end-to-end-tests/src/usecases/room-settings.ts +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; -import { ElementHandle } from "puppeteer"; - -import { openRoomSummaryCard } from "./rightpanel"; -import { acceptDialog } from './dialog'; -import { ElementSession } from "../session"; - -export async function setSettingsToggle(session: ElementSession, toggle: ElementHandle, enabled): Promise { - const className = await session.getElementProperty(toggle, "className"); - const checked = className.includes("mx_ToggleSwitch_on"); - if (checked !== enabled) { - await toggle.click(); - session.log.done(); - return true; - } else { - session.log.done("already set"); - } -} - -export async function checkSettingsToggle(session: ElementSession, - toggle: ElementHandle, shouldBeEnabled: boolean): Promise { - const className = await session.getElementProperty(toggle, "className"); - const checked = className.includes("mx_ToggleSwitch_on"); - if (checked === shouldBeEnabled) { - session.log.done('set as expected'); - } else { - // other logs in the area should give more context as to what this means. - throw new Error("settings toggle value didn't match expectation"); - } -} - -interface Tabs { - securityTabButton: ElementHandle; - generalTabButton: ElementHandle; -} - -async function findTabs(session: ElementSession): Promise { - /// XXX delay is needed here, possibly because the header is being rerendered - /// click doesn't do anything otherwise - await session.delay(1000); - - await openRoomSummaryCard(session); - - const settingsButton = await session.query(".mx_RoomSummaryCard_icon_settings"); - await settingsButton.click(); - - //find tabs - const tabButtons = await session.queryAll(".mx_RoomSettingsDialog .mx_TabbedView_tabLabel"); - const tabLabels = await Promise.all(tabButtons.map(t => session.innerText(t))); - const securityTabButton = tabButtons[tabLabels.findIndex(l => l.toLowerCase().includes("security"))]; - const generalTabButton = tabButtons[tabLabels.findIndex(l => l.toLowerCase().includes("general"))]; - - return { securityTabButton, generalTabButton }; -} - -interface Settings { - encryption?: boolean; - directory?: boolean; - alias?: string; - publishedAlias?: string; - visibility?: string; -} - -export async function checkRoomSettings(session: ElementSession, expectedSettings: Settings): Promise { - session.log.startGroup(`checks the room settings`); - - const { securityTabButton } = await findTabs(session); - const generalSwitches = await session.queryAll(".mx_RoomSettingsDialog .mx_ToggleSwitch"); - const isDirectory = generalSwitches[0]; - - if (typeof expectedSettings.directory === 'boolean') { - session.log.step(`checks directory listing is ${expectedSettings.directory}`); - await checkSettingsToggle(session, isDirectory, expectedSettings.directory); - } - - if (expectedSettings.publishedAlias) { - session.log.step(`checks for published alias of ${expectedSettings.publishedAlias}`); - const publishedAliases = await session.queryAll('#roomAltAliases .mx_EditableItem_item, #roomAltAliases li'); - const publishedAliasTexts = await Promise.all(publishedAliases.map(a => session.innerText(a))); - if (publishedAliasTexts.find(a => a.includes(expectedSettings.publishedAlias))) { - session.log.done("present"); - } else { - throw new Error(`could not find published alias ${expectedSettings.publishedAlias}`); - } - } - - if (expectedSettings.alias) { - session.log.step(`checks for local alias of ${expectedSettings.alias}`); - const summary = await session.query(".mx_RoomSettingsDialog .mx_AliasSettings summary"); - await summary.click(); - const localAliases = await session.queryAll('#roomAliases .mx_EditableItem_item, #roomAliases li'); - const localAliasTexts = await Promise.all(localAliases.map(a => session.innerText(a))); - if (localAliasTexts.find(a => a.includes(expectedSettings.alias))) { - session.log.done("present"); - } else { - throw new Error(`could not find local alias ${expectedSettings.alias}`); - } - } - - await securityTabButton.click(); - await session.delay(500); - const securitySwitches = await session.queryAll(".mx_RoomSettingsDialog .mx_ToggleSwitch"); - const e2eEncryptionToggle = securitySwitches[0]; - - if (typeof expectedSettings.encryption === "boolean") { - session.log.step(`checks room e2e encryption is ${expectedSettings.encryption}`); - await checkSettingsToggle(session, e2eEncryptionToggle, expectedSettings.encryption); - } - - if (expectedSettings.visibility) { - session.log.step(`checks visibility is ${expectedSettings.visibility}`); - const radios = await session.queryAll(".mx_RoomSettingsDialog input[type=radio]"); - // the "Who can read history?" "Anyone" radio option is only shown if visibility is set to public - assert.equal(radios.length, expectedSettings.visibility === "public" ? 7 : 6); - const [inviteOnlyRoom, spaceMembers, publicRoom] = radios; - - let expectedRadio = null; - if (expectedSettings.visibility === "invite_only") { - expectedRadio = inviteOnlyRoom; - } else if (expectedSettings.visibility === "space_members") { - expectedRadio = spaceMembers; - } else if (expectedSettings.visibility === "public") { - expectedRadio = publicRoom; - } else { - throw new Error(`unrecognized room visibility setting: ${expectedSettings.visibility}`); - } - if (await session.isChecked(expectedRadio)) { - session.log.done(); - } else { - throw new Error("room visibility is not as expected"); - } - } - - const closeButton = await session.query(".mx_RoomSettingsDialog .mx_Dialog_cancelButton"); - await closeButton.click(); - - session.log.endGroup(); -} - -async function getValidationError(session: ElementSession): Promise { - try { - // give it 500ms to fail to produce a validation error - const validationDetail = await session.query(".mx_Validation_detail", 500); - return session.innerText(validationDetail); - } catch (e) { - // no validation tooltips - return undefined; - } -} - -export async function changeRoomSettings(session: ElementSession, settings: Settings) { - session.log.startGroup(`changes the room settings`); - - const { securityTabButton, generalTabButton } = await findTabs(session); - - securityTabButton.click(); - await session.delay(500); - const securitySwitches = await session.queryAll(".mx_RoomSettingsDialog .mx_ToggleSwitch"); - const e2eEncryptionToggle = securitySwitches[0]; - - if (typeof settings.encryption === "boolean") { - session.log.step(`sets room e2e encryption to ${settings.encryption}`); - const clicked = await setSettingsToggle(session, e2eEncryptionToggle, settings.encryption); - // if enabling, accept beta warning dialog - if (clicked && settings.encryption) { - await acceptDialog(session, "Enable encryption?"); - } - } - - if (settings.visibility) { - session.log.step(`sets visibility to ${settings.visibility}`); - const radios = await session.queryAll(".mx_RoomSettingsDialog label"); - assert.equal(radios.length, 7); - const [inviteOnlyRoom,, publicRoom] = radios; - - if (settings.visibility === "invite_only") { - await inviteOnlyRoom.click(); - } else if (settings.visibility === "public") { - await publicRoom.click(); - } else { - throw new Error(`unrecognized room visibility setting: ${settings.visibility}`); - } - await session.delay(100); - session.log.done(); - } - - generalTabButton.click(); - await session.delay(500); - const generalSwitches = await session.queryAll(".mx_RoomSettingsDialog .mx_ToggleSwitch"); - const isDirectory = generalSwitches[0]; - - if (typeof settings.directory === "boolean") { - session.log.step(`sets directory listing to ${settings.directory}`); - await setSettingsToggle(session, isDirectory, settings.directory); - } - - if (settings.alias) { - session.log.step(`adding local alias ${settings.alias}`); - const aliasField = await session.query("#roomAliases input[type=text]"); - await session.replaceInputText(aliasField, settings.alias.substring(1, settings.alias.lastIndexOf(":"))); - const addButton = await session.query("#roomAliases .mx_AccessibleButton"); - await addButton.click(); - await session.query("#roomAliases .mx_Field_valid, #roomAliases .mx_Field_invalid"); // await validator - assert.equal(await getValidationError(session), undefined); - session.log.done(); - } - - if (settings.publishedAlias) { - session.log.step(`adding published alias ${settings.alias}`); - const aliasField = await session.query("#roomAltAliases input[type=text]"); - await session.replaceInputText(aliasField, settings.alias.substring(1)); - const addButton = await session.query("#roomAltAliases .mx_AccessibleButton"); - await addButton.click(); - await session.query("#roomAltAliases .mx_Field_valid, #roomAltAliases .mx_Field_invalid"); // await validator - assert.equal(await getValidationError(session), undefined); - session.log.done(); - } - - const closeButton = await session.query(".mx_RoomSettingsDialog .mx_Dialog_cancelButton"); - await closeButton.click(); - - session.log.endGroup(); -} diff --git a/test/end-to-end-tests/src/usecases/security.ts b/test/end-to-end-tests/src/usecases/security.ts deleted file mode 100644 index 66c786a94e..0000000000 --- a/test/end-to-end-tests/src/usecases/security.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { acceptToast } from "./toasts"; -import { ElementSession } from "../session"; - -export async function setupSecureBackup(session: ElementSession): Promise { - session.log.step("sets up Secure Backup"); - - await acceptToast(session, "Set up Secure Backup"); - - // Continue with the default (generate a security key) - const xsignContButton = await session.query('.mx_CreateSecretStorageDialog .mx_Dialog_buttons .mx_Dialog_primary'); - await xsignContButton.click(); - - //ignore the recovery key - //TODO: It's probably important for the tests to know the recovery key - const copyButton = await session.query('.mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn'); - await copyButton.click(); - - //acknowledge that we copied the recovery key to a safe place - const copyContinueButton = await session.query( - '.mx_CreateSecretStorageDialog .mx_Dialog_buttons .mx_Dialog_primary', - ); - await copyContinueButton.click(); - - session.log.done(); -} - -module.exports = { setupSecureBackup }; diff --git a/test/end-to-end-tests/src/usecases/select-room.ts b/test/end-to-end-tests/src/usecases/select-room.ts deleted file mode 100644 index 29bb726c5c..0000000000 --- a/test/end-to-end-tests/src/usecases/select-room.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { findSublist } from "./create-room"; -import { ElementSession } from "../session"; - -export async function selectRoom(session: ElementSession, name: string): Promise { - session.log.step(`select "${name}" room`); - const inviteSublist = await findSublist(session, "rooms"); - const invitesHandles = await inviteSublist.$$(".mx_RoomTile_title"); - const invitesWithText = await Promise.all(invitesHandles.map(async (roomHandle) => { - const text = await session.innerText(roomHandle); - return { roomHandle, text }; - })); - const roomHandle = invitesWithText.find(({ roomHandle, text }) => { - return text.trim() === name; - }).roomHandle; - - await roomHandle.click(); - - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/send-message.ts b/test/end-to-end-tests/src/usecases/send-message.ts deleted file mode 100644 index cbbc836a3b..0000000000 --- a/test/end-to-end-tests/src/usecases/send-message.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; - -import { ElementSession } from "../session"; - -export async function sendMessage(session: ElementSession, message: string): Promise { - session.log.step(`writes "${message}" in room`); - // this selector needs to be the element that has contenteditable=true, - // not any if its parents, otherwise it behaves flaky at best. - const composer = await session.query('.mx_SendMessageComposer'); - // sometimes the focus that type() does internally doesn't seem to work - // and calling click before seems to fix it 🤷 - await composer.click(); - await composer.type(message); - const text = await session.innerText(composer); - assert.equal(text.trim(), message.trim()); - await composer.press("Enter"); - // wait for the message to appear sent - await session.query(".mx_EventTile_last:not(.mx_EventTile_sending)"); - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/send-sticker.ts b/test/end-to-end-tests/src/usecases/send-sticker.ts deleted file mode 100644 index e599d2bd5e..0000000000 --- a/test/end-to-end-tests/src/usecases/send-sticker.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { Frame } from "puppeteer"; - -import { ElementSession } from "../session"; - -export async function sendSticker(session: ElementSession): Promise { - session.log.step(`opens composer menu`); - const kebabButton = await session.query('.mx_MessageComposer_buttonMenu'); - await kebabButton.click(); - session.log.done(); - - let stickerFrame: Frame; - - // look to see if the sticker picker is already there (it's persistent, so - // it will only load a new frame the first time we open it) - for (const f of session.page.frames()) { - if ((await f.title()) === "Fake Sticker Picker") { - stickerFrame = f; - } - } - - const stickerFramePromise = new Promise(resolve => { - session.page.once('frameattached', async f => { - await f.waitForNavigation(); - resolve(f); - }); - }); - - session.log.step(`opens sticker picker`); - - const stickerOption = await session.query('#stickersButton'); - await stickerOption.click(); - - if (stickerFrame === undefined) { - stickerFrame = await stickerFramePromise; - } - - if (stickerFrame === undefined) throw new Error("Couldn't find sticker picker frame"); - session.log.done(); - - session.log.step(`clicks sticker button`); - - const sendStickerButton = await stickerFrame.waitForSelector('#sendsticker'); - sendStickerButton.click(); - - // wait for the message to appear sent - await session.query(".mx_EventTile_last:not(.mx_EventTile_sending)"); - - const stickerSrc = await session.page.evaluate(() => { - return document.querySelector( - '.mx_EventTile_last .mx_MStickerBody_wrapper img', - ).getAttribute('src'); - }); - - if (!stickerSrc.split('?')[0].endsWith('/_matrix/media/r0/thumbnail/somewhere')) { - throw new Error("Unexpected image src for sticker: got " + stickerSrc); - } - - const stickerAlt = await session.page.evaluate(() => { - return document.querySelector( - '.mx_EventTile_last .mx_MStickerBody_wrapper img', - ).getAttribute('alt'); - }); - - if (stickerAlt !== "Test Sticker") { - throw new Error("Unexpected image alt for sticker: got " + stickerAlt); - } - - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/settings.ts b/test/end-to-end-tests/src/usecases/settings.ts deleted file mode 100644 index 110a5b58cc..0000000000 --- a/test/end-to-end-tests/src/usecases/settings.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { ElementSession } from "../session"; - -export async function openSettings(session: ElementSession, section: string): Promise { - const menuButton = await session.query(".mx_UserMenu"); - await menuButton.click(); - const settingsItem = await session.query(".mx_UserMenu_iconSettings"); - await settingsItem.click(); - if (section) { - const sectionButton = await session.query( - `.mx_UserSettingsDialog .mx_TabbedView_tabLabels .mx_UserSettingsDialog_${section}Icon`); - await sectionButton.click(); - } -} diff --git a/test/end-to-end-tests/src/usecases/signup.ts b/test/end-to-end-tests/src/usecases/signup.ts deleted file mode 100644 index 5099d7584e..0000000000 --- a/test/end-to-end-tests/src/usecases/signup.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; - -import { ElementSession } from "../session"; - -export async function signup( - session: ElementSession, - username: string, - password: string, - homeserver: string, -): Promise { - session.log.step("signs up"); - await session.goto(session.url('/#/register')); - // change the homeserver by clicking the advanced section - if (homeserver) { - const changeButton = await session.query('.mx_ServerPicker_change'); - await changeButton.click(); - - const hsInputField = await session.query('.mx_ServerPickerDialog_otherHomeserver'); - await session.replaceInputText(hsInputField, homeserver); - const nextButton = await session.query('.mx_ServerPickerDialog_continue'); - // accept homeserver - await nextButton.click(); - } - // Delay required because of local race condition on macOs - // Where the form is not query-able despite being present in the DOM - await session.delay(100); - //fill out form - const usernameField = await session.query("#mx_RegistrationForm_username"); - const passwordField = await session.query("#mx_RegistrationForm_password"); - const passwordRepeatField = await session.query("#mx_RegistrationForm_passwordConfirm"); - await session.replaceInputText(usernameField, username); - await session.replaceInputText(passwordField, password); - await session.replaceInputText(passwordRepeatField, password); - //wait 300ms because Registration/ServerConfig have a 250ms - //delay to internally set the homeserver url - //see Registration::render and ServerConfig::props::delayTimeMs - await session.delay(300); - /// focus on the button to make sure error validation - /// has happened before checking the form is good to go - const registerButton = await session.query('.mx_Login_submit'); - await registerButton.focus(); - // Password validation is async, wait for it to complete before submit - await session.query(".mx_Field_valid #mx_RegistrationForm_password"); - //check no errors - const errorText = await session.tryGetInnertext('.mx_Login_error'); - assert.strictEqual(errorText, null); - //submit form - //await page.screenshot({path: "beforesubmit.png", fullPage: true}); - await registerButton.click(); - - //confirm dialog saying you cant log back in without e-mail - const continueButton = await session.query('.mx_RegistrationEmailPromptDialog button.mx_Dialog_primary'); - await continueButton.click(); - - //find the privacy policy checkbox and check it - const policyCheckbox = await session.query('.mx_InteractiveAuthEntryComponents_termsPolicy input'); - await policyCheckbox.click(); - - //now click the 'Accept' button to agree to the privacy policy - const acceptButton = await session.query('.mx_InteractiveAuthEntryComponents_termsSubmit'); - await acceptButton.click(); - - //now click the 'Skip' button to skip the use case selection - const skipUseCaseButton = await session.query('.mx_UseCaseSelection_skip .mx_AccessibleButton'); - await skipUseCaseButton.click(); - - //wait for registration to finish so the hash gets set - //onhashchange better? - - const foundHomeUrl = await session.poll(async () => { - const url = session.page.url(); - return url === session.url('/#/home'); - }); - assert(foundHomeUrl); - session.log.done(); -} diff --git a/test/end-to-end-tests/src/usecases/timeline.ts b/test/end-to-end-tests/src/usecases/timeline.ts deleted file mode 100644 index ff88d63452..0000000000 --- a/test/end-to-end-tests/src/usecases/timeline.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; -import { ElementHandle } from "puppeteer"; - -import { ElementSession } from "../session"; - -interface Message { - sender: string; - encrypted?: boolean; - body: string; - continuation?: boolean; -} - -export async function receiveMessage(session: ElementSession, expectedMessage: Message): Promise { - session.log.step(`receives message "${expectedMessage.body}" from ${expectedMessage.sender}`); - // wait for a response to come in that contains the message - // crude, but effective - - async function getLastMessage(): Promise { - const lastTile = await getLastEventTile(session); - return getMessageFromEventTile(lastTile); - } - - let lastMessage; - await session.poll(async () => { - try { - lastMessage = await getLastMessage(); - } catch (err) { - return false; - } - // stop polling when found the expected message - return lastMessage && - lastMessage.body === expectedMessage.body && - lastMessage.sender === expectedMessage.sender; - }); - assertMessage(lastMessage, expectedMessage); - session.log.done(); -} - -function assertMessage(foundMessage: Message, expectedMessage: Message): void { - assert(foundMessage, `message ${JSON.stringify(expectedMessage)} not found in timeline`); - assert.equal(foundMessage.body, expectedMessage.body); - assert.equal(foundMessage.sender, expectedMessage.sender); - if (expectedMessage.hasOwnProperty("encrypted")) { - assert.equal(foundMessage.encrypted, expectedMessage.encrypted); - } -} - -function getLastEventTile(session: ElementSession): Promise { - return session.query(".mx_EventTile_last"); -} - -async function getMessageFromEventTile(eventTile: ElementHandle): Promise { - const senderElement = await eventTile.$(".mx_DisambiguatedProfile_displayName"); - const className: string = await (await eventTile.getProperty("className")).jsonValue(); - const classNames = className.split(" "); - const bodyElement = await eventTile.$(".mx_EventTile_body"); - let sender = null; - if (senderElement) { - sender = await(await senderElement.getProperty("innerText")).jsonValue(); - } - if (!bodyElement) { - return null; - } - const body: string = await(await bodyElement.getProperty("innerText")).jsonValue(); - - return { - sender, - body, - encrypted: classNames.includes("mx_EventTile_verified"), - continuation: classNames.includes("mx_EventTile_continuation"), - }; -} diff --git a/test/end-to-end-tests/src/usecases/toasts.ts b/test/end-to-end-tests/src/usecases/toasts.ts deleted file mode 100644 index 6923471c98..0000000000 --- a/test/end-to-end-tests/src/usecases/toasts.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; - -import { ElementSession } from "../session"; - -export async function assertNoToasts(session: ElementSession): Promise { - try { - await session.query('.mx_Toast_toast', 1000, true); - } catch (e) { - const h2Element = await session.query('.mx_Toast_title h2', 1000); - const toastTitle = await session.innerText(h2Element); - throw new Error(`"${toastTitle}" toast found when none expected`); - } -} - -export async function assertToast(session: ElementSession, expectedTitle: string): Promise { - const h2Element = await session.query('.mx_Toast_title h2'); - const toastTitle = await session.innerText(h2Element); - assert.equal(toastTitle, expectedTitle); -} - -export async function acceptToast(session: ElementSession, expectedTitle: string): Promise { - await assertToast(session, expectedTitle); - const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_primary'); - await btn.click(); -} - -export async function rejectToast(session: ElementSession, expectedTitle: string): Promise { - await assertToast(session, expectedTitle); - const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger_outline'); - await btn.click(); -} diff --git a/test/end-to-end-tests/src/usecases/verify.ts b/test/end-to-end-tests/src/usecases/verify.ts deleted file mode 100644 index 84cd342e6e..0000000000 --- a/test/end-to-end-tests/src/usecases/verify.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright 2019 New Vector Ltd -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { strict as assert } from 'assert'; - -import { openMemberInfo } from "./memberlist"; -import { ElementSession } from "../session"; - -export async function startVerification(session: ElementSession, name: string): Promise { - session.log.step("opens their opponent's profile and starts verification"); - await openMemberInfo(session, name); - // click verify in member info - const firstVerifyButton = await session.query(".mx_UserInfo_verifyButton"); - await firstVerifyButton.click(); - - // wait for the animation to finish - await session.delay(1000); - - // click 'start verification' - const startVerifyButton = await session.query('.mx_UserInfo_container .mx_UserInfo_startVerification'); - await startVerifyButton.click(); - session.log.done(); -} - -async function getSasCodes(session: ElementSession): Promise { - const sasLabelElements = await session.queryAll( - ".mx_VerificationShowSas .mx_VerificationShowSas_emojiSas .mx_VerificationShowSas_emojiSas_label"); - const sasLabels = await Promise.all(sasLabelElements.map(e => session.innerText(e))); - return sasLabels; -} - -async function doSasVerification(session: ElementSession): Promise { - session.log.step("hunts for the emoji to yell at their opponent"); - const sasCodes = await getSasCodes(session); - session.log.done(sasCodes.join("\n")); - - // Assume they match - session.log.step("assumes the emoji match"); - const matchButton = await session.query(".mx_VerificationShowSas .mx_AccessibleButton_kind_primary"); - await matchButton.click(); - session.log.done(); - - // Wait for a big green shield (universal sign that it worked) - session.log.step("waits for a green shield"); - await session.query(".mx_VerificationPanel_verified_section .mx_E2EIcon_verified"); - session.log.done(); - - // Click 'Got It' - session.log.step("confirms the green shield"); - const doneButton = await session.query(".mx_VerificationPanel_verified_section .mx_AccessibleButton_kind_primary"); - await doneButton.click(); - session.log.done(); - - // Wait a bit for the animation - session.log.step("confirms their opponent has a green shield"); - await session.delay(1000); - - // Verify that we now have a green shield in their name (proving it still works) - await session.query('.mx_UserInfo_profile .mx_E2EIcon_verified'); - session.log.done(); - - return sasCodes; -} - -export async function startSasVerification(session: ElementSession, name: string): Promise { - session.log.startGroup("starts verification"); - await startVerification(session, name); - - // expect to be waiting (the presence of a spinner is a good thing) - await session.query('.mx_UserInfo_container .mx_EncryptionInfo_spinner'); - - const sasCodes = await doSasVerification(session); - session.log.endGroup(); - return sasCodes; -} - -export async function acceptSasVerification(session: ElementSession, name: string): Promise { - session.log.startGroup("accepts verification"); - const requestToast = await session.query('.mx_Toast_icon_verification'); - - // verify the toast is for verification - const toastHeader = await requestToast.$("h2"); - const toastHeaderText = await session.innerText(toastHeader); - assert.equal(toastHeaderText, 'Verification requested'); - const toastDescription = await requestToast.$(".mx_Toast_description"); - const toastDescText = await session.innerText(toastDescription); - assert.equal(toastDescText.startsWith(name), true, - `verification opponent mismatch: expected to start with '${name}', got '${toastDescText}'`); - - // accept the verification - const acceptButton = await requestToast.$(".mx_AccessibleButton_kind_primary"); - await acceptButton.click(); - - // find the emoji button - const startEmojiButton = await session.query(".mx_VerificationPanel_verifyByEmojiButton"); - await startEmojiButton.click(); - - const sasCodes = await doSasVerification(session); - session.log.endGroup(); - return sasCodes; -} diff --git a/test/end-to-end-tests/src/util.ts b/test/end-to-end-tests/src/util.ts deleted file mode 100644 index 130519f396..0000000000 --- a/test/end-to-end-tests/src/util.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright 2018 New Vector Ltd -Copyright 2019 - 2022 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { ConsoleMessage } from "puppeteer"; -import { padEnd } from "lodash"; - -import { ElementSession } from "./session"; - -export const delay = function(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); -}; - -export const measureStart = function(session: ElementSession, name: string): Promise { - return session.page.evaluate(_name => { - window.mxPerformanceMonitor.start(_name); - }, name); -}; - -export const measureStop = function(session: ElementSession, name: string): Promise { - return session.page.evaluate(_name => { - window.mxPerformanceMonitor.stop(_name); - }, name); -}; - -export async function serializeLog(msg: ConsoleMessage): Promise { - // 9 characters padding is somewhat arbitrary ("warning".length + some) - let s = `${new Date().toISOString()} | ${ padEnd(msg.type(), 9, ' ')}| ${msg.text()} `; // trailing space is intentional - const args = msg.args(); - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - let val; - try { - val = await arg.jsonValue(); - } catch (error) { - val = ``; - } - - // We handle strings a bit differently because the `jsonValue` will be in a weird-looking - // shape ("JSHandle:words are here"). Weirdly, `msg.text()` also catches text nodes that - // we can't with our parsing, so we trust that it's correct whenever we can. - if (typeof val === 'string') { - if (i === 0) { - // if it's a string, just ignore it because it should have already been caught - // by the `msg.text()` in the initial `s` construction. - continue; - } - - // evaluate the arg as a string by running it through the page context - s += `${await arg.evaluate(a => a.toString())} `; // trailing space is intentional - continue; - } - - // Try and parse the value as an error object first (which will be an empty JSON - // object). Otherwise, parse the object to a string. - // - // Note: we have to run the checks against the object in the page context, so call - // evaluate instead of just doing it ourselves. - const stringyArg: string = await arg.evaluate((argInContext: any) => { - // sometimes the argument will be `null` or similar - treat it safely. - if (argInContext?.stack || (argInContext instanceof Error)) { - // probably an error - toString it and append any properties which might not be - // caught. For example, on HTTP errors the JSON stringification will capture the - // status code. - // - // String format is a bit weird, but basically we're trying to get away from the - // stack trace so the context doesn't blend in but is otherwise indented correctly. - return `${argInContext.toString()}\n\n Error context: ${JSON.stringify(argInContext)}`; - } - - // not an error, as far as we're concerned - return it as human-readable JSON - let ret; - try { - ret = JSON.stringify(argInContext, null, 4); - } catch (error) { - ret = ``; - } - return ret; - }); - s += `${stringyArg} `; // trailing space is intentional - } - return s; -} diff --git a/test/end-to-end-tests/start.ts b/test/end-to-end-tests/start.ts deleted file mode 100644 index 378d24d2a0..0000000000 --- a/test/end-to-end-tests/start.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import * as fs from "fs"; -import { Command } from "commander"; - -import { ElementSession } from './src/session'; -import { scenario } from './src/scenario'; - -const program = new Command(); - -program - .option('--no-logs', "don't output logs, document html on error", false) - .option('--app-url [url]', "url to test", "http://localhost:5000") - .option('--windowed', "dont run tests headless", false) - .option('--slow-mo', "type at a human speed", false) - .option('--dev-tools', "open chrome devtools in browser window", false) - .option('--throttle-cpu [factor]', "factor to slow down the cpu with", parseFloat, 1.0) - .option('--no-sandbox', "same as puppeteer arg", false) - .option('--log-directory ', 'a directory to dump html and network logs in when the tests fail') - .requiredOption('--registration-shared-secret ', 'the secret to use for registering users') - .parse(process.argv); - -const hsUrl = 'http://localhost:5005'; - -async function runTests() { - const sessions = []; - const options = { - slowMo: program.opts().slowMo ? 20 : undefined, - devtools: program.opts().devTools, - headless: !program.opts().windowed, - args: [], - }; - if (!program.opts().sandbox) { - options.args.push('--no-sandbox', '--disable-setuid-sandbox'); - } - if (process.env.CHROME_PATH) { - const path = process.env.CHROME_PATH; - console.log(`(using external chrome/chromium at ${path}, make sure it's compatible with puppeteer)`); - options['executablePath'] = path; - } - - async function createSession(username: string) { - const session = await ElementSession.create( - username, options, program.opts().appUrl, hsUrl, program.opts().throttleCpu, - ); - sessions.push(session); - return session; - } - - let failure = false; - try { - await scenario(createSession); - } catch (err) { - failure = true; - console.log('failure: ', err); - if (program.opts().logDirectory) { - await writeLogs(sessions, program.opts().logDirectory); - } - } - - // wait 5 minutes on failure if not running headless - // to inspect what went wrong - if (failure && options.headless === false) { - await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000)); - } - - let performanceEntries; - - await Promise.all(sessions.map(async (session) => { - // Collecting all performance monitoring data before closing the session - const measurements = await session.page.evaluate(() => { - let measurements; - - // Some tests do redirects away from the app, so don't count those sessions. - if (!window.mxPerformanceMonitor) return JSON.stringify([]); - - window.mxPerformanceMonitor.addPerformanceDataCallback({ - entryNames: [ - window.mxPerformanceEntryNames.REGISTER, - window.mxPerformanceEntryNames.LOGIN, - window.mxPerformanceEntryNames.JOIN_ROOM, - window.mxPerformanceEntryNames.CREATE_DM, - window.mxPerformanceEntryNames.VERIFY_E2EE_USER, - ], - callback: (events) => { - measurements = JSON.stringify(events); - }, - }, true); - return measurements; - }); - - /** - * TODO: temporary only use one user session data - */ - performanceEntries = JSON.parse(measurements ?? "[]"); - return session.close(); - })); - if (performanceEntries?.length > 0) { - fs.writeFileSync(`performance-entries.json`, JSON.stringify(performanceEntries)); - } - if (failure) { - process.exit(-1); - } else { - console.log('all tests finished successfully'); - } -} - -async function writeLogs(sessions, dir) { - const logs = ""; - for (let i = 0; i < sessions.length; ++i) { - const session = sessions[i]; - const userLogDir = `${dir}/${session.username}`; - try { - fs.mkdirSync(userLogDir); - } catch (e) { - // typically this will be EEXIST. If it's something worse, the next few - // lines will fail too. - console.warn(`non-fatal error creating ${userLogDir} :`, e.message); - } - const consoleLogName = `${userLogDir}/console.log`; - const networkLogName = `${userLogDir}/network.log`; - const appHtmlName = `${userLogDir}/app.html`; - const documentHtml = await session.page.content(); - fs.writeFileSync(appHtmlName, documentHtml); - fs.writeFileSync(networkLogName, session.networkLogs()); - fs.writeFileSync(consoleLogName, session.consoleLogs()); - await session.page.screenshot({ path: `${userLogDir}/screenshot.png` }); - } - return logs; -} - -runTests().catch(function(err) { - console.log(err); - process.exit(-1); -}); diff --git a/test/end-to-end-tests/synapse/.gitignore b/test/end-to-end-tests/synapse/.gitignore deleted file mode 100644 index aed68e9f30..0000000000 --- a/test/end-to-end-tests/synapse/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -installations -synapse.zip \ No newline at end of file diff --git a/test/end-to-end-tests/synapse/config-templates/consent/homeserver.yaml b/test/end-to-end-tests/synapse/config-templates/consent/homeserver.yaml deleted file mode 100644 index 9aa67b2220..0000000000 --- a/test/end-to-end-tests/synapse/config-templates/consent/homeserver.yaml +++ /dev/null @@ -1,1141 +0,0 @@ -# vim:ft=yaml - -## Server ## - -# The domain name of the server, with optional explicit port. -# This is used by remote servers to connect to this server, -# e.g. matrix.org, localhost:8080, etc. -# This is also the last part of your UserID. -# -server_name: "localhost" - -# When running as a daemon, the file to store the pid in -# -pid_file: {{SYNAPSE_ROOT}}homeserver.pid - -# CPU affinity mask. Setting this restricts the CPUs on which the -# process will be scheduled. It is represented as a bitmask, with the -# lowest order bit corresponding to the first logical CPU and the -# highest order bit corresponding to the last logical CPU. Not all CPUs -# may exist on a given system but a mask may specify more CPUs than are -# present. -# -# For example: -# 0x00000001 is processor #0, -# 0x00000003 is processors #0 and #1, -# 0xFFFFFFFF is all processors (#0 through #31). -# -# Pinning a Python process to a single CPU is desirable, because Python -# is inherently single-threaded due to the GIL, and can suffer a -# 30-40% slowdown due to cache blow-out and thread context switching -# if the scheduler happens to schedule the underlying threads across -# different cores. See -# https://www.mirantis.com/blog/improve-performance-python-programs-restricting-single-cpu/. -# -# This setting requires the affinity package to be installed! -# -#cpu_affinity: 0xFFFFFFFF - -# The path to the web client which will be served at /_matrix/client/ -# if 'webclient' is configured under the 'listeners' configuration. -# -#web_client_location: "/path/to/web/root" - -# The public-facing base URL that clients use to access this HS -# (not including _matrix/...). This is the same URL a user would -# enter into the 'custom HS URL' field on their client. If you -# use synapse with a reverse proxy, this should be the URL to reach -# synapse via the proxy. -# -public_baseurl: http://localhost:{{SYNAPSE_PORT}}/ - -# Set the soft limit on the number of file descriptors synapse can use -# Zero is used to indicate synapse should set the soft limit to the -# hard limit. -# -#soft_file_limit: 0 - -# Set to false to disable presence tracking on this homeserver. -# -#use_presence: false - -# The GC threshold parameters to pass to `gc.set_threshold`, if defined -# -#gc_thresholds: [700, 10, 10] - -# Set the limit on the returned events in the timeline in the get -# and sync operations. The default value is -1, means no upper limit. -# -#filter_timeline_limit: 5000 - -# Whether room invites to users on this server should be blocked -# (except those sent by local server admins). The default is False. -# -#block_non_admin_invites: True - -# Room searching -# -# If disabled, new messages will not be indexed for searching and users -# will receive errors when searching for messages. Defaults to enabled. -# -#enable_search: false - -# Restrict federation to the following whitelist of domains. -# N.B. we recommend also firewalling your federation listener to limit -# inbound federation traffic as early as possible, rather than relying -# purely on this application-layer restriction. If not specified, the -# default is to whitelist everything. -# -#federation_domain_whitelist: -# - lon.example.com -# - nyc.example.com -# - syd.example.com - -# List of ports that Synapse should listen on, their purpose and their -# configuration. -# -# Options for each listener include: -# -# port: the TCP port to bind to -# -# bind_addresses: a list of local addresses to listen on. The default is -# 'all local interfaces'. -# -# type: the type of listener. Normally 'http', but other valid options are: -# 'manhole' (see docs/manhole.md), -# 'metrics' (see docs/metrics-howto.rst), -# 'replication' (see docs/workers.rst). -# -# tls: set to true to enable TLS for this listener. Will use the TLS -# key/cert specified in tls_private_key_path / tls_certificate_path. -# -# x_forwarded: Only valid for an 'http' listener. Set to true to use the -# X-Forwarded-For header as the client IP. Useful when Synapse is -# behind a reverse-proxy. -# -# resources: Only valid for an 'http' listener. A list of resources to host -# on this port. Options for each resource are: -# -# names: a list of names of HTTP resources. See below for a list of -# valid resource names. -# -# compress: set to true to enable HTTP comression for this resource. -# -# additional_resources: Only valid for an 'http' listener. A map of -# additional endpoints which should be loaded via dynamic modules. -# -# Valid resource names are: -# -# client: the client-server API (/_matrix/client). Also implies 'media' and -# 'static'. -# -# consent: user consent forms (/_matrix/consent). See -# docs/consent_tracking.md. -# -# federation: the server-server API (/_matrix/federation). Also implies -# 'media', 'keys', 'openid' -# -# keys: the key discovery API (/_matrix/keys). -# -# media: the media API (/_matrix/media). -# -# metrics: the metrics interface. See docs/metrics-howto.rst. -# -# openid: OpenID authentication. -# -# replication: the HTTP replication API (/_synapse/replication). See -# docs/workers.rst. -# -# static: static resources under synapse/static (/_matrix/static). (Mostly -# useful for 'fallback authentication'.) -# -# webclient: A web client. Requires web_client_location to be set. -# -listeners: - # TLS-enabled listener: for when matrix traffic is sent directly to synapse. - # - # Disabled by default. To enable it, uncomment the following. (Note that you - # will also need to give Synapse a TLS key and certificate: see the TLS section - # below.) - # - #- port: 8448 - # type: http - # tls: true - # resources: - # - names: [client, federation] - - # Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy - # that unwraps TLS. - # - # If you plan to use a reverse proxy, please see - # https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst. - # - - port: {{SYNAPSE_PORT}} - tls: false - bind_addresses: ['127.0.0.1'] - type: http - x_forwarded: true - - resources: - - names: [client, federation, consent] - compress: false - - # example additonal_resources: - # - #additional_resources: - # "/_matrix/my/custom/endpoint": - # module: my_module.CustomRequestHandler - # config: {} - - # Turn on the twisted ssh manhole service on localhost on the given - # port. - # - #- port: 9000 - # bind_addresses: ['::1', '127.0.0.1'] - # type: manhole - - -## Homeserver blocking ## - -# How to reach the server admin, used in ResourceLimitError -# -#admin_contact: 'mailto:admin@server.com' - -# Global blocking -# -#hs_disabled: False -#hs_disabled_message: 'Human readable reason for why the HS is blocked' -#hs_disabled_limit_type: 'error code(str), to help clients decode reason' - -# Monthly Active User Blocking -# -#limit_usage_by_mau: False -#max_mau_value: 50 -#mau_trial_days: 2 - -# If enabled, the metrics for the number of monthly active users will -# be populated, however no one will be limited. If limit_usage_by_mau -# is true, this is implied to be true. -# -#mau_stats_only: False - -# Sometimes the server admin will want to ensure certain accounts are -# never blocked by mau checking. These accounts are specified here. -# -#mau_limit_reserved_threepids: -# - medium: 'email' -# address: 'reserved_user@example.com' - - -## TLS ## - -# PEM-encoded X509 certificate for TLS. -# This certificate, as of Synapse 1.0, will need to be a valid and verifiable -# certificate, signed by a recognised Certificate Authority. -# -# See 'ACME support' below to enable auto-provisioning this certificate via -# Let's Encrypt. -# -# If supplying your own, be sure to use a `.pem` file that includes the -# full certificate chain including any intermediate certificates (for -# instance, if using certbot, use `fullchain.pem` as your certificate, -# not `cert.pem`). -# -#tls_certificate_path: "{{SYNAPSE_ROOT}}localhost.tls.crt" - -# PEM-encoded private key for TLS -# -#tls_private_key_path: "{{SYNAPSE_ROOT}}localhost.tls.key" - -# ACME support: This will configure Synapse to request a valid TLS certificate -# for your configured `server_name` via Let's Encrypt. -# -# Note that provisioning a certificate in this way requires port 80 to be -# routed to Synapse so that it can complete the http-01 ACME challenge. -# By default, if you enable ACME support, Synapse will attempt to listen on -# port 80 for incoming http-01 challenges - however, this will likely fail -# with 'Permission denied' or a similar error. -# -# There are a couple of potential solutions to this: -# -# * If you already have an Apache, Nginx, or similar listening on port 80, -# you can configure Synapse to use an alternate port, and have your web -# server forward the requests. For example, assuming you set 'port: 8009' -# below, on Apache, you would write: -# -# ProxyPass /.well-known/acme-challenge http://localhost:8009/.well-known/acme-challenge -# -# * Alternatively, you can use something like `authbind` to give Synapse -# permission to listen on port 80. -# -acme: - # ACME support is disabled by default. Uncomment the following line - # (and tls_certificate_path and tls_private_key_path above) to enable it. - # - #enabled: true - - # Endpoint to use to request certificates. If you only want to test, - # use Let's Encrypt's staging url: - # https://acme-staging.api.letsencrypt.org/directory - # - #url: https://acme-v01.api.letsencrypt.org/directory - - # Port number to listen on for the HTTP-01 challenge. Change this if - # you are forwarding connections through Apache/Nginx/etc. - # - #port: 80 - - # Local addresses to listen on for incoming connections. - # Again, you may want to change this if you are forwarding connections - # through Apache/Nginx/etc. - # - #bind_addresses: ['::', '0.0.0.0'] - - # How many days remaining on a certificate before it is renewed. - # - #reprovision_threshold: 30 - - # The domain that the certificate should be for. Normally this - # should be the same as your Matrix domain (i.e., 'server_name'), but, - # by putting a file at 'https:///.well-known/matrix/server', - # you can delegate incoming traffic to another server. If you do that, - # you should give the target of the delegation here. - # - # For example: if your 'server_name' is 'example.com', but - # 'https://example.com/.well-known/matrix/server' delegates to - # 'matrix.example.com', you should put 'matrix.example.com' here. - # - # If not set, defaults to your 'server_name'. - # - #domain: matrix.example.com - -# List of allowed TLS fingerprints for this server to publish along -# with the signing keys for this server. Other matrix servers that -# make HTTPS requests to this server will check that the TLS -# certificates returned by this server match one of the fingerprints. -# -# Synapse automatically adds the fingerprint of its own certificate -# to the list. So if federation traffic is handled directly by synapse -# then no modification to the list is required. -# -# If synapse is run behind a load balancer that handles the TLS then it -# will be necessary to add the fingerprints of the certificates used by -# the loadbalancers to this list if they are different to the one -# synapse is using. -# -# Homeservers are permitted to cache the list of TLS fingerprints -# returned in the key responses up to the "valid_until_ts" returned in -# key. It may be necessary to publish the fingerprints of a new -# certificate and wait until the "valid_until_ts" of the previous key -# responses have passed before deploying it. -# -# You can calculate a fingerprint from a given TLS listener via: -# openssl s_client -connect $host:$port < /dev/null 2> /dev/null | -# openssl x509 -outform DER | openssl sha256 -binary | base64 | tr -d '=' -# or by checking matrix.org/federationtester/api/report?server_name=$host -# -#tls_fingerprints: [{"sha256": ""}] - - - -## Database ## - -database: - # The database engine name - name: "sqlite3" - # Arguments to pass to the engine - args: - # Path to the database - database: ":memory:" - -# Number of events to cache in memory. -# -#event_cache_size: 10K - - -## Logging ## - -# A yaml python logging config file -# -log_config: "{{SYNAPSE_ROOT}}localhost.log.config" - - -## Ratelimiting ## - -# Number of messages a client can send per second -# -rc_messages_per_second: 10000 - -# Number of message a client can send before being throttled -# -rc_message_burst_count: 10000 - -# Ratelimiting settings for registration and login. -# -# Each ratelimiting configuration is made of two parameters: -# - per_second: number of requests a client can send per second. -# - burst_count: number of requests a client can send before being throttled. -# -# Synapse currently uses the following configurations: -# - one for registration that ratelimits registration requests based on the -# client's IP address. -# - one for login that ratelimits login requests based on the client's IP -# address. -# - one for login that ratelimits login requests based on the account the -# client is attempting to log into. -# - one for login that ratelimits login requests based on the account the -# client is attempting to log into, based on the amount of failed login -# attempts for this account. -# -# The defaults are as shown below. -# -rc_registration: - per_second: 10000 - burst_count: 10000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -# The federation window size in milliseconds -# -#federation_rc_window_size: 1000 - -# The number of federation requests from a single server in a window -# before the server will delay processing the request. -# -#federation_rc_sleep_limit: 10 - -# The duration in milliseconds to delay processing events from -# remote servers by if they go over the sleep limit. -# -#federation_rc_sleep_delay: 500 - -# The maximum number of concurrent federation requests allowed -# from a single server -# -#federation_rc_reject_limit: 50 - -# The number of federation requests to concurrently process from a -# single server -# -#federation_rc_concurrent: 3 - -# Target outgoing federation transaction frequency for sending read-receipts, -# per-room. -# -# If we end up trying to send out more read-receipts, they will get buffered up -# into fewer transactions. -# -#federation_rr_transactions_per_room_per_second: 50 - - - -# Directory where uploaded images and attachments are stored. -# -media_store_path: "{{SYNAPSE_ROOT}}media_store" - -# Media storage providers allow media to be stored in different -# locations. -# -#media_storage_providers: -# - module: file_system -# # Whether to write new local files. -# store_local: false -# # Whether to write new remote media -# store_remote: false -# # Whether to block upload requests waiting for write to this -# # provider to complete -# store_synchronous: false -# config: -# directory: /mnt/some/other/directory - -# Directory where in-progress uploads are stored. -# -uploads_path: "{{SYNAPSE_ROOT}}uploads" - -# The largest allowed upload size in bytes -# -#max_upload_size: 10M - -# Maximum number of pixels that will be thumbnailed -# -#max_image_pixels: 32M - -# Whether to generate new thumbnails on the fly to precisely match -# the resolution requested by the client. If true then whenever -# a new resolution is requested by the client the server will -# generate a new thumbnail. If false the server will pick a thumbnail -# from a precalculated list. -# -#dynamic_thumbnails: false - -# List of thumbnails to precalculate when an image is uploaded. -# -#thumbnail_sizes: -# - width: 32 -# height: 32 -# method: crop -# - width: 96 -# height: 96 -# method: crop -# - width: 320 -# height: 240 -# method: scale -# - width: 640 -# height: 480 -# method: scale -# - width: 800 -# height: 600 -# method: scale - -# Is the preview URL API enabled? If enabled, you *must* specify -# an explicit url_preview_ip_range_blacklist of IPs that the spider is -# denied from accessing. -# -#url_preview_enabled: false - -# List of IP address CIDR ranges that the URL preview spider is denied -# from accessing. There are no defaults: you must explicitly -# specify a list for URL previewing to work. You should specify any -# internal services in your network that you do not want synapse to try -# to connect to, otherwise anyone in any Matrix room could cause your -# synapse to issue arbitrary GET requests to your internal services, -# causing serious security issues. -# -#url_preview_ip_range_blacklist: -# - '127.0.0.0/8' -# - '10.0.0.0/8' -# - '172.16.0.0/12' -# - '192.168.0.0/16' -# - '100.64.0.0/10' -# - '169.254.0.0/16' -# - '::1/128' -# - 'fe80::/64' -# - 'fc00::/7' -# -# List of IP address CIDR ranges that the URL preview spider is allowed -# to access even if they are specified in url_preview_ip_range_blacklist. -# This is useful for specifying exceptions to wide-ranging blacklisted -# target IP ranges - e.g. for enabling URL previews for a specific private -# website only visible in your network. -# -#url_preview_ip_range_whitelist: -# - '192.168.1.1' - -# Optional list of URL matches that the URL preview spider is -# denied from accessing. You should use url_preview_ip_range_blacklist -# in preference to this, otherwise someone could define a public DNS -# entry that points to a private IP address and circumvent the blacklist. -# This is more useful if you know there is an entire shape of URL that -# you know that will never want synapse to try to spider. -# -# Each list entry is a dictionary of url component attributes as returned -# by urlparse.urlsplit as applied to the absolute form of the URL. See -# https://docs.python.org/2/library/urlparse.html#urlparse.urlsplit -# The values of the dictionary are treated as an filename match pattern -# applied to that component of URLs, unless they start with a ^ in which -# case they are treated as a regular expression match. If all the -# specified component matches for a given list item succeed, the URL is -# blacklisted. -# -#url_preview_url_blacklist: -# # blacklist any URL with a username in its URI -# - username: '*' -# -# # blacklist all *.google.com URLs -# - netloc: 'google.com' -# - netloc: '*.google.com' -# -# # blacklist all plain HTTP URLs -# - scheme: 'http' -# -# # blacklist http(s)://www.acme.com/foo -# - netloc: 'www.acme.com' -# path: '/foo' -# -# # blacklist any URL with a literal IPv4 address -# - netloc: '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' - -# The largest allowed URL preview spidering size in bytes -# -#max_spider_size: 10M - - -## Captcha ## -# See docs/CAPTCHA_SETUP for full details of configuring this. - -# This homeserver's ReCAPTCHA public key. -# -#recaptcha_public_key: "YOUR_PUBLIC_KEY" - -# This homeserver's ReCAPTCHA private key. -# -#recaptcha_private_key: "YOUR_PRIVATE_KEY" - -# Enables ReCaptcha checks when registering, preventing signup -# unless a captcha is answered. Requires a valid ReCaptcha -# public/private key. -# -#enable_registration_captcha: false - -# A secret key used to bypass the captcha test entirely. -# -#captcha_bypass_secret: "YOUR_SECRET_HERE" - -# The API endpoint to use for verifying m.login.recaptcha responses. -# -#recaptcha_siteverify_api: "https://www.recaptcha.net/recaptcha/api/siteverify" - - -## TURN ## - -# The public URIs of the TURN server to give to clients -# -#turn_uris: [] - -# The shared secret used to compute passwords for the TURN server -# -#turn_shared_secret: "YOUR_SHARED_SECRET" - -# The Username and password if the TURN server needs them and -# does not use a token -# -#turn_username: "TURNSERVER_USERNAME" -#turn_password: "TURNSERVER_PASSWORD" - -# How long generated TURN credentials last -# -#turn_user_lifetime: 1h - -# Whether guests should be allowed to use the TURN server. -# This defaults to True, otherwise VoIP will be unreliable for guests. -# However, it does introduce a slight security risk as it allows users to -# connect to arbitrary endpoints without having first signed up for a -# valid account (e.g. by passing a CAPTCHA). -# -#turn_allow_guests: True - - -## Registration ## -# -# Registration can be rate-limited using the parameters in the "Ratelimiting" -# section of this file. - -# Enable registration for new users. -# -enable_registration: true - -# Enable registration without email or captcha verification. Note: this option is *not* recommended, -# as registration without verification is a known vector for spam and abuse. Defaults to false. Has no effect -# unless `enable_registration` is also enabled. -# -enable_registration_without_verification: true - -# The user must provide all of the below types of 3PID when registering. -# -#registrations_require_3pid: -# - email -# - msisdn - -# Explicitly disable asking for MSISDNs from the registration -# flow (overrides registrations_require_3pid if MSISDNs are set as required) -# -disable_msisdn_registration: false - -# Mandate that users are only allowed to associate certain formats of -# 3PIDs with accounts on this server. -# -#allowed_local_3pids: -# - medium: email -# pattern: '.*@matrix\.org' -# - medium: email -# pattern: '.*@vector\.im' -# - medium: msisdn -# pattern: '\+44' - -# If set, allows registration of standard or admin accounts by anyone who -# has the shared secret, even if registration is otherwise disabled. -# -registration_shared_secret: "{{REGISTRATION_SHARED_SECRET}}" - -# Set the number of bcrypt rounds used to generate password hash. -# Larger numbers increase the work factor needed to generate the hash. -# The default number is 12 (which equates to 2^12 rounds). -# N.B. that increasing this will exponentially increase the time required -# to register or login - e.g. 24 => 2^24 rounds which will take >20 mins. -# -#bcrypt_rounds: 12 - -# Allows users to register as guests without a password/email/etc, and -# participate in rooms hosted on this server which have been made -# accessible to anonymous users. -# -#allow_guest_access: false - -# The identity server which we suggest that clients should use when users log -# in on this server. -# -# (By default, no suggestion is made, so it is left up to the client. -# This setting is ignored unless public_baseurl is also set.) -# -#default_identity_server: https://matrix.org - -# The list of identity servers trusted to verify third party -# identifiers by this server. -# -# Also defines the identity server which will be called when an account is -# deactivated (one will be picked arbitrarily). -# -#trusted_third_party_id_servers: -# - matrix.org -# - vector.im - -# Users who register on this homeserver will automatically be joined -# to these rooms -# -#auto_join_rooms: -# - "#example:example.com" - -# Where auto_join_rooms are specified, setting this flag ensures that the -# the rooms exist by creating them when the first user on the -# homeserver registers. -# Setting to false means that if the rooms are not manually created, -# users cannot be auto-joined since they do not exist. -# -#autocreate_auto_join_rooms: true - - -## Metrics ### - -# Enable collection and rendering of performance metrics -# -#enable_metrics: False - -# Enable sentry integration -# NOTE: While attempts are made to ensure that the logs don't contain -# any sensitive information, this cannot be guaranteed. By enabling -# this option the sentry server may therefore receive sensitive -# information, and it in turn may then diseminate sensitive information -# through insecure notification channels if so configured. -# -#sentry: -# dsn: "..." - -# Whether or not to report anonymized homeserver usage statistics. -report_stats: false - - -## API Configuration ## - -# A list of event types that will be included in the room_invite_state -# -#room_invite_state_types: -# - "m.room.join_rules" -# - "m.room.canonical_alias" -# - "m.room.avatar" -# - "m.room.encryption" -# - "m.room.name" - - -# A list of application service config files to use -# -#app_service_config_files: -# - app_service_1.yaml -# - app_service_2.yaml - -# Uncomment to enable tracking of application service IP addresses. Implicitly -# enables MAU tracking for application service users. -# -#track_appservice_user_ips: True - - -# a secret which is used to sign access tokens. If none is specified, -# the registration_shared_secret is used, if one is given; otherwise, -# a secret key is derived from the signing key. -# -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" - -# Used to enable access token expiration. -# -#expire_access_token: False - -# a secret which is used to calculate HMACs for form values, to stop -# falsification of values. Must be specified for the User Consent -# forms to work. -# -form_secret: "{{FORM_SECRET}}" - -## Signing Keys ## - -# Path to the signing key to sign messages with -# -signing_key_path: "{{SYNAPSE_ROOT}}localhost.signing.key" - -# The keys that the server used to sign messages with but won't use -# to sign new messages. E.g. it has lost its private key -# -#old_signing_keys: -# "ed25519:auto": -# # Base64 encoded public key -# key: "The public part of your old signing key." -# # Millisecond POSIX timestamp when the key expired. -# expired_ts: 123456789123 - -# How long key response published by this server is valid for. -# Used to set the valid_until_ts in /key/v2 APIs. -# Determines how quickly servers will query to check which keys -# are still valid. -# -#key_refresh_interval: 1d - -# The trusted servers to download signing keys from. -# -# When we need to fetch a signing key, each server is tried in parallel. -# -# Normally, the connection to the key server is validated via TLS certificates. -# Additional security can be provided by configuring a `verify key`, which -# will make synapse check that the response is signed by that key. -# -# This setting supercedes an older setting named `perspectives`. The old format -# is still supported for backwards-compatibility, but it is deprecated. -# -# 'trusted_key_servers' defaults to matrix.org, but using it will generate a -# warning on start-up. To suppress this warning, set -# 'suppress_key_server_warning' to true. -# -# Options for each entry in the list include: -# -# server_name: the name of the server. required. -# -# verify_keys: an optional map from key id to base64-encoded public key. -# If specified, we will check that the response is signed by at least -# one of the given keys. -# -# accept_keys_insecurely: a boolean. Normally, if `verify_keys` is unset, -# and federation_verify_certificates is not `true`, synapse will refuse -# to start, because this would allow anyone who can spoof DNS responses -# to masquerade as the trusted key server. If you know what you are doing -# and are sure that your network environment provides a secure connection -# to the key server, you can set this to `true` to override this -# behaviour. -# -# An example configuration might look like: -# -#trusted_key_servers: -# - server_name: "my_trusted_server.example.com" -# verify_keys: -# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr" -# - server_name: "my_other_trusted_server.example.com" -# -trusted_key_servers: - - server_name: "matrix.org" - -# Uncomment the following to disable the warning that is emitted when the -# trusted_key_servers include 'matrix.org'. See above. -# -suppress_key_server_warning: true - -# Enable SAML2 for registration and login. Uses pysaml2. -# -# `sp_config` is the configuration for the pysaml2 Service Provider. -# See pysaml2 docs for format of config. -# -# Default values will be used for the 'entityid' and 'service' settings, -# so it is not normally necessary to specify them unless you need to -# override them. -# -#saml2_config: -# sp_config: -# # point this to the IdP's metadata. You can use either a local file or -# # (preferably) a URL. -# metadata: -# #local: ["saml2/idp.xml"] -# remote: -# - url: https://our_idp/metadata.xml -# -# # The rest of sp_config is just used to generate our metadata xml, and you -# # may well not need it, depending on your setup. Alternatively you -# # may need a whole lot more detail - see the pysaml2 docs! -# -# description: ["My awesome SP", "en"] -# name: ["Test SP", "en"] -# -# organization: -# name: Example com -# display_name: -# - ["Example co", "en"] -# url: "http://example.com" -# -# contact_person: -# - given_name: Bob -# sur_name: "the Sysadmin" -# email_address": ["admin@example.com"] -# contact_type": technical -# -# # Instead of putting the config inline as above, you can specify a -# # separate pysaml2 configuration file: -# # -# config_path: "{{SYNAPSE_ROOT}}sp_conf.py" - - - -# Enable CAS for registration and login. -# -#cas_config: -# enabled: true -# server_url: "https://cas-server.com" -# service_url: "https://homeserver.domain.com:8448" -# #required_attributes: -# # name: value - - -# The JWT needs to contain a globally unique "sub" (subject) claim. -# -#jwt_config: -# enabled: true -# secret: "a secret" -# algorithm: "HS256" - - -password_config: - # Uncomment to disable password login - # - #enabled: false - - # Uncomment and change to a secret random string for extra security. - # DO NOT CHANGE THIS AFTER INITIAL SETUP! - # - #pepper: "EVEN_MORE_SECRET" - - - -# Enable sending emails for notification events -# Defining a custom URL for Element is only needed if email notifications -# should contain links to a self-hosted installation of Element; when set -# the "app_name" setting is ignored. -# -# If your SMTP server requires authentication, the optional smtp_user & -# smtp_pass variables should be used -# -email: - enable_notifs: false - smtp_host: "localhost" - smtp_port: 25 - smtp_user: "exampleusername" - smtp_pass: "examplepassword" - require_transport_security: False - notif_from: "Your Friendly %(app)s homeserver " - app_name: Matrix - # if template_dir is unset, uses the example templates that are part of - # the Synapse distribution. - #template_dir: res/templates - notif_template_html: notif_mail.html - notif_template_text: notif_mail.txt - notif_for_new_users: True - client_base_url: "http://localhost/element" - - -#password_providers: -# - module: "ldap_auth_provider.LdapAuthProvider" -# config: -# enabled: true -# uri: "ldap://ldap.example.com:389" -# start_tls: true -# base: "ou=users,dc=example,dc=com" -# attributes: -# uid: "cn" -# mail: "email" -# name: "givenName" -# #bind_dn: -# #bind_password: -# #filter: "(objectClass=posixAccount)" - - - -# Clients requesting push notifications can either have the body of -# the message sent in the notification poke along with other details -# like the sender, or just the event ID and room ID (`event_id_only`). -# If clients choose the former, this option controls whether the -# notification request includes the content of the event (other details -# like the sender are still included). For `event_id_only` push, it -# has no effect. -# -# For modern android devices the notification content will still appear -# because it is loaded by the app. iPhone, however will send a -# notification saying only that a message arrived and who it came from. -# -#push: -# include_content: true - - -#spam_checker: -# module: "my_custom_project.SuperSpamChecker" -# config: -# example_option: 'things' - - -# Uncomment to allow non-server-admin users to create groups on this server -# -#enable_group_creation: true - -# If enabled, non server admins can only create groups with local parts -# starting with this prefix -# -#group_creation_prefix: "unofficial/" - - - -# User Directory configuration -# -# 'enabled' defines whether users can search the user directory. If -# false then empty responses are returned to all queries. Defaults to -# true. -# -# 'search_all_users' defines whether to search all users visible to your HS -# when searching the user directory, rather than limiting to users visible -# in public rooms. Defaults to false. If you set it True, you'll have to run -# UPDATE user_directory_stream_pos SET stream_id = NULL; -# on your database to tell it to rebuild the user_directory search indexes. -# -#user_directory: -# enabled: true -# search_all_users: false - - -# User Consent configuration -# -# for detailed instructions, see -# https://github.com/matrix-org/synapse/blob/master/docs/consent_tracking.md -# -# Parts of this section are required if enabling the 'consent' resource under -# 'listeners', in particular 'template_dir' and 'version'. -# -# 'template_dir' gives the location of the templates for the HTML forms. -# This directory should contain one subdirectory per language (eg, 'en', 'fr'), -# and each language directory should contain the policy document (named as -# '.html') and a success page (success.html). -# -# 'version' specifies the 'current' version of the policy document. It defines -# the version to be served by the consent resource if there is no 'v' -# parameter. -# -# 'server_notice_content', if enabled, will send a user a "Server Notice" -# asking them to consent to the privacy policy. The 'server_notices' section -# must also be configured for this to work. Notices will *not* be sent to -# guest users unless 'send_server_notice_to_guests' is set to true. -# -# 'block_events_error', if set, will block any attempts to send events -# until the user consents to the privacy policy. The value of the setting is -# used as the text of the error. -# -# 'require_at_registration', if enabled, will add a step to the registration -# process, similar to how captcha works. Users will be required to accept the -# policy before their account is created. -# -# 'policy_name' is the display name of the policy users will see when registering -# for an account. Has no effect unless `require_at_registration` is enabled. -# Defaults to "Privacy Policy". -# -user_consent: - template_dir: res/templates/privacy - version: 1.0 - server_notice_content: - msgtype: m.text - body: >- - To continue using this homeserver you must review and agree to the - terms and conditions at %(consent_uri)s - send_server_notice_to_guests: True - block_events_error: >- - To continue using this homeserver you must review and agree to the - terms and conditions at %(consent_uri)s - require_at_registration: true - -# Server Notices room configuration -# -# Uncomment this section to enable a room which can be used to send notices -# from the server to users. It is a special room which cannot be left; notices -# come from a special "notices" user id. -# -# If you uncomment this section, you *must* define the system_mxid_localpart -# setting, which defines the id of the user which will be used to send the -# notices. -# -# It's also possible to override the room name, the display name of the -# "notices" user, and the avatar for the user. -# -server_notices: - system_mxid_localpart: notices - system_mxid_display_name: "Server Notices" - system_mxid_avatar_url: "mxc://localhost:{{SYNAPSE_PORT}}/oumMVlgDnLYFaPVkExemNVVZ" - room_name: "Server Notices" - -# Uncomment to disable searching the public room list. When disabled -# blocks searching local and remote room lists for local and remote -# users by always returning an empty list for all queries. -# -#enable_room_list_search: false - -# The `alias_creation` option controls who's allowed to create aliases -# on this server. -# -# The format of this option is a list of rules that contain globs that -# match against user_id, room_id and the new alias (fully qualified with -# server name). The action in the first rule that matches is taken, -# which can currently either be "allow" or "deny". -# -# Missing user_id/room_id/alias fields default to "*". -# -# If no rules match the request is denied. An empty list means no one -# can create aliases. -# -# Options for the rules include: -# -# user_id: Matches against the creator of the alias -# alias: Matches against the alias being created -# room_id: Matches against the room ID the alias is being pointed at -# action: Whether to "allow" or "deny" the request if the rule matches -# -# The default is: -# -#alias_creation_rules: -# - user_id: "*" -# alias: "*" -# room_id: "*" -# action: allow - -# The `room_list_publication_rules` option controls who can publish and -# which rooms can be published in the public room list. -# -# The format of this option is the same as that for -# `alias_creation_rules`. -# -# If the room has one or more aliases associated with it, only one of -# the aliases needs to match the alias rule. If there are no aliases -# then only rules with `alias: *` match. -# -# If no rules match the request is denied. An empty list means no one -# can publish rooms. -# -# Options for the rules include: -# -# user_id: Matches agaisnt the creator of the alias -# room_id: Matches against the room ID being published -# alias: Matches against any current local or canonical aliases -# associated with the room -# action: Whether to "allow" or "deny" the request if the rule matches -# -# The default is: -# -#room_list_publication_rules: -# - user_id: "*" -# alias: "*" -# room_id: "*" -# action: allow diff --git a/test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/1.0.html b/test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/1.0.html deleted file mode 100644 index d4959b4bcb..0000000000 --- a/test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/1.0.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - Test Privacy policy - - - {% if has_consented %} -

- Thank you, you've already accepted the license. -

- {% else %} -

- Please accept the license! -

-
- - - - -
- {% endif %} - - \ No newline at end of file diff --git a/test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/success.html b/test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/success.html deleted file mode 100644 index abe27d87ca..0000000000 --- a/test/end-to-end-tests/synapse/config-templates/consent/res/templates/privacy/en/success.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - Test Privacy policy - - -

Danke schon

- - \ No newline at end of file diff --git a/test/end-to-end-tests/synapse/getcfg.sh b/test/end-to-end-tests/synapse/getcfg.sh deleted file mode 100755 index e5e81586a4..0000000000 --- a/test/end-to-end-tests/synapse/getcfg.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -e - -if [ $# -eq 0 ] -then - echo "Prints a configuration directive from the synapse installation" - echo "Usage: getcfg.sh " - exit 1 -fi - -# activate the virtualenv so we have pyyaml -BASE_DIR=$(cd $(dirname $0) && pwd) -cd $BASE_DIR -cd installations/consent/env/bin/ -source activate - -python -c "from yaml import load, Loader; import sys; print(load(sys.stdin, Loader=Loader)['$1'])" < homeserver.yaml diff --git a/test/end-to-end-tests/synapse/install.sh b/test/end-to-end-tests/synapse/install.sh deleted file mode 100755 index a85fdea26d..0000000000 --- a/test/end-to-end-tests/synapse/install.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -set -e - -# config -SYNAPSE_BRANCH=develop -INSTALLATION_NAME=consent -SERVER_DIR=installations/$INSTALLATION_NAME -CONFIG_TEMPLATE=consent -PORT=5005 -# set current directory to script directory -BASE_DIR=$(cd $(dirname $0) && pwd) - -if [ -d $BASE_DIR/$SERVER_DIR ]; then - echo "synapse is already installed" - exit -fi - -cd $BASE_DIR -mkdir -p $SERVER_DIR -cd $SERVER_DIR -virtualenv -p python3 env -source env/bin/activate - -pip install --upgrade pip -pip install --upgrade setuptools -pip install https://codeload.github.com/matrix-org/synapse/zip/$SYNAPSE_BRANCH -# apply configuration -pushd env/bin/ -cp -r $BASE_DIR/config-templates/$CONFIG_TEMPLATE/. ./ - -# Hashes used instead of slashes because we'll get a value back from $(pwd) that'll be -# full of un-escapable slashes. -# Use .bak suffix as using no suffix doesn't work macOS. -sed -i.bak "s#{{SYNAPSE_ROOT}}#$(pwd)/#g" homeserver.yaml -sed -i.bak "s#{{SYNAPSE_PORT}}#${PORT}#g" homeserver.yaml -sed -i.bak "s#{{FORM_SECRET}}#$(uuidgen)#g" homeserver.yaml -sed -i.bak "s#{{REGISTRATION_SHARED_SECRET}}#$(uuidgen)#g" homeserver.yaml -sed -i.bak "s#{{MACAROON_SECRET_KEY}}#$(uuidgen)#g" homeserver.yaml -rm *.bak - -popd -# generate signing keys for signing_key_path -python -m synapse.app.homeserver --generate-keys --config-dir env/bin/ -c env/bin/homeserver.yaml diff --git a/test/end-to-end-tests/synapse/start.sh b/test/end-to-end-tests/synapse/start.sh deleted file mode 100755 index f17047afd0..0000000000 --- a/test/end-to-end-tests/synapse/start.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -BASE_DIR=$(cd $(dirname $0) && pwd) -cd $BASE_DIR -cd installations/consent/env/bin/ -source activate -echo "Synapse log file at $LOGFILE" -./synctl start 2> $LOGFILE diff --git a/test/end-to-end-tests/synapse/stop.sh b/test/end-to-end-tests/synapse/stop.sh deleted file mode 100755 index 08258e5a8c..0000000000 --- a/test/end-to-end-tests/synapse/stop.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -BASE_DIR=$(cd $(dirname $0) && pwd) -cd $BASE_DIR -cd installations/consent/env/bin/ -source activate -./synctl stop diff --git a/test/end-to-end-tests/tsconfig.json b/test/end-to-end-tests/tsconfig.json deleted file mode 100644 index e4ab37b568..0000000000 --- a/test/end-to-end-tests/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "experimentalDecorators": false, - "emitDecoratorMetadata": false, - "resolveJsonModule": true, - "module": "commonjs", - "moduleResolution": "node", - "target": "es2016", - "noImplicitAny": false, - "sourceMap": false, - "outDir": "./lib", - "declaration": true, - "lib": [ - "es2019", - "dom", - "dom.iterable" - ], - }, - "include": [ - "./src/**/*.ts", - "start.ts" - ] -} diff --git a/test/end-to-end-tests/yarn.lock b/test/end-to-end-tests/yarn.lock deleted file mode 100644 index f730e7003a..0000000000 --- a/test/end-to-end-tests/yarn.lock +++ /dev/null @@ -1,886 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/node@*": - version "17.0.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" - integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== - -"@types/puppeteer@^5.4.4": - version "5.4.5" - resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-5.4.5.tgz#154e3850a77bfd3967f036680de8ddc88eb3a12b" - integrity sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA== - dependencies: - "@types/node" "*" - -"@types/yauzl@^2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" - integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== - dependencies: - "@types/node" "*" - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -ajv@^6.5.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer@^5.2.1, buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -cheerio@^1.0.0-rc.2: - version "1.0.0-rc.2" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" - integrity sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs= - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" - integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== - dependencies: - delayed-stream "~1.0.0" - -commander@^9: - version "9.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.0.0.tgz#86d58f24ee98126568936bd1d3574e0308a99a40" - integrity sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cross-fetch@3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -debug@4, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -devtools-protocol@0.0.960912: - version "0.0.960912" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.960912.tgz#411c1fa355eddb72f06c4a8743f2808766db6245" - integrity sha512-I3hWmV9rWHbdnUdmMKHF2NuYutIM2kXz2mdXW8ha7TbRlGTVs+PF+PsB5QWvpCek4Fy9B+msiispCfwlhG5Sqg== - -dom-serializer@0, dom-serializer@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -entities@^1.1.1, entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob@^7.1.3: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -htmlparser2@^3.9.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-proxy-agent@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash@^4.15.0, lodash@^4.17.11: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -mime-db@~1.38.0: - version "1.38.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" - integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.22" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" - integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== - dependencies: - mime-db "~1.38.0" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== - dependencies: - "@types/node" "*" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -pkg-dir@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -progress@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proxy-from-env@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -psl@^1.1.24, psl@^1.1.28: - version "1.1.31" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" - integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -puppeteer@13.4.1: - version "13.4.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-13.4.1.tgz#495b91d2fae3e9761a31bab1820ad179caac0fd9" - integrity sha512-2arcYPEGvLV9HvOw01Zv1b1IAXrMWHqsFJn0Hn00qe9HtCmaF0b8FlrbdLjCIbkaFc6icH5+GqcG8R5KxlJSRg== - dependencies: - cross-fetch "3.1.5" - debug "4.3.3" - devtools-protocol "0.0.960912" - extract-zip "2.0.1" - https-proxy-agent "5.0.0" - pkg-dir "4.2.0" - progress "2.0.3" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.1.1" - unbzip2-stream "1.4.3" - ws "8.5.0" - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -readable-stream@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" - integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -request-promise-core@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" - integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== - dependencies: - lodash "^4.17.11" - -request-promise-native@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" - integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== - dependencies: - request-promise-core "1.1.2" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.88.0: - version "2.88.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -rimraf@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - -string_decoder@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" - integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== - dependencies: - safe-buffer "~5.1.0" - -tar-fs@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tough-cookie@^2.3.3: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -typescript@^4.5.3: - version "4.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" - integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== - -unbzip2-stream@1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -ws@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0"