From 08b735be8b741f24b1f009c7fe4d460dd0d5ccac Mon Sep 17 00:00:00 2001 From: bwallacee Date: Sun, 5 Nov 2023 18:35:13 +0000 Subject: [PATCH] chore(trading): add console -test --- .gitignore | 16 + apps/console-test/.env | 2 + apps/console-test/.env.develop | 2 + apps/console-test/.env.main | 2 + .../.github/workflows/playwright.yml | 125 ++ apps/console-test/.vscode/settings.json | 9 + apps/console-test/README.md | 44 + apps/console-test/actions/utils.py | 7 + apps/console-test/actions/vega.py | 65 + apps/console-test/config.py | 9 + apps/console-test/conftest.py | 236 +++ apps/console-test/fixtures/market.py | 156 ++ apps/console-test/poetry.lock | 1310 +++++++++++++++++ apps/console-test/pyproject.toml | 30 + apps/console-test/tests/assets/test_assets.py | 95 ++ .../tests/chart_depth/test_chart_depth.py | 13 + .../tests/deal_ticket/test_basic_submit.py | 142 ++ .../deal_ticket/test_deal_ticket_basics.py | 35 + .../test_fees_margin_estimations.py | 96 ++ .../tests/deal_ticket/test_stop_order.py | 416 ++++++ .../tests/deal_ticket/test_stop_order_oco.py | 498 +++++++ ...test_trading_deal_ticket_submit_account.py | 51 + .../tests/get_started/test_get_started.py | 205 +++ .../iceberg_orders/test_iceberg_orders.py | 182 +++ .../tests/market/test_closed_markets.py | 136 ++ apps/console-test/tests/market/test_market.py | 208 +++ .../tests/market/test_market_info.py | 294 ++++ .../tests/market/test_market_selector.py | 88 ++ .../tests/market/test_markets_all.py | 160 ++ .../tests/market/test_markets_no_markets.py | 36 + .../tests/market/test_markets_proposed.py | 132 ++ ...itoring_auction_price_volatility_market.py | 131 ++ .../market_lifecycle/test_market_lifecycle.py | 174 +++ .../tests/navigation/test_navigation.py | 111 ++ .../tests/order/test_order_details.py | 64 + .../tests/order/test_order_match.py | 191 +++ .../tests/order/test_order_status.py | 414 ++++++ .../tests/orderbook/test_orderbook.py | 272 ++++ apps/console-test/tests/pnl/test_pnl.py | 143 ++ .../tests/portfolio/test_ledger_entries.py | 31 + .../tests/positions/test_collateral.py | 51 + .../tests/positions/test_positions.py | 29 + .../tests/settings/test_settings.py | 61 + .../successor_market/test_succession_line.py | 47 + .../tests/trade_history/test_trade_history.py | 106 ++ .../tests/trade_match/test_trade_match.py | 214 +++ .../tests/trading_chart/test_trading_chart.py | 139 ++ .../transfer/test_transfer_key_to_key.py | 42 + package.json | 5 +- 49 files changed, 7024 insertions(+), 1 deletion(-) create mode 100644 apps/console-test/.env create mode 100644 apps/console-test/.env.develop create mode 100644 apps/console-test/.env.main create mode 100644 apps/console-test/.github/workflows/playwright.yml create mode 100644 apps/console-test/.vscode/settings.json create mode 100644 apps/console-test/README.md create mode 100644 apps/console-test/actions/utils.py create mode 100644 apps/console-test/actions/vega.py create mode 100644 apps/console-test/config.py create mode 100644 apps/console-test/conftest.py create mode 100644 apps/console-test/fixtures/market.py create mode 100644 apps/console-test/poetry.lock create mode 100644 apps/console-test/pyproject.toml create mode 100644 apps/console-test/tests/assets/test_assets.py create mode 100644 apps/console-test/tests/chart_depth/test_chart_depth.py create mode 100644 apps/console-test/tests/deal_ticket/test_basic_submit.py create mode 100644 apps/console-test/tests/deal_ticket/test_deal_ticket_basics.py create mode 100644 apps/console-test/tests/deal_ticket/test_fees_margin_estimations.py create mode 100644 apps/console-test/tests/deal_ticket/test_stop_order.py create mode 100644 apps/console-test/tests/deal_ticket/test_stop_order_oco.py create mode 100644 apps/console-test/tests/deal_ticket/test_trading_deal_ticket_submit_account.py create mode 100644 apps/console-test/tests/get_started/test_get_started.py create mode 100644 apps/console-test/tests/iceberg_orders/test_iceberg_orders.py create mode 100644 apps/console-test/tests/market/test_closed_markets.py create mode 100644 apps/console-test/tests/market/test_market.py create mode 100644 apps/console-test/tests/market/test_market_info.py create mode 100644 apps/console-test/tests/market/test_market_selector.py create mode 100644 apps/console-test/tests/market/test_markets_all.py create mode 100644 apps/console-test/tests/market/test_markets_no_markets.py create mode 100644 apps/console-test/tests/market/test_markets_proposed.py create mode 100644 apps/console-test/tests/market/test_monitoring_auction_price_volatility_market.py create mode 100644 apps/console-test/tests/market_lifecycle/test_market_lifecycle.py create mode 100644 apps/console-test/tests/navigation/test_navigation.py create mode 100644 apps/console-test/tests/order/test_order_details.py create mode 100644 apps/console-test/tests/order/test_order_match.py create mode 100644 apps/console-test/tests/order/test_order_status.py create mode 100644 apps/console-test/tests/orderbook/test_orderbook.py create mode 100644 apps/console-test/tests/pnl/test_pnl.py create mode 100644 apps/console-test/tests/portfolio/test_ledger_entries.py create mode 100644 apps/console-test/tests/positions/test_collateral.py create mode 100644 apps/console-test/tests/positions/test_positions.py create mode 100644 apps/console-test/tests/settings/test_settings.py create mode 100644 apps/console-test/tests/successor_market/test_succession_line.py create mode 100644 apps/console-test/tests/trade_history/test_trade_history.py create mode 100644 apps/console-test/tests/trade_match/test_trade_match.py create mode 100644 apps/console-test/tests/trading_chart/test_trading_chart.py create mode 100644 apps/console-test/tests/transfer/test_transfer_key_to_key.py diff --git a/.gitignore b/.gitignore index 0a587cc44..976bbdf70 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,19 @@ cypress.env.json /apps/**/cypress/reports/ /apps/**/cypress/downloads/ /apps/**/fixtures/wallet/node** + +#console-test +apps/console-test/node_modules/ +apps/console-test//test-results/ +apps/console-test//playwright-report/ +apps/console-test//playwright/.cache/ +apps/console-test/*.pyc +apps/console-test/.pytest_cache +apps/console-test/traces/ +apps/console-test/.DS_Store +apps/console-test//logs +apps/console-test/logs +apps/console-test/__pycache__/ +apps/console-test/*.py[cod] +__pycache__/ +apps/console-test/.vscode/ diff --git a/apps/console-test/.env b/apps/console-test/.env new file mode 100644 index 000000000..adb0f0afe --- /dev/null +++ b/apps/console-test/.env @@ -0,0 +1,2 @@ +CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop +VEGA_VERSION=v0.73.0-rc.2 diff --git a/apps/console-test/.env.develop b/apps/console-test/.env.develop new file mode 100644 index 000000000..adb0f0afe --- /dev/null +++ b/apps/console-test/.env.develop @@ -0,0 +1,2 @@ +CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop +VEGA_VERSION=v0.73.0-rc.2 diff --git a/apps/console-test/.env.main b/apps/console-test/.env.main new file mode 100644 index 000000000..042016e22 --- /dev/null +++ b/apps/console-test/.env.main @@ -0,0 +1,2 @@ +CONSOLE_IMAGE_NAME=vegaprotocol/trading:main +VEGA_VERSION=v0.72.14 diff --git a/apps/console-test/.github/workflows/playwright.yml b/apps/console-test/.github/workflows/playwright.yml new file mode 100644 index 000000000..08f4c82c4 --- /dev/null +++ b/apps/console-test/.github/workflows/playwright.yml @@ -0,0 +1,125 @@ +name: Build and Run Tests + +on: + pull_request: + branches: + - main + - develop + workflow_dispatch: + inputs: + VEGA_VERSION: + description: 'Vega version to test' + required: true + default: v0.73.0-rc.2 + CONSOLE_IMAGE_NAME: + description: 'Console image name' + required: true + default: vegaprotocol/trading:develop +jobs: + test: + timeout-minutes: 15 + runs-on: 8-cores + steps: + - uses: actions/checkout@v3 + + - name: Load inputs + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo "VEGA_VERSION=${{ github.event.inputs.VEGA_VERSION }}" >> $GITHUB_ENV + echo "CONSOLE_IMAGE_NAME=${{ github.event.inputs.CONSOLE_IMAGE_NAME }}" >> $GITHUB_ENV + + - name: Import env from dotenv (push) + if: ${{ github.event_name == 'push' }} + uses: falti/dotenv-action@v1.0.4 + with: + path: .env.${{ github.ref_name }} + export-variables: true + keys-case: upper + log-variables: true + + - name: Import env from dotenv (pull_request) + if: ${{ github.event_name == 'pull_request' }} + uses: falti/dotenv-action@v1.0.4 + with: + path: .env.${{ github.base_ref }} + keys-case: upper + export-variables: true + log-variables: true + #---------------------------------------------- + # ----- install dependencies ----- + #---------------------------------------------- + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + #---------------------------------------------- + # ----- install & configure poetry ----- + #---------------------------------------------- + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + virtualenvs-path: console-test/.venv + + - name: Install dependencies + run: poetry install --no-interaction --no-root + + #---------------------------------------------- + # ----- find vega binaries path ----- + #---------------------------------------------- + - name: Find vega binaries path + id: vega_bin_path + run: echo path=$(poetry run python -c "import vega_sim; print(vega_sim.vega_bin_path)") >> $GITHUB_OUTPUT + #---------------------------------------------- + # ----- vega binaries cache ----- + #---------------------------------------------- + - name: Vega binaries cache + uses: actions/cache@v3 + id: vega_binaries_cache + with: + path: ${{ steps.vega_bin_path.outputs.path }} + key: ${{ runner.os }}-vega-binaries-${{ env.VEGA_VERSION }} + #---------------------------------------------- + # ----- install vega binaries. ----- + #---------------------------------------------- + - name: Install vega binaries + if: steps.vega_binaries_cache.outputs.cache-hit != 'true' + run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }} + #---------------------------------------------- + # ----- pull console. ----- + #---------------------------------------------- + - name: pull console + run: | + poetry run docker pull ${{ env.CONSOLE_IMAGE_NAME }} + #---------------------------------------------- + # ----- install playwright. ----- + #---------------------------------------------- + - name: Install Playwright + run: poetry run playwright install --with-deps chromium + #---------------------------------------------- + # ----- run tests. ----- + #---------------------------------------------- + - name: Run tests + run: poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=10 + #---------------------------------------------- + # ----- upload traces ----- + #---------------------------------------------- + - name: Upload Playwright Trace + uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-trace + path: ./traces/ + retention-days: 15 + + #---------------------------------------------- + # ----- upload logs ----- + #---------------------------------------------- + - name: Upload worker logs + uses: actions/upload-artifact@v3 + if: always() + with: + name: worker-logs + path: ./logs/ + retention-days: 15 diff --git a/apps/console-test/.vscode/settings.json b/apps/console-test/.vscode/settings.json new file mode 100644 index 000000000..7fe55a0a8 --- /dev/null +++ b/apps/console-test/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.formatOnSave": true + }, + "python.testing.pytestArgs": ["tests"], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} diff --git a/apps/console-test/README.md b/apps/console-test/README.md new file mode 100644 index 000000000..d88cf05bc --- /dev/null +++ b/apps/console-test/README.md @@ -0,0 +1,44 @@ +# Console-Test + +`console-test` is a repository containing end-to-end tests for a console application using vega-market-sim. This README will guide you through setting up your environment and running the tests. + +## Prerequisites + +- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) +- [Docker](https://www.docker.com/) + +## Getting Started + +1. **Install Poetry**: Follow the instructions on the [official Poetry website](https://python-poetry.org/docs/#installing-with-the-official-installer). +1. **Install Docker**: Follow the instructions on the [offical Docker website](https://docs.docker.com/desktop/). +1. **Install Dependencies**: + ```bash + npm run install:console-test + ``` + +If you want to run against specific vega binaries you can update the command in package.json + +1. **Pull the docker image of the trading app**: + You can pull the image you want to test, for example: + ```bash + docker pull vegaprotocol/trading:develop + ``` + or + ```bash + docker pull vegaprotocol/trading:main + ``` + All available images can be found [here](https://hub.docker.com/r/vegaprotocol/trading/tags). +1. **Start Docker**: Make sure your Docker daemon is running. + +1. **Run the tests**: To run a specific test (or group of tests) using its name, use the following command: + ```bash + npm run console-test -- "test_name" -s --headed + ``` + +## Running Tests in Parallel + +If you want to run tests in parallel, use the --numprocesses auto option. --dist loadfile makes sure that there are no multiple runners assigned to single test file: + +```bash +npm run console-test:all +``` diff --git a/apps/console-test/actions/utils.py b/apps/console-test/actions/utils.py new file mode 100644 index 000000000..78a32195c --- /dev/null +++ b/apps/console-test/actions/utils.py @@ -0,0 +1,7 @@ +from playwright.sync_api import Page + +def wait_for_toast_confirmation(page: Page, timeout: int = 30000): + page.wait_for_function(""" + document.querySelector('[data-testid="toast-content"]') && + document.querySelector('[data-testid="toast-content"]').innerText.includes('AWAITING CONFIRMATION') + """, timeout=timeout) diff --git a/apps/console-test/actions/vega.py b/apps/console-test/actions/vega.py new file mode 100644 index 000000000..ff8f83a29 --- /dev/null +++ b/apps/console-test/actions/vega.py @@ -0,0 +1,65 @@ +from typing import List, Tuple, Optional +from vega_sim.service import VegaService, PeggedOrder + +def submit_order( + vega: VegaService, + wallet_name: str, + market_id: str, + side: str, + volume: float, + price: float, + peak_size: Optional[float] = None, + minimum_visible_size: Optional[float] = None, +): + return vega.submit_order( + trading_key=wallet_name, + market_id=market_id, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side=side, + volume=volume, + price=price, + peak_size=peak_size, + minimum_visible_size=minimum_visible_size, + ) + + +def submit_multiple_orders( + vega: VegaService, + wallet_name: str, + market_id: str, + side: str, + volume_price_pair: List[Tuple[float, float]], +): + for volume, price in volume_price_pair: + submit_order(vega, wallet_name, market_id, side, volume, price) + + +def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str): + vega.submit_simple_liquidity( + key_name=wallet_name, + market_id=market_id, + commitment_amount=10000, + fee=0.000, + is_amendment=False, + ) + vega.submit_order( + market_id=market_id, + trading_key=wallet_name, + side="SIDE_BUY", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1), + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + vega.submit_order( + market_id=market_id, + trading_key=wallet_name, + side="SIDE_SELL", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1), + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) \ No newline at end of file diff --git a/apps/console-test/config.py b/apps/console-test/config.py new file mode 100644 index 000000000..c307ad528 --- /dev/null +++ b/apps/console-test/config.py @@ -0,0 +1,9 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +console_image_name = os.getenv( + "CONSOLE_IMAGE_NAME", default="vegaprotocol/trading:develop" +) +vega_version = os.getenv("VEGA_VERSION", default="latest") diff --git a/apps/console-test/conftest.py b/apps/console-test/conftest.py new file mode 100644 index 000000000..d3e059bc9 --- /dev/null +++ b/apps/console-test/conftest.py @@ -0,0 +1,236 @@ +import logging +import docker +import pytest +import os +import json +import requests +import time + +from contextlib import contextmanager +from vega_sim.null_service import VegaServiceNull +from playwright.sync_api import Browser, Page +from config import console_image_name, vega_version +from fixtures.market import ( + setup_simple_market, + setup_opening_auction_market, + setup_continuous_market, +) + +import sys + +# Workaround for current xdist issue with displaying live logs from multiple workers +# https://github.com/pytest-dev/pytest-xdist/issues/402 +sys.stdout = sys.stderr + +docker_client = docker.from_env() +logger = logging.getLogger() + + +@pytest.hookimpl(tryfirst=True) +def pytest_runtest_makereport(item, call): + outcome = "passed" if call.excinfo is None else "failed" + item.config.cache.set(item.nodeid, outcome) + + +def pytest_configure(config): + worker_id = os.environ.get("PYTEST_XDIST_WORKER") + if worker_id is not None: + log_dir = os.path.join(os.getcwd(), "logs") + log_name = f"tests_{worker_id}.log" + if not os.path.exists(log_dir): + os.makedirs(log_dir) + logging.basicConfig( + format=config.getini("log_file_format"), + datefmt=config.getini("log_file_date_format"), + filename=os.path.join(log_dir, log_name), + level=config.getini("log_file_level"), + ) + + +# Start VegaServiceNull and start up docker container for website +@contextmanager +def init_vega(request=None): + default_seconds = 1 + seconds_per_block = default_seconds + if request and hasattr(request, "param"): + seconds_per_block = request.param + + logger.info( + "Starting VegaServiceNull", + extra={"worker_id": os.environ.get("PYTEST_XDIST_WORKER")}, + ) + logger.info(f"Using console image: {console_image_name}") + logger.info(f"Using vega version: {vega_version}") + with VegaServiceNull( + run_with_console=False, + launch_graphql=False, + retain_log_files=True, + use_full_vega_wallet=True, + store_transactions=True, + transactions_per_block=1000, + seconds_per_block=seconds_per_block, + ) as vega: + try: + container = docker_client.containers.run( + console_image_name, detach=True, ports={"80/tcp": vega.console_port} + ) + # docker setup + logger.info( + f"Container {container.id} started", + extra={"worker_id": os.environ.get("PYTEST_XDIST_WORKER")}, + ) + yield vega + except docker.errors.APIError as e: + logger.info(f"Container creation failed.") + logger.info(e) + raise e + finally: + logger.info(f"Stopping container {container.id}") + container.stop() + # Remove the container + logger.info(f"Removing container {container.id}") + container.remove() + + +@contextmanager +def init_page(vega: VegaServiceNull, browser: Browser, request: pytest.FixtureRequest): + with browser.new_context( + viewport={"width": 1920, "height": 1080}, + base_url=f"http://localhost:{vega.console_port}", + ) as context, context.new_page() as page: + context.tracing.start(screenshots=True, snapshots=True, sources=True) + try: + # Wait for the console to be up and running before any tests are run + attempts = 0 + while attempts < 100: + try: + code = requests.get( + f"http://localhost:{vega.console_port}/" + ).status_code + if code == 200: + break + except requests.exceptions.ConnectionError as e: + attempts += 1 + if attempts < 100: + time.sleep(0.1) + continue + else: + raise e + + # Set window._env_ so built docker image data uses datanode from vega market sim + env = json.dumps( + { + "VEGA_URL": f"http://localhost:{vega.data_node_rest_port}/graphql", + "VEGA_WALLET_URL": f"http://localhost:{vega.wallet_port}", + } + ) + window_env = f"window._env_ = Object.assign({{}}, window._env_, {env})" + page.add_init_script(script=window_env) + yield page + finally: + if not os.path.exists("traces"): + os.makedirs("traces") + + # Check whether this test failed or passed + outcome = request.config.cache.get(request.node.nodeid, None) + if outcome != "passed": + try: + trace_path = os.path.join("traces", request.node.name + "trace.zip") + context.tracing.stop(path=trace_path) + except Exception as e: + logger.error(f"Failed to save trace: {e}") + + +# default vega & page fixtures with function scope (refreshed at each test) that can be used in tests +# separate fixtures may be defined in tests if we prefer different scope +@pytest.fixture +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture +def page(vega, browser, request): + with init_page(vega, browser, request) as page: + yield page + + +# Set auth token so eager connection for MarketSim wallet is successful +def auth_setup(vega: VegaServiceNull, page: Page): + DEFAULT_WALLET_NAME = "MarketSim" # This is the default wallet name within VegaServiceNull and CANNOT be changed + + # Calling get_keypairs will internally call _load_tokens for the given wallet + keypairs = vega.wallet.get_keypairs(DEFAULT_WALLET_NAME) + wallet_api_token = vega.wallet.login_tokens[DEFAULT_WALLET_NAME] + + # Set token to localStorage so eager connect hook picks it up and immediately connects + wallet_config = json.dumps( + { + "token": f"VWT {wallet_api_token}", + "connector": "jsonRpc", + "url": f"http://localhost:{vega.wallet_port}", + } + ) + + storage_javascript = [ + # Store wallet config so eager connection is initiated + f"localStorage.setItem('vega_wallet_config', '{wallet_config}');", + # Ensure wallet ris dialog doesnt show, otherwise eager connect wont work + "localStorage.setItem('vega_wallet_risk_accepted', 'true');", + # Ensure initial risk dialog doesnt show + "localStorage.setItem('vega_risk_accepted', 'true');", + ] + script = "".join(storage_javascript) + page.add_init_script(script) + + return { + "wallet": DEFAULT_WALLET_NAME, + "wallet_api_token": wallet_api_token, + "public_key": keypairs["Key 1"], + } + + +@pytest.fixture(scope="function") +def auth(vega: VegaServiceNull, page: Page): + return auth_setup(vega, page) + + +# Set 'risk accepted' flag, so that the risk dialog doesn't show up +def risk_accepted_setup(page: Page): + onboarding_config = json.dumps({"state": {"dismissed": True}, "version": 0}) + storage_javascript = [ + "localStorage.setItem('vega_risk_accepted', 'true');", + f"localStorage.setItem('vega_onboarding', '{onboarding_config}');", + "localStorage.setItem('vega_telemetry_approval', 'false');", + "localStorage.setItem('vega_telemetry_viewed', 'true');", + ] + script = "".join(storage_javascript) + page.add_init_script(script) + + +@pytest.fixture(scope="function") +def risk_accepted(page: Page): + risk_accepted_setup(page) + + +@pytest.fixture(scope="function") +def simple_market(vega, request): + kwargs = {} + if hasattr(request, "param"): + kwargs.update(request.param) + return setup_simple_market(vega, **kwargs) + + +@pytest.fixture(scope="function") +def opening_auction_market(vega): + return setup_opening_auction_market(vega) + + +@pytest.fixture(scope="function") +def continuous_market(vega): + return setup_continuous_market(vega) + + +@pytest.fixture(scope="function") +def proposed_market(vega): + return setup_simple_market(vega, approve_proposal=False) diff --git a/apps/console-test/fixtures/market.py b/apps/console-test/fixtures/market.py new file mode 100644 index 000000000..61f756e87 --- /dev/null +++ b/apps/console-test/fixtures/market.py @@ -0,0 +1,156 @@ +from collections import namedtuple +from vega_sim.service import VegaService, PeggedOrder +from actions.vega import submit_multiple_orders, submit_order, submit_liquidity + + +import logging + +logger = logging.getLogger() + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + +mint_amount: float = 10e5 +market_name = "BTC:DAI_2023" + + +def setup_simple_market( + vega: VegaService, + approve_proposal=True, + custom_market_name=market_name, + custom_asset_name="tDAI", + custom_asset_symbol="tDAI", +): + for wallet in wallets: + vega.create_key(wallet.name) + + vega.mint( + MM_WALLET.name, + asset="VOTE", + amount=mint_amount, + ) + + vega.update_network_parameter( + MM_WALLET.name, parameter="market.fee.factors.makerFee", new_value="0.1" + ) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.create_asset( + MM_WALLET.name, + name=custom_asset_name, + symbol=custom_asset_symbol, + decimals=5, + max_faucet_amount=1e10, + ) + vega.wait_fn(1) + vega.wait_for_total_catchup() + tdai_id = vega.find_asset_id(symbol=custom_asset_symbol) + logger.info(f"Created asset: {custom_asset_symbol}") + + vega.mint( + "Key 1", + asset=tdai_id, + amount=mint_amount, + ) + + vega.mint( + MM_WALLET.name, + asset=tdai_id, + amount=mint_amount, + ) + + vega.mint( + MM_WALLET2.name, + asset=tdai_id, + amount=mint_amount, + ) + vega.wait_fn(1) + vega.wait_for_total_catchup() + + market_id = vega.create_simple_market( + custom_market_name, + proposal_key=MM_WALLET.name, + settlement_asset_id=tdai_id, + termination_key=TERMINATE_WALLET.name, + market_decimals=5, + approve_proposal=approve_proposal, + forward_time_to_enactment=approve_proposal, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + return market_id + + +def setup_simple_successor_market( + vega: VegaService, parent_market_id, tdai_id, market_name, approve_proposal=True +): + market_id = vega.create_simple_market( + market_name, + proposal_key=MM_WALLET.name, + settlement_asset_id=tdai_id, + termination_key=MM_WALLET2.name, + market_decimals=5, + approve_proposal=approve_proposal, + forward_time_to_enactment=approve_proposal, + parent_market_id=parent_market_id, + parent_market_insurance_pool_fraction=0.5, + ) + submit_liquidity(vega, MM_WALLET.name, market_id) + submit_multiple_orders( + vega, MM_WALLET.name, market_id, "SIDE_SELL", [[1, 110], [1, 105]] + ) + submit_multiple_orders( + vega, MM_WALLET2.name, market_id, "SIDE_BUY", [[1, 90], [1, 95]] + ) + + submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + return market_id + + +def setup_opening_auction_market(vega: VegaService, market_id: str = None, **kwargs): + if market_id is None or market_id not in vega.all_markets(): + market_id = setup_simple_market(vega, **kwargs) + + submit_liquidity(vega, MM_WALLET.name, market_id) + submit_multiple_orders( + vega, MM_WALLET.name, market_id, "SIDE_SELL", [[1, 110], [1, 105]] + ) + submit_multiple_orders( + vega, MM_WALLET2.name, market_id, "SIDE_BUY", [[1, 90], [1, 95]] + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + return market_id + + +def setup_continuous_market(vega: VegaService, market_id: str = None, **kwargs): + if market_id is None or market_id not in vega.all_markets(): + market_id = setup_opening_auction_market(vega, **kwargs) + + submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + return market_id diff --git a/apps/console-test/poetry.lock b/apps/console-test/poetry.lock new file mode 100644 index 000000000..c6f6cd253 --- /dev/null +++ b/apps/console-test/poetry.lock @@ -0,0 +1,1310 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.2.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + +[[package]] +name = "docker" +version = "6.1.3" +description = "A Python library for the Docker Engine API." +optional = false +python-versions = ">=3.7" +files = [ + {file = "docker-6.1.3-py3-none-any.whl", hash = "sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9"}, + {file = "docker-6.1.3.tar.gz", hash = "sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20"}, +] + +[package.dependencies] +packaging = ">=14.0" +pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} +requests = ">=2.26.0" +urllib3 = ">=1.26.0" +websocket-client = ">=0.32.0" + +[package.extras] +ssh = ["paramiko (>=2.4.3)"] + +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.0.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.7" +files = [ + {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, + {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "googleapis-common-protos" +version = "1.60.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.60.0.tar.gz", hash = "sha256:e73ebb404098db405ba95d1e1ae0aa91c3e15a71da031a2eeb6b2e23e7bc3708"}, + {file = "googleapis_common_protos-1.60.0-py2.py3-none-any.whl", hash = "sha256:69f9bbcc6acde92cab2db95ce30a70bd2b81d20b12eff3f1aabaffcbe8a93918"}, +] + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "greenlet" +version = "2.0.2" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +files = [ + {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, + {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, + {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, + {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, + {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, + {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d967650d3f56af314b72df7089d96cda1083a7fc2da05b375d2bc48c82ab3f3c"}, + {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, + {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, + {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, + {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, + {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, + {file = "greenlet-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d4606a527e30548153be1a9f155f4e283d109ffba663a15856089fb55f933e47"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, + {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, + {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, + {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, + {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, + {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, + {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, + {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, + {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, + {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, + {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, + {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, + {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, + {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, + {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, + {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, + {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, + {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, + {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, + {file = "greenlet-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1087300cf9700bbf455b1b97e24db18f2f77b55302a68272c56209d5587c12d1"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, + {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, + {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, + {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, + {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, + {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8512a0c38cfd4e66a858ddd1b17705587900dd760c6003998e9472b77b56d417"}, + {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, + {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, + {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, + {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, + {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, + {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, +] + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["objgraph", "psutil"] + +[[package]] +name = "grpcio" +version = "1.58.0" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.58.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3e6bebf1dfdbeb22afd95650e4f019219fef3ab86d3fca8ebade52e4bc39389a"}, + {file = "grpcio-1.58.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:cde11577d5b6fd73a00e6bfa3cf5f428f3f33c2d2878982369b5372bbc4acc60"}, + {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a2d67ff99e70e86b2be46c1017ae40b4840d09467d5455b2708de6d4c127e143"}, + {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ed979b273a81de36fc9c6716d9fb09dd3443efa18dcc8652501df11da9583e9"}, + {file = "grpcio-1.58.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:458899d2ebd55d5ca2350fd3826dfd8fcb11fe0f79828ae75e2b1e6051d50a29"}, + {file = "grpcio-1.58.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc7ffef430b80345729ff0a6825e9d96ac87efe39216e87ac58c6c4ef400de93"}, + {file = "grpcio-1.58.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5b23d75e5173faa3d1296a7bedffb25afd2fddb607ef292dfc651490c7b53c3d"}, + {file = "grpcio-1.58.0-cp310-cp310-win32.whl", hash = "sha256:fad9295fe02455d4f158ad72c90ef8b4bcaadfdb5efb5795f7ab0786ad67dd58"}, + {file = "grpcio-1.58.0-cp310-cp310-win_amd64.whl", hash = "sha256:bc325fed4d074367bebd465a20763586e5e1ed5b943e9d8bc7c162b1f44fd602"}, + {file = "grpcio-1.58.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:652978551af02373a5a313e07bfef368f406b5929cf2d50fa7e4027f913dbdb4"}, + {file = "grpcio-1.58.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:9f13a171281ebb4d7b1ba9f06574bce2455dcd3f2f6d1fbe0fd0d84615c74045"}, + {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8774219e21b05f750eef8adc416e9431cf31b98f6ce9def288e4cea1548cbd22"}, + {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09206106848462763f7f273ca93d2d2d4d26cab475089e0de830bb76be04e9e8"}, + {file = "grpcio-1.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62831d5e251dd7561d9d9e83a0b8655084b2a1f8ea91e4bd6b3cedfefd32c9d2"}, + {file = "grpcio-1.58.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:212f38c6a156862098f6bdc9a79bf850760a751d259d8f8f249fc6d645105855"}, + {file = "grpcio-1.58.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4b12754af201bb993e6e2efd7812085ddaaef21d0a6f0ff128b97de1ef55aa4a"}, + {file = "grpcio-1.58.0-cp311-cp311-win32.whl", hash = "sha256:3886b4d56bd4afeac518dbc05933926198aa967a7d1d237a318e6fbc47141577"}, + {file = "grpcio-1.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:002f228d197fea12797a14e152447044e14fb4fdb2eb5d6cfa496f29ddbf79ef"}, + {file = "grpcio-1.58.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:b5e8db0aff0a4819946215f156bd722b6f6c8320eb8419567ffc74850c9fd205"}, + {file = "grpcio-1.58.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:201e550b7e2ede113b63e718e7ece93cef5b0fbf3c45e8fe4541a5a4305acd15"}, + {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d79b660681eb9bc66cc7cbf78d1b1b9e335ee56f6ea1755d34a31108b80bd3c8"}, + {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ef8d4a76d2c7d8065aba829f8d0bc0055495c998dce1964ca5b302d02514fb3"}, + {file = "grpcio-1.58.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cba491c638c76d3dc6c191d9c75041ca5b8f5c6de4b8327ecdcab527f130bb4"}, + {file = "grpcio-1.58.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6801ff6652ecd2aae08ef994a3e49ff53de29e69e9cd0fd604a79ae4e545a95c"}, + {file = "grpcio-1.58.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:24edec346e69e672daf12b2c88e95c6f737f3792d08866101d8c5f34370c54fd"}, + {file = "grpcio-1.58.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7e473a7abad9af48e3ab5f3b5d237d18208024d28ead65a459bd720401bd2f8f"}, + {file = "grpcio-1.58.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:4891bbb4bba58acd1d620759b3be11245bfe715eb67a4864c8937b855b7ed7fa"}, + {file = "grpcio-1.58.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:e9f995a8a421405958ff30599b4d0eec244f28edc760de82f0412c71c61763d2"}, + {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:2f85f87e2f087d9f632c085b37440a3169fda9cdde80cb84057c2fc292f8cbdf"}, + {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb6b92036ff312d5b4182fa72e8735d17aceca74d0d908a7f08e375456f03e07"}, + {file = "grpcio-1.58.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d81c2b2b24c32139dd2536972f1060678c6b9fbd106842a9fcdecf07b233eccd"}, + {file = "grpcio-1.58.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fbcecb6aedd5c1891db1d70efbfbdc126c986645b5dd616a045c07d6bd2dfa86"}, + {file = "grpcio-1.58.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92ae871a902cf19833328bd6498ec007b265aabf2fda845ab5bd10abcaf4c8c6"}, + {file = "grpcio-1.58.0-cp38-cp38-win32.whl", hash = "sha256:dc72e04620d49d3007771c0e0348deb23ca341c0245d610605dddb4ac65a37cb"}, + {file = "grpcio-1.58.0-cp38-cp38-win_amd64.whl", hash = "sha256:1c1c5238c6072470c7f1614bf7c774ffde6b346a100521de9ce791d1e4453afe"}, + {file = "grpcio-1.58.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fe643af248442221db027da43ed43e53b73e11f40c9043738de9a2b4b6ca7697"}, + {file = "grpcio-1.58.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:128eb1f8e70676d05b1b0c8e6600320fc222b3f8c985a92224248b1367122188"}, + {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:039003a5e0ae7d41c86c768ef8b3ee2c558aa0a23cf04bf3c23567f37befa092"}, + {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f061722cad3f9aabb3fbb27f3484ec9d4667b7328d1a7800c3c691a98f16bb0"}, + {file = "grpcio-1.58.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0af11938acf8cd4cf815c46156bcde36fa5850518120920d52620cc3ec1830"}, + {file = "grpcio-1.58.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d4cef77ad2fed42b1ba9143465856d7e737279854e444925d5ba45fc1f3ba727"}, + {file = "grpcio-1.58.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24765a627eb4d9288ace32d5104161c3654128fe27f2808ecd6e9b0cfa7fc8b9"}, + {file = "grpcio-1.58.0-cp39-cp39-win32.whl", hash = "sha256:f0241f7eb0d2303a545136c59bc565a35c4fc3b924ccbd69cb482f4828d6f31c"}, + {file = "grpcio-1.58.0-cp39-cp39-win_amd64.whl", hash = "sha256:dcfba7befe3a55dab6fe1eb7fc9359dc0c7f7272b30a70ae0af5d5b063842f28"}, + {file = "grpcio-1.58.0.tar.gz", hash = "sha256:532410c51ccd851b706d1fbc00a87be0f5312bd6f8e5dbf89d4e99c7f79d7499"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.58.0)"] + +[[package]] +name = "grpcio-tools" +version = "1.58.0" +description = "Protobuf code generator for gRPC" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-tools-1.58.0.tar.gz", hash = "sha256:6f4d80ceb591e31ca4dceec747dbe56132e1392a0a9bb1c8fe001d1b5cac898a"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:60c874908f3b40f32f1bb0221f7b3ab65ecb53a4d0a9f0a394f031f1b292c177"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:1852e798f31e5437ca7b37abc910e028b34732fb19364862cedb87b1dab66fad"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:149fb48f53cb691a6328f68bed8e4036c730f7106b7f98e92c2c0403f0b9e93c"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba3d383e5ca93826038b70f326fce8e8d12dd9b2f64d363a3d612f7475f12dd2"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6997511e9d2979f7a2389479682dbb06823f21a904e8fb0a5c6baaf1b4b4a863"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8de0b701da479643f71fad71fe66885cddd89441ae16e2c724939b47742dc72e"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43cc23908b63fcaefe690b10f68a2d8652c994b5b36ab77d2271d9608c895320"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-win32.whl", hash = "sha256:2c2221123d010dc6231799e63a37f2f4786bf614ef65b23009c387cd20d8b193"}, + {file = "grpcio_tools-1.58.0-cp310-cp310-win_amd64.whl", hash = "sha256:df2788736bdf58abe7b0e4d6b1ff806f7686c98c5ad900da312252e3322d91c4"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:b6ea5578712cdb29b0ff60bfc6405bf0e8d681b9c71d106dd1cda54fe7fe4e55"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:c29880f491581c83181c0a84a4d11402af2b13166a5266f64e246adf1da7aa66"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:32d51e933c3565414dd0835f930bb28a1cdeba435d9d2c87fa3cf8b1d284db3c"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad9d77f25514584b1ddc981d70c9e50dfcfc388aa5ba943eee67520c5267ed9"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4882382631e6352819059278a5c878ce0b067008dd490911d16d5616e8a36d85"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d84091a189d848d94645b7c48b61734c12ec03b0d46e5fc0049343a26989ac5c"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:85ac28a9621e9b92a3fc416288c4ce45542db0b4c31b3e23031dd8e0a0ec5590"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-win32.whl", hash = "sha256:7371d8ea80234b29affec145e25569523f549520ed7e53b2aa92bed412cdecfd"}, + {file = "grpcio_tools-1.58.0-cp311-cp311-win_amd64.whl", hash = "sha256:6997df6e7c5cf4d3ddc764240c1ff6a04b45d70ec28913b38fbc6396ef743e12"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ac65b8d6e3acaf88b815edf9af88ff844b6600ff3d2591c05ba4f655b45d5fb4"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:88e8191d0dd789bebf42533808728f5ce75d2c51e2a72bdf20abe5b5e3fbec42"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:a3dbece2a121761499a659b799979d4b738586d1065439053de553773eee11ca"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1086fe240c4c879b9721952b47d46996deb283c2d9355a8dc24a804811aacf70"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ae3dca059d5b358dd03fb63277428fa7d771605d4074a019138dd38d70719a"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3f8904ac7fc3da2e874f00b3a986e8b7e004f499344a8e7eb213c26dfb025041"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:aadbd8393ae332e49731adb31e741f2e689989150569b7acc939f5ea43124e2d"}, + {file = "grpcio_tools-1.58.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1cb6e24194786687d4f23c64de1f0ce553af51de22746911bc37340f85f9783e"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:6ec43909095c630df3e479e77469bdad367067431f4af602f6ccb978a3b78afd"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:4be49ed320b0ebcbc21d19ef555fbf229c1c452105522b728e1171ee2052078e"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:28eefebddec3d3adf19baca78f8b82a2287d358e1b1575ae018cdca8eacc6269"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ef8c696e9d78676cc3f583a92bbbf2c84e94e350f7ad22f150a52559f4599d1"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aeb5949e46558d21c51fd3ec3eeecc59c94dbca76c67c0a80d3da6b7437930c"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f7144aad9396d35fb1b80429600a970b559c2ad4d07020eeb180fe83cea2bee"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ee26e9253a721fff355737649678535f76cf5d642aa3ac0cd937832559b90af"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-win32.whl", hash = "sha256:343f572312039059a8797d6e29a7fc62196e73131ab01755660a9d48202267c1"}, + {file = "grpcio_tools-1.58.0-cp38-cp38-win_amd64.whl", hash = "sha256:cd7acfbb43b7338a78cf4a67528d05530d574d92b7c829d185b78dfc451d158f"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:46628247fbce86d18232eead24bd22ed0826c79f3fe2fc2fbdbde45971361049"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:51587842a54e025a3d0d37afcf4ef2b7ac1def9a5d17448665cb424b53d6c287"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a062ae3072a2a39a3c057f4d68b57b021f1dd2956cd09aab39709f6af494e1de"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eec3c93a08df11c80ef1c29a616bcbb0d83dbc6ea41b48306fcacc720416dfa7"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b63f823ac991ff77104da614d2a2485a59d37d57830eb2e387a6e2a3edc7fa2b"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:579c11a9f198847ed48dbc4f211c67fe96a73320b87c81f01b044b72e24a7d77"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2fc1dd8049d417a5034d944c9df05cee76f855b3e431627ab4292e7c01c47"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-win32.whl", hash = "sha256:453023120114c35d3d9d6717ea0820e5d5c140f51f9d0b621de4397ff854471b"}, + {file = "grpcio_tools-1.58.0-cp39-cp39-win_amd64.whl", hash = "sha256:b6c896f1df99c35cf062d4803c15663ff00a33ff09add28baa6e475cf6b5e258"}, +] + +[package.dependencies] +grpcio = ">=1.58.0" +protobuf = ">=4.21.6,<5.0dev" +setuptools = "*" + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "inflection" +version = "0.5.1" +description = "A port of Ruby on Rails inflector to Python" +optional = false +python-versions = ">=3.5" +files = [ + {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, + {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "numpy" +version = "1.25.2" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, + {file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, + {file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, + {file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, + {file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, + {file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, + {file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, + {file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, + {file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, + {file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, + {file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, + {file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, + {file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, + {file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, + {file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, + {file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, + {file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, + {file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pandas" +version = "2.1.0" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40dd20439ff94f1b2ed55b393ecee9cb6f3b08104c2c40b0cb7186a2f0046242"}, + {file = "pandas-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4f38e4fedeba580285eaac7ede4f686c6701a9e618d8a857b138a126d067f2f"}, + {file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6a0fe052cf27ceb29be9429428b4918f3740e37ff185658f40d8702f0b3e09"}, + {file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d81e1813191070440d4c7a413cb673052b3b4a984ffd86b8dd468c45742d3cc"}, + {file = "pandas-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb20252720b1cc1b7d0b2879ffc7e0542dd568f24d7c4b2347cb035206936421"}, + {file = "pandas-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:38f74ef7ebc0ffb43b3d633e23d74882bce7e27bfa09607f3c5d3e03ffd9a4a5"}, + {file = "pandas-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cda72cc8c4761c8f1d97b169661f23a86b16fdb240bdc341173aee17e4d6cedd"}, + {file = "pandas-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d97daeac0db8c993420b10da4f5f5b39b01fc9ca689a17844e07c0a35ac96b4b"}, + {file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c58b1113892e0c8078f006a167cc210a92bdae23322bb4614f2f0b7a4b510f"}, + {file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:629124923bcf798965b054a540f9ccdfd60f71361255c81fa1ecd94a904b9dd3"}, + {file = "pandas-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:70cf866af3ab346a10debba8ea78077cf3a8cd14bd5e4bed3d41555a3280041c"}, + {file = "pandas-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d53c8c1001f6a192ff1de1efe03b31a423d0eee2e9e855e69d004308e046e694"}, + {file = "pandas-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f100b3876b8c6d1a2c66207288ead435dc71041ee4aea789e55ef0e06408cb"}, + {file = "pandas-2.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28f330845ad21c11db51e02d8d69acc9035edfd1116926ff7245c7215db57957"}, + {file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a6ccf0963db88f9b12df6720e55f337447aea217f426a22d71f4213a3099a6"}, + {file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99e678180bc59b0c9443314297bddce4ad35727a1a2656dbe585fd78710b3b9"}, + {file = "pandas-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b31da36d376d50a1a492efb18097b9101bdbd8b3fbb3f49006e02d4495d4c644"}, + {file = "pandas-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0164b85937707ec7f70b34a6c3a578dbf0f50787f910f21ca3b26a7fd3363437"}, + {file = "pandas-2.1.0.tar.gz", hash = "sha256:62c24c7fc59e42b775ce0679cfa7b14a5f9bfb7643cfbe708c960699e05fb918"}, +] + +[package.dependencies] +numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""} +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"] +aws = ["s3fs (>=2022.05.0)"] +clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"] +compression = ["zstandard (>=0.17.0)"] +computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2022.05.0)"] +gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"] +hdf5 = ["tables (>=3.7.0)"] +html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"] +mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"] +spss = ["pyreadstat (>=1.1.5)"] +sql-other = ["SQLAlchemy (>=1.4.36)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.8.0)"] + +[[package]] +name = "playwright" +version = "1.37.0" +description = "A high-level API to automate web browsers" +optional = false +python-versions = ">=3.8" +files = [ + {file = "playwright-1.37.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b476f63251876f1625f490af8d58ec0db90b555c623b7f54105f91d33878c06d"}, + {file = "playwright-1.37.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:68d56efe5ce916bab349177e90726837a6f0cae77ebd6a5200f5333b787b25fb"}, + {file = "playwright-1.37.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:428fdf9bfff586b73f96df53692d50d422afb93ca4650624f61e8181f548fed2"}, + {file = "playwright-1.37.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:41f0280472af94c426e941f6a969ff6a7ea156dc15fd01d09ac4b8f092e2346e"}, + {file = "playwright-1.37.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b574889ef97b7f44a633aa10d72b8966a850a4354d915fd0bc7e8658e825dd63"}, + {file = "playwright-1.37.0-py3-none-win32.whl", hash = "sha256:8b5d96aae54289129ab19d3d0e2e431171ae3e5d88d49a10900dcbe569a27d43"}, + {file = "playwright-1.37.0-py3-none-win_amd64.whl", hash = "sha256:678b9926be2df06321d11a525d4bf08d9f4a5b151354a3b82fe2ac14476322d5"}, +] + +[package.dependencies] +greenlet = "2.0.2" +pyee = "9.0.4" + +[[package]] +name = "plotly" +version = "5.16.1" +description = "An open-source, interactive data visualization library for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "plotly-5.16.1-py2.py3-none-any.whl", hash = "sha256:19cc34f339acd4e624177806c14df22f388f23fb70658b03aad959a0e650a0dc"}, + {file = "plotly-5.16.1.tar.gz", hash = "sha256:295ac25edeb18c893abb71dcadcea075b78fd6fdf07cee4217a4e1009667925b"}, +] + +[package.dependencies] +packaging = "*" +tenacity = ">=6.2.0" + +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "protobuf" +version = "4.24.3" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-4.24.3-cp310-abi3-win32.whl", hash = "sha256:20651f11b6adc70c0f29efbe8f4a94a74caf61b6200472a9aea6e19898f9fcf4"}, + {file = "protobuf-4.24.3-cp310-abi3-win_amd64.whl", hash = "sha256:3d42e9e4796a811478c783ef63dc85b5a104b44aaaca85d4864d5b886e4b05e3"}, + {file = "protobuf-4.24.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:6e514e8af0045be2b56e56ae1bb14f43ce7ffa0f68b1c793670ccbe2c4fc7d2b"}, + {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ba53c2f04798a326774f0e53b9c759eaef4f6a568ea7072ec6629851c8435959"}, + {file = "protobuf-4.24.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f6ccbcf027761a2978c1406070c3788f6de4a4b2cc20800cc03d52df716ad675"}, + {file = "protobuf-4.24.3-cp37-cp37m-win32.whl", hash = "sha256:1b182c7181a2891e8f7f3a1b5242e4ec54d1f42582485a896e4de81aa17540c2"}, + {file = "protobuf-4.24.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b0271a701e6782880d65a308ba42bc43874dabd1a0a0f41f72d2dac3b57f8e76"}, + {file = "protobuf-4.24.3-cp38-cp38-win32.whl", hash = "sha256:e29d79c913f17a60cf17c626f1041e5288e9885c8579832580209de8b75f2a52"}, + {file = "protobuf-4.24.3-cp38-cp38-win_amd64.whl", hash = "sha256:067f750169bc644da2e1ef18c785e85071b7c296f14ac53e0900e605da588719"}, + {file = "protobuf-4.24.3-cp39-cp39-win32.whl", hash = "sha256:2da777d34b4f4f7613cdf85c70eb9a90b1fbef9d36ae4a0ccfe014b0b07906f1"}, + {file = "protobuf-4.24.3-cp39-cp39-win_amd64.whl", hash = "sha256:f631bb982c5478e0c1c70eab383af74a84be66945ebf5dd6b06fc90079668d0b"}, + {file = "protobuf-4.24.3-py3-none-any.whl", hash = "sha256:f6f8dc65625dadaad0c8545319c2e2f0424fede988368893ca3844261342c11a"}, + {file = "protobuf-4.24.3.tar.gz", hash = "sha256:12e9ad2ec079b833176d2921be2cb24281fa591f0b119b208b788adc48c2561d"}, +] + +[[package]] +name = "protoc-gen-openapiv2" +version = "0.0.1" +description = "Provides the missing pieces for gRPC Gateway." +optional = false +python-versions = ">=3.6" +files = [ + {file = "protoc-gen-openapiv2-0.0.1.tar.gz", hash = "sha256:6f79188d842c13177c9c0558845442c340b43011bf67dfef1dfc3bc067506409"}, + {file = "protoc_gen_openapiv2-0.0.1-py3-none-any.whl", hash = "sha256:18090c8be3877c438e7da0f7eb7cace45a9a210306bca4707708dbad367857be"}, +] + +[package.dependencies] +googleapis-common-protos = "*" +protobuf = ">=4.21.0" + +[[package]] +name = "psutil" +version = "5.9.5" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"}, + {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"}, + {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"}, + {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"}, + {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"}, + {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"}, + {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"}, + {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"}, + {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"}, + {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"}, + {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pyee" +version = "9.0.4" +description = "A port of node.js's EventEmitter to python." +optional = false +python-versions = "*" +files = [ + {file = "pyee-9.0.4-py2.py3-none-any.whl", hash = "sha256:9f066570130c554e9cc12de5a9d86f57c7ee47fece163bbdaa3e9c933cfbdfa5"}, + {file = "pyee-9.0.4.tar.gz", hash = "sha256:2770c4928abc721f46b705e6a72b0c59480c4a69c9a83ca0b00bb994f1ea4b32"}, +] + +[package.dependencies] +typing-extensions = "*" + +[[package]] +name = "pynacl" +version = "1.5.0" +description = "Python binding to the Networking and Cryptography (NaCl) library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, +] + +[package.dependencies] +cffi = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] + +[[package]] +name = "pytest" +version = "7.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-base-url" +version = "2.0.0" +description = "pytest plugin for URL based testing" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pytest-base-url-2.0.0.tar.gz", hash = "sha256:e1e88a4fd221941572ccdcf3bf6c051392d2f8b6cef3e0bc7da95abec4b5346e"}, + {file = "pytest_base_url-2.0.0-py3-none-any.whl", hash = "sha256:ed36fd632c32af9f1c08f2c2835dcf42ca8fcd097d6ed44a09f253d365ad8297"}, +] + +[package.dependencies] +pytest = ">=3.0.0,<8.0.0" +requests = ">=2.9" + +[[package]] +name = "pytest-playwright" +version = "0.4.2" +description = "A pytest wrapper with fixtures for Playwright to automate web browsers" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-playwright-0.4.2.tar.gz", hash = "sha256:9e9622e5507f8d27a3c7fd07aadcf43122f0086b69d1b3e6728995c8dbc0e44f"}, + {file = "pytest_playwright-0.4.2-py3-none-any.whl", hash = "sha256:68dd0069e2dbf8e6024c6237d51d0277626687d94ba0dd387cea2a94ae4005b9"}, +] + +[package.dependencies] +playwright = ">=1.18" +pytest = ">=6.2.4,<8.0.0" +pytest-base-url = ">=1.0.0,<3.0.0" +python-slugify = ">=6.0.0,<9.0.0" + +[[package]] +name = "pytest-xdist" +version = "3.3.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, + {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-slugify" +version = "8.0.1" +description = "A Python slugify application that also handles Unicode" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-slugify-8.0.1.tar.gz", hash = "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27"}, + {file = "python_slugify-8.0.1-py2.py3-none-any.whl", hash = "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395"}, +] + +[package.dependencies] +text-unidecode = ">=1.3" + +[package.extras] +unidecode = ["Unidecode (>=1.1.1)"] + +[[package]] +name = "pytz" +version = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "scipy" +version = "1.11.2" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "scipy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2b997a5369e2d30c97995dcb29d638701f8000d04df01b8e947f206e5d0ac788"}, + {file = "scipy-1.11.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:95763fbda1206bec41157582bea482f50eb3702c85fffcf6d24394b071c0e87a"}, + {file = "scipy-1.11.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e367904a0fec76433bf3fbf3e85bf60dae8e9e585ffd21898ab1085a29a04d16"}, + {file = "scipy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d690e1ca993c8f7ede6d22e5637541217fc6a4d3f78b3672a6fe454dbb7eb9a7"}, + {file = "scipy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d2b813bfbe8dec6a75164523de650bad41f4405d35b0fa24c2c28ae07fcefb20"}, + {file = "scipy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:afdb0d983f6135d50770dd979df50bf1c7f58b5b33e0eb8cf5c73c70600eae1d"}, + {file = "scipy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d9886f44ef8c9e776cb7527fb01455bf4f4a46c455c4682edc2c2cc8cd78562"}, + {file = "scipy-1.11.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1342ca385c673208f32472830c10110a9dcd053cf0c4b7d4cd7026d0335a6c1d"}, + {file = "scipy-1.11.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b133f237bd8ba73bad51bc12eb4f2d84cbec999753bf25ba58235e9fc2096d80"}, + {file = "scipy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aeb87661de987f8ec56fa6950863994cd427209158255a389fc5aea51fa7055"}, + {file = "scipy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90d3b1364e751d8214e325c371f0ee0dd38419268bf4888b2ae1040a6b266b2a"}, + {file = "scipy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:f73102f769ee06041a3aa26b5841359b1a93cc364ce45609657751795e8f4a4a"}, + {file = "scipy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa4909c6c20c3d91480533cddbc0e7c6d849e7d9ded692918c76ce5964997898"}, + {file = "scipy-1.11.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ac74b1512d38718fb6a491c439aa7b3605b96b1ed3be6599c17d49d6c60fca18"}, + {file = "scipy-1.11.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8425fa963a32936c9773ee3ce44a765d8ff67eed5f4ac81dc1e4a819a238ee9"}, + {file = "scipy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:542a757e2a6ec409e71df3d8fd20127afbbacb1c07990cb23c5870c13953d899"}, + {file = "scipy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea932570b1c2a30edafca922345854ff2cd20d43cd9123b6dacfdecebfc1a80b"}, + {file = "scipy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:4447ad057d7597476f9862ecbd9285bbf13ba9d73ce25acfa4e4b11c6801b4c9"}, + {file = "scipy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b0620240ef445b5ddde52460e6bc3483b7c9c750275369379e5f609a1050911c"}, + {file = "scipy-1.11.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:f28f1f6cfeb48339c192efc6275749b2a25a7e49c4d8369a28b6591da02fbc9a"}, + {file = "scipy-1.11.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:214cdf04bbae7a54784f8431f976704ed607c4bc69ba0d5d5d6a9df84374df76"}, + {file = "scipy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10eb6af2f751aa3424762948e5352f707b0dece77288206f227864ddf675aca0"}, + {file = "scipy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0f3261f14b767b316d7137c66cc4f33a80ea05841b9c87ad83a726205b901423"}, + {file = "scipy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:2c91cf049ffb5575917f2a01da1da082fd24ed48120d08a6e7297dfcac771dcd"}, + {file = "scipy-1.11.2.tar.gz", hash = "sha256:b29318a5e39bd200ca4381d80b065cdf3076c7d7281c5e36569e99273867f61d"}, +] + +[package.dependencies] +numpy = ">=1.21.6,<1.28.0" + +[package.extras] +dev = ["click", "cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] +doc = ["jupytext", "matplotlib (>2)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-design (>=0.2.0)"] +test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "setuptools" +version = "68.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-68.2.0-py3-none-any.whl", hash = "sha256:af3d5949030c3f493f550876b2fd1dd5ec66689c4ee5d5344f009746f71fd5a8"}, + {file = "setuptools-68.2.0.tar.gz", hash = "sha256:00478ca80aeebeecb2f288d3206b0de568df5cd2b8fada1209843cc9a8d88a48"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tenacity" +version = "8.2.3" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, + {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, +] + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +optional = false +python-versions = "*" +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "urllib3" +version = "2.0.6" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, + {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "vega-sim" +version = "1.2.1" +description = "Simulator for running self-contained Vega chain on local PC" +optional = false +python-versions = "^3.9,<3.12" +files = [] +develop = false + +[package.dependencies] +deprecated = "*" +docker = "*" +grpcio-tools = "*" +inflection = "*" +numpy = "*" +pandas = "*" +plotly = "*" +protoc-gen-openapiv2 = "*" +psutil = "*" +PyNaCl = "*" +python-dotenv = "*" +requests = "*" +scipy = "*" +toml = "*" +websockets = "*" + +[package.extras] +agents = ["TA-Lib"] +jupyter = ["ipywidgets", "jupyter", "jupyterlab", "matplotlib"] +learning = ["gymnasium", "matplotlib", "numba (>=0.57.1,<0.58.0)", "pettingzoo", "stable-baselines3", "tensorboard", "tianshou", "torch", "tqdm"] +profile = ["pytest-profiling", "snakeviz"] + +[package.source] +type = "git" +url = "https://github.com/vegaprotocol/vega-market-sim.git" +reference = "HEAD" +resolved_reference = "4efb75a825b2ddc0ac9979c699309ee4a020b884" + +[[package]] +name = "websocket-client" +version = "1.6.2" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket-client-1.6.2.tar.gz", hash = "sha256:53e95c826bf800c4c465f50093a8c4ff091c7327023b10bfaff40cf1ef170eaa"}, + {file = "websocket_client-1.6.2-py3-none-any.whl", hash = "sha256:ce54f419dfae71f4bdba69ebe65bf7f0a93fe71bc009ad3a010aacc3eebad537"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "11.0.3" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d"}, + {file = "websockets-11.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11"}, + {file = "websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4"}, + {file = "websockets-11.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526"}, + {file = "websockets-11.0.3-cp310-cp310-win32.whl", hash = "sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69"}, + {file = "websockets-11.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288"}, + {file = "websockets-11.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b"}, + {file = "websockets-11.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf"}, + {file = "websockets-11.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd"}, + {file = "websockets-11.0.3-cp311-cp311-win32.whl", hash = "sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c"}, + {file = "websockets-11.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8"}, + {file = "websockets-11.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b"}, + {file = "websockets-11.0.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0"}, + {file = "websockets-11.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af"}, + {file = "websockets-11.0.3-cp37-cp37m-win32.whl", hash = "sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f"}, + {file = "websockets-11.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae"}, + {file = "websockets-11.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86"}, + {file = "websockets-11.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e"}, + {file = "websockets-11.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788"}, + {file = "websockets-11.0.3-cp38-cp38-win32.whl", hash = "sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74"}, + {file = "websockets-11.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd"}, + {file = "websockets-11.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b"}, + {file = "websockets-11.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1"}, + {file = "websockets-11.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311"}, + {file = "websockets-11.0.3-cp39-cp39-win32.whl", hash = "sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128"}, + {file = "websockets-11.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b"}, + {file = "websockets-11.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280"}, + {file = "websockets-11.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4"}, + {file = "websockets-11.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602"}, + {file = "websockets-11.0.3-py3-none-any.whl", hash = "sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6"}, + {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, +] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.11" +content-hash = "d1231fe591b774e34b8f94a54cd02e4d7dae924c57785263841c3b0b0feed505" diff --git a/apps/console-test/pyproject.toml b/apps/console-test/pyproject.toml new file mode 100644 index 000000000..e8f3b191b --- /dev/null +++ b/apps/console-test/pyproject.toml @@ -0,0 +1,30 @@ +[tool.poetry] +name = "console-test" +version = "0.1.0" +description = "" +authors = ["Matthew Russell "] +readme = "README.md" +packages = [{include = "console_test"}] + +[tool.poetry.dependencies] +python = ">=3.9,<3.11" +psutil = "^5.9.5" +vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git"} +pytest-playwright = "^0.4.2" +docker = "^6.1.3" +pytest-xdist = "^3.3.1" +python-dotenv = "^1.0.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +timeout = 120 +log_cli = true +log_cli_format = "%(asctime)s - %(name)s - %(levelname)s: %(message)s" +log_cli_date_format = "%Y-%m-%d %H:%M:%S" +log_cli_level = "INFO" +log_file_format = "%(asctime)s - %(name)s - %(levelname)s: %(message)s" +log_file_date_format = "%Y-%m-%d %H:%M:%S" +log_file_level = "INFO" \ No newline at end of file diff --git a/apps/console-test/tests/assets/test_assets.py b/apps/console-test/tests/assets/test_assets.py new file mode 100644 index 000000000..8643d6fe5 --- /dev/null +++ b/apps/console-test/tests/assets/test_assets.py @@ -0,0 +1,95 @@ +import pytest +import re +from playwright.sync_api import expect, Page + +label_value_tooltip_pairs = [ + { + "label": "ID", + "value": "asset-id", + }, + { + "label": "Type", + "value": "Builtin asset", + "valueToolTip": "A Vega builtin asset", + }, + { + "label": "Name", + "value": "tDAI", + }, + { + "label": "Symbol", + "value": "tDAI", + }, + { + "label": "Decimals", + "value": "5", + "labelTooltip": "Number of decimal / precision handled by this asset", + }, + { + "label": "Quantum", + "value": "0.00001", + "labelTooltip": "The minimum economically meaningful amount of the asset", + }, + { + "label": "Status", + "value": "Enabled", + "labelTooltip": "The status of the asset in the Vega network", + "valueToolTip": "Asset can be used on the Vega network", + }, + { + "label": "Max faucet amount", + "value": "10,000,000,000.00", + "labelTooltip": "Maximum amount that can be requested by a party through the built-in asset faucet at a time", + }, + { + "label": "Infrastructure fee account balance", + "value": "0.00", + "labelTooltip": "The infrastructure fee account in this asset", + }, + { + "label": "Global reward pool account balance", + "value": "0.00", + "labelTooltip": "The global rewards acquired in this asset", + }, +] + + +def tooltip(page: Page, index: int, test_id: str, tooltip: str): + page.locator(f"data-testid={index}_{test_id}").hover() + expect(page.locator('[role="tooltip"]').locator("div")).to_have_text(tooltip) + page.get_by_test_id("dialog-title").click() + + +@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted") +def test_asset_details(page: Page): + page.goto("/#/portfolio") + page.locator('[data-testid="tab-collateral"] >> text=tDAI').click() + + for index, pair in enumerate(label_value_tooltip_pairs): + if index in [7, 8, 9]: # Skip indices 7, 8, and 9. + continue + + label = pair.get("label", "") + value = pair.get("value", "") + label_tooltip = pair.get("labelTooltip", "") + value_tooltip = pair.get("valueToolTip", "") + + if label == "ID": + expect(page.get_by_role("button", name="Copy id to clipboard")).to_be_visible() + asset_id_text = page.locator(f"[data-testid='{index}_value']").inner_text() + pattern = r"^[0-9a-f]{6}\u2026[0-9a-f]{4}" + + assert re.match(pattern, asset_id_text), f"Expected ID to match pattern but got {asset_id_text}" + + else: + expect(page.locator(f"[data-testid='{index}_label']")).to_have_text(label) + expect(page.locator(f"[data-testid='{index}_value']")).to_have_text(value) + + if label_tooltip: + tooltip(page, index, "label", label_tooltip) + + if value_tooltip: + tooltip(page, index, "value", value_tooltip) + + page.get_by_test_id("dialog-close").click() + assert not page.query_selector("dialog-content") diff --git a/apps/console-test/tests/chart_depth/test_chart_depth.py b/apps/console-test/tests/chart_depth/test_chart_depth.py new file mode 100644 index 000000000..896f5fd20 --- /dev/null +++ b/apps/console-test/tests/chart_depth/test_chart_depth.py @@ -0,0 +1,13 @@ +import pytest +from playwright.sync_api import expect, Page + + +@pytest.mark.usefixtures("page", "continuous_market", "risk_accepted") +def test_see_market_depth_chart(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + # Click on the 'Depth' tab + page.get_by_test_id("Depth").click() + # Check if the 'Depth' tab and the depth chart are visible + # 6006-DEPC-001 + expect(page.get_by_test_id("tab-depth")).to_be_visible() + expect(page.locator('[class^="depth-chart-module_canvas__"]').first).to_be_visible() diff --git a/apps/console-test/tests/deal_ticket/test_basic_submit.py b/apps/console-test/tests/deal_ticket/test_basic_submit.py new file mode 100644 index 000000000..42a6d24a0 --- /dev/null +++ b/apps/console-test/tests/deal_ticket/test_basic_submit.py @@ -0,0 +1,142 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from datetime import datetime, timedelta +from conftest import init_vega +from fixtures.market import setup_continuous_market +from actions.utils import wait_for_toast_confirmation + +order_size = "order-size" +order_price = "order-price" +place_order = "place-order" +order_side_sell = "order-side-SIDE_SELL" +market_order = "order-type-Market" +tif = "order-tif" +expire = "expire" + + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture(scope="module") +def continuous_market(vega): + return setup_continuous_market(vega) + + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_limit_buy_order_GTT(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(order_size).fill("10") + page.get_by_test_id(order_price).fill("120") + page.get_by_test_id(tif).select_option("Good 'til Time (GTT)") + expires_at = datetime.now() + timedelta(days=1) + expires_at_input_value = expires_at.strftime("%Y-%m-%dT%H:%M:%S") + page.get_by_test_id("date-picker-field").fill(expires_at_input_value) + # 7002-SORD-011 + expect(page.get_by_test_id("place-order").locator("span").first).to_have_text( + "Place limit order" + ) + expect(page.get_by_test_id("place-order").locator("span").last).to_have_text( + "10 BTC @ 120.00 BTC" + ) + page.get_by_test_id(place_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id("All").click() + # 7002-SORD-017 + expect(page.get_by_role("row").nth(2)).to_contain_text( + "BTC:DAI_2023Futr10+10LimitFilled120.00GTT:" + ) + + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_limit_buy_order(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + + page.get_by_test_id(order_size).fill("10") + page.get_by_test_id(order_price).fill("120") + page.get_by_test_id(place_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id("All").click() + # 7002-SORD-017 + expect(page.get_by_role("row").nth(2)).to_contain_text( + "BTC:DAI_2023Futr10+10LimitFilled120.00GTC" + ) + + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_limit_sell_order(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(order_size).fill("10") + page.get_by_test_id(order_price).fill("100") + page.get_by_test_id(order_side_sell).click() + page.get_by_test_id(tif).select_option("Good for Normal (GFN)") + # 7002-SORD-011 + expect(page.get_by_test_id("place-order").locator("span").first).to_have_text( + "Place limit order" + ) + expect(page.get_by_test_id("place-order").locator("span").last).to_have_text( + "10 BTC @ 100.00 BTC" + ) + page.get_by_test_id(place_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id("All").click() + expect(page.get_by_role("row").nth(2)).to_contain_text( + "BTC:DAI_2023Futr10-10LimitFilled100.00GFN" + ) + + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_market_sell_order(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(market_order).click() + page.get_by_test_id(order_size).fill("10") + page.get_by_test_id(order_side_sell).click() + # 7002-SORD-011 + expect(page.get_by_test_id("place-order").locator("span").first).to_have_text( + "Place market order" + ) + expect(page.get_by_test_id("place-order").locator("span").last).to_have_text( + "10 BTC @ market" + ) + page.get_by_test_id(place_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.get_by_test_id("All").click() + expect(page.get_by_role("row").nth(2)).to_contain_text( + "BTC:DAI_2023Futr10-10MarketFilled-IOC" + ) + + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_market_buy_order(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(market_order).click() + page.get_by_test_id(order_size).fill("10") + page.get_by_test_id(tif).select_option("Fill or Kill (FOK)") + page.get_by_test_id(place_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id("All").click() + # 7002-SORD-010 + # 0003-WTXN-012 + # 0003-WTXN-003 + expect(page.get_by_role("row").nth(2)).to_contain_text( + "BTC:DAI_2023Futr10+10MarketFilled-FOK" + ) diff --git a/apps/console-test/tests/deal_ticket/test_deal_ticket_basics.py b/apps/console-test/tests/deal_ticket/test_deal_ticket_basics.py new file mode 100644 index 000000000..1b6d7a340 --- /dev/null +++ b/apps/console-test/tests/deal_ticket/test_deal_ticket_basics.py @@ -0,0 +1,35 @@ +import pytest +from playwright.sync_api import Page, expect +from conftest import init_vega +from fixtures.market import setup_continuous_market + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + +@pytest.fixture(scope="module") +def continuous_market(vega): + return setup_continuous_market(vega) + +@pytest.mark.skip("We currently can't approve wallet connection through Sim") +@pytest.mark.usefixtures("page", "risk_accepted") +def test_connect_vega_wallet(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("order-price").fill("101") + page.get_by_test_id("order-connect-wallet").click() + expect(page.locator('[role="dialog"]')).to_be_visible() + page.get_by_test_id("connector-jsonRpc").click() + expect(page.get_by_test_id("wallet-dialog-title")).to_be_visible() + # TODO: accept wallet connection and assert wallet is connected. + expect(page.get_by_test_id("order-type-Limit")).to_be_checked() + expect(page.get_by_test_id("order-price")).to_have_value("101") + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_sidebar_should_be_open_after_reload(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + expect(page.get_by_test_id("deal-ticket-form")).to_be_visible() + page.get_by_test_id("Order").click() + expect(page.get_by_test_id("deal-ticket-form")).not_to_be_visible() + page.reload() + expect(page.get_by_test_id("deal-ticket-form")).to_be_visible() diff --git a/apps/console-test/tests/deal_ticket/test_fees_margin_estimations.py b/apps/console-test/tests/deal_ticket/test_fees_margin_estimations.py new file mode 100644 index 000000000..33363c345 --- /dev/null +++ b/apps/console-test/tests/deal_ticket/test_fees_margin_estimations.py @@ -0,0 +1,96 @@ +import pytest +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from actions.vega import submit_order +from actions.utils import wait_for_toast_confirmation + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") + +notional = "deal-ticket-fee-notional" +fees = "deal-ticket-fee-fees" +margin_required = "deal-ticket-fee-margin-required" +item_value = "item-value" +market_trading_mode = "market-trading-mode" + + +@pytest.mark.skip("tbd") +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_margin_and_fees_estimations(continuous_market, vega: VegaService, page: Page): + # setup continuous trading market with one user buy trade + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + + # submit order from UI and verify fees and margin + expect(page.get_by_test_id(notional)).to_have_text("Notional- BTC") + expect(page.get_by_test_id(fees)).to_have_text("Fees- tDAI") + expect(page.get_by_test_id(margin_required)).to_have_text( + "Margin required0.00 tDAI" + ) + page.get_by_test_id("order-size").type("200") + page.get_by_test_id("order-price").type("20") + + expect(page.get_by_test_id(notional)).to_have_text("Notional4,000.00 BTC") + expect(page.get_by_test_id(fees)).to_have_text("Fees~402.00 tDAI") + expect(page.get_by_test_id(margin_required)).to_have_text( + "Margin required1,661.88832 tDAI" + ) + + page.get_by_test_id("place-order").click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + expect(page.get_by_test_id(margin_required)).to_have_text( + "Margin required1,661.88832 tDAI " + ) + page.get_by_test_id("toast-close").click() + + # submit order by sim function + order = submit_order(vega, "Key 1", market_id, "SIDE_BUY", 400, 38329483272398.838) + vega.forward("20s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + expect(page.get_by_test_id(margin_required)).to_have_text( + "Margin required897,716,007,278,798.50 tDAI " + ) + expect(page.get_by_test_id("deal-ticket-warning-margin")).to_contain_text( + "You may not have enough margin available to open this position." + ) + + # cancel order and verify that warning margin disappeared + vega.cancel_order("Key 1", market_id, order) + vega.forward("20s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + expect(page.get_by_test_id("deal-ticket-warning-auction")).to_contain_text( + "Any orders placed now will not trade until the auction ends" + ) + + # add order at the current price so that it is possible to change the status to price monitoring + submit_order(vega, "Key 1", market_id, "SIDE_SELL", 1, 110) + vega.forward("20s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + page.reload() + expect(page.get_by_test_id(margin_required)).to_have_text( + "Margin required1,700.53688 tDAI" + ) + expect( + page.get_by_test_id(market_trading_mode).get_by_test_id(item_value) + ).to_have_text("Continuous") + + # verify if we can submit order after reverted margin + page.get_by_test_id("place-order").click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + # skip temporary + # expect(page.get_by_test_id("toast-content")).to_contain_text( + # "Your transaction has been confirmed" + # ) diff --git a/apps/console-test/tests/deal_ticket/test_stop_order.py b/apps/console-test/tests/deal_ticket/test_stop_order.py new file mode 100644 index 000000000..e09d399f4 --- /dev/null +++ b/apps/console-test/tests/deal_ticket/test_stop_order.py @@ -0,0 +1,416 @@ +import pytest +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from actions.vega import submit_order +from datetime import datetime, timedelta +from conftest import init_vega +from fixtures.market import setup_continuous_market + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +stop_order_btn = "order-type-Stop" +stop_limit_order_btn = "order-type-StopLimit" +stop_market_order_btn = "order-type-StopMarket" +order_side_sell = "order-side-SIDE_SELL" +trigger_above = "triggerDirection-risesAbove" +trigger_below = "triggerDirection-fallsBelow" +trigger_price = "triggerPrice" +trigger_type_price = "triggerType-price" +trigger_type_trailing_percent_offset = "triggerType-trailingPercentOffset" +order_size = "order-size" +order_price = "order-price" +order_tif = "order-tif" +expire = "expire" +expiry_strategy = '[for="expiryStrategy"]' +expiry_strategy_submit = "expiryStrategy-submit" +expiry_strategy_cancel = "expiryStrategy-cancel" +date_picker_field = "date-picker-field" +submit_stop_order = "place-order" +stop_orders_tab = "Stop orders" +row_table = "row" +cancel = "cancel" +market_name_col = '[col-id="market.tradableInstrument.instrument.code"]' +trigger_col = '[col-id="trigger"]' +expiresAt_col = '[col-id="expiresAt"]' +size_col = '[col-id="submission.size"]' +submission_type = '[col-id="submission.type"]' +status_col = '[col-id="status"]' +price_col = '[col-id="submission.price"]' +timeInForce_col = '[col-id="submission.timeInForce"]' +updatedAt_col = '[col-id="updatedAt"]' +close_toast = "toast-close" + + +def wait_for_graphql_response(page, query_name, timeout=5000): + response_data = {} + + def handle_response(route, request): + if "graphql" in request.url: + response = request.response() + if response is not None: + json_response = response.json() + if json_response and "data" in json_response: + data = json_response["data"] + if query_name in data: + response_data["data"] = data + route.continue_() + return + route.continue_() + + # Register the route handler + page.route("**", handle_response) + + # Wait for the response data to be populated + page.wait_for_timeout(timeout) + + # Unregister the route handler + page.unroute("**", handle_response) + + +def create_position(vega: VegaService, market_id): + submit_order(vega, "Key 1", market_id, "SIDE_SELL", 100, 110) + submit_order(vega, "Key 1", market_id, "SIDE_BUY", 100, 110) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup + +@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted") +def test_stop_order_form_error_validation(continuous_market, page: Page): + # 7002-SORD-032 + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.get_by_test_id(submit_stop_order).click() + expect(page.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text( + "You need provide a price" + ) + expect(page.get_by_test_id("stop-order-error-message-size")).to_have_text( + "Size cannot be lower than 1" + ) + + page.get_by_test_id(order_size).fill("1") + page.get_by_test_id(order_price).fill("0.0000001") + expect(page.get_by_test_id("stop-order-error-message-price")).to_have_text( + "Price cannot be lower than 0.00001" + ) + +@pytest.mark.skip("core issue") +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_order_rejected(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_orders_tab).click() + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + page.get_by_test_id(trigger_price).fill("103") + page.get_by_test_id(order_size).fill("3") + page.get_by_test_id(submit_stop_order).click() + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + expect((page.get_by_role(row_table).locator(market_name_col)).nth(1)).to_have_text( + "BTC:DAI_2023Futr" + ) + expect((page.get_by_role(row_table).locator(trigger_col)).nth(1)).to_have_text( + "Mark > 103.00" + ) + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(1)).to_have_text("") + expect((page.get_by_role(row_table).locator(size_col)).nth(1)).to_have_text("+3") + expect((page.get_by_role(row_table).locator(submission_type)).nth(1)).to_have_text( + "Market" + ) + expect((page.get_by_role(row_table).locator(status_col)).nth(1)).to_have_text( + "Rejected" + ) + expect((page.get_by_role(row_table).locator(price_col)).nth(1)).to_have_text("-") + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(1)).to_have_text( + "FOK" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(1) + ).not_to_be_empty() + +@pytest.mark.skip("core issue") +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_market_order_triggered( + continuous_market, vega: VegaService, page: Page +): + # 7002-SORD-071 + # 7002-SORD-074 + # 7002-SORD-075 + # 7002-SORD-067 + # 7002-SORD-068 + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_orders_tab).click() + # create a position because stop order is reduce only type + create_position(vega, continuous_market) + + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.get_by_test_id(trigger_price).fill("103") + page.get_by_test_id(order_size).fill("1") + page.get_by_test_id(expire).click() + expires_at = datetime.now() + timedelta(days=1) + expires_at_input_value = expires_at.strftime("%Y-%m-%dT%H:%M:%S") + page.get_by_test_id("date-picker-field").fill(expires_at_input_value) + page.get_by_test_id(expiry_strategy_cancel).click() + page.get_by_test_id(submit_stop_order).click() + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.wait_for_selector('[data-testid="toast-close"]', state="visible") + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + expect((page.get_by_role(row_table).locator(market_name_col)).nth(1)).to_have_text( + "BTC:DAI_2023Futr" + ) + expect((page.get_by_role(row_table).locator(trigger_col)).nth(1)).to_have_text( + "Mark > 103.00" + ) + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(1)).to_contain_text( + "Cancels" + ) + expect((page.get_by_role(row_table).locator(size_col)).nth(1)).to_have_text("-1") + expect((page.get_by_role(row_table).locator(submission_type)).nth(1)).to_have_text( + "Market" + ) + expect((page.get_by_role(row_table).locator(status_col)).nth(1)).to_have_text( + "Triggered" + ) + expect((page.get_by_role(row_table).locator(price_col)).nth(1)).to_have_text("-") + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(1)).to_have_text( + "FOK" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(1) + ).not_to_be_empty() + +@pytest.mark.skip("core issue") +@pytest.mark.usefixtures("continuous_market", "auth", "risk_accepted") +def test_submit_stop_limit_order_pending( + continuous_market, vega: VegaService, page: Page +): + # 7002-SORD-071 + # 7002-SORD-074 + # 7002-SORD-075 + # 7002-SORD-069 + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_orders_tab).click() + # create a position because stop order is reduce only type + create_position(vega, continuous_market) + + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.get_by_test_id(trigger_below).click() + page.get_by_test_id(trigger_price).fill("102") + page.get_by_test_id(order_price).fill("99") + page.get_by_test_id(order_size).fill("1") + page.get_by_test_id("order-tif").select_option("TIME_IN_FORCE_IOC") + page.get_by_test_id(expire).click() + expires_at = datetime.now() + timedelta(days=1) + expires_at_input_value = expires_at.strftime("%Y-%m-%dT%H:%M:%S") + page.get_by_test_id("date-picker-field").fill(expires_at_input_value) + page.get_by_test_id(submit_stop_order).click() + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.wait_for_selector('[data-testid="toast-close"]', state="visible") + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + expect((page.get_by_role(row_table).locator(market_name_col)).nth(1)).to_have_text( + "BTC:DAI_2023Futr" + ) + expect((page.get_by_role(row_table).locator(trigger_col)).nth(1)).to_have_text( + "Mark < 102.00" + ) + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(1)).to_contain_text( + "Submit" + ) + expect((page.get_by_role(row_table).locator(size_col)).nth(1)).to_have_text("-1") + expect((page.get_by_role(row_table).locator(submission_type)).nth(1)).to_have_text( + "Limit" + ) + expect((page.get_by_role(row_table).locator(status_col)).nth(1)).to_have_text( + "Pending" + ) + expect((page.get_by_role(row_table).locator(price_col)).nth(1)).to_have_text( + "99.00" + ) + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(1)).to_have_text( + "IOC" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(1) + ).not_to_be_empty() + +@pytest.mark.skip("core issue") +@pytest.mark.usefixtures("continuous_market", "auth", "risk_accepted") +def test_submit_stop_limit_order_cancel( + continuous_market, vega: VegaService, page: Page +): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_orders_tab).click() + # create a position because stop order is reduce only type + create_position(vega, continuous_market) + + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.get_by_test_id(trigger_below).click() + page.get_by_test_id(trigger_price).fill("102") + page.get_by_test_id(order_price).fill("99") + page.get_by_test_id(order_size).fill("1") + page.get_by_test_id(submit_stop_order).click() + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.get_by_test_id(close_toast).first.click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(cancel).click() + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id(close_toast).first.click() + + expect( + (page.get_by_role(row_table).locator('[col-id="status"]')).nth(1) + ).to_have_text("Cancelled") + + +class TestStopOcoValidation: + @pytest.fixture(scope="class") + def vega(self, request): + with init_vega(request) as vega: + yield vega + + @pytest.fixture(scope="class") + def continuous_market(self, vega): + return setup_continuous_market(vega) + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_stop_market_order_form_validation(self, continuous_market, page: Page): + # 7002-SORD-052 + # 7002-SORD-055 + # 7002-SORD-056 + # 7002-SORD-057 + # 7002-SORD-058 + # 7002-SORD-064 + # 7002-SORD-065 + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + expect( + page.get_by_test_id("sidebar-content").get_by_text("Trigger").first + ).to_be_visible() + expect(page.locator('[for="triggerDirection-risesAbove"]')).to_have_text( + "Rises above" + ) + expect(page.locator('[for="triggerDirection-fallsBelow"]')).to_have_text( + "Falls below" + ) + page.get_by_test_id(trigger_price).click() + expect(page.get_by_test_id(trigger_price)).to_be_empty + expect(page.locator('[for="triggerType-price"]')).to_have_text("Price") + expect(page.locator('[for="triggerType-trailingPercentOffset"]')).to_have_text( + "Trailing Percent Offset" + ) + expect(page.locator('[for="order-size"]')).to_have_text("Size") + page.get_by_test_id(order_size).click() + expect(page.get_by_test_id(order_size)).to_be_empty + expect(page.get_by_test_id(order_price)).not_to_be_visible() + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_stop_limit_order_form_validation(self, continuous_market, page: Page): + # 7002-SORD-020 + # 7002-SORD-021 + # 7002-SORD-022 + # 7002-SORD-033 + # 7002-SORD-034 + # 7002-SORD-035 + # 7002-SORD-036 + # 7002-SORD-037 + # 7002-SORD-038 + # 7002-SORD-049 + # 7002-SORD-050 + # 7002-SORD-051 + + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + expect( + page.get_by_test_id("sidebar-content").get_by_text("Trigger").first + ).to_be_visible() + expect(page.locator('[for="triggerDirection-risesAbove"]')).to_have_text( + "Rises above" + ) + expect(page.locator('[for="triggerDirection-risesAbove"]')).to_be_checked + expect(page.locator('[for="triggerDirection-fallsBelow"]')).to_have_text( + "Falls below" + ) + page.get_by_test_id(trigger_price).click() + expect(page.get_by_test_id(trigger_price)).to_be_empty + expect(page.locator('[for="triggerType-price"]')).to_have_text("Price") + expect(page.locator('[for="triggerType-price"]')).to_be_checked + expect(page.locator('[for="triggerType-trailingPercentOffset"]')).to_have_text( + "Trailing Percent Offset" + ) + expect(page.locator('[for="order-size"]').first).to_have_text("Size") + expect(page.locator('[for="order-price"]').last).to_have_text("Price") + page.get_by_test_id(order_size).click() + expect(page.get_by_test_id(order_size)).to_be_empty + page.get_by_test_id(order_price).click() + expect(page.get_by_test_id(order_price)).to_be_empty() + + @pytest.mark.skip("core issue") + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_maximum_number_of_active_stop_orders( + self, continuous_market, vega: VegaService, page: Page + ): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(stop_orders_tab).click() + # create a position because stop order is reduce only type + create_position(vega, continuous_market) + for i in range(4): + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.get_by_test_id(trigger_below).click() + page.get_by_test_id(trigger_price).fill("102") + page.get_by_test_id(order_price).fill("99") + page.get_by_test_id(order_size).fill("1") + page.get_by_test_id(submit_stop_order).click() + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + if page.get_by_test_id(close_toast).is_visible(): + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + # 7002-SORD-011 + expect(page.get_by_test_id("stop-order-warning-limit")).to_have_text( + "There is a limit of 4 active stop orders per market. Orders submitted above the limit will be immediately rejected." + ) diff --git a/apps/console-test/tests/deal_ticket/test_stop_order_oco.py b/apps/console-test/tests/deal_ticket/test_stop_order_oco.py new file mode 100644 index 000000000..4fb544b38 --- /dev/null +++ b/apps/console-test/tests/deal_ticket/test_stop_order_oco.py @@ -0,0 +1,498 @@ +from math import exp +import pytest +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from actions.vega import submit_order +from conftest import init_vega, page +from fixtures.market import setup_continuous_market +from actions.utils import wait_for_toast_confirmation + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +stop_order_btn = "order-type-Stop" +stop_limit_order_btn = "order-type-StopLimit" +stop_market_order_btn = "order-type-StopMarket" +order_side_sell = "order-side-SIDE_SELL" +trigger_above = "triggerDirection-risesAbove" +trigger_below = "triggerDirection-fallsBelow" +trigger_price = "triggerPrice" +trigger_type_price = "triggerType-price" +trigger_type_trailing_percent_offset = "triggerType-trailingPercentOffset" +order_size = "order-size" +order_price = "order-price" +order_tif = "order-tif" +expire = "expire" +expiry_strategy = '[for="expiryStrategy"]' +expiry_strategy_submit = "expiryStrategy-submit" +expiry_strategy_cancel = "expiryStrategy-cancel" +date_picker_field = "date-picker-field" +submit_stop_order = "place-order" +stop_orders_tab = "Stop orders" +row_table = "row" +cancel = "cancel" +market_name_col = '[col-id="market.tradableInstrument.instrument.code"]' +trigger_col = '[col-id="trigger"]' +expiresAt_col = '[col-id="expiresAt"]' +size_col = '[col-id="submission.size"]' +submission_type = '[col-id="submission.type"]' +status_col = '[col-id="status"]' +price_col = '[col-id="submission.price"]' +timeInForce_col = '[col-id="submission.timeInForce"]' +updatedAt_col = '[col-id="updatedAt"]' +close_toast = "toast-close" +trigger_direction_fallsBelow_oco = "triggerDirection-fallsBelow-oco" +trigger_direction_fallsAbove_oco = "triggerDirection-fallsAbove-oco" +oco = "oco" +trigger_price_oco = "triggerPrice-oco" +order_size_oco = "order-size-oco" +order_limit_price_oco = "order-price-oco" + + +def wait_for_graphql_response(page, query_name, timeout=5000): + response_data = {} + + def handle_response(route, request): + if "graphql" in request.url: + response = request.response() + if response is not None: + json_response = response.json() + if json_response and "data" in json_response: + data = json_response["data"] + if query_name in data: + response_data["data"] = data + route.continue_() + return + route.continue_() + + # Register the route handler + page.route("**", handle_response) + + # Wait for the response data to be populated + page.wait_for_timeout(timeout) + + # Unregister the route handler + page.unroute("**", handle_response) + + +def create_position(vega: VegaService, market_id): + submit_order(vega, "Key 1", market_id, "SIDE_SELL", 100, 110) + submit_order(vega, "Key 1", market_id, "SIDE_BUY", 100, 110) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup + + +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_order_market_oco_rejected( + continuous_market, vega: VegaService, page: Page +): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_orders_tab).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + page.get_by_test_id(trigger_price).fill("103") + page.get_by_test_id(order_size).fill("3") + # 7002-SORD-098 + expect( + page.get_by_test_id("stop-order-warning-message-trigger-price") + ).to_have_text("Stop order will be triggered immediately") + + # 7002-SORD-082 + page.get_by_test_id(oco).click() + # 7002-SORD-085 + expect(page.get_by_test_id(trigger_direction_fallsBelow_oco)).to_be_checked + # 7002-SORD-086 + page.get_by_test_id(trigger_price_oco).fill("102") + page.get_by_test_id(order_size_oco).fill("3") + page.get_by_test_id(submit_stop_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + + expect((page.get_by_role(row_table).locator(market_name_col)).nth(1)).to_have_text( + "BTC:DAI_2023Futr" + ) + + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(1)).to_have_text("") + expect((page.get_by_role(row_table).locator(size_col)).nth(1)).to_have_text("+3") + # 7002-SORD-083 + expect((page.get_by_role(row_table).locator(submission_type)).nth(1)).to_have_text( + "Market" + ) + expect((page.get_by_role(row_table).locator(status_col)).nth(1)).to_have_text( + "RejectedOCO" + ) + expect((page.get_by_role(row_table).locator(price_col)).nth(1)).to_have_text("-") + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(1)).to_have_text( + "FOK" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(1) + ).not_to_be_empty() + + expect((page.get_by_role(row_table).locator(market_name_col)).nth(2)).to_have_text( + "BTC:DAI_2023Futr" + ) + + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(2)).to_have_text("") + expect((page.get_by_role(row_table).locator(size_col)).nth(2)).to_have_text("+3") + expect((page.get_by_role(row_table).locator(submission_type)).nth(2)).to_have_text( + "Market" + ) + expect((page.get_by_role(row_table).locator(status_col)).nth(2)).to_have_text( + "RejectedOCO" + ) + expect((page.get_by_role(row_table).locator(price_col)).nth(2)).to_have_text("-") + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(2)).to_have_text( + "FOK" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(2) + ).not_to_be_empty() + # 7002-SORD-084 + trigger_price_list = ( + page.locator(".ag-center-cols-container").locator(trigger_col).all_inner_texts() + ) + trigger_value_list = ["Mark < 102.00", "Mark > 103.00"] + assert trigger_price_list.sort() == trigger_value_list.sort() + + +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_oco_market_order_triggered( + continuous_market, vega: VegaService, page: Page +): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_orders_tab).click() + create_position(vega, market_id) + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + page.get_by_test_id(trigger_price).fill("103") + page.get_by_test_id(order_size).fill("3") + + expect( + page.get_by_test_id("stop-order-warning-message-trigger-price") + ).to_have_text("Stop order will be triggered immediately") + + page.get_by_test_id(oco).click() + expect(page.get_by_test_id(trigger_direction_fallsBelow_oco)).to_be_checked + + page.get_by_test_id(trigger_price_oco).fill("102") + page.get_by_test_id(order_size_oco).fill("3") + page.get_by_test_id(submit_stop_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + + expect((page.get_by_role(row_table).locator(market_name_col)).nth(1)).to_have_text( + "BTC:DAI_2023Futr" + ) + + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(1)).to_have_text("") + expect((page.get_by_role(row_table).locator(size_col)).nth(1)).to_have_text("+3") + expect((page.get_by_role(row_table).locator(submission_type)).nth(1)).to_have_text( + "Market" + ) + + expect((page.get_by_role(row_table).locator(price_col)).nth(1)).to_have_text("-") + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(1)).to_have_text( + "FOK" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(1) + ).not_to_be_empty() + + expect((page.get_by_role(row_table).locator(market_name_col)).nth(2)).to_have_text( + "BTC:DAI_2023Futr" + ) + expect((page.get_by_role(row_table).locator(expiresAt_col)).nth(2)).to_have_text("") + expect((page.get_by_role(row_table).locator(size_col)).nth(2)).to_have_text("+3") + expect((page.get_by_role(row_table).locator(submission_type)).nth(2)).to_have_text( + "Market" + ) + + expect((page.get_by_role(row_table).locator(price_col)).nth(2)).to_have_text("-") + expect((page.get_by_role(row_table).locator(timeInForce_col)).nth(2)).to_have_text( + "FOK" + ) + expect( + (page.get_by_role(row_table).locator(updatedAt_col)).nth(2) + ).not_to_be_empty() + + status = ( + page.locator(".ag-center-cols-container").locator(status_col).all_inner_texts() + ) + value = ["StoppedOCO", "TriggeredOCO"] + assert status.sort() == value.sort() + + trigger_price_list = ( + page.locator(".ag-center-cols-container").locator(trigger_col).all_inner_texts() + ) + trigger_value_list = ["Mark < 102.00", "Mark > 103.00"] + assert trigger_price_list.sort() == trigger_value_list.sort() + + +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_oco_market_order_pending( + continuous_market, vega: VegaService, page: Page +): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_orders_tab).click() + create_position(vega, market_id) + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.locator("label").filter(has_text="Falls below").click() + page.get_by_test_id(trigger_price).fill("99") + page.get_by_test_id(order_size).fill("3") + page.get_by_test_id(oco).click() + expect(page.get_by_test_id(trigger_direction_fallsAbove_oco)).to_be_checked + page.get_by_test_id(trigger_price_oco).fill("120") + page.get_by_test_id(order_size_oco).fill("2") + page.get_by_test_id(submit_stop_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + + expect((page.get_by_role(row_table).locator(status_col)).nth(1)).to_have_text( + "PendingOCO" + ) + expect((page.get_by_role(row_table).locator(status_col)).nth(2)).to_have_text( + "PendingOCO" + ) + + +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_oco_limit_order_pending( + continuous_market, vega: VegaService, page: Page +): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_orders_tab).click() + create_position(vega, market_id) + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.locator("label").filter(has_text="Falls below").click() + page.get_by_test_id(trigger_price).fill("102") + page.get_by_test_id(order_size).fill("3") + page.get_by_test_id(order_price).fill("103") + page.get_by_test_id(oco).click() + # 7002-SORD-090 + expect(page.get_by_test_id(trigger_direction_fallsAbove_oco)).to_be_checked + page.get_by_test_id(trigger_price_oco).fill("120") + page.get_by_test_id(order_size_oco).fill("2") + # 7002-SORD-089 + page.get_by_test_id(order_limit_price_oco).fill("99") + page.get_by_test_id(submit_stop_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_role(row_table).locator(market_name_col).nth(1).is_visible() + + expect((page.get_by_role(row_table).locator(submission_type)).nth(1)).to_have_text( + "Limit" + ) + expect((page.get_by_role(row_table).locator(submission_type)).nth(2)).to_have_text( + "Limit" + ) + + price = ( + page.locator(".ag-center-cols-container").locator(price_col).all_inner_texts() + ) + prices = ["103.00", "99.00"] + assert price.sort() == prices.sort() + + # 7002-SORD-091 + trigger_price_list = ( + page.locator(".ag-center-cols-container").locator(trigger_col).all_inner_texts() + ) + trigger_value_list = ["Limit < 102.00", "Limit > 103.00"] + assert trigger_price_list.sort() == trigger_value_list.sort() + + +@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted") +def test_submit_stop_oco_limit_order_cancel( + continuous_market, vega: VegaService, page: Page +): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_orders_tab).click() + create_position(vega, market_id) + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.locator("label").filter(has_text="Falls below").click() + page.get_by_test_id(trigger_price).fill("102") + page.get_by_test_id(order_size).fill("3") + page.get_by_test_id(order_price).fill("103") + page.get_by_test_id(oco).click() + # 7002-SORD-092 + expect(page.get_by_test_id(trigger_direction_fallsAbove_oco)).to_be_checked + # 7002-SORD-094 + page.get_by_test_id(trigger_price_oco).fill("120") + page.get_by_test_id(order_size_oco).fill("2") + # 7002-SORD-093 + page.get_by_test_id(order_limit_price_oco).fill("99") + page.get_by_test_id(submit_stop_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(cancel).first.click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id(close_toast).first.click() + + expect( + page.locator(".ag-center-cols-container").locator('[col-id="status"]').first + ).to_have_text("CancelledOCO") + expect( + page.locator(".ag-center-cols-container").locator('[col-id="status"]').last + ).to_have_text("CancelledOCO") + + +class TestStopOcoValidation: + @pytest.fixture(scope="class") + def vega(self, request): + with init_vega(request) as vega: + yield vega + + @pytest.fixture(scope="class") + def continuous_market(self, vega): + return setup_continuous_market(vega) + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_stop_market_order_oco_form_validation(self, continuous_market, page: Page): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_market_order_btn).click() + page.get_by_test_id(oco).click() + expect( + page.get_by_test_id("sidebar-content").get_by_text("Trigger").last + ).to_be_visible() + # 7002-SORD-084 + expect(page.locator('[for="triggerDirection-risesAbove-oco"]')).to_have_text( + "Rises above" + ) + # 7002-SORD-085 + expect(page.locator('[for="triggerDirection-fallsBelow-oco"]')).to_have_text( + "Falls below" + ) + # 7002-SORD-087 + expect(page.locator('[for="triggerType-price-oco"]')).to_have_text("Price") + expect(page.locator('[for="triggerType-price"]')).to_be_checked + # 7002-SORD-088 + expect( + page.locator('[for="triggerType-trailingPercentOffset-oco"]') + ).to_have_text("Trailing Percent Offset") + expect(page.locator('[for="order-size-oco"]')).to_have_text("Size") + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_stop_limit_order_oco_form_validation(self, continuous_market, page: Page): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_market_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(oco).click() + expect( + page.get_by_test_id("sidebar-content").get_by_text("Trigger").last + ).to_be_visible() + # 7002-SORD-099 + expect(page.locator('[for="triggerDirection-risesAbove-oco"]')).to_have_text( + "Rises above" + ) + # 7002-SORD-091 + expect(page.locator('[for="triggerDirection-fallsBelow-oco"]')).to_have_text( + "Falls below" + ) + # 7002-SORD-095 + expect(page.locator('[for="triggerType-price-oco"]')).to_have_text("Price") + expect(page.locator('[for="triggerType-price"]')).to_be_checked + # 7002-SORD-095 + expect( + page.locator('[for="triggerType-trailingPercentOffset-oco"]') + ).to_have_text("Trailing Percent Offset") + + expect(page.locator('[for="order-size-oco"]')).to_have_text("Size") + expect(page.locator('[for="order-price-oco"]')).to_have_text("Price") + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_maximum_number_of_active_stop_orders_oco( + self, continuous_market, vega: VegaService, page: Page + ): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + page.get_by_test_id(stop_orders_tab).click() + create_position(vega, market_id) + wait_for_graphql_response(page, "stopOrders") + page.get_by_test_id(stop_order_btn).click() + page.get_by_test_id(stop_limit_order_btn).is_visible() + page.get_by_test_id(stop_limit_order_btn).click() + page.get_by_test_id(order_side_sell).click() + page.locator("label").filter(has_text="Falls below").click() + page.get_by_test_id(trigger_price).fill("102") + page.get_by_test_id(order_size).fill("3") + page.get_by_test_id(order_price).fill("103") + page.get_by_test_id(oco).click() + page.get_by_test_id(trigger_price_oco).fill("120") + page.get_by_test_id(order_size_oco).fill("2") + page.get_by_test_id(order_limit_price_oco).fill("99") + for i in range(2): + page.get_by_test_id(submit_stop_order).click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + if page.get_by_test_id(close_toast).is_visible(): + page.get_by_test_id(close_toast).click() + wait_for_graphql_response(page, "stopOrders") + # 7002-SORD-011 + expect(page.get_by_test_id("stop-order-warning-limit")).to_have_text( + "There is a limit of 4 active stop orders per market. Orders submitted above the limit will be immediately rejected." + ) diff --git a/apps/console-test/tests/deal_ticket/test_trading_deal_ticket_submit_account.py b/apps/console-test/tests/deal_ticket/test_trading_deal_ticket_submit_account.py new file mode 100644 index 000000000..517c8f952 --- /dev/null +++ b/apps/console-test/tests/deal_ticket/test_trading_deal_ticket_submit_account.py @@ -0,0 +1,51 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from actions.utils import wait_for_toast_confirmation +from conftest import init_vega +from fixtures.market import setup_continuous_market + + + +order_size = "order-size" +order_price = "order-price" +place_order = "place-order" +deal_ticket_warning_margin = "deal-ticket-warning-margin" +deal_ticket_deposit_dialog_button = "deal-ticket-deposit-dialog-button" + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture(scope="module") +def continuous_market(vega): + return setup_continuous_market(vega) + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_should_display_info_and_button_for_deposit(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id(order_size).fill("200000") + page.get_by_test_id(order_price).fill("20") + # 7002-SORD-060 + expect(page.get_by_test_id(deal_ticket_warning_margin)).to_have_text("You may not have enough margin available to open this position.") + page.get_by_test_id(deal_ticket_warning_margin).hover() + expect(page.get_by_test_id("tooltip-content").nth(0)).to_have_text("1,661,896.6317 tDAI is currently required.You have only 1,000,000.00.Deposit tDAI") + page.get_by_test_id(deal_ticket_deposit_dialog_button).nth(0).click() + expect(page.get_by_test_id("sidebar-content")).to_contain_text("DepositFrom") + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + vega.create_key("key_empty") + page.get_by_test_id("manage-vega-wallet").click() + page.locator('[role="menuitemradio"]').nth(4).click() + page.reload() + page.get_by_test_id(order_size).fill("200") + page.get_by_test_id(order_price).fill("20") + # 7002-SORD-060 + expect(page.get_by_test_id(place_order)).to_be_enabled() + # 7002-SORD-003 + expect(page.get_by_test_id("deal-ticket-error-message-zero-balance")).to_have_text("You need tDAI in your wallet to trade in this market. Make a deposit") + expect(page.get_by_test_id(deal_ticket_deposit_dialog_button)).to_be_visible() diff --git a/apps/console-test/tests/get_started/test_get_started.py b/apps/console-test/tests/get_started/test_get_started.py new file mode 100644 index 000000000..45107bab2 --- /dev/null +++ b/apps/console-test/tests/get_started/test_get_started.py @@ -0,0 +1,205 @@ +import pytest +from playwright.sync_api import expect, Page +import json +from vega_sim.service import VegaService +from fixtures.market import setup_simple_market +from conftest import init_vega +from collections import namedtuple +from actions.vega import submit_order +import logging + +logger = logging.getLogger() + + +@pytest.fixture(scope="class") +def vega(): + with init_vega() as vega: + yield vega + + +# we can reuse vega market-sim service and market in almost all tests +@pytest.fixture(scope="class") +def simple_market(vega: VegaService): + return setup_simple_market(vega) + +class TestGetStarted: + @pytest.mark.usefixtures("page") + def test_get_started_interactive(self, vega: VegaService, page: Page): + page.goto("/") + # 0007-FUGS-001 + expect(page.get_by_test_id("order-connect-wallet")).to_be_visible + expect(page.get_by_test_id("order-connect-wallet")).to_be_enabled + # 0007-FUGS-006 + # 0007-FUGS-002 + expect(page.locator(".list-none")).to_contain_text( + "1.Connect2.Deposit funds3.Open a position" + ) + DEFAULT_WALLET_NAME = "MarketSim" # This is the default wallet name within VegaServiceNull and CANNOT be changed + + # Calling get_keypairs will internally call _load_tokens for the given wallet + keypairs = vega.wallet.get_keypairs(DEFAULT_WALLET_NAME) + wallet_api_token = vega.wallet.login_tokens[DEFAULT_WALLET_NAME] + + # Set token to localStorage so eager connect hook picks it up and immediately connects + wallet_config = json.dumps( + { + "token": f"VWT {wallet_api_token}", + "connector": "jsonRpc", + "url": f"http://localhost:{vega.wallet_port}", + } + ) + + storage_javascript = [ + # Store wallet config so eager connection is initiated + f"localStorage.setItem('vega_wallet_config', '{wallet_config}');", + # Ensure wallet ris dialog doesnt show, otherwise eager connect wont work + "localStorage.setItem('vega_wallet_risk_accepted', 'true');", + # Ensure initial risk dialog doesnt show + "localStorage.setItem('vega_risk_accepted', 'true');", + ] + script = "".join(storage_javascript) + page.add_init_script(script) + page.reload() + + # Assert step 1 complete + expect(page.get_by_test_id("icon-tick")).to_have_count(1) + env = json.dumps( + { + "VEGA_URL": f"http://localhost:{vega.data_node_rest_port}/graphql", + "VEGA_WALLET_URL": f"http://localhost:{vega.wallet_port}", + } + ) + window_env = f"window._env_ = Object.assign({{}}, window._env_, {env})" + page.add_init_script(script=window_env) + + page.reload() + + # Defined namedtuples + WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + + # Wallet Configurations + MM_WALLET = WalletConfig("mm", "pin") + MM_WALLET2 = WalletConfig("mm2", "pin2") + TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + + wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + + mint_amount: float = 10e5 + + for wallet in wallets: + vega.create_key(wallet.name) + + vega.mint( + MM_WALLET.name, + asset="VOTE", + amount=mint_amount, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.create_asset( + MM_WALLET.name, + name="tDAI", + symbol="tDAI", + decimals=5, + max_faucet_amount=1e10, + ) + vega.wait_fn(1) + vega.wait_for_total_catchup() + tdai_id = vega.find_asset_id(symbol="tDAI") + logger.info(f"tDAI: {tdai_id}") + + vega.mint( + "Key 1", + asset=tdai_id, + amount=10, + ) + + vega.wait_fn(1) + vega.wait_for_total_catchup() + # Assert step 2 complete + expect(page.get_by_test_id("icon-tick")).to_have_count(2) + + market_id = vega.create_simple_market( + "tDAI", + proposal_key=MM_WALLET.name, + settlement_asset_id=tdai_id, + termination_key=TERMINATE_WALLET.name, + market_decimals=5, + approve_proposal=True, + forward_time_to_enactment=True, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id("get-started-button").click() + # Assert dialog isn't visible + expect(page.get_by_test_id("welcome-dialog")).not_to_be_visible() + + + @pytest.mark.usefixtures("page", "risk_accepted") + def test_get_started_seen_already(self, simple_market, page: Page): + page.goto(f"/#/markets/{simple_market}") + get_started_locator = page.get_by_test_id("connect-vega-wallet") + page.wait_for_selector('[data-testid="connect-vega-wallet"]', state="attached") + expect(get_started_locator).to_be_enabled + expect(get_started_locator).to_be_visible + # 0007-FUGS-015 + expect(get_started_locator).to_have_text("Get started") + get_started_locator.click() + # 0007-FUGS-007 + expect(page.get_by_test_id("dialog-content").nth(1)).to_be_visible() + + + @pytest.mark.usefixtures("page") + def test_browser_wallet_installed(self, simple_market, page: Page): + page.add_init_script("window.vega = {}") + page.goto(f"/#/markets/{simple_market}") + locator = page.get_by_test_id("connect-vega-wallet") + page.wait_for_selector('[data-testid="connect-vega-wallet"]', state="attached") + expect(locator).to_be_enabled + expect(locator).to_be_visible + expect(locator).to_have_text("Connect") + + + @pytest.mark.usefixtures("page", "risk_accepted") + def test_get_started_deal_ticket(self,simple_market, page: Page): + page.goto(f"/#/markets/{simple_market}") + expect(page.get_by_test_id("order-connect-wallet")).to_have_text("Connect wallet") + + + @pytest.mark.usefixtures("page", "risk_accepted") + def test_browser_wallet_installed_deal_ticket(simple_market, page: Page): + page.add_init_script("window.vega = {}") + page.goto(f"/#/markets/{simple_market}") + # 0007-FUGS-013 + page.wait_for_selector('[data-testid="sidebar-content"]', state="visible") + expect(page.get_by_test_id("get-started-banner")).not_to_be_visible() + + @pytest.mark.usefixtures("page") + def test_redirect_default_market(self, continuous_market, vega: VegaService, page: Page): + page.goto("/") + # 0007-FUGS-012 + expect(page).to_have_url( + f"http://localhost:{vega.console_port}/#/markets/{continuous_market}" + ) + page.get_by_test_id("icon-cross").click() + # 0007-FUGS-018 + expect(page.get_by_test_id("welcome-dialog")).not_to_be_visible() + +class TestBrowseAll: + @pytest.mark.usefixtures("page") + def test_get_started_browse_all(self, simple_market, vega: VegaService, page: Page): + page.goto("/") + print(simple_market) + page.get_by_test_id("browse-markets-button").click() + # 0007-FUGS-005 + expect(page).to_have_url(f"http://localhost:{vega.console_port}/#/markets/{simple_market}") \ No newline at end of file diff --git a/apps/console-test/tests/iceberg_orders/test_iceberg_orders.py b/apps/console-test/tests/iceberg_orders/test_iceberg_orders.py new file mode 100644 index 000000000..744da7a8b --- /dev/null +++ b/apps/console-test/tests/iceberg_orders/test_iceberg_orders.py @@ -0,0 +1,182 @@ +import pytest +from collections import namedtuple +from playwright.sync_api import expect, Page +from vega_sim.service import VegaService +from actions.vega import submit_order +from conftest import init_vega +from fixtures.market import setup_continuous_market + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + + +wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + + +def hover_and_assert_tooltip(page: Page, element_text): + element = page.get_by_text(element_text) + element.hover() + expect(page.get_by_role("tooltip")).to_be_visible() + + +class TestIcebergOrdersValidations: + @pytest.fixture(scope="class") + def vega(self, request): + with init_vega(request) as vega: + yield vega + + @pytest.fixture(scope="class") + def continuous_market(self, vega): + return setup_continuous_market(vega) + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_iceberg_submit(self, continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("iceberg").click() + page.get_by_test_id("order-peak-size").type("2") + page.get_by_test_id("order-minimum-size").type("1") + page.get_by_test_id("order-size").type("3") + page.get_by_test_id("order-price").type("107") + page.get_by_test_id("place-order").click() + + expect(page.get_by_test_id("toast-content")).to_have_text( + "Awaiting confirmationPlease wait for your transaction to be confirmedView in block explorer" + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + expect(page.get_by_test_id("toast-content")).to_have_text( + "Order filledYour transaction has been confirmed View in block explorerSubmit order - filledBTC:DAI_2023+3 @ 107.00 tDAI" + ) + page.get_by_test_id("All").click() + expect( + (page.get_by_role("row").locator('[col-id="type"]')).nth(1) + ).to_have_text("Limit (Iceberg)") + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_iceberg_tooltips(self, continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("iceberg").hover() + expect(page.get_by_role("tooltip")).to_be_visible() + page.get_by_test_id("iceberg").click() + hover_and_assert_tooltip(page, "Peak size") + hover_and_assert_tooltip(page, "Minimum size") + + @pytest.mark.usefixtures("page", "auth", "risk_accepted") + def test_iceberg_validations(self, continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("iceberg").click() + page.get_by_test_id("place-order").click() + expect(page.get_by_test_id("deal-ticket-peak-error-message")).to_be_visible() + expect(page.get_by_test_id("deal-ticket-peak-error-message")).to_have_text( + "You need to provide a peak size" + ) + expect(page.get_by_test_id("deal-ticket-minimum-error-message")).to_be_visible() + expect(page.get_by_test_id("deal-ticket-minimum-error-message")).to_have_text( + "You need to provide a minimum visible size" + ) + page.get_by_test_id("order-peak-size").clear() + page.get_by_test_id("order-peak-size").type("1") + page.get_by_test_id("order-minimum-size").clear() + page.get_by_test_id("order-minimum-size").type("2") + expect(page.get_by_test_id("deal-ticket-peak-error-message")).to_be_visible() + expect(page.get_by_test_id("deal-ticket-peak-error-message")).to_have_text( + "Peak size cannot be greater than the size (0)" + ) + expect(page.get_by_test_id("deal-ticket-minimum-error-message")).to_be_visible() + expect(page.get_by_test_id("deal-ticket-minimum-error-message")).to_have_text( + "Minimum visible size cannot be greater than the peak size (1)" + ) + page.get_by_test_id("order-minimum-size").clear() + page.get_by_test_id("order-minimum-size").type("0.1") + expect(page.get_by_test_id("deal-ticket-minimum-error-message")).to_be_visible() + expect(page.get_by_test_id("deal-ticket-minimum-error-message")).to_have_text( + "Minimum visible size cannot be lower than 1" + ) + + +@pytest.mark.usefixtures("vega", "page", "continuous_market", "auth", "risk_accepted") +def test_iceberg_open_order(continuous_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/{continuous_market}") + + submit_order(vega, "Key 1", continuous_market, "SIDE_SELL", 102, 101, 2, 1) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + page.wait_for_selector(".ag-center-cols-container .ag-row") + expect( + page.locator( + ".ag-center-cols-container .ag-row [col-id='openVolume'] [data-testid='stack-cell-primary']" + ) + ).to_have_text("-98") + page.get_by_test_id("Open").click() + page.wait_for_selector(".ag-center-cols-container .ag-row") + + expect( + page.locator(".ag-center-cols-container .ag-row [col-id='remaining']") + ).to_have_text("99") + expect( + page.locator(".ag-center-cols-container .ag-row [col-id='size']") + ).to_have_text("-102") + expect( + page.locator(".ag-center-cols-container .ag-row [col-id='type'] ") + ).to_have_text("Limit (Iceberg)") + expect( + page.locator(".ag-center-cols-container .ag-row [col-id='status']") + ).to_have_text("Active") + expect(page.get_by_test_id("price-10100000")).to_be_visible + expect(page.get_by_test_id("ask-vol-10100000")).to_have_text("3") + page.get_by_test_id("Trades").click() + expect(page.locator('[id^="cell-price-"]').first).to_have_text("101.50") + expect(page.locator('[id^="cell-size-"]').first).to_have_text("99") + + submit_order(vega, MM_WALLET2.name, continuous_market, "SIDE_BUY", 103, 101) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + expect( + page.locator( + '[data-testid="tab-open-orders"] .ag-center-cols-container .ag-row' + ) + ).not_to_be_visible + page.get_by_test_id("Closed").click() + expect( + page.locator(".ag-center-cols-container .ag-row [col-id='remaining']").first + ).to_have_text("102") + expect( + page.locator( + "[data-testid=\"tab-closed-orders\"] .ag-center-cols-container .ag-row [col-id='size']" + ).first + ).to_have_text("-102") + expect( + page.locator( + "[data-testid=\"tab-closed-orders\"] .ag-center-cols-container .ag-row [col-id='type']" + ).first + ).to_have_text("Limit (Iceberg)") + expect( + page.locator( + "[data-testid=\"tab-closed-orders\"] .ag-center-cols-container .ag-row [col-id='status']" + ).first + ).to_have_text("Filled") + expect(page.locator('[id^="cell-price-"]').nth(2)).to_have_text("101.00") + expect(page.locator('[id^="cell-size-"]').nth(2)).to_have_text("3") + + +def verify_order_label(page: Page, test_id: str, expected_text: str): + element = page.get_by_test_id(test_id) + expect(element).to_be_visible() + expect(element).to_have_text(expected_text) + + +def verify_order_value(page: Page, test_id: str, expected_text: str): + element = page.get_by_test_id(test_id) + expect(element).to_be_visible() + expect(element).to_have_text(expected_text) diff --git a/apps/console-test/tests/market/test_closed_markets.py b/apps/console-test/tests/market/test_closed_markets.py new file mode 100644 index 000000000..c30de7079 --- /dev/null +++ b/apps/console-test/tests/market/test_closed_markets.py @@ -0,0 +1,136 @@ +import pytest +import re +import vega_sim.api.governance as governance +from vega_sim.service import VegaService +from playwright.sync_api import Page, expect +from fixtures.market import setup_continuous_market +from conftest import init_vega + + +@pytest.fixture(scope="class") +def vega(): + with init_vega() as vega: + yield vega + + +@pytest.fixture(scope="class") +def create_settled_market(vega): + market_id = setup_continuous_market(vega) + vega.settle_market( + settlement_key="FJMKnwfZdd48C8NqvYrG", + settlement_price=110, + market_id=market_id, + ) + vega.forward("10s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + + +class TestSettledMarket: + @pytest.mark.usefixtures("risk_accepted", "auth") + def test_settled_header(self, page: Page, create_settled_market): + page.goto(f"/#/markets/all") + page.get_by_test_id("Closed markets").click() + headers = [ + "Market", + "Status", + "Settlement date", + "Best bid", + "Best offer", + "Mark price", + "Settlement price", + "Settlement asset", + "", + ] + + page.wait_for_selector('[data-testid="tab-closed-markets"]', state="visible") + page_headers = ( + page.get_by_test_id("tab-closed-markets") + .locator(".ag-header-cell-text") + .all() + ) + for i, header in enumerate(headers): + expect(page_headers[i]).to_have_text(header) + + @pytest.mark.usefixtures( + "risk_accepted", + "auth", + ) + def test_settled_rows(self, page: Page, create_settled_market): + page.goto(f"/#/markets/all") + page.get_by_test_id("Closed markets").click() + + row_selector = page.locator( + '[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row' + ).first + + # 6001-MARK-001 + expect(row_selector.locator('[col-id="code"]')).to_have_text("BTC:DAI_2023Futr") + # 6001-MARK-003 + expect(row_selector.locator('[col-id="state"]')).to_have_text("Settled") + # 6001-MARK-004 + # 6001-MARK-005 + # 6001-MARK-009 + # 6001-MARK-008 + # 6001-MARK-010 + pattern = r"(\d+)\s+months\s+ago" + date_text = row_selector.locator('[col-id="settlementDate"]').inner_text() + assert re.match(pattern, date_text), f"Expected text to match pattern but got {date_text}" + + + expected_pattern = re.compile(r"https://.*?/oracles/[a-f0-9]{64}") + actual_href = row_selector.locator( + '[col-id="settlementDate"] [data-testid="link"]' + ).get_attribute("href") + assert expected_pattern.match( + actual_href + ), f"Expected href to match {expected_pattern.pattern}, but got {actual_href}" + # 6001-MARK-011 + expect(row_selector.locator('[col-id="bestBidPrice"]')).to_have_text("0.00") + # 6001-MARK-012 + expect(row_selector.locator('[col-id="bestOfferPrice"]')).to_have_text("0.00") + # 6001-MARK-013 + expect(row_selector.locator('[col-id="markPrice"]')).to_have_text("110.00") + # 6001-MARK-014 + # 6001-MARK-015 + # 6001-MARK-016 + #tbd currently we have value unknown + # expect(row_selector.locator('[col-id="settlementDataOracleId"]')).to_have_text( + # "110.00" + # ) + expected_pattern = re.compile(r"https://.*?/oracles/[a-f0-9]{64}") + actual_href = row_selector.locator( + '[col-id="settlementDataOracleId"] [data-testid="link"]' + ).get_attribute("href") + assert expected_pattern.match( + actual_href + ), f"Expected href to match {expected_pattern.pattern}, but got {actual_href}" + + # 6001-MARK-018 + expect(row_selector.locator('[col-id="settlementAsset"]')).to_have_text("tDAI") + # 6001-MARK-020 + assert re.match(pattern, date_text), f"Expected text to match pattern but got {date_text}" + + +@pytest.mark.usefixtures("risk_accepted", "auth") +def test_terminated_market_no_settlement_date(page: Page, vega: VegaService): + setup_continuous_market(vega) + governance.settle_oracle( + wallet=vega.wallet, + oracle_name="INVALID_ORACLE", + settlement_price=110, + key_name="FJMKnwfZdd48C8NqvYrG", + ) + vega.forward("60s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + page.goto(f"/#/markets/all") + page.get_by_test_id("Closed markets").click() + row_selector = page.locator( + '[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row' + ).first + expect(row_selector.locator('[col-id="state"]')).to_have_text("Trading Terminated") + expect(row_selector.locator('[col-id="settlementDate"]')).to_have_text("Unknown") + + # TODO Create test for terminated market with settlement date in future + # TODO Create test for terminated market with settlement date in past diff --git a/apps/console-test/tests/market/test_market.py b/apps/console-test/tests/market/test_market.py new file mode 100644 index 000000000..69bf2723f --- /dev/null +++ b/apps/console-test/tests/market/test_market.py @@ -0,0 +1,208 @@ +import pytest + +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService, PeggedOrder +from actions.vega import submit_order + +import logging + +logger = logging.getLogger() + + +# Wallet Configurations +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + +table_row_selector = ( + '[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row' +) +trading_mode_col = '[col-id="tradingMode"]' +state_col = '[col-id="state"]' +item_value = "item-value" +price_monitoring_bounds_row = "key-value-table-row" +market_trading_mode = "market-trading-mode" +market_state = "market-state" +liquidity_supplied = "liquidity-supplied" +item_value = "item-value" +price_monitoring_bounds_row = "key-value-table-row" +market_trading_mode = "market-trading-mode" +market_state = "market-state" +liquidity_supplied = "liquidity-supplied" + +initial_commitment: float = 100 +initial_price: float = 1 +initial_volume: float = 1 +initial_spread: float = 0.1 +market_name = "BTC:DAI_2023" + + +@pytest.mark.usefixtures("vega", "page", "simple_market", "risk_accepted") +def test_price_monitoring(simple_market, vega: VegaService, page: Page): + page.goto(f"/#/markets/all") + expect(page.locator(table_row_selector).locator(trading_mode_col)).to_have_text( + "Opening auction" + ) + expect(page.locator(table_row_selector).locator('[col-id="state"]')).to_have_text( + "Pending" + ) + result = page.get_by_text(market_name) + result.first.click() + page.get_by_test_id(market_trading_mode).get_by_text("Opening auction").hover() + expect(page.get_by_test_id("opening-auction-sub-status").first).to_have_text( + "Opening auction: Not enough liquidity to open" + ) + logger.info(page.get_by_test_id("opening-auction-sub-status").inner_text) + vega.submit_liquidity( + key_name=MM_WALLET.name, + market_id=simple_market, + commitment_amount=initial_commitment, + fee=0.002, + is_amendment=False, + ) + + vega.submit_order( + market_id=simple_market, + trading_key=MM_WALLET.name, + side="SIDE_BUY", + order_type="TYPE_LIMIT", + price=initial_price - 0.0005, + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + vega.submit_order( + market_id=simple_market, + trading_key=MM_WALLET.name, + side="SIDE_SELL", + order_type="TYPE_LIMIT", + price=initial_price + 0.0005, + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + + expect( + page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value) + ).to_have_text("0.00 (0.00%)") + + # add orders to provide liquidity + submit_order( + vega, MM_WALLET.name, simple_market, "SIDE_BUY", initial_volume, initial_price + ) + submit_order( + vega, MM_WALLET.name, simple_market, "SIDE_SELL", initial_volume, initial_price + ) + submit_order( + vega, + MM_WALLET.name, + simple_market, + "SIDE_BUY", + initial_volume, + initial_price + initial_spread / 2, + ) + submit_order( + vega, + MM_WALLET.name, + simple_market, + "SIDE_SELL", + initial_volume, + initial_price + initial_spread / 2, + ) + submit_order( + vega, MM_WALLET2.name, simple_market, "SIDE_SELL", initial_volume, initial_price + ) + expect( + page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value) + ).to_have_text("100.00 (>100%)") + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + expect( + page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value) + ).to_have_text("50.00 (>100%)") + + page.goto(f"/#/markets/all") + # temporary skip + # expect(page.locator(table_row_selector).locator(trading_mode_col)).to_have_text( + # "Continuous" + # ) + + # commented out because we have an issue #4233 + # expect(page.locator(row_selector).locator(state_col) + # ).to_have_text("Pending") + + page.goto(f"/#/markets/all") + result = page.get_by_text(market_name) + result.first.click() + + page.get_by_test_id("Info").click() + page.get_by_test_id("accordion-title").get_by_text( + "Price monitoring bounds 1" + ).click() + expect( + page.get_by_test_id(price_monitoring_bounds_row).first.get_by_text( + "1.32217 BTC" + ) + ).to_be_visible() + expect( + page.get_by_test_id(price_monitoring_bounds_row).last.get_by_text("0.79245 BTC") + ).to_be_visible() + + # add orders that change the price so that it goes beyond the limits of price monitoring + submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 110) + submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 90) + submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 105) + submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 95) + + # add order at the current price so that it is possible to change the status to price monitoring + to_cancel = submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 105) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + expect( + page.get_by_test_id(price_monitoring_bounds_row).first.get_by_text( + "135.44204 BTC" + ) + ).to_be_visible() + expect( + page.get_by_test_id(price_monitoring_bounds_row).last.get_by_text( + "81.17758 BTC" + ) + ).to_be_visible() + expect( + page.get_by_test_id(market_trading_mode).get_by_test_id(item_value) + ).to_have_text("Monitoring auction - price") + expect(page.get_by_test_id(market_state).get_by_test_id(item_value)).to_have_text( + "Suspended" + ) + expect( + page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value) + ).to_have_text("50.00 (8.78%)") + + # cancel order to increase liquidity + vega.cancel_order(MM_WALLET2.name, simple_market, to_cancel) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + expect(page.get_by_text(market_name).first).to_be_attached() + expect( + page.get_by_test_id(market_trading_mode).get_by_test_id(item_value) + ).to_have_text("Continuous") + expect(page.get_by_test_id(market_state).get_by_test_id(item_value)).to_have_text( + "Active" + ) + # commented out because we have an issue #4233 + # expect(page.get_by_text("Opening auction")).to_be_hidden() + + expect( + page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value) + ).to_have_text("50.00 (>100%)") diff --git a/apps/console-test/tests/market/test_market_info.py b/apps/console-test/tests/market/test_market_info.py new file mode 100644 index 000000000..fa1fb46ea --- /dev/null +++ b/apps/console-test/tests/market/test_market_info.py @@ -0,0 +1,294 @@ +import re +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from fixtures.market import setup_continuous_market + +from conftest import init_page, init_vega, risk_accepted_setup + +market_title_test_id = "accordion-title" + + +@pytest.fixture(scope="module") +def vega(): + with init_vega() as vega: + yield vega + + +# setting up everything in this single fixture, as all of the tests need the same setup, so no point in creating separate ones +@pytest.fixture(scope="module") +def page(vega, browser, request): + with init_page(vega, browser, request) as page: + setup_continuous_market(vega) + risk_accepted_setup(page) + page.goto("/") + page.get_by_test_id("Info").click() + yield page + + +@pytest.fixture(autouse=True) +def after_each(page: Page): + yield + opened_element = page.locator('h3[data-state="open"]') + if opened_element.all() and opened_element.get_by_role("button").is_visible(): + opened_element.get_by_role("button").click() + + +def validate_info_section(page: Page, fields: [[str, str]]): + for rowNumber, field in enumerate(fields): + name, value = field + expect( + page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dt") + ).to_contain_text(name) + expect( + page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dd") + ).to_contain_text(value) + + +def test_market_info_current_fees(page: Page): + # 6002-MDET-101 + page.get_by_test_id(market_title_test_id).get_by_text("Current fees").click() + fields = [ + ["Maker Fee", "10.00%"], + ["Infrastructure Fee", "0.05%"], + ["Liquidity Fee", "0.00%"], + ["Total Fees", "10.05%"], + ] + validate_info_section(page, fields) + + +def test_market_info_market_price(page: Page): + # 6002-MDET-102 + page.get_by_test_id(market_title_test_id).get_by_text("Market price").click() + fields = [ + ["Mark Price", "107.50"], + ["Best Bid Price", "101.50"], + ["Best Offer Price", "103.50"], + ["Quote Unit", "BTC"], + ] + validate_info_section(page, fields) + + +def test_market_info_market_volume(page: Page): + # 6002-MDET-103 + page.get_by_test_id(market_title_test_id).get_by_text("Market volume").click() + fields = [ + ["24 Hour Volume", "-"], + ["Open Interest", "1"], + ["Best Bid Volume", "99"], + ["Best Offer Volume", "99"], + ["Best Static Bid Volume", "1"], + ["Best Static Offer Volume", "1"], + ] + validate_info_section(page, fields) + + +def test_market_info_insurance_pool(page: Page): + # 6002-MDET-104 + page.get_by_test_id(market_title_test_id).get_by_text("Insurance pool").click() + fields = [["Balance", "0.00 tDAI"]] + validate_info_section(page, fields) + + +def test_market_info_key_details(page: Page, vega: VegaService): + # 6002-MDET-201 + page.get_by_test_id(market_title_test_id).get_by_text("Key details").click() + market_id = vega.find_market_id("BTC:DAI_2023") + short_market_id = market_id[:6] + "…" + market_id[-4:] + fields = [ + ["Market ID", short_market_id], + ["Name", "BTC:DAI_2023"], + ["Parent Market ID", "-"], + ["Insurance Pool Fraction", "-"], + ["Status", "Active"], + ["Trading Mode", "Continuous"], + ["Market Decimal Places", "5"], + ["Position Decimal Places", "0"], + ["Settlement Asset Decimal Places", "5"], + ] + validate_info_section(page, fields) + + +def test_market_info_instrument(page: Page): + # 6002-MDET-202 + page.get_by_test_id(market_title_test_id).get_by_text("Instrument").click() + fields = [ + ["Market Name", "BTC:DAI_2023"], + ["Code", "BTC:DAI_2023"], + ["Product Type", "Future"], + ["Quote Name", "BTC"], + ] + validate_info_section(page, fields) + + +# @pytest.mark.skip("oracle test to be fixed") +def test_market_info_oracle(page: Page, vega: VegaService): + # 6002-MDET-203 + page.get_by_test_id(market_title_test_id).get_by_text("Oracle").click() + expect( + page.locator('[data-state="open"]').get_by_test_id("accordion-content") + ).to_contain_text("No oracle proof for settlement data") + expect(page.get_by_test_id("oracle-spec-links")).to_have_text( + "View settlement data specification" + ) + # expect(page.get_by_test_id("oracle-spec-links")).to_have_attribute( + # "href", re.compile(rf'(\/oracles\/{vega.find_market_id("BTC:DAI_2023")})') + # ) + + +def test_market_info_settlement_asset(page: Page, vega: VegaService): + # 6002-MDET-206 + page.get_by_test_id(market_title_test_id).get_by_text("Settlement asset").click() + tdai_id = vega.find_asset_id("tDAI") + tdai_id_short = tdai_id[:6] + "…" + tdai_id[-4:] + fields = [ + ["ID", tdai_id_short], + ["Type", "Builtin asset"], + ["Name", "tDAI"], + ["Symbol", "tDAI"], + ["Decimals", "5"], + ["Quantum", "0.00001"], + ["Status", "Enabled"], + ["Max faucet amount", "10,000,000,000.00"], + ["Infrastructure fee account balance", "0.00"], + ["Global reward pool account balance", "0.00"], + ] + validate_info_section(page, fields) + + +def test_market_info_metadata(page: Page): + # 6002-MDET-207 + page.get_by_test_id(market_title_test_id).get_by_text("Metadata").click() + fields = [ + ["Base", "BTC"], + ] + validate_info_section(page, fields) + + +def test_market_info_risk_model(page: Page): + # 6002-MDET-208 + page.get_by_test_id(market_title_test_id).get_by_text("Risk model").click() + fields = [ + ["Tau", "0.00011407711613050422"], + ["Risk Aversion Parameter", "0.000001"], + ["Sigma", "1"], + ] + validate_info_section(page, fields) + + +def test_market_info_margin_scaling_factors(page: Page): + # 6002-MDET-209 + page.get_by_test_id(market_title_test_id).get_by_text( + "Margin scaling factors" + ).click() + fields = [ + ["Linear Slippage Factor", "0.001"], + ["Quadratic Slippage Factor", "0"], + ["Search Level", "1.1"], + ["Initial Margin", "1.5"], + ["Collateral Release", "1.7"], + ] + validate_info_section(page, fields) + + +def test_market_info_risk_factors(page: Page): + # 6002-MDET-210 + page.get_by_test_id(market_title_test_id).get_by_text("Risk factors").click() + fields = [ + ["Long", "0.05153"], + ["Short", "0.05422"], + ["Max Leverage Long", "19.036"], + ["Max Leverage Short", "18.111"], + ["Max Initial Leverage Long", "12.691"], + ["Max Initial Leverage Short", "12.074"], + ] + validate_info_section(page, fields) + + +def test_market_info_price_monitoring_bounds(page: Page): + # 6002-MDET-211 + page.get_by_test_id(market_title_test_id).get_by_text( + "Price monitoring bounds 1" + ).click() + expect(page.locator("p.col-span-1").nth(0)).to_contain_text( + "99.9999% probability price bounds" + ) + expect(page.locator("p.col-span-1").nth(1)).to_contain_text("Within 86,400 seconds") + fields = [ + ["Highest Price", "138.66685 BTC"], + ["Lowest Price", "83.11038 BTC"], + ] + validate_info_section(page, fields) + + +def test_market_info_liquidity_monitoring_parameters(page: Page): + # 6002-MDET-212 + page.get_by_test_id(market_title_test_id).get_by_text( + "Liquidity monitoring parameters" + ).click() + fields = [ + ["Triggering Ratio", "0.7"], + ["Time Window", "3,600"], + ["Scaling Factor", "1"], + ] + validate_info_section(page, fields) + + +# Liquidity resolves to 3 results +def test_market_info_liquidit(page: Page): + # 6002-MDET-213 + page.get_by_test_id(market_title_test_id).get_by_text( + "Liquidity", exact=True + ).click() + fields = [ + ["Target Stake", "5.82757 tDAI"], + ["Supplied Stake", "10,000.00 tDAI"], + ] + validate_info_section(page, fields) + + +def test_market_info_liquidity_price_range(page: Page): + # 6002-MDET-214 + page.get_by_test_id(market_title_test_id).get_by_text( + "Liquidity price range" + ).click() + fields = [ + ["Liquidity Price Range", "100.00% of mid price"], + ["Lowest Price", "0.00 BTC"], + ["Highest Price", "205.00 BTC"], + ] + validate_info_section(page, fields) + + +def test_market_info_proposal(page: Page, vega: VegaService): + # 6002-MDET-301 + page.get_by_test_id(market_title_test_id).get_by_text("Proposal").click() + first_link = ( + page.get_by_test_id("accordion-content").get_by_test_id("external-link").first + ) + second_link = ( + page.get_by_test_id("accordion-content").get_by_test_id("external-link").nth(1) + ) + expect(first_link).to_have_text("View governance proposal") + expect(first_link).to_have_attribute( + "href", re.compile(rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})') + ) + expect(second_link).to_have_text("Propose a change to market") + + # create regular expression that matches "/proposals/propose/update-market" string + expect(second_link).to_have_attribute( + "href", re.compile(r"(\/proposals\/propose\/update-market)") + ) + + +def test_market_info_succession_line(page: Page, vega: VegaService): + page.get_by_test_id(market_title_test_id).get_by_text("Succession line").click() + market_id = vega.find_market_id("BTC:DAI_2023") + succession_line = page.get_by_test_id("succession-line-item") + expect(succession_line.get_by_test_id("external-link")).to_have_text("BTC:DAI_2023") + expect(succession_line.get_by_test_id("external-link")).to_have_attribute( + "href", re.compile(rf"(\/proposals\/{market_id})") + ) + expect(page.get_by_test_id("succession-line-item-market-id")).to_have_text( + market_id + ) diff --git a/apps/console-test/tests/market/test_market_selector.py b/apps/console-test/tests/market/test_market_selector.py new file mode 100644 index 000000000..21602c2de --- /dev/null +++ b/apps/console-test/tests/market/test_market_selector.py @@ -0,0 +1,88 @@ +import pytest +from playwright.sync_api import expect, Page + + +@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted") +def test_market_selector(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + expect(page.get_by_test_id("market-selector")).not_to_be_visible() + page.get_by_test_id("header-title").click() + # 6001-MARK-066 + expect(page.get_by_test_id("market-selector")).to_be_visible() + + # 6001-MARK-021 + # 6001-MARK-022 + # 6001-MARK-024 + # 6001-MARK-025 + btc_market = page.locator('[data-testid="market-selector-list"] a') + expect(btc_market.locator("h3")).to_have_text("BTC:DAI_2023Futr") + expect(btc_market.locator('[data-testid="market-selector-volume"]')).to_have_text( + "0.00" + ) + expect(btc_market.locator('[data-testid="market-selector-price"]')).to_have_text( + "107.50 tDAI" + ) + expect(btc_market.locator("span.rounded-md.leading-none")).to_be_visible() + expect(btc_market.locator("span.rounded-md.leading-none")).to_have_text("Futr") + expect(btc_market.locator('[data-testid="sparkline-svg"]')).not_to_be_visible + + +@pytest.mark.usefixtures("page", "continuous_market", "simple_market", "auth", "risk_accepted") +@pytest.mark.parametrize( + "simple_market", + [ + { + "custom_market_name": "APPL.MF21", + "custom_asset_name": "tUSDC", + "custom_asset_symbol": "tUSDC", + } + ], + indirect=True, +) +def test_market_selector_filter(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("header-title").click() + # 6001-MARK-027 + + page.get_by_test_id("product-Spot").click() + expect(page.get_by_test_id("market-selector-list")).to_contain_text( + "Spot markets coming soon." + ) + page.get_by_test_id("product-Perpetual").click() + expect(page.get_by_test_id("market-selector-list")).to_contain_text( + "No perpetual markets." + ) + page.get_by_test_id("product-Future").click() + expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(2) + + # 6001-MARK-029 + page.get_by_test_id("search-term").fill("btc") + expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(1) + expect(page.locator('[data-testid="market-selector-list"] a').nth(0)).to_have_text( + "BTC:DAI_2023107.50 tDAI0.00" + ) + + page.get_by_test_id("search-term").clear() + expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(2) + + # 6001-MARK-030 + # 6001-MARK-031 + # 6001-MARK-032 + # 6001-MARK-033 + page.get_by_test_id("sort-trigger").click() + + expect(page.get_by_test_id("sort-item-Gained")).to_have_text("Top gaining") + expect(page.get_by_test_id("sort-item-Gained")).to_be_visible() + expect(page.get_by_test_id("sort-item-Lost")).to_have_text("Top losing") + expect(page.get_by_test_id("sort-item-Lost")).to_be_visible() + expect(page.get_by_test_id("sort-item-New")).to_have_text("New markets") + expect(page.get_by_test_id("sort-item-New")).to_be_visible() + + # 6001-MARK-028 + page.get_by_test_id("sort-trigger").click(force=True) + page.get_by_test_id("asset-trigger").click() + page.get_by_role("menuitemcheckbox").nth(0).get_by_text("tDAI").click() + expect(page.locator('[data-testid="market-selector-list"] a')).to_have_count(1) + expect(page.locator('[data-testid="market-selector-list"] a').nth(0)).to_have_text( + "BTC:DAI_2023107.50 tDAI0.00" + ) diff --git a/apps/console-test/tests/market/test_markets_all.py b/apps/console-test/tests/market/test_markets_all.py new file mode 100644 index 000000000..6d911bebd --- /dev/null +++ b/apps/console-test/tests/market/test_markets_all.py @@ -0,0 +1,160 @@ +import pytest +from playwright.sync_api import Page, expect +from fixtures.market import setup_continuous_market + +from conftest import init_vega + +market_names = ["ETHBTC.QM21", "BTCUSD.MF21", "SOLUSD", "AAPL.MF21"] + + +@pytest.fixture(scope="module") +def vega(): + with init_vega() as vega: + yield vega + + +@pytest.fixture(scope="module") +def create_markets(vega): + for market_name in market_names: + setup_continuous_market(vega, custom_market_name=market_name) + + +@pytest.mark.usefixtures("risk_accepted") +def test_table_headers(page: Page, create_markets): + page.goto(f"/#/markets/all") + headers = [ + "Market", + "Description", + "Trading mode", + "Status", + "Mark price", + "24h volume", + "Settlement asset", + "Spread", + "", + ] + + page.wait_for_selector('[data-testid="tab-open-markets"]', state="visible") + page_headers = ( + page.get_by_test_id("tab-open-markets").locator(".ag-header-cell-text").all() + ) + for i, header in enumerate(headers): + expect(page_headers[i]).to_have_text(header) + + +@pytest.mark.usefixtures("risk_accepted") +def test_markets_tab(page: Page, create_markets): + page.goto(f"/#/markets/all") + expect(page.get_by_test_id("Open markets")).to_have_attribute( + "data-state", "active" + ) + expect(page.get_by_test_id("Proposed markets")).to_have_attribute( + "data-state", "inactive" + ) + expect(page.get_by_test_id("Closed markets")).to_have_attribute( + "data-state", "inactive" + ) + + +@pytest.mark.usefixtures("risk_accepted") +def test_markets_content(page: Page, create_markets): + page.goto(f"/#/markets/all") + row_selector = page.locator( + '[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row' + ).first + instrument_code_locator = '[col-id="tradableInstrument.instrument.code"] [data-testid="stack-cell-primary"]' + # 6001-MARK-035 + expect(row_selector.locator(instrument_code_locator)).to_have_text("ETHBTC.QM21") + + # 6001-MARK-073 + expect(row_selector.locator('[title="Future"]')).to_have_text("Futr") + + # 6001-MARK-036 + expect( + row_selector.locator('[col-id="tradableInstrument.instrument.name"]') + ).to_have_text("ETHBTC.QM21") + + # 6001-MARK-037 + expect(row_selector.locator('[col-id="tradingMode"]')).to_have_text("Continuous") + + # 6001-MARK-038 + expect(row_selector.locator('[col-id="state"]')).to_have_text("Active") + + # 6001-MARK-039 + expect(row_selector.locator('[col-id="data.markPrice"]')).to_have_text("107.50") + + # 6001-MARK-040 + expect(row_selector.locator('[col-id="data.candles"]')).to_have_text("0.00") + + # 6001-MARK-042 + expect( + row_selector.locator( + '[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"]' + ) + ).to_have_text("tDAI") + + expect(row_selector.locator('[col-id="data.bestBidPrice"]')).to_have_text("2") + + # 6001-MARK-043 + row_selector.locator( + '[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"] button' + ).click() + expect(page.get_by_test_id("dialog-title")).to_have_text("Asset details - tDAI") + # 6001-MARK-019 + page.get_by_test_id("close-asset-details-dialog").click() + + +@pytest.mark.usefixtures("risk_accepted") +def test_market_actions(page: Page, create_markets): + # 6001-MARK-044 + # 6001-MARK-045 + # 6001-MARK-046 + # 6001-MARK-047 + page.goto(f"/#/markets/all") + page.locator( + '.ag-pinned-right-cols-container [col-id="market-actions"]' + ).first.locator("button").click() + + actions = [ + "Copy Market ID", + "View on Explorer", + "View settlement asset details", + ] + action_elements = ( + page.get_by_test_id("market-actions-content").get_by_role("menuitem").all() + ) + + for i, action in enumerate(actions): + expect(action_elements[i]).to_have_text(action) + + +@pytest.mark.usefixtures("risk_accepted") +def test_sort_markets(page: Page, create_markets): + # 6001-MARK-064 + + page.goto(f"/#/markets/all") + sorted_market_names = [ + "AAPL.MF21", + "BTCUSD.MF21", + "ETHBTC.QM21", + "SOLUSD", + ] + page.locator('.ag-header-row [col-id="tradableInstrument.instrument.code"]').click() + for i, market_name in enumerate(sorted_market_names): + expect( + page.locator( + f'[row-index="{i}"] [col-id="tradableInstrument.instrument.name"]' + ) + ).to_have_text(market_name) + + +@pytest.mark.usefixtures("risk_accepted") +def test_drag_and_drop_column(page: Page, create_markets): + # 6001-MARK-065 + page.goto(f"/#/markets/all") + col_instrument_code = '.ag-header-row [col-id="tradableInstrument.instrument.code"]' + + page.locator(col_instrument_code).drag_to( + page.locator('.ag-header-row [col-id="data.bestBidPrice"]') + ) + expect(page.locator(col_instrument_code)).to_have_attribute("aria-colindex", "8") diff --git a/apps/console-test/tests/market/test_markets_no_markets.py b/apps/console-test/tests/market/test_markets_no_markets.py new file mode 100644 index 000000000..fd2e2a6f0 --- /dev/null +++ b/apps/console-test/tests/market/test_markets_no_markets.py @@ -0,0 +1,36 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService + +from conftest import init_page, init_vega, risk_accepted_setup + + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture(scope="module") +def page(vega, browser, request): + with init_page(vega, browser, request) as page: + risk_accepted_setup(page) + page.goto("/#/markets/all") + yield page + + +def test_no_open_markets(page: Page): + # 6001-MARK-034 + page.get_by_test_id("Open markets").click() + expect(page.locator(".ag-overlay-wrapper")).to_have_text("No markets") + + +def test_no_closed_markets(page: Page): + page.get_by_test_id("Closed markets").click() + expect(page.locator(".ag-overlay-wrapper")).to_have_text("No markets") + + +def test_no_proposed_markets(page: Page): + # 6001-MARK-061 + page.get_by_test_id("Proposed markets").click() + expect(page.locator(".ag-overlay-wrapper")).to_have_text("No proposed markets") diff --git a/apps/console-test/tests/market/test_markets_proposed.py b/apps/console-test/tests/market/test_markets_proposed.py new file mode 100644 index 000000000..81839f924 --- /dev/null +++ b/apps/console-test/tests/market/test_markets_proposed.py @@ -0,0 +1,132 @@ +from math import exp +import pytest +import vega_sim.api.governance as governance +import re + +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService + +from conftest import init_vega +from fixtures.market import setup_simple_market + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + +row_selector = '[data-testid="tab-proposed-markets"] .ag-center-cols-container .ag-row' +col_market_id = '[col-id="market"] [data-testid="stack-cell-primary"]' + + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture(scope="module") +def proposed_market(vega: VegaService): + # setup market without liquidity provided + market_id = setup_simple_market(vega, approve_proposal=False) + # approve market + governance.approve_proposal( + key_name=MM_WALLET.name, + proposal_id=market_id, + wallet=vega.wallet, + ) + return market_id + + +@pytest.mark.usefixtures("risk_accepted") +def test_can_see_table_headers(proposed_market, page: Page): + page.goto("/#/markets/all") + page.click('[data-testid="Proposed markets"]') + + # Test that you can see table headers + headers = [ + "Market", + "Settlement asset", + "State", + "Parent market", + "Voting", + "Closing date", + "Enactment date", + "", + ] + + header_elements = page.locator(".ag-header-cell-text") + for i, header in enumerate(headers): + assert header_elements.nth(i).inner_text() == header + + +@pytest.mark.usefixtures("risk_accepted") +def test_renders_markets_correctly(proposed_market, page: Page): + page.goto(f"/#/markets/all") + page.click('[data-testid="Proposed markets"]') + row = page.locator(row_selector) + # 6001-MARK-049 + expect(row.locator(col_market_id)).to_have_text("BTC:DAI_2023") + + # 6001-MARK-051 + expect(row.locator('[col-id="asset"]')).to_have_text("tDAI") + + # 6001-MARK-052 + # 6001-MARK-053 + expect(row.locator('[col-id="state"]')).to_have_text("Open") + expect( + row.locator('[col-id="terms.change.successorConfiguration.parentMarketId"]') + ).to_have_text("-") + + # 6001-MARK-054 + # 6001-MARK-055 + expect(row.get_by_test_id("vote-progress-bar-against")).to_be_visible() + + # 6001-MARK-056 + expect(row.locator('[col-id="closing-date"]')).not_to_be_empty() + + # 6001-MARK-057 + expect(row.locator('[col-id="enactment-date"]')).not_to_be_empty + + # 6001-MARK-058 + page.get_by_test_id("dropdown-menu").click() + dropdown_content = '[data-testid="proposal-actions-content"]' + first_item_link = ( + page.locator(f"{dropdown_content} [role='menuitem']").nth(0).locator("a") + ) + + # 6001-MARK-059 + expect(first_item_link).to_contain_text("View proposal") + expect(first_item_link).to_have_attribute( + "href", + re.compile(r"\/proposals\/[a-f0-9]{64}$"), + ) + + # temporary skip + # 6001-MARK-060 + # proposed_markets_tab = page.get_by_test_id("tab-proposed-markets") + # external_links = proposed_markets_tab.locator("font-alpha") + # last_link = external_links.last + # assert last_link.inner_text() == 'Propose a new market' + + # expected_href = f"https://governance.stagnet1.vega.rocks/proposals/propose/new-market" + # assert last_link.get_attribute('href') == expected_href + + +@pytest.mark.usefixtures("risk_accepted") +def test_can_drag_and_drop_columns(proposed_market, page: Page): + # 6001-MARK-063 + page.goto("/#/markets/all") + page.click('[data-testid="Proposed markets"]') + col_market = page.locator('[col-id="market"]').first + col_vote = page.locator('[col-id="voting"]').first + col_market.drag_to(col_vote) + + # Check the attribute of the dragged element + attribute_value = col_market.get_attribute("aria-colindex") + assert attribute_value != "1" diff --git a/apps/console-test/tests/market/test_monitoring_auction_price_volatility_market.py b/apps/console-test/tests/market/test_monitoring_auction_price_volatility_market.py new file mode 100644 index 000000000..ac2689b75 --- /dev/null +++ b/apps/console-test/tests/market/test_monitoring_auction_price_volatility_market.py @@ -0,0 +1,131 @@ +import pytest +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from actions.vega import submit_order +from fixtures.market import setup_simple_market +from conftest import init_vega + +from actions.utils import wait_for_toast_confirmation + + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture(scope="module") +def simple_market(vega): + return setup_simple_market(vega) + +@pytest.fixture(scope="module") +def setup_market_monitoring_auction(vega: VegaService, simple_market): + vega.submit_liquidity( + key_name=MM_WALLET.name, + market_id=simple_market, + commitment_amount=100, + fee=0.002, + is_amendment=False, + ) + + vega.submit_order( + market_id=simple_market, + trading_key=MM_WALLET.name, + side="SIDE_BUY", + order_type="TYPE_LIMIT", + price=1 - 0.0005, + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + vega.submit_order( + market_id=simple_market, + trading_key=MM_WALLET.name, + side="SIDE_SELL", + order_type="TYPE_LIMIT", + price=1 + 0.0005, + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + + + # add orders to provide liquidity + submit_order(vega, MM_WALLET.name, simple_market, "SIDE_BUY", 1, 1) + submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 1, 1) + submit_order(vega,MM_WALLET.name,simple_market, "SIDE_BUY",1,1 + 0.1 / 2,) + submit_order(vega,MM_WALLET.name,simple_market,"SIDE_SELL",1,1 + 0.1 / 2) + submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_SELL", 1, 1) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # add orders that change the price so that it goes beyond the limits of price monitoring + submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 110) + submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 90) + submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 105) + submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 95) + submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 105) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + +@pytest.mark.usefixtures("page", "risk_accepted", "simple_market", "auth", "setup_market_monitoring_auction") +def test_market_monitoring_auction_price_volatility_limit_order(page: Page, simple_market, vega: VegaService): + + page.goto(f"/#/markets/{simple_market}") + page.get_by_test_id("order-size").clear() + page.get_by_test_id("order-size").type("1") + page.get_by_test_id("order-price").clear() + page.get_by_test_id("order-price").type("110") + page.get_by_test_id("order-tif").select_option("Fill or Kill (FOK)") + page.get_by_test_id("place-order").click() + + expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text("This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders") + expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_be_visible() + + expect(page.get_by_test_id("deal-ticket-warning-auction")).to_have_text("Any orders placed now will not trade until the auction ends") + expect(page.get_by_test_id("deal-ticket-warning-auction")).to_be_visible() + + page.get_by_test_id("order-tif").select_option("Good 'til Cancelled (GTC)") + + expect(page.get_by_test_id("deal-ticket-error-message-tif")).not_to_be_visible() + + page.get_by_test_id("place-order").click() + + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.get_by_test_id("All").click() + expect(page.get_by_role("row").nth(2)).to_contain_text( + "BTC:DAI_2023Futr0+1LimitActive110.00GTC" + ) + +@pytest.mark.usefixtures("page", "risk_accepted", "simple_market", "auth", "setup_market_monitoring_auction") +def test_market_monitoring_auction_price_volatility_market_order(page: Page, simple_market): + page.goto(f"/#/markets/{simple_market}") + page.get_by_test_id("order-type-Market").click() + page.get_by_test_id("order-size").clear() + page.get_by_test_id("order-size").type("1") + # 7002-SORD-060 + page.get_by_test_id("place-order").click() + + expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text("This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders") + expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_be_visible() + + expect(page.get_by_test_id("deal-ticket-error-message-type")).to_have_text("This market is in auction due to high price volatility. Only limit orders are permitted when market is in auction") + expect(page.get_by_test_id("deal-ticket-error-message-type")).to_be_visible() diff --git a/apps/console-test/tests/market_lifecycle/test_market_lifecycle.py b/apps/console-test/tests/market_lifecycle/test_market_lifecycle.py new file mode 100644 index 000000000..c4c1b64ce --- /dev/null +++ b/apps/console-test/tests/market_lifecycle/test_market_lifecycle.py @@ -0,0 +1,174 @@ +import pytest +import vega_sim.api.governance as governance +import re + +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService, PeggedOrder +import vega_sim.proto.vega as vega_protos +import vega_sim.api.governance as governance +from actions.vega import submit_order +from fixtures.market import setup_continuous_market +from datetime import datetime +from datetime import timedelta + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") +TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs") + +wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET] + + +@pytest.mark.usefixtures("vega", "page", "proposed_market", "risk_accepted") +def test_market_lifecycle(proposed_market, vega: VegaService, page: Page): + trading_mode = page.get_by_test_id("market-trading-mode").get_by_test_id( + "item-value" + ) + market_state = page.get_by_test_id("market-state").get_by_test_id("item-value") + + # setup market in proposed step, without liquidity provided + market_id = proposed_market + page.goto(f"/#/markets/{market_id}") + + # check that market is in proposed state + expect(trading_mode).to_have_text("No trading") + expect(market_state).to_have_text("Proposed") + + # approve market + governance.approve_proposal( + key_name=MM_WALLET.name, + proposal_id=market_id, + wallet=vega.wallet, + ) + + # "wait" for market to be approved and enacted + vega.forward("60s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # check that market is in pending state + expect(trading_mode).to_have_text("Opening auction") + expect(market_state).to_have_text("Pending") + + # Add liquidity and place some orders. Orders should match to produce the uncrossing price. A market can only move from opening auction to continuous trading when the enactment date has passed, there is sufficient liquidity and an uncrossing price is produced. + vega.submit_simple_liquidity( + key_name=MM_WALLET.name, + market_id=market_id, + commitment_amount=10000, + fee=0.000, + is_amendment=False, + ) + + vega.submit_order( + market_id=market_id, + trading_key=MM_WALLET.name, + side="SIDE_BUY", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1), + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + vega.submit_order( + market_id=market_id, + trading_key=MM_WALLET.name, + side="SIDE_SELL", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1), + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=99, + ) + + submit_order(vega, MM_WALLET.name, market_id, "SIDE_SELL", 1, 110) + submit_order(vega, MM_WALLET2.name, market_id, "SIDE_BUY", 1, 90) + submit_order(vega, MM_WALLET.name, market_id, "SIDE_SELL", 1, 105) + submit_order(vega, MM_WALLET2.name, market_id, "SIDE_BUY", 1, 95) + submit_order(vega, MM_WALLET.name, market_id, "SIDE_SELL", 1, 100) + submit_order(vega, MM_WALLET2.name, market_id, "SIDE_BUY", 1, 100) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # check market state is now active and trading mode is continuous + expect(trading_mode).to_have_text("Continuous") + expect(market_state).to_have_text("Active") + + # put invalid oracle to trigger market termination + governance.settle_oracle( + wallet=vega.wallet, + oracle_name="INVALID_ORACLE", + settlement_price=1, + key_name=TERMINATE_WALLET.name, + ) + vega.forward("60s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # market state should be changed to "Trading Terminated" because of the invalid oracle + expect(trading_mode).to_have_text("No trading") + expect(market_state).to_have_text("Trading Terminated") + + # settle market + vega.settle_market( + settlement_key=TERMINATE_WALLET.name, + settlement_price=100, + market_id=market_id, + ) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # check market state is now settled + expect(trading_mode).to_have_text("No trading") + expect(market_state).to_have_text("Settled") + + +@pytest.mark.usefixtures("page", "risk_accepted", "continuous_market") +def test_market_closing_banners(page: Page, continuous_market, vega: VegaService): + market_id = continuous_market + page.goto(f"/#/markets/{market_id}") + proposalID = vega.update_market_state( + continuous_market, + "mm", + vega_protos.governance.MarketStateUpdateType.MARKET_STATE_UPDATE_TYPE_TERMINATE, + approve_proposal=False, + vote_enactment_time = datetime.now() + timedelta(weeks=1), + forward_time_to_enactment = False, + price=107, + ) + may_close_warning_pattern = r"TRADING ON MARKET BTC:DAI_2023 MAY STOP ON \d+ \w+\.\s*THERE IS OPEN PROPOSAL TO CLOSE THIS MARKET\.\nProposed final price is 107\.00 BTC\.\nView proposal" + match_result = re.fullmatch(may_close_warning_pattern, page.locator(".grow").inner_text()) + assert match_result is not None + + vega.update_market_state( + continuous_market, + "mm", + vega_protos.governance.MarketStateUpdateType.MARKET_STATE_UPDATE_TYPE_TERMINATE, + approve_proposal=False, + vote_enactment_time = datetime.now() + timedelta(weeks=1), + forward_time_to_enactment = False, + price=110, + ) + + expect(page.locator(".grow")).to_have_text("Trading on Market BTC:DAI_2023 may stop. There are open proposals to close this marketView proposals") + + governance.approve_proposal( + proposal_id=proposalID, + wallet=vega.wallet, + key_name="mm" + + ) + vega.forward("60s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + + will_close_pattern = r"TRADING ON MARKET BTC:DAI_2023 WILL STOP ON \d+ \w+\nYou will no longer be able to hold a position on this market when it closes in \d+ days \d+ hours\. The final price will be 107\.00 BTC\." + match_result = re.fullmatch(will_close_pattern, page.locator(".grow").inner_text()) + assert match_result is not None + \ No newline at end of file diff --git a/apps/console-test/tests/navigation/test_navigation.py b/apps/console-test/tests/navigation/test_navigation.py new file mode 100644 index 000000000..a4bfa8614 --- /dev/null +++ b/apps/console-test/tests/navigation/test_navigation.py @@ -0,0 +1,111 @@ +import pytest +from playwright.sync_api import Page, expect, Locator + +from conftest import init_page, init_vega + + +@pytest.fixture(scope="module") +def vega(): + with init_vega() as vega: + yield vega + + +# we can reuse single page instance in all tests +@pytest.fixture(scope="module") +def page(vega, browser, request): + with init_page(vega, browser, request) as page: + yield page + + +@pytest.mark.usefixtures("risk_accepted") +def test_network_switcher(page: Page): + page.goto("/#/disclaimer") + navbar = page.locator('nav[aria-label="Main"]') + assert_network_switcher(navbar) + + +@pytest.mark.usefixtures("risk_accepted") +def test_navbar_pages(page: Page): + page.goto("/#/disclaimer") + navbar = page.locator('nav[aria-label="Main"]') + assert_links(navbar) + + +@pytest.mark.usefixtures("risk_accepted") +def test_navigation_mobile(page: Page): + page.goto("/#/disclaimer") + page.set_viewport_size({"width": 800, "height": 1040}) + navbar = page.locator('nav[aria-label="Main"]') + + # region navigation + burger = navbar.get_by_test_id("navbar-mobile-burger") + expect(burger).to_be_visible() + burger.click() + menu = navbar.get_by_test_id("navbar-menu-content") + expect(menu).to_be_visible() + assert_links(menu) + assert_network_switcher(menu) + menu.get_by_role("button", name="Close menu").click() + # endregion + + # region wallet + wallet_button = navbar.get_by_test_id("navbar-mobile-wallet") + expect(wallet_button).to_be_visible() + wallet_button.click() + dialog = page.get_by_test_id("dialog-content") + expect(dialog.get_by_test_id("wallet-dialog-title")).to_be_visible() + # endregion + + +def assert_links(container: Locator): + pages = [ + {"name": "Markets", "href": "#/markets"}, + {"name": "Trading", "href": "#/markets/"}, + {"name": "Portfolio", "href": "#/portfolio"}, + ] + + for page in pages: + link = container.get_by_role("link", name=page["name"]) + expect(link).to_be_visible() + expect(link).to_have_attribute("href", page["href"]) + + # False indicates external link configured by env var + resource_pages = [ + {"name": "Docs", "href": False}, + {"name": "Give Feedback", "href": False}, + {"name": "Disclaimer", "href": "#/disclaimer"}, + ] + + container.get_by_role("button", name="Resources").click() + + dropdown = container.get_by_test_id("navbar-content-resources") + + for resource_page in resource_pages: + page_name = resource_page["name"] + page_href = resource_page["href"] + link = dropdown.get_by_role("link", name=page_name) + expect(link).to_be_visible() + if not page_href: + href = link.get_attribute("href") + expect(link).to_have_attribute("target", "_blank") + assert len(href) >= 0, f"href for {page_name} is empty" + else: + expect(link).to_have_attribute("href", page_href) + + +def assert_network_switcher(container: Locator): + network_switcher_trigger = container.get_by_test_id( + "navbar-network-switcher-trigger" + ) + # 0006-NETW-002 + expect(network_switcher_trigger).to_have_text = "Fairground testnet" + network_switcher_trigger.click() + dropdown = container.get_by_test_id("navbar-content-network-switcher") + expect(dropdown).to_be_visible() + links = dropdown.get_by_role("link") + expect(links).to_have_count(2) + mainnet_link = container.get_by_role("link", name="Mainnet") + expect(mainnet_link).to_be_visible() + # 0006-NETW-003 + expect(mainnet_link).to_have_attribute("href", "https://console.vega.xyz") + expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible() diff --git a/apps/console-test/tests/order/test_order_details.py b/apps/console-test/tests/order/test_order_details.py new file mode 100644 index 000000000..016b79b64 --- /dev/null +++ b/apps/console-test/tests/order/test_order_details.py @@ -0,0 +1,64 @@ +import pytest +import re +from playwright.sync_api import expect, Page +from vega_sim.service import VegaService +from actions.vega import submit_order + +order_details = [ + ("order-market-label", "Market", "order-market-value", "BTC:DAI_2023"), + ("order-side-label", "Side", "order-side-value", "Short"), + ("order-type-label", "Type", "order-type-value", "Limit"), + ("order-price-label", "Price", "order-price-value", "101.00"), + ("order-size-label", "Size", "order-size-value", "-102"), + ("order-remaining-label", "Remaining", "order-remaining-value", "-2"), + ("order-status-label", "Status", "order-status-value", "Active"), + ("order-id-label", "Order ID", "order-id-value", r"^.{10}\u2026.+Copy$", True), + ( + "order-created-label", + "Created", + "order-created-value", + r"^\d{1,2}/\d{1,2}/\d{4}, \d{1,2}:\d{2}:\d{2}$", + True, + ), + ( + "order-time-in-force-label", + "Time in force", + "order-time-in-force-value", + "Good 'til Cancelled (GTC)", + ), +] + + +def verify_order_label(page: Page, test_id: str, expected_text: str): + element = page.get_by_test_id(test_id) + expect(element).to_be_visible() + expect(element).to_have_text(expected_text) + + +def verify_order_value( + page: Page, test_id: str, expected_text: str, is_regex: bool = False +): + element = page.get_by_test_id(test_id) + expect(element).to_be_visible() + if is_regex: + actual_text = element.text_content() + assert re.match( + expected_text, actual_text + ), f"Expected {expected_text}, but got {actual_text}" + else: + expect(element).to_have_text(expected_text) + +@pytest.mark.skip("tbd") +@pytest.mark.usefixtures("vega", "page", "continuous_market", "auth", "risk_accepted") +def test_order_details_are_correctly_displayed( + continuous_market, vega: VegaService, page: Page +): + page.goto(f"/#/markets/{continuous_market}") + submit_order(vega, "Key 1", vega.all_markets()[0].id, "SIDE_SELL", 102, 101, 2, 1) + page.get_by_test_id("Open").click() + page.get_by_test_id("icon-kebab").click() + page.get_by_test_id("view-order").click() + for detail in order_details: + label_id, label_text, value_id, value_text, is_regex = (*detail, False)[:5] + verify_order_label(page, label_id, label_text) + verify_order_value(page, value_id, value_text, is_regex) diff --git a/apps/console-test/tests/order/test_order_match.py b/apps/console-test/tests/order/test_order_match.py new file mode 100644 index 000000000..31f0bd5b2 --- /dev/null +++ b/apps/console-test/tests/order/test_order_match.py @@ -0,0 +1,191 @@ +import pytest +import re +from playwright.sync_api import expect, Page +from vega_sim.service import VegaService + +from playwright.sync_api import expect +from actions.vega import submit_order + +import logging + +logger = logging.getLogger() + + +# Could be turned into a helper function in the future. +def verify_data_grid(page, data_test_id, expected_pattern): + page.get_by_test_id(data_test_id).click() + # Required so that we can get liquidation price + if data_test_id == "Positions": + wait_for_graphql_response(page, "EstimatePosition") + expect( + page.locator( + f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first' + ) + ).to_be_visible() + actual_text = page.locator( + f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first' + ).text_content() + lines = actual_text.strip().split("\n") + for expected, actual in zip(expected_pattern, lines): + # We are using regex so that we can run tests in different timezones. + if re.match(r"^\\d", expected): # check if it's a regex + if re.search(expected, actual): + logger.info(f"Matched: {expected} == {actual}") + else: + logger.info(f"Not Matched: {expected} != {actual}") + raise AssertionError(f"Pattern does not match: {expected} != {actual}") + else: # it's not a regex, so we escape it + if re.search(re.escape(expected), actual): + logger.info(f"Matched: {expected} == {actual}") + else: + logger.info(f"Not Matched: {expected} != {actual}") + raise AssertionError(f"Pattern does not match: {expected} != {actual}") + + +# Required so that we can get liquidation price - Could also become a helper +def wait_for_graphql_response(page, query_name, timeout=5000): + response_data = {} + + def handle_response(route, request): + if "graphql" in request.url: + response = request.response() + if response is not None: + json_response = response.json() + if json_response and "data" in json_response: + data = json_response["data"] + if query_name in data: + response_data["data"] = data + route.continue_() + return + route.continue_() + + # Register the route handler + page.route("**", handle_response) + + # Wait for the response data to be populated + page.wait_for_timeout(timeout) + + # Unregister the route handler + page.unroute("**", handle_response) + + +def submit_order(vega, wallet_name, market_id, side, volume, price): + vega.submit_order( + trading_key=wallet_name, + market_id=market_id, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side=side, + volume=volume, + price=price, + ) + + +@pytest.mark.usefixtures( + "vega", "page", "opening_auction_market", "auth", "risk_accepted" +) +def test_limit_order_trade_open_order( + opening_auction_market, vega: VegaService, page: Page +): + market_id = opening_auction_market + submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110) + + page.goto(f"/#/markets/{market_id}") + + # Assert that the user order is displayed on the orderbook + orderbook_trade = page.get_by_test_id("price-11000000").nth(1) + # 6003-ORDB-001 + # 6003-ORDB-002 + expect(orderbook_trade).to_be_visible() + + expected_open_order = [ + "BTC:DAI_2023", + "+1", + "Limit", + "Active", + "0/1", + "110.00", + "Good 'til Cancelled (GTC)", + r"\d{1,2}/\d{1,2}/\d{4},\s*\d{1,2}:\d{2}:\d{2}\s*(?:AM|PM)", + "-", + ] + logger.info("Assert Open orders:") + verify_data_grid(page, "Open", expected_open_order) + + +@pytest.mark.usefixtures("vega", "page", "continuous_market", "auth", "risk_accepted") +def test_limit_order_trade_open_position(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + + primary_id = "stack-cell-primary" + secondary_id = "stack-cell-secondary" + + position = { + "market_code": "BTC:DAI_2023", + "settlement_asset": "tDAI", + "product_type": "Futr", + "size": "+1", + "notional": "107.50", + "average_entry_price": "107.50", + "mark_price": "107.50", + "margin": "8.50269", + "leverage": "1.0x", + "liquidation": "0.00", + "realised_pnl": "0.00", + "unrealised_pnl": "0.00", + } + + tab = page.get_by_test_id("tab-positions") + table = tab.locator(".ag-center-cols-container") + + # 7004-POSI-001 + # 7004-POSI-002 + + market = table.locator("[col-id='marketCode']") + expect(market.get_by_test_id(primary_id)).to_have_text(position["market_code"]) + expect(market.get_by_test_id(secondary_id)).to_have_text( + position["settlement_asset"] + position["product_type"] + ) + + size_and_notional = table.locator("[col-id='openVolume']") + expect(size_and_notional.get_by_test_id(primary_id)).to_have_text(position["size"]) + expect(size_and_notional.get_by_test_id(secondary_id)).to_have_text( + position["notional"] + ) + + entry_and_mark = table.locator("[col-id='markPrice']") + expect(entry_and_mark.get_by_test_id(primary_id)).to_have_text( + position["average_entry_price"] + ) + expect(entry_and_mark.get_by_test_id(secondary_id)).to_have_text( + position["mark_price"] + ) + + margin_and_leverage = table.locator("[col-id='margin']") + expect(margin_and_leverage.get_by_test_id(primary_id)).to_have_text( + position["margin"] + ) + expect(margin_and_leverage.get_by_test_id(secondary_id)).to_have_text( + position["leverage"] + ) + + liquidation = table.locator("[col-id='liquidationPrice']") + expect(liquidation.get_by_test_id("liquidation-price")).to_have_text( + position["liquidation"] + ) + + realisedPNL = table.locator("[col-id='realisedPNL']") + expect(realisedPNL).to_have_text(position["realised_pnl"]) + + unrealisedPNL = table.locator("[col-id='unrealisedPNL']") + expect(unrealisedPNL).to_have_text(position["unrealised_pnl"]) + + +@pytest.mark.usefixtures("vega", "page", "continuous_market", "auth", "risk_accepted") +def test_limit_order_trade_order_trade_away(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + # Assert that the order is no longer on the orderbook + page.get_by_test_id("Orderbook").click() + price_element = page.get_by_test_id("price-11000000").nth(1) + # 6003-ORDB-010 + expect(price_element).to_be_hidden() diff --git a/apps/console-test/tests/order/test_order_status.py b/apps/console-test/tests/order/test_order_status.py new file mode 100644 index 000000000..2487c5fe4 --- /dev/null +++ b/apps/console-test/tests/order/test_order_status.py @@ -0,0 +1,414 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService, PeggedOrder +from conftest import auth_setup, init_page, init_vega, risk_accepted_setup +from fixtures.market import setup_continuous_market, setup_simple_market +from actions.utils import wait_for_toast_confirmation + +order_tab = "tab-orders" + + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + + +@pytest.fixture(scope="module", autouse=True) +def markets(vega: VegaService): + market_1 = setup_continuous_market( + vega, + custom_market_name="market-1", + ) + market_2 = setup_continuous_market( + vega, + custom_market_name="market-2", + ) + market_3 = setup_continuous_market( + vega, + custom_market_name="market-3", + ) + market_4 = setup_continuous_market( + vega, + custom_market_name="market-4", + ) + market_5 = setup_simple_market( + vega, + custom_market_name="market-5", + ) + + vega.submit_order( + trading_key="Key 1", + market_id=market_1, + time_in_force="TIME_IN_FORCE_IOC", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=100, + price=130, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_1, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=100, + price=88, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_1, + time_in_force="TIME_IN_FORCE_IOC", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=100, + price=88, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_1, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=1e10, + price=130, + wait=False, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_2, + time_in_force="TIME_IN_FORCE_IOC", + order_type="TYPE_LIMIT", + side="SIDE_BUY", + volume=100, + price=104, + ) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_3, + time_in_force="TIME_IN_FORCE_GTT", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=10, + price=120, + expires_at=vega.get_blockchain_time() + 5 * 1e9, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + market_id=market_4, + trading_key="Key 1", + side="SIDE_BUY", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=5), + time_in_force="TIME_IN_FORCE_GTC", + volume=20, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + market_id=market_4, + trading_key="Key 1", + side="SIDE_BUY", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_BID", offset=10), + time_in_force="TIME_IN_FORCE_GTC", + volume=40, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + market_id=market_4, + trading_key="Key 1", + side="SIDE_SELL", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_ASK", offset=15), + time_in_force="TIME_IN_FORCE_GTC", + volume=60, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + market_id=market_5, + trading_key="Key 1", + side="SIDE_SELL", + order_type="TYPE_LIMIT", + pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_ASK", offset=15), + wait=False, + time_in_force="TIME_IN_FORCE_GTC", + volume=60, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_2, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=10, + price=150, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_2, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side="SIDE_SELL", + volume=10, + price=160, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + vega.submit_order( + trading_key="Key 1", + market_id=market_3, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side="SIDE_BUY", + volume=10, + price=60, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + +@pytest.fixture(scope="module") +def page(vega, browser, request): + with init_page(vega, browser, request) as page: + risk_accepted_setup(page) + auth_setup(vega, page) + page.goto("/") + page.get_by_test_id("All").click() + yield page + + +# close toast that is still opened after test +@pytest.fixture(autouse=True) +def after_each(page: Page): + yield + if page.get_by_test_id("toast-close").is_visible(): + page.get_by_test_id("toast-close").click() + + +# 7002-SORD-040 (as all the tests are about status) + + +def test_order_sorted(page: Page): + # 7003-MORD-002 + orders_update_date = page.locator( + '.ag-center-cols-container [col-id="updatedAt"]' + ).all_text_contents() + + orders_update_date_sorted = sorted(orders_update_date, reverse=True) + + assert all([a == b for a, b in zip(orders_update_date, orders_update_date_sorted)]) + + +def test_order_status_active(page: Page): + # 7002-SORD-041 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-2Futr" + "0" + "-10" + "Limit" + "Active" + "150.00" + "GTC" + ) + + +def test_status_expired(page: Page): + # 7002-SORD-042 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-3Futr" + "0" + "-10" + "Limit" + "Expired" + "120.00" + "GTT:" + ) + + +def test_order_status_Stopped(page: Page): + # 7002-SORD-044 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-1Futr" + "0" + "-100" + "Limit" + "Stopped" + "130.00" + "IOC" + ) + + +def test_order_status_partially_filled(page: Page): + # 7002-SORD-045 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-2Futr" + "99" + "+100" + "Limit" + "Partially Filled" + "104.00" + "IOC" + ) + + +def test_order_status_filled(page: Page): + # 7002-SORD-046 + # 7003-MORD-020 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-1Futr" + "100" + "-100" + "Limit" + "Filled" + "88.00" + "GTC" + ) + + +def test_order_status_rejected(page: Page): + # 7002-SORD-047 + # 7003-MORD-018 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-1Futr" + + "0" + + "-10,000,000,000" + + "Limit" + + "Rejected: Margin check failed" + + "130.00" + + "GTC" + ) + + +def test_order_status_parked(page: Page): + # 7002-SORD-048 + # 7003-MORD-016 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-5Futr" + + "0" + + "-60" + + "Ask + 15.00 Peg limit" + + "Parked" + + "0.00" + + "GTC" + ) + + +def test_order_status_pegged_ask(page: Page): + # 7003-MORD-016 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-4Futr" + + "0" + + "-60" + + "Ask + 15.00 Peg limit" + + "Active" + + "125.00" + + "GTC" + ) + + +def test_order_status_pegged_bid(page: Page): + # 7003-MORD-016 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-4Futr" + + "0" + + "+40" + + "Bid - 10.00 Peg limit" + + "Active" + + "85.00" + + "GTC" + ) + + +def test_order_status_pegged_mid(page: Page): + # 7003-MORD-016 + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-4Futr" + + "0" + + "+20" + + "Mid - 5.00 Peg limit" + + "Active" + + "97.50" + + "GTC" + ) + + +def test_order_amend_order(vega: VegaService, page: Page): + # 7002-SORD-053 + # 7003-MORD-012 + # 7003-MORD-014 + # 7003-MORD-015 + page.get_by_test_id("edit").nth(1).click() + page.locator("#limitPrice").fill("170") + page.locator("#size").fill("15") + page.get_by_role("button", name="Update").click() + + wait_for_toast_confirmation(page, timeout=5000) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-2Futr" + "0" + "-15" + "Limit" + "Active" + "170.00" + "GTC" + ) + + +def test_order_cancel_single_order(vega: VegaService, page: Page): + # 7003-MORD-009 + # 7003-MORD-010 + # 7003-MORD-011 + # 7002-SORD-043 + page.get_by_test_id("cancel").first.click() + + wait_for_toast_confirmation(page, timeout=5000) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + expect(page.get_by_test_id(order_tab)).to_contain_text( + "market-3Futr" + "0" + "+10" + "Limit" + "Cancelled" + "60.00" + "GTC" + ) + + +def test_order_cancel_all_orders(vega: VegaService, page: Page): + # 7003-MORD-009 + # 7003-MORD-010 + # 7003-MORD-011 + # 7002-SORD-043 + + page.get_by_test_id("cancelAll").click() + + wait_for_toast_confirmation(page, timeout=5000) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + expect(page.get_by_test_id("cancelAll")).not_to_be_visible() + expect(page.get_by_test_id("cancel")).not_to_be_visible() + expect( + page.locator('.ag-cell[col-id="status"]', has_text="Cancelled") + ).to_have_count(7) diff --git a/apps/console-test/tests/orderbook/test_orderbook.py b/apps/console-test/tests/orderbook/test_orderbook.py new file mode 100644 index 000000000..d25bf127e --- /dev/null +++ b/apps/console-test/tests/orderbook/test_orderbook.py @@ -0,0 +1,272 @@ +import pytest +from collections import namedtuple +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from typing import List +from actions.vega import submit_order, submit_liquidity, submit_multiple_orders +from conftest import init_vega +from fixtures.market import setup_simple_market + +# Defined namedtuples +WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"]) + +# Wallet Configurations +MM_WALLET = WalletConfig("mm", "pin") +MM_WALLET2 = WalletConfig("mm2", "pin2") + + +@pytest.fixture(scope="module") +def vega(): + with init_vega() as vega: + yield vega + + +@pytest.fixture(scope="module") +def setup_market(vega): + market_id = setup_simple_market(vega) + submit_liquidity(vega, MM_WALLET.name, market_id) + submit_multiple_orders( + vega, + MM_WALLET.name, + market_id, + "SIDE_SELL", + [[10, 130.005], [3, 130], [7, 120], [5, 110], [2, 105]], + ) + submit_multiple_orders( + vega, + MM_WALLET2.name, + market_id, + "SIDE_BUY", + [[10, 69.995], [5, 70], [5, 85], [3, 90], [3, 95]], + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + return [ + vega, + market_id, + ] + + +# these values don't align with the multiple orders above as +# creating a trade triggers the liquidity provision +orderbook_content = [ + [130.00500, 10, 94], + [130.00000, 3, 84], + [120.00000, 7, 81], + [110.00000, 5, 74], + [105.00000, 2, 69], + [101.00000, 67, 67], + # mid + [99.00000, 102, 102], + [95.00000, 3, 105], + [90.00000, 3, 108], + [85.00000, 5, 113], + [70.00000, 5, 118], + [69.99500, 10, 128], +] + + +def verify_orderbook_grid( + page: Page, content: List[List[float]], last_trade_price: float = False +): + rows = page.locator("[data-testid$=-rows-container]").all() + for row_index, content_row in enumerate(content): + cells = rows[row_index].locator("button").all() + for cell_index, content_cell in enumerate(content_row): + assert float(cells[cell_index].text_content()) == content_cell + + +def verify_prices_descending(page: Page): + prices_locator = page.get_by_test_id("tab-orderbook").locator( + '[data-testid^="price-"]' + ) + prices_locator.first.wait_for(state="visible") + prices = [float(price.text_content()) for price in prices_locator.all()] + assert prices == sorted(prices, reverse=True) + +@pytest.mark.skip("tbd") +@pytest.mark.usefixtures("page", "risk_accepted") +def test_orderbook_grid_content(setup_market, page: Page): + vega = setup_market[0] + market_id = setup_market[1] + + # Create a so that lastTradePrice is shown in the mid section + # of the book + matching_order = [1, 100] + submit_order( + vega, + MM_WALLET.name, + market_id, + "SIDE_SELL", + matching_order[0], + matching_order[1], + ) + submit_order( + vega, + MM_WALLET2.name, + market_id, + "SIDE_BUY", + matching_order[0], + matching_order[1], + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # 6003-ORDB-001 + # 6003-ORDB-002 + # 6003-ORDB-003 + # 6003-ORDB-004 + # 6003-ORDB-005 + # 6003-ORDB-006 + # 6003-ORDB-007 + page.goto(f"/#/markets/{market_id}") + + page.locator("[data-testid=Orderbook]").click() + + # 6003-ORDB-013 + assert ( + float(page.locator("[data-testid*=last-traded]").text_content()) + == matching_order[1] + ) + + # 6003-ORDB-011 + # get the spread text trimming off the parentheses on either end + spread_text = page.locator("[data-testid=spread]").text_content()[1:-1] + assert ( + # TODO: figure out how to not have hardcoded value + spread_text + == "2.00" + ) + + verify_orderbook_grid(page, orderbook_content) + verify_prices_descending(page) + + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_orderbook_resolution_change(setup_market, page: Page): + market_id = setup_market[1] + # 6003-ORDB-008 + orderbook_content_0_00 = [ + [130.01, 10, 94], + [130.00, 3, 84], + [120.00, 7, 81], + [110.00, 5, 74], + [105.00, 2, 69], + [101.00, 67, 67], + # mid + [99.00, 102, 102], + [95.00, 3, 105], + [90.00, 3, 108], + [85.00, 5, 113], + [70.00, 15, 128], + ] + + orderbook_content_10 = [ + [130, 13, 94], + [120, 7, 81], + [110, 7, 74], + [100, 67, 67], + # mid + [100, 105, 105], + [90, 8, 113], + [70, 15, 128], + ] + + orderbook_content_100 = [ + [100, 94, 94], + # mid + [100, 128, 128], + ] + + resolutions = [ + ["0.00", orderbook_content_0_00], + ["10", orderbook_content_10], + ["100", orderbook_content_100], + ] + + page.goto(f"/#/markets/{market_id}") + # temporary skip + # for resolution in resolutions: + # page.get_by_test_id("resolution").click() + # page.get_by_role("menu").get_by_text(resolution[0], exact=True).click() + # verify_orderbook_grid(page, resolution[1]) + + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_orderbook_price_size_copy(setup_market, page: Page): + market_id = setup_market[1] + # 6003-ORDB-009 + prices = page.get_by_test_id("tab-orderbook").locator('[data-testid^="price-"]') + volumes = page.get_by_test_id("tab-orderbook").locator('[data-testid*="-vol-"]') + + page.goto(f"/#/markets/{market_id}") + prices.first.wait_for(state="visible") + + for price in prices.all(): + price.click() + expect(page.get_by_test_id("order-price")).to_have_value(price.text_content()) + + for volume in volumes.all(): + volume.click() + expect(page.get_by_test_id("order-size")).to_have_value(volume.text_content()) + +@pytest.mark.skip("tbd") +@pytest.mark.usefixtures("page", "risk_accepted") +def test_orderbook_price_movement(setup_market, page: Page): + vega = setup_market[0] + market_id = setup_market[1] + + page.goto(f"/#/markets/{market_id}") + page.locator("[data-testid=Orderbook]").click() + + book_el = page.locator("[data-testid=orderbook-grid-element]") + + # no arrow shown on load + expect(book_el.locator("[data-testid^=icon-arrow]")).not_to_be_attached() + + matching_order_1 = [1, 101] + submit_order( + vega, + MM_WALLET2.name, + market_id, + "SIDE_BUY", + matching_order_1[0], + matching_order_1[1], + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # 6003-ORDB-013 + expect(book_el.locator("[data-testid=icon-arrow-up]")).to_be_attached() + assert ( + float(page.locator("[data-testid*=last-traded]").text_content()) + == matching_order_1[1] + ) + + matching_order_2 = [1, 99] + submit_order( + vega, + MM_WALLET2.name, + market_id, + "SIDE_SELL", + matching_order_2[0], + matching_order_2[1], + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + expect(book_el.locator("[data-testid=icon-arrow-down]")).to_be_attached() + + assert ( + float(page.locator("[data-testid*=last-traded]").text_content()) + == matching_order_2[1] + ) diff --git a/apps/console-test/tests/pnl/test_pnl.py b/apps/console-test/tests/pnl/test_pnl.py new file mode 100644 index 000000000..ad001f116 --- /dev/null +++ b/apps/console-test/tests/pnl/test_pnl.py @@ -0,0 +1,143 @@ +import pytest +from playwright.sync_api import Page +from vega_sim.service import VegaService +from actions.vega import submit_order + + +def wait_for_graphql_response(page, query_name, timeout=5000): + response_data = {} + + def handle_response(route, request): + if "graphql" in request.url: + response = request.response() + if response is not None: + json_response = response.json() + if json_response and "data" in json_response: + data = json_response["data"] + if query_name in data: + response_data["data"] = data + route.continue_() + return + route.continue_() + + # Register the route handler + page.route("**", handle_response) + + # Wait for the response data to be populated + page.wait_for_timeout(timeout) + + # Unregister the route handler + page.unroute("**", handle_response) + + +def check_pnl_color_value(element, expected_color, expected_value): + color = element.evaluate("element => getComputedStyle(element).color") + value = element.inner_text() + assert color == expected_color, f"Unexpected color: {color}" + assert value == expected_value, f"Unexpected value: {value}" + +@pytest.mark.usefixtures("vega", "page", "continuous_market", "auth", "risk_accepted") +def test_pnl(continuous_market, vega: VegaService, page: Page): + page.set_viewport_size({"width": 1748, "height": 977}) + submit_order(vega, "Key 1", continuous_market, "SIDE_BUY", 1, 104.50000) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.goto(f"/#/markets/{continuous_market}") + # Loss Trading unrealised + row = ( + page.get_by_test_id("tab-positions") + .locator(".ag-center-cols-container .ag-row") + .nth(0) + ) + realised_pnl = row.locator("[col-id='realisedPNL']") + unrealised_pnl = row.locator("[col-id='unrealisedPNL']") + + check_pnl_color_value(realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(unrealised_pnl, "rgb(236, 0, 60)", "-4.00") + + # profit Trading unrealised + page.get_by_test_id("manage-vega-wallet").click() + page.locator('[role="menuitemradio"] >> .mr-2.uppercase').nth(1).click(position={ "x": 0, "y": 0}, force=True) + check_pnl_color_value(realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(unrealised_pnl, "rgb(1, 145, 75)", "4.00") + + # neutral Trading unrealised + page.locator('[role="menuitemradio"] >> .mr-2.uppercase').nth(2).click(position={ "x": 0, "y": 0}, force=True) + check_pnl_color_value(realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + # Portfolio Unrealised + page.get_by_test_id("manage-vega-wallet").click(force=True) + page.get_by_role("link", name="Portfolio").click() + page.get_by_test_id("Positions").click() + wait_for_graphql_response(page, "EstimatePosition") + page.wait_for_selector( + '[data-testid="tab-positions"] .ag-center-cols-container .ag-row', + state="visible", + ) + + key_1 = page.query_selector( + '//div[@role="row" and .//div[@col-id="partyId"]/div/span[text()="Key 1"]]' + ) + key_mm = page.query_selector( + '//div[@role="row" and .//div[@col-id="partyId"]/div/span[text()="mm"]]' + ) + key_mm2 = page.query_selector( + '//div[@role="row" and .//div[@col-id="partyId"]/div/span[text()="mm2"]]' + ) + + key_1_unrealised_pnl = key_1.query_selector('xpath=./div[@col-id="unrealisedPNL"]') + key_1_realised_pnl = key_1.query_selector('xpath=./div[@col-id="realisedPNL"]') + key_mm_unrealised_pnl = key_mm.query_selector('xpath=./div[@col-id="unrealisedPNL"]') + key_mm_realised_pnl = key_mm.query_selector('xpath=./div[@col-id="realisedPNL"]') + key_mm2_unrealised_pnl = key_mm2.query_selector('xpath=./div[@col-id="unrealisedPNL"]') + key_mm2_realised_pnl = key_mm2.query_selector('xpath=./div[@col-id="realisedPNL"]') + check_pnl_color_value(key_1_realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(key_1_unrealised_pnl, "rgb(236, 0, 60)", "-4.00") + + check_pnl_color_value(key_mm_realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(key_mm_unrealised_pnl, "rgb(1, 145, 75)", "4.00") + + check_pnl_color_value(key_mm2_realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(key_mm2_unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + submit_order(vega, "Key 1", continuous_market, "SIDE_SELL", 2, 101.50000) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + wait_for_graphql_response(page, "EstimatePosition") + + check_pnl_color_value(key_1_realised_pnl, "rgb(236, 0, 60)", "-8.00") + check_pnl_color_value(key_1_unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + check_pnl_color_value(key_mm_realised_pnl, "rgb(1, 145, 75)", "8.00") + check_pnl_color_value(key_mm_unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + check_pnl_color_value(key_mm2_realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(key_mm2_unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + page.get_by_role("link", name="Trading").click() + + row = ( + page.get_by_test_id("tab-positions") + .locator(".ag-center-cols-container .ag-row") + .nth(0) + ) + realised_pnl = row.locator("[col-id='realisedPNL']") + unrealised_pnl = row.locator("[col-id='unrealisedPNL']") + + # neutral trading realised + check_pnl_color_value(realised_pnl, "rgb(0, 0, 0)", "0.00") + check_pnl_color_value(unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + # profit trading realised + page.get_by_test_id("manage-vega-wallet").click() + page.locator('[role="menuitemradio"] >> .mr-2.uppercase').nth(1).click(position={ "x": 0, "y": 0}, force=True) + check_pnl_color_value(realised_pnl, "rgb(1, 145, 75)", "8.00") + check_pnl_color_value(unrealised_pnl, "rgb(0, 0, 0)", "0.00") + + # loss trading realised + page.locator('[role="menuitemradio"] >> .mr-2.uppercase').nth(0).click(position={ "x": 0, "y": 0}, force=True) + check_pnl_color_value(realised_pnl, "rgb(236, 0, 60)", "-8.00") + check_pnl_color_value(unrealised_pnl, "rgb(0, 0, 0)", "0.00") diff --git a/apps/console-test/tests/portfolio/test_ledger_entries.py b/apps/console-test/tests/portfolio/test_ledger_entries.py new file mode 100644 index 000000000..b6237f50c --- /dev/null +++ b/apps/console-test/tests/portfolio/test_ledger_entries.py @@ -0,0 +1,31 @@ +import os +import pytest +from playwright.sync_api import Page, expect + +from actions.utils import wait_for_toast_confirmation + +@pytest.mark.usefixtures("page", "auth", "risk_accepted", "continuous_market") +def test_ledger_entries_downloads(page: Page): + page.goto("/#/portfolio") + page.get_by_test_id("Ledger entries").click() + expect(page.get_by_test_id("ledger-download-button")).to_be_enabled() + # 7007-LEEN-001 + page.get_by_test_id("ledger-download-button").click() + #7007-LEEN-009 + expect(page.get_by_test_id("toast-content")).to_contain_text(("Your file is ready")) + # Get the user's Downloads directory + downloads_directory = os.path.expanduser("~") + "/Downloads/" + # Start waiting for the download + with page.expect_download() as download_info: + # Perform the action that initiates download + page.get_by_role("link", name="Get file here").click() + + + download = download_info.value + # Wait for the download process to complete and save the downloaded file in the Downloads directory + download.save_as(os.path.join(downloads_directory, download.suggested_filename)) + + # Verify the download by asserting that the file exists + downloaded_file_path = os.path.join(downloads_directory, download.suggested_filename) + assert os.path.exists(downloaded_file_path), f"Download failed! File not found at: {downloaded_file_path}" + diff --git a/apps/console-test/tests/positions/test_collateral.py b/apps/console-test/tests/positions/test_collateral.py new file mode 100644 index 000000000..3a6ff19c4 --- /dev/null +++ b/apps/console-test/tests/positions/test_collateral.py @@ -0,0 +1,51 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from conftest import init_vega +from fixtures.market import setup_continuous_market + +TOOLTIP_LABEL = "margin-health-tooltip-label" +TOOLTIP_VALUE = "margin-health-tooltip-value" +COL_ID_USED = ".ag-center-cols-container [col-id='used'] .ag-cell-value" + +@pytest.fixture(scope="module") +def vega(request): + with init_vega(request) as vega: + yield vega + +@pytest.fixture(scope="module") +def continuous_market(vega: VegaService): + return setup_continuous_market(vega) + +@pytest.mark.usefixtures("auth", "risk_accepted") +def test_usage_breakdown(continuous_market, page: Page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("Collateral").click() + page.locator(".ag-floating-top-container .ag-row [col-id='used']").click() + usage_breakdown = page.get_by_test_id('usage-breakdown') + + # Verify headers + headers = ['Market', 'Account type', 'Balance', 'Margin health'] + ag_headers = usage_breakdown.locator('.ag-header-cell-text').element_handles() + for i, header_element in enumerate(ag_headers): + header_text = header_element.text_content() + assert header_text == headers[i] + + # Other expectations + expect(usage_breakdown.locator('[class="mb-2 text-sm"]')).to_have_text("You have 1,000,000.00 tDAI in total.") + expect(usage_breakdown.locator(COL_ID_USED).first).to_have_text("8.50269 (0%)") + expect(usage_breakdown.locator(COL_ID_USED).nth(1)).to_have_text("999,991.49731 (99%)") + + # Maintenance Level + expect(usage_breakdown.locator(".ag-center-cols-container [col-id='market.id'] .ag-cell-value").first).to_have_text("2.85556 above maintenance level") + + # Margin health tooltip + usage_breakdown.get_by_test_id("margin-health-chart-track").hover() + tooltip_data = [("maintenance level", "5.64713"), ("search level", "6.21184"), ("initial level", "8.47069"), ("balance", "8.50269"), ("release level", "9.60012")] + + for index, (label, value) in enumerate(tooltip_data): + expect(page.get_by_test_id(TOOLTIP_LABEL).nth(index)).to_have_text(label) + expect(page.get_by_test_id(TOOLTIP_VALUE).nth(index)).to_have_text(value) + + + page.get_by_test_id('dialog-close').click() diff --git a/apps/console-test/tests/positions/test_positions.py b/apps/console-test/tests/positions/test_positions.py new file mode 100644 index 000000000..77904fccf --- /dev/null +++ b/apps/console-test/tests/positions/test_positions.py @@ -0,0 +1,29 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from fixtures.market import ( + setup_continuous_market, +) + +@pytest.mark.usefixtures("auth", "risk_accepted") +def test_closed_market_position(vega: VegaService, page: Page): + market_id = setup_continuous_market(vega) + + vega.settle_market( + settlement_key="FJMKnwfZdd48C8NqvYrG", + settlement_price=110, + market_id=market_id, + ) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + page.goto(f"/#/markets/{market_id}") + expect(page.locator(".ag-overlay-panel")).to_have_text("No positions") + page.get_by_test_id("open-transfer").click() + tab = page.get_by_test_id("tab-positions") + table = tab.locator(".ag-center-cols-container") + market = table.locator("[col-id='marketCode']") + expect(market.get_by_test_id("stack-cell-primary")).to_have_text("BTC:DAI_2023") + page.get_by_test_id("open-transfer").click() + expect(page.locator(".ag-overlay-panel")).to_have_text("No positions") + \ No newline at end of file diff --git a/apps/console-test/tests/settings/test_settings.py b/apps/console-test/tests/settings/test_settings.py new file mode 100644 index 000000000..e32ff8c1b --- /dev/null +++ b/apps/console-test/tests/settings/test_settings.py @@ -0,0 +1,61 @@ +import pytest +from playwright.sync_api import expect, Page +from conftest import init_vega + + +@pytest.fixture(scope="module") +def vega(): + with init_vega() as vega: + yield vega + + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_share_usage_data(page: Page): + page.goto("/") + # page.get_by_test_id("icon-cross").click() + page.get_by_test_id("Settings").click() + telemetry_switch = page.locator("#switch-settings-telemetry-switch") + expect(telemetry_switch).to_have_attribute("data-state", "unchecked") + + telemetry_switch.click() + expect(telemetry_switch).to_have_attribute("data-state", "checked") + page.reload() + page.get_by_test_id("Settings").click() + expect(telemetry_switch).to_have_attribute("data-state", "unchecked") + + telemetry_switch.click() + expect(telemetry_switch).to_have_attribute("data-state", "checked") + page.reload() + page.get_by_test_id("Settings").click() + expect(telemetry_switch).to_have_attribute("data-state", "unchecked") + + +# Define a mapping of icon selectors to toast selectors +ICON_TO_TOAST = { + 'aria-label="arrow-top-left icon"': 'class="group absolute z-20 top-0 left-0 max-w-full max-h-full overflow-x-hidden overflow-y-auto p-4"', + 'aria-label="arrow-up icon"': 'class="group absolute z-20 top-0 left-[50%] translate-x-[-50%] max-w-full max-h-full overflow-x-hidden overflow-y-auto p-4"', + 'aria-label="arrow-top-right icon"': 'class="group absolute z-20 top-0 right-0 max-w-full max-h-full overflow-x-hidden overflow-y-auto p-4"', + 'aria-label="arrow-bottom-left icon"': 'class="group absolute z-20 bottom-0 left-0 max-w-full max-h-full overflow-x-hidden overflow-y-auto p-4"', + 'aria-label="arrow-down icon"': 'class="group absolute z-20 bottom-0 left-[50%] translate-x-[-50%] max-w-full max-h-full overflow-x-hidden overflow-y-auto p-4"', + 'aria-label="arrow-bottom-right icon"': 'class="group absolute z-20 bottom-0 right-0 max-w-full max-h-full overflow-x-hidden overflow-y-auto p-4"', +} + + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_toast_positions(page: Page): + page.goto("/") + page.get_by_test_id("Settings").click() + for icon_selector, toast_selector in ICON_TO_TOAST.items(): + # Click the icon + page.click(f"[{icon_selector}]") + # Expect that the toast is displayed + expect(page.locator(f"[{toast_selector}]")).to_be_visible() + + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_dark_mode(page: Page): + page.goto("/") + page.get_by_test_id("Settings").click() + expect(page.locator("html")).not_to_have_attribute("class", "dark") + page.locator("#switch-settings-theme-switch").click() + expect(page.locator("html")).to_have_attribute("class", "dark") diff --git a/apps/console-test/tests/successor_market/test_succession_line.py b/apps/console-test/tests/successor_market/test_succession_line.py new file mode 100644 index 000000000..e4bef8962 --- /dev/null +++ b/apps/console-test/tests/successor_market/test_succession_line.py @@ -0,0 +1,47 @@ +import pytest +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from fixtures.market import setup_continuous_market, setup_simple_successor_market + + +@pytest.fixture +@pytest.mark.usefixtures("vega") +def successor_market(vega: VegaService): + parent_market_id = setup_continuous_market(vega) + tdai_id = vega.find_asset_id(symbol="tDAI") + successor_market_id = setup_simple_successor_market( + vega, parent_market_id, tdai_id, "successor_market" + ) + vega.settle_market( + settlement_key="FJMKnwfZdd48C8NqvYrG", + settlement_price=110, + market_id=parent_market_id, + ) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + return successor_market_id + + + +@pytest.mark.usefixtures("page", "risk_accepted") +def test_succession_line(page: Page, successor_market): + page.goto(f"/#/markets/{successor_market}") + page.get_by_test_id("Info").click() + page.get_by_text("Succession line").click() + + expect(page.get_by_test_id("succession-line-item").first).to_contain_text( + "BTC:DAI_2023BTC:DAI_2023" + ) + expect( + page.get_by_test_id("succession-line-item").first.get_by_role("link") + ).to_be_attached + expect(page.get_by_test_id("succession-line-item").last).to_contain_text( + "successor_marketsuccessor_market" + ) + expect( + page.get_by_test_id("succession-line-item").last.get_by_role("link") + ).to_be_attached + expect( + page.get_by_test_id("succession-line-item").last.get_by_test_id("icon-bullet") + ).to_be_visible diff --git a/apps/console-test/tests/trade_history/test_trade_history.py b/apps/console-test/tests/trade_history/test_trade_history.py new file mode 100644 index 000000000..0608159e3 --- /dev/null +++ b/apps/console-test/tests/trade_history/test_trade_history.py @@ -0,0 +1,106 @@ +import pytest +import re +from playwright.sync_api import expect +from actions.vega import submit_order +from conftest import init_vega +import logging + +logger = logging.getLogger() + + +@pytest.fixture(scope="module") +def vega(): + with init_vega() as vega: + yield vega + + +# Could be turned into a helper function in the future. +def verify_data_grid(page, data_test_id, expected_pattern): + page.get_by_test_id(data_test_id).click() + # Required so that we can get liquidation price + if data_test_id == "Positions": + wait_for_graphql_response(page, "EstimatePosition") + expect( + page.locator( + f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first' + ) + ).to_be_visible() + actual_text = page.locator( + f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container' + ).text_content() + lines = actual_text.strip().split("\n") + for expected, actual in zip(expected_pattern, lines): + # We are using regex so that we can run tests in different timezones. + if re.match(r"^\\d", expected): # check if it's a regex + if re.search(expected, actual): + logger.info(f"Matched: {expected} == {actual}") + else: + logger.info(f"Not Matched: {expected} != {actual}") + raise AssertionError(f"Pattern does not match: {expected} != {actual}") + else: # it's not a regex, so we escape it + if re.search(re.escape(expected), actual): + logger.info(f"Matched: {expected} == {actual}") + else: + logger.info(f"Not Matched: {expected} != {actual}") + raise AssertionError(f"Pattern does not match: {expected} != {actual}") + + +# Required so that we can get liquidation price - Could also become a helper +def wait_for_graphql_response(page, query_name, timeout=5000): + response_data = {} + + def handle_response(route, request): + if "graphql" in request.url: + response = request.response() + if response is not None: + json_response = response.json() + if json_response and "data" in json_response: + data = json_response["data"] + if query_name in data: + response_data["data"] = data + route.continue_() + return + route.continue_() + + # Register the route handler + page.route("**", handle_response) + + # Wait for the response data to be populated + page.wait_for_timeout(timeout) + + # Unregister the route handler + page.unroute("**", handle_response) + + +@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted") +def test_limit_order_new_trade_top_of_list(continuous_market, vega, page): + submit_order(vega, "Key 1", continuous_market, "SIDE_BUY", 1, 110) + page.goto(f"/#/markets/{continuous_market}") + + vega.forward("10s") + vega.wait_fn(10) + vega.wait_for_total_catchup() + expected_trade = [ + "103.50", + "1", + r"\d{1,2}/\d{1,2}/\d{4},\s*\d{1,2}:\d{2}:\d{2}\s*(?:AM|PM)" "107.50", + "1", + r"\d{1,2}/\d{1,2}/\d{4},\s*\d{1,2}:\d{2}:\d{2}\s*(?:AM|PM)", + ] + # 6005-THIS-001 + # 6005-THIS-002 + # 6005-THIS-003 + # 6005-THIS-004 + # 6005-THIS-005 + # 6005-THIS-006 + verify_data_grid(page, "Trades", expected_trade) + + +@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted") +def test_price_copied_to_deal_ticket(continuous_market, page): + page.goto(f"/#/markets/{continuous_market}") + page.get_by_test_id("Trades").click() + wait_for_graphql_response(page, "Trades") + page.locator("[col-id=price]").last.click() + # 6005-THIS-007 + expect(page.get_by_test_id("order-price")).to_have_value("107.50000") diff --git a/apps/console-test/tests/trade_match/test_trade_match.py b/apps/console-test/tests/trade_match/test_trade_match.py new file mode 100644 index 000000000..9df8b251b --- /dev/null +++ b/apps/console-test/tests/trade_match/test_trade_match.py @@ -0,0 +1,214 @@ +import pytest +from playwright.sync_api import expect, Page +from vega_sim.service import VegaService + +from actions.vega import submit_multiple_orders + +@pytest.mark.skip("tbd") +@pytest.mark.usefixtures( + "page", "vega", "opening_auction_market", "auth", "risk_accepted" +) +def test_trade_match_table(opening_auction_market: str, vega: VegaService, page: Page): + row_locator = ".ag-center-cols-container .ag-row" + page.goto(f"/#/markets/{opening_auction_market}") + + # sending order to be rejected, wait=False to avoid returning error from market-sim + vega.submit_order( + trading_key="Key 1", + market_id=opening_auction_market, + time_in_force="TIME_IN_FORCE_GTC", + order_type="TYPE_LIMIT", + side="SIDE_BUY", + volume=1, + price=10e15, + wait=False, + ) + + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + submit_multiple_orders( + vega, + "Key 1", + opening_auction_market, + "SIDE_BUY", + [[5, 110], [5, 105], [1, 50]], + ) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + submit_multiple_orders( + vega, + "Key 1", + opening_auction_market, + "SIDE_SELL", + [[5, 90], [5, 95], [1, 150]], + ) + vega.forward("60s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + + # Positions + position = { + "market_code": "BTC:DAI_2023", + "settlement_asset": "tDAI", + "product_type": "Futr", + "size": "+2", + "notional": "220.00", + "average_entry_price": "110.00", + "mark_price": "110.00", + "margin": "93.52953", + "leverage": "1.0x", + "liquidation": "0.00", + "realised_pnl": "0.00", + "unrealised_pnl": "0.00", + } + page.goto(f"/#/markets/{opening_auction_market}") + # 7004-POSI-001 + # 7004-POSI-002 + primary_id = "stack-cell-primary" + secondary_id = "stack-cell-secondary" + + tab = page.get_by_test_id("tab-positions") + table = tab.locator(".ag-center-cols-container") + + market = table.locator("[col-id='marketCode']") + expect(market.get_by_test_id(primary_id)).to_have_text(position["market_code"]) + expect(market.get_by_test_id(secondary_id)).to_have_text( + position["settlement_asset"] + position["product_type"] + ) + size_and_notional = table.locator("[col-id='openVolume']") + expect(size_and_notional.get_by_test_id(primary_id)).to_have_text(position["size"]) + expect(size_and_notional.get_by_test_id(secondary_id)).to_have_text( + position["notional"] + ) + + entry_and_mark = table.locator("[col-id='markPrice']") + expect(entry_and_mark.get_by_test_id(primary_id)).to_have_text( + position["average_entry_price"] + ) + expect(entry_and_mark.get_by_test_id(secondary_id)).to_have_text( + position["mark_price"] + ) + + margin_and_leverage = table.locator("[col-id='margin']") + expect(margin_and_leverage.get_by_test_id(primary_id)).to_have_text( + position["margin"] + ) + expect(margin_and_leverage.get_by_test_id(secondary_id)).to_have_text( + position["leverage"] + ) + liquidation = table.locator("[col-id='liquidationPrice']") + expect(liquidation.get_by_test_id("liquidation-price")).to_have_text( + position["liquidation"] + ) + + realisedPNL = table.locator("[col-id='realisedPNL']") + expect(realisedPNL).to_have_text(position["realised_pnl"]) + + unrealisedPNL = table.locator("[col-id='unrealisedPNL']") + expect(unrealisedPNL).to_have_text(position["unrealised_pnl"]) + + # Open + page.get_by_test_id("Open").click() + rows = page.get_by_test_id("tab-open-orders").locator(row_locator).all() + expect(rows[0]).to_contain_text( + "BTC:DAI_2023Futr" + "0" + "-1" + "Limit" + "Active" + "150.00" + "GTC" + ) + expect(rows[1]).to_contain_text( + "BTC:DAI_2023Futr" + "0" + "+1" + "Limit" + "Active" + "50.00" + "GTC" + ) + expect(rows[2]).to_contain_text( + "BTC:DAI_2023Futr" + "0" + "+5" + "Limit" + "Active" + "105.00" + "GTC" + ) + + # Closed + page.get_by_test_id("Closed").click() + rows = page.get_by_test_id("tab-closed-orders").locator(row_locator).all() + expect(rows[0]).to_contain_text( + "BTC:DAI_2023Futr" + "0" + "-5" + "Limit" + "Filled" + "95.00" + "GTC" + ) + expect(rows[1]).to_contain_text( + "BTC:DAI_2023Futr" + "5" + "-5" + "Limit" + "Filled" + "90.00" + "GTC" + ) + expect(rows[2]).to_contain_text( + "BTC:DAI_2023Futr" + "5" + "+5" + "Limit" + "Filled" + "110.00" + "GTC" + ) + + # Rejected + page.get_by_test_id("Rejected").click() + expect( + page.get_by_test_id("tab-rejected-orders").locator(row_locator) + ).to_contain_text( + "BTC:DAI_2023Futr" + + "0" + + "+1" + + "Limit" + + "Rejected: Margin check failed" + + "10,000,000,000,000,000.00" + + "GTC" + ) + + # All + page.get_by_test_id("All").click() + rows = page.get_by_test_id("tab-orders").locator(row_locator).all() + expect(rows[0]).to_contain_text( + "BTC:DAI_2023Futr" + "0" + "-1" + "Limit" + "Active" + "150.00" + "GTC" + ) + expect(rows[1]).to_contain_text( + "BTC:DAI_2023Futr" + "5" + "-5" + "Limit" + "Filled" + "95.00" + "GTC" + ) + expect(rows[2]).to_contain_text( + "BTC:DAI_2023Futr" + "5" + "-5" + "Limit" + "Filled" + "90.00" + "GTC" + ) + expect(rows[3]).to_contain_text( + "BTC:DAI_2023Futr" + + "0" + + "+1" + + "Limit" + + "Rejected: Margin check failed" + + "10,000,000,000,000,000.00" + + "GTC" + ) + expect(rows[4]).to_contain_text( + "BTC:DAI_2023Futr" + "0" + "+1" + "Limit" + "Active" + "50.00" + "GTC" + ) + expect(rows[5]).to_contain_text( + "BTC:DAI_2023Futr" + "1" + "+5" + "Limit" + "Active" + "105.00" + "GTC" + ) + expect(rows[6]).to_contain_text( + "BTC:DAI_2023Futr" + "5" + "+5" + "Limit" + "Filled" + "110.00" + "GTC" + ) + + # Stop Orders + page.get_by_test_id("Stop orders").click() + expect(page.get_by_test_id("tab-stop-orders")).to_be_visible() + expect(page.get_by_test_id("tab-stop-orders").locator(row_locator)).to_be_visible( + visible=False + ) + + # Fills + page.get_by_test_id("Fills").click() + rows = page.get_by_test_id("tab-fills").locator(row_locator).all() + expect(rows[0]).to_contain_text( + "BTC:DAI_2023Futr" + + "-5" + + "106.50 tDAI" + + "532.50 tDAI" + + "Taker" + + "53.51625 tDAI" + ) + expect(rows[1]).to_contain_text( + "BTC:DAI_2023Futr" + "+1" + "105.00 tDAI" + "105.00 tDAI" + "-" + "0.00 tDAI" + ) + expect(rows[2]).to_contain_text( + "BTC:DAI_2023Futr" + "+5" + "105.00 tDAI" + "525.00 tDAI" + "-" + "0.00 tDAI" + ) + + # Collateral + page.get_by_test_id("Collateral").click() + expect( + page.get_by_test_id("tab-accounts").locator(".ag-floating-top-viewport .ag-row") + ).to_contain_text("tDAI" + "43.94338" + "0.00%" + "999,904.04037" + "999,947.98375") diff --git a/apps/console-test/tests/trading_chart/test_trading_chart.py b/apps/console-test/tests/trading_chart/test_trading_chart.py new file mode 100644 index 000000000..1295cd890 --- /dev/null +++ b/apps/console-test/tests/trading_chart/test_trading_chart.py @@ -0,0 +1,139 @@ +# import pytest +# import re +# from collections import namedtuple +# from playwright.sync_api import Page +# from vega_sim.service import VegaService +# from actions.vega import submit_order + +# import logging + +# logger = logging.getLogger() + +# InfoItem = namedtuple('InfoItem', ['name', 'infoText']) + +# @pytest.mark.skip("temporary skip") +# @pytest.mark.parametrize("vega", [120], indirect=True) +# @pytest.mark.usefixtures("continuous_market","risk_accepted", "auth") +# def test_trading_chart(continuous_market, vega: VegaService, page: Page): +# page.goto(f"/#/markets/{continuous_market}") +# vega.forward("24h") +# vega.wait_for_total_catchup() +# submit_order(vega, "mm", continuous_market, "SIDE_SELL", 1, 101.50000) +# submit_order(vega, "mm2", continuous_market, "SIDE_SELL", 1, 101.50000) +# vega.forward("10s") +# vega.wait_for_total_catchup() + + +# page.click("button[aria-haspopup='menu']:has-text('Interval:')") +# page.click(f"div[role='menuitemradio']:text-is('15m')") +# page.wait_for_selector(".indicator-info-wrapper:visible") +# # Check chart views and select +# chart = "[aria-label$='chart icon']" +# valid_chart_views = ['Mountain', 'Candlestick', 'Line', 'OHLC'] +# #6004-CHAR-002 +# #6004-CHAR-003 +# check_menu_items(page, chart, valid_chart_views, 'Candlestick') + +# # Check study info +# study_info = [ +# InfoItem("Eldar-ray","Eldar-ray: Bull -5.14286Bear -5.14286"), +# InfoItem("Force index", "Force index: -0.85714"), +# InfoItem("MACD", "MACD: S -0.09573D -0.38291MACD -0.47863"), +# InfoItem("RSI", "RSI: 0.00000"), +# InfoItem("Volume", "Volume: 1") +# ] +# """Preparation steps to check study info on the page.""" +# element = page.locator(".plot-area-interaction").nth(1) +# element.hover() + +# page.click(".pane__close-button-wrapper") + +# info_items = page.query_selector_all(".plot-area") + +# assert (len(info_items)) == 1 +# #6004-CHAR-005 +# #6004-CHAR-006 +# #6004-CHAR-007 +# #6004-CHAR-042 +# #6004-CHAR-045 +# #6004-CHAR-047 +# #6004-CHAR-049 +# #6004-CHAR-051 +# page.mouse.wheel(0, 10) +# check_menu_item_checkbox(page, "Studies", study_info) +# page.get_by_text("Studies").click(force=True) + + +# # Check overlay info +# overlay_info = [ +# InfoItem("Bollinger bands", "Bollinger: Upper 110.69473Lower 103.10527"), +# InfoItem("Envelope", "Envelope: Upper 111.65000Lower 91.35000"), +# InfoItem("EMA", "EMA: 106.30000"), +# InfoItem("Moving average", "Moving average: 106.90000"), +# InfoItem("Price monitoring bounds", "Price Monitoring Bounds 1: Min 83.11038Max 138.66685Reference 107.50000") +# ] +# #6004-CHAR-004 +# #6004-CHAR-008 +# #6004-CHAR-009 +# #6004-CHAR-034 +# #6004-CHAR-037 +# #6004-CHAR-039 +# #6004-CHAR-041 +# check_menu_item_checkbox(page, "Overlays", overlay_info) + +# # Check chart info +# # 6004-CHAR-010 +# expected_date_regex = r"^\d{2}:\d{2} \d{2} [A-Za-z]{3} \d{4}$" +# expected_ohlc = "O 101.50000H 101.50000L 101.50000C 101.50000Change −6.00000(−5.58%)" +# indicator_info_locator = page.locator(".indicator-info-wrapper").nth(0) +# texts = indicator_info_locator.all_text_contents() +# combined_text = ''.join(texts) +# actual_date = combined_text[:-67] +# actual_ohlc = combined_text[-67:] +# logger.info(actual_date) +# logger.info(actual_ohlc) +# assert re.match(expected_date_regex, actual_date) +# assert actual_ohlc == expected_ohlc +# # Check interval options and select '15m' +# interval = "button[aria-haspopup='menu']:has-text('Interval:')" +# valid_intervals = ['1m', '5m', '15m', '1H', '6H', '1D'] +# #6004-CHAR-001 +# page.click("button[aria-haspopup='menu']:has-text('Interval:')", force=True) +# check_menu_items(page, interval, valid_intervals, '1m') + + +# def check_menu_items(page, trigger_selector, valid_texts, click_item=None): +# page.click(trigger_selector, force=True) +# items = page.locator("div[role='menuitemradio']").all() +# assert len(items) == len(valid_texts), f"Expected {len(valid_texts)} items but found {len(items)} items." + +# for i, el in enumerate(items): +# text = el.text_content().strip() +# assert text == valid_texts[i], f"Expected text '{valid_texts[i]}' but found '{text}'." +# if click_item: +# page.click(f"div[role='menuitemradio']:text-is('{click_item}')") +# page.click(trigger_selector) +# checked_item_text = page.text_content("div[role='menuitemradio'][data-state='checked']").strip() +# assert checked_item_text == click_item, f"Expected checked item text '{click_item}' but found '{checked_item_text}'." +# page.click(trigger_selector, force=True) + +# def check_menu_item_checkbox(page, button_text, items): +# button_selector = f"button:has-text('{button_text}')" + +# for item in items: +# page.click(button_selector) +# page.click(f"div[role='menuitemcheckbox']:has-text('{item.name}')") + +# page.click(button_selector) +# checkbox_items = page.query_selector_all("div[role='menuitemcheckbox']") + +# assert len(checkbox_items) == len(items), f"Expected {len(items)} checkboxes but found {len(checkbox_items)}." + +# for i, el in enumerate(checkbox_items): +# text = el.text_content().strip() +# assert text == items[i].name, f"Expected checkbox text '{items[i].name}' but found '{text}'." + +# for i, item in enumerate(items[0:]): +# info_locator = page.locator(".indicator-info-wrapper").nth(i+1) +# info_text = info_locator.text_content().strip() +# assert info_text == item.infoText, f"Expected info text '{item.infoText}' but found '{info_text}'." diff --git a/apps/console-test/tests/transfer/test_transfer_key_to_key.py b/apps/console-test/tests/transfer/test_transfer_key_to_key.py new file mode 100644 index 000000000..abee69bff --- /dev/null +++ b/apps/console-test/tests/transfer/test_transfer_key_to_key.py @@ -0,0 +1,42 @@ +import pytest +import re +from playwright.sync_api import Page, expect +from vega_sim.service import VegaService +from actions.utils import wait_for_toast_confirmation + +@pytest.mark.usefixtures("page", "auth", "risk_accepted") +def test_transfer_submit(continuous_market, vega: VegaService, page: Page): + # 1003-TRAN-001 + # 1003-TRAN-006 + # 1003-TRAN-007 + # 1003-TRAN-008 + # 1003-TRAN-009 + # 1003-TRAN-010 + # 1003-TRAN-023 + page.goto('/#/portfolio') + + expect(page.get_by_test_id('transfer-form')).to_be_visible + page.select_option('[data-testid=transfer-form] [name="toAddress"]', index=1) + + page.get_by_test_id('select-asset').click() + expect(page.get_by_test_id('rich-select-option')).to_have_count(1) + + page.get_by_test_id('rich-select-option').click() + expected_asset_text = re.compile(r"tDAI tDAI999,991.49731 tDAI.{6}….{4}") + actual_asset_text = page.get_by_test_id('select-asset').text_content().strip() + + assert expected_asset_text.search(actual_asset_text), f"Expected pattern not found in {actual_asset_text}" + + page.locator('[data-testid=transfer-form] input[name="amount"]').fill('1') + expect(page.locator('[data-testid=transfer-form] input[name="amount"]')).not_to_be_empty() + + page.locator('[data-testid=transfer-form] [type="submit"]').click() + wait_for_toast_confirmation(page) + vega.forward("10s") + vega.wait_fn(1) + vega.wait_for_total_catchup() + expected_confirmation_text = re.compile(r"Transfer completeYour transaction has been confirmed View in block explorerTransferTo .{6}….{6}1\.00 tDAI") + actual_confirmation_text = page.get_by_test_id('toast-content').text_content() + print(f"Actual text is: {actual_confirmation_text}") + assert expected_confirmation_text.search(actual_confirmation_text), f"Expected pattern not found in {actual_confirmation_text}" + \ No newline at end of file diff --git a/package.json b/package.json index 124ba8b28..1fcb1f127 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,10 @@ "lint:all": "nx run-many --all --target=lint", "e2e:all": "nx run-many --all --target=e2e", "vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl", - "release": "git checkout develop ; git pull ; node scripts/make-release.js" + "release": "git checkout develop ; git pull ; node scripts/make-release.js", + "install:console-test": "cd apps/console-test && poetry install && poetry run playwright install chromium && python -m vega_sim.tools.load_binaries --force --version v0.73.1", + "console-test": "cd apps/console-test && poetry run pytest -k", + "console-test:all": "cd apps/console-test && poetry run pytest -s --headed --numprocesses 8 --dist loadfile" }, "engines": { "node": ">=16.15.1"