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
This commit is contained in:
parent
1e4c336fed
commit
f566c600e2
60 changed files with 105 additions and 4524 deletions
57
.github/workflows/end-to-end-tests.yaml
vendored
57
.github/workflows/end-to-end-tests.yaml
vendored
|
@ -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' }}
|
|
98
cypress/e2e/toasts/analytics-toast.ts
Normal file
98
cypress/e2e/toasts/analytics-toast.ts
Normal file
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
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<JQuery> {
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -52,7 +52,6 @@
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:cypress": "cypress run",
|
"test:cypress": "cypress run",
|
||||||
"test:cypress:open": "cypress open",
|
"test:cypress:open": "cypress open",
|
||||||
"test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080",
|
|
||||||
"coverage": "yarn test --coverage"
|
"coverage": "yarn test --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -635,8 +635,8 @@ async function doSetLoggedIn(
|
||||||
}
|
}
|
||||||
|
|
||||||
dis.fire(Action.OnLoggedIn);
|
dis.fire(Action.OnLoggedIn);
|
||||||
|
|
||||||
await startMatrixClient(/*startSyncing=*/!softLogout);
|
await startMatrixClient(/*startSyncing=*/!softLogout);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1326,11 +1326,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.showScreenAfterLogin();
|
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")) {
|
if (SdkConfig.get("mobile_guide_toast")) {
|
||||||
// The toast contains further logic to detect mobile platforms,
|
// The toast contains further logic to detect mobile platforms,
|
||||||
// check if it has been dismissed before, etc.
|
// check if it has been dismissed before, etc.
|
||||||
|
@ -1646,6 +1641,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
// we implement more settings.
|
// we implement more settings.
|
||||||
cli.setGlobalErrorOnUnknownDevices(false);
|
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}) {
|
public showScreen(screen: string, params?: {[key: string]: any}) {
|
||||||
|
|
7
test/end-to-end-tests/.gitignore
vendored
7
test/end-to-end-tests/.gitignore
vendored
|
@ -1,7 +0,0 @@
|
||||||
node_modules
|
|
||||||
*.png
|
|
||||||
element/env
|
|
||||||
performance-entries.json
|
|
||||||
lib
|
|
||||||
logs
|
|
||||||
homeserver.log
|
|
|
@ -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 <parameters>
|
|
||||||
```
|
|
||||||
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 <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 <factor>` 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
|
|
||||||
|
|
|
@ -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 "
|
|
|
@ -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
|
|
2
test/end-to-end-tests/element/.gitignore
vendored
2
test/end-to-end-tests/element/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
element-web
|
|
||||||
element.pid
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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");
|
|
|
@ -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
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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'));
|
|
||||||
}
|
|
|
@ -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 <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
|
|
24
test/end-to-end-tests/src/@types/global.d.ts
vendored
24
test/end-to-end-tests/src/@types/global.d.ts
vendored
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<EventMapperArg extends Parameters<Parameters<Page['on']>[1]>[0]> {
|
|
||||||
buffer: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
page: Page,
|
|
||||||
eventName: keyof PageEventObject,
|
|
||||||
eventMapper: (arg: EventMapperArg) => Promise<string>,
|
|
||||||
initialValue = "",
|
|
||||||
) {
|
|
||||||
this.buffer = initialValue;
|
|
||||||
page.on(eventName, (arg: EventMapperArg) => {
|
|
||||||
eventMapper(arg).then((r) => this.buffer += r);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<ElementSession>): Promise<void> {
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
scenarios contains the high-level playbook for the test suite
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<puppeteer.ConsoleMessage>;
|
|
||||||
readonly networkLog: LogBuffer<puppeteer.HTTPRequest>;
|
|
||||||
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() ?? '<no response>'} ${req.method()} ${req.url()} \n`;
|
|
||||||
});
|
|
||||||
this.log = new Logger(this.username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async create(username: string, puppeteerOptions: Parameters<typeof puppeteer.launch>[0],
|
|
||||||
elementServer: string, hsUrl: string, throttleCpuFactor = 1): Promise<ElementSession> {
|
|
||||||
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<string> {
|
|
||||||
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<string> {
|
|
||||||
const propHandle = await handle.getProperty(property);
|
|
||||||
return await propHandle.jsonValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public innerText(field: puppeteer.ElementHandle): Promise<string> {
|
|
||||||
return this.getElementProperty(field, 'innerText');
|
|
||||||
}
|
|
||||||
|
|
||||||
public isChecked(field: puppeteer.ElementHandle): Promise<string> {
|
|
||||||
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<void> {
|
|
||||||
// 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<puppeteer.ElementHandle> {
|
|
||||||
return this.page.waitForSelector(selector, { visible: true, timeout, hidden });
|
|
||||||
}
|
|
||||||
|
|
||||||
public queryWithoutWaiting(selector: string): Promise<puppeteer.ElementHandle> {
|
|
||||||
return this.page.$(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async queryAll(selector: string): Promise<puppeteer.ElementHandle[]> {
|
|
||||||
const timeout = DEFAULT_TIMEOUT;
|
|
||||||
await this.query(selector, timeout);
|
|
||||||
return await this.page.$$(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async waitNoSpinner(): Promise<void> {
|
|
||||||
await this.page.waitForSelector(".mx_Spinner", { hidden: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
public goto(url: string): Promise<puppeteer.HTTPResponse> {
|
|
||||||
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<void> {
|
|
||||||
return this.browser.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async poll(callback: () => Promise<boolean>, interval = 100): Promise<boolean> {
|
|
||||||
const timeout = DEFAULT_TIMEOUT;
|
|
||||||
let waited = 0;
|
|
||||||
while (waited < timeout) {
|
|
||||||
await this.delay(interval);
|
|
||||||
waited += interval;
|
|
||||||
if (await callback()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
const roomDirectoryButton = await session.query('.mx_LeftPanel_exploreButton');
|
|
||||||
await roomDirectoryButton.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function findSublist(session: ElementSession, name: string): Promise<puppeteer.ElementHandle> {
|
|
||||||
return await session.query(`.mx_RoomSublist[aria-label="${name}" i]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createRoom(session: ElementSession, roomName: string, encrypted = false): Promise<void> {
|
|
||||||
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<void> {
|
|
||||||
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");
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<boolean> {
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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<MemberName[]> {
|
|
||||||
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) };
|
|
||||||
}));
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
const button = await session.query(".mx_BaseCard_close");
|
|
||||||
await button.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openThreadListPanel(session: ElementSession): Promise<void> {
|
|
||||||
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<void> {
|
|
||||||
await session.query('.mx_RoomHeader .mx_AccessibleButton[aria-label="Threads"] ' +
|
|
||||||
'.mx_RightPanel_headerButton_unreadIndicator');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function clickLatestThreadInThreadListPanel(session: ElementSession): Promise<void> {
|
|
||||||
const threads = await session.queryAll(".mx_ThreadPanel .mx_EventTile");
|
|
||||||
await threads[threads.length - 1].click();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openRoomRightPanel(session: ElementSession): Promise<void> {
|
|
||||||
// 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<void> {
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -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<boolean> {
|
|
||||||
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<void> {
|
|
||||||
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<Tabs> {
|
|
||||||
/// 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<void> {
|
|
||||||
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<string | undefined> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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 };
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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<Frame>(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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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<Message> {
|
|
||||||
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<ElementHandle> {
|
|
||||||
return session.query(".mx_EventTile_last");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getMessageFromEventTile(eventTile: ElementHandle): Promise<Message> {
|
|
||||||
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"),
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
await assertToast(session, expectedTitle);
|
|
||||||
const btn = await session.query('.mx_Toast_buttons .mx_AccessibleButton_kind_danger_outline');
|
|
||||||
await btn.click();
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
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<string[]> {
|
|
||||||
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<string[]> {
|
|
||||||
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<string[]> {
|
|
||||||
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<string[]> {
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -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<void> {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const measureStart = function(session: ElementSession, name: string): Promise<void> {
|
|
||||||
return session.page.evaluate(_name => {
|
|
||||||
window.mxPerformanceMonitor.start(_name);
|
|
||||||
}, name);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const measureStop = function(session: ElementSession, name: string): Promise<void> {
|
|
||||||
return session.page.evaluate(_name => {
|
|
||||||
window.mxPerformanceMonitor.stop(_name);
|
|
||||||
}, name);
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function serializeLog(msg: ConsoleMessage): Promise<string> {
|
|
||||||
// 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 = `<error: ${error}>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = `<error: ${error}>`;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
});
|
|
||||||
s += `${stringyArg} `; // trailing space is intentional
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
|
@ -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 <dir>', 'a directory to dump html and network logs in when the tests fail')
|
|
||||||
.requiredOption('--registration-shared-secret <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);
|
|
||||||
});
|
|
2
test/end-to-end-tests/synapse/.gitignore
vendored
2
test/end-to-end-tests/synapse/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
installations
|
|
||||||
synapse.zip
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,23 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Test Privacy policy</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{% if has_consented %}
|
|
||||||
<p>
|
|
||||||
Thank you, you've already accepted the license.
|
|
||||||
</p>
|
|
||||||
{% else %}
|
|
||||||
<p>
|
|
||||||
Please accept the license!
|
|
||||||
</p>
|
|
||||||
<form method="post" action="consent">
|
|
||||||
<input type="hidden" name="v" value="{{version}}"/>
|
|
||||||
<input type="hidden" name="u" value="{{user}}"/>
|
|
||||||
<input type="hidden" name="h" value="{{userhmac}}"/>
|
|
||||||
<input type="submit" value="Sure thing!"/>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>Test Privacy policy</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Danke schon</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -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 <synapse config file directive>"
|
|
||||||
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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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"
|
|
Loading…
Reference in a new issue