diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 233df140..1bab1630 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 0.2.0
+current_version = 0.3.0
commit = False
tag = False
allow_dirty = True
diff --git a/.github/actions/build-rln-node/action.yml b/.github/actions/build-rln-node/action.yml
index 9d490a08..7426aecc 100644
--- a/.github/actions/build-rln-node/action.yml
+++ b/.github/actions/build-rln-node/action.yml
@@ -12,7 +12,7 @@ runs:
- name: Install Rust
run: |
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.81.0
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
source "$HOME/.cargo/env"
shell: bash
diff --git a/.github/actions/publish-test-reports/action.yml b/.github/actions/publish-test-reports/action.yml
index efabe347..2ee8090e 100644
--- a/.github/actions/publish-test-reports/action.yml
+++ b/.github/actions/publish-test-reports/action.yml
@@ -39,11 +39,16 @@ runs:
shell: bash
- name: Generate reports
- run: ./.github/scripts/generate-reports.sh
shell: bash
+ env:
+ RUN_ID: ${{ github.run_id }}
+ COMMIT_SHA: ${{ github.sha }}
+ run: ./.github/scripts/generate-reports.sh
- - name: Deploy to GitHub Pages
- uses: peaceiris/actions-gh-pages@v4
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
with:
- github_token: ${{ github.token }}
- publish_dir: gh-pages
+ path: site
+
+ - name: Deploy to GitHub Pages
+ uses: actions/deploy-pages@v4
diff --git a/.github/scripts/generate-reports.sh b/.github/scripts/generate-reports.sh
index aedb582c..b3a46b6d 100755
--- a/.github/scripts/generate-reports.sh
+++ b/.github/scripts/generate-reports.sh
@@ -3,33 +3,33 @@
set -euo pipefail
-mkdir -p gh-pages
+mkdir -p site
# Generate embedded report
if [ -d "allure-results-embedded" ] && [ "$(ls -A allure-results-embedded 2>/dev/null)" ]; then
- allure generate allure-results-embedded --clean -o gh-pages/embedded
+ allure generate allure-results-embedded --clean -o site/embedded
else
- mkdir -p gh-pages/embedded
- echo "
No embedded test results available
" > gh-pages/embedded/index.html
+ mkdir -p site/embedded
+ echo "No embedded test results available
" > site/embedded/index.html
fi
# Generate remote report
if [ -d "allure-results-remote" ] && [ "$(ls -A allure-results-remote 2>/dev/null)" ]; then
- allure generate allure-results-remote --clean -o gh-pages/remote
+ allure generate allure-results-remote --clean -o site/remote
else
- mkdir -p gh-pages/remote
- echo "No remote test results available
" > gh-pages/remote/index.html
+ mkdir -p site/remote
+ echo "No remote test results available
" > site/remote/index.html
fi
# Copy coverage report
if [ -d "coverage-report" ] && [ "$(ls -A coverage-report 2>/dev/null)" ]; then
- cp -r coverage-report gh-pages/coverage
+ cp -r coverage-report site/coverage
else
- mkdir -p gh-pages/coverage
- echo "No coverage report available
" > gh-pages/coverage/index.html
+ mkdir -p site/coverage
+ echo "No coverage report available
" > site/coverage/index.html
fi
# Create index page with links to all reports
-cp .github/pages/allure-index.html gh-pages/index.html
+cp .github/pages/allure-index.html site/index.html
echo "✅ Reports generated successfully"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index bbf3d73a..447a4cd7 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -146,16 +146,16 @@ jobs:
- test_ask_auth_for_imp_operations
- test_settings
- test_btc_channel_tx
- - test_rgb20_channel_tx
+ - test_nia_channel_tx
- test_help
- test_about
- test_backup_and_restore
- - test_send_and_receive_rgb20_asset
- - test_send_and_receive_rgb25_asset
+ - test_send_and_receive_nia_asset
+ - test_send_and_receive_cfa_asset
- test_hide_exhausted_asset
- test_fail_transfer
- - test_issue_rgb20_asset
- - test_issue_rgb25_asset
+ - test_issue_nia_asset
+ - test_issue_cfa_asset
- test_view_unspent
- test_refresh_transfer
env:
@@ -270,8 +270,12 @@ jobs:
runs-on: ubuntu-latest
if: always()
permissions:
- contents: write
- actions: write
+ contents: read
+ pages: write
+ id-token: write
+
+ environment:
+ name: github-pages
steps:
- name: Checkout repository
diff --git a/accessible_constant.py b/accessible_constant.py
index 130660ed..dcb92590 100644
--- a/accessible_constant.py
+++ b/accessible_constant.py
@@ -65,13 +65,13 @@
SEND_ASSET_BUTTON = 'send_asset_button'
ASSET_ADDRESS_VALIDATION_LABEL = 'asset_address_validation_label'
-# Issue RGB20 asset page
-ISSUE_RGB20_ASSET = 'issue_rgb20_asset'
-ISSUE_RGB20_ASSET_CLOSE_BUTTON = 'issue_rgb20_asset_close_button'
-RGB20_ASSET_TICKER = 'rgb20_asset_ticker'
-RGB20_ASSET_NAME = 'rgb20_asset_name'
-RGB20_ASSET_AMOUNT = 'rgb20_asset_amount'
-ISSUE_RGB20_BUTTON = 'issue_rgb20_button'
+# Issue NIA asset page
+ISSUE_NIA_ASSET = 'issue_nia_asset'
+ISSUE_NIA_ASSET_CLOSE_BUTTON = 'issue_nia_asset_close_button'
+NIA_ASSET_TICKER = 'nia_asset_ticker'
+NIA_ASSET_NAME = 'nia_asset_name'
+NIA_ASSET_AMOUNT = 'nia_asset_amount'
+ISSUE_NIA_BUTTON = 'issue_nia_button'
# Success page
SUCCESS_PAGE_CLOSE_BUTTON = 'success_page_close_button'
@@ -95,14 +95,14 @@
VIEW_UNSPENT_LIST_BUTTON = 'view_unspent_list_button'
SETTINGS_BUTTON = 'settings_button'
-# Issue RGB25 asset page
-ISSUE_RGB25_ASSET = 'issue_rgb25_asset'
-ISSUE_RGB25_BUTTON = 'issue_rgb25_button'
-RGB25_ASSET_DESCRIPTION = 'rgb25_asset_description'
-RGB25_ASSET_NAME = 'rgb25_asset_name'
-RGB25_ASSET_AMOUNT = 'rgb25_asset_amount'
-RGB25_UPLOAD_FILE_BUTTON = 'rgb25_upload_file_button'
-ISSUE_RGB25_ASSET_CLOSE_BUTTON = 'issue_rgb25_asset_close_button'
+# Issue CFA asset page
+ISSUE_CFA_ASSET = 'issue_cfa_asset'
+ISSUE_CFA_BUTTON = 'issue_cfa_button'
+CFA_ASSET_DESCRIPTION = 'cfa_asset_description'
+CFA_ASSET_NAME = 'cfa_asset_name'
+CFA_ASSET_AMOUNT = 'cfa_asset_amount'
+CFA_UPLOAD_FILE_BUTTON = 'cfa_upload_file_button'
+ISSUE_CFA_ASSET_CLOSE_BUTTON = 'issue_cfa_asset_close_button'
# File chooser
FILE_CHOOSER = 'file chooser'
diff --git a/e2e_tests/compose.yaml b/e2e_tests/compose.yaml
index 19abc48e..c10175f6 100644
--- a/e2e_tests/compose.yaml
+++ b/e2e_tests/compose.yaml
@@ -1,6 +1,6 @@
services:
bitcoind:
- image: registry.gitlab.com/hashbeam/docker/bitcoind:28.1
+ image: registry.gitlab.com/hashbeam/docker/bitcoind:30.0
command: "-fallbackfee=0.0002"
environment:
MYUID: 1000
@@ -11,7 +11,7 @@ services:
volumes:
- ./datacore:/srv/app/.bitcoin
electrs:
- image: registry.gitlab.com/hashbeam/docker/electrs:0.10.9
+ image: registry.gitlab.com/hashbeam/docker/electrs:0.10.10
environment:
MYUID: 1000
MYGID: 1000
diff --git a/e2e_tests/conftest.py b/e2e_tests/conftest.py
index 8ed19bec..5182ea70 100644
--- a/e2e_tests/conftest.py
+++ b/e2e_tests/conftest.py
@@ -4,12 +4,17 @@
"""
from __future__ import annotations
+import io
import os
import subprocess
import time
+import allure
import pytest
from dogtail.tree import root
+from PIL import Image
+from Xlib import display
+from Xlib import X
from accessible_constant import DEFAULT_WALLET_MODES
from src.model.enums.enums_model import WalletType
@@ -132,6 +137,82 @@ def pytest_runtest_logreport(report):
_print_test_result('⏭️', 'SKIPPED', test_name)
+def _capture_screenshot():
+ """
+ Capture a screenshot of the entire screen using Xlib.
+
+ Returns:
+ bytes: PNG image data, or None if capture fails
+ """
+ try:
+ # Get the display and screen
+ dpy = display.Display()
+ screen = dpy.screen()
+ root_window = screen.root
+
+ # Get screen dimensions
+ width = screen.width_in_pixels
+ height = screen.height_in_pixels
+
+ # Capture the screen
+ raw_image = root_window.get_image(
+ 0, 0, width, height, X.ZPixmap, 0xffffffff,
+ )
+
+ # Convert to PIL Image
+ image = Image.frombytes(
+ 'RGB',
+ (width, height),
+ raw_image.data,
+ 'raw',
+ 'BGRX',
+ )
+
+ # Save to bytes buffer
+ buffer = io.BytesIO()
+ image.save(buffer, format='PNG')
+ buffer.seek(0)
+
+ return buffer.getvalue()
+ except Exception as e:
+ print(f"[SCREENSHOT] Failed to capture screenshot: {e}")
+ return None
+
+
+@pytest.hookimpl(tryfirst=True, hookwrapper=True)
+def pytest_runtest_makereport(item):
+ """
+ Pytest hook to capture screenshots on test failure and attach to Allure report.
+
+ This hook runs after each test phase (setup, call, teardown) and captures
+ a screenshot if the test failed during the 'call' phase.
+ """
+ # Execute all other hooks to obtain the report object
+ outcome = yield
+ report = outcome.get_result()
+
+ # Only capture screenshot on test failure during the 'call' phase
+ if report.when == 'call' and report.failed:
+ try:
+ screenshot_bytes = _capture_screenshot()
+
+ if screenshot_bytes:
+ # Attach screenshot to Allure report
+ allure.attach(
+ screenshot_bytes,
+ name='failure_screenshot',
+ attachment_type=allure.attachment_type.PNG,
+ )
+ print(f"""[SCREENSHOT] ✅ Screenshot captured and attached for failed test:
+ {item.nodeid}""")
+ else:
+ print(f"""[SCREENSHOT] ⚠️ Failed to capture screenshot for:
+ {item.nodeid}""")
+ except Exception as e:
+ print(f"""[SCREENSHOT] ❌ Error capturing screenshot for
+ {item.nodeid}: {e}""")
+
+
def _is_ci_environment():
"""Check if running in CI environment."""
return os.getenv('CI', '').lower() in ('true', '1', 'yes')
diff --git a/e2e_tests/test/features/channel.py b/e2e_tests/test/features/channel.py
index 25fd52fc..f23811db 100644
--- a/e2e_tests/test/features/channel.py
+++ b/e2e_tests/test/features/channel.py
@@ -1,6 +1,6 @@
# pylint:disable=too-many-branches
"""
-This module contains the IssueRgb20 class, which provides methods for issuing RGB20 assets.
+This module contains the Channel class, which provides methods for channel operations.
"""
from __future__ import annotations
@@ -144,7 +144,7 @@ def get_node_uri_for_embedded(self, application, ip_address):
if self.do_is_displayed(self.sidebar_page_objects.fungibles_button()):
self.sidebar_page_objects.click_fungibles_button()
- embedded_node_uri = f"{node_pubkey}@{ip_address}:{ln_port}"
+ embedded_node_uri = f'{node_pubkey}@{ip_address}:{ln_port}'
return embedded_node_uri
@@ -163,6 +163,6 @@ def get_node_uri_for_remote(self, application, ip_address, ln_port):
if self.do_is_displayed(self.sidebar_page_objects.fungibles_button()):
self.sidebar_page_objects.click_fungibles_button()
- remote_node_uri = f"{node_pubkey}@{ip_address}:{ln_port}"
+ remote_node_uri = f'{node_pubkey}@{ip_address}:{ln_port}'
return remote_node_uri
diff --git a/e2e_tests/test/features/issue_cfa.py b/e2e_tests/test/features/issue_cfa.py
new file mode 100644
index 00000000..6858b04e
--- /dev/null
+++ b/e2e_tests/test/features/issue_cfa.py
@@ -0,0 +1,145 @@
+"""
+Module for testing CFA asset issuance.
+"""
+from __future__ import annotations
+
+import os
+
+from e2e_tests.test.pageobjects.main_page_objects import MainPageObjects
+from e2e_tests.test.utilities.asset_copy import copy_cfa_image_to_home_directory
+from e2e_tests.test.utilities.base_operation import BaseOperations
+
+
+class IssueCfa(MainPageObjects, BaseOperations):
+ """
+ Class for testing CFA asset issuance.
+ """
+
+ def __init__(self, application):
+ """
+ Initialize the IssueCfa class.
+ """
+ super().__init__(application)
+
+ def issue_cfa_with_sufficient_sats_and_utxo(self, application, asset_name, asset_description, asset_amount, is_native_auth_enabled: bool = False):
+ """
+ Issue CFA asset with sufficient sats and utxo.
+ """
+ self.do_focus_on_application(application)
+ copy_cfa_image_to_home_directory(os.getcwd())
+
+ if self.do_is_displayed(self.sidebar_page_objects.collectibles_button()):
+ self.sidebar_page_objects.click_collectibles_button()
+
+ if self.do_is_displayed(self.collectible_page_objects.issue_cfa_button()):
+ self.collectible_page_objects.click_issue_cfa_button()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_name()):
+ self.issue_cfa_page_objects.enter_asset_name(asset_name)
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_description()):
+ self.issue_cfa_page_objects.enter_asset_description(
+ asset_description,
+ )
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_amount()):
+ self.issue_cfa_page_objects.enter_asset_amount(asset_amount)
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.upload_file_button()):
+ self.issue_cfa_page_objects.click_upload_file_button()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.cfa_asset_media()):
+ self.issue_cfa_page_objects.click_cfa_asset_media()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.issue_cfa_button()):
+ self.issue_cfa_page_objects.click_issue_cfa_button()
+
+ if is_native_auth_enabled is True:
+ self.enter_native_password()
+
+ if self.do_is_displayed(self.success_page_objects.home_button()):
+ self.success_page_objects.click_home_button()
+
+ def issue_cfa_asset_without_sat(self, application, asset_name, asset_description, asset_amount):
+ """
+ Issue CFA asset without sat.
+ """
+ description = None
+ self.do_focus_on_application(application)
+ copy_cfa_image_to_home_directory(os.getcwd())
+
+ if self.do_is_displayed(self.sidebar_page_objects.collectibles_button()):
+ self.sidebar_page_objects.click_collectibles_button()
+
+ if self.do_is_displayed(self.collectible_page_objects.issue_cfa_button()):
+ self.collectible_page_objects.click_issue_cfa_button()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_name()):
+ self.issue_cfa_page_objects.enter_asset_name(asset_name)
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_description()):
+ self.issue_cfa_page_objects.enter_asset_description(
+ asset_description,
+ )
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_amount()):
+ self.issue_cfa_page_objects.enter_asset_amount(asset_amount)
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.upload_file_button()):
+ self.issue_cfa_page_objects.click_upload_file_button()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.cfa_asset_media()):
+ self.issue_cfa_page_objects.click_cfa_asset_media()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.issue_cfa_button()):
+ self.issue_cfa_page_objects.click_issue_cfa_button()
+
+ if self.do_is_displayed(self.toaster_page_objects.toaster_frame()):
+ description = self.toaster_page_objects.click_and_get_description()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.close_button()):
+ self.issue_cfa_page_objects.click_close_button()
+
+ if self.do_is_displayed(self.sidebar_page_objects.fungibles_button()):
+ self.sidebar_page_objects.click_fungibles_button()
+
+ return description
+
+ def issue_cfa_with_sufficient_sats_and_no_utxo(self, application, asset_name, asset_description, asset_amount):
+ """
+ Issue CFA asset with sufficient sats and no utxo.
+ """
+ self.do_focus_on_application(application)
+ copy_cfa_image_to_home_directory(os.getcwd())
+
+ if self.do_is_displayed(self.sidebar_page_objects.view_unspents_button()):
+ self.sidebar_page_objects.click_view_unspents_button()
+
+ if self.do_is_displayed(self.sidebar_page_objects.collectibles_button()):
+ self.sidebar_page_objects.click_collectibles_button()
+
+ if self.do_is_displayed(self.collectible_page_objects.issue_cfa_button()):
+ self.collectible_page_objects.click_issue_cfa_button()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_name()):
+ self.issue_cfa_page_objects.enter_asset_name(asset_name)
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_description()):
+ self.issue_cfa_page_objects.enter_asset_description(
+ asset_description,
+ )
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.asset_amount()):
+ self.issue_cfa_page_objects.enter_asset_amount(asset_amount)
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.upload_file_button()):
+ self.issue_cfa_page_objects.click_upload_file_button()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.cfa_asset_media()):
+ self.issue_cfa_page_objects.click_cfa_asset_media()
+
+ if self.do_is_displayed(self.issue_cfa_page_objects.issue_cfa_button()):
+ self.issue_cfa_page_objects.click_issue_cfa_button()
+
+ if self.do_is_displayed(self.success_page_objects.home_button()):
+ self.success_page_objects.click_home_button()
diff --git a/e2e_tests/test/features/issue_nia.py b/e2e_tests/test/features/issue_nia.py
new file mode 100644
index 00000000..8cba021a
--- /dev/null
+++ b/e2e_tests/test/features/issue_nia.py
@@ -0,0 +1,101 @@
+"""
+This module contains the IssueNia class, which provides methods for issuing NIA assets.
+"""
+from __future__ import annotations
+
+from e2e_tests.test.pageobjects.main_page_objects import MainPageObjects
+from e2e_tests.test.utilities.base_operation import BaseOperations
+
+
+class IssueNia(MainPageObjects, BaseOperations):
+ """
+ This class provides methods for issuing NIA assets.
+ """
+
+ def __init__(self, application):
+ """
+ Initializes the IssueNia class.
+ """
+ super().__init__(application)
+
+ def issue_nia_with_sufficient_sats_and_no_utxo(self, application, asset_ticker, asset_name, asset_amount):
+ """
+ Issues an NIA asset with sufficient sats and no UTXO.
+ """
+ self.do_focus_on_application(application)
+
+ if self.do_is_displayed(self.sidebar_page_objects.fungibles_button()):
+ self.sidebar_page_objects.click_fungibles_button()
+
+ if self.do_is_displayed(self.fungible_page_objects.issue_nia_button()):
+ self.fungible_page_objects.click_issue_nia_button()
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_ticker()):
+ self.issue_nia_page_objects.enter_asset_ticker(asset_ticker)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_name()):
+ self.issue_nia_page_objects.enter_asset_name(asset_name)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_amount()):
+ self.issue_nia_page_objects.enter_asset_amount(asset_amount)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.issue_nia_button()):
+ self.issue_nia_page_objects.click_issue_nia_button()
+
+ if self.do_is_displayed(self.success_page_objects.home_button()):
+ self.success_page_objects.click_home_button()
+
+ def issue_nia_asset_without_sat(self, application, asset_ticker, asset_name, asset_amount):
+ """
+ Issues an NIA asset without sufficient sats.
+ """
+ description = None
+ self.do_focus_on_application(application)
+ if self.do_is_displayed(self.fungible_page_objects.issue_nia_button()):
+ self.fungible_page_objects.click_issue_nia_button()
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_ticker()):
+ self.issue_nia_page_objects.enter_asset_ticker(asset_ticker)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_name()):
+ self.issue_nia_page_objects.enter_asset_name(asset_name)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_amount()):
+ self.issue_nia_page_objects.enter_asset_amount(asset_amount)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.issue_nia_button()):
+ self.issue_nia_page_objects.click_issue_nia_button()
+
+ if self.do_is_displayed(self.toaster_page_objects.toaster_frame()):
+ description = self.toaster_page_objects.click_and_get_description()
+
+ if self.do_is_displayed(self.issue_nia_page_objects.close_button()):
+ self.issue_nia_page_objects.click_close_button()
+
+ return description
+
+ def issue_nia_with_sufficient_sats_and_utxo(self, application, asset_ticker, asset_name, asset_amount, is_native_auth_enabled: bool = False):
+ """
+ Issues an NIA asset with sufficient sats and UTXO.
+ """
+ self.do_focus_on_application(application)
+ if self.do_is_displayed(self.fungible_page_objects.issue_nia_button()):
+ self.fungible_page_objects.click_issue_nia_button()
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_ticker()):
+ self.issue_nia_page_objects.enter_asset_ticker(asset_ticker)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_name()):
+ self.issue_nia_page_objects.enter_asset_name(asset_name)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.asset_amount()):
+ self.issue_nia_page_objects.enter_asset_amount(asset_amount)
+
+ if self.do_is_displayed(self.issue_nia_page_objects.issue_nia_button()):
+ self.issue_nia_page_objects.click_issue_nia_button()
+
+ if is_native_auth_enabled is True:
+ self.enter_native_password()
+
+ if self.do_is_displayed(self.success_page_objects.home_button()):
+ self.success_page_objects.click_home_button()
diff --git a/e2e_tests/test/features/issue_rgb20.py b/e2e_tests/test/features/issue_rgb20.py
deleted file mode 100644
index 050ab894..00000000
--- a/e2e_tests/test/features/issue_rgb20.py
+++ /dev/null
@@ -1,101 +0,0 @@
-"""
-This module contains the IssueRgb20 class, which provides methods for issuing RGB20 assets.
-"""
-from __future__ import annotations
-
-from e2e_tests.test.pageobjects.main_page_objects import MainPageObjects
-from e2e_tests.test.utilities.base_operation import BaseOperations
-
-
-class IssueRgb20(MainPageObjects, BaseOperations):
- """
- This class provides methods for issuing RGB20 assets.
- """
-
- def __init__(self, application):
- """
- Initializes the IssueRgb20 class.
- """
- super().__init__(application)
-
- def issue_rgb20_with_sufficient_sats_and_no_utxo(self, application, asset_ticker, asset_name, asset_amount):
- """
- Issues an RGB20 asset with sufficient sats and no UTXO.
- """
- self.do_focus_on_application(application)
-
- if self.do_is_displayed(self.sidebar_page_objects.fungibles_button()):
- self.sidebar_page_objects.click_fungibles_button()
-
- if self.do_is_displayed(self.fungible_page_objects.issue_rgb20_button()):
- self.fungible_page_objects.click_issue_rgb20_button()
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_ticker()):
- self.issue_rgb20_page_objects.enter_asset_ticker(asset_ticker)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_name()):
- self.issue_rgb20_page_objects.enter_asset_name(asset_name)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_amount()):
- self.issue_rgb20_page_objects.enter_asset_amount(asset_amount)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.issue_rgb20_button()):
- self.issue_rgb20_page_objects.click_issue_rgb20_button()
-
- if self.do_is_displayed(self.success_page_objects.home_button()):
- self.success_page_objects.click_home_button()
-
- def issue_rgb20_asset_without_sat(self, application, asset_ticker, asset_name, asset_amount):
- """
- Issues an RGB20 asset without sufficient sats.
- """
- description = None
- self.do_focus_on_application(application)
- if self.do_is_displayed(self.fungible_page_objects.issue_rgb20_button()):
- self.fungible_page_objects.click_issue_rgb20_button()
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_ticker()):
- self.issue_rgb20_page_objects.enter_asset_ticker(asset_ticker)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_name()):
- self.issue_rgb20_page_objects.enter_asset_name(asset_name)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_amount()):
- self.issue_rgb20_page_objects.enter_asset_amount(asset_amount)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.issue_rgb20_button()):
- self.issue_rgb20_page_objects.click_issue_rgb20_button()
-
- if self.do_is_displayed(self.toaster_page_objects.toaster_frame()):
- description = self.toaster_page_objects.click_and_get_description()
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.close_button()):
- self.issue_rgb20_page_objects.click_close_button()
-
- return description
-
- def issue_rgb20_with_sufficient_sats_and_utxo(self, application, asset_ticker, asset_name, asset_amount, is_native_auth_enabled: bool = False):
- """
- Issues an RGB20 asset with sufficient sats and UTXO.
- """
- self.do_focus_on_application(application)
- if self.do_is_displayed(self.fungible_page_objects.issue_rgb20_button()):
- self.fungible_page_objects.click_issue_rgb20_button()
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_ticker()):
- self.issue_rgb20_page_objects.enter_asset_ticker(asset_ticker)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_name()):
- self.issue_rgb20_page_objects.enter_asset_name(asset_name)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.asset_amount()):
- self.issue_rgb20_page_objects.enter_asset_amount(asset_amount)
-
- if self.do_is_displayed(self.issue_rgb20_page_objects.issue_rgb20_button()):
- self.issue_rgb20_page_objects.click_issue_rgb20_button()
-
- if is_native_auth_enabled is True:
- self.enter_native_password()
-
- if self.do_is_displayed(self.success_page_objects.home_button()):
- self.success_page_objects.click_home_button()
diff --git a/e2e_tests/test/features/issue_rgb25.py b/e2e_tests/test/features/issue_rgb25.py
deleted file mode 100644
index 0ddd8d6f..00000000
--- a/e2e_tests/test/features/issue_rgb25.py
+++ /dev/null
@@ -1,145 +0,0 @@
-"""
-Module for testing RGB25 asset issuance.
-"""
-from __future__ import annotations
-
-import os
-
-from e2e_tests.test.pageobjects.main_page_objects import MainPageObjects
-from e2e_tests.test.utilities.asset_copy import copy_rgb25_image_to_home_directory
-from e2e_tests.test.utilities.base_operation import BaseOperations
-
-
-class IssueRgb25(MainPageObjects, BaseOperations):
- """
- Class for testing RGB25 asset issuance.
- """
-
- def __init__(self, application):
- """
- Initialize the IssueRgb25 class.
- """
- super().__init__(application)
-
- def issue_rgb25_with_sufficient_sats_and_utxo(self, application, asset_name, asset_description, asset_amount, is_native_auth_enabled: bool = False):
- """
- Issue RGB25 asset with sufficient sats and utxo.
- """
- self.do_focus_on_application(application)
- copy_rgb25_image_to_home_directory(os.getcwd())
-
- if self.do_is_displayed(self.sidebar_page_objects.collectibles_button()):
- self.sidebar_page_objects.click_collectibles_button()
-
- if self.do_is_displayed(self.collectible_page_objects.issue_rgb25_button()):
- self.collectible_page_objects.click_issue_rgb25_button()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_name()):
- self.issue_rgb25_page_objects.enter_asset_name(asset_name)
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_description()):
- self.issue_rgb25_page_objects.enter_asset_description(
- asset_description,
- )
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_amount()):
- self.issue_rgb25_page_objects.enter_asset_amount(asset_amount)
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.upload_file_button()):
- self.issue_rgb25_page_objects.click_upload_file_button()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.rgb25_asset_media()):
- self.issue_rgb25_page_objects.click_rgb25_asset_media()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.issue_rgb25_button()):
- self.issue_rgb25_page_objects.click_issue_rgb25_button()
-
- if is_native_auth_enabled is True:
- self.enter_native_password()
-
- if self.do_is_displayed(self.success_page_objects.home_button()):
- self.success_page_objects.click_home_button()
-
- def issue_rgb25_asset_without_sat(self, application, asset_name, asset_description, asset_amount):
- """
- Issue RGB25 asset without sat.
- """
- description = None
- self.do_focus_on_application(application)
- copy_rgb25_image_to_home_directory(os.getcwd())
-
- if self.do_is_displayed(self.sidebar_page_objects.collectibles_button()):
- self.sidebar_page_objects.click_collectibles_button()
-
- if self.do_is_displayed(self.collectible_page_objects.issue_rgb25_button()):
- self.collectible_page_objects.click_issue_rgb25_button()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_name()):
- self.issue_rgb25_page_objects.enter_asset_name(asset_name)
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_description()):
- self.issue_rgb25_page_objects.enter_asset_description(
- asset_description,
- )
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_amount()):
- self.issue_rgb25_page_objects.enter_asset_amount(asset_amount)
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.upload_file_button()):
- self.issue_rgb25_page_objects.click_upload_file_button()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.rgb25_asset_media()):
- self.issue_rgb25_page_objects.click_rgb25_asset_media()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.issue_rgb25_button()):
- self.issue_rgb25_page_objects.click_issue_rgb25_button()
-
- if self.do_is_displayed(self.toaster_page_objects.toaster_frame()):
- description = self.toaster_page_objects.click_and_get_description()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.close_button()):
- self.issue_rgb25_page_objects.click_close_button()
-
- if self.do_is_displayed(self.sidebar_page_objects.fungibles_button()):
- self.sidebar_page_objects.click_fungibles_button()
-
- return description
-
- def issue_rgb25_with_sufficient_sats_and_no_utxo(self, application, asset_name, asset_description, asset_amount):
- """
- Issue RGB25 asset with sufficient sats and no utxo.
- """
- self.do_focus_on_application(application)
- copy_rgb25_image_to_home_directory(os.getcwd())
-
- if self.do_is_displayed(self.sidebar_page_objects.view_unspents_button()):
- self.sidebar_page_objects.click_view_unspents_button()
-
- if self.do_is_displayed(self.sidebar_page_objects.collectibles_button()):
- self.sidebar_page_objects.click_collectibles_button()
-
- if self.do_is_displayed(self.collectible_page_objects.issue_rgb25_button()):
- self.collectible_page_objects.click_issue_rgb25_button()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_name()):
- self.issue_rgb25_page_objects.enter_asset_name(asset_name)
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_description()):
- self.issue_rgb25_page_objects.enter_asset_description(
- asset_description,
- )
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.asset_amount()):
- self.issue_rgb25_page_objects.enter_asset_amount(asset_amount)
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.upload_file_button()):
- self.issue_rgb25_page_objects.click_upload_file_button()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.rgb25_asset_media()):
- self.issue_rgb25_page_objects.click_rgb25_asset_media()
-
- if self.do_is_displayed(self.issue_rgb25_page_objects.issue_rgb25_button()):
- self.issue_rgb25_page_objects.click_issue_rgb25_button()
-
- if self.do_is_displayed(self.success_page_objects.home_button()):
- self.success_page_objects.click_home_button()
diff --git a/e2e_tests/test/features/main_features.py b/e2e_tests/test/features/main_features.py
index dd00d9e6..4896dd36 100644
--- a/e2e_tests/test/features/main_features.py
+++ b/e2e_tests/test/features/main_features.py
@@ -5,8 +5,8 @@
from __future__ import annotations
from e2e_tests.test.features.channel import Channel
-from e2e_tests.test.features.issue_rgb20 import IssueRgb20
-from e2e_tests.test.features.issue_rgb25 import IssueRgb25
+from e2e_tests.test.features.issue_cfa import IssueCfa
+from e2e_tests.test.features.issue_nia import IssueNia
from e2e_tests.test.features.receive import ReceiveOperation
from e2e_tests.test.features.send import SendOperation
from e2e_tests.test.features.wallet import Wallet
@@ -25,9 +25,9 @@ def __init__(self, application):
self.wallet_features = Wallet(self.application)
- self.issue_rgb20_features = IssueRgb20(self.application)
+ self.issue_nia_features = IssueNia(self.application)
- self.issue_rgb25_features = IssueRgb25(self.application)
+ self.issue_cfa_features = IssueCfa(self.application)
self.receive_features = ReceiveOperation(self.application)
diff --git a/e2e_tests/test/features/wallet.py b/e2e_tests/test/features/wallet.py
index 8f0ddcdb..d6048bdc 100644
--- a/e2e_tests/test/features/wallet.py
+++ b/e2e_tests/test/features/wallet.py
@@ -122,7 +122,7 @@ def remote_wallet(self, application, url):
if self.do_is_displayed(self.set_password_page_objects.proceed_button()):
self.set_password_page_objects.click_proceed_button()
- def create_and_fund_wallet(self, wallets_and_operations, application, application_url, fund=True):
+ def create_and_fund_wallet(self, wallets_and_operations, application, application_url=None, fund=True):
"""
Create a new wallet and fund it.
"""
diff --git a/e2e_tests/test/pageobjects/collectible_page.py b/e2e_tests/test/pageobjects/collectible_page.py
index d58dd1b8..e9723d95 100644
--- a/e2e_tests/test/pageobjects/collectible_page.py
+++ b/e2e_tests/test/pageobjects/collectible_page.py
@@ -3,7 +3,7 @@
"""
from __future__ import annotations
-from accessible_constant import ISSUE_RGB25_ASSET
+from accessible_constant import ISSUE_CFA_ASSET
from e2e_tests.test.utilities.base_operation import BaseOperations
@@ -16,32 +16,32 @@ def __init__(self, application):
"""
super().__init__(application)
- self.rgb25_asset_name = None
+ self.cfa_asset_name = None
# Define elements using lambdas for lazy evaluation
- self.issue_rgb25_button = lambda: self.perform_action_on_element(
- role_name='push button', name=ISSUE_RGB25_ASSET,
+ self.issue_cfa_button = lambda: self.perform_action_on_element(
+ role_name='push button', name=ISSUE_CFA_ASSET,
)
- def click_issue_rgb25_button(self):
+ def click_issue_cfa_button(self):
"""
- Click the issue RGB25 button if it's displayed.
+ Click the issue CFA button if it's displayed.
"""
- return self.do_click(self.issue_rgb25_button()) if self.do_is_displayed(self.issue_rgb25_button()) else None
+ return self.do_click(self.issue_cfa_button()) if self.do_is_displayed(self.issue_cfa_button()) else None
- def get_rgb25_asset_name(self, asset_name):
+ def get_cfa_asset_name(self, asset_name):
"""
Get the asset name if it's displayed.
"""
- self.rgb25_asset_name = self.perform_action_on_element(
+ self.cfa_asset_name = self.perform_action_on_element(
role_name='label', name=asset_name,
)
- return self.do_get_text(self.rgb25_asset_name) if self.do_is_displayed(self.rgb25_asset_name) else None
+ return self.do_get_text(self.cfa_asset_name) if self.do_is_displayed(self.cfa_asset_name) else None
- def click_rgb25_frame(self, asset_name):
+ def click_cfa_frame(self, asset_name):
"""
- Click the RGB25 frame if it's displayed.
+ Click the CFA frame if it's displayed.
"""
- self.rgb25_asset_name = self.perform_action_on_element(
+ self.cfa_asset_name = self.perform_action_on_element(
role_name='label', name=asset_name,
)
- return self.do_click(self.rgb25_asset_name) if self.do_is_displayed(self.rgb25_asset_name) else None
+ return self.do_click(self.cfa_asset_name) if self.do_is_displayed(self.cfa_asset_name) else None
diff --git a/e2e_tests/test/pageobjects/fungible_page.py b/e2e_tests/test/pageobjects/fungible_page.py
index 0fbbece8..bdea42af 100644
--- a/e2e_tests/test/pageobjects/fungible_page.py
+++ b/e2e_tests/test/pageobjects/fungible_page.py
@@ -4,7 +4,7 @@
from __future__ import annotations
from accessible_constant import FUNGIBLES_SCROLL_WIDGETS
-from accessible_constant import ISSUE_RGB20_ASSET
+from accessible_constant import ISSUE_NIA_ASSET
from accessible_constant import NETWORK_AND_BACKUP_FRAME
from e2e_tests.test.utilities.base_operation import BaseOperations
@@ -22,9 +22,9 @@ def __init__(self, application):
super().__init__(application)
# Define elements using lambdas for lazy evaluation
- self.rgb_20_asset_name = None
- self.issue_rgb20_button = lambda: self.perform_action_on_element(
- role_name='push button', name=ISSUE_RGB20_ASSET,
+ self.nia_asset_name = None
+ self.issue_nia_button = lambda: self.perform_action_on_element(
+ role_name='push button', name=ISSUE_NIA_ASSET,
)
self.bitcoin_frame = lambda: self.perform_action_on_element(
role_name='label', name='regtest bitcoin',
@@ -40,14 +40,14 @@ def get_child_count(self):
"""Returns child count"""
return self.do_get_child_count(self.fungibles_scroll_area()) if self.do_is_displayed(self.fungibles_scroll_area()) else None
- def click_issue_rgb20_button(self):
+ def click_issue_nia_button(self):
"""
- Clicks the issue RGB20 button if it is displayed.
+ Clicks the issue NIA button if it is displayed.
Returns:
bool: True if the button is clicked, None otherwise.
"""
- return self.do_click(self.issue_rgb20_button()) if self.do_is_displayed(self.issue_rgb20_button()) else None
+ return self.do_click(self.issue_nia_button()) if self.do_is_displayed(self.issue_nia_button()) else None
def click_bitcoin_frame(self):
"""
@@ -58,7 +58,7 @@ def click_bitcoin_frame(self):
"""
return self.do_click(self.bitcoin_frame()) if self.do_is_displayed(self.bitcoin_frame()) else None
- def get_rgb20_asset_name(self, asset_name):
+ def get_nia_asset_name(self, asset_name):
"""
Retrieves the asset name if it is displayed.
@@ -68,14 +68,14 @@ def get_rgb20_asset_name(self, asset_name):
Returns:
str: The asset name if it is displayed, None otherwise.
"""
- self.rgb_20_asset_name = self.perform_action_on_element(
+ self.nia_asset_name = self.perform_action_on_element(
role_name='label', name=asset_name,
)
- return self.do_get_text(self.rgb_20_asset_name) if self.do_is_displayed(self.rgb_20_asset_name) else None
+ return self.do_get_text(self.nia_asset_name) if self.do_is_displayed(self.nia_asset_name) else None
- def click_rgb20_frame(self, asset_name):
+ def click_nia_frame(self, asset_name):
"""
- Clicks the RGB20 frame if it is displayed.
+ Clicks the NIA frame if it is displayed.
Args:
asset_name (str): The name of the asset.
@@ -83,10 +83,10 @@ def click_rgb20_frame(self, asset_name):
Returns:
bool: True if the frame is clicked, None otherwise.
"""
- self.rgb_20_asset_name = self.perform_action_on_element(
+ self.nia_asset_name = self.perform_action_on_element(
role_name='label', name=asset_name,
)
- return self.do_click(self.rgb_20_asset_name) if self.do_is_displayed(self.rgb_20_asset_name) else None
+ return self.do_click(self.nia_asset_name) if self.do_is_displayed(self.nia_asset_name) else None
def get_backup_tooltip(self):
"""
diff --git a/e2e_tests/test/pageobjects/issue_rgb25_page.py b/e2e_tests/test/pageobjects/issue_cfa_page.py
similarity index 62%
rename from e2e_tests/test/pageobjects/issue_rgb25_page.py
rename to e2e_tests/test/pageobjects/issue_cfa_page.py
index e07c28fa..c701c922 100644
--- a/e2e_tests/test/pageobjects/issue_rgb25_page.py
+++ b/e2e_tests/test/pageobjects/issue_cfa_page.py
@@ -1,24 +1,24 @@
"""
-IssueRgb25PageObjects class provides methods to interact with the issue RGB25 page.
+IssueCfaPageObjects class provides methods to interact with the issue CFA page.
"""
from __future__ import annotations
from dogtail.rawinput import keyCombo
from dogtail.tree import root
+from accessible_constant import CFA_ASSET_AMOUNT
+from accessible_constant import CFA_ASSET_DESCRIPTION
+from accessible_constant import CFA_ASSET_NAME
+from accessible_constant import CFA_UPLOAD_FILE_BUTTON
from accessible_constant import FILE_CHOOSER
-from accessible_constant import ISSUE_RGB25_ASSET_CLOSE_BUTTON
-from accessible_constant import ISSUE_RGB25_BUTTON
-from accessible_constant import RGB25_ASSET_AMOUNT
-from accessible_constant import RGB25_ASSET_DESCRIPTION
-from accessible_constant import RGB25_ASSET_NAME
-from accessible_constant import RGB25_UPLOAD_FILE_BUTTON
+from accessible_constant import ISSUE_CFA_ASSET_CLOSE_BUTTON
+from accessible_constant import ISSUE_CFA_BUTTON
from e2e_tests.test.utilities.base_operation import BaseOperations
-class IssueRgb25PageObjects(BaseOperations):
+class IssueCfaPageObjects(BaseOperations):
"""
- Initialize the IssueRgb25PageObjects class.
+ Initialize the IssueCfaPageObjects class.
Args:
application: The application instance.
@@ -26,7 +26,7 @@ class IssueRgb25PageObjects(BaseOperations):
def __init__(self, application):
"""
- Initialize the IssueRgb25PageObjects class.
+ Initialize the IssueCfaPageObjects class.
Args:
application: The application instance.
@@ -34,31 +34,31 @@ def __init__(self, application):
super().__init__(application)
self.close_button = lambda: self.perform_action_on_element(
- role_name='push button', name=ISSUE_RGB25_ASSET_CLOSE_BUTTON,
+ role_name='push button', name=ISSUE_CFA_ASSET_CLOSE_BUTTON,
)
self.asset_name = lambda: self.perform_action_on_element(
- role_name='text', name=RGB25_ASSET_NAME,
+ role_name='text', name=CFA_ASSET_NAME,
)
self.asset_description = lambda: self.perform_action_on_element(
- role_name='text', name=RGB25_ASSET_DESCRIPTION,
+ role_name='text', name=CFA_ASSET_DESCRIPTION,
)
self.asset_amount = lambda: self.perform_action_on_element(
- role_name='text', name=RGB25_ASSET_AMOUNT,
+ role_name='text', name=CFA_ASSET_AMOUNT,
)
- self.issue_rgb25_button = lambda: self.perform_action_on_element(
- role_name='push button', name=ISSUE_RGB25_BUTTON,
+ self.issue_cfa_button = lambda: self.perform_action_on_element(
+ role_name='push button', name=ISSUE_CFA_BUTTON,
)
self.upload_file_button = lambda: self.perform_action_on_element(
- role_name='push button', name=RGB25_UPLOAD_FILE_BUTTON,
+ role_name='push button', name=CFA_UPLOAD_FILE_BUTTON,
)
self.file_dialog = lambda: root.child(roleName=FILE_CHOOSER)
- self.rgb25_asset_media = lambda: self.file_dialog().child(
+ self.cfa_asset_media = lambda: self.file_dialog().child(
roleName='table cell', name='sample.png',
)
def click_close_button(self):
"""
- Click the close button on the issue RGB25 page.
+ Click the close button on the issue CFA page.
Returns:
bool: True if the button is clicked, None otherwise.
@@ -67,7 +67,7 @@ def click_close_button(self):
def enter_asset_name(self, asset_name):
"""
- Enter the asset name on the issue RGB25 page.
+ Enter the asset name on the issue CFA page.
Args:
asset_name (str): The asset name to enter.
@@ -79,7 +79,7 @@ def enter_asset_name(self, asset_name):
def enter_asset_description(self, asset_description):
"""
- Enter the asset description on the issue RGB25 page.
+ Enter the asset description on the issue CFA page.
Args:
asset_description (str): The asset description to enter.
@@ -91,7 +91,7 @@ def enter_asset_description(self, asset_description):
def enter_asset_amount(self, asset_amount):
"""
- Enter the asset amount on the issue RGB25 page.
+ Enter the asset amount on the issue CFA page.
Args:
asset_amount (str): The asset amount to enter.
@@ -101,33 +101,33 @@ def enter_asset_amount(self, asset_amount):
"""
return self.do_set_value(self.asset_amount(), asset_amount) if self.do_is_displayed(self.asset_amount()) else None
- def click_issue_rgb25_button(self):
+ def click_issue_cfa_button(self):
"""
- Click the issue RGB25 button on the issue RGB25 page.
+ Click the issue CFA button on the issue CFA page.
Returns:
bool: True if the button is clicked, None otherwise.
"""
- return self.do_click(self.issue_rgb25_button()) if self.do_is_displayed(self.issue_rgb25_button()) else None
+ return self.do_click(self.issue_cfa_button()) if self.do_is_displayed(self.issue_cfa_button()) else None
def click_upload_file_button(self):
"""
- Click the upload file button on the issue RGB25 page.
+ Click the upload file button on the issue CFA page.
Returns:
bool: True if the button is clicked, None otherwise.
"""
return self.do_click(self.upload_file_button()) if self.do_is_displayed(self.upload_file_button()) else None
- def click_rgb25_asset_media(self):
+ def click_cfa_asset_media(self):
"""
- Select the RGB25 asset by focusing on it and pressing Enter.
+ Select the CFA asset by focusing on it and pressing Enter.
Returns:
bool: True if the asset is selected, False otherwise.
"""
- if self.do_is_displayed(self.rgb25_asset_media()):
- self.rgb25_asset_media().grabFocus()
+ if self.do_is_displayed(self.cfa_asset_media()):
+ self.cfa_asset_media().grabFocus()
keyCombo('enter')
return True
return False # Return False if the element is not displayed
diff --git a/e2e_tests/test/pageobjects/issue_rgb20_page.py b/e2e_tests/test/pageobjects/issue_nia_page.py
similarity index 59%
rename from e2e_tests/test/pageobjects/issue_rgb20_page.py
rename to e2e_tests/test/pageobjects/issue_nia_page.py
index d2eddcb8..9c04e674 100644
--- a/e2e_tests/test/pageobjects/issue_rgb20_page.py
+++ b/e2e_tests/test/pageobjects/issue_nia_page.py
@@ -1,24 +1,24 @@
"""
-IssueRgb20PageObjects class provides methods to interact with issue RGB20 page elements.
+IssueNiaPageObjects class provides methods to interact with issue NIA page elements.
"""
from __future__ import annotations
-from accessible_constant import ISSUE_RGB20_ASSET_CLOSE_BUTTON
-from accessible_constant import ISSUE_RGB20_BUTTON
-from accessible_constant import RGB20_ASSET_AMOUNT
-from accessible_constant import RGB20_ASSET_NAME
-from accessible_constant import RGB20_ASSET_TICKER
+from accessible_constant import ISSUE_NIA_ASSET_CLOSE_BUTTON
+from accessible_constant import ISSUE_NIA_BUTTON
+from accessible_constant import NIA_ASSET_AMOUNT
+from accessible_constant import NIA_ASSET_NAME
+from accessible_constant import NIA_ASSET_TICKER
from e2e_tests.test.utilities.base_operation import BaseOperations
-class IssueRgb20PageObjects(BaseOperations):
+class IssueNiaPageObjects(BaseOperations):
"""
- IssueRgb20PageObjects class provides methods to interact with issue RGB20 page elements.
+ IssueNiaPageObjects class provides methods to interact with issue NIA page elements.
"""
def __init__(self, application):
"""
- Initializes the IssueRgb20PageObjects class with the application object.
+ Initializes the IssueNiaPageObjects class with the application object.
Args:
application (object): The application object.
@@ -26,24 +26,24 @@ def __init__(self, application):
super().__init__(application)
self.close_button = lambda: self.perform_action_on_element(
- role_name='push button', name=ISSUE_RGB20_ASSET_CLOSE_BUTTON,
+ role_name='push button', name=ISSUE_NIA_ASSET_CLOSE_BUTTON,
)
self.asset_name = lambda: self.perform_action_on_element(
- role_name='text', name=RGB20_ASSET_NAME,
+ role_name='text', name=NIA_ASSET_NAME,
)
self.asset_ticker = lambda: self.perform_action_on_element(
- role_name='text', name=RGB20_ASSET_TICKER,
+ role_name='text', name=NIA_ASSET_TICKER,
)
self.asset_amount = lambda: self.perform_action_on_element(
- role_name='text', name=RGB20_ASSET_AMOUNT,
+ role_name='text', name=NIA_ASSET_AMOUNT,
)
- self.issue_rgb20_button = lambda: self.perform_action_on_element(
- role_name='push button', name=ISSUE_RGB20_BUTTON,
+ self.issue_nia_button = lambda: self.perform_action_on_element(
+ role_name='push button', name=ISSUE_NIA_BUTTON,
)
def click_close_button(self):
"""
- Clicks the close button on the issue RGB20 page.
+ Clicks the close button on the issue NIA page.
Returns:
bool: True if the button is clicked, False otherwise.
@@ -52,7 +52,7 @@ def click_close_button(self):
def enter_asset_name(self, asset_name):
"""
- Enters the asset name on the issue RGB20 page.
+ Enters the asset name on the issue NIA page.
Args:
asset_name (str): The asset name to enter.
@@ -64,7 +64,7 @@ def enter_asset_name(self, asset_name):
def enter_asset_ticker(self, asset_ticker):
"""
- Enters the asset ticker on the issue RGB20 page.
+ Enters the asset ticker on the issue NIA page.
Args:
asset_ticker (str): The asset ticker to enter.
@@ -76,7 +76,7 @@ def enter_asset_ticker(self, asset_ticker):
def enter_asset_amount(self, asset_amount):
"""
- Enters the asset amount on the issue RGB20 page.
+ Enters the asset amount on the issue NIA page.
Args:
asset_amount (str): The asset amount to enter.
@@ -86,11 +86,11 @@ def enter_asset_amount(self, asset_amount):
"""
return self.do_set_value(self.asset_amount(), asset_amount) if self.do_is_displayed(self.asset_amount()) else None
- def click_issue_rgb20_button(self):
+ def click_issue_nia_button(self):
"""
- Clicks the issue RGB20 button on the issue RGB20 page.
+ Clicks the issue NIA button on the issue NIA page.
Returns:
bool: True if the button is clicked, False otherwise.
"""
- return self.do_click(self.issue_rgb20_button()) if self.do_is_displayed(self.issue_rgb20_button()) else None
+ return self.do_click(self.issue_nia_button()) if self.do_is_displayed(self.issue_nia_button()) else None
diff --git a/e2e_tests/test/pageobjects/main_page_objects.py b/e2e_tests/test/pageobjects/main_page_objects.py
index f06b1ad1..b42ee5e0 100644
--- a/e2e_tests/test/pageobjects/main_page_objects.py
+++ b/e2e_tests/test/pageobjects/main_page_objects.py
@@ -20,8 +20,8 @@
from e2e_tests.test.pageobjects.enter_wallet_password_page import EnterWalletPasswordPageObjects
from e2e_tests.test.pageobjects.fungible_page import FungiblePageObjects
from e2e_tests.test.pageobjects.help_page import HelpPageObjects
-from e2e_tests.test.pageobjects.issue_rgb20_page import IssueRgb20PageObjects
-from e2e_tests.test.pageobjects.issue_rgb25_page import IssueRgb25PageObjects
+from e2e_tests.test.pageobjects.issue_cfa_page import IssueCfaPageObjects
+from e2e_tests.test.pageobjects.issue_nia_page import IssueNiaPageObjects
from e2e_tests.test.pageobjects.keyring_dialog_page import KeyringDialogBoxPageObjects
from e2e_tests.test.pageobjects.ln_endpoint_page import LnEndpointPageObjects
from e2e_tests.test.pageobjects.receive_asset_page import ReceiveAssetPageObjects
@@ -88,7 +88,7 @@ def __init__(self, application):
self.send_asset_page_objects = SendAssetPageObjects(self.application)
- self.issue_rgb20_page_objects = IssueRgb20PageObjects(self.application)
+ self.issue_nia_page_objects = IssueNiaPageObjects(self.application)
self.success_page_objects = SuccessPageObjects(self.application)
@@ -96,7 +96,7 @@ def __init__(self, application):
self.sidebar_page_objects = SidebarPageObjects(self.application)
- self.issue_rgb25_page_objects = IssueRgb25PageObjects(self.application)
+ self.issue_cfa_page_objects = IssueCfaPageObjects(self.application)
self.asset_detail_page_objects = AssetDetailPageObjects(
self.application,
diff --git a/e2e_tests/test/spec/test_about.py b/e2e_tests/test/spec/test_about.py
index fb7b83fc..d4a67ac6 100644
--- a/e2e_tests/test/spec/test_about.py
+++ b/e2e_tests/test/spec/test_about.py
@@ -27,7 +27,7 @@
@allure.story('Tests for copy buttons for node info')
def test_node_info(wallets_and_operations: WalletTestSetup):
"""Test asserting node info"""
- with allure.step('Create and fund first wallet for issue rgb20'):
+ with allure.step('Create and fund first wallet for issue nia'):
wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL, fund=False,
)
diff --git a/e2e_tests/test/spec/test_ask_auth_for_imp_operations.py b/e2e_tests/test/spec/test_ask_auth_for_imp_operations.py
index 0554126a..6351ff5f 100644
--- a/e2e_tests/test/spec/test_ask_auth_for_imp_operations.py
+++ b/e2e_tests/test/spec/test_ask_auth_for_imp_operations.py
@@ -24,7 +24,7 @@
ASSET_TICKER = 'TTK'
ASSET_NAME_1 = 'Tether'
ASSET_AMOUNT = '2000'
-ASSET_DESCRIPTION = 'RGB25 asset'
+ASSET_DESCRIPTION = 'CFA asset'
ASSET_NAME_2 = 'Test asset'
@@ -98,38 +98,38 @@ def test_ask_auth_for_imp_question_send_bitcoin_on(wallets_and_operations: Walle
@allure.story('Issuing and sending the RGB assets')
-def test_ask_auth_for_imp_question_issue_rgb_20_on(wallets_and_operations: WalletTestSetup):
- """Issuing RGB 20 asset with ask auth for important operations on"""
- with allure.step('Issuing RGB20 asset'):
+def test_ask_auth_for_imp_question_issue_nia_on(wallets_and_operations: WalletTestSetup):
+ """Issuing NIA asset with ask auth for important operations on"""
+ with allure.step('Issuing NIA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
FIRST_APPLICATION, ASSET_TICKER, ASSET_NAME_1, ASSET_AMOUNT, is_native_auth_enabled=True,
)
-@allure.story('Sending RGB20 asset')
-def test_ask_auth_for_imp_question_send_rgb20_on(wallets_and_operations: WalletTestSetup):
- """Sending RGB20 asset with ask auth for important operations on"""
+@allure.story('Sending NIA asset')
+def test_ask_auth_for_imp_question_send_nia_on(wallets_and_operations: WalletTestSetup):
+ """Sending NIA asset with ask auth for important operations on"""
with allure.step('Getting an RGB invoice'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
- rgb20_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
+ nia_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
SECOND_APPLICATION,
)
- with allure.step('Sending the RGB20 asset'):
+ with allure.step('Sending the NIA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
ASSET_AMOUNT,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_features.send_features.send(
- FIRST_APPLICATION, rgb20_invoice, ASSET_AMOUNT, is_native_auth_enabled=True,
+ FIRST_APPLICATION, nia_invoice, ASSET_AMOUNT, is_native_auth_enabled=True,
)
_, toaster_description = wallets_and_operations.first_page_objects.toaster_page_objects.click_toaster_frame()
with allure.step('asserting tx id'):
@@ -137,7 +137,7 @@ def test_ask_auth_for_imp_question_send_rgb20_on(wallets_and_operations: WalletT
SECOND_APPLICATION,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME_1,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_rgb_transaction_on_chain_frame()
@@ -148,40 +148,40 @@ def test_ask_auth_for_imp_question_send_rgb20_on(wallets_and_operations: WalletT
assert toaster_description == INFO_ASSET_SENT.format(tx_id)
-@allure.story('Issuing RGB25 asset')
-def test_ask_auth_for_imp_question_issue_rgb_25_on(wallets_and_operations: WalletTestSetup):
- """Issuing RGB25 asset with ask auth for important operations on"""
- with allure.step('Issuing RGB25 asset'):
+@allure.story('Issuing CFA asset')
+def test_ask_auth_for_imp_question_issue_cfa_on(wallets_and_operations: WalletTestSetup):
+ """Issuing CFA asset with ask auth for important operations on"""
+ with allure.step('Issuing CFA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_features.issue_rgb25_features.issue_rgb25_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_cfa_features.issue_cfa_with_sufficient_sats_and_utxo(
FIRST_APPLICATION, ASSET_NAME_1, ASSET_DESCRIPTION, ASSET_AMOUNT, is_native_auth_enabled=True,
)
-@allure.story('Sending RGB25 asset')
-def test_ask_auth_for_imp_question_send_rgb_25_on(wallets_and_operations: WalletTestSetup):
- """Sending RGB25 asset with ask auth for important operations on"""
+@allure.story('Sending CFA asset')
+def test_ask_auth_for_imp_question_send_cfa_on(wallets_and_operations: WalletTestSetup):
+ """Sending CFA asset with ask auth for important operations on"""
with allure.step('Getting an RGB invoice'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
- rgb25_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
+ cfa_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
SECOND_APPLICATION,
)
- with allure.step('Sending the RGB25 asset'):
+ with allure.step('Sending the CFA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
wallets_and_operations.first_page_objects.sidebar_page_objects.click_collectibles_button()
- wallets_and_operations.first_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.first_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME_1,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_features.send_features.send(
- FIRST_APPLICATION, rgb25_invoice, ASSET_AMOUNT, is_native_auth_enabled=True,
+ FIRST_APPLICATION, cfa_invoice, ASSET_AMOUNT, is_native_auth_enabled=True,
)
_, toaster_description = wallets_and_operations.first_page_objects.toaster_page_objects.click_toaster_frame()
with allure.step('asserting tx id'):
@@ -190,7 +190,7 @@ def test_ask_auth_for_imp_question_send_rgb_25_on(wallets_and_operations: Wallet
)
wallets_and_operations.second_page_objects.sidebar_page_objects.click_collectibles_button()
wallets_and_operations.second_page_objects.collectible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.second_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME_1,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_rgb_transaction_on_chain_frame()
@@ -255,39 +255,39 @@ def test_ask_auth_for_imp_question_send_bitcoin_off(wallets_and_operations: Wall
assert toaster_title == 'Success'
-@allure.story('Issuing RGB20 asset')
-def test_ask_auth_for_imp_question_issue_rgb_20_off(wallets_and_operations: WalletTestSetup):
- """Issuing RGB20 asset with ask auth for important operations off"""
- with allure.step('Issuing RGB20 asset'):
+@allure.story('Issuing NIA asset')
+def test_ask_auth_for_imp_question_issue_nia_off(wallets_and_operations: WalletTestSetup):
+ """Issuing NIA asset with ask auth for important operations off"""
+ with allure.step('Issuing NIA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
FIRST_APPLICATION, ASSET_TICKER, ASSET_NAME_2, ASSET_AMOUNT,
)
-@allure.story('Sending RGB20 asset')
-def test_ask_auth_for_imp_question_send_rgb_20_off(wallets_and_operations: WalletTestSetup):
- """Sending RGB20 asset with ask auth for important operations off"""
+@allure.story('Sending NIA asset')
+def test_ask_auth_for_imp_question_send_nia_off(wallets_and_operations: WalletTestSetup):
+ """Sending NIA asset with ask auth for important operations off"""
with allure.step('Getting an RGB invoice'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
- rgb20_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
+ nia_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
SECOND_APPLICATION,
)
- with allure.step('Sending the RGB20 asset'):
+ with allure.step('Sending the NIA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME_2,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_features.send_features.send(
- FIRST_APPLICATION, rgb20_invoice, ASSET_AMOUNT,
+ FIRST_APPLICATION, nia_invoice, ASSET_AMOUNT,
)
_, toaster_description = wallets_and_operations.first_page_objects.toaster_page_objects.click_toaster_frame()
wallets_and_operations.first_page_objects.fungible_page_objects.click_refresh_button()
@@ -296,7 +296,7 @@ def test_ask_auth_for_imp_question_send_rgb_20_off(wallets_and_operations: Walle
SECOND_APPLICATION,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME_2,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_rgb_transaction_on_chain_frame()
@@ -307,41 +307,41 @@ def test_ask_auth_for_imp_question_send_rgb_20_off(wallets_and_operations: Walle
assert toaster_description == INFO_ASSET_SENT.format(tx_id)
-@allure.story('Issuing RGB25 asset')
-def test_ask_auth_for_imp_question_issue_rgb_25_off(wallets_and_operations: WalletTestSetup):
- """Issuing RGB20 asset with ask auth for important operations off"""
- with allure.step('Issuing RGB25 asset'):
+@allure.story('Issuing CFA asset')
+def test_ask_auth_for_imp_question_issue_cfa_off(wallets_and_operations: WalletTestSetup):
+ """Issuing NIA asset with ask auth for important operations off"""
+ with allure.step('Issuing CFA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
wallets_and_operations.first_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.first_page_features.issue_rgb25_features.issue_rgb25_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_cfa_features.issue_cfa_with_sufficient_sats_and_utxo(
FIRST_APPLICATION, ASSET_NAME_2, ASSET_DESCRIPTION, ASSET_AMOUNT,
)
-@allure.story('Sending RGB25 asset')
-def test_ask_auth_for_imp_question_send_rgb_25_off(wallets_and_operations: WalletTestSetup):
- """Sending RGB25 asset with ask auth for important operations off"""
+@allure.story('Sending CFA asset')
+def test_ask_auth_for_imp_question_send_cfa_off(wallets_and_operations: WalletTestSetup):
+ """Sending CFA asset with ask auth for important operations off"""
with allure.step('Getting an RGB invoice'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
- rgb25_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
+ cfa_invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
SECOND_APPLICATION,
)
- with allure.step('Sending the RGB25 asset'):
+ with allure.step('Sending the CFA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
wallets_and_operations.first_page_objects.collectible_page_objects.click_refresh_button()
- wallets_and_operations.first_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.first_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME_2,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_features.send_features.send(
- FIRST_APPLICATION, rgb25_invoice, ASSET_AMOUNT,
+ FIRST_APPLICATION, cfa_invoice, ASSET_AMOUNT,
)
_, toaster_description = wallets_and_operations.first_page_objects.toaster_page_objects.click_toaster_frame()
with allure.step('asserting tx id'):
@@ -350,7 +350,7 @@ def test_ask_auth_for_imp_question_send_rgb_25_off(wallets_and_operations: Walle
)
wallets_and_operations.second_page_objects.sidebar_page_objects.click_collectibles_button()
wallets_and_operations.second_page_objects.collectible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.second_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME_2,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_rgb_transaction_on_chain_frame()
diff --git a/e2e_tests/test/spec/test_backup_and_restore.py b/e2e_tests/test/spec/test_backup_and_restore.py
index e36f6e6e..25425d62 100644
--- a/e2e_tests/test/spec/test_backup_and_restore.py
+++ b/e2e_tests/test/spec/test_backup_and_restore.py
@@ -9,6 +9,7 @@
from dotenv import load_dotenv
from accessible_constant import FIRST_APPLICATION
+from accessible_constant import SECOND_APPLICATION
from e2e_tests.test.utilities.app_setup import load_qm_translation
from e2e_tests.test.utilities.app_setup import test_environment
from e2e_tests.test.utilities.app_setup import wallets_and_operations
@@ -21,12 +22,22 @@
BACKUP_EMAIL_PASSWORD = os.getenv('BACKUP_EMAIL_PASSWORD')
MNEMONIC = None
PASSWORD = None
+NIA_ASSET_ID = None
+CFA_ASSET_ID = None
pytestmark = pytest.mark.order(1)
+NIA_TICKER = 'N20'
+NIA_NAME = 'NIA_Asset_A'
+NIA_TOTAL = '200'
+NIA_SEND_AMOUNT = '20'
+
+CFA_NAME = 'CFA_Asset'
+CFA_DESC = 'CFA Asset'
+CFA_TOTAL = '300'
+CFA_SEND_AMOUNT = '25'
@pytest.mark.skip_for_remote
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
@allure.feature('Mnemonic and backup configuration')
@allure.story('Mnemonic and backup configuration functionality')
def test_mnemonic_and_backup_configure(wallets_and_operations: WalletTestSetup, load_qm_translation):
@@ -43,10 +54,19 @@ def test_mnemonic_and_backup_configure(wallets_and_operations: WalletTestSetup,
global MNEMONIC
global PASSWORD
with allure.step('Create a embedded wallet'):
- wallets_and_operations.first_page_features.wallet_features.create_embedded_wallet(
+ wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
+ wallets_and_operations,
FIRST_APPLICATION,
)
+ wallets_and_operations.second_page_features.wallet_features.create_and_fund_wallet(
+ wallets_and_operations,
+ SECOND_APPLICATION,
+ )
+
with allure.step('Check the backup configuration'):
+ wallets_and_operations.first_page_operations.do_focus_on_application(
+ FIRST_APPLICATION,
+ )
backup_tooltip = wallets_and_operations.first_page_objects.fungible_page_objects.get_backup_tooltip()
assert backup_tooltip == TranslationManager.translate(
'backup_tooltip_text',
@@ -70,10 +90,88 @@ def test_mnemonic_and_backup_configure(wallets_and_operations: WalletTestSetup,
wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
+@allure.feature('Backup/Restore with cross-wallet transfers')
+@allure.story('Issue NIA/CFA, send between wallets, send BTC, backup and restore wallet A, then assert state')
+@pytest.mark.skip_for_remote
+def test_transfer_btc_and_asset(wallets_and_operations: WalletTestSetup, load_qm_translation, test_environment):
+ """
+ Scenario:
+ - Launch two apps (A and B) and fund both wallets
+ - In wallet A: issue NIA and send to wallet B (amount 20)
+ - In wallet B: issue CFA and send to wallet A (amount 25)
+ - Send BTC from A to B
+ - Backup wallet A, restart env, restore wallet A
+ - Assert wallet A shows received CFA and sent NIA, and BTC history reflects send
+ """
+ global NIA_ASSET_ID, CFA_ASSET_ID
+ with allure.step('Wallet A issues NIA'):
+ wallets_and_operations.first_page_operations.do_focus_on_application(
+ FIRST_APPLICATION,
+ )
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
+ application=FIRST_APPLICATION, asset_ticker=NIA_TICKER, asset_name=NIA_NAME, asset_amount=NIA_TOTAL,
+ )
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_NAME,
+ )
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_copy_button()
+ NIA_ASSET_ID = wallets_and_operations.first_page_operations.do_get_copied_address()
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_close_button()
+
+ with allure.step('Wallet B generates RGB receive invoice for NIA'):
+ wallets_and_operations.second_page_operations.do_focus_on_application(
+ SECOND_APPLICATION,
+ )
+ invoice_b = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
+ SECOND_APPLICATION,
+ )
+
+ with allure.step('Wallet A sends NIA to wallet B'):
+ wallets_and_operations.first_page_operations.do_focus_on_application(
+ FIRST_APPLICATION,
+ )
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_NAME,
+ )
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
+ wallets_and_operations.first_page_features.send_features.send(
+ application=FIRST_APPLICATION, receiver_invoice=invoice_b, amount=NIA_SEND_AMOUNT,
+ )
+
+ with allure.step('Wallet B issues CFA'):
+ wallets_and_operations.second_page_features.issue_cfa_features.issue_cfa_with_sufficient_sats_and_utxo(
+ application=SECOND_APPLICATION, asset_name=CFA_NAME, asset_description=CFA_DESC, asset_amount=CFA_TOTAL,
+ )
+ wallets_and_operations.second_page_objects.sidebar_page_objects.click_collectibles_button()
+ wallets_and_operations.second_page_objects.collectible_page_objects.click_cfa_frame(
+ CFA_NAME,
+ )
+ wallets_and_operations.second_page_objects.asset_detail_page_objects.click_copy_button()
+ CFA_ASSET_ID = wallets_and_operations.second_page_operations.do_get_copied_address()
+ wallets_and_operations.second_page_objects.asset_detail_page_objects.click_close_button()
+
+ with allure.step('Wallet A generates RGB receive invoice for CFA'):
+ invoice_a = wallets_and_operations.first_page_features.receive_features.receive_asset_from_sidebar(
+ FIRST_APPLICATION,
+ )
+
+ with allure.step('Wallet B sends CFA to wallet A'):
+ wallets_and_operations.second_page_operations.do_focus_on_application(
+ SECOND_APPLICATION,
+ )
+ wallets_and_operations.second_page_objects.sidebar_page_objects.click_collectibles_button()
+ wallets_and_operations.second_page_objects.collectible_page_objects.click_cfa_frame(
+ CFA_NAME,
+ )
+ wallets_and_operations.second_page_objects.asset_detail_page_objects.click_send_button()
+ wallets_and_operations.second_page_features.send_features.send(
+ application=SECOND_APPLICATION, receiver_invoice=invoice_a, amount=CFA_SEND_AMOUNT,
+ )
+
+
@allure.feature('Backup page')
@allure.story('Backup page functionality')
@pytest.mark.skip_for_remote
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
def test_backup(test_environment, wallets_and_operations: WalletTestSetup):
"""
Test the backup page functionality.
@@ -84,6 +182,9 @@ def test_backup(test_environment, wallets_and_operations: WalletTestSetup):
"""
description = None
with allure.step('Configure backup'):
+ wallets_and_operations.first_page_operations.do_focus_on_application(
+ FIRST_APPLICATION,
+ )
wallets_and_operations.first_page_objects.sidebar_page_objects.click_backup_button()
wallets_and_operations.first_page_objects.backup_page_objects.click_configurable_button()
wallets_and_operations.first_page_objects.backup_page_objects.click_backup_window()
@@ -118,13 +219,15 @@ def test_backup(test_environment, wallets_and_operations: WalletTestSetup):
@allure.feature('Restore page')
@allure.story('Restore page functionality')
@pytest.mark.skip_for_remote
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
def test_restore(wallets_and_operations: WalletTestSetup):
"""
This test case is used to restore the wallet from the backup.
"""
description = None
with allure.step('Restore the wallet'):
+ wallets_and_operations.first_page_operations.do_focus_on_application(
+ FIRST_APPLICATION,
+ )
wallets_and_operations.first_page_objects.term_and_condition_page_objects.scroll_to_end()
wallets_and_operations.first_page_objects.term_and_condition_page_objects.click_accept_button()
wallets_and_operations.first_page_objects.wallet_selection_page_objects.click_embedded_button()
@@ -160,3 +263,42 @@ def test_restore(wallets_and_operations: WalletTestSetup):
wallets_and_operations.first_page_operations.wait_for_toaster_message()
_, description = wallets_and_operations.first_page_objects.toaster_page_objects.click_toaster_frame()
assert description == INFO_RESTORE_COMPLETED
+ wallets_and_operations.first_page_objects.enter_wallet_password_page_objects.enter_password(
+ PASSWORD,
+ )
+ wallets_and_operations.first_page_objects.enter_wallet_password_page_objects.click_login_button()
+
+
+@allure.feature('Restore page')
+@allure.story('Restore page functionality')
+@pytest.mark.skip_for_remote
+def test_restore_asset(wallets_and_operations: WalletTestSetup):
+ """
+ This test case is used to restore the wallet from the backup.
+ """
+ with allure.step('assert the wallet assets'):
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_NAME,
+ )
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_copy_button()
+ nia_asset_id = wallets_and_operations.first_page_operations.do_get_copied_address()
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_rgb_transaction_on_chain_frame()
+ nia_transferred_amount = wallets_and_operations.first_page_objects.asset_transaction_detail_page_objects.get_transferred_amount()
+ wallets_and_operations.first_page_objects.asset_transaction_detail_page_objects.click_close_button()
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_close_button()
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_collectibles_button()
+ wallets_and_operations.first_page_objects.collectible_page_objects.click_cfa_frame(
+ CFA_NAME,
+ )
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_copy_button()
+ cfa_asset_id = wallets_and_operations.first_page_operations.do_get_copied_address()
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_rgb_transaction_on_chain_frame()
+ cfa_transferred_amount = wallets_and_operations.first_page_objects.asset_transaction_detail_page_objects.get_transferred_amount()
+ wallets_and_operations.first_page_objects.asset_transaction_detail_page_objects.click_close_button()
+ wallets_and_operations.first_page_objects.asset_detail_page_objects.click_close_button()
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_collectibles_button()
+
+ assert nia_asset_id == NIA_ASSET_ID
+ assert cfa_asset_id == CFA_ASSET_ID
+ assert nia_transferred_amount == f'-{NIA_SEND_AMOUNT}'
+ assert cfa_transferred_amount == f'+{CFA_SEND_AMOUNT}'
diff --git a/e2e_tests/test/spec/test_create_and_close_channel.py b/e2e_tests/test/spec/test_create_and_close_channel.py
index c349b5f8..1e1f155f 100644
--- a/e2e_tests/test/spec/test_create_and_close_channel.py
+++ b/e2e_tests/test/spec/test_create_and_close_channel.py
@@ -43,14 +43,14 @@ def test_create_channel_with_actual_value(wallets_and_operations: WalletTestSetu
wallets_and_operations=wallets_and_operations, application=SECOND_APPLICATION, application_url=SECOND_APPLICATION_URL,
)
- with allure.step('Issue RGB20 asset for rgb20 channel creation'):
+ with allure.step('Issue NIA asset for nia channel creation'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=ASSET_NAME, asset_amount=ASSET_AMOUNT,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
asset_name=ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_copy_button()
diff --git a/e2e_tests/test/spec/test_fail_transfer.py b/e2e_tests/test/spec/test_fail_transfer.py
index 47f17efc..edadc932 100644
--- a/e2e_tests/test/spec/test_fail_transfer.py
+++ b/e2e_tests/test/spec/test_fail_transfer.py
@@ -14,7 +14,7 @@
ASSET_TICKER = 'TTK'
-RGB20_ASSET_NAME = 'Tether'
+NIA_ASSET_NAME = 'Tether'
ASSET_AMOUNT = '2000'
@@ -30,13 +30,13 @@ def test_fail_transfer(wallets_and_operations: WalletTestSetup):
)
with allure.step('Issuing an RGB asset'):
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
- FIRST_APPLICATION, ASSET_TICKER, RGB20_ASSET_NAME, ASSET_AMOUNT,
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
+ FIRST_APPLICATION, ASSET_TICKER, NIA_ASSET_NAME, ASSET_AMOUNT,
)
with allure.step('Generating an invoice'):
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
- asset_name=RGB20_ASSET_NAME,
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ asset_name=NIA_ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_receive_button()
wallets_and_operations.first_page_features.receive_features.receive(
@@ -44,8 +44,8 @@ def test_fail_transfer(wallets_and_operations: WalletTestSetup):
)
with allure.step('Failing the transfer'):
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
- asset_name=RGB20_ASSET_NAME,
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ asset_name=NIA_ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_fail_transfer_button()
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_confirmation_continue_button()
diff --git a/e2e_tests/test/spec/test_hide_exhausted_asset.py b/e2e_tests/test/spec/test_hide_exhausted_asset.py
index 604ba894..72cd7b8a 100644
--- a/e2e_tests/test/spec/test_hide_exhausted_asset.py
+++ b/e2e_tests/test/spec/test_hide_exhausted_asset.py
@@ -15,7 +15,7 @@
ASSET_TICKER = 'TTK'
ASSET_NAME = 'Tether'
ASSET_AMOUNT = '2000'
-ISSUE_RGB20_TOASTER_MESSAGE = 'You have insufficient funds'
+ISSUE_NIA_TOASTER_MESSAGE = 'You have insufficient funds'
@allure.feature('Hide exhausted asset')
@@ -46,7 +46,7 @@ def test_hide_exhausted_asset_on(wallets_and_operations: WalletTestSetup):
with allure.step('Issuing a asset'):
wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
FIRST_APPLICATION, ASSET_TICKER, ASSET_NAME, ASSET_AMOUNT,
)
@@ -63,7 +63,7 @@ def test_hide_exhausted_asset_on(wallets_and_operations: WalletTestSetup):
FIRST_APPLICATION,
)
wallets_and_operations.first_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
diff --git a/e2e_tests/test/spec/test_issue_cfa_asset.py b/e2e_tests/test/spec/test_issue_cfa_asset.py
new file mode 100644
index 00000000..73d3b710
--- /dev/null
+++ b/e2e_tests/test/spec/test_issue_cfa_asset.py
@@ -0,0 +1,105 @@
+# pylint: disable=redefined-outer-name, unused-import
+"""
+Tests for issuing cfa assets with different scenarios.
+"""
+from __future__ import annotations
+
+import allure
+import pytest
+
+from accessible_constant import FIRST_APPLICATION
+from accessible_constant import FIRST_APPLICATION_URL
+from e2e_tests.test.utilities.app_setup import test_environment
+from e2e_tests.test.utilities.app_setup import wallets_and_operations
+from e2e_tests.test.utilities.app_setup import WalletTestSetup
+
+CFA_ASSET_NAME = 'Cfa'
+ASSET_DESCRIPTION = 'This is Cfa asset'
+ASSET_AMOUNT = '2000'
+ISSUE_CFA_TOASTER_MESSAGE = 'You have insufficient funds'
+
+
+@pytest.mark.parametrize('test_environment', [False], indirect=True)
+@allure.feature('Issue cfa asset without sufficient sats')
+@allure.story('Issue cfa asset without sufficient sats which will produce error toaster')
+def test_issue_cfa_without_sufficient_sats(wallets_and_operations: WalletTestSetup):
+ """
+ Test issuing cfa asset without sufficient sats.
+ """
+
+ with allure.step('Create and fund first wallet for issue cfa'):
+ wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
+ wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL, fund=False,
+ )
+
+ with allure.step('Issue cfa asset without sat'):
+ description = wallets_and_operations.first_page_features.issue_cfa_features.issue_cfa_asset_without_sat(
+ FIRST_APPLICATION, CFA_ASSET_NAME, ASSET_DESCRIPTION, ASSET_AMOUNT,
+ )
+
+ with allure.step('Verify toaster title and message'):
+ assert description == ISSUE_CFA_TOASTER_MESSAGE
+
+
+@pytest.mark.parametrize('test_environment', [False], indirect=True)
+@allure.feature('Issue cfa asset with sufficient sats but no utxo')
+@allure.story('Issue cfa asset with sufficient sats and no utxo which will first create utxo and then create asset')
+def test_issue_cfa_with_sufficient_sats_and_no_utxo(wallets_and_operations: WalletTestSetup):
+ """
+ Test issuing cfa asset with sufficient sats but no utxo.
+ """
+
+ with allure.step('Fund wallet for issue cfa asset'):
+ wallets_and_operations.first_page_features.wallet_features.fund_wallet(
+ FIRST_APPLICATION,
+ )
+
+ with allure.step('Verifies there is no utxo for issue cfa asset'):
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
+ count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
+ assert count == 1
+
+ with allure.step('Issue cfa with sufficient sats and no utxo'):
+ wallets_and_operations.first_page_features.issue_cfa_features.issue_cfa_with_sufficient_sats_and_no_utxo(
+ FIRST_APPLICATION, CFA_ASSET_NAME, ASSET_DESCRIPTION, ASSET_AMOUNT,
+ )
+
+ with allure.step('Verify asset name'):
+ asset_name = wallets_and_operations.first_page_objects.collectible_page_objects.get_cfa_asset_name(
+ CFA_ASSET_NAME,
+ )
+ assert asset_name == CFA_ASSET_NAME
+
+
+@pytest.mark.parametrize('test_environment', [False], indirect=True)
+@allure.feature('Issue cfa asset with sufficient sats and utxo')
+@allure.story('Issue cfa asset with sufficient sats and utxo which will create asset')
+def test_issue_cfa_with_sufficient_sats_and_utxo(wallets_and_operations: WalletTestSetup):
+ """
+ Test issuing cfa asset with sufficient sats and utxo.
+ """
+
+ with allure.step('Verified that one utxo exists for issue cfa asset'):
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
+
+ count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
+
+ asset_id = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_utxo_asset_id(
+ 'NA',
+ )
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
+
+ assert count == 3
+ assert asset_id == 'NA'
+
+ with allure.step('Issue cfa with sufficient sats and utxo'):
+ wallets_and_operations.first_page_features.issue_cfa_features.issue_cfa_with_sufficient_sats_and_utxo(
+ FIRST_APPLICATION, CFA_ASSET_NAME, ASSET_DESCRIPTION, ASSET_AMOUNT,
+ )
+
+ with allure.step('Verify asset name'):
+ asset_name = wallets_and_operations.first_page_objects.collectible_page_objects.get_cfa_asset_name(
+ CFA_ASSET_NAME,
+ )
+ assert asset_name == CFA_ASSET_NAME
diff --git a/e2e_tests/test/spec/test_issue_nia_asset.py b/e2e_tests/test/spec/test_issue_nia_asset.py
new file mode 100644
index 00000000..1884ad08
--- /dev/null
+++ b/e2e_tests/test/spec/test_issue_nia_asset.py
@@ -0,0 +1,101 @@
+# pylint: disable=redefined-outer-name, unused-import
+"""
+Tests for nia asset issuance.
+"""
+from __future__ import annotations
+
+import allure
+import pytest
+
+from accessible_constant import FIRST_APPLICATION
+from accessible_constant import FIRST_APPLICATION_URL
+from e2e_tests.test.utilities.app_setup import test_environment
+from e2e_tests.test.utilities.app_setup import wallets_and_operations
+from e2e_tests.test.utilities.app_setup import WalletTestSetup
+
+ASSET_TICKER = 'TTK'
+NIA_ASSET_NAME = 'Tether'
+ASSET_AMOUNT = '2000'
+ISSUE_NIA_TOASTER_MESSAGE = 'You have insufficient funds'
+
+
+@pytest.mark.parametrize('test_environment', [False], indirect=True)
+@allure.feature('Issue nia asset without sufficient sats')
+@allure.story('Issue nia asset without sufficient sats which will produce error toaster')
+def test_issue_nia_without_sufficient_sats(wallets_and_operations: WalletTestSetup):
+ """
+ Test nia asset issuance without sufficient sats.
+ """
+
+ with allure.step('Create and fund first wallet for issue nia'):
+ wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
+ wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL, fund=False,
+ )
+
+ with allure.step('Issue nia asset without sufficient sats'):
+ description = wallets_and_operations.first_page_features.issue_nia_features.issue_nia_asset_without_sat(
+ FIRST_APPLICATION, ASSET_TICKER, NIA_ASSET_NAME, ASSET_AMOUNT,
+ )
+
+ assert description == ISSUE_NIA_TOASTER_MESSAGE
+
+
+@pytest.mark.parametrize('test_environment', [False], indirect=True)
+@allure.feature('Issue nia asset with sufficient sats and no utxo')
+@allure.story('Issue nia asset with sufficient sats which will create utxo and create asset')
+def test_issue_nia_with_sufficient_sats_and_no_utxo(wallets_and_operations: WalletTestSetup):
+ """
+ Test nia asset issuance with sufficient sats and no utxo.
+ """
+
+ with allure.step('Fund wallet for issue nia asset'):
+ wallets_and_operations.first_page_features.wallet_features.fund_wallet(
+ FIRST_APPLICATION,
+ )
+
+ with allure.step('Verifies there is no utxo for issue nia asset'):
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
+ count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
+ assert count == 1
+
+ with allure.step('Issue nia asset with sufficient sats and no utxo'):
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_no_utxo(
+ FIRST_APPLICATION, ASSET_TICKER, NIA_ASSET_NAME, ASSET_AMOUNT,
+ )
+
+ with allure.step('Verify asset name'):
+ asset_name = wallets_and_operations.first_page_objects.fungible_page_objects.get_nia_asset_name(
+ NIA_ASSET_NAME,
+ )
+ assert asset_name == NIA_ASSET_NAME
+
+
+@pytest.mark.parametrize('test_environment', [False], indirect=True)
+@allure.feature('Issue nia asset with sufficient sats')
+@allure.story('Issue nia asset with sufficient sats which will create asset')
+def test_issue_nia_with_sufficient_sats_and_utxo(wallets_and_operations: WalletTestSetup):
+ """
+ Test nia asset issuance with sufficient sats and utxo.
+ """
+
+ with allure.step('Verified that one utxo exists for issue nia asset'):
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
+ count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
+ nia_asset_id = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_utxo_asset_id(
+ 'NA',
+ )
+ wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
+ assert count == 3
+ assert nia_asset_id == 'NA'
+
+ with allure.step('Issue nia asset with sufficient sats and utxo'):
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
+ FIRST_APPLICATION, ASSET_TICKER, NIA_ASSET_NAME, ASSET_AMOUNT,
+ )
+
+ with allure.step('Verify asset name'):
+ asset_name = wallets_and_operations.first_page_objects.fungible_page_objects.get_nia_asset_name(
+ NIA_ASSET_NAME,
+ )
+ assert asset_name == NIA_ASSET_NAME
diff --git a/e2e_tests/test/spec/test_issue_rgb20_asset.py b/e2e_tests/test/spec/test_issue_rgb20_asset.py
deleted file mode 100644
index a93969ef..00000000
--- a/e2e_tests/test/spec/test_issue_rgb20_asset.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# pylint: disable=redefined-outer-name, unused-import
-"""
-Tests for rgb20 asset issuance.
-"""
-from __future__ import annotations
-
-import allure
-import pytest
-
-from accessible_constant import FIRST_APPLICATION
-from accessible_constant import FIRST_APPLICATION_URL
-from e2e_tests.test.utilities.app_setup import test_environment
-from e2e_tests.test.utilities.app_setup import wallets_and_operations
-from e2e_tests.test.utilities.app_setup import WalletTestSetup
-from src.model.enums.enums_model import WalletType
-
-ASSET_TICKER = 'TTK'
-RGB20_ASSET_NAME = 'Tether'
-ASSET_AMOUNT = '2000'
-ISSUE_RGB20_TOASTER_MESSAGE = 'You have insufficient funds'
-
-
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
-@allure.feature('Issue rgb20 asset without sufficient sats')
-@allure.story('Issue rgb20 asset without sufficient sats which will produce error toaster')
-def test_issue_rgb20_without_sufficient_sats(wallets_and_operations: WalletTestSetup):
- """
- Test rgb20 asset issuance without sufficient sats.
- """
-
- with allure.step('Create and fund first wallet for issue rgb20'):
- wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
- wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL, fund=False,
- )
-
- with allure.step('Issue rgb20 asset without sufficient sats'):
- description = wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_asset_without_sat(
- FIRST_APPLICATION, ASSET_TICKER, RGB20_ASSET_NAME, ASSET_AMOUNT,
- )
-
- assert description == ISSUE_RGB20_TOASTER_MESSAGE
-
-
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
-@allure.feature('Issue rgb20 asset with sufficient sats and no utxo')
-@allure.story('Issue rgb20 asset with sufficient sats which will create utxo and create asset')
-def test_issue_rgb20_with_sufficient_sats_and_no_utxo(wallets_and_operations: WalletTestSetup):
- """
- Test rgb20 asset issuance with sufficient sats and no utxo.
- """
-
- with allure.step('Fund wallet for issue rgb20 asset'):
- wallets_and_operations.first_page_features.wallet_features.fund_wallet(
- FIRST_APPLICATION,
- )
-
- with allure.step('Verifies there is no utxo for issue rgb20 asset'):
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
- count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
- assert count == 1
-
- with allure.step('Issue rgb20 asset with sufficient sats and no utxo'):
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_no_utxo(
- FIRST_APPLICATION, ASSET_TICKER, RGB20_ASSET_NAME, ASSET_AMOUNT,
- )
-
- with allure.step('Verify asset name'):
- asset_name = wallets_and_operations.first_page_objects.fungible_page_objects.get_rgb20_asset_name(
- RGB20_ASSET_NAME,
- )
- assert asset_name == RGB20_ASSET_NAME
-
-
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
-@allure.feature('Issue rgb20 asset with sufficient sats')
-@allure.story('Issue rgb20 asset with sufficient sats which will create asset')
-def test_issue_rgb20_with_sufficient_sats_and_utxo(wallets_and_operations: WalletTestSetup):
- """
- Test rgb20 asset issuance with sufficient sats and utxo.
- """
-
- with allure.step('Verified that one utxo exists for issue rgb20 asset'):
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
- count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
- rgb20_asset_id = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_utxo_asset_id(
- 'NA',
- )
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
- assert count == 3
- assert rgb20_asset_id == 'NA'
-
- with allure.step('Issue rgb20 asset with sufficient sats and utxo'):
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
- FIRST_APPLICATION, ASSET_TICKER, RGB20_ASSET_NAME, ASSET_AMOUNT,
- )
-
- with allure.step('Verify asset name'):
- asset_name = wallets_and_operations.first_page_objects.fungible_page_objects.get_rgb20_asset_name(
- RGB20_ASSET_NAME,
- )
- assert asset_name == RGB20_ASSET_NAME
diff --git a/e2e_tests/test/spec/test_issue_rgb25_asset.py b/e2e_tests/test/spec/test_issue_rgb25_asset.py
deleted file mode 100644
index f3295e9c..00000000
--- a/e2e_tests/test/spec/test_issue_rgb25_asset.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# pylint: disable=redefined-outer-name, unused-import
-"""
-Tests for issuing rgb25 assets with different scenarios.
-"""
-from __future__ import annotations
-
-import allure
-import pytest
-
-from accessible_constant import FIRST_APPLICATION
-from accessible_constant import FIRST_APPLICATION_URL
-from e2e_tests.test.utilities.app_setup import test_environment
-from e2e_tests.test.utilities.app_setup import wallets_and_operations
-from e2e_tests.test.utilities.app_setup import WalletTestSetup
-from src.model.enums.enums_model import WalletType
-
-RGB25_ASSET_NAME = 'Rgb25'
-ASSET_DESCRIPTION = 'This is Rgb25 asset'
-ASSET_AMOUNT = '2000'
-ISSUE_RGB25_TOASTER_MESSAGE = 'You have insufficient funds'
-
-
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
-@allure.feature('Issue rgb25 asset without sufficient sats')
-@allure.story('Issue rgb25 asset without sufficient sats which will produce error toaster')
-def test_issue_rgb25_without_sufficient_sats(wallets_and_operations: WalletTestSetup):
- """
- Test issuing rgb25 asset without sufficient sats.
- """
-
- with allure.step('Create and fund first wallet for issue rgb25'):
- wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
- wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL, fund=False,
- )
-
- with allure.step('Issue rgb25 asset without sat'):
- description = wallets_and_operations.first_page_features.issue_rgb25_features.issue_rgb25_asset_without_sat(
- FIRST_APPLICATION, RGB25_ASSET_NAME, ASSET_DESCRIPTION, ASSET_AMOUNT,
- )
-
- with allure.step('Verify toaster title and message'):
- assert description == ISSUE_RGB25_TOASTER_MESSAGE
-
-
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
-@allure.feature('Issue rgb25 asset with sufficient sats but no utxo')
-@allure.story('Issue rgb25 asset with sufficient sats and no utxo which will first create utxo and then create asset')
-def test_issue_rgb25_with_sufficient_sats_and_no_utxo(wallets_and_operations: WalletTestSetup):
- """
- Test issuing rgb25 asset with sufficient sats but no utxo.
- """
-
- with allure.step('Fund wallet for issue rgb25 asset'):
- wallets_and_operations.first_page_features.wallet_features.fund_wallet(
- FIRST_APPLICATION,
- )
-
- with allure.step('Verifies there is no utxo for issue rgb25 asset'):
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
- count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
- assert count == 1
-
- with allure.step('Issue rgb25 with sufficient sats and no utxo'):
- wallets_and_operations.first_page_features.issue_rgb25_features.issue_rgb25_with_sufficient_sats_and_no_utxo(
- FIRST_APPLICATION, RGB25_ASSET_NAME, ASSET_DESCRIPTION, ASSET_AMOUNT,
- )
-
- with allure.step('Verify asset name'):
- asset_name = wallets_and_operations.first_page_objects.collectible_page_objects.get_rgb25_asset_name(
- RGB25_ASSET_NAME,
- )
- assert asset_name == RGB25_ASSET_NAME
-
-
-@pytest.mark.parametrize('test_environment', [False], indirect=True)
-@allure.feature('Issue rgb25 asset with sufficient sats and utxo')
-@allure.story('Issue rgb25 asset with sufficient sats and utxo which will create asset')
-def test_issue_rgb25_with_sufficient_sats_and_utxo(wallets_and_operations: WalletTestSetup):
- """
- Test issuing rgb25 asset with sufficient sats and utxo.
- """
-
- with allure.step('Verified that one utxo exists for issue rgb25 asset'):
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_view_unspents_button()
-
- count = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_widget()
-
- asset_id = wallets_and_operations.first_page_objects.view_unspent_list_page_objects.get_unspent_utxo_asset_id(
- 'NA',
- )
- wallets_and_operations.first_page_objects.sidebar_page_objects.click_fungibles_button()
-
- assert count == 3
- assert asset_id == 'NA'
-
- with allure.step('Issue rgb25 with sufficient sats and utxo'):
- wallets_and_operations.first_page_features.issue_rgb25_features.issue_rgb25_with_sufficient_sats_and_utxo(
- FIRST_APPLICATION, RGB25_ASSET_NAME, ASSET_DESCRIPTION, ASSET_AMOUNT,
- )
-
- with allure.step('Verify asset name'):
- asset_name = wallets_and_operations.first_page_objects.collectible_page_objects.get_rgb25_asset_name(
- RGB25_ASSET_NAME,
- )
- assert asset_name == RGB25_ASSET_NAME
diff --git a/e2e_tests/test/spec/test_rgb20_channel_tx.py b/e2e_tests/test/spec/test_nia_channel_tx.py
similarity index 87%
rename from e2e_tests/test/spec/test_rgb20_channel_tx.py
rename to e2e_tests/test/spec/test_nia_channel_tx.py
index 67e0da34..61935af8 100644
--- a/e2e_tests/test/spec/test_rgb20_channel_tx.py
+++ b/e2e_tests/test/spec/test_nia_channel_tx.py
@@ -1,5 +1,5 @@
# pylint: disable=redefined-outer-name, unused-import, too-many-statements,unused-argument
-"""Tests for send/receive RGB20 asset with channel"""
+"""Tests for send/receive NIA asset with channel"""
from __future__ import annotations
import re
@@ -17,7 +17,6 @@
from e2e_tests.test.utilities.app_setup import WalletTestSetup
from e2e_tests.test.utilities.model import TransferType
from e2e_tests.test.utilities.translation_utils import TranslationManager
-
ASSET_TICKER = 'TTK'
ASSET_NAME = 'Tether'
ASSET_AMOUNT = '2000'
@@ -30,31 +29,27 @@
INVALID_AMOUNT = '120'
-@allure.feature('Channel send and receive for RGB20 asset')
-@allure.story('Send and receive with correct invoice for RGB20 asset')
-def test_send_and_receive_with_correct_invoice_for_rgb20(wallets_and_operations: WalletTestSetup):
- """Test sending and receiving with correct invoice for RGB20"""
+@allure.feature('Channel send and receive for NIA asset')
+@allure.story('Send and receive with correct invoice for NIA asset')
+def test_send_and_receive_with_correct_invoice_for_nia(wallets_and_operations: WalletTestSetup):
+ """Test sending and receiving with correct invoice for NIA"""
node_uri = None
-
- with allure.step('Create and fund first wallet for rgb20 channel'):
+ with allure.step('Create and fund first wallet for nia channel'):
wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL,
)
-
- with allure.step('Create and fund second wallet for rgb20 channel'):
+ with allure.step('Create and fund second wallet for nia channel'):
wallets_and_operations.second_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=SECOND_APPLICATION, application_url=SECOND_APPLICATION_URL,
)
-
- with allure.step('Issue RGB20 asset'):
+ with allure.step('Issue NIA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=ASSET_NAME, asset_amount=ASSET_AMOUNT,
)
-
- with allure.step('Get node URI for rgb20 channel'):
+ with allure.step('Get node URI for nia channel'):
if wallets_and_operations.wallet_mode == 'embedded':
node_uri = wallets_and_operations.second_page_features.channel_features.get_node_uri_for_embedded(
application=SECOND_APPLICATION, ip_address=IP_ADDRESS,
@@ -63,21 +58,19 @@ def test_send_and_receive_with_correct_invoice_for_rgb20(wallets_and_operations:
node_uri = wallets_and_operations.second_page_features.channel_features.get_node_uri_for_remote(
application=SECOND_APPLICATION, ip_address=IP_ADDRESS, ln_port=LN_PORT,
)
-
- with allure.step('Create channel for rgb20'):
+ with allure.step('Create channel for nia'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
_channel_status = wallets_and_operations.first_page_features.channel_features.create_channel(
application=FIRST_APPLICATION, node_uri=node_uri, asset_ticker=ASSET_TICKER, asset_amount=CREATE_ASSET_CHANNEL_WITH_AMOUNT, channel_capacity=CHANNEL_CAPACITY,
)
-
with allure.step('Create invoice'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
asset_name=ASSET_NAME,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_receive_button()
@@ -85,25 +78,23 @@ def test_send_and_receive_with_correct_invoice_for_rgb20(wallets_and_operations:
application=SECOND_APPLICATION, transfer_type=TransferType.LIGHTNING.value, value=INVOICE_AMOUNT,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
-
with allure.step('Send with invoice'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
asset_name=ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_features.send_features.send(
application=FIRST_APPLICATION, receiver_invoice=invoice, transfer_type=TransferType.LIGHTNING.value,
)
-
with allure.step('validate transaction amount'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
asset_name=ASSET_NAME,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_refresh_button()
@@ -126,13 +117,12 @@ def test_create_wrong_amount_invoice(wallets_and_operations: WalletTestSetup, lo
"""
Test case to create wrong ln invoice with insufficient amount
"""
-
with allure.step('Create invoice'):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
asset_name=ASSET_NAME,
)
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_receive_button()
diff --git a/e2e_tests/test/spec/test_refresh_transfer.py b/e2e_tests/test/spec/test_refresh_transfer.py
index c7bd3215..58b85ef6 100644
--- a/e2e_tests/test/spec/test_refresh_transfer.py
+++ b/e2e_tests/test/spec/test_refresh_transfer.py
@@ -34,8 +34,8 @@ def test_refresh_transfer(wallets_and_operations: WalletTestSetup):
wallets_and_operations=wallets_and_operations, application=SECOND_APPLICATION, application_url=SECOND_APPLICATION_URL,
)
- with allure.step('Issue RGB20 asset for refresh transfer'):
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
+ with allure.step('Issue NIA asset for refresh transfer'):
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=ASSET_NAME, asset_amount=ASSET_AMOUNT,
)
@@ -44,12 +44,12 @@ def test_refresh_transfer(wallets_and_operations: WalletTestSetup):
SECOND_APPLICATION,
)
- with allure.step('Send RGB20 asset to correct invoice'):
+ with allure.step('Send NIA asset to correct invoice'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME,
)
@@ -81,7 +81,7 @@ def test_refresh_transfer(wallets_and_operations: WalletTestSetup):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME,
)
actual_transfer_status_first_app = wallets_and_operations.first_page_objects.asset_detail_page_objects.get_transfer_status()
@@ -89,7 +89,7 @@ def test_refresh_transfer(wallets_and_operations: WalletTestSetup):
wallets_and_operations.second_page_operations.do_focus_on_application(
SECOND_APPLICATION,
)
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
ASSET_NAME,
)
actual_transfer_status_second_app = wallets_and_operations.second_page_objects.asset_detail_page_objects.get_transfer_status()
diff --git a/e2e_tests/test/spec/test_send_and_receive_rgb25_asset.py b/e2e_tests/test/spec/test_send_and_receive_cfa_asset.py
similarity index 82%
rename from e2e_tests/test/spec/test_send_and_receive_rgb25_asset.py
rename to e2e_tests/test/spec/test_send_and_receive_cfa_asset.py
index fe06cafd..9fed4edd 100644
--- a/e2e_tests/test/spec/test_send_and_receive_rgb25_asset.py
+++ b/e2e_tests/test/spec/test_send_and_receive_cfa_asset.py
@@ -1,5 +1,5 @@
# pylint: disable=redefined-outer-name, unused-import
-"""Iris wallet send and receive operation automation test suite for rgb25 asset"""
+"""Iris wallet send and receive operation automation test suite for cfa asset"""
from __future__ import annotations
import allure
@@ -15,39 +15,39 @@
from e2e_tests.test.utilities.translation_utils import TranslationManager
from src.model.enums.enums_model import TransactionStatusEnumModel
-ASSET_NAME = 'RGB25'
-ASSET_DESCRIPTION = 'This is rgb25 asset'
+ASSET_NAME = 'CFA'
+ASSET_DESCRIPTION = 'This is cfa asset'
ASSET_AMOUNT = '2000'
SEND_AMOUNT = '50'
INVOICE = 'rgb:~/~/utxob:2msKeFq-uPjwpYxVY-jKS2ymYBq-SqmyP3ovg-AGvth8491-J7seMBm?expiry=1709616110&endpoints=rpc://10.0.2.2:3000/json-rpc'
-@allure.feature('Automation of send operation for RGB25 asset in iris wallet')
-@allure.story('Testing send RGB25 asset with expired invoice')
-def test_send_rgb25_with_expired_invoice(wallets_and_operations: WalletTestSetup):
- """Test send rgb25 asset with expired invoice"""
+@allure.feature('Automation of send operation for CFA asset in iris wallet')
+@allure.story('Testing send CFA asset with expired invoice')
+def test_send_cfa_with_expired_invoice(wallets_and_operations: WalletTestSetup):
+ """Test send cfa asset with expired invoice"""
- with allure.step('Create and fund first wallet for send and receive rgb25'):
+ with allure.step('Create and fund first wallet for send and receive cfa'):
wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL,
)
- with allure.step('Create and fund second wallet for send and receive rgb25'):
+ with allure.step('Create and fund second wallet for send and receive cfa'):
wallets_and_operations.second_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=SECOND_APPLICATION, application_url=SECOND_APPLICATION_URL,
)
- with allure.step('Issue RGB25 asset'):
- wallets_and_operations.first_page_features.issue_rgb25_features.issue_rgb25_with_sufficient_sats_and_utxo(
+ with allure.step('Issue CFA asset'):
+ wallets_and_operations.first_page_features.issue_cfa_features.issue_cfa_with_sufficient_sats_and_utxo(
application=FIRST_APPLICATION, asset_description=ASSET_DESCRIPTION, asset_name=ASSET_NAME, asset_amount=ASSET_AMOUNT,
)
- with allure.step('Send RGB25 asset with expired invoice'):
+ with allure.step('Send CFA asset with expired invoice'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
wallets_and_operations.first_page_objects.sidebar_page_objects.click_collectibles_button()
- wallets_and_operations.first_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.first_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
@@ -58,27 +58,27 @@ def test_send_rgb25_with_expired_invoice(wallets_and_operations: WalletTestSetup
validation_label = wallets_and_operations.first_page_objects.send_asset_page_objects.get_asset_address_validation_label()
wallets_and_operations.first_page_objects.send_asset_page_objects.click_send_asset_close_button()
- with allure.step('Verify error message for rgb25 asset'):
+ with allure.step('Verify error message for cfa asset'):
assert validation_label == TranslationManager.translate(
'invalid_invoice',
)
-@allure.feature('Automation of receive, send, and transaction status for RGB25 asset in iris wallet')
-@allure.story('End-to-End testing of receiving, sending, and verifying transaction status for RGB25 asset')
-def test_send_and_receive_rgb25_asset_operation(wallets_and_operations: WalletTestSetup):
- """Test send and receive operation for rgb25 asset"""
+@allure.feature('Automation of receive, send, and transaction status for CFA asset in iris wallet')
+@allure.story('End-to-End testing of receiving, sending, and verifying transaction status for CFA asset')
+def test_send_and_receive_cfa_asset_operation(wallets_and_operations: WalletTestSetup):
+ """Test send and receive operation for cfa asset"""
with allure.step('Generate invoice'):
invoice = wallets_and_operations.second_page_features.receive_features.receive_asset_from_sidebar(
SECOND_APPLICATION,
)
- with allure.step('Send RGB25 asset'):
+ with allure.step('Send CFA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.first_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
@@ -87,7 +87,7 @@ def test_send_and_receive_rgb25_asset_operation(wallets_and_operations: WalletTe
)
with allure.step('Verify transfer status'):
- wallets_and_operations.first_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.first_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME,
)
actual_transfer_status = wallets_and_operations.first_page_objects.asset_detail_page_objects.get_transfer_status()
@@ -100,7 +100,7 @@ def test_send_and_receive_rgb25_asset_operation(wallets_and_operations: WalletTe
)
wallets_and_operations.second_page_objects.collectible_page_objects.click_refresh_button()
wallets_and_operations.second_page_objects.sidebar_page_objects.click_collectibles_button()
- wallets_and_operations.second_page_objects.collectible_page_objects.click_rgb25_frame(
+ wallets_and_operations.second_page_objects.collectible_page_objects.click_cfa_frame(
ASSET_NAME,
)
received_amount = wallets_and_operations.second_page_objects.asset_detail_page_objects.get_on_chain_total_balance()
diff --git a/e2e_tests/test/spec/test_send_and_receive_rgb20_asset.py b/e2e_tests/test/spec/test_send_and_receive_nia_asset.py
similarity index 75%
rename from e2e_tests/test/spec/test_send_and_receive_rgb20_asset.py
rename to e2e_tests/test/spec/test_send_and_receive_nia_asset.py
index a1b99293..176092ed 100644
--- a/e2e_tests/test/spec/test_send_and_receive_rgb20_asset.py
+++ b/e2e_tests/test/spec/test_send_and_receive_nia_asset.py
@@ -1,5 +1,5 @@
# pylint: disable=redefined-outer-name, unused-import, unused-argument
-"""Iris wallet send and receive operation automation test suite for rgb20 asset"""
+"""Iris wallet send and receive operation automation test suite for nia asset"""
from __future__ import annotations
import allure
@@ -16,38 +16,38 @@
from src.model.enums.enums_model import TransactionStatusEnumModel
ASSET_TICKER = 'TTK'
-RGB20_ASSET_NAME = 'Tether'
+NIA_ASSET_NAME = 'Tether'
ASSET_AMOUNT = '2000'
SEND_AMOUNT = '50'
INVOICE = 'rgb:~/~/utxob:2msKeFq-uPjwpYxVY-jKS2ymYBq-SqmyP3ovg-AGvth8491-J7seMBm?expiry=1709616110&endpoints=rpc://10.0.2.2:3000/json-rpc'
-@allure.feature('Automation of send operation for RGB20 asset in iris wallet')
-@allure.story('Testing send RGB20 asset with expired invoice')
-def test_send_rgb20_with_expired_invoice(wallets_and_operations: WalletTestSetup, load_qm_translation):
- """Test send rgb20 asset with expired invoice"""
+@allure.feature('Automation of send operation for NIA asset in iris wallet')
+@allure.story('Testing send NIA asset with expired invoice')
+def test_send_nia_with_expired_invoice(wallets_and_operations: WalletTestSetup, load_qm_translation):
+ """Test send nia asset with expired invoice"""
validation_label = None
- with allure.step('Create and fund first wallet for send and receive rgb20'):
+ with allure.step('Create and fund first wallet for send and receive nia'):
wallets_and_operations.first_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=FIRST_APPLICATION, application_url=FIRST_APPLICATION_URL,
)
- with allure.step('Create and fund second wallet for send and receive rgb20'):
+ with allure.step('Create and fund second wallet for send and receive nia'):
wallets_and_operations.second_page_features.wallet_features.create_and_fund_wallet(
wallets_and_operations=wallets_and_operations, application=SECOND_APPLICATION, application_url=SECOND_APPLICATION_URL,
)
- with allure.step('Issue RGB20 asset'):
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
- application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=RGB20_ASSET_NAME, asset_amount=ASSET_AMOUNT,
+ with allure.step('Issue NIA asset'):
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
+ application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=NIA_ASSET_NAME, asset_amount=ASSET_AMOUNT,
)
- with allure.step('Send RGB20 asset with expired invoice'):
+ with allure.step('Send NIA asset with expired invoice'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
- RGB20_ASSET_NAME,
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_objects.send_asset_page_objects.enter_asset_invoice(
@@ -56,20 +56,20 @@ def test_send_rgb20_with_expired_invoice(wallets_and_operations: WalletTestSetup
validation_label = wallets_and_operations.first_page_objects.send_asset_page_objects.get_asset_address_validation_label()
wallets_and_operations.first_page_objects.send_asset_page_objects.click_send_asset_close_button()
- with allure.step('Verify error message for rgb20 asset'):
+ with allure.step('Verify error message for nia asset'):
assert validation_label == TranslationManager.translate(
'invalid_invoice',
)
-@allure.feature('Automation of receive, send, and transaction status for RGB20 asset in iris wallet')
-@allure.story('End-to-End testing of receiving, sending, and verifying transaction status for RGB20 asset')
-def test_send_and_receive_rgb20_asset_operation(wallets_and_operations: WalletTestSetup):
- """Test send and receive operation for rgb20 asset"""
+@allure.feature('Automation of receive, send, and transaction status for NIA asset in iris wallet')
+@allure.story('End-to-End testing of receiving, sending, and verifying transaction status for NIA asset')
+def test_send_and_receive_nia_asset_operation(wallets_and_operations: WalletTestSetup):
+ """Test send and receive operation for nia asset"""
- with allure.step('Issue RGB20 asset'):
- wallets_and_operations.first_page_features.issue_rgb20_features.issue_rgb20_with_sufficient_sats_and_utxo(
- application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=RGB20_ASSET_NAME, asset_amount=ASSET_AMOUNT,
+ with allure.step('Issue NIA asset'):
+ wallets_and_operations.first_page_features.issue_nia_features.issue_nia_with_sufficient_sats_and_utxo(
+ application=FIRST_APPLICATION, asset_ticker=ASSET_TICKER, asset_name=NIA_ASSET_NAME, asset_amount=ASSET_AMOUNT,
)
with allure.step('Generate invoice'):
@@ -77,12 +77,12 @@ def test_send_and_receive_rgb20_asset_operation(wallets_and_operations: WalletTe
SECOND_APPLICATION,
)
- with allure.step('Send RGB20 asset'):
+ with allure.step('Send NIA asset'):
wallets_and_operations.first_page_operations.do_focus_on_application(
FIRST_APPLICATION,
)
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
- RGB20_ASSET_NAME,
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_ASSET_NAME,
)
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_send_button()
wallets_and_operations.first_page_features.send_features.send(
@@ -90,8 +90,8 @@ def test_send_and_receive_rgb20_asset_operation(wallets_and_operations: WalletTe
)
with allure.step('Verify transaction status'):
- wallets_and_operations.first_page_objects.fungible_page_objects.click_rgb20_frame(
- RGB20_ASSET_NAME,
+ wallets_and_operations.first_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_ASSET_NAME,
)
actual_transfer_status = wallets_and_operations.first_page_objects.asset_detail_page_objects.get_transfer_status()
wallets_and_operations.first_page_objects.asset_detail_page_objects.click_close_button()
@@ -101,8 +101,8 @@ def test_send_and_receive_rgb20_asset_operation(wallets_and_operations: WalletTe
SECOND_APPLICATION,
)
wallets_and_operations.second_page_objects.fungible_page_objects.click_refresh_button()
- wallets_and_operations.second_page_objects.fungible_page_objects.click_rgb20_frame(
- RGB20_ASSET_NAME,
+ wallets_and_operations.second_page_objects.fungible_page_objects.click_nia_frame(
+ NIA_ASSET_NAME,
)
received_amount = wallets_and_operations.second_page_objects.asset_detail_page_objects.get_on_chain_total_balance()
wallets_and_operations.second_page_objects.asset_detail_page_objects.click_close_button()
diff --git a/e2e_tests/test/spec/test_view_unspent.py b/e2e_tests/test/spec/test_view_unspent.py
index bc1ffb20..52fb914c 100644
--- a/e2e_tests/test/spec/test_view_unspent.py
+++ b/e2e_tests/test/spec/test_view_unspent.py
@@ -12,7 +12,6 @@
from e2e_tests.test.utilities.app_setup import test_environment
from e2e_tests.test.utilities.app_setup import wallets_and_operations
from e2e_tests.test.utilities.app_setup import WalletTestSetup
-from src.model.enums.enums_model import WalletType
@pytest.mark.parametrize('test_environment', [False], indirect=True)
diff --git a/e2e_tests/test/utilities/app_setup.py b/e2e_tests/test/utilities/app_setup.py
index 8a599a28..4f752677 100644
--- a/e2e_tests/test/utilities/app_setup.py
+++ b/e2e_tests/test/utilities/app_setup.py
@@ -134,6 +134,7 @@ def start_rgb_node(self, data_dir, daemon_port, peer_port):
str(peer_port),
'--network',
'regtest',
+ '--disable-authentication',
]
process = subprocess.Popen(command, cwd=ln_binary_dir)
return process
diff --git a/e2e_tests/test/utilities/asset_copy.py b/e2e_tests/test/utilities/asset_copy.py
index 3ce53119..f1e79ebf 100644
--- a/e2e_tests/test/utilities/asset_copy.py
+++ b/e2e_tests/test/utilities/asset_copy.py
@@ -9,7 +9,7 @@
from pathlib import Path
-def copy_rgb25_image_to_home_directory(current_working_directory):
+def copy_cfa_image_to_home_directory(current_working_directory):
"""
Copies the sample.png image from the e2e_tests/assets directory to the home directory.
diff --git a/poetry.lock b/poetry.lock
index 28127458..67337ed2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,33 +1,29 @@
-# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "allure-pytest"
-version = "2.14.0"
+version = "2.15.3"
description = "Allure pytest integration"
optional = false
python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
- {file = "allure_pytest-2.14.0-py3-none-any.whl", hash = "sha256:6ddb68ef42bd5a2dfbcc136a184bf3e78e631ede7b8c54750026ffd407bda9af"},
- {file = "allure_pytest-2.14.0.tar.gz", hash = "sha256:2b485dc307755f8f3207783a69558ca1cc72f1e2c97bedc65c93fdb77adf328f"},
+ {file = "allure_pytest-2.15.3-py3-none-any.whl", hash = "sha256:66711efdc44c7c0d629f4eeab73ca20932f8786c251d61e242449f7b4ec18ca2"},
+ {file = "allure_pytest-2.15.3.tar.gz", hash = "sha256:b6576f8258de44f48ec59cfaee73cdbd1142c2ad6cfdc82d894fd1db8abf22e9"},
]
[package.dependencies]
-allure-python-commons = "2.14.0"
+allure-python-commons = "2.15.3"
pytest = ">=4.5.0"
[[package]]
name = "allure-python-commons"
-version = "2.14.0"
+version = "2.15.3"
description = "Contains the API for end users as well as helper functions and classes to build Allure adapters for Python test frameworks"
optional = false
python-versions = ">=3.6"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
- {file = "allure_python_commons-2.14.0-py3-none-any.whl", hash = "sha256:9200f40abee697133e9ed9f68887cde996a24b9eb33fcf528da8fe50fae88e43"},
- {file = "allure_python_commons-2.14.0.tar.gz", hash = "sha256:9b217e2f6c74cdbd0e253f89059d4165346e95fcb28228fae333ff4dccea0bd5"},
+ {file = "allure_python_commons-2.15.3-py3-none-any.whl", hash = "sha256:50e9b346d8a060c84af8d19f221bd9da6e1aa0002a4e7f770e151167365219d0"},
+ {file = "allure_python_commons-2.15.3.tar.gz", hash = "sha256:b42a96d6076fb323c9e43645dfb84c0574f6bad0a0e005d92564015cd172d564"},
]
[package.dependencies]
@@ -36,14 +32,13 @@ pluggy = ">=0.4.0"
[[package]]
name = "altgraph"
-version = "0.17.4"
+version = "0.17.5"
description = "Python graph (network) package"
optional = false
python-versions = "*"
-groups = ["dev"]
files = [
- {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"},
- {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"},
+ {file = "altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597"},
+ {file = "altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7"},
]
[[package]]
@@ -52,7 +47,6 @@ version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -64,7 +58,6 @@ version = "3.1.0"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.8.0"
-groups = ["dev"]
files = [
{file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"},
{file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"},
@@ -79,7 +72,6 @@ version = "3.4.3"
description = "reference implementation of PEP 3156"
optional = false
python-versions = "*"
-groups = ["dev"]
files = [
{file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"},
{file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"},
@@ -89,36 +81,25 @@ files = [
[[package]]
name = "attrs"
-version = "25.3.0"
+version = "25.4.0"
description = "Classes Without Boilerplate"
optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
+python-versions = ">=3.9"
files = [
- {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
- {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
+ {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"},
+ {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"},
]
-[package.extras]
-benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
-tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
-
[[package]]
name = "backports-tarfile"
version = "1.2.0"
description = "Backport of CPython tarfile module"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev"]
files = [
{file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"},
{file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"},
]
-markers = {main = "python_version < \"3.12\"", dev = "python_version < \"3.12\" and sys_platform == \"linux\""}
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
@@ -130,7 +111,6 @@ version = "0.3.post4"
description = "Utilities for generating and using Bitcoin Hierarchical Deterministic wallets (BIP0032)."
optional = false
python-versions = "*"
-groups = ["dev"]
files = [
{file = "bip32utils-0.3.post4-py3-none-any.whl", hash = "sha256:86125e16732101f17dbf4307ca0311a06ded0aca94220ac961e5bce0a444e972"},
{file = "bip32utils-0.3.post4.tar.gz", hash = "sha256:5970f40fbb727a89d3cc06b6387b348252f7c8af6b3470df704276de728c48c2"},
@@ -145,7 +125,6 @@ version = "24.4.1"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "black-24.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f7749fd0d97ff9415975a1432fac7df89bf13c3833cea079e55fa004d5f28c0"},
{file = "black-24.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859f3cc5d2051adadf8fd504a01e02b0fd866d7549fff54bc9202d524d2e8bd7"},
@@ -182,7 +161,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""]
+d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
@@ -192,256 +171,275 @@ version = "1.0.1"
description = "Version-bump your software with a single command!"
optional = false
python-versions = ">=3.5"
-groups = ["dev"]
files = [
{file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"},
{file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"},
]
-[[package]]
-name = "cachetools"
-version = "5.5.2"
-description = "Extensible memoizing collections and decorators"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"},
- {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"},
-]
-
[[package]]
name = "cattrs"
-version = "24.1.3"
+version = "25.3.0"
description = "Composable complex class support for attrs and dataclasses."
optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
+python-versions = ">=3.9"
files = [
- {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"},
- {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"},
+ {file = "cattrs-25.3.0-py3-none-any.whl", hash = "sha256:9896e84e0a5bf723bc7b4b68f4481785367ce07a8a02e7e9ee6eb2819bc306ff"},
+ {file = "cattrs-25.3.0.tar.gz", hash = "sha256:1ac88d9e5eda10436c4517e390a4142d88638fe682c436c93db7ce4a277b884a"},
]
[package.dependencies]
-attrs = ">=23.1.0"
+attrs = ">=25.4.0"
exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""}
-typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""}
+typing-extensions = ">=4.14.0"
[package.extras]
bson = ["pymongo (>=4.4.0)"]
cbor2 = ["cbor2 (>=5.4.6)"]
msgpack = ["msgpack (>=1.0.5)"]
-msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""]
-orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""]
+msgspec = ["msgspec (>=0.19.0)"]
+orjson = ["orjson (>=3.11.3)"]
pyyaml = ["pyyaml (>=6.0)"]
tomlkit = ["tomlkit (>=0.11.8)"]
-ujson = ["ujson (>=5.7.0)"]
+ujson = ["ujson (>=5.10.0)"]
[[package]]
name = "certifi"
-version = "2025.1.31"
+version = "2026.1.4"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
-python-versions = ">=3.6"
-groups = ["main", "dev"]
+python-versions = ">=3.7"
files = [
- {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
- {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
+ {file = "certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c"},
+ {file = "certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120"},
]
[[package]]
name = "cffi"
-version = "1.17.1"
+version = "2.0.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
-markers = "platform_python_implementation != \"PyPy\""
-files = [
- {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
- {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
- {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
- {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
- {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
- {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
- {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
- {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
- {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
- {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
- {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
- {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
- {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
- {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
- {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
- {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
- {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
- {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
- {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
- {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
- {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
- {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
- {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
- {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
- {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
- {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
- {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
- {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
- {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
- {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
- {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
- {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
- {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
- {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
- {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
- {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
- {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
+python-versions = ">=3.9"
+files = [
+ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
+ {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
+ {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"},
+ {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"},
+ {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"},
+ {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"},
+ {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"},
+ {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"},
+ {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"},
+ {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"},
+ {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"},
+ {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"},
+ {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"},
+ {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"},
+ {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"},
+ {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"},
+ {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"},
+ {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"},
+ {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"},
+ {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"},
+ {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"},
+ {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"},
+ {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"},
+ {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"},
+ {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"},
+ {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"},
+ {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"},
+ {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"},
+ {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"},
+ {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"},
+ {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"},
+ {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"},
+ {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"},
+ {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"},
+ {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"},
+ {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"},
+ {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"},
+ {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"},
+ {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"},
+ {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"},
+ {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"},
+ {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"},
+ {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"},
+ {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"},
+ {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"},
+ {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"},
+ {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"},
+ {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"},
+ {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"},
+ {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"},
+ {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"},
+ {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"},
+ {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"},
+ {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"},
+ {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"},
+ {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"},
+ {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"},
+ {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"},
+ {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"},
+ {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"},
+ {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"},
+ {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"},
+ {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"},
+ {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"},
+ {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"},
+ {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"},
+ {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"},
+ {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"},
+ {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"},
+ {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"},
+ {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"},
+ {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"},
+ {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"},
+ {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"},
+ {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"},
+ {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"},
+ {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"},
+ {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"},
+ {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"},
+ {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"},
+ {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"},
+ {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"},
+ {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"},
+ {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"},
]
[package.dependencies]
-pycparser = "*"
+pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
[[package]]
name = "cfgv"
-version = "3.4.0"
+version = "3.5.0"
description = "Validate configuration and produce human readable error messages."
optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
+python-versions = ">=3.10"
files = [
- {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
- {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
+ {file = "cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0"},
+ {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"},
]
[[package]]
name = "charset-normalizer"
-version = "3.4.1"
+version = "3.4.4"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7"
-groups = ["main", "dev"]
-files = [
- {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"},
- {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"},
- {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"},
- {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"},
- {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"},
- {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"},
- {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"},
- {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"},
- {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"},
- {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"},
+files = [
+ {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"},
+ {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"},
+ {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"},
+ {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"},
+ {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"},
+ {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"},
+ {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"},
+ {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"},
+ {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"},
+ {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"},
]
[[package]]
@@ -450,7 +448,6 @@ version = "2.1.0"
description = "Cleo allows you to create beautiful and testable command-line interfaces."
optional = false
python-versions = ">=3.7,<4.0"
-groups = ["main"]
files = [
{file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"},
{file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"},
@@ -462,14 +459,13 @@ rapidfuzz = ">=3.0.0,<4.0.0"
[[package]]
name = "click"
-version = "8.1.8"
+version = "8.3.1"
description = "Composable command line interface toolkit"
optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
+python-versions = ">=3.10"
files = [
- {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
- {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
+ {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"},
+ {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"},
]
[package.dependencies]
@@ -481,91 +477,117 @@ 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"
-groups = ["main", "dev", "test"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
-markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\" or platform_system == \"Windows\"", test = "sys_platform == \"win32\""}
[[package]]
name = "coverage"
-version = "7.8.0"
+version = "7.13.1"
description = "Code coverage measurement for Python"
optional = false
-python-versions = ">=3.9"
-groups = ["test"]
-files = [
- {file = "coverage-7.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe"},
- {file = "coverage-7.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28"},
- {file = "coverage-7.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3"},
- {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676"},
- {file = "coverage-7.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d"},
- {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a"},
- {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c"},
- {file = "coverage-7.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f"},
- {file = "coverage-7.8.0-cp310-cp310-win32.whl", hash = "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f"},
- {file = "coverage-7.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23"},
- {file = "coverage-7.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27"},
- {file = "coverage-7.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea"},
- {file = "coverage-7.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7"},
- {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040"},
- {file = "coverage-7.8.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543"},
- {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2"},
- {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318"},
- {file = "coverage-7.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9"},
- {file = "coverage-7.8.0-cp311-cp311-win32.whl", hash = "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c"},
- {file = "coverage-7.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78"},
- {file = "coverage-7.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc"},
- {file = "coverage-7.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6"},
- {file = "coverage-7.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d"},
- {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05"},
- {file = "coverage-7.8.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a"},
- {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6"},
- {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47"},
- {file = "coverage-7.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe"},
- {file = "coverage-7.8.0-cp312-cp312-win32.whl", hash = "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545"},
- {file = "coverage-7.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b"},
- {file = "coverage-7.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd"},
- {file = "coverage-7.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00"},
- {file = "coverage-7.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64"},
- {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067"},
- {file = "coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008"},
- {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733"},
- {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323"},
- {file = "coverage-7.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3"},
- {file = "coverage-7.8.0-cp313-cp313-win32.whl", hash = "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d"},
- {file = "coverage-7.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487"},
- {file = "coverage-7.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25"},
- {file = "coverage-7.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42"},
- {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502"},
- {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1"},
- {file = "coverage-7.8.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4"},
- {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73"},
- {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a"},
- {file = "coverage-7.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883"},
- {file = "coverage-7.8.0-cp313-cp313t-win32.whl", hash = "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada"},
- {file = "coverage-7.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257"},
- {file = "coverage-7.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f"},
- {file = "coverage-7.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a"},
- {file = "coverage-7.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82"},
- {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814"},
- {file = "coverage-7.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c"},
- {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd"},
- {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4"},
- {file = "coverage-7.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899"},
- {file = "coverage-7.8.0-cp39-cp39-win32.whl", hash = "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f"},
- {file = "coverage-7.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3"},
- {file = "coverage-7.8.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd"},
- {file = "coverage-7.8.0-py3-none-any.whl", hash = "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7"},
- {file = "coverage-7.8.0.tar.gz", hash = "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501"},
+python-versions = ">=3.10"
+files = [
+ {file = "coverage-7.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1fa280b3ad78eea5be86f94f461c04943d942697e0dac889fa18fff8f5f9147"},
+ {file = "coverage-7.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c3d8c679607220979434f494b139dfb00131ebf70bb406553d69c1ff01a5c33d"},
+ {file = "coverage-7.13.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:339dc63b3eba969067b00f41f15ad161bf2946613156fb131266d8debc8e44d0"},
+ {file = "coverage-7.13.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db622b999ffe49cb891f2fff3b340cdc2f9797d01a0a202a0973ba2562501d90"},
+ {file = "coverage-7.13.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1443ba9acbb593fa7c1c29e011d7c9761545fe35e7652e85ce7f51a16f7e08d"},
+ {file = "coverage-7.13.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c832ec92c4499ac463186af72f9ed4d8daec15499b16f0a879b0d1c8e5cf4a3b"},
+ {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:562ec27dfa3f311e0db1ba243ec6e5f6ab96b1edfcfc6cf86f28038bc4961ce6"},
+ {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4de84e71173d4dada2897e5a0e1b7877e5eefbfe0d6a44edee6ce31d9b8ec09e"},
+ {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a5a68357f686f8c4d527a2dc04f52e669c2fc1cbde38f6f7eb6a0e58cbd17cae"},
+ {file = "coverage-7.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:77cc258aeb29a3417062758975521eae60af6f79e930d6993555eeac6a8eac29"},
+ {file = "coverage-7.13.1-cp310-cp310-win32.whl", hash = "sha256:bb4f8c3c9a9f34423dba193f241f617b08ffc63e27f67159f60ae6baf2dcfe0f"},
+ {file = "coverage-7.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:c8e2706ceb622bc63bac98ebb10ef5da80ed70fbd8a7999a5076de3afaef0fb1"},
+ {file = "coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88"},
+ {file = "coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3"},
+ {file = "coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9"},
+ {file = "coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee"},
+ {file = "coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf"},
+ {file = "coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3"},
+ {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef"},
+ {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851"},
+ {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb"},
+ {file = "coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba"},
+ {file = "coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19"},
+ {file = "coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a"},
+ {file = "coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c"},
+ {file = "coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3"},
+ {file = "coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e"},
+ {file = "coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c"},
+ {file = "coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62"},
+ {file = "coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968"},
+ {file = "coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e"},
+ {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f"},
+ {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee"},
+ {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf"},
+ {file = "coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c"},
+ {file = "coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7"},
+ {file = "coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6"},
+ {file = "coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c"},
+ {file = "coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78"},
+ {file = "coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b"},
+ {file = "coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd"},
+ {file = "coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992"},
+ {file = "coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4"},
+ {file = "coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a"},
+ {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766"},
+ {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4"},
+ {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398"},
+ {file = "coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784"},
+ {file = "coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461"},
+ {file = "coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500"},
+ {file = "coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9"},
+ {file = "coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc"},
+ {file = "coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a"},
+ {file = "coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4"},
+ {file = "coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6"},
+ {file = "coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1"},
+ {file = "coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd"},
+ {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c"},
+ {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0"},
+ {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e"},
+ {file = "coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53"},
+ {file = "coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842"},
+ {file = "coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2"},
+ {file = "coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09"},
+ {file = "coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894"},
+ {file = "coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a"},
+ {file = "coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f"},
+ {file = "coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909"},
+ {file = "coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4"},
+ {file = "coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75"},
+ {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9"},
+ {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465"},
+ {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864"},
+ {file = "coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9"},
+ {file = "coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5"},
+ {file = "coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a"},
+ {file = "coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0"},
+ {file = "coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a"},
+ {file = "coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6"},
+ {file = "coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673"},
+ {file = "coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5"},
+ {file = "coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d"},
+ {file = "coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8"},
+ {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486"},
+ {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564"},
+ {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7"},
+ {file = "coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416"},
+ {file = "coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f"},
+ {file = "coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79"},
+ {file = "coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4"},
+ {file = "coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573"},
+ {file = "coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd"},
]
[package.dependencies]
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras]
-toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
+toml = ["tomli"]
[[package]]
name = "crashtest"
@@ -573,7 +595,6 @@ version = "0.4.1"
description = "Manage Python errors with ease"
optional = false
python-versions = ">=3.7,<4.0"
-groups = ["main"]
files = [
{file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"},
{file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"},
@@ -585,7 +606,6 @@ version = "43.0.3"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
-groups = ["main", "dev"]
files = [
{file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"},
{file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"},
@@ -631,14 +651,13 @@ test-randomorder = ["pytest-randomly"]
[[package]]
name = "dill"
-version = "0.3.9"
+version = "0.4.0"
description = "serialize all of Python"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
- {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"},
- {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"},
+ {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"},
+ {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"},
]
[package.extras]
@@ -647,14 +666,13 @@ profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "distlib"
-version = "0.3.9"
+version = "0.4.0"
description = "Distribution utilities"
optional = false
python-versions = "*"
-groups = ["dev"]
files = [
- {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
- {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
+ {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"},
+ {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
]
[[package]]
@@ -663,8 +681,6 @@ version = "1.0.7"
description = "GUI test tool and automation framework that uses Accessibility (a11y) technologies to communicate with desktop applications."
optional = false
python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
{file = "dogtail-1.0.7-py3-none-any.whl", hash = "sha256:efbb225cbdbe0e6fb3b9aa5cf479d1907355126027b79973ea62263db4c5ad3c"},
{file = "dogtail-1.0.7.tar.gz", hash = "sha256:55447073c962ef5d1845d11972e21ac9579240490de82b4e3770b9a8c3a4fb3b"},
@@ -676,7 +692,6 @@ version = "0.19.1"
description = "ECDSA cryptographic signature library (pure python)"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6"
-groups = ["dev"]
files = [
{file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"},
{file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"},
@@ -691,30 +706,30 @@ gmpy2 = ["gmpy2"]
[[package]]
name = "exceptiongroup"
-version = "1.2.2"
+version = "1.3.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
-groups = ["dev", "test"]
-markers = "python_version == \"3.10\""
files = [
- {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
- {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
+ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"},
+ {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"},
]
+[package.dependencies]
+typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
+
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "execnet"
-version = "2.1.1"
+version = "2.1.2"
description = "execnet: rapid multi-Python deployment"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
- {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
- {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
+ {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"},
+ {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"},
]
[package.extras]
@@ -722,31 +737,24 @@ testing = ["hatch", "pre-commit", "pytest", "tox"]
[[package]]
name = "filelock"
-version = "3.18.0"
+version = "3.20.3"
description = "A platform independent file lock."
optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
+python-versions = ">=3.10"
files = [
- {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"},
- {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"},
+ {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"},
+ {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"},
]
-[package.extras]
-docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
-typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
-
[[package]]
name = "google-api-core"
-version = "2.24.2"
+version = "2.29.0"
description = "Google API client core library"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
- {file = "google_api_core-2.24.2-py3-none-any.whl", hash = "sha256:810a63ac95f3c441b7c0e43d344e372887f62ce9071ba972eacf32672e072de9"},
- {file = "google_api_core-2.24.2.tar.gz", hash = "sha256:81718493daf06d96d6bc76a91c23874dbf2fac0adbbf542831b805ee6e974696"},
+ {file = "google_api_core-2.29.0-py3-none-any.whl", hash = "sha256:d30bc60980daa36e314b5d5a3e5958b0200cb44ca8fa1be2b614e932b75a3ea9"},
+ {file = "google_api_core-2.29.0.tar.gz", hash = "sha256:84181be0f8e6b04006df75ddfe728f24489f0af57c96a529ff7cf45bc28797f7"},
]
[package.dependencies]
@@ -757,21 +765,20 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4
requests = ">=2.18.0,<3.0.0"
[package.extras]
-async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"]
-grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\""]
-grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
-grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
+async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.0)"]
+grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0)"]
+grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"]
+grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"]
[[package]]
name = "google-api-python-client"
-version = "2.166.0"
+version = "2.187.0"
description = "Google API Client Library for Python"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
- {file = "google_api_python_client-2.166.0-py2.py3-none-any.whl", hash = "sha256:dd8cc74d9fc18538ab05cbd2e93cb4f82382f910c5f6945db06c91f1deae6e45"},
- {file = "google_api_python_client-2.166.0.tar.gz", hash = "sha256:b8cf843bd9d736c134aef76cf1dc7a47c9283a2ef24267b97207b9dd43b30ef7"},
+ {file = "google_api_python_client-2.187.0-py3-none-any.whl", hash = "sha256:d8d0f6d85d7d1d10bdab32e642312ed572bdc98919f72f831b44b9a9cebba32f"},
+ {file = "google_api_python_client-2.187.0.tar.gz", hash = "sha256:e98e8e8f49e1b5048c2f8276473d6485febc76c9c47892a8b4d1afa2c9ec8278"},
]
[package.dependencies]
@@ -783,55 +790,54 @@ uritemplate = ">=3.0.1,<5"
[[package]]
name = "google-auth"
-version = "2.38.0"
+version = "2.47.0"
description = "Google Authentication Library"
optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
+python-versions = ">=3.8"
files = [
- {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"},
- {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"},
+ {file = "google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498"},
+ {file = "google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da"},
]
[package.dependencies]
-cachetools = ">=2.0.0,<6.0"
pyasn1-modules = ">=0.2.1"
rsa = ">=3.1.4,<5"
[package.extras]
-aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"]
+aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"]
+cryptography = ["cryptography (>=38.0.3)"]
enterprise-cert = ["cryptography", "pyopenssl"]
pyjwt = ["cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
reauth = ["pyu2f (>=0.1.5)"]
-requests = ["requests (>=2.20.0,<3.0.0.dev0)"]
+requests = ["requests (>=2.20.0,<3.0.0)"]
+testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (>=38.0.3)", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"]
+urllib3 = ["packaging", "urllib3"]
[[package]]
name = "google-auth-httplib2"
-version = "0.2.0"
+version = "0.2.1"
description = "Google Authentication Library: httplib2 transport"
optional = false
-python-versions = "*"
-groups = ["dev"]
+python-versions = ">=3.7"
files = [
- {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"},
- {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"},
+ {file = "google_auth_httplib2-0.2.1-py3-none-any.whl", hash = "sha256:1be94c611db91c01f9703e7f62b0a59bbd5587a95571c7b6fade510d648bc08b"},
+ {file = "google_auth_httplib2-0.2.1.tar.gz", hash = "sha256:5ef03be3927423c87fb69607b42df23a444e434ddb2555b73b3679793187b7de"},
]
[package.dependencies]
-google-auth = "*"
-httplib2 = ">=0.19.0"
+google-auth = ">=1.32.0,<3.0.0"
+httplib2 = ">=0.19.0,<1.0.0"
[[package]]
name = "google-auth-oauthlib"
-version = "1.2.1"
+version = "1.2.2"
description = "Google Authentication Library"
optional = false
python-versions = ">=3.6"
-groups = ["dev"]
files = [
- {file = "google_auth_oauthlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:2d58a27262d55aa1b87678c3ba7142a080098cbc2024f903c62355deb235d91f"},
- {file = "google_auth_oauthlib-1.2.1.tar.gz", hash = "sha256:afd0cad092a2eaa53cd8e8298557d6de1034c6cb4a740500b5357b648af97263"},
+ {file = "google_auth_oauthlib-1.2.2-py3-none-any.whl", hash = "sha256:fd619506f4b3908b5df17b65f39ca8d66ea56986e5472eb5978fd8f3786f00a2"},
+ {file = "google_auth_oauthlib-1.2.2.tar.gz", hash = "sha256:11046fb8d3348b296302dd939ace8af0a724042e8029c1b872d87fabc9f41684"},
]
[package.dependencies]
@@ -843,14 +849,13 @@ tool = ["click (>=6.0.0)"]
[[package]]
name = "googleapis-common-protos"
-version = "1.69.2"
+version = "1.72.0"
description = "Common protobufs used in Google APIs"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
- {file = "googleapis_common_protos-1.69.2-py3-none-any.whl", hash = "sha256:0b30452ff9c7a27d80bfc5718954063e8ab53dd3697093d3bc99581f5fd24212"},
- {file = "googleapis_common_protos-1.69.2.tar.gz", hash = "sha256:3e1b904a27a33c821b4b749fd31d334c0c9c30e6113023d495e48979a3dc9c5f"},
+ {file = "googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038"},
+ {file = "googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5"},
]
[package.dependencies]
@@ -861,29 +866,27 @@ grpc = ["grpcio (>=1.44.0,<2.0.0)"]
[[package]]
name = "httplib2"
-version = "0.22.0"
+version = "0.31.0"
description = "A comprehensive HTTP client library."
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-groups = ["dev"]
+python-versions = ">=3.6"
files = [
- {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"},
- {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"},
+ {file = "httplib2-0.31.0-py3-none-any.whl", hash = "sha256:b9cd78abea9b4e43a7714c6e0f8b6b8561a6fc1e95d5dbd367f5bf0ef35f5d24"},
+ {file = "httplib2-0.31.0.tar.gz", hash = "sha256:ac7ab497c50975147d4f7b1ade44becc7df2f8954d42b38b3d69c515f531135c"},
]
[package.dependencies]
-pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""}
+pyparsing = ">=3.0.4,<4"
[[package]]
name = "identify"
-version = "2.6.9"
+version = "2.6.16"
description = "File identification library for Python"
optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
+python-versions = ">=3.10"
files = [
- {file = "identify-2.6.9-py2.py3-none-any.whl", hash = "sha256:c98b4322da415a8e5a70ff6e51fbc2d2932c015532d77e9f8537b4ba7813b150"},
- {file = "identify-2.6.9.tar.gz", hash = "sha256:d40dfe3142a1421d8518e3d3985ef5ac42890683e32306ad614a29490abeb6bf"},
+ {file = "identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0"},
+ {file = "identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980"},
]
[package.extras]
@@ -891,14 +894,13 @@ license = ["ukkonen"]
[[package]]
name = "idna"
-version = "3.10"
+version = "3.11"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
-python-versions = ">=3.6"
-groups = ["main", "dev"]
+python-versions = ">=3.8"
files = [
- {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
- {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
+ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"},
+ {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"},
]
[package.extras]
@@ -906,28 +908,26 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
[[package]]
name = "importlib-metadata"
-version = "8.6.1"
+version = "8.7.1"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev"]
files = [
- {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
- {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
+ {file = "importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151"},
+ {file = "importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb"},
]
-markers = {main = "python_version < \"3.12\""}
[package.dependencies]
zipp = ">=3.20"
[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-enabler = ["pytest-enabler (>=2.2)"]
+enabler = ["pytest-enabler (>=3.4)"]
perf = ["ipython"]
-test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
-type = ["pytest-mypy"]
+test = ["flufl.flake8", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+type = ["mypy (<1.19)", "pytest-mypy (>=1.0.1)"]
[[package]]
name = "importlib-resources"
@@ -935,14 +935,13 @@ version = "6.5.2"
description = "Read resources from Python packages"
optional = false
python-versions = ">=3.9"
-groups = ["dev"]
files = [
{file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"},
{file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"},
]
[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
enabler = ["pytest-enabler (>=2.2)"]
@@ -951,14 +950,13 @@ type = ["pytest-mypy"]
[[package]]
name = "iniconfig"
-version = "2.1.0"
+version = "2.3.0"
description = "brain-dead simple config-ini parsing"
optional = false
-python-versions = ">=3.8"
-groups = ["dev", "test"]
+python-versions = ">=3.10"
files = [
- {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
- {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
+ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"},
+ {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
]
[[package]]
@@ -967,7 +965,6 @@ version = "5.13.2"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
-groups = ["dev"]
files = [
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
@@ -982,12 +979,10 @@ version = "3.4.0"
description = "Utility functions for Python class constructs"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev"]
files = [
{file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"},
{file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"},
]
-markers = {dev = "sys_platform == \"linux\""}
[package.dependencies]
more-itertools = "*"
@@ -998,46 +993,47 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena
[[package]]
name = "jaraco-context"
-version = "6.0.1"
+version = "6.1.0"
description = "Useful decorators and context managers"
optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
+python-versions = ">=3.9"
files = [
- {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"},
- {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"},
+ {file = "jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda"},
+ {file = "jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f"},
]
-markers = {dev = "sys_platform == \"linux\""}
[package.dependencies]
"backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""}
[package.extras]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
+cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+enabler = ["pytest-enabler (>=3.4)"]
+test = ["jaraco.test (>=5.6.0)", "portend", "pytest (>=6,!=8.1.*)"]
+type = ["mypy (<1.19)", "pytest-mypy (>=1.0.1)"]
[[package]]
name = "jaraco-functools"
-version = "4.1.0"
+version = "4.4.0"
description = "Functools like those found in stdlib"
optional = false
-python-versions = ">=3.8"
-groups = ["main"]
+python-versions = ">=3.9"
files = [
- {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"},
- {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"},
+ {file = "jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176"},
+ {file = "jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb"},
]
[package.dependencies]
-more-itertools = "*"
+more_itertools = "*"
[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-enabler = ["pytest-enabler (>=2.2)"]
+enabler = ["pytest-enabler (>=3.4)"]
test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"]
-type = ["pytest-mypy"]
+type = ["mypy (<1.19)", "pytest-mypy (>=1.0.1)"]
[[package]]
name = "jeepney"
@@ -1045,15 +1041,13 @@ version = "0.9.0"
description = "Low-level, pure Python DBus protocol wrapper."
optional = false
python-versions = ">=3.7"
-groups = ["main"]
-markers = "sys_platform == \"linux\""
files = [
{file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"},
{file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"},
]
[package.extras]
-test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"]
+test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"]
trio = ["trio"]
[[package]]
@@ -1062,7 +1056,6 @@ version = "3.1.6"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
-groups = ["test"]
files = [
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
@@ -1080,7 +1073,6 @@ version = "25.2.1"
description = "Store and access your passwords safely."
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "keyring-25.2.1-py3-none-any.whl", hash = "sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50"},
{file = "keyring-25.2.1.tar.gz", hash = "sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b"},
@@ -1106,8 +1098,6 @@ version = "5.0.2"
description = "Alternate keyring implementations"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
{file = "keyrings.alt-5.0.2-py3-none-any.whl", hash = "sha256:6be74693192f3f37bbb752bfac9b86e6177076b17d2ac12a390f1d6abff8ac7c"},
{file = "keyrings_alt-5.0.2.tar.gz", hash = "sha256:8f097ebe9dc8b185106502b8cdb066c926d2180e13b4689fd4771a3eab7d69fb"},
@@ -1119,19 +1109,17 @@ files = [
[package.extras]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-test = ["gdata ; python_version == \"2.7\"", "keyring (>=20)", "pycryptodome", "pycryptodomex", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "python-keyczar ; python_version == \"2.7\""]
+test = ["gdata", "keyring (>=20)", "pycryptodome", "pycryptodomex", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "python-keyczar"]
[[package]]
name = "macholib"
-version = "1.16.3"
+version = "1.16.4"
description = "Mach-O header analysis and editing"
optional = false
python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"darwin\""
files = [
- {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"},
- {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"},
+ {file = "macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea"},
+ {file = "macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362"},
]
[package.dependencies]
@@ -1139,73 +1127,100 @@ altgraph = ">=0.17"
[[package]]
name = "markupsafe"
-version = "3.0.2"
+version = "3.0.3"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.9"
-groups = ["test"]
-files = [
- {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
- {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
+files = [
+ {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"},
+ {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"},
+ {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"},
+ {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"},
+ {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"},
+ {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"},
+ {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"},
+ {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"},
+ {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"},
+ {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"},
+ {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"},
+ {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"},
+ {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"},
+ {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"},
+ {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"},
+ {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"},
+ {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"},
+ {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"},
+ {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"},
+ {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"},
+ {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"},
+ {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"},
+ {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"},
+ {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"},
+ {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"},
+ {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"},
+ {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"},
+ {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"},
+ {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"},
+ {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"},
+ {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"},
+ {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"},
+ {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"},
+ {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"},
+ {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"},
+ {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"},
+ {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"},
+ {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"},
+ {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"},
+ {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"},
+ {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"},
+ {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"},
+ {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"},
+ {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"},
+ {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"},
+ {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"},
+ {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"},
+ {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"},
+ {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"},
+ {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"},
+ {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"},
+ {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"},
+ {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"},
+ {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"},
+ {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"},
+ {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"},
+ {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"},
+ {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"},
+ {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"},
+ {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"},
+ {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"},
+ {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"},
+ {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"},
+ {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"},
+ {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"},
+ {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"},
+ {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"},
+ {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"},
+ {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"},
]
[[package]]
@@ -1214,7 +1229,6 @@ version = "0.7.0"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = ">=3.6"
-groups = ["dev"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
@@ -1226,7 +1240,6 @@ version = "0.21"
description = "Implementation of Bitcoin BIP-0039"
optional = false
python-versions = ">=3.8.1"
-groups = ["dev"]
files = [
{file = "mnemonic-0.21-py3-none-any.whl", hash = "sha256:72dc9de16ec5ef47287237b9b6943da11647a03fe7cf1f139fc3d7c4a7439288"},
{file = "mnemonic-0.21.tar.gz", hash = "sha256:1fe496356820984f45559b1540c80ff10de448368929b9c60a2b55744cc88acf"},
@@ -1234,51 +1247,46 @@ files = [
[[package]]
name = "more-itertools"
-version = "10.6.0"
+version = "10.8.0"
description = "More routines for operating on iterables, beyond itertools"
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev"]
files = [
- {file = "more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b"},
- {file = "more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89"},
+ {file = "more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b"},
+ {file = "more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd"},
]
-markers = {dev = "sys_platform == \"linux\""}
[[package]]
name = "mypy-extensions"
-version = "1.0.0"
+version = "1.1.0"
description = "Type system extensions for programs checked with the mypy type checker."
optional = false
-python-versions = ">=3.5"
-groups = ["dev"]
+python-versions = ">=3.8"
files = [
- {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
- {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
+ {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
+ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
]
[[package]]
name = "nodeenv"
-version = "1.9.1"
+version = "1.10.0"
description = "Node.js virtual environment builder"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-groups = ["dev"]
files = [
- {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
- {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
+ {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"},
+ {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"},
]
[[package]]
name = "oauthlib"
-version = "3.2.2"
+version = "3.3.1"
description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
+python-versions = ">=3.8"
files = [
- {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"},
- {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"},
+ {file = "oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1"},
+ {file = "oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9"},
]
[package.extras]
@@ -1288,39 +1296,41 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
[[package]]
name = "packaging"
-version = "24.2"
+version = "25.0"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
-groups = ["dev", "test"]
files = [
- {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
- {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
+ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
+ {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
]
[[package]]
name = "pathspec"
-version = "0.12.1"
+version = "1.0.3"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
+python-versions = ">=3.9"
files = [
- {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
- {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
+ {file = "pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c"},
+ {file = "pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d"},
]
+[package.extras]
+hyperscan = ["hyperscan (>=0.7)"]
+optional = ["typing-extensions (>=4)"]
+re2 = ["google-re2 (>=1.1)"]
+tests = ["pytest (>=9)", "typing-extensions (>=4.15)"]
+
[[package]]
name = "pefile"
-version = "2023.2.7"
+version = "2024.8.26"
description = "Python PE parsing module"
optional = false
python-versions = ">=3.6.0"
-groups = ["dev"]
-markers = "sys_platform == \"win32\""
files = [
- {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"},
- {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"},
+ {file = "pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f"},
+ {file = "pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632"},
]
[[package]]
@@ -1329,7 +1339,6 @@ version = "10.4.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
{file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
@@ -1418,41 +1427,39 @@ docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline
fpx = ["olefile"]
mic = ["olefile"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
-typing = ["typing-extensions ; python_version < \"3.10\""]
+typing = ["typing-extensions"]
xmp = ["defusedxml"]
[[package]]
name = "platformdirs"
-version = "4.3.7"
+version = "4.5.1"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
+python-versions = ">=3.10"
files = [
- {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"},
- {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"},
+ {file = "platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31"},
+ {file = "platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda"},
]
[package.extras]
-docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
-type = ["mypy (>=1.14.1)"]
+docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"]
+type = ["mypy (>=1.18.2)"]
[[package]]
name = "pluggy"
-version = "1.5.0"
+version = "1.6.0"
description = "plugin and hook calling mechanisms for python"
optional = false
-python-versions = ">=3.8"
-groups = ["dev", "test"]
+python-versions = ">=3.9"
files = [
- {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
- {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
+ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
+ {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
]
[package.extras]
dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
+testing = ["coverage", "pytest", "pytest-benchmark"]
[[package]]
name = "pre-commit"
@@ -1460,7 +1467,6 @@ version = "3.8.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.9"
-groups = ["dev"]
files = [
{file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
{file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
@@ -1475,14 +1481,13 @@ virtualenv = ">=20.10.0"
[[package]]
name = "proto-plus"
-version = "1.26.1"
+version = "1.27.0"
description = "Beautiful, Pythonic protocol buffers"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
- {file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"},
- {file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"},
+ {file = "proto_plus-1.27.0-py3-none-any.whl", hash = "sha256:1baa7f81cf0f8acb8bc1f6d085008ba4171eaf669629d1b6d1673b21ed1c0a82"},
+ {file = "proto_plus-1.27.0.tar.gz", hash = "sha256:873af56dd0d7e91836aee871e5799e1c6f1bda86ac9a983e0bb9f0c266a568c4"},
]
[package.dependencies]
@@ -1493,21 +1498,21 @@ testing = ["google-api-core (>=1.31.5)"]
[[package]]
name = "protobuf"
-version = "6.30.2"
+version = "6.33.4"
description = ""
optional = false
python-versions = ">=3.9"
-groups = ["dev"]
files = [
- {file = "protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103"},
- {file = "protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9"},
- {file = "protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b"},
- {file = "protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815"},
- {file = "protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d"},
- {file = "protobuf-6.30.2-cp39-cp39-win32.whl", hash = "sha256:524afedc03b31b15586ca7f64d877a98b184f007180ce25183d1a5cb230ee72b"},
- {file = "protobuf-6.30.2-cp39-cp39-win_amd64.whl", hash = "sha256:acec579c39c88bd8fbbacab1b8052c793efe83a0a5bd99db4a31423a25c0a0e2"},
- {file = "protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51"},
- {file = "protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048"},
+ {file = "protobuf-6.33.4-cp310-abi3-win32.whl", hash = "sha256:918966612c8232fc6c24c78e1cd89784307f5814ad7506c308ee3cf86662850d"},
+ {file = "protobuf-6.33.4-cp310-abi3-win_amd64.whl", hash = "sha256:8f11ffae31ec67fc2554c2ef891dcb561dae9a2a3ed941f9e134c2db06657dbc"},
+ {file = "protobuf-6.33.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2fe67f6c014c84f655ee06f6f66213f9254b3a8b6bda6cda0ccd4232c73c06f0"},
+ {file = "protobuf-6.33.4-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:757c978f82e74d75cba88eddec479df9b99a42b31193313b75e492c06a51764e"},
+ {file = "protobuf-6.33.4-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c7c64f259c618f0bef7bee042075e390debbf9682334be2b67408ec7c1c09ee6"},
+ {file = "protobuf-6.33.4-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:3df850c2f8db9934de4cf8f9152f8dc2558f49f298f37f90c517e8e5c84c30e9"},
+ {file = "protobuf-6.33.4-cp39-cp39-win32.whl", hash = "sha256:955478a89559fa4568f5a81dce77260eabc5c686f9e8366219ebd30debf06aa6"},
+ {file = "protobuf-6.33.4-cp39-cp39-win_amd64.whl", hash = "sha256:0f12ddbf96912690c3582f9dffb55530ef32015ad8e678cd494312bd78314c4f"},
+ {file = "protobuf-6.33.4-py3-none-any.whl", hash = "sha256:1fe3730068fcf2e595816a6c34fe66eeedd37d51d0400b72fabc848811fdc1bc"},
+ {file = "protobuf-6.33.4.tar.gz", hash = "sha256:dc2e61bca3b10470c1912d166fe0af67bfc20eb55971dcef8dfa48ce14f0ed91"},
]
[[package]]
@@ -1516,7 +1521,6 @@ version = "6.1.1"
description = "Cross-platform lib for process and system monitoring in Python."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-groups = ["dev"]
files = [
{file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"},
{file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"},
@@ -1547,7 +1551,6 @@ version = "0.6.1"
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
{file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
@@ -1559,7 +1562,6 @@ version = "0.4.2"
description = "A collection of ASN.1-based protocols modules"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"},
{file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"},
@@ -1570,37 +1572,40 @@ pyasn1 = ">=0.6.1,<0.7.0"
[[package]]
name = "pycairo"
-version = "1.27.0"
+version = "1.29.0"
description = "Python interface for cairo"
optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
-files = [
- {file = "pycairo-1.27.0-cp310-cp310-win32.whl", hash = "sha256:e20f431244634cf244ab6b4c3a2e540e65746eed1324573cf291981c3e65fc05"},
- {file = "pycairo-1.27.0-cp310-cp310-win_amd64.whl", hash = "sha256:03bf570e3919901572987bc69237b648fe0de242439980be3e606b396e3318c9"},
- {file = "pycairo-1.27.0-cp311-cp311-win32.whl", hash = "sha256:9a9b79f92a434dae65c34c830bb9abdbd92654195e73d52663cbe45af1ad14b2"},
- {file = "pycairo-1.27.0-cp311-cp311-win_amd64.whl", hash = "sha256:d40a6d80b15dacb3672dc454df4bc4ab3988c6b3f36353b24a255dc59a1c8aea"},
- {file = "pycairo-1.27.0-cp312-cp312-win32.whl", hash = "sha256:e2239b9bb6c05edae5f3be97128e85147a155465e644f4d98ea0ceac7afc04ee"},
- {file = "pycairo-1.27.0-cp312-cp312-win_amd64.whl", hash = "sha256:27cb4d3a80e3b9990af552818515a8e466e0317063a6e61585533f1a86f1b7d5"},
- {file = "pycairo-1.27.0-cp313-cp313-win32.whl", hash = "sha256:01505c138a313df2469f812405963532fc2511fb9bca9bdc8e0ab94c55d1ced8"},
- {file = "pycairo-1.27.0-cp313-cp313-win_amd64.whl", hash = "sha256:b0349d744c068b6644ae23da6ada111c8a8a7e323b56cbce3707cba5bdb474cc"},
- {file = "pycairo-1.27.0-cp39-cp39-win32.whl", hash = "sha256:f9ca8430751f1fdcd3f072377560c9e15608b9a42d61375469db853566993c9b"},
- {file = "pycairo-1.27.0-cp39-cp39-win_amd64.whl", hash = "sha256:1b1321652a6e27c4de3069709b1cae22aed2707fd8c5e889c04a95669228af2a"},
- {file = "pycairo-1.27.0.tar.gz", hash = "sha256:5cb21e7a00a2afcafea7f14390235be33497a2cce53a98a19389492a60628430"},
+python-versions = ">=3.10"
+files = [
+ {file = "pycairo-1.29.0-cp310-cp310-win32.whl", hash = "sha256:96c67e6caba72afd285c2372806a0175b1aa2f4537aa88fb4d9802d726effcd1"},
+ {file = "pycairo-1.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:65bddd944aee9f7d7d72821b1c87e97593856617c2820a78d589d66aa8afbd08"},
+ {file = "pycairo-1.29.0-cp310-cp310-win_arm64.whl", hash = "sha256:15b36aea699e2ff215cb6a21501223246032e572a3a10858366acdd69c81a1c8"},
+ {file = "pycairo-1.29.0-cp311-cp311-win32.whl", hash = "sha256:12757ebfb304b645861283c20585c9204c3430671fad925419cba04844d6dfed"},
+ {file = "pycairo-1.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:3391532db03f9601c1cee9ebfa15b7d1db183c6020f3e75c1348cee16825934f"},
+ {file = "pycairo-1.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:b69be8bb65c46b680771dc6a1a422b1cdd0cffb17be548f223e8cbbb6205567c"},
+ {file = "pycairo-1.29.0-cp312-cp312-win32.whl", hash = "sha256:91bcd7b5835764c616a615d9948a9afea29237b34d2ed013526807c3d79bb1d0"},
+ {file = "pycairo-1.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:3f01c3b5e49ef9411fff6bc7db1e765f542dc1c9cfed4542958a5afa3a8b8e76"},
+ {file = "pycairo-1.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:eafe3d2076f3533535ad4a361fa0754e0ee66b90e548a3a0f558fed00b1248f2"},
+ {file = "pycairo-1.29.0-cp313-cp313-win32.whl", hash = "sha256:3eb382a4141591807073274522f7aecab9e8fa2f14feafd11ac03a13a58141d7"},
+ {file = "pycairo-1.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:91114e4b3fbf4287c2b0788f83e1f566ce031bda49cf1c3c3c19c3e986e95c38"},
+ {file = "pycairo-1.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:09b7f69a5ff6881e151354ea092137b97b0b1f0b2ab4eb81c92a02cc4a08e335"},
+ {file = "pycairo-1.29.0-cp314-cp314-win32.whl", hash = "sha256:69e2a7968a3fbb839736257bae153f547bca787113cc8d21e9e08ca4526e0b6b"},
+ {file = "pycairo-1.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:e91243437a21cc4c67c401eff4433eadc45745275fa3ade1a0d877e50ffb90da"},
+ {file = "pycairo-1.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:b72200ea0e5f73ae4c788cd2028a750062221385eb0e6d8f1ecc714d0b4fdf82"},
+ {file = "pycairo-1.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5e45fce6185f553e79e4ef1722b8e98e6cde9900dbc48cb2637a9ccba86f627a"},
+ {file = "pycairo-1.29.0-cp314-cp314t-win_arm64.whl", hash = "sha256:caba0837a4b40d47c8dfb0f24cccc12c7831e3dd450837f2a356c75f21ce5a15"},
+ {file = "pycairo-1.29.0.tar.gz", hash = "sha256:f3f7fde97325cae80224c09f12564ef58d0d0f655da0e3b040f5807bd5bd3142"},
]
[[package]]
name = "pycparser"
-version = "2.22"
+version = "2.23"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev"]
-markers = "platform_python_implementation != \"PyPy\""
files = [
- {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
- {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
+ {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"},
+ {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
]
[[package]]
@@ -1609,7 +1614,6 @@ version = "2.7.1"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
@@ -1629,7 +1633,6 @@ version = "2.18.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
@@ -1721,7 +1724,6 @@ version = "0.3.1"
description = "Library for generating identicons. Port of Sigil (https://github.com/cupcake/sigil) with enhancements."
optional = false
python-versions = "*"
-groups = ["main"]
files = [
{file = "pydenticon-0.3.1.tar.gz", hash = "sha256:2ef363cdd6f4f0193ce62257486027e36884570f6140bbde51de72df321b77f1"},
]
@@ -1729,14 +1731,26 @@ files = [
[package.dependencies]
Pillow = "*"
+[[package]]
+name = "pygments"
+version = "2.19.2"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
+ {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
+]
+
+[package.extras]
+windows-terminal = ["colorama (>=0.4.6)"]
+
[[package]]
name = "pygobject"
version = "3.50.0"
description = "Python bindings for GObject Introspection"
optional = false
python-versions = "<4.0,>=3.9"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
{file = "pygobject-3.50.0.tar.gz", hash = "sha256:4500ad3dbf331773d8dedf7212544c999a76fc96b63a91b3dcac1e5925a1d103"},
]
@@ -1746,32 +1760,31 @@ pycairo = ">=1.16"
[[package]]
name = "pyinstaller"
-version = "6.12.0"
+version = "6.18.0"
description = "PyInstaller bundles a Python application and all its dependencies into a single package."
optional = false
-python-versions = "<3.14,>=3.8"
-groups = ["dev"]
+python-versions = "<3.15,>=3.8"
files = [
- {file = "pyinstaller-6.12.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:68f1e4cecf88a6272063977fa2a2c69ad37cf568e5901769d7206d0314c74f47"},
- {file = "pyinstaller-6.12.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:fea76fc9b55ffa730fcf90beb897cce4399938460b0b6f40507fbebfc752c753"},
- {file = "pyinstaller-6.12.0-py3-none-manylinux2014_i686.whl", hash = "sha256:dac8a27988dbc33cdc34f2046803258bc3f6829de24de52745a5daa22bdba0f1"},
- {file = "pyinstaller-6.12.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:83c7f3bde9871b4a6aa71c66a96e8ba5c21668ce711ed97f510b9382d10aac6c"},
- {file = "pyinstaller-6.12.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:a69818815c6e0711c727edc30680cb1f81c691b59de35db81a2d9e0ae26a9ef1"},
- {file = "pyinstaller-6.12.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a2abf5fde31a8b38b6df7939bcef8ac1d0c51e97e25317ce3555cd675259750f"},
- {file = "pyinstaller-6.12.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:8e92e9873a616547bbabbb5a3a9843d5f2ab40c3d8b26810acdf0fe257bee4cf"},
- {file = "pyinstaller-6.12.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:aefe502d55c9cf6aeaed7feba80b5f8491ce43f8f2b5fe2d9aadca3ee5a05bc4"},
- {file = "pyinstaller-6.12.0-py3-none-win32.whl", hash = "sha256:138856a5a503bb69c066377e0a22671b0db063e9cc14d5cf5c798a53561200d3"},
- {file = "pyinstaller-6.12.0-py3-none-win_amd64.whl", hash = "sha256:0e62d3906309248409f215b386f33afec845214e69cc0f296b93222b26a88f43"},
- {file = "pyinstaller-6.12.0-py3-none-win_arm64.whl", hash = "sha256:0c271896a3a168f4f91827145702543db9c5427f4c7372a6df8c75925a3ac18a"},
- {file = "pyinstaller-6.12.0.tar.gz", hash = "sha256:1834797be48ce1b26015af68bdeb3c61a6c7500136f04e0fc65e468115dec777"},
+ {file = "pyinstaller-6.18.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:cb7aa5a71bfa7c0af17a4a4e21855663c89e4bd7c40f1d337c8370636d8847c3"},
+ {file = "pyinstaller-6.18.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:07785459b3bf8a48889eac0b4d0667ade84aef8930ce030bc7cbb32f41283b33"},
+ {file = "pyinstaller-6.18.0-py3-none-manylinux2014_i686.whl", hash = "sha256:f998675b7ccb2dabbb1dc2d6f18af61d55428ad6d38e6c4d700417411b697d37"},
+ {file = "pyinstaller-6.18.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:779817a0cf69604cddcdb5be1fd4959dc2ce048d6355c73e5da97884df2f3387"},
+ {file = "pyinstaller-6.18.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:31b5d109f8405be0b7cddcede43e7b074792bc9a5bbd54ec000a3e779183c2af"},
+ {file = "pyinstaller-6.18.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4328c9837f1aef4fe1a127d4ff1b09a12ce53c827ce87c94117628b0e1fd098b"},
+ {file = "pyinstaller-6.18.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:3638fc81eb948e5e5eab1d4ad8f216e3fec6d4a350648304f0adb227b746ee5e"},
+ {file = "pyinstaller-6.18.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe59da34269e637f97fd3c43024f764586fc319141d245ff1a2e9af1036aa3"},
+ {file = "pyinstaller-6.18.0-py3-none-win32.whl", hash = "sha256:496205e4fa92ec944f9696eb597962a83aef4d4c3479abfab83d730e1edf016b"},
+ {file = "pyinstaller-6.18.0-py3-none-win_amd64.whl", hash = "sha256:976fabd90ecfbda47571c87055ad73413ec615ff7dea35e12a4304174de78de9"},
+ {file = "pyinstaller-6.18.0-py3-none-win_arm64.whl", hash = "sha256:dba4b70e3c9ba09aab51152c72a08e58a751851548f77ad35944d32a300c8381"},
+ {file = "pyinstaller-6.18.0.tar.gz", hash = "sha256:cdc507542783511cad4856fce582fdc37e9f29665ca596889c663c83ec8c6ec9"},
]
[package.dependencies]
altgraph = "*"
macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
packaging = ">=22.0"
-pefile = {version = ">=2022.5.30,<2024.8.26 || >2024.8.26", markers = "sys_platform == \"win32\""}
-pyinstaller-hooks-contrib = ">=2025.1"
+pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""}
+pyinstaller-hooks-contrib = ">=2025.9"
pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""}
setuptools = ">=42.0.0"
@@ -1781,14 +1794,13 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"]
[[package]]
name = "pyinstaller-hooks-contrib"
-version = "2025.2"
+version = "2025.11"
description = "Community maintained hooks for PyInstaller"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
- {file = "pyinstaller_hooks_contrib-2025.2-py3-none-any.whl", hash = "sha256:0b2bc7697075de5eb071ff13ef4a156d3beae6c19c7cbdcd70f37978d2013e30"},
- {file = "pyinstaller_hooks_contrib-2025.2.tar.gz", hash = "sha256:ccdd41bc30290f725f3e48f4a39985d11855af81d614d167e3021e303acb9102"},
+ {file = "pyinstaller_hooks_contrib-2025.11-py3-none-any.whl", hash = "sha256:777e163e2942474aa41a8e6d31ac1635292d63422c3646c176d584d04d971c34"},
+ {file = "pyinstaller_hooks_contrib-2025.11.tar.gz", hash = "sha256:dfe18632e06655fa88d218e0d768fd753e1886465c12a6d4bce04f1aaeec917d"},
]
[package.dependencies]
@@ -1801,7 +1813,6 @@ version = "3.1.0"
description = "python code static checker"
optional = false
python-versions = ">=3.8.0"
-groups = ["dev"]
files = [
{file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"},
{file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"},
@@ -1813,7 +1824,7 @@ colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = [
{version = ">=0.2", markers = "python_version < \"3.11\""},
{version = ">=0.3.7", markers = "python_version >= \"3.12\""},
- {version = ">=0.3.6", markers = "python_version == \"3.11\""},
+ {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
]
isort = ">=4.2.5,<5.13.0 || >5.13.0,<6"
mccabe = ">=0.6,<0.8"
@@ -1831,7 +1842,6 @@ version = "2.9.0"
description = "Python One Time Password Library"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
{file = "pyotp-2.9.0-py3-none-any.whl", hash = "sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612"},
{file = "pyotp-2.9.0.tar.gz", hash = "sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63"},
@@ -1842,14 +1852,13 @@ test = ["coverage", "mypy", "ruff", "wheel"]
[[package]]
name = "pyparsing"
-version = "3.2.3"
-description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+version = "3.3.1"
+description = "pyparsing - Classes and methods to define and execute parsing grammars"
optional = false
python-versions = ">=3.9"
-groups = ["dev"]
files = [
- {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"},
- {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"},
+ {file = "pyparsing-3.3.1-py3-none-any.whl", hash = "sha256:023b5e7e5520ad96642e2c6db4cb683d3970bd640cdf7115049a6e9c3682df82"},
+ {file = "pyparsing-3.3.1.tar.gz", hash = "sha256:47fad0f17ac1e2cad3de3b458570fbc9b03560aa029ed5e16ee5554da9a2251c"},
]
[package.extras]
@@ -1857,14 +1866,13 @@ diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pyperclip"
-version = "1.9.0"
+version = "1.11.0"
description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)"
optional = false
python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
- {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"},
+ {file = "pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273"},
+ {file = "pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6"},
]
[[package]]
@@ -1873,7 +1881,6 @@ version = "0.20220715.0"
description = "Pure Python library for saving and loading PNG images"
optional = false
python-versions = "*"
-groups = ["main"]
files = [
{file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"},
{file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"},
@@ -1885,7 +1892,6 @@ version = "1.3.3"
description = "A fully customizable toast notification library for PyQt and PySide"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
{file = "pyqt-toast-notification-1.3.3.tar.gz", hash = "sha256:595dbf4b9edee77329e2514255d9a9415ff5d70708c50790db0fee207d323a29"},
{file = "pyqt_toast_notification-1.3.3-py3-none-any.whl", hash = "sha256:65ba011f536aa728f0b3ad3a452ab2b578a17f6e0e3468d6f1dc97f5b1cd4d15"},
@@ -1900,7 +1906,6 @@ version = "6.7.3"
description = "Python bindings for the Qt cross-platform application and UI framework"
optional = false
python-versions = "<3.13,>=3.9"
-groups = ["main"]
files = [
{file = "PySide6-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:1c21c4cf6cdd29bd13bbd7a2514756a19188eab992b92af03e64bf06a9b33d5b"},
{file = "PySide6-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a21480cc746358f70768975fcc452322f03b3c3622625bfb1743b40ce4e24beb"},
@@ -1919,7 +1924,6 @@ version = "6.7.3"
description = "Python bindings for the Qt cross-platform application and UI framework (Addons)"
optional = false
python-versions = "<3.13,>=3.9"
-groups = ["main"]
files = [
{file = "PySide6_Addons-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:3174cb3a373c09c98740b452e8e8f4945d64cfa18ed8d43964111d570f0dc647"},
{file = "PySide6_Addons-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:bde1eb03dbffd089b50cd445847aaecaf4056cea84c49ea592d00f84f247251e"},
@@ -1937,7 +1941,6 @@ version = "6.7.3"
description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)"
optional = false
python-versions = "<3.13,>=3.9"
-groups = ["main"]
files = [
{file = "PySide6_Essentials-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:f9e08a4e9e7dc7b5ab72fde20abce8c97df7af1b802d9743f098f577dfe1f649"},
{file = "PySide6_Essentials-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cda6fd26aead48f32e57f044d18aa75dc39265b49d7957f515ce7ac3989e7029"},
@@ -1950,26 +1953,26 @@ shiboken6 = "6.7.3"
[[package]]
name = "pytest"
-version = "8.3.5"
+version = "8.4.2"
description = "pytest: simple powerful testing with Python"
optional = false
-python-versions = ">=3.8"
-groups = ["dev", "test"]
+python-versions = ">=3.9"
files = [
- {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"},
- {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"},
+ {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"},
+ {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"},
]
[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
-iniconfig = "*"
-packaging = "*"
+colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
+iniconfig = ">=1"
+packaging = ">=20"
pluggy = ">=1.5,<2"
+pygments = ">=2.7.2"
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
[package.extras]
-dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-cov"
@@ -1977,7 +1980,6 @@ version = "5.0.0"
description = "Pytest plugin for measuring coverage."
optional = false
python-versions = ">=3.8"
-groups = ["test"]
files = [
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
@@ -1996,7 +1998,6 @@ version = "4.1.1"
description = "pytest plugin for generating HTML reports"
optional = false
python-versions = ">=3.8"
-groups = ["test"]
files = [
{file = "pytest_html-4.1.1-py3-none-any.whl", hash = "sha256:c8152cea03bd4e9bee6d525573b67bbc6622967b72b9628dda0ea3e2a0b5dd71"},
{file = "pytest_html-4.1.1.tar.gz", hash = "sha256:70a01e8ae5800f4a074b56a4cb1025c8f4f9b038bba5fe31e3c98eb996686f07"},
@@ -2017,7 +2018,6 @@ version = "3.1.1"
description = "pytest plugin for test session metadata"
optional = false
python-versions = ">=3.8"
-groups = ["test"]
files = [
{file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"},
{file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"},
@@ -2031,14 +2031,13 @@ test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>
[[package]]
name = "pytest-mock"
-version = "3.14.0"
+version = "3.15.1"
description = "Thin-wrapper around the mock package for easier use with pytest"
optional = false
-python-versions = ">=3.8"
-groups = ["test"]
+python-versions = ">=3.9"
files = [
- {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"},
- {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"},
+ {file = "pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d"},
+ {file = "pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f"},
]
[package.dependencies]
@@ -2053,7 +2052,6 @@ version = "1.3.0"
description = "pytest plugin to run your tests in a specific order"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
{file = "pytest_order-1.3.0-py3-none-any.whl", hash = "sha256:2cd562a21380345dd8d5774aa5fd38b7849b6ee7397ca5f6999bbe6e89f07f6e"},
{file = "pytest_order-1.3.0.tar.gz", hash = "sha256:51608fec3d3ee9c0adaea94daa124a5c4c1d2bb99b00269f098f414307f23dde"},
@@ -2064,34 +2062,33 @@ pytest = {version = ">=6.2.4", markers = "python_version >= \"3.10\""}
[[package]]
name = "pytest-qt"
-version = "4.4.0"
+version = "4.5.0"
description = "pytest support for PyQt and PySide applications"
optional = false
-python-versions = ">=3.8"
-groups = ["test"]
+python-versions = ">=3.9"
files = [
- {file = "pytest-qt-4.4.0.tar.gz", hash = "sha256:76896142a940a4285339008d6928a36d4be74afec7e634577e842c9cc5c56844"},
- {file = "pytest_qt-4.4.0-py3-none-any.whl", hash = "sha256:001ed2f8641764b394cf286dc8a4203e40eaf9fff75bf0bfe5103f7f8d0c591d"},
+ {file = "pytest_qt-4.5.0-py3-none-any.whl", hash = "sha256:ed21ea9b861247f7d18090a26bfbda8fb51d7a8a7b6f776157426ff2ccf26eff"},
+ {file = "pytest_qt-4.5.0.tar.gz", hash = "sha256:51620e01c488f065d2036425cbc1cbcf8a6972295105fd285321eb47e66a319f"},
]
[package.dependencies]
pluggy = ">=1.1"
pytest = "*"
+typing_extensions = "*"
[package.extras]
dev = ["pre-commit", "tox"]
-doc = ["sphinx", "sphinx-rtd-theme"]
+doc = ["sphinx", "sphinx_rtd_theme"]
[[package]]
name = "pytest-xdist"
-version = "3.6.1"
+version = "3.8.0"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
+python-versions = ">=3.9"
files = [
- {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"},
- {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"},
+ {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"},
+ {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"},
]
[package.dependencies]
@@ -2105,14 +2102,13 @@ testing = ["filelock"]
[[package]]
name = "python-dotenv"
-version = "1.1.0"
+version = "1.2.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
python-versions = ">=3.9"
-groups = ["dev"]
files = [
- {file = "python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d"},
- {file = "python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5"},
+ {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"},
+ {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"},
]
[package.extras]
@@ -2124,8 +2120,6 @@ version = "1.19.0"
description = ""
optional = false
python-versions = ">=3.6"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
{file = "python3_pyatspi-1.19.0-py3-none-any.whl", hash = "sha256:cc0d5436bf925199c28f421279dc3e428a2aeba92bcb4d8e63013eb01b205dfc"},
{file = "python3_pyatspi-1.19.0.tar.gz", hash = "sha256:c6f2d8ccded46b25c161a42b106053d33c0e23da53a63f346aec4a97e6e5aafd"},
@@ -2137,8 +2131,6 @@ version = "0.15"
description = "Python3 X Library"
optional = false
python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"linux\""
files = [
{file = "python3-xlib-0.15.tar.gz", hash = "sha256:dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8"},
]
@@ -2149,8 +2141,6 @@ version = "306"
description = "Python for Window Extensions"
optional = false
python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"win32\""
files = [
{file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"},
{file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"},
@@ -2174,8 +2164,6 @@ version = "0.2.3"
description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
optional = false
python-versions = ">=3.6"
-groups = ["main", "dev"]
-markers = "sys_platform == \"win32\""
files = [
{file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"},
{file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"},
@@ -2183,65 +2171,84 @@ files = [
[[package]]
name = "pyyaml"
-version = "6.0.2"
+version = "6.0.3"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
- {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
- {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
- {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
- {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
- {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
- {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
- {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
- {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
- {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
- {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
- {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
- {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
- {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
- {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
- {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
- {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
- {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
- {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
- {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
- {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
- {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
- {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
- {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
- {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
- {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
- {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
- {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
- {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
- {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
- {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
- {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
- {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
- {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
- {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
- {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
- {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
- {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
- {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
- {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
- {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
- {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
- {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
- {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
- {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
- {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
- {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
- {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
- {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
- {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
- {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
- {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
- {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
+files = [
+ {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"},
+ {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"},
+ {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"},
+ {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"},
+ {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"},
+ {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"},
+ {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"},
+ {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"},
+ {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"},
+ {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"},
+ {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"},
+ {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"},
+ {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"},
+ {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"},
+ {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"},
+ {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"},
+ {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"},
+ {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"},
+ {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"},
+ {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"},
+ {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"},
+ {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"},
+ {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"},
+ {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"},
+ {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"},
+ {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"},
+ {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"},
+ {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"},
+ {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"},
+ {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"},
+ {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"},
+ {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"},
+ {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"},
+ {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"},
+ {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"},
+ {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"},
+ {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"},
+ {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"},
+ {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"},
+ {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"},
+ {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"},
+ {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"},
+ {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"},
+ {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"},
+ {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"},
+ {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"},
+ {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"},
+ {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"},
+ {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"},
+ {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"},
+ {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"},
+ {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"},
+ {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"},
+ {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"},
+ {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"},
+ {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"},
+ {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"},
+ {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"},
+ {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"},
+ {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"},
+ {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"},
+ {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"},
+ {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"},
+ {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"},
+ {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"},
]
[[package]]
@@ -2250,7 +2257,6 @@ version = "7.4.2"
description = "QR Code image generator"
optional = false
python-versions = ">=3.7"
-groups = ["main"]
files = [
{file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"},
{file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"},
@@ -2274,7 +2280,6 @@ version = "2.4.3"
description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)."
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
{file = "QtPy-2.4.3-py3-none-any.whl", hash = "sha256:72095afe13673e017946cc258b8d5da43314197b741ed2890e563cf384b51aa1"},
{file = "qtpy-2.4.3.tar.gz", hash = "sha256:db744f7832e6d3da90568ba6ccbca3ee2b3b4a890c3d6fbbc63142f6e4cdf5bb"},
@@ -2288,106 +2293,94 @@ test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"]
[[package]]
name = "rapidfuzz"
-version = "3.13.0"
+version = "3.14.3"
description = "rapid fuzzy string matching"
optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "rapidfuzz-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aafc42a1dc5e1beeba52cd83baa41372228d6d8266f6d803c16dbabbcc156255"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85c9a131a44a95f9cac2eb6e65531db014e09d89c4f18c7b1fa54979cb9ff1f3"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d7cec4242d30dd521ef91c0df872e14449d1dffc2a6990ede33943b0dae56c3"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e297c09972698c95649e89121e3550cee761ca3640cd005e24aaa2619175464e"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef0f5f03f61b0e5a57b1df7beafd83df993fd5811a09871bad6038d08e526d0d"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8cf5f7cd6e4d5eb272baf6a54e182b2c237548d048e2882258336533f3f02b7"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9256218ac8f1a957806ec2fb9a6ddfc6c32ea937c0429e88cf16362a20ed8602"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1bdd2e6d0c5f9706ef7595773a81ca2b40f3b33fd7f9840b726fb00c6c4eb2e"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5280be8fd7e2bee5822e254fe0a5763aa0ad57054b85a32a3d9970e9b09bbcbf"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd742c03885db1fce798a1cd87a20f47f144ccf26d75d52feb6f2bae3d57af05"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5435fcac94c9ecf0504bf88a8a60c55482c32e18e108d6079a0089c47f3f8cf6"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:93a755266856599be4ab6346273f192acde3102d7aa0735e2f48b456397a041f"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3abe6a4e8eb4cfc4cda04dd650a2dc6d2934cbdeda5def7e6fd1c20f6e7d2a0b"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8ddb58961401da7d6f55f185512c0d6bd24f529a637078d41dd8ffa5a49c107"},
- {file = "rapidfuzz-3.13.0-cp310-cp310-win_arm64.whl", hash = "sha256:c523620d14ebd03a8d473c89e05fa1ae152821920c3ff78b839218ff69e19ca3"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d395a5cad0c09c7f096433e5fd4224d83b53298d53499945a9b0e5a971a84f3a"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7b3eda607a019169f7187328a8d1648fb9a90265087f6903d7ee3a8eee01805"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e0bfa602e1942d542de077baf15d658bd9d5dcfe9b762aff791724c1c38b70"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bef86df6d59667d9655905b02770a0c776d2853971c0773767d5ef8077acd624"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fedd316c165beed6307bf754dee54d3faca2c47e1f3bcbd67595001dfa11e969"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5158da7f2ec02a930be13bac53bb5903527c073c90ee37804090614cab83c29e"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b6f913ee4618ddb6d6f3e387b76e8ec2fc5efee313a128809fbd44e65c2bbb2"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d25fdbce6459ccbbbf23b4b044f56fbd1158b97ac50994eaae2a1c0baae78301"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25343ccc589a4579fbde832e6a1e27258bfdd7f2eb0f28cb836d6694ab8591fc"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a9ad1f37894e3ffb76bbab76256e8a8b789657183870be11aa64e306bb5228fd"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5dc71ef23845bb6b62d194c39a97bb30ff171389c9812d83030c1199f319098c"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b7f4c65facdb94f44be759bbd9b6dda1fa54d0d6169cdf1a209a5ab97d311a75"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-win32.whl", hash = "sha256:b5104b62711565e0ff6deab2a8f5dbf1fbe333c5155abe26d2cfd6f1849b6c87"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:9093cdeb926deb32a4887ebe6910f57fbcdbc9fbfa52252c10b56ef2efb0289f"},
- {file = "rapidfuzz-3.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:f70f646751b6aa9d05be1fb40372f006cc89d6aad54e9d79ae97bd1f5fce5203"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a1a6a906ba62f2556372282b1ef37b26bca67e3d2ea957277cfcefc6275cca7"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fd0975e015b05c79a97f38883a11236f5a24cca83aa992bd2558ceaa5652b26"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d4e13593d298c50c4f94ce453f757b4b398af3fa0fd2fde693c3e51195b7f69"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed6f416bda1c9133000009d84d9409823eb2358df0950231cc936e4bf784eb97"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dc82b6ed01acb536b94a43996a94471a218f4d89f3fdd9185ab496de4b2a981"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9d824de871daa6e443b39ff495a884931970d567eb0dfa213d234337343835f"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d18228a2390375cf45726ce1af9d36ff3dc1f11dce9775eae1f1b13ac6ec50f"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5fe634c9482ec5d4a6692afb8c45d370ae86755e5f57aa6c50bfe4ca2bdd87"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:694eb531889f71022b2be86f625a4209c4049e74be9ca836919b9e395d5e33b3"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:11b47b40650e06147dee5e51a9c9ad73bb7b86968b6f7d30e503b9f8dd1292db"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:98b8107ff14f5af0243f27d236bcc6e1ef8e7e3b3c25df114e91e3a99572da73"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b836f486dba0aceb2551e838ff3f514a38ee72b015364f739e526d720fdb823a"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-win32.whl", hash = "sha256:4671ee300d1818d7bdfd8fa0608580d7778ba701817216f0c17fb29e6b972514"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e2065f68fb1d0bf65adc289c1bdc45ba7e464e406b319d67bb54441a1b9da9e"},
- {file = "rapidfuzz-3.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:65cc97c2fc2c2fe23586599686f3b1ceeedeca8e598cfcc1b7e56dc8ca7e2aa7"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:09e908064d3684c541d312bd4c7b05acb99a2c764f6231bd507d4b4b65226c23"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57c390336cb50d5d3bfb0cfe1467478a15733703af61f6dffb14b1cd312a6fae"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0da54aa8547b3c2c188db3d1c7eb4d1bb6dd80baa8cdaeaec3d1da3346ec9caa"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df8e8c21e67afb9d7fbe18f42c6111fe155e801ab103c81109a61312927cc611"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:461fd13250a2adf8e90ca9a0e1e166515cbcaa5e9c3b1f37545cbbeff9e77f6b"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2b3dd5d206a12deca16870acc0d6e5036abeb70e3cad6549c294eff15591527"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1343d745fbf4688e412d8f398c6e6d6f269db99a54456873f232ba2e7aeb4939"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b1b065f370d54551dcc785c6f9eeb5bd517ae14c983d2784c064b3aa525896df"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:11b125d8edd67e767b2295eac6eb9afe0b1cdc82ea3d4b9257da4b8e06077798"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c33f9c841630b2bb7e69a3fb5c84a854075bb812c47620978bddc591f764da3d"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae4574cb66cf1e85d32bb7e9ec45af5409c5b3970b7ceb8dea90168024127566"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e05752418b24bbd411841b256344c26f57da1148c5509e34ea39c7eb5099ab72"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-win32.whl", hash = "sha256:0e1d08cb884805a543f2de1f6744069495ef527e279e05370dd7c83416af83f8"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9a7c6232be5f809cd39da30ee5d24e6cadd919831e6020ec6c2391f4c3bc9264"},
- {file = "rapidfuzz-3.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:3f32f15bacd1838c929b35c84b43618481e1b3d7a61b5ed2db0291b70ae88b53"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc64da907114d7a18b5e589057e3acaf2fec723d31c49e13fedf043592a3f6a7"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4d9d7f84c8e992a8dbe5a3fdbea73d733da39bf464e62c912ac3ceba9c0cff93"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a79a2f07786a2070669b4b8e45bd96a01c788e7a3c218f531f3947878e0f956"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f338e71c45b69a482de8b11bf4a029993230760120c8c6e7c9b71760b6825a1"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb40ca8ddfcd4edd07b0713a860be32bdf632687f656963bcbce84cea04b8d8"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48719f7dcf62dfb181063b60ee2d0a39d327fa8ad81b05e3e510680c44e1c078"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9327a4577f65fc3fb712e79f78233815b8a1c94433d0c2c9f6bc5953018b3565"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:200030dfc0a1d5d6ac18e993c5097c870c97c41574e67f227300a1fb74457b1d"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cc269e74cad6043cb8a46d0ce580031ab642b5930562c2bb79aa7fbf9c858d26"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e62779c6371bd2b21dbd1fdce89eaec2d93fd98179d36f61130b489f62294a92"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f4797f821dc5d7c2b6fc818b89f8a3f37bcc900dd9e4369e6ebf1e525efce5db"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d21f188f6fe4fbf422e647ae9d5a68671d00218e187f91859c963d0738ccd88c"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-win32.whl", hash = "sha256:45dd4628dd9c21acc5c97627dad0bb791764feea81436fb6e0a06eef4c6dceaa"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:624a108122039af89ddda1a2b7ab2a11abe60c1521956f142f5d11bcd42ef138"},
- {file = "rapidfuzz-3.13.0-cp39-cp39-win_arm64.whl", hash = "sha256:435071fd07a085ecbf4d28702a66fd2e676a03369ee497cc38bcb69a46bc77e2"},
- {file = "rapidfuzz-3.13.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe5790a36d33a5d0a6a1f802aa42ecae282bf29ac6f7506d8e12510847b82a45"},
- {file = "rapidfuzz-3.13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cdb33ee9f8a8e4742c6b268fa6bd739024f34651a06b26913381b1413ebe7590"},
- {file = "rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c99b76b93f7b495eee7dcb0d6a38fb3ce91e72e99d9f78faa5664a881cb2b7d"},
- {file = "rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6af42f2ede8b596a6aaf6d49fdee3066ca578f4856b85ab5c1e2145de367a12d"},
- {file = "rapidfuzz-3.13.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c0efa73afbc5b265aca0d8a467ae2a3f40d6854cbe1481cb442a62b7bf23c99"},
- {file = "rapidfuzz-3.13.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7ac21489de962a4e2fc1e8f0b0da4aa1adc6ab9512fd845563fecb4b4c52093a"},
- {file = "rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1ba007f4d35a45ee68656b2eb83b8715e11d0f90e5b9f02d615a8a321ff00c27"},
- {file = "rapidfuzz-3.13.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d7a217310429b43be95b3b8ad7f8fc41aba341109dc91e978cd7c703f928c58f"},
- {file = "rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:558bf526bcd777de32b7885790a95a9548ffdcce68f704a81207be4a286c1095"},
- {file = "rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:202a87760f5145140d56153b193a797ae9338f7939eb16652dd7ff96f8faf64c"},
- {file = "rapidfuzz-3.13.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcccc08f671646ccb1e413c773bb92e7bba789e3a1796fd49d23c12539fe2e4"},
- {file = "rapidfuzz-3.13.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1f219f1e3c3194d7a7de222f54450ce12bc907862ff9a8962d83061c1f923c86"},
- {file = "rapidfuzz-3.13.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ccbd0e7ea1a216315f63ffdc7cd09c55f57851afc8fe59a74184cb7316c0598b"},
- {file = "rapidfuzz-3.13.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50856f49a4016ef56edd10caabdaf3608993f9faf1e05c3c7f4beeac46bd12a"},
- {file = "rapidfuzz-3.13.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fd05336db4d0b8348d7eaaf6fa3c517b11a56abaa5e89470ce1714e73e4aca7"},
- {file = "rapidfuzz-3.13.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:573ad267eb9b3f6e9b04febce5de55d8538a87c56c64bf8fd2599a48dc9d8b77"},
- {file = "rapidfuzz-3.13.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30fd1451f87ccb6c2f9d18f6caa483116bbb57b5a55d04d3ddbd7b86f5b14998"},
- {file = "rapidfuzz-3.13.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6dd36d4916cf57ddb05286ed40b09d034ca5d4bca85c17be0cb6a21290597d9"},
- {file = "rapidfuzz-3.13.0.tar.gz", hash = "sha256:d2eaf3839e52cbcc0accbe9817a67b4b0fcf70aaeb229cfddc1c28061f9ce5d8"},
+python-versions = ">=3.10"
+files = [
+ {file = "rapidfuzz-3.14.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9fcd4d751a4fffa17aed1dde41647923c72c74af02459ad1222e3b0022da3a1"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ad73afb688b36864a8d9b7344a9cf6da186c471e5790cbf541a635ee0f457f2"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5fb2d978a601820d2cfd111e2c221a9a7bfdf84b41a3ccbb96ceef29f2f1ac7"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d83b8b712fa37e06d59f29a4b49e2e9e8635e908fbc21552fe4d1163db9d2a1"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:dc8c07801df5206b81ed6bd6c35cb520cf9b6c64b9b0d19d699f8633dc942897"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c71ce6d4231e5ef2e33caa952bfe671cb9fd42e2afb11952df9fad41d5c821f9"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0e38828d1381a0cceb8a4831212b2f673d46f5129a1897b0451c883eaf4a1747"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da2a007434323904719158e50f3076a4dadb176ce43df28ed14610c773cc9825"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-win32.whl", hash = "sha256:fce3152f94afcfd12f3dd8cf51e48fa606e3cb56719bccebe3b401f43d0714f9"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-win_amd64.whl", hash = "sha256:37d3c653af15cd88592633e942f5407cb4c64184efab163c40fcebad05f25141"},
+ {file = "rapidfuzz-3.14.3-cp310-cp310-win_arm64.whl", hash = "sha256:cc594bbcd3c62f647dfac66800f307beaee56b22aaba1c005e9c4c40ed733923"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dea2d113e260a5da0c4003e0a5e9fdf24a9dc2bb9eaa43abd030a1e46ce7837d"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e6c31a4aa68cfa75d7eede8b0ed24b9e458447db604c2db53f358be9843d81d3"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02821366d928e68ddcb567fed8723dad7ea3a979fada6283e6914d5858674850"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe8df315ab4e6db4e1be72c5170f8e66021acde22cd2f9d04d2058a9fd8162e"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:769f31c60cd79420188fcdb3c823227fc4a6deb35cafec9d14045c7f6743acae"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54fa03062124e73086dae66a3451c553c1e20a39c077fd704dc7154092c34c63"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:834d1e818005ed0d4ae38f6b87b86fad9b0a74085467ece0727d20e15077c094"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:948b00e8476a91f510dd1ec07272efc7d78c275d83b630455559671d4e33b678"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-win32.whl", hash = "sha256:43d0305c36f504232f18ea04e55f2059bb89f169d3119c4ea96a0e15b59e2a91"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:ef6bf930b947bd0735c550683939a032090f1d688dfd8861d6b45307b96fd5c5"},
+ {file = "rapidfuzz-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:f3eb0ff3b75d6fdccd40b55e7414bb859a1cda77c52762c9c82b85569f5088e7"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:685c93ea961d135893b5984a5a9851637d23767feabe414ec974f43babbd8226"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa7c8f26f009f8c673fbfb443792f0cf8cf50c4e18121ff1e285b5e08a94fbdb"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57f878330c8d361b2ce76cebb8e3e1dc827293b6abf404e67d53260d27b5d941"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c5f545f454871e6af05753a0172849c82feaf0f521c5ca62ba09e1b382d6382"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:07aa0b5d8863e3151e05026a28e0d924accf0a7a3b605da978f0359bb804df43"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73b07566bc7e010e7b5bd490fb04bb312e820970180df6b5655e9e6224c137db"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6de00eb84c71476af7d3110cf25d8fe7c792d7f5fa86764ef0b4ca97e78ca3ed"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7843a1abf0091773a530636fdd2a49a41bcae22f9910b86b4f903e76ddc82dc"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-win32.whl", hash = "sha256:dea97ac3ca18cd3ba8f3d04b5c1fe4aa60e58e8d9b7793d3bd595fdb04128d7a"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:b5100fd6bcee4d27f28f4e0a1c6b5127bc8ba7c2a9959cad9eab0bf4a7ab3329"},
+ {file = "rapidfuzz-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:4e49c9e992bc5fc873bd0fff7ef16a4405130ec42f2ce3d2b735ba5d3d4eb70f"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbcb726064b12f356bf10fffdb6db4b6dce5390b23627c08652b3f6e49aa56ae"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1704fc70d214294e554a2421b473779bcdeef715881c5e927dc0f11e1692a0ff"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc65e72790ddfd310c2c8912b45106e3800fefe160b0c2ef4d6b6fec4e826457"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e38c1305cffae8472572a0584d4ffc2f130865586a81038ca3965301f7c97c"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:e195a77d06c03c98b3fc06b8a28576ba824392ce40de8c708f96ce04849a052e"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b7ef2f4b8583a744338a18f12c69693c194fb6777c0e9ada98cd4d9e8f09d10"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a2135b138bcdcb4c3742d417f215ac2d8c2b87bde15b0feede231ae95f09ec41"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33a325ed0e8e1aa20c3e75f8ab057a7b248fdea7843c2a19ade0008906c14af0"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-win32.whl", hash = "sha256:8383b6d0d92f6cd008f3c9216535be215a064b2cc890398a678b56e6d280cb63"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:e6b5e3036976f0fde888687d91be86d81f9ac5f7b02e218913c38285b756be6c"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:7ba009977601d8b0828bfac9a110b195b3e4e79b350dcfa48c11269a9f1918a0"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0a28add871425c2fe94358c6300bbeb0bc2ed828ca003420ac6825408f5a424"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:010e12e2411a4854b0434f920e72b717c43f8ec48d57e7affe5c42ecfa05dd0e"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cfc3d57abd83c734d1714ec39c88a34dd69c85474918ebc21296f1e61eb5ca8"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89acb8cbb52904f763e5ac238083b9fc193bed8d1f03c80568b20e4cef43a519"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-manylinux_2_31_armv7l.whl", hash = "sha256:7d9af908c2f371bfb9c985bd134e295038e3031e666e4b2ade1e7cb7f5af2f1a"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:1f1925619627f8798f8c3a391d81071336942e5fe8467bc3c567f982e7ce2897"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:152555187360978119e98ce3e8263d70dd0c40c7541193fc302e9b7125cf8f58"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52619d25a09546b8db078981ca88939d72caa6b8701edd8b22e16482a38e799f"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-win32.whl", hash = "sha256:489ce98a895c98cad284f0a47960c3e264c724cb4cfd47a1430fa091c0c25204"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-win_amd64.whl", hash = "sha256:656e52b054d5b5c2524169240e50cfa080b04b1c613c5f90a2465e84888d6f15"},
+ {file = "rapidfuzz-3.14.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c7e40c0a0af02ad6e57e89f62bef8604f55a04ecae90b0ceeda591bbf5923317"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:442125473b247227d3f2de807a11da6c08ccf536572d1be943f8e262bae7e4ea"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ec0c8c0c3d4f97ced46b2e191e883f8c82dbbf6d5ebc1842366d7eff13cd5a6"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2dc37bc20272f388b8c3a4eba4febc6e77e50a8f450c472def4751e7678f55e4"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee362e7e79bae940a5e2b3f6d09c6554db6a4e301cc68343886c08be99844f1"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:4b39921df948388a863f0e267edf2c36302983459b021ab928d4b801cbe6a421"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:beda6aa9bc44d1d81242e7b291b446be352d3451f8217fcb068fc2933927d53b"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6a014ba09657abfcfeed64b7d09407acb29af436d7fc075b23a298a7e4a6b41c"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:32eeafa3abce138bb725550c0e228fc7eaeec7059aa8093d9cbbec2b58c2371a"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-win32.whl", hash = "sha256:adb44d996fc610c7da8c5048775b21db60dd63b1548f078e95858c05c86876a3"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:f3d15d8527e2b293e38ce6e437631af0708df29eafd7c9fc48210854c94472f9"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:576e4b9012a67e0bf54fccb69a7b6c94d4e86a9540a62f1a5144977359133583"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cec3c0da88562727dd5a5a364bd9efeb535400ff0bfb1443156dd139a1dd7b50"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d1fa009f8b1100e4880868137e7bf0501422898f7674f2adcd85d5a67f041296"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b86daa7419b5e8b180690efd1fdbac43ff19230803282521c5b5a9c83977655"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7bd1816db05d6c5ffb3a4df0a2b7b56fb8c81ef584d08e37058afa217da91b1"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:33da4bbaf44e9755b0ce192597f3bde7372fe2e381ab305f41b707a95ac57aa7"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3fecce764cf5a991ee2195a844196da840aba72029b2612f95ac68a8b74946bf"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ecd7453e02cf072258c3a6b8e930230d789d5d46cc849503729f9ce475d0e785"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea188aa00e9bcae8c8411f006a5f2f06c4607a02f24eab0d8dc58566aa911f35"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:7ccbf68100c170e9a0581accbe9291850936711548c6688ce3bfb897b8c589ad"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9ec02e62ae765a318d6de38df609c57fc6dacc65c0ed1fd489036834fd8a620c"},
+ {file = "rapidfuzz-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e805e52322ae29aa945baf7168b6c898120fbc16d2b8f940b658a5e9e3999253"},
+ {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7cf174b52cb3ef5d49e45d0a1133b7e7d0ecf770ed01f97ae9962c5c91d97d23"},
+ {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:442cba39957a008dfc5bdef21a9c3f4379e30ffb4e41b8555dbaf4887eca9300"},
+ {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1faa0f8f76ba75fd7b142c984947c280ef6558b5067af2ae9b8729b0a0f99ede"},
+ {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e6eefec45625c634926a9fd46c9e4f31118ac8f3156fff9494422cee45207e6"},
+ {file = "rapidfuzz-3.14.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56fefb4382bb12250f164250240b9dd7772e41c5c8ae976fd598a32292449cc5"},
+ {file = "rapidfuzz-3.14.3.tar.gz", hash = "sha256:2491937177868bc4b1e469087601d53f925e8d270ccc21e07404b4b5814b7b5f"},
]
[package.extras]
@@ -2399,7 +2392,6 @@ version = "2.31.0"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.7"
-groups = ["main", "dev"]
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
@@ -2421,7 +2413,6 @@ version = "1.2.1"
description = "A persistent cache for python requests"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "requests_cache-1.2.1-py3-none-any.whl", hash = "sha256:1285151cddf5331067baa82598afe2d47c7495a1334bfe7a7d329b43e9fd3603"},
{file = "requests_cache-1.2.1.tar.gz", hash = "sha256:68abc986fdc5b8d0911318fbb5f7c80eebcd4d01bfacc6685ecf8876052511d1"},
@@ -2452,7 +2443,6 @@ version = "2.0.0"
description = "OAuthlib authentication support for Requests."
optional = false
python-versions = ">=3.4"
-groups = ["dev"]
files = [
{file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"},
{file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"},
@@ -2467,30 +2457,28 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
[[package]]
name = "rgb-lib"
-version = "0.3.0a12"
+version = "0.3.0b4"
description = "RGB Lib Python language bindings."
optional = false
python-versions = ">=3.9.0"
-groups = ["dev"]
files = [
- {file = "rgb_lib-0.3.0a12-py3-none-macosx_12_0_arm64.whl", hash = "sha256:f4e5a3b2d1f4cb04f2466834e7d8985599df3982299c915ab10c79c9c790cb75"},
- {file = "rgb_lib-0.3.0a12-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:aba59d56bcf28af3da79d9674775c1bd2149841f97b4264b60e71939fa305ae8"},
- {file = "rgb_lib-0.3.0a12-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:3a24355bf16edd893445084126b7523b42b0a71322deaec53bb498d7192973d6"},
- {file = "rgb_lib-0.3.0a12-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:40463f14af98327ee02a27feb78b66d46fc5bdd1db5063ff6c9fbf3d790e65f2"},
- {file = "rgb_lib-0.3.0a12-py3-none-win_amd64.whl", hash = "sha256:96dc6942e61a94427e0dd0ff6101f2ea8bc965280e6ff11fcecc7d93207ca28f"},
- {file = "rgb_lib-0.3.0a12.tar.gz", hash = "sha256:ad9bd876f276f9d2e365c19fb16e09ddc1c3aa4d115fba2fad9817c984d120ee"},
+ {file = "rgb_lib-0.3.0b4-py3-none-macosx_12_0_arm64.whl", hash = "sha256:dac156401abc7881e94eb5ebcbe3204b83b67669ddcc7fdfbc0ba476e91e60b2"},
+ {file = "rgb_lib-0.3.0b4-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:20d3f552ca202c7564ed9e501f569c17e31d7dc8051fc8d076d11011062e1703"},
+ {file = "rgb_lib-0.3.0b4-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:77d5ca45ca7d37abb92764468e2bc09de3dc1c7ad05fff7e38560bcfc2a7772f"},
+ {file = "rgb_lib-0.3.0b4-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:44081536c8dd3ae1dc5175399e789f9748eade3e9e1cb7ed416066f66f675d80"},
+ {file = "rgb_lib-0.3.0b4-py3-none-win_amd64.whl", hash = "sha256:3219cccce0b3d489b32e3f9122cc6867cf0efba266348ebd8955fa335c5d26a6"},
+ {file = "rgb_lib-0.3.0b4.tar.gz", hash = "sha256:8ff21bc4c2fecf3f25561711d90a5c87049c5b5d60ae541b9b20d4bee99ed76e"},
]
[[package]]
name = "rsa"
-version = "4.9"
+version = "4.9.1"
description = "Pure-Python RSA implementation"
optional = false
-python-versions = ">=3.6,<4"
-groups = ["dev"]
+python-versions = "<4,>=3.6"
files = [
- {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
- {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
+ {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"},
+ {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"},
]
[package.dependencies]
@@ -2498,15 +2486,13 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "secretstorage"
-version = "3.3.3"
+version = "3.5.0"
description = "Python bindings to FreeDesktop.org Secret Service API"
optional = false
-python-versions = ">=3.6"
-groups = ["main"]
-markers = "sys_platform == \"linux\""
+python-versions = ">=3.10"
files = [
- {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"},
- {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"},
+ {file = "secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137"},
+ {file = "secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be"},
]
[package.dependencies]
@@ -2515,24 +2501,19 @@ jeepney = ">=0.6"
[[package]]
name = "setuptools"
-version = "78.1.0"
+version = "68.0.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
+python-versions = ">=3.7"
files = [
- {file = "setuptools-78.1.0-py3-none-any.whl", hash = "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8"},
- {file = "setuptools-78.1.0.tar.gz", hash = "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54"},
+ {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"},
+ {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"},
]
[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""]
-core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"]
-cover = ["pytest-cov"]
-doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
-enabler = ["pytest-enabler (>=2.2)"]
-test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
-type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"]
+docs = ["furo", "jaraco.packaging (>=9)", "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 (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "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)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "shiboken6"
@@ -2540,7 +2521,6 @@ version = "6.7.3"
description = "Python/C++ bindings helper module"
optional = false
python-versions = "<3.13,>=3.9"
-groups = ["main"]
files = [
{file = "shiboken6-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:285fe3cf79be3135fe1ad1e2b9ff6db3a48698887425af6aa6ed7a05a9abc3d6"},
{file = "shiboken6-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f0852e5781de78be5b13c140ec4c7fb9734e2aaf2986eb2d6a224363e03efccc"},
@@ -2554,7 +2534,6 @@ version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-groups = ["dev"]
files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
@@ -2562,140 +2541,147 @@ files = [
[[package]]
name = "tomli"
-version = "2.2.1"
+version = "2.4.0"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
-groups = ["dev", "test"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
- {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
- {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
- {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
- {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
- {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
- {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
- {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
- {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
- {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
- {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
- {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
- {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
- {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
- {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
- {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
- {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
- {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
- {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
- {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
- {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
- {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
- {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
- {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
- {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
- {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
- {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
- {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
- {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
- {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
- {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
- {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
+files = [
+ {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"},
+ {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"},
+ {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"},
+ {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"},
+ {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"},
+ {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"},
+ {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"},
+ {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"},
+ {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"},
+ {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"},
+ {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"},
+ {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"},
+ {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"},
+ {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"},
+ {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"},
+ {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"},
+ {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"},
+ {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"},
+ {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"},
+ {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"},
+ {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"},
+ {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"},
+ {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"},
+ {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"},
+ {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"},
+ {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"},
+ {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"},
+ {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"},
+ {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"},
+ {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"},
+ {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"},
+ {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"},
+ {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"},
+ {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"},
+ {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"},
+ {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"},
+ {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"},
+ {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"},
+ {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"},
+ {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"},
+ {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"},
+ {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"},
+ {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"},
+ {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"},
+ {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"},
+ {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"},
+ {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"},
]
[[package]]
name = "tomlkit"
-version = "0.13.2"
+version = "0.14.0"
description = "Style preserving TOML library"
optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
+python-versions = ">=3.9"
files = [
- {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"},
- {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"},
+ {file = "tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680"},
+ {file = "tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064"},
]
[[package]]
name = "typing-extensions"
-version = "4.13.2"
-description = "Backported and Experimental Type Hints for Python 3.8+"
+version = "4.15.0"
+description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
+python-versions = ">=3.9"
files = [
- {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
- {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
+ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
+ {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
]
-markers = {dev = "python_version == \"3.10\""}
[[package]]
name = "uritemplate"
-version = "4.1.1"
+version = "4.2.0"
description = "Implementation of RFC 6570 URI Templates"
optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
+python-versions = ">=3.9"
files = [
- {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
- {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
+ {file = "uritemplate-4.2.0-py3-none-any.whl", hash = "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686"},
+ {file = "uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e"},
]
[[package]]
name = "url-normalize"
-version = "2.2.0"
+version = "2.2.1"
description = "URL normalization for Python"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
- {file = "url_normalize-2.2.0-py3-none-any.whl", hash = "sha256:3fe387b62f5b66db94304bc703bf6d34de52aaa9590d4d1f1bbdf305a1430064"},
- {file = "url_normalize-2.2.0.tar.gz", hash = "sha256:0f0b7cc95a95d2d9b0c9a51d47a326559bc05bd1558accdada21bb0c9504de85"},
+ {file = "url_normalize-2.2.1-py3-none-any.whl", hash = "sha256:3deb687587dc91f7b25c9ae5162ffc0f057ae85d22b1e15cf5698311247f567b"},
+ {file = "url_normalize-2.2.1.tar.gz", hash = "sha256:74a540a3b6eba1d95bdc610c24f2c0141639f3ba903501e61a52a8730247ff37"},
]
[package.dependencies]
idna = ">=3.3"
[package.extras]
-dev = ["mypy", "pre-commit", "pytest", "pytest-cov", "pytest-ruff", "pytest-socket", "ruff", "tox"]
+dev = ["mypy", "pre-commit", "pytest", "pytest-cov", "pytest-socket", "ruff"]
[[package]]
name = "urllib3"
-version = "2.4.0"
+version = "2.6.3"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev"]
files = [
- {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"},
- {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"},
+ {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"},
+ {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"},
]
[package.extras]
-brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
+brotli = ["brotli (>=1.2.0)", "brotlicffi (>=1.2.0.0)"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["zstandard (>=0.18.0)"]
+zstd = ["backports-zstd (>=1.0.0)"]
[[package]]
name = "virtualenv"
-version = "20.30.0"
+version = "20.36.1"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
- {file = "virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"},
- {file = "virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"},
+ {file = "virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f"},
+ {file = "virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba"},
]
[package.dependencies]
distlib = ">=0.3.7,<1"
-filelock = ">=3.12.2,<4"
+filelock = {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""}
platformdirs = ">=3.9.1,<5"
+typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""}
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[[package]]
name = "winsdk"
@@ -2703,8 +2689,6 @@ version = "1.0.0b10"
description = "Python bindings for the Windows SDK"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
-markers = "sys_platform == \"win32\""
files = [
{file = "winsdk-1.0.0b10-cp310-cp310-win32.whl", hash = "sha256:90f75c67e166d588a045bcde0117a4631c705904f7af4ac42644479dcf0d8c52"},
{file = "winsdk-1.0.0b10-cp310-cp310-win_amd64.whl", hash = "sha256:c3be3fbf692b8888bac8c0712c490c080ab8976649ef01f9f6365947f4e5a8b1"},
@@ -2725,26 +2709,24 @@ files = [
[[package]]
name = "zipp"
-version = "3.21.0"
+version = "3.23.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.9"
-groups = ["main", "dev"]
files = [
- {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
- {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
+ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"},
+ {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"},
]
-markers = {main = "python_version < \"3.12\""}
[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
enabler = ["pytest-enabler (>=2.2)"]
-test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
+test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
type = ["pytest-mypy"]
[metadata]
-lock-version = "2.1"
+lock-version = "2.0"
python-versions = ">=3.10,<3.13"
-content-hash = "2c8c40dab7561f11bae1e6a8c22aaf1141ba692a73ff8ebfa97889232b1f0e15"
+content-hash = "415b4fab8ae0b6d3633a620d4e298447f10024c479bb913abd6cc464582a6378"
diff --git a/pyproject.toml b/pyproject.toml
index 3389a8e5..1c1243b3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "iris-wallet-desktop"
-version = "0.2.0"
+version = "0.3.0"
description = ""
readme = "README.md"
license = ""
@@ -24,6 +24,7 @@ qrcode = "^7.4.2"
pydenticon = "^0.3.0"
# Development dependencies
+setuptools = "68.0.0"
[tool.poetry.dev-dependencies]
pyqt-toast-notification = "^1.2.0"
pre-commit = "^3.7.1"
@@ -40,7 +41,7 @@ cryptography = "^43.0.0"
asyncio = "^3.4.3"
requests-cache = "^1.2.1"
pytest-xdist = "^3.6.1"
-rgb-lib = "0.3.0a12"
+rgb-lib = "0.3.0b4"
psutil = "^6.1.1"
pyotp = "^2.9.0"
pytest-order = "^1.3.0"
diff --git a/src/assets/icons/testnet4-icon.png b/src/assets/icons/testnet4-icon.png
new file mode 100644
index 00000000..7413cf5f
Binary files /dev/null and b/src/assets/icons/testnet4-icon.png differ
diff --git a/src/data/repository/setting_card_repository.py b/src/data/repository/setting_card_repository.py
index 95cf1d2b..ed55fc0a 100644
--- a/src/data/repository/setting_card_repository.py
+++ b/src/data/repository/setting_card_repository.py
@@ -25,19 +25,23 @@
from src.utils.constant import BITCOIND_RPC_HOST_MAINNET
from src.utils.constant import BITCOIND_RPC_HOST_REGTEST
from src.utils.constant import BITCOIND_RPC_HOST_TESTNET
+from src.utils.constant import BITCOIND_RPC_HOST_TESTNET4
from src.utils.constant import BITCOIND_RPC_PORT_MAINNET
from src.utils.constant import BITCOIND_RPC_PORT_REGTEST
from src.utils.constant import BITCOIND_RPC_PORT_TESTNET
+from src.utils.constant import BITCOIND_RPC_PORT_TESTNET4
from src.utils.constant import FEE_RATE
from src.utils.constant import INDEXER_URL_MAINNET
from src.utils.constant import INDEXER_URL_REGTEST
from src.utils.constant import INDEXER_URL_TESTNET
+from src.utils.constant import INDEXER_URL_TESTNET4
from src.utils.constant import LN_INVOICE_EXPIRY_TIME
from src.utils.constant import LN_INVOICE_EXPIRY_TIME_UNIT
from src.utils.constant import MIN_CONFIRMATION
from src.utils.constant import PROXY_ENDPOINT_MAINNET
from src.utils.constant import PROXY_ENDPOINT_REGTEST
from src.utils.constant import PROXY_ENDPOINT_TESTNET
+from src.utils.constant import PROXY_ENDPOINT_TESTNET4
from src.utils.constant import SAVED_ANNOUNCE_ADDRESS
from src.utils.constant import SAVED_ANNOUNCE_ALIAS
from src.utils.constant import SAVED_BITCOIND_RPC_HOST
@@ -199,6 +203,8 @@ def get_default_indexer_url() -> DefaultIndexerUrl:
indexer_url = INDEXER_URL_MAINNET
elif stored_network == NetworkEnumModel.TESTNET:
indexer_url = INDEXER_URL_TESTNET
+ elif stored_network == NetworkEnumModel.TESTNET4:
+ indexer_url = INDEXER_URL_TESTNET4
elif stored_network == NetworkEnumModel.REGTEST:
indexer_url = INDEXER_URL_REGTEST
url = local_store.get_value(SAVED_INDEXER_URL)
@@ -242,6 +248,8 @@ def get_default_proxy_endpoint() -> DefaultProxyEndpoint:
proxy_endpoint = PROXY_ENDPOINT_MAINNET
elif stored_network == NetworkEnumModel.TESTNET:
proxy_endpoint = PROXY_ENDPOINT_TESTNET
+ elif stored_network == NetworkEnumModel.TESTNET4:
+ proxy_endpoint = PROXY_ENDPOINT_TESTNET4
elif stored_network == NetworkEnumModel.REGTEST:
proxy_endpoint = PROXY_ENDPOINT_REGTEST
endpoint = local_store.get_value(SAVED_PROXY_ENDPOINT)
@@ -267,6 +275,8 @@ def get_default_bitcoind_host() -> DefaultBitcoindHost:
bitcoind_host = BITCOIND_RPC_HOST_MAINNET
elif stored_network == NetworkEnumModel.TESTNET:
bitcoind_host = BITCOIND_RPC_HOST_TESTNET
+ elif stored_network == NetworkEnumModel.TESTNET4:
+ bitcoind_host = BITCOIND_RPC_HOST_TESTNET4
elif stored_network == NetworkEnumModel.REGTEST:
bitcoind_host = BITCOIND_RPC_HOST_REGTEST
host = local_store.get_value(SAVED_BITCOIND_RPC_HOST)
@@ -292,6 +302,8 @@ def get_default_bitcoind_port() -> DefaultBitcoindPort:
bitcoind_port = BITCOIND_RPC_PORT_MAINNET
elif stored_network == NetworkEnumModel.TESTNET:
bitcoind_port = BITCOIND_RPC_PORT_TESTNET
+ elif stored_network == NetworkEnumModel.TESTNET4:
+ bitcoind_port = BITCOIND_RPC_PORT_TESTNET4
elif stored_network == NetworkEnumModel.REGTEST:
bitcoind_port = BITCOIND_RPC_PORT_REGTEST
port = local_store.get_value(SAVED_BITCOIND_RPC_PORT)
diff --git a/src/data/service/asset_detail_page_services.py b/src/data/service/asset_detail_page_services.py
index 80cdff55..1126b608 100644
--- a/src/data/service/asset_detail_page_services.py
+++ b/src/data/service/asset_detail_page_services.py
@@ -176,21 +176,35 @@ def assign_transfer_status(transaction):
# Assign transfer statuses based on the transaction kind
if transaction.kind == AssetTransferStatusEnumModel.ISSUANCE.value:
transaction.transfer_Status = TransferStatusEnumModel.INTERNAL
- transaction.amount_status = f'+{
- str(transaction.amount)
- }'
+ transaction.amount_status = f"""+{
+ str(transaction.assignments[0].value)
+ }"""
elif transaction.kind in (
AssetTransferStatusEnumModel.RECEIVE_BLIND.value,
AssetTransferStatusEnumModel.RECEIVE_WITNESS.value,
):
transaction.transfer_Status = TransferStatusEnumModel.RECEIVED
- transaction.amount_status = f'+{
- str(transaction.amount)
- }'
+ amount_val = None
+ if (
+ transaction.assignments
+ and len(transaction.assignments) > 0
+ and transaction.assignments[0].value is not None
+ ):
+ amount_val = transaction.assignments[0].value
+ elif (
+ transaction.requested_assignment
+ and transaction.requested_assignment.value is not None
+ ):
+ amount_val = transaction.requested_assignment.value
+
+ transaction.amount_status = f"+{
+ str(amount_val)
+ if amount_val is not None else '0'
+ }"
elif transaction.kind == AssetTransferStatusEnumModel.SEND.value:
- transaction.amount_status = f'-{
- str(transaction.amount)
- }'
+ transaction.amount_status = f"""-{
+ str(transaction.requested_assignment.value)
+ }"""
transaction.transfer_Status = TransferStatusEnumModel.SENT
else:
raise ServiceOperationException(
diff --git a/src/data/service/backup_service.py b/src/data/service/backup_service.py
index f7c6ef4d..38acbb7f 100644
--- a/src/data/service/backup_service.py
+++ b/src/data/service/backup_service.py
@@ -15,6 +15,7 @@
from src.utils.error_message import ERROR_UNABLE_TO_GET_PASSWORD
from src.utils.gdrive_operation import GoogleDriveManager
from src.utils.handle_exception import handle_exceptions
+from src.utils.helpers import write_ln_node_commit_id_file
from src.utils.logging import logger
@@ -93,7 +94,14 @@ def backup(mnemonic: str, password: str) -> bool:
success: bool = backup.upload_to_drive(
file_path=backup_file_path, file_name=backup_file_name,
)
- return success
+
+ commit_id_file_path, commit_id_file_name = write_ln_node_commit_id_file(
+ hashed_mnemonic,
+ )
+ commit_id_success: bool = backup.upload_to_drive(
+ file_path=commit_id_file_path, file_name=commit_id_file_name,
+ )
+ return success and commit_id_success
except Exception as exc:
return handle_exceptions(exc)
finally:
diff --git a/src/data/service/helpers/faucet_service_helper.py b/src/data/service/helpers/faucet_service_helper.py
index 3f74ea11..6f19fe27 100644
--- a/src/data/service/helpers/faucet_service_helper.py
+++ b/src/data/service/helpers/faucet_service_helper.py
@@ -6,6 +6,7 @@
from src.model.enums.enums_model import NetworkEnumModel
from src.utils.constant import rgbMainnetFaucetURLs
from src.utils.constant import rgbRegtestFaucetURLs
+from src.utils.constant import rgbTestnet4FaucetURLs
from src.utils.constant import rgbTestnetFaucetURLs
from src.utils.custom_exception import ServiceOperationException
from src.utils.error_message import ERROR_FAILED_TO_GET_FAUCET_URL
@@ -19,6 +20,8 @@ def get_faucet_url(network: NetworkEnumModel) -> str:
return rgbRegtestFaucetURLs[0]
if network.value == NetworkEnumModel.TESTNET.value:
return rgbTestnetFaucetURLs[0]
+ if network.value == NetworkEnumModel.TESTNET4.value:
+ return rgbTestnet4FaucetURLs[0]
if network.value == NetworkEnumModel.MAINNET.value:
return rgbMainnetFaucetURLs[0]
raise ServiceOperationException(ERROR_INVALID_NETWORK_TYPE)
diff --git a/src/data/service/helpers/main_asset_page_helper.py b/src/data/service/helpers/main_asset_page_helper.py
index 82133175..ac9c92b2 100644
--- a/src/data/service/helpers/main_asset_page_helper.py
+++ b/src/data/service/helpers/main_asset_page_helper.py
@@ -24,7 +24,7 @@ def get_offline_asset_ticker(network: NetworkEnumModel):
try:
if network.value == NetworkEnumModel.REGTEST.value:
return 'r' + 'BTC'
- if network.value == NetworkEnumModel.TESTNET.value:
+ if network.value in (NetworkEnumModel.TESTNET.value, NetworkEnumModel.TESTNET4.value):
return 't' + 'BTC'
if network.value == NetworkEnumModel.MAINNET.value:
return 'BTC'
@@ -48,7 +48,7 @@ def get_asset_name(network: NetworkEnumModel):
try:
if network.value == NetworkEnumModel.REGTEST.value:
return 'r' + 'Bitcoin'
- if network.value == NetworkEnumModel.TESTNET.value:
+ if network.value in (NetworkEnumModel.TESTNET.value, NetworkEnumModel.TESTNET4.value):
return 't' + 'Bitcoin'
if network.value == NetworkEnumModel.MAINNET.value:
return 'Bitcoin'
diff --git a/src/data/service/restore_service.py b/src/data/service/restore_service.py
index beb1d9ab..97927977 100644
--- a/src/data/service/restore_service.py
+++ b/src/data/service/restore_service.py
@@ -8,16 +8,19 @@
import shutil
from src.data.repository.common_operations_repository import CommonOperationRepository
+from src.data.repository.setting_repository import SettingRepository
from src.data.service.common_operation_service import CommonOperationService
from src.model.common_operation_model import RestoreRequestModel
from src.model.common_operation_model import RestoreResponseModel
from src.utils.build_app_path import app_paths
+from src.utils.constant import COMPATIBLE_RLN_NODE_COMMITS
from src.utils.custom_exception import CommonException
from src.utils.error_message import ERROR_NOT_BACKUP_FILE
from src.utils.error_message import ERROR_UNABLE_TO_GET_PASSWORD
from src.utils.error_message import ERROR_WHILE_RESTORE_DOWNLOAD_FROM_DRIVE
from src.utils.gdrive_operation import GoogleDriveManager
from src.utils.handle_exception import handle_exceptions
+from src.utils.helpers import read_ln_node_commit_id_file
from src.utils.logging import logger
@@ -66,6 +69,17 @@ def restore(mnemonic: str, password: str) -> RestoreResponseModel:
# Download restore zip from Google Drive
logger.info('Downloading restore zip from drive')
restore = GoogleDriveManager()
+ commit_id_file_name = f'{hashed_mnemonic}.commit'
+ restore.download_from_drive(
+ file_name=commit_id_file_name, destination_dir=restore_folder_path,
+ )
+
+ commit_id = read_ln_node_commit_id_file(commit_id_file_name)
+ if commit_id not in COMPATIBLE_RLN_NODE_COMMITS:
+ raise CommonException('RGB_LIB_INCOMPATIBLE')
+ SettingRepository.set_rln_node_commit_id(
+ commit_id,
+ )
success: bool | None = restore.download_from_drive(
file_name=restore_file_name, destination_dir=restore_folder_path,
)
diff --git a/src/model/btc_model.py b/src/model/btc_model.py
index d7ad17b3..fba60551 100644
--- a/src/model/btc_model.py
+++ b/src/model/btc_model.py
@@ -12,6 +12,7 @@
from src.model.enums.enums_model import TransactionStatusEnumModel
from src.model.enums.enums_model import TransferStatusEnumModel
+from src.model.rgb_model import AssignmentModel
# -------------------- Helper models -----------------------
@@ -47,7 +48,7 @@ class Utxo(BaseModel):
class RgbAllocation(BaseModel):
"""Model part of list unspents api response model"""
asset_id: str | None = None
- amount: int
+ assignment: AssignmentModel
settled: bool
@@ -71,7 +72,6 @@ class OfflineAsset(BaseModel):
ticker: str
balance: BalanceStatus
name: str
- asset_iface: str = 'BITCOIN'
# -------------------- Request Models -----------------------
diff --git a/src/model/common_operation_model.py b/src/model/common_operation_model.py
index 966e5c50..1fe0cb80 100644
--- a/src/model/common_operation_model.py
+++ b/src/model/common_operation_model.py
@@ -123,7 +123,8 @@ class NodeInfoResponseModel(BaseModel):
eventual_close_fees_sat: int
pending_outbound_payments_sat: int
num_peers: int
- onchain_pubkey: str
+ account_xpub_vanilla: str
+ account_xpub_colored: str
max_media_upload_size_mb: int
rgb_htlc_min_msat: int
rgb_channel_capacity_min_sat: int
diff --git a/src/model/enums/enums_model.py b/src/model/enums/enums_model.py
index 9dcee7cc..f7338c46 100644
--- a/src/model/enums/enums_model.py
+++ b/src/model/enums/enums_model.py
@@ -11,6 +11,8 @@ class NetworkEnumModel(str, Enum):
REGTEST = 'regtest'
MAINNET = 'mainnet'
TESTNET = 'testnet'
+ TESTNET4 = 'testnet4'
+ SIGNET = 'signet'
class FilterAssetEnumModel(str, Enum):
@@ -52,7 +54,7 @@ class AssetTransferStatusEnumModel(str, Enum):
class NativeAuthType(str, Enum):
"""Enum for authentication type for native"""
LOGGING_TO_APP = 'LOGGING_TO_APP'
- # operation like issue rgb20 or rgb25 and transactions
+ # operation like issue nia or cfa and transactions
MAJOR_OPERATION = 'MAJOR_OPERATION'
@@ -71,8 +73,9 @@ class WalletType(str, Enum):
class AssetType(str, Enum):
"""Enum for asset type"""
- RGB20 = 'RGB20'
- RGB25 = 'RGB25'
+ NIA = 'NIA'
+ CFA = 'CFA'
+ UDA = 'UDA'
BITCOIN = 'BITCOIN'
@@ -89,6 +92,7 @@ class TokenSymbol(str, Enum):
"""Enum for token symbol"""
BITCOIN = 'BTC'
TESTNET_BITCOIN = 'tBTC'
+ TESTNET4_BITCOIN = 'tBTC'
REGTEST_BITCOIN = 'rBTC'
SAT = 'SAT'
@@ -125,3 +129,12 @@ class ChannelFetchingModel(str, Enum):
FETCHING = 'fetching'
FETCHED = 'fetched'
FAILED = 'failed'
+
+
+class AssignmentEnumModel(str, Enum):
+ """Enum for assignment"""
+ FUNGIBLE = 'Fungible'
+ NON_FUNGIBLE = 'NonFungible'
+ INFLATION_RIGHT = 'InflationRight'
+ REPLACE_RIGHT = 'ReplaceRight'
+ ANY = 'Any'
diff --git a/src/model/rgb_model.py b/src/model/rgb_model.py
index 6f9f6e8a..4e206e37 100644
--- a/src/model/rgb_model.py
+++ b/src/model/rgb_model.py
@@ -8,6 +8,7 @@
from pydantic import Field
from pydantic import model_validator
+from src.model.enums.enums_model import AssignmentEnumModel
from src.model.enums.enums_model import FilterAssetEnumModel
from src.model.enums.enums_model import TransferStatusEnumModel
from src.model.payments_model import BaseTimeStamps
@@ -26,6 +27,12 @@ class StatusModel(BaseModel):
status: bool
+class AssignmentModel(BaseModel):
+ """Assignment model"""
+ type: AssignmentEnumModel
+ value: int | None = None
+
+
class TransactionTxModel(BaseModel):
"""Mode for get single transaction method of asset detail page service"""
tx_id: str | None = None
@@ -79,7 +86,6 @@ class Token(BaseModel):
class AssetModel(BaseModel):
"""Model for asset """
asset_id: str
- asset_iface: str
ticker: str | None = None
name: str
details: str | None
@@ -105,7 +111,8 @@ class TransferAsset(BaseTimeStamps):
idx: int
status: str
- amount: int
+ requested_assignment: AssignmentModel | None
+ assignments: list[AssignmentModel | None] | None = []
amount_status: str | None = None # this for ui purpose
kind: str
transfer_Status: TransferStatusEnumModel | None = None
@@ -176,13 +183,14 @@ class RgbInvoiceRequestModel(BaseModel):
min_confirmations: int
asset_id: str | None = None
duration_seconds: int = RGB_INVOICE_DURATION_SECONDS
+ witness: bool = False
class SendAssetRequestModel(BaseModel):
"""Request model for sending assets."""
asset_id: str
- amount: int
+ assignment: AssignmentModel
recipient_id: str
donation: bool | None = False
fee_rate: int
@@ -230,9 +238,9 @@ class DecodeRgbInvoiceResponseModel(BaseModel):
"""Response model for decoding RGB invoices."""
recipient_id: str
- asset_iface: str | None = None
+ asset_schema: str | None = None
asset_id: str | None = None
- amount: str | None = None
+ assignment: AssignmentModel | None
network: str
expiration_timestamp: int
transport_endpoints: list[str]
diff --git a/src/translations/en_IN.qm b/src/translations/en_IN.qm
index 80711c0d..f5eda409 100644
Binary files a/src/translations/en_IN.qm and b/src/translations/en_IN.qm differ
diff --git a/src/translations/en_IN.ts b/src/translations/en_IN.ts
index 027b8ead..6e29a225 100644
--- a/src/translations/en_IN.ts
+++ b/src/translations/en_IN.ts
@@ -324,11 +324,11 @@ If you understand the above remarks and wish to proceed, press the button below
CHANGE UPLOADED FILE
- issue_new_rgb25_asset
- Issue new RGB25 asset
+ issue_new_cfa_asset
+ Issue new CFA asset
- rgb25_address_info
+ cfa_address_info
The blinded UTXO in this invoice will expire in 24 hours after its creation and will be valid only for this asset
@@ -588,8 +588,8 @@ If you understand the above remarks and wish to proceed, press the button below
Fungibles
- issue_new_rgb20_asset
- Issue new RGB20 asset
+ issue_new_nia_asset
+ Issue new NIA asset
backup_message
diff --git a/src/utils/build_helpers.py b/src/utils/build_helpers.py
index 5173023a..f5025f5f 100644
--- a/src/utils/build_helpers.py
+++ b/src/utils/build_helpers.py
@@ -20,9 +20,9 @@ def add_network_argument(parser: ArgumentParser) -> None:
"""Add the common --network argument to an ArgumentParser."""
parser.add_argument(
'--network',
- choices=['mainnet', 'testnet', 'regtest'],
+ choices=['mainnet', 'testnet', 'regtest', 'testnet4'],
required=True,
- help="Specify the network to build for: 'mainnet', 'testnet', or 'regtest'.",
+ help="Specify the network to build for: 'mainnet', 'testnet', 'regtest', or 'testnet4'.",
)
diff --git a/src/utils/common_utils.py b/src/utils/common_utils.py
index 759fe5b6..575519b4 100644
--- a/src/utils/common_utils.py
+++ b/src/utils/common_utils.py
@@ -152,7 +152,7 @@ def set_qr_code(data):
def generate_identicon(data, size=40):
- """This method generates the identicon for rgb20 asset"""
+ """This method generates the identicon for nia asset"""
generator = pydenticon.Generator(
5, 5,
foreground=[
@@ -586,6 +586,7 @@ def get_bitcoin_info_by_network():
NetworkEnumModel.MAINNET.value: ':/assets/bitcoin.png',
NetworkEnumModel.REGTEST.value: ':/assets/regtest_bitcoin.png',
NetworkEnumModel.TESTNET.value: ':/assets/testnet_bitcoin.png',
+ NetworkEnumModel.TESTNET4.value: ':/assets/testnet_bitcoin.png',
}
img_path = bitcoin_img_path.get(network.value)
@@ -595,6 +596,8 @@ def get_bitcoin_info_by_network():
return (ticker, f'{bitcoin_asset}', img_path)
if ticker == TokenSymbol.TESTNET_BITCOIN.value:
return (ticker, f'{NetworkEnumModel.TESTNET.value} {bitcoin_asset}', img_path)
+ if ticker == TokenSymbol.TESTNET4_BITCOIN.value:
+ return (ticker, f'{NetworkEnumModel.TESTNET4.value} {bitcoin_asset}', img_path)
if ticker == TokenSymbol.REGTEST_BITCOIN.value:
return (ticker, f'{NetworkEnumModel.REGTEST.value} {bitcoin_asset}', img_path)
diff --git a/src/utils/constant.py b/src/utils/constant.py
index e767f867..eb2600bb 100644
--- a/src/utils/constant.py
+++ b/src/utils/constant.py
@@ -30,6 +30,7 @@
CACHE_FILE_NAME = {
NetworkEnumModel.MAINNET: 'iris-wallet-cache-mainnet',
NetworkEnumModel.TESTNET: 'iris-wallet-cache-testnet',
+ NetworkEnumModel.TESTNET4: 'iris-wallet-cache-testnet4',
NetworkEnumModel.REGTEST: 'iris-wallet-cache-regtest',
}
DEFAULT_CACHE_FILENAME = 'iris-wallet-cache-default'
@@ -80,6 +81,22 @@
PROXY_ENDPOINT_TESTNET = 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
LDK_DATA_NAME_TESTNET = 'dataldktestnet'
+BITCOIND_RPC_USER_TESTNET4 = 'user'
+BITCOIND_RPC_PASSWORD_TESTNET4 = 'password'
+BITCOIND_RPC_HOST_TESTNET4 = 'electrum.iriswallet.com'
+BITCOIND_RPC_PORT_TESTNET4 = 48332
+INDEXER_URL_TESTNET4 = 'ssl://electrum.iriswallet.com:50053'
+PROXY_ENDPOINT_TESTNET4 = 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
+LDK_DATA_NAME_TESTNET4 = 'dataldktestnet4'
+
+BITCOIND_RPC_USER_SIGNET = 'user'
+BITCOIND_RPC_PASSWORD_SIGNET = 'password'
+BITCOIND_RPC_HOST_SIGNET = 'electrum.iriswallet.com'
+BITCOIND_RPC_PORT_SIGNET = 18443
+INDEXER_URL_SIGNET = 'ssl://electrum.iriswallet.com:50033'
+PROXY_ENDPOINT_SIGNET = 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
+LDK_DATA_NAME_SIGNET = 'dataldksignet'
+
BITCOIND_RPC_USER_MAINNET = 'user'
BITCOIND_RPC_PASSWORD_MAINNET = 'password'
BITCOIND_RPC_HOST_MAINNET = 'localhost'
@@ -105,6 +122,10 @@
'https://rgb-faucet.iriswallet.com/testnet-planb2023',
'https://rgb-faucet.iriswallet.com/testnet-random2023',
]
+rgbTestnet4FaucetURLs: list[str] = [
+ 'https://rgb-faucet.iriswallet.com/testnet4-planb2023',
+ 'https://rgb-faucet.iriswallet.com/testnet4-random2023',
+]
rgbMainnetFaucetURLs: list[str] = [
'https://rgb-faucet.iriswallet.com/mainnet-random2023',
]
@@ -128,9 +149,9 @@
# RGB lightning node commit ID
RGB_LN_COMMIT_ID_KEY = 'rgb_ln_commit_id'
-CURRENT_RLN_NODE_COMMIT = 'a623edbd7c49639dc41c72c5aef98d808d6c1d00'
+CURRENT_RLN_NODE_COMMIT = '358d2d266aca22cf8e3ae142370ce5befd58ce7c'
COMPATIBLE_RLN_NODE_COMMITS = [
- 'a623edbd7c49639dc41c72c5aef98d808d6c1d00',
+ '358d2d266aca22cf8e3ae142370ce5befd58ce7c',
]
# Directory names used in paths
diff --git a/src/utils/error_message.py b/src/utils/error_message.py
index c2793c29..27ab398e 100644
--- a/src/utils/error_message.py
+++ b/src/utils/error_message.py
@@ -71,3 +71,7 @@
ERROR_UNABLE_TO_SET_MIN_CONFIRMATION = 'Unable to set min confirmation'
ERROR_NODE_INCOMPATIBILITY = 'The commit ID of the RGB Lightning Node is incompatible with the one used to initialize the wallet'
ERROR_WHILE_DOWNLOADING_LOGS = 'An error occurred while downloading logs.'
+ERROR_INSUFFICIENT_ASSET = 'You have insufficient assets'
+ERROR_INVALID_PEER_TYPE = 'The peer info is invalid'
+ERROR_INVALID_PUBKEY_TYPE = 'The peer pubkey is invalid'
+ERROR_NO_UNCOLORED_UTXOS_AVAILABLE = 'You do not have any available UTXOs'
diff --git a/src/utils/helpers.py b/src/utils/helpers.py
index 2d19425a..bcb9383d 100644
--- a/src/utils/helpers.py
+++ b/src/utils/helpers.py
@@ -33,26 +33,38 @@
from src.utils.constant import ANNOUNCE_ALIAS
from src.utils.constant import BITCOIND_RPC_HOST_MAINNET
from src.utils.constant import BITCOIND_RPC_HOST_REGTEST
+from src.utils.constant import BITCOIND_RPC_HOST_SIGNET
from src.utils.constant import BITCOIND_RPC_HOST_TESTNET
+from src.utils.constant import BITCOIND_RPC_HOST_TESTNET4
from src.utils.constant import BITCOIND_RPC_PASSWORD_MAINNET
from src.utils.constant import BITCOIND_RPC_PASSWORD_REGTEST
+from src.utils.constant import BITCOIND_RPC_PASSWORD_SIGNET
from src.utils.constant import BITCOIND_RPC_PASSWORD_TESTNET
+from src.utils.constant import BITCOIND_RPC_PASSWORD_TESTNET4
from src.utils.constant import BITCOIND_RPC_PORT_MAINNET
from src.utils.constant import BITCOIND_RPC_PORT_REGTEST
+from src.utils.constant import BITCOIND_RPC_PORT_SIGNET
from src.utils.constant import BITCOIND_RPC_PORT_TESTNET
+from src.utils.constant import BITCOIND_RPC_PORT_TESTNET4
from src.utils.constant import BITCOIND_RPC_USER_MAINNET
from src.utils.constant import BITCOIND_RPC_USER_REGTEST
+from src.utils.constant import BITCOIND_RPC_USER_SIGNET
from src.utils.constant import BITCOIND_RPC_USER_TESTNET
+from src.utils.constant import BITCOIND_RPC_USER_TESTNET4
from src.utils.constant import DAEMON_PORT
from src.utils.constant import INDEXER_URL_MAINNET
from src.utils.constant import INDEXER_URL_REGTEST
+from src.utils.constant import INDEXER_URL_SIGNET
from src.utils.constant import INDEXER_URL_TESTNET
+from src.utils.constant import INDEXER_URL_TESTNET4
from src.utils.constant import LDK_PORT
from src.utils.constant import LDK_PORT_KEY
from src.utils.constant import LIGHTNING_URL_KEY
from src.utils.constant import PROXY_ENDPOINT_MAINNET
from src.utils.constant import PROXY_ENDPOINT_REGTEST
+from src.utils.constant import PROXY_ENDPOINT_SIGNET
from src.utils.constant import PROXY_ENDPOINT_TESTNET
+from src.utils.constant import PROXY_ENDPOINT_TESTNET4
from src.utils.constant import SAVED_ANNOUNCE_ADDRESS
from src.utils.constant import SAVED_ANNOUNCE_ALIAS
from src.utils.constant import SAVED_BITCOIND_RPC_HOST
@@ -259,6 +271,7 @@ def get_node_arg_config(network: NetworkEnumModel) -> list:
'--daemon-listening-port', str(daemon_port),
'--ldk-peer-listening-port', str(ldk_port),
'--network', network.value,
+ '--disable-authentication',
]
except Exception as exc:
raise exc
@@ -345,6 +358,22 @@ def get_bitcoin_config(network: NetworkEnumModel, password) -> UnlockRequestMode
SAVED_INDEXER_URL: INDEXER_URL_REGTEST,
SAVED_PROXY_ENDPOINT: PROXY_ENDPOINT_REGTEST,
},
+ NetworkEnumModel.TESTNET4: {
+ SAVED_BITCOIND_RPC_USER: BITCOIND_RPC_USER_TESTNET4,
+ SAVED_BITCOIND_RPC_PASSWORD: BITCOIND_RPC_PASSWORD_TESTNET4,
+ SAVED_BITCOIND_RPC_HOST: BITCOIND_RPC_HOST_TESTNET4,
+ SAVED_BITCOIND_RPC_PORT: BITCOIND_RPC_PORT_TESTNET4,
+ SAVED_INDEXER_URL: INDEXER_URL_TESTNET4,
+ SAVED_PROXY_ENDPOINT: PROXY_ENDPOINT_TESTNET4,
+ },
+ NetworkEnumModel.SIGNET: {
+ SAVED_BITCOIND_RPC_USER: BITCOIND_RPC_USER_SIGNET,
+ SAVED_BITCOIND_RPC_PASSWORD: BITCOIND_RPC_PASSWORD_SIGNET,
+ SAVED_BITCOIND_RPC_HOST: BITCOIND_RPC_HOST_SIGNET,
+ SAVED_BITCOIND_RPC_PORT: BITCOIND_RPC_PORT_SIGNET,
+ SAVED_INDEXER_URL: INDEXER_URL_SIGNET,
+ SAVED_PROXY_ENDPOINT: PROXY_ENDPOINT_SIGNET,
+ },
}
# Retrieve the appropriate configuration based on the network
network_config = config_mapping.get(network) or {}
@@ -448,3 +477,51 @@ def check_node(context: str) -> bool:
}): Error while checking if node is locked',
)
return False
+
+
+def write_ln_node_commit_id_file(file_name: str) -> tuple[str, str]:
+ """
+ Write the ln_node commit to a .commit file in the same directory as the backup file.
+
+ Args:
+ file_name (str): The name to use for the commit file (e.g., 'wallet.commit').
+
+ Returns:
+ str: The full path to the created version file.
+ """
+ version = SettingRepository.get_rln_node_commit_id()
+ version_file_name = f'{file_name}.commit'
+ version_file_path = os.path.join(
+ app_paths.backup_folder_path, version_file_name,
+ )
+
+ try:
+ with open(version_file_path, 'w', encoding='utf-8') as f:
+ f.write(version)
+ return version_file_path, version_file_name
+ except OSError as e:
+ raise RuntimeError(f'Failed to write version file: {e}') from e
+
+
+def read_ln_node_commit_id_file(file_name: str) -> str:
+ """
+ Read the ln_node commit from a .commit file in the same directory as the backup file.
+
+ Args:
+ file_name (str): The name of the commit file (e.g., 'wallet.commit').
+ backup_path (str): The full path to the backup file (used to derive the directory).
+
+ Returns:
+ str: The ln_node commit if available, otherwise "unknown".
+ """
+ commit_id_file_path = os.path.join(
+ app_paths.restore_folder_path, file_name,
+ )
+
+ try:
+ with open(commit_id_file_path, encoding='utf-8') as f:
+ return f.read().strip()
+ except FileNotFoundError:
+ return 'unknown'
+ except OSError as e:
+ raise RuntimeError(f'Failed to read version file: {e}') from e
diff --git a/src/utils/page_navigation.py b/src/utils/page_navigation.py
index 9a0e0845..c68ca6ca 100644
--- a/src/utils/page_navigation.py
+++ b/src/utils/page_navigation.py
@@ -24,8 +24,8 @@
from src.views.ui_faucets import FaucetsWidget
from src.views.ui_fungible_asset import FungibleAssetWidget
from src.views.ui_help import HelpWidget
-from src.views.ui_issue_rgb20 import IssueRGB20Widget
-from src.views.ui_issue_rgb25 import IssueRGB25Widget
+from src.views.ui_issue_cfa import IssueCFAWidget
+from src.views.ui_issue_nia import IssueNIAWidget
from src.views.ui_ln_endpoint import LnEndpointWidget
from src.views.ui_network_selection_page import NetworkSelectionWidget
from src.views.ui_receive_bitcoin import ReceiveBitcoinWidget
@@ -62,19 +62,19 @@ def __init__(self, _ui):
'FungibleAssetWidget': FungibleAssetWidget,
'CollectiblesAssetWidget': CollectiblesAssetWidget,
'SetWalletPassword': SetWalletPasswordWidget,
- 'IssueRGB20': IssueRGB20Widget,
+ 'IssueNIA': IssueNIAWidget,
'Bitcoin': BtcWidget,
- 'IssueRGB25': IssueRGB25Widget,
- 'SendRGB25': SendRGBAssetWidget,
- 'ReceiveRGB25': ReceiveRGBAssetWidget,
- 'RGB25Detail': RGBAssetDetailWidget,
+ 'IssueCFA': IssueCFAWidget,
+ 'SendCFA': SendRGBAssetWidget,
+ 'ReceiveCFA': ReceiveRGBAssetWidget,
+ 'CFADetail': RGBAssetDetailWidget,
'SendBitcoin': SendBitcoinWidget,
'ReceiveBitcoin': ReceiveBitcoinWidget,
'ChannelManagement': ChannelManagement,
'CreateChannel': CreateChannelWidget,
'ViewUnspentList': ViewUnspentList,
'EnterWalletPassword': EnterWalletPassword,
- 'RGB25TransactionDetail': RGBAssetTransactionDetail,
+ 'CFATransactionDetail': RGBAssetTransactionDetail,
'BitcoinTransactionDetail': BitcoinTransactionDetail,
'Backup': Backup,
'Swap': SwapWidget,
@@ -128,23 +128,23 @@ def __init__(self, _ui):
self.event_based_navigation.enter_wallet_password_page_signal.connect(
self.enter_wallet_password_page,
)
- self.event_based_navigation.issue_rgb20_asset_page_signal.connect(
- self.issue_rgb20_asset_page,
+ self.event_based_navigation.issue_nia_asset_page_signal.connect(
+ self.issue_nia_asset_page,
)
self.event_based_navigation.bitcoin_page_signal.connect(
self.bitcoin_page,
)
- self.event_based_navigation.issue_rgb25_asset_page_signal.connect(
- self.issue_rgb25_asset_page,
+ self.event_based_navigation.issue_cfa_asset_page_signal.connect(
+ self.issue_cfa_asset_page,
)
- self.event_based_navigation.send_rgb25_page_signal.connect(
- self.send_rgb25_page,
+ self.event_based_navigation.send_cfa_page_signal.connect(
+ self.send_cfa_page,
)
- self.event_based_navigation.receive_rgb25_page_signal.connect(
- self.receive_rgb25_page,
+ self.event_based_navigation.receive_cfa_page_signal.connect(
+ self.receive_cfa_page,
)
- self.event_based_navigation.rgb25_detail_page_signal.connect(
- self.rgb25_detail_page,
+ self.event_based_navigation.cfa_detail_page_signal.connect(
+ self.cfa_detail_page,
)
self.event_based_navigation.send_bitcoin_page_signal.connect(
self.send_bitcoin_page,
@@ -161,8 +161,8 @@ def __init__(self, _ui):
self.event_based_navigation.view_unspent_list_page_signal.connect(
self.view_unspent_list_page,
)
- self.event_based_navigation.rgb25_transaction_detail_page_signal.connect(
- self.rgb25_transaction_detail_page,
+ self.event_based_navigation.cfa_transaction_detail_page_signal.connect(
+ self.cfa_transaction_detail_page,
)
self.event_based_navigation.bitcoin_transaction_detail_page_signal.connect(
self.bitcoin_transaction_detail_page,
@@ -291,35 +291,35 @@ def enter_wallet_password_page(self):
"""This method display the set wallet password page."""
self.navigate_to_page('EnterWalletPassword')
- def issue_rgb20_asset_page(self):
- """This method display the issue rgb20 asset page."""
- self.navigate_to_page('IssueRGB20')
+ def issue_nia_asset_page(self):
+ """This method display the issue nia asset page."""
+ self.navigate_to_page('IssueNIA')
def bitcoin_page(self):
"""This method display the bitcoin page."""
self.navigate_to_page('Bitcoin')
- def issue_rgb25_asset_page(self):
- """This method display the issue rgb25 page."""
- self.navigate_to_page('IssueRGB25')
+ def issue_cfa_asset_page(self):
+ """This method display the issue cfa page."""
+ self.navigate_to_page('IssueCFA')
- def send_rgb25_page(self):
- """This method display the send rgb25 page."""
- self.navigate_to_page('SendRGB25')
+ def send_cfa_page(self):
+ """This method display the send cfa page."""
+ self.navigate_to_page('SendCFA')
- def receive_rgb25_page(self, params):
- """This method display the receive rgb25 asset page."""
+ def receive_cfa_page(self, params):
+ """This method display the receive cfa asset page."""
self.current_stack = {
- 'name': 'ReceiveRGB25',
- 'widget': self.pages['ReceiveRGB25'](self._ui.view_model, params),
+ 'name': 'ReceiveCFA',
+ 'widget': self.pages['ReceiveCFA'](self._ui.view_model, params),
}
self.navigate_and_toggle(False)
- def rgb25_detail_page(self, params: RgbAssetPageLoadModel):
- """This method display the rgb25 detail page."""
+ def cfa_detail_page(self, params: RgbAssetPageLoadModel):
+ """This method display the cfa detail page."""
self.current_stack = {
- 'name': 'RGB25Detail',
- 'widget': self.pages['RGB25Detail'](self._ui.view_model, params),
+ 'name': 'CFADetail',
+ 'widget': self.pages['CFADetail'](self._ui.view_model, params),
}
self.navigate_and_toggle(False)
@@ -343,11 +343,11 @@ def view_unspent_list_page(self):
"""This method display the view unspent list page."""
self.navigate_to_page('ViewUnspentList', show_sidebar=True)
- def rgb25_transaction_detail_page(self, params: TransactionDetailPageModel):
- """This method display the rgb25 transaction detail page."""
+ def cfa_transaction_detail_page(self, params: TransactionDetailPageModel):
+ """This method display the cfa transaction detail page."""
self.current_stack = {
- 'name': 'RGB25TransactionDetail',
- 'widget': self.pages['RGB25TransactionDetail'](self._ui.view_model, params),
+ 'name': 'CFATransactionDetail',
+ 'widget': self.pages['CFATransactionDetail'](self._ui.view_model, params),
}
self.navigate_and_toggle(False)
diff --git a/src/utils/page_navigation_events.py b/src/utils/page_navigation_events.py
index e63c292d..ce5d5616 100644
--- a/src/utils/page_navigation_events.py
+++ b/src/utils/page_navigation_events.py
@@ -24,18 +24,18 @@ class PageNavigationEventManager(QObject):
collectibles_asset_page_signal = Signal()
set_wallet_password_page_signal = Signal(object)
enter_wallet_password_page_signal = Signal()
- issue_rgb20_asset_page_signal = Signal()
+ issue_nia_asset_page_signal = Signal()
bitcoin_page_signal = Signal()
- issue_rgb25_asset_page_signal = Signal()
- send_rgb25_page_signal = Signal()
- receive_rgb25_page_signal = Signal(object)
- rgb25_detail_page_signal = Signal(str)
+ issue_cfa_asset_page_signal = Signal()
+ send_cfa_page_signal = Signal()
+ receive_cfa_page_signal = Signal(object)
+ cfa_detail_page_signal = Signal(str)
send_bitcoin_page_signal = Signal()
receive_bitcoin_page_signal = Signal()
channel_management_page_signal = Signal()
create_channel_page_signal = Signal()
view_unspent_list_page_signal = Signal()
- rgb25_transaction_detail_page_signal = Signal(object)
+ cfa_transaction_detail_page_signal = Signal(object)
bitcoin_transaction_detail_page_signal = Signal(object)
backup_page_signal = Signal()
swap_page_signal = Signal()
diff --git a/src/utils/rgb_asset_helpers.py b/src/utils/rgb_asset_helpers.py
new file mode 100644
index 00000000..2843c9c3
--- /dev/null
+++ b/src/utils/rgb_asset_helpers.py
@@ -0,0 +1,111 @@
+"""Helper utilities for RGB asset detail operations.
+
+This module contains validation and image handling functions extracted from
+the RGBAssetDetailWidget to improve code organization and testability.
+"""
+from __future__ import annotations
+
+import re
+
+from PySide6.QtCore import QSize
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QLabel
+
+from src.utils.common_utils import convert_hex_to_image
+from src.utils.common_utils import resize_image
+
+
+def is_path(file_path: str) -> bool:
+ """Check if the string is a valid Unix-like file path.
+
+ Args:
+ file_path: String to validate as a file path.
+
+ Returns:
+ True if file_path matches Unix file path pattern, False otherwise.
+ """
+ if not isinstance(file_path, str):
+ return False
+ # Define a basic regex pattern for Unix-like file paths
+ pattern = r'^(\/[a-zA-Z0-9_.-]+)+\/?$'
+ # Check if the file_path matches the pattern
+ return bool(re.match(pattern, file_path))
+
+
+def is_hex_string(bytes_hex: str) -> bool:
+ """Check if the string is a valid hex string.
+
+ Args:
+ bytes_hex: String to validate as hexadecimal.
+
+ Returns:
+ True if bytes_hex is a valid hex string with even length, False otherwise.
+ """
+ if len(bytes_hex) % 2 != 0:
+ return False
+ hex_pattern = re.compile(r'^[0-9a-fA-F]+$')
+ return bool(hex_pattern.match(bytes_hex))
+
+
+def set_asset_image(label_widget: QLabel, image_hex: str) -> None:
+ """Set the asset image on a QLabel from hex string or file path.
+
+ Args:
+ label_widget: QLabel widget to set the image on.
+ image_hex: Hex string or file path of the image.
+ """
+ if is_hex_string(image_hex):
+ pixmap = convert_hex_to_image(image_hex)
+ resized_image = resize_image(pixmap, 335, 335)
+ label_widget.setPixmap(resized_image)
+ else:
+ resized_image = resize_image(image_hex, 335, 335)
+ label_widget.setPixmap(resized_image)
+
+
+def handle_img_path(
+ widget, image_path: str, asset_image_layout,
+ asset_id_frame, label_asset_name: QLabel | None = None,
+) -> QLabel:
+ """Configure the asset detail widget based on the provided image path.
+
+ Adjusts the layout and styles, and sets the asset image.
+
+ Args:
+ widget: The parent widget to configure.
+ image_path: Path or hex string of the asset image.
+ asset_image_layout: Layout to add the image label to.
+ asset_id_frame: Frame containing the asset ID.
+ label_asset_name: Optional existing label, or None to create new one.
+
+ Returns:
+ The QLabel widget containing the asset image.
+ """
+ if not image_path:
+ return label_asset_name
+
+ widget.setMinimumSize(QSize(466, 848))
+ widget.setFixedWidth(499)
+
+ if label_asset_name is None:
+ label_asset_name = QLabel(widget)
+ label_asset_name.setObjectName('label_asset_name')
+
+ label_asset_name.setMaximumSize(QSize(335, 335))
+ asset_id_frame.setMinimumSize(QSize(335, 86))
+ asset_id_frame.setMaximumSize(QSize(335, 86))
+ label_asset_name.setStyleSheet(
+ "font: 14px \"Inter\";\n"
+ 'color: #B3B6C3;\n'
+ 'background: transparent;\n'
+ 'border: none;\n'
+ 'border-radius: 8px;\n'
+ 'font-weight: 400;\n'
+ '',
+ )
+ asset_image_layout.addWidget(
+ label_asset_name, 0, Qt.AlignHCenter,
+ )
+ set_asset_image(label_asset_name, image_hex=image_path)
+
+ return label_asset_name
diff --git a/src/version.py b/src/version.py
index 233addc2..caa5939e 100644
--- a/src/version.py
+++ b/src/version.py
@@ -6,4 +6,4 @@
"""
from __future__ import annotations
-__version__ = '0.2.0'
+__version__ = '0.3.0'
diff --git a/src/viewmodels/__init__.py b/src/viewmodels/__init__.py
index 17ad7226..db07d542 100644
--- a/src/viewmodels/__init__.py
+++ b/src/viewmodels/__init__.py
@@ -9,7 +9,7 @@
Submodules:
-----------
-- IssueRGB20ViewModel: Connects RGB20 models to IssueRGB20Widget
+- IssueNIAViewModel: Connects NIA models to IssueNIAWidget
to enable communication between them.
- MainAssetViewModel: Connects main assets models to MainAssetWidget
to enable communication between them.
@@ -24,8 +24,8 @@
------
Examples of how to use the utilities in this package:
- >>> from viewmodels import IssueRGB20ViewModel
- >>> model = IssueRGB20ViewModel()
+ >>> from viewmodels import IssueNIAViewModel
+ >>> model = IssueNIAViewModel()
>>> print(model)
"""
from __future__ import annotations
diff --git a/src/viewmodels/rgb_25_view_model.py b/src/viewmodels/cfa_view_model.py
similarity index 85%
rename from src/viewmodels/rgb_25_view_model.py
rename to src/viewmodels/cfa_view_model.py
index 512620b7..4f560bfb 100644
--- a/src/viewmodels/rgb_25_view_model.py
+++ b/src/viewmodels/cfa_view_model.py
@@ -1,6 +1,6 @@
# pylint: disable=too-many-instance-attributes
# mypy: ignore-errors
-"""This module contains the RGB25DetailViewModel class, which represents the view model
+"""This module contains the CFADetailViewModel class, which represents the view model
for the Bitcoin page activities.
"""
from __future__ import annotations
@@ -14,8 +14,10 @@
from src.data.repository.setting_repository import SettingRepository
from src.data.service.asset_detail_page_services import AssetDetailPageService
from src.model.enums.enums_model import AssetType
+from src.model.enums.enums_model import AssignmentEnumModel
from src.model.enums.enums_model import NativeAuthType
from src.model.enums.enums_model import ToastPreset
+from src.model.rgb_model import AssignmentModel
from src.model.rgb_model import FailTransferRequestModel
from src.model.rgb_model import FailTransferResponseModel
from src.model.rgb_model import ListOnAndOffChainTransfersWithBalance
@@ -34,12 +36,12 @@
from src.views.components.toast import ToastManager
-class RGB25ViewModel(QObject, ThreadManager):
+class CFAViewModel(QObject, ThreadManager):
"""This class represents the activities of the bitcoin page."""
asset_info = Signal(str, str, str, str)
txn_list_loaded = Signal(str, str, str, str)
- send_rgb25_button_clicked = Signal(bool)
+ send_cfa_button_clicked = Signal(bool)
message = Signal(ToastPreset, str)
is_loading = Signal(bool)
refresh = Signal(bool)
@@ -48,7 +50,7 @@ class RGB25ViewModel(QObject, ThreadManager):
def __init__(self, page_navigation: Any) -> None:
super().__init__()
self._page_navigation = page_navigation
- self.asset_info.connect(self.get_rgb25_asset_detail)
+ self.asset_info.connect(self.get_cfa_asset_detail)
# Initializing default values for attributes
self.asset_id = None
@@ -62,11 +64,11 @@ def __init__(self, page_navigation: Any) -> None:
self.min_confirmation = None
self.txn_list = []
- def get_rgb25_asset_detail(self, asset_id: str, asset_name: str, image_path: str, asset_type: str) -> None:
- """Retrieve RGB25 asset list."""
+ def get_cfa_asset_detail(self, asset_id: str, asset_name: str, image_path: str, asset_type: str) -> None:
+ """Retrieve CFA asset list."""
def on_success(response: ListOnAndOffChainTransfersWithBalance) -> None:
- """Handle success for the RGB25 asset detail list."""
+ """Handle success for the CFA asset detail list."""
self.txn_list = response
self.txn_list_loaded.emit(
asset_id, asset_name, image_path, asset_type,
@@ -94,27 +96,27 @@ def on_error(error: CommonException) -> None:
except Exception as e:
on_error(CommonException(message=str(e)))
- def on_success_rgb25(self, tx_id: SendAssetResponseModel) -> None:
- """Handle success for sending RGB25 asset."""
+ def on_success_cfa(self, tx_id: SendAssetResponseModel) -> None:
+ """Handle success for sending CFA asset."""
self.is_loading.emit(False)
- self.send_rgb25_button_clicked.emit(False)
+ self.send_cfa_button_clicked.emit(False)
ToastManager.success(description=INFO_ASSET_SENT.format(tx_id.txid))
- if self.asset_type == AssetType.RGB25.value:
+ if self.asset_type == AssetType.CFA.value:
self._page_navigation.collectibles_asset_page()
- elif self.asset_type == AssetType.RGB20.value:
+ elif self.asset_type == AssetType.NIA.value:
self._page_navigation.fungibles_asset_page()
def on_error(self, error: CommonException) -> None:
- """Handle error for sending RGB25 asset."""
+ """Handle error for sending CFA asset."""
self.is_loading.emit(False)
- self.send_rgb25_button_clicked.emit(False)
+ self.send_cfa_button_clicked.emit(False)
ToastManager.error(description=error.message)
def on_success_send_rgb_asset(self, success: bool) -> None:
"""Callback function after native authentication is successful."""
if success:
- self.send_rgb25_button_clicked.emit(True)
+ self.send_cfa_button_clicked.emit(True)
self.is_loading.emit(True)
try:
self.run_in_thread(
@@ -123,14 +125,16 @@ def on_success_send_rgb_asset(self, success: bool) -> None:
'args': [
SendAssetRequestModel(
asset_id=self.asset_id,
- amount=self.amount,
+ assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=self.amount,
+ ),
recipient_id=self.blinded_utxo,
transport_endpoints=self.transport_endpoints,
fee_rate=self.fee_rate,
min_confirmations=self.min_confirmation,
),
],
- 'callback': self.on_success_rgb25,
+ 'callback': self.on_success_cfa,
'error_callback': self.on_error,
},
)
@@ -147,7 +151,7 @@ def on_error_native_auth(self, error: Exception) -> None:
ToastManager.error(description=description)
def on_send_click(self, amount: int, blinded_utxo: str, transport_endpoints: list, fee_rate: int, min_confirmation: int) -> None:
- """Starts a thread to execute the send_rgb25 function with the provided arguments."""
+ """Starts a thread to execute the send_cfa function with the provided arguments."""
self.amount = amount
self.blinded_utxo = blinded_utxo
self.transport_endpoints = transport_endpoints
@@ -167,7 +171,7 @@ def on_refresh_click(self) -> None:
cache = Cache.get_cache_session()
if cache is not None:
cache.invalidate_cache()
- self.send_rgb25_button_clicked.emit(True)
+ self.send_cfa_button_clicked.emit(True)
self.is_loading.emit(True)
def on_success_refresh() -> None:
@@ -175,7 +179,7 @@ def on_success_refresh() -> None:
self.is_loading.emit(False)
self.refresh.emit(True)
ToastManager.success(description=INFO_REFRESH_SUCCESSFULLY)
- self.get_rgb25_asset_detail(
+ self.get_cfa_asset_detail(
self.asset_id, self.asset_name, None, self.asset_type,
)
@@ -206,7 +210,7 @@ def on_fail_transfer(self, batch_transfer_idx: int) -> None:
def on_success_fail_transfer(response: FailTransferResponseModel) -> None:
"""Handle success for failing a transfer."""
if response.transfers_changed:
- self.get_rgb25_asset_detail(
+ self.get_cfa_asset_detail(
self.asset_id, self.asset_name, None, self.asset_type,
)
ToastManager.success(
diff --git a/src/viewmodels/channel_management_viewmodel.py b/src/viewmodels/channel_management_viewmodel.py
index eded3fb6..468bd16d 100644
--- a/src/viewmodels/channel_management_viewmodel.py
+++ b/src/viewmodels/channel_management_viewmodel.py
@@ -24,7 +24,10 @@
from src.utils.custom_exception import CommonException
from src.utils.error_message import ERROR_CREATE_UTXO
from src.utils.error_message import ERROR_INSUFFICIENT_ALLOCATION_SLOT
-from src.utils.error_message import ERROR_NOT_ENOUGH_UNCOLORED
+from src.utils.error_message import ERROR_INSUFFICIENT_ASSET
+from src.utils.error_message import ERROR_INVALID_PEER_TYPE
+from src.utils.error_message import ERROR_INVALID_PUBKEY_TYPE
+from src.utils.error_message import ERROR_NO_UNCOLORED_UTXOS_AVAILABLE
from src.utils.error_message import ERROR_SOMETHING_WENT_WRONG
from src.utils.info_message import INFO_CHANNEL_DELETED
from src.utils.worker import ThreadManager
@@ -124,15 +127,16 @@ def on_success(response: OpenChannelResponseModel):
self.channel_created.emit()
def on_error(error: CommonException):
- if error.message == ERROR_INSUFFICIENT_ALLOCATION_SLOT or ERROR_NOT_ENOUGH_UNCOLORED:
+ if error.message in (ERROR_INVALID_PUBKEY_TYPE, ERROR_INVALID_PEER_TYPE, ERROR_INSUFFICIENT_ASSET):
+ ToastManager.error(description=error.message)
+ self.is_loading.emit(False)
+ elif error.message in (ERROR_INSUFFICIENT_ALLOCATION_SLOT, ERROR_NO_UNCOLORED_UTXOS_AVAILABLE):
params = HandleInsufficientAllocationSlotsModel(
capacity_sat=capacity_sat, pub_key=pub_key, push_msat=push_msat, asset_id=asset_id, amount=amount,
)
self.handle_insufficient_allocation(params)
else:
- ToastManager.error(
- description=error.message,
- )
+ ToastManager.error(description=error.message)
self.is_loading.emit(False)
self.run_in_thread(
diff --git a/src/viewmodels/issue_rgb25_view_model.py b/src/viewmodels/issue_cfa_view_model.py
similarity index 86%
rename from src/viewmodels/issue_rgb25_view_model.py
rename to src/viewmodels/issue_cfa_view_model.py
index c11fe6f2..0873d8ed 100644
--- a/src/viewmodels/issue_rgb25_view_model.py
+++ b/src/viewmodels/issue_cfa_view_model.py
@@ -1,6 +1,6 @@
"""
-This module contains the IssueRGB25ViewModel class, which represents the view model
-for the Issue RGB25 Asset page activities.
+This module contains the IssueCFAViewModel class, which represents the view model
+for the Issue CFA Asset page activities.
"""
from __future__ import annotations
@@ -26,12 +26,12 @@
from src.views.components.toast import ToastManager
-class IssueRGB25ViewModel(QObject, ThreadManager):
- """This class represents the activities of the Issue RGB25 Asset page."""
+class IssueCFAViewModel(QObject, ThreadManager):
+ """This class represents the activities of the Issue CFA Asset page."""
is_loading = Signal(bool)
file_upload_message = Signal(str)
success_page_message = Signal(str)
- rgb25_success_message = Signal(str)
+ cfa_success_message = Signal(str)
def __init__(self, page_navigation) -> None:
"""
@@ -47,7 +47,7 @@ def __init__(self, page_navigation) -> None:
self.amount = None
self.asset_name = None
- def on_success_native_auth_rgb25(self, success: bool):
+ def on_success_native_auth_cfa(self, success: bool):
"""Callback function after native authentication successful"""
try:
if self.amount is None or self.asset_name is None or self.asset_ticker is None:
@@ -87,7 +87,7 @@ def on_success_native_auth_rgb25(self, success: bool):
description=ERROR_SOMETHING_WENT_WRONG,
)
- def on_error_native_auth_rgb25(self, error: Exception):
+ def on_error_native_auth_cfa(self, error: Exception):
"""Callback function on error"""
self.is_loading.emit(False)
err_message = error.message if isinstance(
@@ -115,7 +115,7 @@ def open_file_dialog(self) -> None:
)
def on_success(self, response: IssueAssetResponseModel):
- """on success callback of issue rgb25 """
+ """on success callback of issue cfa """
ToastManager.success(
description=INFO_ASSET_ISSUED.format(response.asset_id),
)
@@ -123,18 +123,18 @@ def on_success(self, response: IssueAssetResponseModel):
self.is_loading.emit(False)
def on_error(self, error: CommonException):
- """on error callback of issue rgb25 """
+ """on error callback of issue cfa """
ToastManager.error(
description=error.message,
)
self.is_loading.emit(False)
- def issue_rgb25_asset(
+ def issue_cfa_asset(
self, asset_ticker,
asset_name,
amount,
):
- """Issue an RGB25 asset with the provided details."""
+ """Issue an CFA asset with the provided details."""
self.is_loading.emit(True)
self.asset_name = asset_name
self.asset_ticker = asset_ticker
@@ -143,7 +143,7 @@ def issue_rgb25_asset(
SettingRepository.native_authentication,
{
'args': [NativeAuthType.MAJOR_OPERATION],
- 'callback': self.on_success_native_auth_rgb25,
- 'error_callback': self.on_error_native_auth_rgb25,
+ 'callback': self.on_success_native_auth_cfa,
+ 'error_callback': self.on_error_native_auth_cfa,
},
)
diff --git a/src/viewmodels/issue_rgb20_view_model.py b/src/viewmodels/issue_nia_view_model.py
similarity index 81%
rename from src/viewmodels/issue_rgb20_view_model.py
rename to src/viewmodels/issue_nia_view_model.py
index e8496921..d91035da 100644
--- a/src/viewmodels/issue_rgb20_view_model.py
+++ b/src/viewmodels/issue_nia_view_model.py
@@ -1,4 +1,4 @@
-"""This module contains the IssueRGB20ViewModel class, which represents the view model
+"""This module contains the IssueNIAViewModel class, which represents the view model
for the issue RG20 page activities.
"""
from __future__ import annotations
@@ -19,8 +19,8 @@
from src.views.components.toast import ToastManager
-class IssueRGB20ViewModel(QObject, ThreadManager):
- """This class represents the activities of the issue RGB20 page."""
+class IssueNIAViewModel(QObject, ThreadManager):
+ """This class represents the activities of the issue NIA page."""
issue_button_clicked = Signal(bool)
close_button_clicked = Signal(bool)
@@ -33,7 +33,7 @@ def __init__(self, page_navigation: Any) -> None:
super().__init__()
self._page_navigation = page_navigation
- def on_success_native_auth_rgb20(self, success: bool):
+ def on_success_native_auth_nia(self, success: bool):
"""Callback function after native authentication successful"""
try:
if not success:
@@ -64,7 +64,7 @@ def on_success_native_auth_rgb20(self, success: bool):
description=ERROR_SOMETHING_WENT_WRONG,
)
- def on_error_native_auth_rgb20(self, error: Exception):
+ def on_error_native_auth_nia(self, error: Exception):
"""Callback function on error"""
self.issue_button_clicked.emit(False)
description = error.message if isinstance(
@@ -75,7 +75,7 @@ def on_error_native_auth_rgb20(self, error: Exception):
def on_issue_click(self, short_identifier: str, asset_name: str, amount: str):
""""
Executes the set_wallet_password method in a separate thread.
- This method starts a thread to execute the issue_rgb20 function with the provided arguments.
+ This method starts a thread to execute the issue_nia function with the provided arguments.
It emits a signal to indicate loading state and defines a callback for when the operation is successful.
"""
self.issue_button_clicked.emit(True)
@@ -86,23 +86,23 @@ def on_issue_click(self, short_identifier: str, asset_name: str, amount: str):
SettingRepository.native_authentication,
{
'args': [NativeAuthType.MAJOR_OPERATION],
- 'callback': self.on_success_native_auth_rgb20,
- 'error_callback': self.on_error_native_auth_rgb20,
+ 'callback': self.on_success_native_auth_nia,
+ 'error_callback': self.on_error_native_auth_nia,
},
)
def on_success(self, response: IssueAssetResponseModel) -> None:
- """This method is used handle onsuccess for the RGB20 issue page."""
+ """This method is used handle onsuccess for the NIA issue page."""
self.issue_button_clicked.emit(False)
self.is_issued.emit(response.name)
def on_error(self, error) -> None:
- """This method is used handle onerror for the RGB20 issue page."""
+ """This method is used handle onerror for the NIA issue page."""
self.issue_button_clicked.emit(False)
ToastManager.error(
description=error.message,
)
def on_close_click(self) -> None:
- """This method is used for close the RGB20 issue page."""
+ """This method is used for close the NIA issue page."""
self._page_navigation.fungibles_asset_page()
diff --git a/src/viewmodels/main_view_model.py b/src/viewmodels/main_view_model.py
index d0f710af..8022d78d 100644
--- a/src/viewmodels/main_view_model.py
+++ b/src/viewmodels/main_view_model.py
@@ -8,20 +8,20 @@
from src.viewmodels.backup_view_model import BackupViewModel
from src.viewmodels.bitcoin_view_model import BitcoinViewModel
+from src.viewmodels.cfa_view_model import CFAViewModel
from src.viewmodels.channel_management_viewmodel import ChannelManagementViewModel
from src.viewmodels.enter_password_view_model import EnterWalletPasswordViewModel
from src.viewmodels.faucets_view_model import FaucetsViewModel
from src.viewmodels.fee_rate_view_model import EstimateFeeViewModel
from src.viewmodels.header_frame_view_model import HeaderFrameViewModel
-from src.viewmodels.issue_rgb20_view_model import IssueRGB20ViewModel
-from src.viewmodels.issue_rgb25_view_model import IssueRGB25ViewModel
+from src.viewmodels.issue_cfa_view_model import IssueCFAViewModel
+from src.viewmodels.issue_nia_view_model import IssueNIAViewModel
from src.viewmodels.ln_endpoint_view_model import LnEndpointViewModel
from src.viewmodels.ln_offchain_view_model import LnOffChainViewModel
from src.viewmodels.main_asset_view_model import MainAssetViewModel
from src.viewmodels.receive_bitcoin_view_model import ReceiveBitcoinViewModel
-from src.viewmodels.receive_rgb25_view_model import ReceiveRGB25ViewModel
+from src.viewmodels.receive_cfa_view_model import ReceiveCFAViewModel
from src.viewmodels.restore_view_model import RestoreViewModel
-from src.viewmodels.rgb_25_view_model import RGB25ViewModel
from src.viewmodels.send_bitcoin_view_model import SendBitcoinViewModel
from src.viewmodels.set_wallet_password_view_model import SetWalletPasswordViewModel
from src.viewmodels.setting_view_model import SettingViewModel
@@ -45,7 +45,7 @@ def __init__(self, page_navigation):
self.main_asset_view_model = MainAssetViewModel(self.page_navigation)
- self.issue_rgb20_asset_view_model = IssueRGB20ViewModel(
+ self.issue_nia_asset_view_model = IssueNIAViewModel(
self.page_navigation,
)
self.set_wallet_password_view_model = SetWalletPasswordViewModel(
@@ -65,20 +65,20 @@ def __init__(self, page_navigation):
self.unspent_view_model = UnspentListViewModel(self.page_navigation)
- self.issue_rgb25_asset_view_model = IssueRGB25ViewModel(
+ self.issue_cfa_asset_view_model = IssueCFAViewModel(
self.page_navigation,
)
self.ln_endpoint_view_model = LnEndpointViewModel(
self.page_navigation,
)
- self.rgb25_view_model = RGB25ViewModel(
+ self.cfa_view_model = CFAViewModel(
self.page_navigation,
)
self.enter_wallet_password_view_model = EnterWalletPasswordViewModel(
self.page_navigation,
)
- self.receive_rgb25_view_model = ReceiveRGB25ViewModel(
+ self.receive_cfa_view_model = ReceiveCFAViewModel(
self.page_navigation,
)
diff --git a/src/viewmodels/receive_rgb25_view_model.py b/src/viewmodels/receive_cfa_view_model.py
similarity index 88%
rename from src/viewmodels/receive_rgb25_view_model.py
rename to src/viewmodels/receive_cfa_view_model.py
index 09469d26..98a8f91e 100644
--- a/src/viewmodels/receive_rgb25_view_model.py
+++ b/src/viewmodels/receive_cfa_view_model.py
@@ -1,6 +1,6 @@
"""
-This module contains the ReceiveRGB25ViewModel class, which represents the view model
-for the Receive RGB25 Asset page activities.
+This module contains the ReceiveCFAViewModel class, which represents the view model
+for the Receive CFA Asset page activities.
"""
from __future__ import annotations
@@ -16,8 +16,8 @@
from src.views.components.toast import ToastManager
-class ReceiveRGB25ViewModel(QObject, ThreadManager):
- """This class represents the activities of the Receive RGB25 Asset page."""
+class ReceiveCFAViewModel(QObject, ThreadManager):
+ """This class represents the activities of the Receive CFA Asset page."""
address = Signal(str)
message = Signal(ToastPreset, str)
show_loading = Signal(bool)
diff --git a/src/viewmodels/restore_view_model.py b/src/viewmodels/restore_view_model.py
index d95d70be..09c3bccd 100644
--- a/src/viewmodels/restore_view_model.py
+++ b/src/viewmodels/restore_view_model.py
@@ -19,9 +19,14 @@
from src.utils.error_message import ERROR_WHILE_RESTORE
from src.utils.gauth import authenticate
from src.utils.info_message import INFO_RESTORE_COMPLETED
+from src.utils.info_message import INFO_WALLET_RESET
from src.utils.keyring_storage import set_value
+from src.utils.local_store import local_store
+from src.utils.logging import logger
+from src.utils.reset_app import delete_app_data
from src.utils.worker import ThreadManager
from src.views.components.keyring_error_dialog import KeyringErrorDialog
+from src.views.components.node_incompatibility import NodeIncompatibilityDialog
class RestoreViewModel(QObject, ThreadManager):
@@ -86,7 +91,9 @@ def on_error(self, exc: Exception):
exc (Exception): The exception that was raised.
"""
self.is_loading.emit(False)
- if isinstance(exc, CommonException):
+ if isinstance(exc, CommonException) and getattr(exc, 'message', '') == 'RGB_LIB_INCOMPATIBLE':
+ self.handle_rgb_lib_incompatibility()
+ elif isinstance(exc, CommonException):
self.message.emit(
ToastPreset.ERROR,
exc.message,
@@ -124,3 +131,28 @@ def restore(self, mnemonic: str, password: str):
except Exception as exc:
self.is_loading.emit(False)
self.on_error(exc)
+
+ def handle_rgb_lib_incompatibility(self):
+ """Handles the case when the RGB lib version is incompatible."""
+ rgb_lib_incompatible_dialog = NodeIncompatibilityDialog()
+ rgb_lib_incompatible_dialog.show_node_incompatibility_dialog()
+ clicked_button = rgb_lib_incompatible_dialog.node_incompatibility_dialog.clickedButton()
+
+ if clicked_button == rgb_lib_incompatible_dialog.close_button:
+ QApplication.instance().exit()
+
+ elif clicked_button == rgb_lib_incompatible_dialog.delete_app_data_button:
+ rgb_lib_incompatible_dialog.show_confirmation_dialog()
+ confirm_button = rgb_lib_incompatible_dialog.confirmation_dialog.clickedButton()
+
+ if confirm_button == rgb_lib_incompatible_dialog.confirm_delete_button:
+ self.on_delete_app_data()
+ elif confirm_button == rgb_lib_incompatible_dialog.cancel:
+ self.handle_rgb_lib_incompatibility()
+
+ def on_delete_app_data(self):
+ """This function deletes the wallet data after user confirms when using an invalid rgb lib"""
+ basepath = local_store.get_path()
+ network_type = SettingRepository.get_wallet_network()
+ delete_app_data(basepath, network=network_type.value)
+ logger.info(INFO_WALLET_RESET)
diff --git a/src/views/__init__.py b/src/views/__init__.py
index 811269c6..d99c4d0e 100644
--- a/src/views/__init__.py
+++ b/src/views/__init__.py
@@ -14,7 +14,7 @@
- Sidebar: Contains UI elements and functions for the sidebar navigation.
- SetWalletPasswordWidget: Contains UI elements and functions for setting the wallet password.
- MainAssetWidget: Contains UI elements and functions for displaying and managing main assets.
-- IssueRGB20Widget: Contains UI elements and functions for issuing RGB20 tokens.
+- IssueNIAWidget: Contains UI elements and functions for issuing NIA tokens.
- MainWindow: Contains the main application window and manages the primary layout and interactions.
Usage:
diff --git a/src/views/components/settings_helpers.py b/src/views/components/settings_helpers.py
new file mode 100644
index 00000000..f2f7a06f
--- /dev/null
+++ b/src/views/components/settings_helpers.py
@@ -0,0 +1,173 @@
+"""Helper utilities for settings widget configuration.
+
+This module contains helper functions for settings frame operations,
+network endpoint configuration, and keyring state management.
+"""
+from __future__ import annotations
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QDialog
+
+from src.data.repository.setting_repository import SettingRepository
+from src.model.enums.enums_model import NetworkEnumModel
+from src.utils.constant import BITCOIND_RPC_HOST_MAINNET
+from src.utils.constant import BITCOIND_RPC_HOST_REGTEST
+from src.utils.constant import BITCOIND_RPC_HOST_TESTNET
+from src.utils.constant import BITCOIND_RPC_HOST_TESTNET4
+from src.utils.constant import BITCOIND_RPC_PORT_MAINNET
+from src.utils.constant import BITCOIND_RPC_PORT_REGTEST
+from src.utils.constant import BITCOIND_RPC_PORT_TESTNET
+from src.utils.constant import BITCOIND_RPC_PORT_TESTNET4
+from src.utils.constant import INDEXER_URL_MAINNET
+from src.utils.constant import INDEXER_URL_REGTEST
+from src.utils.constant import INDEXER_URL_TESTNET
+from src.utils.constant import INDEXER_URL_TESTNET4
+from src.utils.constant import IRIS_WALLET_TRANSLATIONS_CONTEXT
+from src.utils.constant import PROXY_ENDPOINT_MAINNET
+from src.utils.constant import PROXY_ENDPOINT_REGTEST
+from src.utils.constant import PROXY_ENDPOINT_TESTNET
+from src.utils.constant import PROXY_ENDPOINT_TESTNET4
+from src.utils.constant import WALLET_PASSWORD_KEY
+from src.utils.keyring_storage import get_value
+from src.views.ui_restore_mnemonic import RestoreMnemonicWidget
+
+
+def set_endpoint_based_on_network() -> tuple[str, str, str, int]:
+ """Set various endpoints based on the currently selected wallet network.
+
+ Returns:
+ Tuple containing (indexer_url, proxy_endpoint, bitcoind_host, bitcoind_port)
+
+ Raises:
+ ValueError: If the stored network type is unsupported.
+ """
+ network_config_map = {
+ NetworkEnumModel.MAINNET: (
+ INDEXER_URL_MAINNET, PROXY_ENDPOINT_MAINNET,
+ BITCOIND_RPC_HOST_MAINNET, BITCOIND_RPC_PORT_MAINNET,
+ ),
+ NetworkEnumModel.TESTNET: (
+ INDEXER_URL_TESTNET, PROXY_ENDPOINT_TESTNET,
+ BITCOIND_RPC_HOST_TESTNET, BITCOIND_RPC_PORT_TESTNET,
+ ),
+ NetworkEnumModel.TESTNET4: (
+ INDEXER_URL_TESTNET4, PROXY_ENDPOINT_TESTNET4,
+ BITCOIND_RPC_HOST_TESTNET4, BITCOIND_RPC_PORT_TESTNET4,
+ ),
+ NetworkEnumModel.REGTEST: (
+ INDEXER_URL_REGTEST, PROXY_ENDPOINT_REGTEST,
+ BITCOIND_RPC_HOST_REGTEST, BITCOIND_RPC_PORT_REGTEST,
+ ),
+ }
+ stored_network: NetworkEnumModel = SettingRepository.get_wallet_network()
+ config = network_config_map.get(stored_network)
+ if config:
+ return config
+ raise ValueError(f"Unsupported network type: {stored_network}")
+
+
+def set_frame_content(
+ frame, input_value, expiry_time_unit=None,
+ validator=None, time_unit_combobox=None, suggestion_desc=None,
+):
+ """Set the content for a given settings frame.
+
+ Args:
+ frame: The frame to configure.
+ input_value: The value to set in the input field.
+ expiry_time_unit: Current expiry time unit for comparison.
+ validator: Optional validator for the input field.
+ time_unit_combobox: Optional time unit combobox widget.
+ suggestion_desc: Optional suggestion description widget.
+ """
+ if isinstance(input_value, float) and input_value.is_integer():
+ input_value = int(input_value)
+
+ frame.input_value.setText(str(input_value))
+ frame.input_value.setPlaceholderText(str(input_value))
+ frame.input_value.setValidator(validator)
+
+ if not suggestion_desc:
+ frame.suggestion_desc.hide()
+
+ if time_unit_combobox:
+ index = time_unit_combobox.findText(
+ expiry_time_unit, Qt.MatchFixedString,
+ )
+ if index != -1:
+ time_unit_combobox.setCurrentIndex(index)
+ else:
+ frame.time_unit_combobox.hide()
+
+ frame.input_value.textChanged.connect(
+ lambda: update_save_button(frame, input_value, expiry_time_unit),
+ )
+
+ if time_unit_combobox:
+ frame.time_unit_combobox.currentTextChanged.connect(
+ lambda: update_save_button(
+ frame, input_value, expiry_time_unit, time_unit_combobox,
+ ),
+ )
+
+ # Initial call to set the correct button state
+ update_save_button(
+ frame, input_value,
+ expiry_time_unit, time_unit_combobox,
+ )
+
+
+def update_save_button(frame, input_value, expiry_time_unit: str, time_unit_combobox=None):
+ """Update the state of the save button based on input changes.
+
+ Args:
+ frame: The frame containing the save button.
+ input_value: The original value to compare against.
+ expiry_time_unit: Current expiry time unit for comparison.
+ time_unit_combobox: Optional time unit combobox widget.
+ """
+ current_text = frame.input_value.text().strip()
+ current_unit = frame.time_unit_combobox.currentText() if time_unit_combobox else ''
+
+ time_unit_changed = current_unit != expiry_time_unit
+
+ if current_text and (current_text != str(input_value) or (time_unit_combobox and time_unit_changed)):
+ frame.save_button.setDisabled(False)
+ else:
+ frame.save_button.setDisabled(True)
+
+
+def check_keyring_state_for_password(parent, view_model) -> str | None:
+ """Check keyring status and retrieve wallet password.
+
+ Retrieves password either from secure storage if keyring is disabled,
+ or via user prompt through a mnemonic dialog if enabled.
+
+ Args:
+ parent: Parent widget for dialog display.
+ view_model: The main view model.
+
+ Returns:
+ The wallet password string if retrieved, None otherwise.
+ """
+ keyring_status = SettingRepository.get_keyring_status()
+ if keyring_status is False:
+ network: NetworkEnumModel = SettingRepository.get_wallet_network()
+ password: str = get_value(WALLET_PASSWORD_KEY, network.value)
+ return password
+ if keyring_status is True:
+ mnemonic_dialog = RestoreMnemonicWidget(
+ parent=parent, view_model=view_model, origin_page='setting_card', mnemonic_visibility=False,
+ )
+ mnemonic_dialog.mnemonic_detail_text_label.setText(
+ QCoreApplication.translate(
+ IRIS_WALLET_TRANSLATIONS_CONTEXT, 'lock_unlock_password_required', None,
+ ),
+ )
+ mnemonic_dialog.mnemonic_detail_text_label.setFixedHeight(40)
+ result = mnemonic_dialog.exec()
+ if result == QDialog.Accepted:
+ password = mnemonic_dialog.password_input.text()
+ return password
+ return None
diff --git a/src/views/main_window.py b/src/views/main_window.py
index 1c0a7075..069d77ed 100644
--- a/src/views/main_window.py
+++ b/src/views/main_window.py
@@ -124,6 +124,8 @@ def set_app_icon(self):
app_icon = QIcon(':/assets/icons/regtest-icon.ico')
if network.value == NetworkEnumModel.TESTNET.value:
app_icon = QIcon(':/assets/icons/testnet-icon.ico')
+ if network.value == NetworkEnumModel.TESTNET4.value:
+ app_icon = QIcon(':/assets/icons/testnet-icon.ico')
if network.value == NetworkEnumModel.MAINNET.value:
app_icon = QIcon(':/assets/icons/mainnet-icon.ico')
self.main_window.setWindowIcon(app_icon)
diff --git a/src/views/qss/issue_rgb25_style.qss b/src/views/qss/issue_cfa_style.qss
similarity index 78%
rename from src/views/qss/issue_rgb25_style.qss
rename to src/views/qss/issue_cfa_style.qss
index c7db3596..1bb47ede 100644
--- a/src/views/qss/issue_rgb25_style.qss
+++ b/src/views/qss/issue_cfa_style.qss
@@ -1,5 +1,5 @@
-/* Styles for the issue RGB25 card */
-QWidget#issue_rgb_25_card {
+/* Styles for the issue CFA card */
+QWidget#issue_cfa_card {
background: transparent;
background-color: rgb(3, 11, 37);
border: 1px solid rgb(102, 108, 129);
@@ -10,7 +10,7 @@ QWidget#issue_rgb_25_card {
max-height: 608px;
}
-/* Styles for line edit elements within the issue RGB25 card */
+/* Styles for line edit elements within the issue CFA card */
QLineEdit#asset_description_input, QLineEdit#name_of_the_asset_input, QLineEdit#amount_input {
padding-left: 10px;
font: 15px "Inter";
@@ -21,14 +21,14 @@ QLineEdit#asset_description_input, QLineEdit#name_of_the_asset_input, QLineEdit#
border-radius: 4px;
}
-/* Styles for line elements within the issue RGB25 card */
+/* Styles for line elements within the issue CFA card */
QFrame#line_top, QFrame#line_bottom, QFrame#line_6 {
border: none;
border-bottom: 1px solid rgb(27, 35, 59);
}
-/* Styles for label elements within the issue RGB25 card */
-QLabel#asset_name_label, QLabel#asset_description_label, QLabel#total_supply_label, QLabel#asset_file, QLabel#issue_rgb_25_asset_title_label {
+/* Styles for label elements within the issue CFA card */
+QLabel#asset_name_label, QLabel#asset_description_label, QLabel#total_supply_label, QLabel#asset_file, QLabel#issue_cfa_asset_title_label {
font: 14px "Inter";
color: rgb(121, 128, 148);
background: transparent;
@@ -36,7 +36,7 @@ QLabel#asset_name_label, QLabel#asset_description_label, QLabel#total_supply_lab
font-weight: 600;
}
-QLabel#issue_rgb_25_asset_title_label {
+QLabel#issue_cfa_asset_title_label {
font: 24px "Inter";
color: #D0D3DD;
padding-bottom: 15px;
@@ -72,8 +72,8 @@ QPushButton#upload_file:hover {
border-radius: 4px;
}
-/* Styles for the RGB25 close button */
-QPushButton#rgb_25_close_btn {
+/* Styles for the CFA close button */
+QPushButton#cfa_close_btn {
background: transparent;
border: none;
min-width: 24px;
diff --git a/src/views/qss/issue_rgb20_style.qss b/src/views/qss/issue_nia_style.qss
similarity index 63%
rename from src/views/qss/issue_rgb20_style.qss
rename to src/views/qss/issue_nia_style.qss
index 2d6da3ba..95b4bb83 100644
--- a/src/views/qss/issue_rgb20_style.qss
+++ b/src/views/qss/issue_nia_style.qss
@@ -11,8 +11,8 @@ QLabel#logo_title {
color: white;
}
-/* Styles for the issue RGB20 widget */
-QWidget#issue_rgb20_widget {
+/* Styles for the issue NIA widget */
+QWidget#issue_nia_widget {
background: transparent;
background-color: rgb(3, 11, 37);
border: 1px solid rgb(102, 108, 129);
@@ -20,8 +20,8 @@ QWidget#issue_rgb20_widget {
}
-/* Styles for line edit elements within the issue RGB20 widget */
-QLineEdit#issue_rgb20_input, QLineEdit#asset_name_input, QLineEdit#amount_input {
+/* Styles for line edit elements within the issue NIA widget */
+QLineEdit#issue_nia_input, QLineEdit#asset_name_input, QLineEdit#amount_input {
padding-left: 10px;
font: 15px "Inter";
color: rgb(102, 108, 129);
@@ -30,8 +30,8 @@ QLineEdit#issue_rgb20_input, QLineEdit#asset_name_input, QLineEdit#amount_input
border-radius: 4px;
}
-/* Styles for label elements within the issue RGB20 widget */
-QLabel#issue_rgb20_label, QLabel#asset_ticker_label, QLabel#asset_name_label, QLabel#total_supply_label {
+/* Styles for label elements within the issue NIA widget */
+QLabel#issue_nia_label, QLabel#asset_ticker_label, QLabel#asset_name_label, QLabel#total_supply_label {
font: 14px "Inter";
color: rgb(121, 128, 148);
background: transparent;
@@ -39,7 +39,7 @@ QLabel#issue_rgb20_label, QLabel#asset_ticker_label, QLabel#asset_name_label, QL
font-weight: 600;
}
-/* Styles for the issue RGB20 title */
+/* Styles for the issue NIA title */
QLabel#set_wallet_password_label {
font: 24px "Inter";
border: none;
@@ -50,17 +50,17 @@ QLabel#set_wallet_password_label {
}
-/* Styles for the RGB20 close button */
-QPushButton#rgb_20_close_btn {
+/* Styles for the NIA close button */
+QPushButton#nia_close_btn {
background: transparent;
border: none;
}
-QFrame#issue_rgb20_wallet_logo{
+QFrame#issue_nia_wallet_logo{
border:none
}
-/* Styles for line elements within the issue RGB20 widget */
+/* Styles for line elements within the issue NIA widget */
QFrame#line_3, QFrame#bottom_line_frame {
border: none;
border-bottom: 1px solid rgb(27, 35, 59);
diff --git a/src/views/ui_backup.py b/src/views/ui_backup.py
index d80c910e..b97f2377 100644
--- a/src/views/ui_backup.py
+++ b/src/views/ui_backup.py
@@ -468,8 +468,8 @@ def close_button_navigation(self):
navigation_map = {
'fungibles': self._view_model.page_navigation.fungibles_asset_page,
- 'RGB20': self._view_model.page_navigation.fungibles_asset_page,
- 'RGB25': self._view_model.page_navigation.collectibles_asset_page,
+ 'NIA': self._view_model.page_navigation.fungibles_asset_page,
+ 'CFA': self._view_model.page_navigation.collectibles_asset_page,
'create_invoice': self._view_model.page_navigation.fungibles_asset_page,
'channel_management': self._view_model.page_navigation.channel_management_page,
'collectibles': self._view_model.page_navigation.collectibles_asset_page,
@@ -487,9 +487,9 @@ def close_button_navigation(self):
ToastManager.show_toast(
parent=self,
preset=ToastPreset.ERROR,
- description=f'No navigation defined for {
+ description=f"""No navigation defined for {
originating_page
- }',
+ }""",
)
def handle_mnemonic_visibility(self):
diff --git a/src/views/ui_collectible_asset.py b/src/views/ui_collectible_asset.py
index a7968cec..fb2d119c 100644
--- a/src/views/ui_collectible_asset.py
+++ b/src/views/ui_collectible_asset.py
@@ -19,7 +19,8 @@
from PySide6.QtWidgets import QWidget
import src.resources_rc
-from accessible_constant import ISSUE_RGB25_ASSET
+from accessible_constant import ISSUE_CFA_ASSET
+from src.model.enums.enums_model import AssetType
from src.model.enums.enums_model import ToastPreset
from src.model.rgb_model import RgbAssetPageLoadModel
from src.utils.clickable_frame import ClickableFrame
@@ -81,7 +82,7 @@ def __init__(self, view_model):
title_name='collectibles', title_logo_path=':/assets/my_asset.png',
)
self.collectible_header_frame.action_button.setAccessibleName(
- ISSUE_RGB25_ASSET,
+ ISSUE_CFA_ASSET,
)
self.vertical_layout_2.addWidget(self.collectible_header_frame)
@@ -212,7 +213,7 @@ def create_collectible_frame(self, coll_asset):
coll_asset.asset_id,
coll_asset.name,
image_path=image_path,
- asset_type=coll_asset.asset_iface,
+ asset_type=AssetType.CFA,
)
collectibles_frame.setObjectName('collectibles_frame')
collectibles_frame.setCursor(
@@ -304,7 +305,7 @@ def setup_ui_connection(self):
)
self.collectible_header_frame.action_button.clicked.connect(
lambda: self._view_model.main_asset_view_model.navigate_issue_asset(
- self._view_model.page_navigation.issue_rgb25_asset_page,
+ self._view_model.page_navigation.issue_cfa_asset_page,
),
)
self._view_model.main_asset_view_model.loading_started.connect(
@@ -342,10 +343,10 @@ def handle_collectible_frame_click(self, asset_id, asset_name, image_path, asset
"""This method handles collectibles asset click of the main asset page."""
if asset_id is None or asset_name is None or image_path is None or asset_type is None:
return
- self._view_model.rgb25_view_model.asset_info.emit(
+ self._view_model.cfa_view_model.asset_info.emit(
asset_id, asset_name, image_path, asset_type,
)
- self._view_model.page_navigation.rgb25_detail_page(
+ self._view_model.page_navigation.cfa_detail_page(
RgbAssetPageLoadModel(asset_type=asset_type),
)
diff --git a/src/views/ui_create_ln_invoice.py b/src/views/ui_create_ln_invoice.py
index 09a7509e..10acee00 100644
--- a/src/views/ui_create_ln_invoice.py
+++ b/src/views/ui_create_ln_invoice.py
@@ -437,7 +437,7 @@ def retranslate_ui(self):
def on_close(self):
"""Navigate to the fungibles page."""
- if self.asset_type == AssetType.RGB25.value:
+ if self.asset_type == AssetType.CFA.value:
self._view_model.page_navigation.collectibles_asset_page()
else:
self._view_model.page_navigation.fungibles_asset_page()
@@ -500,7 +500,7 @@ def get_ln_invoice(self):
asset_id=self.asset_id, amount=self.amount_input.text(), expiry=expiry_time, amount_msat=push_msat,
)
self.render_timer.stop()
- self._view_model.page_navigation.receive_rgb25_page(
+ self._view_model.page_navigation.receive_cfa_page(
params=AssetDataModel(
asset_type='create_invoice',
close_page_navigation=self.asset_type,
diff --git a/src/views/ui_fungible_asset.py b/src/views/ui_fungible_asset.py
index 47bc8f28..921531c4 100644
--- a/src/views/ui_fungible_asset.py
+++ b/src/views/ui_fungible_asset.py
@@ -24,7 +24,7 @@
import src.resources_rc
from accessible_constant import FUNGIBLES_SCROLL_WIDGETS
-from accessible_constant import ISSUE_RGB20_ASSET
+from accessible_constant import ISSUE_NIA_ASSET
from src.data.repository.setting_repository import SettingRepository
from src.data.service.common_operation_service import CommonOperationService
from src.model.enums.enums_model import AssetType
@@ -84,7 +84,7 @@ def __init__(self, view_model):
self.title_frame = HeaderFrame(
title_logo_path=':/assets/my_asset.png', title_name='fungibles',
)
- self.title_frame.action_button.setAccessibleName(ISSUE_RGB20_ASSET)
+ self.title_frame.action_button.setAccessibleName(ISSUE_NIA_ASSET)
self.fungible_frame = None
self.vertical_layout_fungible_frame = None
self.grid_layout_fungible_frame = None
@@ -231,6 +231,7 @@ def show_assets(self):
bitcoin_img_path = {
NetworkEnumModel.MAINNET.value: ':/assets/bitcoin.png',
NetworkEnumModel.REGTEST.value: ':/assets/regtest_bitcoin.png',
+ NetworkEnumModel.TESTNET4.value: ':/assets/testnet_bitcoin.png',
NetworkEnumModel.TESTNET.value: ':/assets/testnet_bitcoin.png',
}
@@ -273,10 +274,64 @@ def show_assets(self):
),
)
+ def _get_asset_logo_pixmap(self, asset, img_path):
+ """Generate and return the pixmap for the asset logo."""
+ if img_path:
+ return QPixmap(img_path)
+
+ img_str = generate_identicon(asset.asset_id)
+ image = QImage.fromData(QByteArray.fromBase64(img_str.encode()))
+ return QPixmap.fromImage(image)
+
+ def _get_address_text(self, asset, img_path):
+ """Get the appropriate address text based on asset type and network."""
+ if not img_path:
+ return asset.asset_id
+
+ network = SettingRepository.get_wallet_network()
+ network_token_map = {
+ NetworkEnumModel.REGTEST: TokenSymbol.REGTEST_BITCOIN,
+ NetworkEnumModel.TESTNET: TokenSymbol.TESTNET_BITCOIN,
+ NetworkEnumModel.TESTNET4: TokenSymbol.TESTNET4_BITCOIN,
+ }
+ return network_token_map.get(network, '')
+
+ def _get_outbound_balance_text(self, asset, img_path):
+ """Get the outbound balance text for the asset."""
+ if img_path:
+ return 'N/A'
+
+ return str(asset.balance.offchain_outbound) if asset.balance.offchain_outbound else 'N/A'
+
+ def _apply_bitcoin_naming(self, asset):
+ """Apply Bitcoin-specific naming conventions based on ticker."""
+ if 'BTC' not in asset.ticker:
+ return
+
+ self.token_symbol.setText(TokenSymbol.SAT.value)
+ bitcoin_asset = AssetType.BITCOIN.value.lower()
+
+ # For mainnet Bitcoin
+ if asset.ticker == TokenSymbol.BITCOIN.value:
+ self.asset_name.setText(bitcoin_asset)
+ return
+
+ # For testnet variants and regtest, use network to distinguish
+ # since TESTNET_BITCOIN and TESTNET4_BITCOIN have the same ticker 'tBTC'
+ network = SettingRepository.get_wallet_network()
+ network_naming_map = {
+ NetworkEnumModel.TESTNET: f'{NetworkEnumModel.TESTNET.value} {bitcoin_asset}',
+ NetworkEnumModel.TESTNET4: f'{NetworkEnumModel.TESTNET4.value} {bitcoin_asset}',
+ NetworkEnumModel.REGTEST: f'{NetworkEnumModel.REGTEST.value} {bitcoin_asset}',
+ }
+
+ if network in network_naming_map:
+ self.asset_name.setText(network_naming_map[network])
+
def create_fungible_card(self, asset, img_path=None):
"""This method creates all the fungible assets elements of the main asset page."""
self.fungible_frame = ClickableFrame(
- asset.asset_id, asset.name, self.fungibles_widget, asset_type=asset.asset_iface,
+ asset.asset_id, asset.name, self.fungibles_widget, asset_type=AssetType.BITCOIN if img_path else AssetType.NIA,
)
self.fungible_frame.setStyleSheet(
load_stylesheet('views/qss/fungible_asset_style.qss'),
@@ -298,23 +353,16 @@ def create_fungible_card(self, asset, img_path=None):
'horizontal_layout_7',
)
self.grid_layout_fungible_frame.setContentsMargins(6, 0, 6, 0)
+
+ # Asset Logo
self.asset_logo = QLabel(self.fungible_frame)
self.asset_logo.setObjectName('asset_logo')
-
self.asset_logo.setMinimumSize(QSize(40, 40))
self.asset_logo.setMaximumSize(QSize(40, 40))
-
- if img_path:
- self.asset_logo.setPixmap(QPixmap(img_path))
-
- else:
- img_str = generate_identicon(asset.asset_id)
- image = QImage.fromData(QByteArray.fromBase64(img_str.encode()))
- pixmap = QPixmap.fromImage(image)
- self.asset_logo.setPixmap(pixmap)
-
+ self.asset_logo.setPixmap(self._get_asset_logo_pixmap(asset, img_path))
self.grid_layout_fungible_frame.addWidget(self.asset_logo, 0, 0)
+ # Asset Name
self.asset_name = QLabel(self.fungible_frame)
self.asset_name.setObjectName('asset_name')
self.asset_name.setMinimumSize(QSize(135, 40))
@@ -326,31 +374,21 @@ def create_fungible_card(self, asset, img_path=None):
self.asset_name.setText(asset.name)
self.grid_layout_fungible_frame.addWidget(self.asset_name, 0, 1)
+ # Address
self.address = QLabel(self.fungible_frame)
self.address.setObjectName('address')
self.address.setMinimumSize(QSize(600, 0))
self.address.setMaximumSize(QSize(16777215, 16777215))
- self.address.setStyleSheet(
- 'padding-left:10px;',
- )
-
- if asset.asset_iface == AssetType.BITCOIN:
- network = SettingRepository.get_wallet_network()
- if network == NetworkEnumModel.REGTEST:
- self.address.setText(TokenSymbol.REGTEST_BITCOIN)
- elif network == NetworkEnumModel.TESTNET:
- self.address.setText(TokenSymbol.TESTNET_BITCOIN)
- else:
- self.address.setText(asset.asset_id)
-
+ self.address.setStyleSheet('padding-left:10px;')
+ self.address.setText(self._get_address_text(asset, img_path))
self.grid_layout_fungible_frame.addWidget(
self.address, 0, 2, Qt.AlignLeft,
)
+ # Amount
self.amount = QLabel(self.fungible_frame)
self.amount.setObjectName('amount')
self.amount.setMinimumSize(QSize(100, 40))
-
self.amount.setText(str(asset.balance.future))
self.grid_layout_fungible_frame.addWidget(
self.amount, 0, 3, Qt.AlignLeft,
@@ -360,21 +398,16 @@ def create_fungible_card(self, asset, img_path=None):
self.outbound_balance = QLabel(self.fungible_frame)
self.outbound_balance.setObjectName('outbound_balance')
self.outbound_balance.setMinimumSize(QSize(80, 40))
-
- if asset.asset_iface == AssetType.RGB20:
- self.outbound_balance.setText(
- str(asset.balance.offchain_outbound) if asset.balance.offchain_outbound else 'N/A',
- )
- else:
- # Fallback for other asset types
- self.outbound_balance.setText('N/A')
+ self.outbound_balance.setText(
+ self._get_outbound_balance_text(asset, img_path),
+ )
self.grid_layout_fungible_frame.addWidget(
self.outbound_balance, 0, 4, Qt.AlignLeft,
)
+ # Token Symbol
self.token_symbol = QLabel(self.fungible_frame)
self.token_symbol.setObjectName('token_symbol')
-
self.token_symbol.setText(asset.ticker)
self.grid_layout_fungible_frame.addWidget(
self.token_symbol, 0, 5, Qt.AlignLeft,
@@ -384,19 +417,8 @@ def create_fungible_card(self, asset, img_path=None):
self.grid_layout_fungible_frame,
)
- if 'BTC' in asset.ticker:
- self.token_symbol.setText(TokenSymbol.SAT.value)
- bitcoin_asset = AssetType.BITCOIN.value.lower()
- if asset.ticker == TokenSymbol.BITCOIN.value:
- self.asset_name.setText(bitcoin_asset)
- if asset.ticker == TokenSymbol.TESTNET_BITCOIN.value:
- self.asset_name.setText(
- f'{NetworkEnumModel.TESTNET.value} {bitcoin_asset}',
- )
- if asset.ticker == TokenSymbol.REGTEST_BITCOIN.value:
- self.asset_name.setText(
- f'{NetworkEnumModel.REGTEST.value} {bitcoin_asset}',
- )
+ # Apply Bitcoin-specific naming
+ self._apply_bitcoin_naming(asset)
self.vertical_layout_3.addWidget(self.fungible_frame)
self.fungible_frame.clicked.connect(self.handle_asset_frame_click)
@@ -411,7 +433,7 @@ def setup_ui_connection(self):
)
self.title_frame.action_button.clicked.connect(
lambda: self._view_model.main_asset_view_model.navigate_issue_asset(
- self._view_model.page_navigation.issue_rgb20_asset_page,
+ self._view_model.page_navigation.issue_nia_asset_page,
),
)
self._view_model.main_asset_view_model.loading_started.connect(
@@ -445,10 +467,10 @@ def handle_asset_frame_click(self, asset_id, asset_name, image_path, asset_type)
if asset_type == AssetType.BITCOIN.value:
self._view_model.page_navigation.bitcoin_page()
else:
- self._view_model.rgb25_view_model.asset_info.emit(
+ self._view_model.cfa_view_model.asset_info.emit(
asset_id, asset_name, image_path, asset_type,
)
- self._view_model.page_navigation.rgb25_detail_page(
+ self._view_model.page_navigation.cfa_detail_page(
RgbAssetPageLoadModel(asset_type=asset_type),
)
diff --git a/src/views/ui_issue_rgb25.py b/src/views/ui_issue_cfa.py
similarity index 68%
rename from src/views/ui_issue_rgb25.py
rename to src/views/ui_issue_cfa.py
index f4002cac..cb7eff72 100644
--- a/src/views/ui_issue_rgb25.py
+++ b/src/views/ui_issue_cfa.py
@@ -1,6 +1,6 @@
# pylint: disable=too-many-instance-attributes, too-many-statements, unused-import
-"""This module contains the IssueRGB25Widget class,
- which represents the UI for issuing RGB25 assets.
+"""This module contains the IssueCFAWidget class,
+ which represents the UI for issuing CFA assets.
"""
from __future__ import annotations
@@ -23,12 +23,12 @@
from PySide6.QtWidgets import QWidget
import src.resources_rc
-from accessible_constant import ISSUE_RGB25_ASSET_CLOSE_BUTTON
-from accessible_constant import ISSUE_RGB25_BUTTON
-from accessible_constant import RGB25_ASSET_AMOUNT
-from accessible_constant import RGB25_ASSET_DESCRIPTION
-from accessible_constant import RGB25_ASSET_NAME
-from accessible_constant import RGB25_UPLOAD_FILE_BUTTON
+from accessible_constant import CFA_ASSET_AMOUNT
+from accessible_constant import CFA_ASSET_DESCRIPTION
+from accessible_constant import CFA_ASSET_NAME
+from accessible_constant import CFA_UPLOAD_FILE_BUTTON
+from accessible_constant import ISSUE_CFA_ASSET_CLOSE_BUTTON
+from accessible_constant import ISSUE_CFA_BUTTON
from src.model.common_operation_model import NodeInfoResponseModel
from src.model.node_info_model import NodeInfoModel
from src.model.success_model import SuccessPageModel
@@ -43,14 +43,14 @@
from src.views.components.wallet_logo_frame import WalletLogoFrame
-class IssueRGB25Widget(QWidget):
- """This class represents the UI for issuing RGB25 assets."""
+class IssueCFAWidget(QWidget):
+ """This class represents the UI for issuing CFA assets."""
def __init__(self, view_model: MainViewModel):
- """Initialize the IssueRGB25Widget class."""
+ """Initialize the IssueCFAWidget class."""
super().__init__()
- self.render_timer = RenderTimer(task_name='IssueRGB25Asset Rendering')
- self.setStyleSheet(load_stylesheet('views/qss/issue_rgb25_style.qss'))
+ self.render_timer = RenderTimer(task_name='IssueCFAAsset Rendering')
+ self.setStyleSheet(load_stylesheet('views/qss/issue_cfa_style.qss'))
self._view_model: MainViewModel = view_model
self.grid_layout = QGridLayout(self)
self.grid_layout.setObjectName('gridLayout')
@@ -64,11 +64,11 @@ def __init__(self, view_model: MainViewModel):
self.grid_layout.addItem(self.vertical_spacer, 0, 2, 1, 1)
- self.issue_rgb_25_card = QWidget(self)
- self.issue_rgb_25_card.setObjectName('issue_rgb_25_card')
- self.issue_rgb_25_card.setMinimumSize(QSize(499, 608))
- self.issue_rgb_25_card.setMaximumSize(QSize(499, 608))
- self.grid_layout_1 = QGridLayout(self.issue_rgb_25_card)
+ self.issue_cfa_card = QWidget(self)
+ self.issue_cfa_card.setObjectName('issue_cfa_card')
+ self.issue_cfa_card.setMinimumSize(QSize(499, 608))
+ self.issue_cfa_card.setMaximumSize(QSize(499, 608))
+ self.grid_layout_1 = QGridLayout(self.issue_cfa_card)
self.grid_layout_1.setObjectName('gridLayout_26')
self.grid_layout_1.setContentsMargins(1, -1, 1, 35)
self.horizontal_spacer_1 = QSpacerItem(
@@ -77,16 +77,16 @@ def __init__(self, view_model: MainViewModel):
self.grid_layout_1.addItem(self.horizontal_spacer_1, 6, 0)
- self.issue_rgb25_button = PrimaryButton()
- self.issue_rgb25_button.setAccessibleName(ISSUE_RGB25_BUTTON)
- self.issue_rgb25_button.setMinimumSize(QSize(402, 40))
- self.issue_rgb25_button.setMaximumSize(QSize(402, 40))
+ self.issue_cfa_button = PrimaryButton()
+ self.issue_cfa_button.setAccessibleName(ISSUE_CFA_BUTTON)
+ self.issue_cfa_button.setMinimumSize(QSize(402, 40))
+ self.issue_cfa_button.setMaximumSize(QSize(402, 40))
self.grid_layout_1.addWidget(
- self.issue_rgb25_button, 7, 0, Qt.AlignCenter,
+ self.issue_cfa_button, 7, 0, Qt.AlignCenter,
)
- self.header_line = QFrame(self.issue_rgb_25_card)
+ self.header_line = QFrame(self.issue_cfa_card)
self.header_line.setObjectName('line_top')
self.header_line.setFrameShape(QFrame.Shape.HLine)
@@ -94,63 +94,63 @@ def __init__(self, view_model: MainViewModel):
self.grid_layout_1.addWidget(self.header_line, 1, 0, 1, 1)
- self.issue_rgb25_details_layout = QVBoxLayout()
- self.issue_rgb25_details_layout.setSpacing(12)
- self.issue_rgb25_details_layout.setObjectName('vertical_layout_6')
- self.issue_rgb25_details_layout.setContentsMargins(45, 15, 45, -1)
- self.asset_name_label = QLabel(self.issue_rgb_25_card)
+ self.issue_cfa_details_layout = QVBoxLayout()
+ self.issue_cfa_details_layout.setSpacing(12)
+ self.issue_cfa_details_layout.setObjectName('vertical_layout_6')
+ self.issue_cfa_details_layout.setContentsMargins(45, 15, 45, -1)
+ self.asset_name_label = QLabel(self.issue_cfa_card)
self.asset_name_label.setObjectName('asset_name_label')
self.asset_name_label.setAutoFillBackground(False)
self.asset_name_label.setFrameShadow(QFrame.Plain)
self.asset_name_label.setLineWidth(1)
- self.issue_rgb25_details_layout.addWidget(self.asset_name_label)
+ self.issue_cfa_details_layout.addWidget(self.asset_name_label)
- self.name_of_the_asset_input = QLineEdit(self.issue_rgb_25_card)
+ self.name_of_the_asset_input = QLineEdit(self.issue_cfa_card)
self.name_of_the_asset_input.setObjectName('name_of_the_asset_input')
- self.name_of_the_asset_input.setAccessibleName(RGB25_ASSET_NAME)
+ self.name_of_the_asset_input.setAccessibleName(CFA_ASSET_NAME)
self.name_of_the_asset_input.setMinimumSize(QSize(403, 40))
self.name_of_the_asset_input.setMaximumSize(QSize(403, 16777215))
self.name_of_the_asset_input.setClearButtonEnabled(True)
- self.issue_rgb25_details_layout.addWidget(self.name_of_the_asset_input)
+ self.issue_cfa_details_layout.addWidget(self.name_of_the_asset_input)
- self.asset_description_label = QLabel(self.issue_rgb_25_card)
+ self.asset_description_label = QLabel(self.issue_cfa_card)
self.asset_description_label.setObjectName('asset_description_label')
- self.asset_description_input = QLineEdit(self.issue_rgb_25_card)
+ self.asset_description_input = QLineEdit(self.issue_cfa_card)
self.asset_description_input.setObjectName('asset_description_input')
- self.asset_description_input.setAccessibleName(RGB25_ASSET_DESCRIPTION)
+ self.asset_description_input.setAccessibleName(CFA_ASSET_DESCRIPTION)
self.asset_description_input.setMinimumSize(QSize(403, 40))
self.asset_description_input.setMaximumSize(QSize(403, 16777215))
self.asset_description_input.setFrame(False)
self.asset_description_input.setClearButtonEnabled(True)
- self.issue_rgb25_details_layout.addWidget(self.asset_description_label)
+ self.issue_cfa_details_layout.addWidget(self.asset_description_label)
- self.issue_rgb25_details_layout.addWidget(self.asset_description_input)
- self.total_supply_label = QLabel(self.issue_rgb_25_card)
+ self.issue_cfa_details_layout.addWidget(self.asset_description_input)
+ self.total_supply_label = QLabel(self.issue_cfa_card)
self.total_supply_label.setObjectName('total_supply_label')
- self.issue_rgb25_details_layout.addWidget(self.total_supply_label)
+ self.issue_cfa_details_layout.addWidget(self.total_supply_label)
- self.amount_input = QLineEdit(self.issue_rgb_25_card)
+ self.amount_input = QLineEdit(self.issue_cfa_card)
self.amount_input.setObjectName('amount_input')
- self.amount_input.setAccessibleName(RGB25_ASSET_AMOUNT)
+ self.amount_input.setAccessibleName(CFA_ASSET_AMOUNT)
self.amount_input.setMinimumSize(QSize(403, 40))
self.amount_input.setMaximumSize(QSize(403, 40))
self.amount_input.setClearButtonEnabled(True)
set_number_validator(self.amount_input)
- self.issue_rgb25_details_layout.addWidget(self.amount_input)
+ self.issue_cfa_details_layout.addWidget(self.amount_input)
self.vertical_spacer_3 = QSpacerItem(
20, 30, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding,
)
- self.issue_rgb25_details_layout.addItem(self.vertical_spacer_3)
+ self.issue_cfa_details_layout.addItem(self.vertical_spacer_3)
- self.form_divider_line = QFrame(self.issue_rgb_25_card)
+ self.form_divider_line = QFrame(self.issue_cfa_card)
self.form_divider_line.setObjectName('line_bottom')
self.form_divider_line.setMinimumSize(QSize(403, 1))
self.form_divider_line.setMaximumSize(QSize(403, 1))
@@ -158,7 +158,7 @@ def __init__(self, view_model: MainViewModel):
self.form_divider_line.setFrameShape(QFrame.Shape.HLine)
self.form_divider_line.setFrameShadow(QFrame.Shadow.Sunken)
- self.issue_rgb25_details_layout.addWidget(
+ self.issue_cfa_details_layout.addWidget(
self.form_divider_line, 0, Qt.AlignHCenter,
)
@@ -166,18 +166,18 @@ def __init__(self, view_model: MainViewModel):
20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding,
)
- self.issue_rgb25_details_layout.addItem(self.vertical_spacer_4)
+ self.issue_cfa_details_layout.addItem(self.vertical_spacer_4)
- self.asset_file = QLabel(self.issue_rgb_25_card)
+ self.asset_file = QLabel(self.issue_cfa_card)
self.asset_file.setObjectName('asset_file')
self.asset_file.setMinimumSize(QSize(403, 17))
self.asset_file.setMaximumSize(QSize(403, 16777215))
- self.issue_rgb25_details_layout.addWidget(self.asset_file)
+ self.issue_cfa_details_layout.addWidget(self.asset_file)
- self.upload_file = QPushButton(self.issue_rgb_25_card)
+ self.upload_file = QPushButton(self.issue_cfa_card)
self.upload_file.setObjectName('upload_file')
- self.upload_file.setAccessibleName(RGB25_UPLOAD_FILE_BUTTON)
+ self.upload_file.setAccessibleName(CFA_UPLOAD_FILE_BUTTON)
self.upload_file.setMinimumSize(QSize(403, 40))
self.upload_file.setMaximumSize(QSize(403, 40))
self.upload_file.setAcceptDrops(False)
@@ -190,59 +190,59 @@ def __init__(self, view_model: MainViewModel):
icon.addFile(':/assets/upload.png', QSize(), QIcon.Normal, QIcon.Off)
self.upload_file.setIcon(icon)
- self.issue_rgb25_details_layout.addWidget(
+ self.issue_cfa_details_layout.addWidget(
self.upload_file, 0, Qt.AlignHCenter,
)
- self.file_path = QLabel(self.issue_rgb_25_card)
+ self.file_path = QLabel(self.issue_cfa_card)
self.file_path.setObjectName('asset_ti')
- self.issue_rgb25_details_layout.addWidget(
+ self.issue_cfa_details_layout.addWidget(
self.file_path, 0, Qt.AlignHCenter,
)
self.vertical_spacer_5 = QSpacerItem(
20, 70, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred,
)
- self.issue_rgb25_details_layout.addItem(self.vertical_spacer_5)
+ self.issue_cfa_details_layout.addItem(self.vertical_spacer_5)
self.grid_layout_1.addLayout(
- self.issue_rgb25_details_layout, 2, 0, 1, 1,
+ self.issue_cfa_details_layout, 2, 0, 1, 1,
)
self.grid_layout_2 = QGridLayout()
self.grid_layout_2.setSpacing(0)
self.grid_layout_2.setObjectName('grid_layout_2')
self.grid_layout_2.setContentsMargins(36, 8, 40, -1)
- self.issue_rgb_25_asset_title_label = QLabel(self.issue_rgb_25_card)
- self.issue_rgb_25_asset_title_label.setObjectName(
- 'issue_rgb_25_asset_title_label',
+ self.issue_cfa_asset_title_label = QLabel(self.issue_cfa_card)
+ self.issue_cfa_asset_title_label.setObjectName(
+ 'issue_cfa_asset_title_label',
)
self.grid_layout_2.addWidget(
- self.issue_rgb_25_asset_title_label, 0, 0, 1, 1,
+ self.issue_cfa_asset_title_label, 0, 0, 1, 1,
)
- self.rgb_25_close_btn = QPushButton(self.issue_rgb_25_card)
- self.rgb_25_close_btn.setObjectName('rgb_25_close_btn')
- self.rgb_25_close_btn.setAccessibleName(ISSUE_RGB25_ASSET_CLOSE_BUTTON)
- self.rgb_25_close_btn.setMinimumSize(QSize(24, 24))
- self.rgb_25_close_btn.setMaximumSize(QSize(50, 65))
- self.rgb_25_close_btn.setAutoFillBackground(False)
+ self.cfa_close_btn = QPushButton(self.issue_cfa_card)
+ self.cfa_close_btn.setObjectName('cfa_close_btn')
+ self.cfa_close_btn.setAccessibleName(ISSUE_CFA_ASSET_CLOSE_BUTTON)
+ self.cfa_close_btn.setMinimumSize(QSize(24, 24))
+ self.cfa_close_btn.setMaximumSize(QSize(50, 65))
+ self.cfa_close_btn.setAutoFillBackground(False)
icon1 = QIcon()
icon1.addFile(
':/assets/x_circle.png',
QSize(), QIcon.Normal, QIcon.Off,
)
- self.rgb_25_close_btn.setIcon(icon1)
- self.rgb_25_close_btn.setIconSize(QSize(24, 24))
- self.rgb_25_close_btn.setCheckable(False)
- self.rgb_25_close_btn.setChecked(False)
- self.rgb_25_close_btn.setCursor(
+ self.cfa_close_btn.setIcon(icon1)
+ self.cfa_close_btn.setIconSize(QSize(24, 24))
+ self.cfa_close_btn.setCheckable(False)
+ self.cfa_close_btn.setChecked(False)
+ self.cfa_close_btn.setCursor(
QCursor(Qt.CursorShape.PointingHandCursor),
)
- self.grid_layout_2.addWidget(self.rgb_25_close_btn, 0, 1, 1, 1)
+ self.grid_layout_2.addWidget(self.cfa_close_btn, 0, 1, 1, 1)
self.grid_layout_1.addLayout(self.grid_layout_2, 0, 0, 1, 1)
@@ -258,7 +258,7 @@ def __init__(self, view_model: MainViewModel):
self.grid_layout_1.addItem(self.vertical_spacer_7, 7, 0, 1, 1)
- self.footer_line = QFrame(self.issue_rgb_25_card)
+ self.footer_line = QFrame(self.issue_cfa_card)
self.footer_line.setObjectName('line_6')
self.footer_line.setMinimumSize(QSize(498, 1))
@@ -273,7 +273,7 @@ def __init__(self, view_model: MainViewModel):
self.grid_layout_1.addItem(self.vertical_spacer_8, 4, 0, 1, 1)
- self.grid_layout.addWidget(self.issue_rgb_25_card, 1, 1, 2, 2)
+ self.grid_layout.addWidget(self.issue_cfa_card, 1, 1, 2, 2)
self.horizontal_spacer_2 = QSpacerItem(
301, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum,
@@ -292,13 +292,13 @@ def __init__(self, view_model: MainViewModel):
)
self.grid_layout.addItem(self.vertical_spacer_2, 3, 1, 1, 1)
- self.issue_rgb25_button.setDisabled(True)
+ self.issue_cfa_button.setDisabled(True)
self.retranslate_ui()
self.setup_ui_connections()
def retranslate_ui(self):
"""Retranslate the UI elements."""
- self.issue_rgb25_button.setText(
+ self.issue_cfa_button.setText(
QCoreApplication.translate(
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'issue_asset', None,
),
@@ -343,23 +343,23 @@ def retranslate_ui(self):
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'upload_file', None,
),
)
- self.issue_rgb_25_asset_title_label.setText(
+ self.issue_cfa_asset_title_label.setText(
QCoreApplication.translate(
- IRIS_WALLET_TRANSLATIONS_CONTEXT, 'issue_new_rgb25_asset', None,
+ IRIS_WALLET_TRANSLATIONS_CONTEXT, 'issue_new_cfa_asset', None,
),
)
def setup_ui_connections(self):
"""Set up connections for UI elements."""
- self.rgb_25_close_btn.clicked.connect(
+ self.cfa_close_btn.clicked.connect(
self.on_close,
)
- self.issue_rgb25_button.clicked.connect(self.on_issue_rgb25)
+ self.issue_cfa_button.clicked.connect(self.on_issue_cfa)
self.upload_file.clicked.connect(self.on_upload_asset_file)
- self._view_model.issue_rgb25_asset_view_model.is_loading.connect(
+ self._view_model.issue_cfa_asset_view_model.is_loading.connect(
self.update_loading_state,
)
- self._view_model.issue_rgb25_asset_view_model.file_upload_message.connect(
+ self._view_model.issue_cfa_asset_view_model.file_upload_message.connect(
self.show_file_preview,
)
self.amount_input.textChanged.connect(self.handle_button_enabled)
@@ -369,7 +369,7 @@ def setup_ui_connections(self):
self.name_of_the_asset_input.textChanged.connect(
self.handle_button_enabled,
)
- self._view_model.issue_rgb25_asset_view_model.success_page_message.connect(
+ self._view_model.issue_cfa_asset_view_model.success_page_message.connect(
self.show_asset_issued,
)
self.amount_input.textChanged.connect(
@@ -387,11 +387,11 @@ def show_file_preview(self, file_upload_message):
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'image_validation', None,
).format(get_max_file_size.max_media_upload_size_mb)
self.file_path.setText(validation_text)
- self.issue_rgb25_button.setDisabled(True)
- self.issue_rgb_25_card.setMaximumSize(QSize(499, 608))
+ self.issue_cfa_button.setDisabled(True)
+ self.issue_cfa_card.setMaximumSize(QSize(499, 608))
else:
self.file_path.setText(file_upload_message)
- self.issue_rgb_25_card.setMaximumSize(QSize(499, 808))
+ self.issue_cfa_card.setMaximumSize(QSize(499, 808))
pixmap = resize_image(file_upload_message, 242, 242)
self.file_path.setPixmap(
QPixmap(pixmap),
@@ -401,20 +401,20 @@ def show_file_preview(self, file_upload_message):
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'change_uploaded_file', 'CHANGE UPLOADED FILE',
),
)
- self.issue_rgb25_button.setDisabled(False)
+ self.issue_cfa_button.setDisabled(False)
- def on_issue_rgb25(self):
- """Issue rgb25 while issue rgb25 button clicked"""
+ def on_issue_cfa(self):
+ """Issue cfa while issue cfa button clicked"""
asset_description = self.asset_description_input.text()
asset_name = self.name_of_the_asset_input.text()
total_supply = self.amount_input.text()
- self._view_model.issue_rgb25_asset_view_model.issue_rgb25_asset(
+ self._view_model.issue_cfa_asset_view_model.issue_cfa_asset(
asset_description, asset_name, total_supply,
)
def on_upload_asset_file(self):
"""This method handled upload asset file operation."""
- self._view_model.issue_rgb25_asset_view_model.open_file_dialog()
+ self._view_model.issue_cfa_asset_view_model.open_file_dialog()
def on_close(self):
"""Navigate to the collectibles page."""
@@ -423,9 +423,9 @@ def on_close(self):
def handle_button_enabled(self):
"""Updates the enabled state of the send button."""
if (self.amount_input.text() and self.asset_description_input.text() and self.name_of_the_asset_input.text() and self.amount_input.text() != '0'):
- self.issue_rgb25_button.setDisabled(False)
+ self.issue_cfa_button.setDisabled(False)
else:
- self.issue_rgb25_button.setDisabled(True)
+ self.issue_cfa_button.setDisabled(True)
def update_loading_state(self, is_loading: bool):
"""
@@ -436,11 +436,11 @@ def update_loading_state(self, is_loading: bool):
"""
if is_loading:
self.render_timer.start()
- self.issue_rgb25_button.start_loading()
- self.rgb_25_close_btn.setDisabled(True)
+ self.issue_cfa_button.start_loading()
+ self.cfa_close_btn.setDisabled(True)
else:
- self.issue_rgb25_button.stop_loading()
- self.rgb_25_close_btn.setDisabled(False)
+ self.issue_cfa_button.stop_loading()
+ self.cfa_close_btn.setDisabled(False)
def show_asset_issued(self, asset_name):
"""This method handled after the asset issue"""
diff --git a/src/views/ui_issue_rgb20.py b/src/views/ui_issue_nia.py
similarity index 62%
rename from src/views/ui_issue_rgb20.py
rename to src/views/ui_issue_nia.py
index 2b997de0..989b3f2a 100644
--- a/src/views/ui_issue_rgb20.py
+++ b/src/views/ui_issue_nia.py
@@ -1,6 +1,6 @@
# pylint: disable=too-many-instance-attributes, too-many-statements, unused-import
-"""This module contains the IssueRGB20Widget class,
- which represents the UI for issuing RGB20 assets.
+"""This module contains the IssueNIAWidget class,
+ which represents the UI for issuing NIA assets.
"""
from __future__ import annotations
@@ -21,11 +21,11 @@
from PySide6.QtWidgets import QWidget
import src.resources_rc
-from accessible_constant import ISSUE_RGB20_ASSET_CLOSE_BUTTON
-from accessible_constant import ISSUE_RGB20_BUTTON
-from accessible_constant import RGB20_ASSET_AMOUNT
-from accessible_constant import RGB20_ASSET_NAME
-from accessible_constant import RGB20_ASSET_TICKER
+from accessible_constant import ISSUE_NIA_ASSET_CLOSE_BUTTON
+from accessible_constant import ISSUE_NIA_BUTTON
+from accessible_constant import NIA_ASSET_AMOUNT
+from accessible_constant import NIA_ASSET_NAME
+from accessible_constant import NIA_ASSET_TICKER
from src.model.success_model import SuccessPageModel
from src.utils.common_utils import set_number_validator
from src.utils.common_utils import set_placeholder_value
@@ -37,43 +37,43 @@
from src.views.components.wallet_logo_frame import WalletLogoFrame
-class IssueRGB20Widget(QWidget):
- """This class represents the UI for issuing RGB20 assets."""
+class IssueNIAWidget(QWidget):
+ """This class represents the UI for issuing NIA assets."""
def __init__(self, view_model):
super().__init__()
- self.render_timer = RenderTimer(task_name='IssueRGB20Asset Rendering')
+ self.render_timer = RenderTimer(task_name='IssueNIAAsset Rendering')
self._view_model: MainViewModel = view_model
- self.setStyleSheet(load_stylesheet('views/qss/issue_rgb20_style.qss'))
- self.setObjectName('issue_rgb20_page')
- self.issue_rgb20_grid_layout = QGridLayout(self)
- self.issue_rgb20_grid_layout.setObjectName('issue_rgb20_grid_layout')
- self.issue_rgb20_wallet_logo = WalletLogoFrame(self)
+ self.setStyleSheet(load_stylesheet('views/qss/issue_nia_style.qss'))
+ self.setObjectName('issue_nia_page')
+ self.issue_nia_grid_layout = QGridLayout(self)
+ self.issue_nia_grid_layout.setObjectName('issue_nia_grid_layout')
+ self.issue_nia_wallet_logo = WalletLogoFrame(self)
- self.issue_rgb20_grid_layout.addWidget(
- self.issue_rgb20_wallet_logo, 0, 0, 1, 2,
+ self.issue_nia_grid_layout.addWidget(
+ self.issue_nia_wallet_logo, 0, 0, 1, 2,
)
- self.horizontal_spacer_rgb20_widget = QSpacerItem(
+ self.horizontal_spacer_nia_widget = QSpacerItem(
265,
20,
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Minimum,
)
- self.issue_rgb20_grid_layout.addItem(
- self.horizontal_spacer_rgb20_widget, 1, 3, 1, 1,
+ self.issue_nia_grid_layout.addItem(
+ self.horizontal_spacer_nia_widget, 1, 3, 1, 1,
)
- self.vertical_spacer_rgb20_widget = QSpacerItem(
+ self.vertical_spacer_nia_widget = QSpacerItem(
20,
190,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding,
)
- self.issue_rgb20_grid_layout.addItem(
- self.vertical_spacer_rgb20_widget, 3, 1, 1, 1,
+ self.issue_nia_grid_layout.addItem(
+ self.vertical_spacer_nia_widget, 3, 1, 1, 1,
)
self.horizontal_spacer_2 = QSpacerItem(
@@ -83,93 +83,93 @@ def __init__(self, view_model):
QSizePolicy.Policy.Minimum,
)
- self.issue_rgb20_grid_layout.addItem(
+ self.issue_nia_grid_layout.addItem(
self.horizontal_spacer_2, 2, 0, 1, 1,
)
- self.issue_rgb20_vertical_spacer_1 = QSpacerItem(
+ self.issue_nia_vertical_spacer_1 = QSpacerItem(
20,
190,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding,
)
- self.issue_rgb20_grid_layout.addItem(
- self.issue_rgb20_vertical_spacer_1, 0, 2, 1, 1,
+ self.issue_nia_grid_layout.addItem(
+ self.issue_nia_vertical_spacer_1, 0, 2, 1, 1,
)
- self.issue_rgb_20_widget = QWidget(self)
- self.issue_rgb_20_widget.setObjectName(
- 'issue_rgb20_widget',
+ self.issue_nia_widget = QWidget(self)
+ self.issue_nia_widget.setObjectName(
+ 'issue_nia_widget',
)
- self.issue_rgb_20_widget.setMinimumSize(QSize(499, 608))
- self.issue_rgb_20_widget.setMaximumSize(QSize(466, 608))
+ self.issue_nia_widget.setMinimumSize(QSize(499, 608))
+ self.issue_nia_widget.setMaximumSize(QSize(466, 608))
- self.inner_grid_layout = QGridLayout(self.issue_rgb_20_widget)
+ self.inner_grid_layout = QGridLayout(self.issue_nia_widget)
self.inner_grid_layout.setSpacing(6)
self.inner_grid_layout.setObjectName('inner_grid_layout')
self.inner_grid_layout.setContentsMargins(1, 4, 1, 30)
- self.vertical_layout_issue_rgb20 = QVBoxLayout()
- self.vertical_layout_issue_rgb20.setSpacing(6)
- self.vertical_layout_issue_rgb20.setObjectName(
+ self.vertical_layout_issue_nia = QVBoxLayout()
+ self.vertical_layout_issue_nia.setSpacing(6)
+ self.vertical_layout_issue_nia.setObjectName(
'vertical_layout_setup_wallet_password',
)
- self.issue_rgb20_title_layout = QHBoxLayout()
- self.issue_rgb20_title_layout.setObjectName('horizontal_layout_1')
- self.issue_rgb20_title_layout.setContentsMargins(35, 9, 40, 0)
- self.issue_rgb20_title = QLabel(
- self.issue_rgb_20_widget,
+ self.issue_nia_title_layout = QHBoxLayout()
+ self.issue_nia_title_layout.setObjectName('horizontal_layout_1')
+ self.issue_nia_title_layout.setContentsMargins(35, 9, 40, 0)
+ self.issue_nia_title = QLabel(
+ self.issue_nia_widget,
)
- self.issue_rgb20_title.setObjectName(
+ self.issue_nia_title.setObjectName(
'set_wallet_password_label',
)
- self.issue_rgb20_title.setMinimumSize(QSize(415, 63))
+ self.issue_nia_title.setMinimumSize(QSize(415, 63))
- self.issue_rgb20_title_layout.addWidget(self.issue_rgb20_title)
+ self.issue_nia_title_layout.addWidget(self.issue_nia_title)
- self.rgb_20_close_btn = QPushButton(self.issue_rgb_20_widget)
- self.rgb_20_close_btn.setAccessibleName(ISSUE_RGB20_ASSET_CLOSE_BUTTON)
- self.rgb_20_close_btn.setObjectName('rgb_20_close_btn')
- self.rgb_20_close_btn.setMinimumSize(QSize(24, 24))
- self.rgb_20_close_btn.setMaximumSize(QSize(50, 65))
- self.rgb_20_close_btn.setAutoFillBackground(False)
- self.rgb_20_close_btn.setCursor(
+ self.nia_close_btn = QPushButton(self.issue_nia_widget)
+ self.nia_close_btn.setAccessibleName(ISSUE_NIA_ASSET_CLOSE_BUTTON)
+ self.nia_close_btn.setObjectName('nia_close_btn')
+ self.nia_close_btn.setMinimumSize(QSize(24, 24))
+ self.nia_close_btn.setMaximumSize(QSize(50, 65))
+ self.nia_close_btn.setAutoFillBackground(False)
+ self.nia_close_btn.setCursor(
QCursor(Qt.CursorShape.PointingHandCursor),
)
- issue_rgb20_close_icon = QIcon()
- issue_rgb20_close_icon.addFile(
+ issue_nia_close_icon = QIcon()
+ issue_nia_close_icon.addFile(
':/assets/x_circle.png',
QSize(),
QIcon.Normal,
QIcon.Off,
)
- self.rgb_20_close_btn.setIcon(issue_rgb20_close_icon)
- self.rgb_20_close_btn.setIconSize(QSize(24, 24))
- self.rgb_20_close_btn.setCheckable(False)
- self.rgb_20_close_btn.setChecked(False)
+ self.nia_close_btn.setIcon(issue_nia_close_icon)
+ self.nia_close_btn.setIconSize(QSize(24, 24))
+ self.nia_close_btn.setCheckable(False)
+ self.nia_close_btn.setChecked(False)
- self.issue_rgb20_title_layout.addWidget(
- self.rgb_20_close_btn, 0, Qt.AlignHCenter,
+ self.issue_nia_title_layout.addWidget(
+ self.nia_close_btn, 0, Qt.AlignHCenter,
)
- self.vertical_layout_issue_rgb20.addLayout(
- self.issue_rgb20_title_layout,
+ self.vertical_layout_issue_nia.addLayout(
+ self.issue_nia_title_layout,
)
- self.header_line = QFrame(self.issue_rgb_20_widget)
+ self.header_line = QFrame(self.issue_nia_widget)
self.header_line.setObjectName('line_3')
self.header_line.setFrameShape(QFrame.HLine)
self.header_line.setFrameShadow(QFrame.Sunken)
- self.vertical_layout_issue_rgb20.addWidget(self.header_line)
+ self.vertical_layout_issue_nia.addWidget(self.header_line)
self.asset_ticker_layout = QVBoxLayout()
self.asset_ticker_layout.setSpacing(0)
self.asset_ticker_layout.setObjectName('vertical_layout_1')
self.asset_ticker_layout.setContentsMargins(60, -1, 0, -1)
- self.asset_ticker_label = QLabel(self.issue_rgb_20_widget)
+ self.asset_ticker_label = QLabel(self.issue_nia_widget)
self.asset_ticker_label.setObjectName('asset_ticker_label')
self.asset_ticker_label.setMinimumSize(QSize(0, 35))
self.asset_ticker_label.setBaseSize(QSize(0, 0))
@@ -180,10 +180,10 @@ def __init__(self, view_model):
self.asset_ticker_layout.addWidget(self.asset_ticker_label)
self.short_identifier_input = QLineEdit(
- self.issue_rgb_20_widget,
+ self.issue_nia_widget,
)
- self.short_identifier_input.setObjectName('issue_rgb20_input')
- self.short_identifier_input.setAccessibleName(RGB20_ASSET_TICKER)
+ self.short_identifier_input.setObjectName('issue_nia_input')
+ self.short_identifier_input.setAccessibleName(NIA_ASSET_TICKER)
self.short_identifier_input.setMinimumSize(QSize(0, 40))
self.short_identifier_input.setMaximumSize(QSize(370, 40))
@@ -192,7 +192,7 @@ def __init__(self, view_model):
self.asset_ticker_layout.addWidget(self.short_identifier_input)
- self.vertical_layout_issue_rgb20.addLayout(
+ self.vertical_layout_issue_nia.addLayout(
self.asset_ticker_layout,
)
@@ -201,17 +201,17 @@ def __init__(self, view_model):
self.asset_name_layout.setObjectName('vertical_layout_2')
self.asset_name_layout.setContentsMargins(60, -1, 0, -1)
- self.asset_name_label = QLabel(self.issue_rgb_20_widget)
+ self.asset_name_label = QLabel(self.issue_nia_widget)
self.asset_name_label.setObjectName('asset_name_label')
self.asset_name_label.setMinimumSize(QSize(0, 40))
self.asset_name_label.setMaximumSize(QSize(370, 40))
self.asset_name_layout.addWidget(self.asset_name_label)
self.asset_name_input = QLineEdit(
- self.issue_rgb_20_widget,
+ self.issue_nia_widget,
)
self.asset_name_input.setObjectName('asset_name_input')
- self.asset_name_input.setAccessibleName(RGB20_ASSET_NAME)
+ self.asset_name_input.setAccessibleName(NIA_ASSET_NAME)
self.asset_name_input.setMinimumSize(QSize(0, 40))
self.asset_name_input.setMaximumSize(QSize(370, 40))
@@ -220,7 +220,7 @@ def __init__(self, view_model):
self.asset_name_layout.addWidget(self.asset_name_input)
- self.vertical_layout_issue_rgb20.addLayout(
+ self.vertical_layout_issue_nia.addLayout(
self.asset_name_layout,
)
self.asset_supply_layout = QVBoxLayout()
@@ -228,17 +228,17 @@ def __init__(self, view_model):
self.asset_supply_layout.setObjectName('vertical_layout_3')
self.asset_supply_layout.setContentsMargins(60, -1, 0, -1)
- self.total_supply_label = QLabel(self.issue_rgb_20_widget)
+ self.total_supply_label = QLabel(self.issue_nia_widget)
self.total_supply_label.setObjectName('total_supply_label')
self.total_supply_label.setMinimumSize(QSize(0, 40))
self.total_supply_label.setMaximumSize(QSize(370, 40))
self.asset_supply_layout.addWidget(self.total_supply_label)
self.amount_input = QLineEdit(
- self.issue_rgb_20_widget,
+ self.issue_nia_widget,
)
self.amount_input.setObjectName('amount_input')
- self.amount_input.setAccessibleName(RGB20_ASSET_AMOUNT)
+ self.amount_input.setAccessibleName(NIA_ASSET_AMOUNT)
self.amount_input.setMinimumSize(QSize(0, 40))
self.amount_input.setMaximumSize(QSize(370, 40))
set_number_validator(self.amount_input)
@@ -247,58 +247,58 @@ def __init__(self, view_model):
self.asset_supply_layout.addWidget(self.amount_input)
- self.vertical_layout_issue_rgb20.addLayout(
+ self.vertical_layout_issue_nia.addLayout(
self.asset_supply_layout,
)
- self.vertical_spacer_issue_rgb20 = QSpacerItem(
+ self.vertical_spacer_issue_nia = QSpacerItem(
20,
40,
QSizePolicy.Policy.Minimum,
QSizePolicy.Policy.Expanding,
)
- self.vertical_layout_issue_rgb20.addItem(
- self.vertical_spacer_issue_rgb20,
+ self.vertical_layout_issue_nia.addItem(
+ self.vertical_spacer_issue_nia,
)
- self.footer_line = QFrame(self.issue_rgb_20_widget)
+ self.footer_line = QFrame(self.issue_nia_widget)
self.footer_line.setObjectName('bottom_line_frame')
self.footer_line.setFrameShape(QFrame.HLine)
self.footer_line.setFrameShadow(QFrame.Sunken)
- self.vertical_layout_issue_rgb20.addWidget(self.footer_line)
+ self.vertical_layout_issue_nia.addWidget(self.footer_line)
self.issue_button_spacer = QSpacerItem(
20, 22, QSizePolicy.Preferred, QSizePolicy.Preferred,
)
- self.vertical_layout_issue_rgb20.addItem(self.issue_button_spacer)
- self.issue_rgb20_btn = PrimaryButton()
- self.issue_rgb20_btn.setAccessibleName(ISSUE_RGB20_BUTTON)
- self.issue_rgb20_btn.setCursor(
+ self.vertical_layout_issue_nia.addItem(self.issue_button_spacer)
+ self.issue_nia_btn = PrimaryButton()
+ self.issue_nia_btn.setAccessibleName(ISSUE_NIA_BUTTON)
+ self.issue_nia_btn.setCursor(
QCursor(Qt.CursorShape.PointingHandCursor),
)
- self.issue_rgb20_btn.setMinimumSize(QSize(402, 40))
- self.issue_rgb20_btn.setMaximumSize(QSize(402, 40))
+ self.issue_nia_btn.setMinimumSize(QSize(402, 40))
+ self.issue_nia_btn.setMaximumSize(QSize(402, 40))
- self.issue_rgb20_btn.setCursor(
+ self.issue_nia_btn.setCursor(
QCursor(Qt.CursorShape.PointingHandCursor),
)
- self.vertical_layout_issue_rgb20.addWidget(
- self.issue_rgb20_btn, 0, Qt.AlignCenter,
+ self.vertical_layout_issue_nia.addWidget(
+ self.issue_nia_btn, 0, Qt.AlignCenter,
)
self.inner_grid_layout.addLayout(
- self.vertical_layout_issue_rgb20,
+ self.vertical_layout_issue_nia,
0,
0,
1,
1,
)
- self.issue_rgb20_grid_layout.addWidget(
- self.issue_rgb_20_widget,
+ self.issue_nia_grid_layout.addWidget(
+ self.issue_nia_widget,
1,
1,
2,
@@ -315,14 +315,14 @@ def setup_ui_connection(self):
self.handle_button_enabled,
)
self.amount_input.textChanged.connect(self.handle_button_enabled)
- self.rgb_20_close_btn.clicked.connect(
- self._view_model.issue_rgb20_asset_view_model.on_close_click,
+ self.nia_close_btn.clicked.connect(
+ self._view_model.issue_nia_asset_view_model.on_close_click,
)
- self._view_model.issue_rgb20_asset_view_model.issue_button_clicked.connect(
+ self._view_model.issue_nia_asset_view_model.issue_button_clicked.connect(
self.update_loading_state,
)
- self.issue_rgb20_btn.clicked.connect(self.on_issue_rgb20_click)
- self._view_model.issue_rgb20_asset_view_model.is_issued.connect(
+ self.issue_nia_btn.clicked.connect(self.on_issue_nia_click)
+ self._view_model.issue_nia_asset_view_model.is_issued.connect(
self.asset_issued,
)
self.amount_input.textChanged.connect(
@@ -331,11 +331,11 @@ def setup_ui_connection(self):
def retranslate_ui(self):
"""Retranslate the UI elements."""
- self.issue_rgb20_btn.setDisabled(True)
- self.issue_rgb20_title.setText(
+ self.issue_nia_btn.setDisabled(True)
+ self.issue_nia_title.setText(
QCoreApplication.translate(
IRIS_WALLET_TRANSLATIONS_CONTEXT,
- 'issue_new_rgb20_asset',
+ 'issue_new_nia_asset',
None,
),
)
@@ -381,7 +381,7 @@ def retranslate_ui(self):
None,
),
)
- self.issue_rgb20_btn.setText(
+ self.issue_nia_btn.setText(
QCoreApplication.translate(
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'issue_asset', None,
),
@@ -389,28 +389,28 @@ def retranslate_ui(self):
def update_loading_state(self, is_loading: bool):
"""
- Updates the loading state of the issue_rgb20_btn.
+ Updates the loading state of the issue_nia_btn.
This method prints the loading state and starts or stops the loading animation
of the proceed_wallet_password object based on the value of is_loading.
"""
if is_loading:
self.render_timer.start()
- self.issue_rgb20_btn.start_loading()
- self.rgb_20_close_btn.setDisabled(True)
+ self.issue_nia_btn.start_loading()
+ self.nia_close_btn.setDisabled(True)
else:
self.render_timer.stop()
- self.issue_rgb20_btn.stop_loading()
- self.rgb_20_close_btn.setDisabled(False)
+ self.issue_nia_btn.stop_loading()
+ self.nia_close_btn.setDisabled(False)
- def on_issue_rgb20_click(self):
- """Handle the click event for issuing a new RGB20 asset."""
+ def on_issue_nia_click(self):
+ """Handle the click event for issuing a new NIA asset."""
# Retrieve text values from input fields
short_identifier = self.short_identifier_input.text().upper()
asset_name = self.asset_name_input.text()
amount_to_issue = self.amount_input.text()
# Call the view model method and pass the text values as arguments
- self._view_model.issue_rgb20_asset_view_model.on_issue_click(
+ self._view_model.issue_nia_asset_view_model.on_issue_click(
short_identifier,
asset_name,
amount_to_issue,
@@ -419,9 +419,9 @@ def on_issue_rgb20_click(self):
def handle_button_enabled(self):
"""Updates the enabled state of the send button."""
if (self.short_identifier_input.text() and self.amount_input.text() and self.asset_name_input.text() and self.amount_input.text() != '0'):
- self.issue_rgb20_btn.setDisabled(False)
+ self.issue_nia_btn.setDisabled(False)
else:
- self.issue_rgb20_btn.setDisabled(True)
+ self.issue_nia_btn.setDisabled(True)
def asset_issued(self, asset_name):
"""This method handled after channel created"""
diff --git a/src/views/ui_receive_rgb_asset.py b/src/views/ui_receive_rgb_asset.py
index 8f9ad62c..a55f2cf6 100644
--- a/src/views/ui_receive_rgb_asset.py
+++ b/src/views/ui_receive_rgb_asset.py
@@ -1,6 +1,6 @@
# pylint: disable=too-many-instance-attributes, too-many-statements, unused-import
"""This module contains the ReceiveRGBAssetWidget class,
- which represents the UI for receive rgb25.
+ which represents the UI for receive cfa.
"""
from __future__ import annotations
@@ -38,8 +38,8 @@ def __init__(self, view_model, params: AssetDataModel):
self.default_min_confirmation = SettingCardRepository.get_default_min_confirmation()
self.receive_rgb_asset_page = ReceiveAssetWidget(
self._view_model,
- 'RGB25 page',
- 'rgb25_address_info',
+ 'CFA page',
+ 'cfa_address_info',
)
self.__loading_translucent_screen = LoadingTranslucentScreen(
parent=self, description_text='Loading', dot_animation=True,
@@ -54,9 +54,9 @@ def __init__(self, view_model, params: AssetDataModel):
def generate_invoice(self):
"""Call get rgb invoice to get invoice"""
if self.originating_page in [
- 'RGB20',
+ 'NIA',
'fungibles',
- 'RGB25',
+ 'CFA',
'collectibles',
'channel_management',
'view_unspent_list',
@@ -66,7 +66,7 @@ def generate_invoice(self):
'about',
'backup',
]:
- self._view_model.receive_rgb25_view_model.get_rgb_invoice(
+ self._view_model.receive_cfa_view_model.get_rgb_invoice(
self.default_min_confirmation.min_confirmation, self.asset_id,
)
@@ -92,19 +92,19 @@ def setup_ui_connection(self):
self.receive_rgb_asset_page.receive_asset_close_button.clicked.connect(
self.close_button_navigation,
)
- self._view_model.receive_rgb25_view_model.address.connect(
+ self._view_model.receive_cfa_view_model.address.connect(
self.update_address,
)
self._view_model.ln_offchain_view_model.invoice_get_event.connect(
lambda address: self.update_address(address, ln_invoice=True),
)
- self._view_model.receive_rgb25_view_model.message.connect(
+ self._view_model.receive_cfa_view_model.message.connect(
self.handle_message,
)
self._view_model.ln_offchain_view_model.invoice_get_event.connect(
self.hide_loading_screen,
)
- self._view_model.receive_rgb25_view_model.hide_loading.connect(
+ self._view_model.receive_cfa_view_model.hide_loading.connect(
self.hide_loading_screen,
)
@@ -113,16 +113,16 @@ def close_button_navigation(self):
Navigate to the specified page when the close button is clicked.
"""
- if self.close_page_navigation == AssetType.RGB25.value:
+ if self.close_page_navigation == AssetType.CFA.value:
self._view_model.page_navigation.collectibles_asset_page()
- elif self.close_page_navigation == AssetType.RGB20.value:
+ elif self.close_page_navigation == AssetType.NIA.value:
self._view_model.page_navigation.fungibles_asset_page()
else:
navigation_map = {
- 'RGB20': self._view_model.page_navigation.fungibles_asset_page,
+ 'NIA': self._view_model.page_navigation.fungibles_asset_page,
'fungibles': self._view_model.page_navigation.fungibles_asset_page,
'create_invoice': self._view_model.page_navigation.fungibles_asset_page,
- 'RGB25': self._view_model.page_navigation.collectibles_asset_page,
+ 'CFA': self._view_model.page_navigation.collectibles_asset_page,
'collectibles': self._view_model.page_navigation.collectibles_asset_page,
'channel_management': self._view_model.page_navigation.channel_management_page,
'view_unspent_list': self._view_model.page_navigation.view_unspent_list_page,
diff --git a/src/views/ui_rgb_asset_detail.py b/src/views/ui_rgb_asset_detail.py
index 96cb2818..25958ee9 100644
--- a/src/views/ui_rgb_asset_detail.py
+++ b/src/views/ui_rgb_asset_detail.py
@@ -4,8 +4,6 @@
"""
from __future__ import annotations
-import re
-
from PySide6.QtCore import QCoreApplication
from PySide6.QtCore import QRect
from PySide6.QtCore import QSize
@@ -49,12 +47,11 @@
from src.model.selection_page_model import AssetDataModel
from src.model.selection_page_model import SelectionPageModel
from src.model.transaction_detail_page_model import TransactionDetailPageModel
-from src.utils.common_utils import convert_hex_to_image
from src.utils.common_utils import copy_text
-from src.utils.common_utils import resize_image
from src.utils.constant import IRIS_WALLET_TRANSLATIONS_CONTEXT
from src.utils.helpers import load_stylesheet
from src.utils.render_timer import RenderTimer
+from src.utils.rgb_asset_helpers import handle_img_path
from src.viewmodels.main_view_model import MainViewModel
from src.views.components.buttons import AssetTransferButton
from src.views.components.confirmation_dialog import ConfirmationDialog
@@ -97,6 +94,7 @@ def __init__(self, view_model: MainViewModel, params: RgbAssetPageLoadModel):
NetworkEnumModel.MAINNET.value: ':/assets/bitcoin.png',
NetworkEnumModel.REGTEST.value: ':/assets/regtest_bitcoin.png',
NetworkEnumModel.TESTNET.value: ':/assets/testnet_bitcoin.png',
+ NetworkEnumModel.TESTNET4.value: ':/assets/testnet_bitcoin.png',
}
self.__loading_translucent_screen = LoadingTranslucentScreen(self)
self.asset_type = params.asset_type
@@ -482,7 +480,7 @@ def navigate_to_selection_page(self, navigation):
asset_id=self.asset_id_detail.toPlainText(),
asset_name=self.asset_name,
callback=navigation,
- back_page_navigation=lambda: self._view_model.page_navigation.rgb25_detail_page(
+ back_page_navigation=lambda: self._view_model.page_navigation.cfa_detail_page(
RgbAssetPageLoadModel(asset_type=self.asset_type),
),
rgb_asset_page_load_model=rgb_asset_page_load_model,
@@ -496,7 +494,7 @@ def select_receive_transfer_type(self):
TransferStatusEnumModel.RECEIVE.value,
)
else:
- self._view_model.page_navigation.receive_rgb25_page(
+ self._view_model.page_navigation.receive_cfa_page(
params=AssetDataModel(
asset_type=self.asset_type, asset_id=self.asset_id_detail.toPlainText(),
),
@@ -509,7 +507,7 @@ def select_send_transfer_type(self):
TransferStatusEnumModel.SEND.value,
)
else:
- self._view_model.page_navigation.send_rgb25_page()
+ self._view_model.page_navigation.send_cfa_page()
def setup_ui_connection(self):
"""Set up connections for UI elements."""
@@ -530,23 +528,23 @@ def setup_ui_connection(self):
self.copy_button.clicked.connect(
lambda: copy_text(self.asset_id_detail),
)
- self._view_model.rgb25_view_model.txn_list_loaded.connect(
+ self._view_model.cfa_view_model.txn_list_loaded.connect(
self.set_transaction_detail_frame,
)
self.close_btn.clicked.connect(
self.handle_page_navigation,
)
- self._view_model.rgb25_view_model.is_loading.connect(
+ self._view_model.cfa_view_model.is_loading.connect(
self.show_loading_screen,
)
self.asset_refresh_button.clicked.connect(
- self._view_model.rgb25_view_model.on_refresh_click,
+ self._view_model.cfa_view_model.on_refresh_click,
)
def refresh_transaction(self):
"""Refresh the transaction of the assets"""
self.render_timer.start()
- self._view_model.rgb25_view_model.on_refresh_click()
+ self._view_model.cfa_view_model.on_refresh_click()
def set_transaction_detail_frame(self, asset_id, asset_name, image_path, asset_type):
"""This method sets up the transaction detail frame in the UI.
@@ -557,7 +555,7 @@ def set_transaction_detail_frame(self, asset_id, asset_name, image_path, asset_t
self.asset_type = asset_type
self.asset_name = asset_name
self.handle_img_path(image_path=self.image_path)
- asset_transactions: ListOnAndOffChainTransfersWithBalance = self._view_model.rgb25_view_model.txn_list
+ asset_transactions: ListOnAndOffChainTransfersWithBalance = self._view_model.cfa_view_model.txn_list
self.asset_total_balance.setText(
str(asset_transactions.asset_balance.future),
)
@@ -591,7 +589,7 @@ def set_transaction_detail_frame(self, asset_id, asset_name, image_path, asset_t
no_transaction_widget, 0, 0, 1, 1,
)
return
- if asset_type == AssetType.RGB20.value:
+ if asset_type == AssetType.NIA.value:
self.rgb_asset_detail_widget.setMinimumSize(QSize(499, 730))
self.rgb_asset_detail_widget.setMaximumSize(QSize(499, 730))
self.scroll_area.setMaximumSize(QSize(335, 225))
@@ -638,7 +636,7 @@ def set_transaction_detail_frame(self, asset_id, asset_name, image_path, asset_t
def handle_asset_frame_click(self, params: TransactionDetailPageModel):
"""Pass emit value to navigation page"""
- self._view_model.page_navigation.rgb25_transaction_detail_page(params)
+ self._view_model.page_navigation.cfa_transaction_detail_page(params)
def handle_show_hide(self, transaction_detail_frame):
"""It handled to hide and show transaction details frame"""
@@ -681,38 +679,12 @@ def show_loading_screen(self, loading: bool):
self.receive_rgb_asset.setDisabled(False)
def handle_page_navigation(self):
- """Handle the page navigation according the RGB20 or RGB25 page"""
- if self.asset_type == AssetType.RGB20.value:
+ """Handle the page navigation according the NIA or CFA page"""
+ if self.asset_type == AssetType.NIA.value:
self._view_model.page_navigation.fungibles_asset_page()
else:
self._view_model.page_navigation.collectibles_asset_page()
- def is_path(self, file_path):
- """Check the file path"""
- if not isinstance(file_path, str):
- return False
- # Define a basic regex pattern for Unix-like file paths
- pattern = r'^(\/[a-zA-Z0-9_.-]+)+\/?$'
- # Check if the file_path matches the pattern
- return bool(re.match(pattern, file_path))
-
- def is_hex_string(self, bytes_hex):
- """Check if the string is a valid hex string."""
- if len(bytes_hex) % 2 != 0:
- return False
- hex_pattern = re.compile(r'^[0-9a-fA-F]+$')
- return bool(hex_pattern.match(bytes_hex))
-
- def set_asset_image(self, image_hex):
- """This method set the asset image according to the media path or image hex """
- if self.is_hex_string(image_hex):
- pixmap = convert_hex_to_image(image_hex)
- resized_image = resize_image(pixmap, 335, 335)
- self.label_asset_name.setPixmap(resized_image)
- else:
- resized_image = resize_image(image_hex, 335, 335)
- self.label_asset_name.setPixmap(resized_image)
-
def is_channel_open_for_asset(self):
"""Check if there is an open channel for the current asset."""
self.asset_id_detail.textChanged.connect(self.set_lightning_balance)
@@ -772,35 +744,21 @@ def handle_fail_transfer(self, idx, tx_id):
def _confirm_fail_transfer(self, idx):
"""Confirms the fail transfer action and closes the confirmation dialog."""
- self._view_model.rgb25_view_model.on_fail_transfer(idx)
+ self._view_model.cfa_view_model.on_fail_transfer(idx)
def handle_img_path(self, image_path):
- """
- Configures the asset detail widget and related components based on the provided image path.
- Adjusts the layout and styles, and sets the asset image.
- """
+ """Configure the asset detail widget based on the provided image path."""
if image_path:
- self.rgb_asset_detail_widget.setMinimumSize(QSize(466, 848))
- self.rgb_asset_detail_widget.setFixedWidth(499)
self.lightning_balance_frame.setMinimumSize(QSize(159, 120))
- self.label_asset_name = QLabel(self.rgb_asset_detail_widget)
- self.label_asset_name.setObjectName('label_asset_name')
- self.label_asset_name.setMaximumSize(QSize(335, 335))
- self.asset_id_frame.setMinimumSize(QSize(335, 86))
- self.asset_id_frame.setMaximumSize(QSize(335, 86))
- self.label_asset_name.setStyleSheet(
- "font: 14px \"Inter\";\n"
- 'color: #B3B6C3;\n'
- 'background: transparent;\n'
- 'border: none;\n'
- 'border-radius: 8px;\n'
- 'font-weight: 400;\n'
- '',
- )
- self.asset_image_layout.addWidget(
- self.label_asset_name, 0, Qt.AlignHCenter,
+ self.label_asset_name = handle_img_path(
+ self.rgb_asset_detail_widget,
+ image_path,
+ self.asset_image_layout,
+ self.asset_id_frame,
+ self.label_asset_name if hasattr(
+ self, 'label_asset_name',
+ ) else None,
)
- self.set_asset_image(image_hex=image_path)
self.transactions_label.setMinimumWidth(305)
def set_on_chain_transaction_frame(self, transaction, asset_name, asset_type, asset_id, image_path):
diff --git a/src/views/ui_rgb_asset_transaction_detail.py b/src/views/ui_rgb_asset_transaction_detail.py
index f814f00c..95d15b79 100644
--- a/src/views/ui_rgb_asset_transaction_detail.py
+++ b/src/views/ui_rgb_asset_transaction_detail.py
@@ -442,18 +442,18 @@ def handle_close(self):
"""
Handle the close action for the transaction detail view.
- This method emits a signal with the asset information and navigates to the RGB25 detail page.
+ This method emits a signal with the asset information and navigates to the CFA detail page.
Attributes:
self (object): The instance of the class containing the view model and navigation logic.
"""
- self._view_model.rgb25_view_model.asset_info.emit(
+ self._view_model.cfa_view_model.asset_info.emit(
self.params.asset_id,
self.params.asset_name,
self.params.image_path,
self.params.asset_type,
)
- self._view_model.page_navigation.rgb25_detail_page(
+ self._view_model.page_navigation.cfa_detail_page(
RgbAssetPageLoadModel(asset_type=self.params.asset_type),
)
diff --git a/src/views/ui_send_ln_invoice.py b/src/views/ui_send_ln_invoice.py
index f4230cde..45e025d1 100644
--- a/src/views/ui_send_ln_invoice.py
+++ b/src/views/ui_send_ln_invoice.py
@@ -648,7 +648,7 @@ def send_asset(self):
def on_success_sent_navigation(self):
"""This method is used to navigate to collectibles or fungibles page when the originating page is create ln invoice"""
- if self.asset_type == AssetType.RGB25.value:
+ if self.asset_type == AssetType.CFA.value:
self._view_model.page_navigation.collectibles_asset_page()
else:
self._view_model.page_navigation.fungibles_asset_page()
@@ -671,7 +671,7 @@ def update_loading_state(self, is_loading: bool):
def on_click_close_button(self):
"""This method is used to navigate to fungibles or collectibles page based on asset type"""
- if self.asset_type == AssetType.RGB25.value:
+ if self.asset_type == AssetType.CFA.value:
self._view_model.page_navigation.collectibles_asset_page()
else:
self._view_model.page_navigation.fungibles_asset_page()
diff --git a/src/views/ui_send_rgb_asset.py b/src/views/ui_send_rgb_asset.py
index c7674764..d6babca4 100644
--- a/src/views/ui_send_rgb_asset.py
+++ b/src/views/ui_send_rgb_asset.py
@@ -54,7 +54,7 @@ def __init__(self, view_model):
layout = QVBoxLayout()
layout.addWidget(self.send_rgb_asset_page)
self.setLayout(layout)
- self.set_originating_page(self._view_model.rgb25_view_model.asset_type)
+ self.set_originating_page(self._view_model.cfa_view_model.asset_type)
self.setup_ui_connection()
self.handle_button_enabled()
self.set_asset_balance()
@@ -72,8 +72,8 @@ def setup_ui_connection(self):
self.send_rgb_asset_page.close_button.clicked.connect(
self.rgb_asset_page_navigation,
)
- self._view_model.rgb25_view_model.message.connect(
- self.show_rgb25_message,
+ self._view_model.cfa_view_model.message.connect(
+ self.show_cfa_message,
)
self.send_rgb_asset_page.asset_address_value.textChanged.connect(
self.validate_rgb_invoice,
@@ -84,16 +84,16 @@ def setup_ui_connection(self):
self.send_rgb_asset_page.asset_address_value.textChanged.connect(
self.handle_button_enabled,
)
- self._view_model.rgb25_view_model.is_loading.connect(
+ self._view_model.cfa_view_model.is_loading.connect(
self.update_loading_state,
)
self.send_rgb_asset_page.refresh_button.clicked.connect(
self.refresh_asset,
)
- self._view_model.rgb25_view_model.txn_list_loaded.connect(
+ self._view_model.cfa_view_model.txn_list_loaded.connect(
self.set_asset_balance,
)
- self._view_model.rgb25_view_model.txn_list_loaded.connect(
+ self._view_model.cfa_view_model.txn_list_loaded.connect(
self.handle_spendable_balance_validation,
)
self._view_model.estimate_fee_view_model.loading_status.connect(
@@ -106,25 +106,25 @@ def setup_ui_connection(self):
def refresh_asset(self):
"""This method handle the refresh asset on send asset page"""
self.loading_performer = 'REFRESH_BUTTON'
- view_model = self._view_model.rgb25_view_model
+ view_model = self._view_model.cfa_view_model
view_model.on_refresh_click()
self.asset_id = view_model.asset_id
self.asset_name = view_model.asset_name
self.image_path = view_model.image_path
self.asset_type = view_model.asset_type
- self._view_model.rgb25_view_model.get_rgb25_asset_detail(
+ self._view_model.cfa_view_model.get_cfa_asset_detail(
asset_id=self.asset_id, asset_name=self.asset_name, image_path=self.image_path, asset_type=self.asset_type,
)
def set_originating_page(self, asset_type):
"""This method sets the originating page for when closing send asset"""
- if asset_type == 'RGB20':
- self.asset_type = 'RGB20'
+ if asset_type == 'NIA':
+ self.asset_type = 'NIA'
def rgb_asset_page_navigation(self):
"""Navigate to the collectibles asset page."""
self.sidebar = self._view_model.page_navigation.sidebar()
- if self.asset_type == 'RGB20':
+ if self.asset_type == 'NIA':
self.sidebar.my_fungibles.setChecked(True)
self._view_model.page_navigation.fungibles_asset_page()
else:
@@ -146,7 +146,7 @@ def send_rgb_asset_button(self):
DecodeRgbInvoiceRequestModel(invoice=provided_invoice),
)
try:
- self._view_model.rgb25_view_model.on_send_click(
+ self._view_model.cfa_view_model.on_send_click(
amount, decoded_rgb_invoice.recipient_id, decoded_rgb_invoice.transport_endpoints, fee_rate, default_min_confirmation.min_confirmation,
)
# Success toast or indicator can be added here if needed
@@ -161,7 +161,7 @@ def send_rgb_asset_button(self):
description=ERROR_UNEXPECTED.format(str(e.message)),
)
- def show_rgb25_message(self, msg_type: ToastPreset, message: str):
+ def show_cfa_message(self, msg_type: ToastPreset, message: str):
"""Handle show message"""
if msg_type == ToastPreset.ERROR:
ToastManager.error(message)
@@ -254,7 +254,7 @@ def handle_show_message(self, msg_type: ToastPreset, message: str):
def set_asset_balance(self):
"""Set the spendable and total balance of the asset"""
- view_model = self._view_model.rgb25_view_model
+ view_model = self._view_model.cfa_view_model
asset_transactions: ListTransferAssetWithBalanceResponseModel = view_model.txn_list
self.asset_spendable_balance = asset_transactions.asset_balance.spendable
self.send_rgb_asset_page.asset_balance_label_total.setText(
diff --git a/src/views/ui_settings.py b/src/views/ui_settings.py
index cc1cac9a..c17c7058 100644
--- a/src/views/ui_settings.py
+++ b/src/views/ui_settings.py
@@ -44,13 +44,16 @@
from src.utils.constant import BITCOIND_RPC_HOST_MAINNET
from src.utils.constant import BITCOIND_RPC_HOST_REGTEST
from src.utils.constant import BITCOIND_RPC_HOST_TESTNET
+from src.utils.constant import BITCOIND_RPC_HOST_TESTNET4
from src.utils.constant import BITCOIND_RPC_PORT_MAINNET
from src.utils.constant import BITCOIND_RPC_PORT_REGTEST
from src.utils.constant import BITCOIND_RPC_PORT_TESTNET
+from src.utils.constant import BITCOIND_RPC_PORT_TESTNET4
from src.utils.constant import FEE_RATE
from src.utils.constant import INDEXER_URL_MAINNET
from src.utils.constant import INDEXER_URL_REGTEST
from src.utils.constant import INDEXER_URL_TESTNET
+from src.utils.constant import INDEXER_URL_TESTNET4
from src.utils.constant import IRIS_WALLET_TRANSLATIONS_CONTEXT
from src.utils.constant import LN_INVOICE_EXPIRY_TIME
from src.utils.constant import LN_INVOICE_EXPIRY_TIME_UNIT
@@ -59,6 +62,7 @@
from src.utils.constant import PROXY_ENDPOINT_MAINNET
from src.utils.constant import PROXY_ENDPOINT_REGTEST
from src.utils.constant import PROXY_ENDPOINT_TESTNET
+from src.utils.constant import PROXY_ENDPOINT_TESTNET4
from src.utils.constant import WALLET_PASSWORD_KEY
from src.utils.helpers import load_stylesheet
from src.utils.info_message import INFO_VALIDATION_OF_NODE_PASSWORD_AND_KEYRING_ACCESS
@@ -68,6 +72,9 @@
from src.views.components.header_frame import HeaderFrame
from src.views.components.keyring_error_dialog import KeyringErrorDialog
from src.views.components.loading_screen import LoadingTranslucentScreen
+from src.views.components.settings_helpers import check_keyring_state_for_password
+from src.views.components.settings_helpers import set_endpoint_based_on_network
+from src.views.components.settings_helpers import set_frame_content
from src.views.components.toast import ToastManager
from src.views.components.toggle_switch import ToggleSwitch
from src.views.ui_restore_mnemonic import RestoreMnemonicWidget
@@ -91,7 +98,7 @@ def __init__(self, view_model):
self.announce_address = ANNOUNCE_ADDRESS
self.announce_alias = ANNOUNCE_ALIAS
self.min_confirmation = MIN_CONFIRMATION
- self._set_endpoint_based_on_network()
+ self.indexer_url, self.proxy_endpoint, self.bitcoind_host, self.bitcoind_port = set_endpoint_based_on_network()
self.current_network = None
self.grid_layout = QGridLayout(self)
self.grid_layout.setSpacing(0)
@@ -661,7 +668,7 @@ def _set_expiry_time(self):
def _set_indexer_url(self):
"""Set the default indexer url based on user input. """
- password = self._check_keyring_state()
+ password = check_keyring_state_for_password(self, self._view_model)
if password:
self._view_model.setting_view_model.check_indexer_url_endpoint(
self.set_indexer_url_frame.input_value.text(), password,
@@ -669,7 +676,7 @@ def _set_indexer_url(self):
def _set_proxy_endpoint(self):
"""Set the default proxy endpoint based on user input."""
- password = self._check_keyring_state()
+ password = check_keyring_state_for_password(self, self._view_model)
if password:
self._view_model.setting_view_model.check_proxy_endpoint(
self.set_proxy_endpoint_frame.input_value.text(), password,
@@ -677,7 +684,7 @@ def _set_proxy_endpoint(self):
def _set_bitcoind_host(self):
""" Set the default bitcoind host based on user input."""
- password = self._check_keyring_state()
+ password = check_keyring_state_for_password(self, self._view_model)
if password:
self._view_model.setting_view_model.set_bitcoind_host(
self.set_bitcoind_rpc_host_frame.input_value.text(), password,
@@ -685,7 +692,7 @@ def _set_bitcoind_host(self):
def _set_bitcoind_port(self):
"""Set the default bitcoind port based on user input."""
- password = self._check_keyring_state()
+ password = check_keyring_state_for_password(self, self._view_model)
if password:
self._view_model.setting_view_model.set_bitcoind_port(
int(self.set_bitcoind_rpc_port_frame.input_value.text()), password,
@@ -693,7 +700,7 @@ def _set_bitcoind_port(self):
def _set_announce_address(self):
"""Set the default announce address based on user input."""
- password = self._check_keyring_state()
+ password = check_keyring_state_for_password(self, self._view_model)
if password:
self._view_model.setting_view_model.set_announce_address(
self.set_announce_address_frame.input_value.text(), password,
@@ -701,7 +708,7 @@ def _set_announce_address(self):
def _set_announce_alias(self):
"""Set the default announce alias based on user input."""
- password = self._check_keyring_state()
+ password = check_keyring_state_for_password(self, self._view_model)
if password:
self._view_model.setting_view_model.set_announce_alias(
self.set_announce_alias_frame.input_value.text(), password,
@@ -834,112 +841,21 @@ def _update_loading_state(self, is_loading: bool):
else:
frame.save_button.stop_loading()
- def _check_keyring_state(self):
- """Checks the keyring status and retrieves the wallet password, either
- from secure storage if the keyring is disabled or via a user prompt
- through a mnemonic dialog if enabled."""
- keyring_status = SettingRepository.get_keyring_status()
- if keyring_status is False:
- network: NetworkEnumModel = SettingRepository.get_wallet_network()
- password: str = get_value(WALLET_PASSWORD_KEY, network.value)
- return password
- if keyring_status is True:
- mnemonic_dialog = RestoreMnemonicWidget(
- parent=self, view_model=self._view_model, origin_page='setting_card', mnemonic_visibility=False,
- )
- mnemonic_dialog.mnemonic_detail_text_label.setText(
- QCoreApplication.translate(
- IRIS_WALLET_TRANSLATIONS_CONTEXT, 'lock_unlock_password_required', None,
- ),
- )
- mnemonic_dialog.mnemonic_detail_text_label.setFixedHeight(40)
- result = mnemonic_dialog.exec()
- if result == QDialog.Accepted:
- password = mnemonic_dialog.password_input.text()
- return password
- return None
-
- def _set_endpoint_based_on_network(self):
- """Sets various endpoints and configuration parameters
- based on the currently selected wallet network."""
- network_config_map = {
- NetworkEnumModel.MAINNET: (INDEXER_URL_MAINNET, PROXY_ENDPOINT_MAINNET, BITCOIND_RPC_HOST_MAINNET, BITCOIND_RPC_PORT_MAINNET),
- NetworkEnumModel.TESTNET: (INDEXER_URL_TESTNET, PROXY_ENDPOINT_TESTNET, BITCOIND_RPC_HOST_TESTNET, BITCOIND_RPC_PORT_TESTNET),
- NetworkEnumModel.REGTEST: (INDEXER_URL_REGTEST, PROXY_ENDPOINT_REGTEST, BITCOIND_RPC_HOST_REGTEST, BITCOIND_RPC_PORT_REGTEST),
- }
- stored_network: NetworkEnumModel = SettingRepository.get_wallet_network()
- config = network_config_map.get(stored_network)
- if config:
- self.indexer_url, self.proxy_endpoint, self.bitcoind_host, self.bitcoind_port = config
- else:
- raise ValueError(f"Unsupported network type: {stored_network}")
-
- def _set_frame_content(self, frame, input_value, validator=None, time_unit_combobox=None, suggestion_desc=None):
- """
- Sets the content for a given frame, configuring the input value and optionally hiding/showing other widgets.
- """
- if isinstance(input_value, float) and input_value.is_integer():
- input_value = int(input_value)
-
- frame.input_value.setText(str(input_value))
- frame.input_value.setPlaceholderText(str(input_value))
- frame.input_value.setValidator(validator)
-
- if not suggestion_desc:
- frame.suggestion_desc.hide()
-
- if time_unit_combobox:
- index = time_unit_combobox.findText(
- self.expiry_time_unit, Qt.MatchFixedString,
- )
- if index != -1:
- time_unit_combobox.setCurrentIndex(index)
- else:
- frame.time_unit_combobox.hide()
-
- frame.input_value.textChanged.connect(
- lambda: self._update_save_button(frame, input_value),
- )
-
- if time_unit_combobox:
- frame.time_unit_combobox.currentTextChanged.connect(
- lambda: self._update_save_button(
- frame, input_value, time_unit_combobox,
- ),
- )
-
- # Initial call to set the correct button state
- self._update_save_button(frame, input_value, time_unit_combobox)
-
- def _update_save_button(self, frame, input_value, time_unit_combobox=None):
- """
- Updates the state of the save button based on input value and time unit changes.
- """
- current_text = frame.input_value.text().strip()
- current_unit = frame.time_unit_combobox.currentText() if time_unit_combobox else ''
-
- time_unit_changed = current_unit != self.expiry_time_unit
-
- if current_text and (current_text != str(input_value) or (time_unit_combobox and time_unit_changed)):
- frame.save_button.setDisabled(False)
- else:
- frame.save_button.setDisabled(True)
-
def handle_fee_rate_frame(self):
"""Handle the frame for setting the fee rate."""
- self._set_frame_content(
+ set_frame_content(
self.set_fee_rate_frame,
self.fee_rate,
- QIntValidator(),
+ validator=QIntValidator(),
suggestion_desc=self.set_fee_rate_frame.suggestion_desc,
)
def handle_expiry_time_frame(self):
"""Handle the frame for setting the expiry time and unit."""
- self._set_frame_content(
+ set_frame_content(
self.set_expiry_time_frame,
self.expiry_time,
- QIntValidator(),
+ validator=QIntValidator(),
suggestion_desc=self.set_expiry_time_frame.suggestion_desc,
time_unit_combobox=self.set_expiry_time_frame.time_unit_combobox,
@@ -950,50 +866,50 @@ def handle_expiry_time_frame(self):
def handle_indexer_url_frame(self):
"""Handle the frame for setting the indexer url."""
- self._set_frame_content(
+ set_frame_content(
self.set_indexer_url_frame,
self.indexer_url,
)
def handle_proxy_endpoint_frame(self):
"""Handle the frame for setting the proxy endpoint."""
- self._set_frame_content(
+ set_frame_content(
self.set_proxy_endpoint_frame,
self.proxy_endpoint,
)
def handle_bitcoind_host_frame(self):
"""Handle the frame for setting the bitcoind host."""
- self._set_frame_content(
+ set_frame_content(
self.set_bitcoind_rpc_host_frame,
self.bitcoind_host,
)
def handle_bitcoind_port_frame(self):
"""Handle the frame for setting the bitcoind port."""
- self._set_frame_content(
+ set_frame_content(
self.set_bitcoind_rpc_port_frame,
self.bitcoind_port,
- QIntValidator(),
+ validator=QIntValidator(),
)
def handle_announce_address_frame(self):
"""Handle the frame for setting the announce address."""
- self._set_frame_content(
+ set_frame_content(
self.set_announce_address_frame,
self.announce_address,
)
def handle_announce_alias_frame(self):
"""Handle the frame for setting the announce alias."""
- self._set_frame_content(
+ set_frame_content(
self.set_announce_alias_frame,
self.announce_alias,
)
def handle_minimum_confirmation_frame(self):
"""Handle the frame for setting the minimum confirmation."""
- self._set_frame_content(
+ set_frame_content(
self.set_minimum_confirmation_frame,
self.min_confirmation,
QIntValidator(),
diff --git a/src/views/ui_sidebar.py b/src/views/ui_sidebar.py
index 060a931f..56eeaa0c 100644
--- a/src/views/ui_sidebar.py
+++ b/src/views/ui_sidebar.py
@@ -200,7 +200,7 @@ def setup_ui_connections(self):
)
self.help.clicked.connect(self._view_model.page_navigation.help_page)
self.receive_asset_button.clicked.connect(
- lambda: self._view_model.page_navigation.receive_rgb25_page(
+ lambda: self._view_model.page_navigation.receive_cfa_page(
params=AssetDataModel(
asset_type=self.get_checked_button_translation_key(),
),
diff --git a/src/views/ui_view_unspent_list.py b/src/views/ui_view_unspent_list.py
index 2f5dc138..a89f2d78 100644
--- a/src/views/ui_view_unspent_list.py
+++ b/src/views/ui_view_unspent_list.py
@@ -33,7 +33,6 @@
from src.viewmodels.main_view_model import MainViewModel
from src.views.components.header_frame import HeaderFrame
from src.views.components.loading_screen import LoadingTranslucentScreen
-from src.views.components.toast import ToastManager
class ViewUnspentList(QWidget):
@@ -325,4 +324,5 @@ def get_image_path(self, _list):
NetworkEnumModel.MAINNET.value: ':/assets/bitcoin.png',
NetworkEnumModel.REGTEST.value: ':/assets/regtest_bitcoin.png',
NetworkEnumModel.TESTNET.value: ':/assets/testnet_bitcoin.png',
+ NetworkEnumModel.TESTNET4.value: ':/assets/testnet_bitcoin.png',
}.get(self.network.value)
diff --git a/src/views/ui_wallet_or_transfer_selection.py b/src/views/ui_wallet_or_transfer_selection.py
index 4cc44ac3..b3eb5f67 100644
--- a/src/views/ui_wallet_or_transfer_selection.py
+++ b/src/views/ui_wallet_or_transfer_selection.py
@@ -346,13 +346,13 @@ def handle_frame_click(self, _id):
elif _id == TransferType.ON_CHAIN.value:
# Navigate to the appropriate page based on the transfer type
if transfer_type == TransferStatusEnumModel.RECEIVE.value:
- self._view_model.page_navigation.receive_rgb25_page(
+ self._view_model.page_navigation.receive_cfa_page(
params=AssetDataModel(
asset_type=self.asset_type, asset_id=self._params.asset_id,
),
)
elif transfer_type == TransferStatusEnumModel.SEND.value:
- self._view_model.page_navigation.send_rgb25_page()
+ self._view_model.page_navigation.send_cfa_page()
elif transfer_type == TransferStatusEnumModel.SEND_BTC.value:
self._view_model.page_navigation.send_bitcoin_page()
elif transfer_type == TransferStatusEnumModel.RECEIVE_BTC.value:
@@ -415,7 +415,7 @@ def close_button_navigation(self):
self._params.back_page_navigation()
if self._params.rgb_asset_page_load_model is not None:
- self._view_model.rgb25_view_model.asset_info.emit(
+ self._view_model.cfa_view_model.asset_info.emit(
self._params.rgb_asset_page_load_model.asset_id,
self._params.rgb_asset_page_load_model.asset_name,
self._params.rgb_asset_page_load_model.image_path,
diff --git a/unit_tests/service_test_resources/mocked_fun_return_values/asset_detail_page_service.py b/unit_tests/service_test_resources/mocked_fun_return_values/asset_detail_page_service.py
index 9ee5593d..cc203d2a 100644
--- a/unit_tests/service_test_resources/mocked_fun_return_values/asset_detail_page_service.py
+++ b/unit_tests/service_test_resources/mocked_fun_return_values/asset_detail_page_service.py
@@ -3,8 +3,10 @@
"""
from __future__ import annotations
+from src.model.enums.enums_model import AssignmentEnumModel
from src.model.enums.enums_model import TransferStatusEnumModel
from src.model.rgb_model import AssetBalanceResponseModel
+from src.model.rgb_model import AssignmentModel
from src.model.rgb_model import ListTransferAssetResponseModel
from src.model.rgb_model import ListTransferAssetWithBalanceResponseModel
from src.model.rgb_model import TransportEndpoint
@@ -15,7 +17,14 @@
created_at=1717565849,
updated_at=1717565849,
status='Settled',
- amount=1600,
+ assignments=[
+ AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=1600,
+ ),
+ ],
+ requested_assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=1600,
+ ),
kind='Issuance',
)
@@ -24,7 +33,14 @@
created_at=1717566312,
updated_at=1717567082,
status='Settled',
- amount=1000,
+ requested_assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=1000,
+ ),
+ assignments=[
+ AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=1000,
+ ),
+ ],
kind='Send',
txid='5872b8b5333054e1e3768d897d9d0ccceb0e5a9388f2f83649241e8d2125a6ae',
recipient_id='utxob:2okFKi2-8Ex84DQNt-jzCHrU4HA-vozR9aDut-VEdc5yBUX-Ktfqhk8',
@@ -43,7 +59,10 @@
created_at=1717566191,
updated_at=1717567096,
status='Settled',
- amount=42,
+ requested_assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=42,
+ ),
+ assignments=[AssignmentModel(type=AssignmentEnumModel.FUNGIBLE, value=42)],
kind='ReceiveBlind',
txid='5872b8b5333054e1e3768d897d9d0ccceb0e5a9388f2f83649241e8d2125a6ae',
recipient_id='utxob:2okFKi2-8Ex84DQNt-jzCHrU4HA-vozR9aDut-VEdc5yBUX-Ktfqhk8',
@@ -63,7 +82,10 @@
created_at=1717566191,
updated_at=1717567096,
status='Settled',
- amount=42,
+ requested_assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=42,
+ ),
+ assignments=[AssignmentModel(type=AssignmentEnumModel.FUNGIBLE, value=42)],
kind='ReceiveWitness',
txid='5872b8b5333054e1e3768d897d9d0ccceb0e5a9388f2f83649241e8d2125a6ae',
recipient_id='utxob:2okFKi2-8Ex84DQNt-jzCHrU4HA-vozR9aDut-VEdc5yBUX-Ktfqhk8',
@@ -83,7 +105,10 @@
created_at=1717566191,
updated_at=1717567096,
status='Settled',
- amount=42,
+ requested_assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=42,
+ ),
+ assignments=[AssignmentModel(type=AssignmentEnumModel.FUNGIBLE, value=42)],
kind='Invalid',
txid='5872b8b5333054e1e3768d897d9d0ccceb0e5a9388f2f83649241e8d2125a6ae',
recipient_id='utxob:2okFKi2-8Ex84DQNt-jzCHrU4HA-vozR9aDut-VEdc5yBUX-Ktfqhk8',
diff --git a/unit_tests/service_test_resources/mocked_fun_return_values/faucet_service.py b/unit_tests/service_test_resources/mocked_fun_return_values/faucet_service.py
index a7cb95dd..21b8831f 100644
--- a/unit_tests/service_test_resources/mocked_fun_return_values/faucet_service.py
+++ b/unit_tests/service_test_resources/mocked_fun_return_values/faucet_service.py
@@ -67,7 +67,8 @@
'eventual_close_fees_sat': 1,
'pending_outbound_payments_sat': 1,
'num_peers': 1,
- 'onchain_pubkey': 'tpubDDqiQYzNMGsKVVmWYG4FCPPcbd5S4uW9u7a6zUFkgbh16VmFTvsaNyo37mAgkCku6jStBcU8VaLxG2SE7ab2sEwwagjfbS8U9H82BadjKR1',
+ 'account_xpub_vanilla': 'tpubDDqiQYzNMGsKVVmWYG4FCPPcbd5S4uW9u7a6zUFkgbh16VmFTvsaNyo37mAgkCku6jStBcU8VaLxG2SE7ab2sEwwagjfbS8U9H82BadjKR1',
+ 'account_xpub_colored': 'tpubDDqiQYzNMGsKVVmWYG4FCPPcbd5S4uW9u7a6zUFkgbh16VmFTvsaNyo37mAgkCku6jStBcU8VaLxG2SE7ab2sEwwagjfbS8U9H82BadjKR1',
'max_media_upload_size_mb': 5,
'rgb_htlc_min_msat': 1,
'rgb_channel_capacity_min_sat': 1,
diff --git a/unit_tests/service_test_resources/mocked_fun_return_values/issue_asset_service.py b/unit_tests/service_test_resources/mocked_fun_return_values/issue_asset_service.py
index cbe3f6ac..cb03e24f 100644
--- a/unit_tests/service_test_resources/mocked_fun_return_values/issue_asset_service.py
+++ b/unit_tests/service_test_resources/mocked_fun_return_values/issue_asset_service.py
@@ -38,7 +38,6 @@
example_data_of_issue_asset_api = {
'asset_id': 'rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- 'asset_iface': 'RGB20',
'name': 'Collectible',
'details': 'asset details',
'precision': 0,
diff --git a/unit_tests/service_test_resources/mocked_fun_return_values/main_asset_service.py b/unit_tests/service_test_resources/mocked_fun_return_values/main_asset_service.py
index c48644f2..6f823fcb 100644
--- a/unit_tests/service_test_resources/mocked_fun_return_values/main_asset_service.py
+++ b/unit_tests/service_test_resources/mocked_fun_return_values/main_asset_service.py
@@ -13,7 +13,6 @@
mock_nia_asset = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- asset_iface='RGB20',
ticker='USDT',
name='Tether',
details='asset details',
@@ -30,7 +29,6 @@
mock_uda_asset = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- asset_iface='RGB20',
ticker='UNI',
name='Unique',
details='asset details',
@@ -74,7 +72,6 @@
mock_cfa_asset = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- asset_iface='RGB20',
name='Collectible',
details='asset details',
precision=0,
@@ -94,7 +91,6 @@
mock_cfa_asset_when_wallet_type_connect = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- asset_iface='RGB20',
name='Collectible',
details='asset details',
precision=0,
@@ -133,7 +129,8 @@
eventual_close_fees_sat=892,
pending_outbound_payments_sat=7852,
num_peers=0,
- onchain_pubkey='02270dadcd6e7ba0ef707dac72acccae1a3607453a8dd2aef36ff3be4e0d31f043',
+ account_xpub_vanilla='02270dadcd6e7ba0ef707dac72acccae1a3607453a8dd2aef36ff3be4e0d31f043',
+ account_xpub_colored='02270dadcd6e7ba0ef707dac72acccae1a3607453a8dd2aef36ff3be4e0d31f043',
max_media_upload_size_mb=5,
rgb_htlc_min_msat=1,
rgb_channel_capacity_min_sat=1,
@@ -150,7 +147,6 @@
mock_nia_asset_exhausted_asset = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k333',
- asset_iface='RGB20',
ticker='TTK',
name='super man',
details='asset details',
@@ -167,7 +163,6 @@
mock_uda_asset_exhausted_asset = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- asset_iface='RGB20',
ticker='UNI',
name='Unique',
details='asset details',
@@ -211,7 +206,6 @@
mock_cfa_asset_exhausted_asset = AssetModel(
asset_id='rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- asset_iface='RGB20',
name='Collectible',
details='asset details',
precision=0,
diff --git a/unit_tests/tests/conftest.py b/unit_tests/tests/conftest.py
index 9a19ada5..ea17a45f 100644
--- a/unit_tests/tests/conftest.py
+++ b/unit_tests/tests/conftest.py
@@ -15,6 +15,11 @@
"""
from __future__ import annotations
+import gc
+import os
+import tempfile
+
+import keyring
import pytest
from PySide6.QtCore import QThread
from PySide6.QtWidgets import QApplication
@@ -34,10 +39,7 @@ def qt_app():
def mock_qthread_start(mocker):
"""
Automatically mock QThread.start() to prevent actual thread creation.
-
This prevents 'QThread: Destroyed while thread is still running' errors
- that cause pytest-xdist to fail with 'ValueError: list.remove(x): x not in list'.
-
Tests that need actual threading should call thread.run() directly instead.
"""
@@ -50,15 +52,59 @@ def mock_start_wrapper(self):
@pytest.fixture(autouse=True)
-def mock_toast_manager(mocker, request):
+def mock_qthreadpool(mocker):
+ """
+ Automatically mock QThreadPool to prevent actual thread creation.
+ This prevents Qt code execution inside mocks during parallel tests.
+ """
+ mock_pool = mocker.Mock()
+ # Mock globalInstance to return our mock pool
+ mocker.patch(
+ 'PySide6.QtCore.QThreadPool.globalInstance',
+ return_value=mock_pool,
+ )
+ # Mock constructor to return our mock pool
+ mocker.patch('PySide6.QtCore.QThreadPool', return_value=mock_pool)
+ # Ensure start does nothing
+ mock_pool.start.side_effect = lambda runnable: None
+
+
+@pytest.fixture(autouse=True)
+def mock_timer(mocker, request):
+ """
+ Prevent HeaderFrameViewModel from starting infinite thread loops via QTimer.
+ Skip for header_frame_view_model_test.py so it can test the actual logic.
+ """
+ if 'header_frame_view_model_test.py' in str(request.fspath):
+ return
+
+ mocker.patch(
+ 'src.viewmodels.header_frame_view_model.HeaderFrameViewModel.start_network_check',
+ )
+
+
+@pytest.fixture(autouse=True)
+def gc_collect():
+ """Force garbage collection after each test to prevent QObject accumulation."""
+ yield
+ gc.collect()
+
+
+@pytest.fixture(autouse=True)
+def mock_network_calls(mocker):
"""
- Automatically mock ToastManager methods to prevent 'Main window not set' errors.
+ Mock socket.create_connection to prevent real network calls during tests.
+ """
+ mock_socket = mocker.MagicMock()
+ mocker.patch('socket.create_connection', return_value=mock_socket)
- ToastManager requires a main window to be set before creating toasts,
- which is not available in unit tests. This fixture mocks all toast methods
- to prevent the ValueError from being raised during tests.
- Skip mocking for toast_test.py since those tests specifically test ToastManager.
+@pytest.fixture(autouse=True)
+def mock_toast_manager(mocker, request):
+ """
+ Mock ToastManager methods to isolate tests from UI dependencies.
+ ToastManager requires a main window, which is unavailable in unit tests.
+ Skipped for toast_test.py to allow direct ToastManager testing.
"""
# Skip mocking for toast_test.py which specifically tests ToastManager
if 'toast_test.py' in str(request.fspath):
@@ -68,3 +114,65 @@ def mock_toast_manager(mocker, request):
mocker.patch('src.views.components.toast.ToastManager.success')
mocker.patch('src.views.components.toast.ToastManager.info')
mocker.patch('src.views.components.toast.ToastManager.warning')
+
+# ---------------- Global Safety Fixtures -----------------
+
+
+@pytest.fixture(scope='session', autouse=True)
+def _isolate_user_dirs():
+ """Redirect user data/config/cache dirs to a temporary location for the entire test session."""
+ with tempfile.TemporaryDirectory(prefix='iris_wallet_tests_') as td:
+ os.environ.setdefault('HOME', td)
+ os.environ.setdefault(
+ 'XDG_DATA_HOME', os.path.join(td, '.local', 'share'),
+ )
+ os.environ.setdefault('XDG_CONFIG_HOME', os.path.join(td, '.config'))
+ os.environ.setdefault('XDG_CACHE_HOME', os.path.join(td, '.cache'))
+ os.environ.setdefault(
+ 'IRIS_WALLET_DATA_DIR',
+ os.path.join(td, 'iris-wallet-vault'),
+ )
+ yield
+
+
+@pytest.fixture(autouse=True)
+def _mock_keyring(monkeypatch):
+ """Ensure keyring operations are in-memory and never hit the system keychain."""
+
+ _store: dict[tuple[str, str], str] = {}
+
+ def _get_password(service_name: str, username: str) -> str | None:
+ return _store.get((service_name, username))
+
+ def _set_password(service_name: str, username: str, password: str) -> None:
+ _store[(service_name, username)] = password
+
+ def _delete_password(service_name: str, username: str) -> None:
+ _store.pop((service_name, username), None)
+
+ monkeypatch.setattr(keyring, 'get_password', _get_password, raising=False)
+ monkeypatch.setattr(keyring, 'set_password', _set_password, raising=False)
+ monkeypatch.setattr(
+ keyring, 'delete_password',
+ _delete_password, raising=False,
+ )
+
+ if hasattr(keyring, 'set_keyring'):
+ class _InMemoryBackend: # pragma: no cover
+ priority = 1
+
+ def get_password(self, service: str, username: str) -> str | None:
+ """Get password from in-memory store."""
+ return _get_password(service, username)
+
+ def set_password(self, service: str, username: str, password: str) -> None:
+ """Set password in in-memory store."""
+ return _set_password(service, username, password)
+
+ def delete_password(self, service: str, username: str) -> None:
+ """Delete password from in-memory store."""
+ return _delete_password(service, username)
+ try:
+ keyring.set_keyring(_InMemoryBackend())
+ except Exception:
+ pass
diff --git a/unit_tests/tests/service_tests/asset_detail_page_test/get_asset_transactions_test.py b/unit_tests/tests/service_tests/asset_detail_page_test/get_asset_transactions_test.py
index 3325f049..ae572a92 100644
--- a/unit_tests/tests/service_tests/asset_detail_page_test/get_asset_transactions_test.py
+++ b/unit_tests/tests/service_tests/asset_detail_page_test/get_asset_transactions_test.py
@@ -6,12 +6,9 @@
from src.data.service.asset_detail_page_services import AssetDetailPageService
from src.model.payments_model import ListPaymentResponseModel
-from src.model.rgb_model import AssetBalanceResponseModel
from src.model.rgb_model import AssetIdModel
from src.model.rgb_model import ListOnAndOffChainTransfersWithBalance
-from src.model.rgb_model import ListTransferAssetWithBalanceResponseModel
from src.model.rgb_model import ListTransfersRequestModel
-from src.model.rgb_model import TransferAsset
from src.utils.custom_exception import CommonException
from src.utils.custom_exception import ServiceOperationException
from unit_tests.repository_fixture.rgb_repository_mock import mock_get_asset_balance
diff --git a/unit_tests/tests/service_tests/asset_detail_page_test/get_single_asset_transaction_test.py b/unit_tests/tests/service_tests/asset_detail_page_test/get_single_asset_transaction_test.py
index 4fedfdd1..c42b5c0f 100644
--- a/unit_tests/tests/service_tests/asset_detail_page_test/get_single_asset_transaction_test.py
+++ b/unit_tests/tests/service_tests/asset_detail_page_test/get_single_asset_transaction_test.py
@@ -47,7 +47,7 @@ def test_get_single_asset_transaction_by_txid(mocked_get_asset_transaction_servi
)
assert result == mocked_data_when_transaction_type_send
assert result.idx == mocked_data_when_transaction_type_send.idx
- assert result.amount == mocked_data_when_transaction_type_send.amount
+ assert result.assignments[0].value == mocked_data_when_transaction_type_send.assignments[0].value
assert result.txid == mocked_data_when_transaction_type_send.txid
assert (
result.created_at_date == mocked_data_when_transaction_type_send.created_at_date
@@ -104,7 +104,7 @@ def test_get_single_asset_transaction_by_idx(mocked_get_asset_transaction_servic
)
assert result == mocked_data_when_transaction_type_send
assert result.idx == mocked_data_when_transaction_type_send.idx
- assert result.amount == mocked_data_when_transaction_type_send.amount
+ assert result.assignments[0].value == mocked_data_when_transaction_type_send.assignments[0].value
assert result.txid == mocked_data_when_transaction_type_send.txid
assert (
result.created_at_date == mocked_data_when_transaction_type_send.created_at_date
diff --git a/unit_tests/tests/service_tests/backup_test.py b/unit_tests/tests/service_tests/backup_test.py
index df2f61ba..186148d3 100644
--- a/unit_tests/tests/service_tests/backup_test.py
+++ b/unit_tests/tests/service_tests/backup_test.py
@@ -52,24 +52,33 @@ def teardown_directory_after_test():
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
-@patch('src.utils.local_store.local_store.get_path')
+@patch('src.data.service.backup_service.app_paths')
@patch('src.data.service.backup_service.BackupService.backup_file_exists')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.backup')
+@patch('src.data.service.backup_service.write_ln_node_commit_id_file')
@patch('src.data.service.backup_service.GoogleDriveManager')
-def test_backup(mock_google_drive_manager, mock_backup, mock_backup_file_exits, mock_get_path, mock_get_hashed_mnemonic, setup_directory):
+def test_backup(mock_google_drive_manager, mock_write_commit_file, mock_backup, mock_backup_file_exits, mock_app_paths, mock_get_hashed_mnemonic, setup_directory):
"""Case 1 : Test backup service"""
- test_dir, _ = setup_directory
+ test_dir, backup_dir = setup_directory
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_get_path.return_value = test_dir
+ mock_app_paths.backup_folder_path = backup_dir
+ mock_app_paths.iriswallet_temp_folder_path = test_dir
mock_backup_instance = MagicMock()
mock_backup.return_value = None
mock_backup_file_exits.return_value = True
mock_backup_instance.return_value = None
mock_google_drive_manager.return_value = mock_backup_instance
- mock_backup_instance.upload_to_drive.return_value = True
+ # Two uploads: backup file and commit id file
+ mock_backup_instance.upload_to_drive.side_effect = [True, True]
+ # commit id file path and name
+ mock_write_commit_file.return_value = (
+ os.path.join(
+ backup_dir, 'e23ddff3cc.commit',
+ ), 'e23ddff3cc.commit',
+ )
result = BackupService.backup(mock_valid_mnemonic, mock_password)
@@ -80,40 +89,53 @@ def test_backup(mock_google_drive_manager, mock_backup, mock_backup_file_exits,
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
-@patch('src.utils.local_store.local_store.get_path')
+@patch('src.data.service.backup_service.app_paths')
@patch('src.data.service.backup_service.BackupService.backup_file_exists')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.backup')
+@patch('src.data.service.backup_service.write_ln_node_commit_id_file')
@patch('src.data.service.backup_service.GoogleDriveManager')
-def test_backup_when_backup_file_not_exits(mock_google_drive_manager, mock_backup, mock_backup_file_exits, mock_get_path, mock_get_hashed_mnemonic, setup_directory):
+def test_backup_when_backup_file_not_exits(
+ mock_google_drive_manager, mock_write_commit_file,
+ mock_backup, mock_backup_file_exits, mock_app_paths, mock_get_hashed_mnemonic, setup_directory,
+):
"""Case 2: When backup not exits after api call"""
- test_dir, _ = setup_directory
+ test_dir, backup_dir = setup_directory
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_get_path.return_value = test_dir
+ mock_app_paths.backup_folder_path = backup_dir
+ mock_app_paths.iriswallet_temp_folder_path = test_dir
mock_backup_instance = MagicMock()
mock_backup.return_value = None
mock_backup_file_exits.return_value = False
mock_backup_instance.return_value = None
mock_google_drive_manager.return_value = mock_backup_instance
- mock_backup_instance.upload_to_drive.return_value = True
+ mock_backup_instance.upload_to_drive.side_effect = [True, True]
+ mock_write_commit_file.return_value = (
+ os.path.join(
+ backup_dir, 'e23ddff3cc.commit',
+ ), 'e23ddff3cc.commit',
+ )
error_message = ERROR_BACKUP_FILE_NOT_EXITS
with pytest.raises(CommonException, match=error_message):
BackupService.backup(mock_valid_mnemonic, mock_password)
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
-@patch('src.utils.local_store.local_store.get_path')
+@patch('src.data.service.backup_service.app_paths')
@patch('src.data.service.backup_service.BackupService.backup_file_exists')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.backup')
-def test_backup_no_mnemonic(mock_backup, mock_backup_file_exits, mock_get_path, mock_get_hashed_mnemonic):
+def test_backup_no_mnemonic(mock_backup, mock_backup_file_exits, mock_app_paths, mock_get_hashed_mnemonic):
"""Case 3 : Test backup service with missing mnemonic"""
# Setup mocks
mock_get_hashed_mnemonic.side_effect = CommonException(
ERROR_UNABLE_GET_MNEMONIC,
)
- mock_get_path.return_value = os.path.join(
+ mock_app_paths.backup_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
os.path.dirname(__file__), 'some_path',
)
mock_backup.return_value = None
@@ -128,13 +150,16 @@ def test_backup_no_mnemonic(mock_backup, mock_backup_file_exits, mock_get_path,
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
-@patch('src.utils.local_store.local_store.get_path')
-def test_backup_no_password(mock_get_path, mock_get_hashed_mnemonic):
+@patch('src.data.service.backup_service.app_paths')
+def test_backup_no_password(mock_app_paths, mock_get_hashed_mnemonic):
"""Case 4 : Test backup service with missing password"""
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_get_path.return_value = os.path.join(
+ mock_app_paths.backup_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
os.path.dirname(__file__), 'some_path',
)
@@ -147,15 +172,22 @@ def test_backup_no_password(mock_get_path, mock_get_hashed_mnemonic):
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
+@patch('src.data.service.backup_service.app_paths')
@patch('src.data.service.backup_service.BackupService.backup_file_exists')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.backup')
-def test_backup_no_hashed_value(mock_backup, mock_backup_file_exits, mock_get_hashed_mnemonic):
+def test_backup_no_hashed_value(mock_backup, mock_backup_file_exits, mock_app_paths, mock_get_hashed_mnemonic):
"""Case 5 : Test backup service with missing hashed value"""
# Setup mocks
mock_get_hashed_mnemonic.side_effect = CommonException(
ERROR_UNABLE_TO_GET_HASHED_MNEMONIC,
)
+ mock_app_paths.backup_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
mock_backup.return_value = None
mock_backup_file_exits.return_value = True
diff --git a/unit_tests/tests/service_tests/main_asset_page_service_test.py b/unit_tests/tests/service_tests/main_asset_page_service_test.py
index cea0b506..2b638385 100644
--- a/unit_tests/tests/service_tests/main_asset_page_service_test.py
+++ b/unit_tests/tests/service_tests/main_asset_page_service_test.py
@@ -2,12 +2,6 @@
# pylint: disable=redefined-outer-name,unused-argument,too-many-arguments,unused-import
from __future__ import annotations
-from unittest import mock
-
-import pytest
-from pytest_mock import mocker
-
-from src.data.repository.rgb_repository import RgbRepository
from src.data.service.main_asset_page_service import MainAssetPageDataService
from src.model.common_operation_model import MainPageDataResponseModel
from src.model.enums.enums_model import FilterAssetEnumModel
@@ -15,7 +9,6 @@
from src.model.rgb_model import FilterAssetRequestModel
from src.model.rgb_model import RefreshTransferResponseModel
from src.model.setting_model import IsHideExhaustedAssetEnabled
-from src.utils.custom_exception import CommonException
from unit_tests.repository_fixture.btc_repository_mock import mock_get_btc_balance
from unit_tests.repository_fixture.rgb_repository_mock import mock_get_asset
from unit_tests.repository_fixture.rgb_repository_mock import mock_refresh_transfer
diff --git a/unit_tests/tests/service_tests/restore_test.py b/unit_tests/tests/service_tests/restore_test.py
index a9ad0639..81952bb0 100644
--- a/unit_tests/tests/service_tests/restore_test.py
+++ b/unit_tests/tests/service_tests/restore_test.py
@@ -53,23 +53,29 @@ def teardown_directory_after_test():
# Test function
+@patch('src.data.service.restore_service.COMPATIBLE_RLN_NODE_COMMITS', ['abc'])
+@patch('src.data.service.restore_service.read_ln_node_commit_id_file')
+@patch('src.data.service.restore_service.SettingRepository.set_rln_node_commit_id')
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
@patch('src.utils.local_store.local_store.get_path')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.restore')
@patch('src.data.service.restore_service.GoogleDriveManager')
-def test_restore(mock_google_drive_manager, mock_restore, mock_get_path, mock_get_hashed_mnemonic, setup_directory):
+def test_restore(mock_google_drive_manager, mock_restore, mock_app_paths, mock_get_hashed_mnemonic, mock_set_commit_id, mock_read_commit_id, setup_directory):
"""Case 1: Test restore service"""
- test_dir, _ = setup_directory
+ test_dir, restore_dir = setup_directory
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_get_path.return_value = test_dir
+ mock_app_paths.restore_folder_path = restore_dir
+ mock_app_paths.iriswallet_temp_folder_path = test_dir
+ mock_read_commit_id.return_value = 'abc'
mock_restore_instance = MagicMock()
mock_restore.return_value = RestoreResponseModel(status=True)
mock_restore_instance.return_value = None
mock_google_drive_manager.return_value = mock_restore_instance
- mock_restore_instance.download_from_drive.return_value = True
+ # Two downloads: commit id file, then backup file
+ mock_restore_instance.download_from_drive.side_effect = [True, True]
result = RestoreService.restore(mock_valid_mnemonic, mock_password)
@@ -79,23 +85,32 @@ def test_restore(mock_google_drive_manager, mock_restore, mock_get_path, mock_ge
# Test function
+@patch('src.data.service.restore_service.COMPATIBLE_RLN_NODE_COMMITS', ['abc'])
+@patch('src.data.service.restore_service.read_ln_node_commit_id_file')
+@patch('src.data.service.restore_service.SettingRepository.set_rln_node_commit_id')
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
@patch('src.utils.local_store.local_store.get_path')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.restore')
@patch('src.data.service.restore_service.GoogleDriveManager')
-def test_restore_when_file_not_exists(mock_google_drive_manager, mock_restore, mock_get_path, mock_get_hashed_mnemonic, setup_directory):
+def test_restore_when_file_not_exists(
+ mock_google_drive_manager, mock_restore,
+ mock_app_paths, mock_get_hashed_mnemonic, mock_set_commit_id, mock_read_commit_id, setup_directory,
+):
"""Case 2: When restore file does not exist after download"""
- test_dir, _ = setup_directory
+ test_dir, restore_dir = setup_directory
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_get_path.return_value = test_dir
+ mock_app_paths.restore_folder_path = restore_dir
+ mock_app_paths.iriswallet_temp_folder_path = test_dir
+ mock_read_commit_id.return_value = 'abc'
mock_restore_instance = MagicMock()
mock_restore.return_value = RestoreResponseModel(status=True)
mock_restore_instance.return_value = None
mock_google_drive_manager.return_value = mock_restore_instance
- mock_restore_instance.download_from_drive.return_value = None
+ # First: commit id download succeeds, Second: backup download returns None
+ mock_restore_instance.download_from_drive.side_effect = [True, None]
error_message = ERROR_NOT_BACKUP_FILE
with pytest.raises(CommonException, match=error_message):
@@ -103,16 +118,19 @@ def test_restore_when_file_not_exists(mock_google_drive_manager, mock_restore, m
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
-@patch('src.utils.local_store.local_store.get_path')
+@patch('src.data.service.restore_service.app_paths')
@patch('src.data.service.restore_service.GoogleDriveManager')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.restore')
-def test_restore_no_mnemonic(mock_restore, mock_google_drive_manager, mock_get_path, mock_get_hashed_mnemonic):
+def test_restore_no_mnemonic(mock_restore, mock_google_drive_manager, mock_app_paths, mock_get_hashed_mnemonic):
"""Case 3: Test restore service with missing mnemonic"""
# Setup mocks
mock_get_hashed_mnemonic.side_effect = CommonException(
ERROR_UNABLE_GET_MNEMONIC,
)
- mock_get_path.return_value = os.path.join(
+ mock_app_paths.restore_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
os.path.dirname(__file__), 'some_path',
)
mock_google_drive_manager.return_value = MagicMock()
@@ -128,13 +146,16 @@ def test_restore_no_mnemonic(mock_restore, mock_google_drive_manager, mock_get_p
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
-@patch('src.utils.local_store.local_store.get_path')
-def test_restore_no_password(mock_get_path, mock_get_hashed_mnemonic):
+@patch('src.data.service.restore_service.app_paths')
+def test_restore_no_password(mock_app_paths, mock_get_hashed_mnemonic):
"""Case 4: Test restore service with missing password"""
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_get_path.return_value = os.path.join(
+ mock_app_paths.restore_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
os.path.dirname(__file__), 'some_path',
)
@@ -147,15 +168,22 @@ def test_restore_no_password(mock_get_path, mock_get_hashed_mnemonic):
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
+@patch('src.data.service.restore_service.app_paths')
@patch('src.data.service.restore_service.GoogleDriveManager')
@patch('src.data.repository.common_operations_repository.CommonOperationRepository.restore')
-def test_restore_no_hashed_value(mock_restore, mock_google_drive_manager, mock_get_hashed_mnemonic):
+def test_restore_no_hashed_value(mock_restore, mock_google_drive_manager, mock_app_paths, mock_get_hashed_mnemonic):
"""Case 5: Test restore service with missing hashed value"""
# Setup mocks
mock_get_hashed_mnemonic.side_effect = CommonException(
ERROR_UNABLE_TO_GET_HASHED_MNEMONIC,
)
+ mock_app_paths.restore_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
mock_google_drive_manager.return_value = MagicMock()
mock_google_drive_manager.return_value.download_from_drive.return_value = True
mock_restore.return_value = RestoreResponseModel(status=True)
@@ -165,15 +193,27 @@ def test_restore_no_hashed_value(mock_restore, mock_google_drive_manager, mock_g
RestoreService.restore(mock_valid_mnemonic, mock_password)
+@patch('src.data.service.restore_service.COMPATIBLE_RLN_NODE_COMMITS', ['abc'])
+@patch('src.data.service.restore_service.read_ln_node_commit_id_file')
@patch('src.data.service.common_operation_service.CommonOperationService.get_hashed_mnemonic')
+@patch('src.data.service.restore_service.app_paths')
@patch('src.data.service.restore_service.GoogleDriveManager')
-def test_restore_download_error(mock_google_drive_manager, mock_get_hashed_mnemonic):
+def test_restore_download_error(mock_google_drive_manager, mock_app_paths, mock_get_hashed_mnemonic, mock_read_commit_id):
"""Case 6: Test restore service with download failure"""
# Setup mocks
mock_get_hashed_mnemonic.return_value = 'e23ddff3cc'
- mock_google_drive_manager.return_value = MagicMock()
- mock_google_drive_manager.return_value.download_from_drive.return_value = False
+ mock_app_paths.restore_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_app_paths.iriswallet_temp_folder_path = os.path.join(
+ os.path.dirname(__file__), 'some_path',
+ )
+ mock_read_commit_id.return_value = 'abc'
+ drive = MagicMock()
+ mock_google_drive_manager.return_value = drive
+ # First commit id download ok, second backup download fails
+ drive.download_from_drive.side_effect = [True, False]
# Call the RestoreService.restore method
with pytest.raises(CommonException, match=ERROR_WHILE_RESTORE_DOWNLOAD_FROM_DRIVE):
diff --git a/unit_tests/tests/service_tests/services_helper_test/faucet_service_helper_test.py b/unit_tests/tests/service_tests/services_helper_test/faucet_service_helper_test.py
index c12a42ab..011ee1e0 100644
--- a/unit_tests/tests/service_tests/services_helper_test/faucet_service_helper_test.py
+++ b/unit_tests/tests/service_tests/services_helper_test/faucet_service_helper_test.py
@@ -9,6 +9,7 @@
from src.model.enums.enums_model import NetworkEnumModel
from src.utils.constant import rgbMainnetFaucetURLs
from src.utils.constant import rgbRegtestFaucetURLs
+from src.utils.constant import rgbTestnet4FaucetURLs
from src.utils.constant import rgbTestnetFaucetURLs
from src.utils.custom_exception import ServiceOperationException
from src.utils.error_message import ERROR_FAILED_TO_GET_FAUCET_URL
@@ -21,9 +22,11 @@ def test_get_faucet_url():
"""Case 1 : Test all network"""
response_net_regtest = get_faucet_url(NetworkEnumModel.REGTEST)
response_net_testnet = get_faucet_url(NetworkEnumModel.TESTNET)
+ response_net_testnet4 = get_faucet_url(NetworkEnumModel.TESTNET4)
response_net_mainnet = get_faucet_url(NetworkEnumModel.MAINNET)
assert response_net_mainnet == rgbMainnetFaucetURLs[0]
assert response_net_testnet == rgbTestnetFaucetURLs[0]
+ assert response_net_testnet4 == rgbTestnet4FaucetURLs[0]
assert response_net_regtest == rgbRegtestFaucetURLs[0]
diff --git a/unit_tests/tests/service_tests/services_helper_test/main_asset_page_service_helper_test.py b/unit_tests/tests/service_tests/services_helper_test/main_asset_page_service_helper_test.py
index c301ff86..df797b02 100644
--- a/unit_tests/tests/service_tests/services_helper_test/main_asset_page_service_helper_test.py
+++ b/unit_tests/tests/service_tests/services_helper_test/main_asset_page_service_helper_test.py
@@ -22,9 +22,11 @@ def test_get_offline_asset_ticker():
"""Case 1 : Test all network"""
response_net_regtest = get_offline_asset_ticker(NetworkEnumModel.REGTEST)
response_net_testnet = get_offline_asset_ticker(NetworkEnumModel.TESTNET)
+ response_net_testnet4 = get_offline_asset_ticker(NetworkEnumModel.TESTNET4)
response_net_mainnet = get_offline_asset_ticker(NetworkEnumModel.MAINNET)
assert response_net_mainnet == 'BTC'
assert response_net_testnet == 'tBTC'
+ assert response_net_testnet4 == 'tBTC'
assert response_net_regtest == 'rBTC'
diff --git a/unit_tests/tests/ui_tests/settings_helpers_test.py b/unit_tests/tests/ui_tests/settings_helpers_test.py
new file mode 100644
index 00000000..4df68fad
--- /dev/null
+++ b/unit_tests/tests/ui_tests/settings_helpers_test.py
@@ -0,0 +1,253 @@
+"""Unit tests for settings helper functions."""
+from __future__ import annotations
+
+from unittest.mock import MagicMock
+
+import pytest
+from PySide6.QtWidgets import QDialog
+
+from src.model.enums.enums_model import NetworkEnumModel
+from src.views.components.settings_helpers import check_keyring_state_for_password
+from src.views.components.settings_helpers import set_endpoint_based_on_network
+from src.views.components.settings_helpers import set_frame_content
+from src.views.components.settings_helpers import update_save_button
+
+
+def test_set_endpoint_based_on_network_mainnet(mocker):
+ """Test setting endpoints for mainnet network."""
+ # Mock SettingRepository to return mainnet
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_wallet_network',
+ return_value=NetworkEnumModel.MAINNET,
+ )
+
+ # Call function
+ indexer_url, proxy_endpoint, bitcoind_host, bitcoind_port = set_endpoint_based_on_network()
+
+ # Verify correct endpoints were returned
+ assert indexer_url == 'http://127.0.0.1:50003'
+ assert proxy_endpoint == 'http://127.0.0.1:3002/json-rpc'
+ assert bitcoind_host == 'localhost'
+ assert bitcoind_port == 18447
+
+
+def test_set_endpoint_based_on_network_testnet(mocker):
+ """Test setting endpoints for testnet network."""
+ # Mock SettingRepository to return testnet
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_wallet_network',
+ return_value=NetworkEnumModel.TESTNET,
+ )
+
+ # Call function
+ indexer_url, proxy_endpoint, bitcoind_host, bitcoind_port = set_endpoint_based_on_network()
+
+ # Verify correct endpoints were returned
+ assert indexer_url == 'ssl://electrum.iriswallet.com:50013'
+ assert proxy_endpoint == 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
+ assert bitcoind_host == 'electrum.iriswallet.com'
+ assert bitcoind_port == 18332
+
+
+def test_set_endpoint_based_on_network_testnet4(mocker):
+ """Test setting endpoints for testnet4 network."""
+ # Mock SettingRepository to return testnet4
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_wallet_network',
+ return_value=NetworkEnumModel.TESTNET4,
+ )
+
+ # Call function
+ indexer_url, proxy_endpoint, bitcoind_host, bitcoind_port = set_endpoint_based_on_network()
+
+ # Verify correct endpoints were returned
+ assert indexer_url == 'ssl://electrum.iriswallet.com:50053'
+ assert proxy_endpoint == 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
+ assert bitcoind_host == 'electrum.iriswallet.com'
+ assert bitcoind_port == 48332
+
+
+def test_set_endpoint_based_on_network_regtest(mocker):
+ """Test setting endpoints for regtest network."""
+ # Mock SettingRepository to return regtest
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_wallet_network',
+ return_value=NetworkEnumModel.REGTEST,
+ )
+
+ # Call function
+ indexer_url, proxy_endpoint, bitcoind_host, bitcoind_port = set_endpoint_based_on_network()
+
+ # Verify correct endpoints were returned
+ assert indexer_url == 'electrum.rgbtools.org:50041'
+ assert proxy_endpoint == 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
+ assert bitcoind_host == 'regtest-bitcoind.rgbtools.org'
+ assert bitcoind_port == 80
+
+
+def test_set_endpoint_based_on_network_invalid(mocker):
+ """Test setting endpoints with invalid network type."""
+ # Mock SettingRepository to return invalid network
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_wallet_network',
+ return_value='invalid_network',
+ )
+
+ # Verify ValueError is raised
+ with pytest.raises(ValueError) as exc_info:
+ set_endpoint_based_on_network()
+
+ assert 'Unsupported network type' in str(exc_info.value)
+
+
+def test_set_frame_content_basic():
+ """Test setting frame content with basic configuration."""
+ # Create mock frame and components
+ mock_frame = MagicMock()
+ mock_frame.input_value = MagicMock()
+ mock_frame.suggestion_desc = MagicMock()
+ mock_frame.time_unit_combobox = MagicMock()
+ mock_frame.save_button = MagicMock()
+
+ # Test with float input that's an integer
+ set_frame_content(mock_frame, 10.0, 'hours')
+ mock_frame.input_value.setText.assert_called_with('10')
+ mock_frame.input_value.setPlaceholderText.assert_called_with('10')
+ mock_frame.suggestion_desc.hide.assert_called_once()
+ mock_frame.time_unit_combobox.hide.assert_called_once()
+
+
+def test_set_frame_content_with_validator():
+ """Test setting frame content with validator."""
+ mock_frame = MagicMock()
+ mock_validator = MagicMock()
+
+ set_frame_content(mock_frame, 10, 'hours', validator=mock_validator)
+ mock_frame.input_value.setValidator.assert_called_with(mock_validator)
+
+
+def test_set_frame_content_with_time_unit_combobox():
+ """Test setting frame content with time unit combobox."""
+ mock_frame = MagicMock()
+ mock_combobox = MagicMock()
+ mock_combobox.findText.return_value = 1
+
+ set_frame_content(
+ mock_frame, 10, 'hours', time_unit_combobox=mock_combobox,
+ )
+ mock_combobox.setCurrentIndex.assert_called_with(1)
+
+
+def test_update_save_button_value_changed():
+ """Test updating save button when input value has changed."""
+ mock_frame = MagicMock()
+ mock_frame.input_value.text.return_value = '20'
+ mock_frame.time_unit_combobox.currentText.return_value = 'hours'
+
+ update_save_button(mock_frame, '10', 'hours')
+ mock_frame.save_button.setDisabled.assert_called_with(False)
+
+
+def test_update_save_button_value_unchanged():
+ """Test updating save button when input value hasn't changed."""
+ mock_frame = MagicMock()
+ mock_frame.input_value.text.return_value = '10'
+ mock_frame.time_unit_combobox.currentText.return_value = 'hours'
+
+ update_save_button(mock_frame, '10', 'hours')
+ mock_frame.save_button.setDisabled.assert_called_with(True)
+
+
+def test_update_save_button_time_unit_changed():
+ """Test updating save button with time unit change."""
+ mock_frame = MagicMock()
+ mock_frame.input_value.text.return_value = '10'
+ mock_frame.time_unit_combobox.currentText.return_value = 'hours'
+
+ update_save_button(
+ mock_frame, '10', 'minutes', mock_frame.time_unit_combobox,
+ )
+ mock_frame.save_button.setDisabled.assert_called_with(False)
+
+
+def test_check_keyring_state_disabled(mocker):
+ """Test checking keyring state when keyring is disabled."""
+ # Mock dependencies
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_keyring_status',
+ return_value=False,
+ )
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_wallet_network',
+ return_value=NetworkEnumModel.MAINNET,
+ )
+ mock_get_value = mocker.patch(
+ 'src.views.components.settings_helpers.get_value',
+ return_value='test_password',
+ )
+
+ # Call function
+ result = check_keyring_state_for_password(None, None)
+
+ # Verify password was retrieved from storage
+ mock_get_value.assert_called_once_with('wallet_password', 'mainnet')
+ assert result == 'test_password'
+
+
+def test_check_keyring_state_enabled_accepted(mocker):
+ """Test checking keyring state when keyring is enabled and dialog is accepted."""
+ # Mock dependencies
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_keyring_status',
+ return_value=True,
+ )
+ mock_dialog = MagicMock()
+ mock_dialog.exec.return_value = QDialog.Accepted
+ mock_dialog.password_input.text.return_value = 'dialog_password'
+ mock_dialog_class = mocker.patch(
+ 'src.views.components.settings_helpers.RestoreMnemonicWidget',
+ return_value=mock_dialog,
+ )
+
+ # Call function
+ parent_mock = MagicMock()
+ view_model_mock = MagicMock()
+ result = check_keyring_state_for_password(parent_mock, view_model_mock)
+
+ # Verify dialog was created with correct parameters
+ mock_dialog_class.assert_called_once_with(
+ parent=parent_mock,
+ view_model=view_model_mock,
+ origin_page='setting_card',
+ mnemonic_visibility=False,
+ )
+
+ # Verify dialog was configured correctly
+ mock_dialog.mnemonic_detail_text_label.setText.assert_called_once()
+ mock_dialog.mnemonic_detail_text_label.setFixedHeight.assert_called_once_with(
+ 40,
+ )
+
+ # Verify password was retrieved from dialog
+ assert result == 'dialog_password'
+
+
+def test_check_keyring_state_enabled_rejected(mocker):
+ """Test checking keyring state when keyring is enabled and dialog is rejected."""
+ # Mock dependencies
+ mocker.patch(
+ 'src.views.components.settings_helpers.SettingRepository.get_keyring_status',
+ return_value=True,
+ )
+ mock_dialog = MagicMock()
+ mock_dialog.exec.return_value = QDialog.Rejected
+ mocker.patch(
+ 'src.views.components.settings_helpers.RestoreMnemonicWidget',
+ return_value=mock_dialog,
+ )
+
+ # Call function
+ result = check_keyring_state_for_password(None, None)
+
+ # Verify None is returned when dialog is rejected
+ assert result is None
diff --git a/unit_tests/tests/ui_tests/ui_collectible_asset_test.py b/unit_tests/tests/ui_tests/ui_collectible_asset_test.py
index 4354c437..d7aae010 100644
--- a/unit_tests/tests/ui_tests/ui_collectible_asset_test.py
+++ b/unit_tests/tests/ui_tests/ui_collectible_asset_test.py
@@ -20,6 +20,7 @@
from PySide6.QtWidgets import QScrollArea
from PySide6.QtWidgets import QWidget
+from src.model.enums.enums_model import AssetType
from src.model.enums.enums_model import ToastPreset
from src.model.rgb_model import RgbAssetPageLoadModel
from src.utils.constant import IRIS_WALLET_TRANSLATIONS_CONTEXT
@@ -71,7 +72,6 @@ def test_create_collectible_frame_with_image(collectible_asset_widget, mocker):
coll_asset.name = 'Mock Asset'
coll_asset.media.file_path = 'mock_image_path'
coll_asset.media.hex = None # Valid image path, no hex
- coll_asset.asset_iface = 'mock_iface'
# Mock the resize_image method to return a dummy QPixmap
mocker.patch('src.utils.common_utils.resize_image', return_value=QPixmap())
@@ -126,7 +126,6 @@ def test_create_collectible_frame_with_empty_name(collectible_asset_widget, mock
coll_asset.name = '' # Empty name
coll_asset.media.file_path = 'mock_image_path'
coll_asset.media.hex = None
- coll_asset.asset_iface = 'mock_iface'
# Mock the resize_image method to return a dummy QPixmap
mocker.patch('src.utils.common_utils.resize_image', return_value=QPixmap())
@@ -139,27 +138,6 @@ def test_create_collectible_frame_with_empty_name(collectible_asset_widget, mock
assert asset_name_label.text() == '' # The label should display an empty name
-def test_create_collectible_frame_with_different_asset_type(collectible_asset_widget, mocker):
- """Test the creation of a collectible frame with a different asset type."""
-
- # Mock a collectible asset with a different asset type
- coll_asset = MagicMock()
- coll_asset.asset_id = 'mock_id'
- coll_asset.name = 'Mock Asset'
- coll_asset.media.file_path = 'mock_image_path'
- coll_asset.media.hex = None
- coll_asset.asset_iface = 'different_type' # Different asset type
-
- # Mock the resize_image method to return a dummy QPixmap
- mocker.patch('src.utils.common_utils.resize_image', return_value=QPixmap())
-
- # Call the create_collectible_frame method
- frame = collectible_asset_widget.create_collectible_frame(coll_asset)
-
- # Assert that the asset type is correctly passed and used
- assert frame._asset_type == 'different_type'
-
-
def test_create_collectible_frame_edge_case(collectible_asset_widget, mocker):
"""Test the creation of a collectible frame with edge case values (e.g., very large image path)."""
@@ -169,7 +147,6 @@ def test_create_collectible_frame_edge_case(collectible_asset_widget, mocker):
coll_asset.name = 'Edge Case Asset'
coll_asset.media.file_path = 'a' * 1000 # Very large file path
coll_asset.media.hex = None
- coll_asset.asset_iface = 'mock_iface'
# Mock the resize_image method to return a dummy QPixmap
mocker.patch('src.utils.common_utils.resize_image', return_value=QPixmap())
@@ -202,7 +179,6 @@ def test_create_collectible_frame(collectible_asset_widget, mocker):
coll_asset.name = 'Mock Asset'
coll_asset.media.file_path = 'mock_path'
coll_asset.media.hex = None
- coll_asset.asset_iface = 'mock_iface'
# Mock the resize_image and convert_hex_to_image methods
mocker.patch('src.utils.common_utils.resize_image', return_value=QPixmap())
@@ -218,7 +194,7 @@ def test_create_collectible_frame(collectible_asset_widget, mocker):
QLabel, 'collectible_asset_name',
).text() == 'Mock Asset'
assert frame.findChild(QLabel, 'collectible_image').pixmap() is not None
- assert frame._asset_type == 'mock_iface'
+ assert frame._asset_type == AssetType.CFA
assert frame.cursor().shape() == Qt.CursorShape.PointingHandCursor
assert frame.styleSheet() == (
'background: transparent;\n'
@@ -336,17 +312,17 @@ def test_handle_collectible_frame_click(collectible_asset_widget, mocker):
asset_type = 'mock_type'
mock_view_model = collectible_asset_widget._view_model
- mock_view_model.rgb25_view_model.asset_info.emit = MagicMock()
- mock_view_model.page_navigation.rgb25_detail_page = MagicMock()
+ mock_view_model.cfa_view_model.asset_info.emit = MagicMock()
+ mock_view_model.page_navigation.cfa_detail_page = MagicMock()
collectible_asset_widget.handle_collectible_frame_click(
asset_id, asset_name, image_path, asset_type,
)
- mock_view_model.rgb25_view_model.asset_info.emit.assert_called_once_with(
+ mock_view_model.cfa_view_model.asset_info.emit.assert_called_once_with(
asset_id, asset_name, image_path, asset_type,
)
- mock_view_model.page_navigation.rgb25_detail_page.assert_called_once_with(
+ mock_view_model.page_navigation.cfa_detail_page.assert_called_once_with(
RgbAssetPageLoadModel(
asset_id=None, asset_name=None,
image_path=None, asset_type='mock_type',
@@ -396,7 +372,6 @@ def test_create_collectible_frame_image_loading_failure(collectible_asset_widget
coll_asset.name = 'Mock Asset'
coll_asset.media.file_path = 'mock_path'
coll_asset.media.hex = None
- coll_asset.asset_iface = 'mock_iface'
# Simulate image loading failure
mocker.patch('src.utils.common_utils.resize_image', return_value=None)
@@ -417,16 +392,16 @@ def test_handle_collectible_frame_click_invalid_data(collectible_asset_widget):
# Mock view model methods to ensure they aren't called
mock_view_model = collectible_asset_widget._view_model
- mock_view_model.rgb25_view_model.asset_info.emit = MagicMock()
- mock_view_model.page_navigation.rgb25_detail_page = MagicMock()
+ mock_view_model.cfa_view_model.asset_info.emit = MagicMock()
+ mock_view_model.page_navigation.cfa_detail_page = MagicMock()
collectible_asset_widget.handle_collectible_frame_click(
asset_id, asset_name, image_path, asset_type,
)
# Ensure that no method was called with invalid asset ID
- mock_view_model.rgb25_view_model.asset_info.emit.assert_not_called()
- mock_view_model.page_navigation.rgb25_detail_page.assert_not_called()
+ mock_view_model.cfa_view_model.asset_info.emit.assert_not_called()
+ mock_view_model.page_navigation.cfa_detail_page.assert_not_called()
# Negative test case: Test failure during asset loading
diff --git a/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py b/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py
index 7de08688..934202fc 100644
--- a/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py
+++ b/unit_tests/tests/ui_tests/ui_create_ln_invoice_test.py
@@ -160,7 +160,7 @@ def test_get_ln_invoice(mock_create_ln_invoice_view_model, create_ln_invoice_wid
# Test page navigation after invoice creation
mock_page_navigation = MagicMock()
- widget._view_model.page_navigation.receive_rgb25_page = mock_page_navigation
+ widget._view_model.page_navigation.receive_cfa_page = mock_page_navigation
widget.get_ln_invoice()
mock_page_navigation.assert_called_once()
@@ -431,9 +431,9 @@ def test_on_close(create_ln_invoice_widget):
create_ln_invoice_widget._view_model.page_navigation.collectibles_asset_page = MagicMock()
create_ln_invoice_widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
- # Test case when asset_type is RGB25 (should navigate to collectibles_asset_page)
- # Assuming AssetType.RGB25.value is 'RGB25'
- create_ln_invoice_widget.asset_type = 'RGB25'
+ # Test case when asset_type is CFA (should navigate to collectibles_asset_page)
+ # Assuming AssetType.CFA.value is 'CFA'
+ create_ln_invoice_widget.asset_type = 'CFA'
create_ln_invoice_widget.on_close()
# Check that collectibles_asset_page is called and fungibles_asset_page is not
@@ -444,8 +444,8 @@ def test_on_close(create_ln_invoice_widget):
create_ln_invoice_widget._view_model.page_navigation.collectibles_asset_page.reset_mock()
create_ln_invoice_widget._view_model.page_navigation.fungibles_asset_page.reset_mock()
- # Test case when asset_type is not RGB25 (should navigate to fungibles_asset_page)
- # Any asset type other than 'RGB25'
+ # Test case when asset_type is not CFA (should navigate to fungibles_asset_page)
+ # Any asset type other than 'CFA'
create_ln_invoice_widget.asset_type = 'OTHER_ASSET'
create_ln_invoice_widget.on_close()
diff --git a/unit_tests/tests/ui_tests/ui_fungible_asset_test.py b/unit_tests/tests/ui_tests/ui_fungible_asset_test.py
index 40f646dd..806dc8a8 100644
--- a/unit_tests/tests/ui_tests/ui_fungible_asset_test.py
+++ b/unit_tests/tests/ui_tests/ui_fungible_asset_test.py
@@ -37,7 +37,6 @@ def mock_fungible_asset_view_model():
mock_view_model.main_asset_view_model.assets.vanilla = MagicMock(
asset_id='1',
name='Bitcoin',
- asset_iface=AssetType.BITCOIN.value,
ticker=TokenSymbol.BITCOIN.value,
balance=MagicMock(future=0.5),
)
@@ -122,7 +121,6 @@ def test_ui_update_after_asset_loading(create_fungible_asset_widget: FungibleAss
bitcoin_mock.name = 'bitcoin' # Set as string
bitcoin_mock.balance = MagicMock()
bitcoin_mock.balance.future = '0.5'
- bitcoin_mock.asset_iface = 'BTC'
# Mock assets in the view model
widget._view_model.main_asset_view_model.assets.vanilla = bitcoin_mock
@@ -149,7 +147,6 @@ def test_show_assets_with_various_assets(create_fungible_asset_widget, qtbot):
bitcoin_mock.asset_id = 'btc_asset_id'
bitcoin_mock.name = 'Bitcoin'
bitcoin_mock.balance.future = '0.5'
- bitcoin_mock.asset_iface = 'BTC'
bitcoin_mock.ticker = 'BTC'
# Mock NIA asset
@@ -157,7 +154,6 @@ def test_show_assets_with_various_assets(create_fungible_asset_widget, qtbot):
nia_mock.asset_id = 'nia_asset_id'
nia_mock.name = 'NIA'
nia_mock.balance.future = '1.0'
- nia_mock.asset_iface = 'NIA'
nia_mock.ticker = 'NIA'
# Mock assets in the view model
@@ -417,7 +413,7 @@ def test_handle_asset_frame_click(create_fungible_asset_widget):
# Mock the view model and navigation methods
widget._view_model.page_navigation = MagicMock()
- widget._view_model.rgb25_view_model = MagicMock()
+ widget._view_model.cfa_view_model = MagicMock()
# Test for Bitcoin asset type
bitcoin_asset_id = 'btc_asset_id'
@@ -434,8 +430,8 @@ def test_handle_asset_frame_click(create_fungible_asset_widget):
# Verify navigation to Bitcoin page
widget._view_model.page_navigation.bitcoin_page.assert_called_once()
- widget._view_model.rgb25_view_model.asset_info.emit.assert_not_called()
- widget._view_model.page_navigation.rgb25_detail_page.assert_not_called()
+ widget._view_model.cfa_view_model.asset_info.emit.assert_not_called()
+ widget._view_model.page_navigation.cfa_detail_page.assert_not_called()
# Reset mocks for the next scenario
widget._view_model.page_navigation.bitcoin_page.reset_mock()
@@ -444,7 +440,7 @@ def test_handle_asset_frame_click(create_fungible_asset_widget):
rgb_asset_id = 'rgb_asset_id'
rgb_asset_name = 'RGB Asset'
rgb_image_path = ':/assets/rgb.png'
- rgb_asset_type = AssetType.RGB25.value
+ rgb_asset_type = AssetType.CFA.value
widget.handle_asset_frame_click(
asset_id=rgb_asset_id,
@@ -454,12 +450,12 @@ def test_handle_asset_frame_click(create_fungible_asset_widget):
)
# Verify asset_info signal is emitted with correct parameters
- widget._view_model.rgb25_view_model.asset_info.emit.assert_called_once_with(
+ widget._view_model.cfa_view_model.asset_info.emit.assert_called_once_with(
rgb_asset_id, rgb_asset_name, rgb_image_path, rgb_asset_type,
)
# Verify navigation to RGB detail page
- widget._view_model.page_navigation.rgb25_detail_page.assert_called_once_with(
+ widget._view_model.page_navigation.cfa_detail_page.assert_called_once_with(
RgbAssetPageLoadModel(asset_type=rgb_asset_type),
)
@@ -480,14 +476,11 @@ def test_create_fungible_card(create_fungible_asset_widget, qtbot):
asset = MagicMock()
asset.asset_id = 'sample_asset_id'
asset.name = 'Sample Asset'
- asset.asset_iface = AssetType.RGB20
asset.balance.future = 1000
asset.balance.offchain_outbound = 200
asset.ticker = 'SAMPLE'
- # Call the method with and without an image path
widget.create_fungible_card(asset)
- widget.create_fungible_card(asset, img_path=':/assets/sample_icon.png')
# Verify the fungible_frame is created with the correct settings
assert widget.fungible_frame is not None
@@ -499,23 +492,24 @@ def test_create_fungible_card(create_fungible_asset_widget, qtbot):
assert widget.asset_name.text() == asset.name
assert widget.asset_name.minimumSize() == QSize(135, 40)
- # Verify address is set correctly for RGB20
+ # Verify address is set correctly for NIA
assert widget.address.text() == asset.asset_id
# Verify amount is set
assert widget.amount.text() == str(asset.balance.future)
- # Verify outbound_balance is set for RGB20
+ # Verify outbound_balance is set for NIA
assert widget.outbound_balance.text() == str(asset.balance.offchain_outbound)
# Verify token_symbol is set
assert widget.token_symbol.text() == asset.ticker
+ widget.create_fungible_card(asset, img_path=':/assets/sample_icon.png')
+
# Verify that the fungible frame is added to the layout
widget.vertical_layout_3.addWidget.assert_called()
# Test for Bitcoin-specific behavior
- asset.asset_iface = AssetType.BITCOIN
asset.name = 'Bitcoin'
# Test for BTC (mainnet Bitcoin)
@@ -524,21 +518,32 @@ def test_create_fungible_card(create_fungible_asset_widget, qtbot):
assert widget.token_symbol.text() == TokenSymbol.SAT.value
assert widget.asset_name.text() == AssetType.BITCOIN.value.lower()
+ # Test for TESTNET4_BITCOIN
+ with patch('src.views.ui_fungible_asset.SettingRepository.get_wallet_network', return_value=NetworkEnumModel.TESTNET4):
+ asset.ticker = TokenSymbol.TESTNET4_BITCOIN.value
+ widget.create_fungible_card(asset)
+ assert widget.token_symbol.text() == TokenSymbol.SAT.value
+ assert widget.asset_name.text() == f'{NetworkEnumModel.TESTNET4.value} {
+ AssetType.BITCOIN.value.lower()
+ }'
+
# Test for TESTNET_BITCOIN
- asset.ticker = TokenSymbol.TESTNET_BITCOIN.value
- widget.create_fungible_card(asset)
- assert widget.token_symbol.text() == TokenSymbol.SAT.value
- assert widget.asset_name.text() == f'{NetworkEnumModel.TESTNET.value} {
- AssetType.BITCOIN.value.lower()
- }'
+ with patch('src.views.ui_fungible_asset.SettingRepository.get_wallet_network', return_value=NetworkEnumModel.TESTNET):
+ asset.ticker = TokenSymbol.TESTNET_BITCOIN.value
+ widget.create_fungible_card(asset)
+ assert widget.token_symbol.text() == TokenSymbol.SAT.value
+ assert widget.asset_name.text() == f'{NetworkEnumModel.TESTNET.value} {
+ AssetType.BITCOIN.value.lower()
+ }'
# Test for REGTEST_BITCOIN
- asset.ticker = TokenSymbol.REGTEST_BITCOIN.value
- widget.create_fungible_card(asset)
- assert widget.token_symbol.text() == TokenSymbol.SAT.value
- assert widget.asset_name.text() == f'{NetworkEnumModel.REGTEST.value} {
- AssetType.BITCOIN.value.lower()
- }'
+ with patch('src.views.ui_fungible_asset.SettingRepository.get_wallet_network', return_value=NetworkEnumModel.REGTEST):
+ asset.ticker = TokenSymbol.REGTEST_BITCOIN.value
+ widget.create_fungible_card(asset)
+ assert widget.token_symbol.text() == TokenSymbol.SAT.value
+ assert widget.asset_name.text() == f'{NetworkEnumModel.REGTEST.value} {
+ AssetType.BITCOIN.value.lower()
+ }'
def test_show_assets(create_fungible_asset_widget, qtbot):
@@ -571,7 +576,6 @@ def test_show_assets(create_fungible_asset_widget, qtbot):
asset = MagicMock()
asset.name = 'Bitcoin' # Mock the 'name' attribute to return a string
asset.asset_id = 'asset123' # Mock the 'asset_id' attribute
- asset.asset_iface = 'bitcoin_iface' # Mock the 'asset_iface' attribute
asset.ticker = 'BTC'
# Mock the assets in the view model (if needed)
diff --git a/unit_tests/tests/ui_tests/ui_issue_rgb25_test.py b/unit_tests/tests/ui_tests/ui_issue_cfa_test.py
similarity index 62%
rename from unit_tests/tests/ui_tests/ui_issue_rgb25_test.py
rename to unit_tests/tests/ui_tests/ui_issue_cfa_test.py
index cc1bb11d..48e5013e 100644
--- a/unit_tests/tests/ui_tests/ui_issue_rgb25_test.py
+++ b/unit_tests/tests/ui_tests/ui_issue_cfa_test.py
@@ -1,4 +1,4 @@
-"""Unit test for Issue RGB25 UI."""
+"""Unit test for Issue CFA UI."""
# Disable the redefined-outer-name warning as
# it's normal to pass mocked objects in test functions
# pylint: disable=redefined-outer-name,unused-argument,protected-access
@@ -15,43 +15,43 @@
from src.model.common_operation_model import NodeInfoResponseModel
from src.utils.constant import IRIS_WALLET_TRANSLATIONS_CONTEXT
from src.viewmodels.main_view_model import MainViewModel
-from src.views.ui_issue_rgb25 import IssueRGB25Widget
+from src.views.ui_issue_cfa import IssueCFAWidget
from unit_tests.tests.ui_tests.ui_helper_test.issue_asset_helper_test import assert_success_page_called
@pytest.fixture
-def issue_rgb25_page_navigation():
+def issue_cfa_page_navigation():
"""Fixture to create a mocked page navigation object."""
mock_navigation = MagicMock()
return mock_navigation
@pytest.fixture
-def mock_issue_rgb25_view_model(issue_rgb25_page_navigation: MagicMock):
+def mock_issue_cfa_view_model(issue_cfa_page_navigation: MagicMock):
"""Fixture to create a MainViewModel instance with mocked page navigation."""
- return MainViewModel(issue_rgb25_page_navigation)
+ return MainViewModel(issue_cfa_page_navigation)
@pytest.fixture
-def issue_rgb25_widget(mock_issue_rgb25_view_model: MainViewModel):
- """Fixture to create a IssueRGB25Widget instance."""
+def issue_cfa_widget(mock_issue_cfa_view_model: MainViewModel):
+ """Fixture to create a IssueCFAWidget instance."""
- return IssueRGB25Widget(mock_issue_rgb25_view_model)
+ return IssueCFAWidget(mock_issue_cfa_view_model)
-def test_retranslate_ui(issue_rgb25_widget: IssueRGB25Widget):
+def test_retranslate_ui(issue_cfa_widget: IssueCFAWidget):
"""Test that the UI strings are correctly translated."""
- issue_rgb25_widget.retranslate_ui()
- assert issue_rgb25_widget.total_supply_label.text() == 'total_supply'
- assert issue_rgb25_widget.asset_name_label.text() == 'asset_name'
+ issue_cfa_widget.retranslate_ui()
+ assert issue_cfa_widget.total_supply_label.text() == 'total_supply'
+ assert issue_cfa_widget.asset_name_label.text() == 'asset_name'
-def test_on_issue_rgb25(issue_rgb25_widget: IssueRGB25Widget, qtbot):
- """Test the on_issue_rgb25 method."""
- widget = issue_rgb25_widget
+def test_on_issue_cfa(issue_cfa_widget: IssueCFAWidget, qtbot):
+ """Test the on_issue_cfa method."""
+ widget = issue_cfa_widget
# Mock the view model method
- widget._view_model.issue_rgb25_asset_view_model.issue_rgb25_asset = MagicMock()
+ widget._view_model.issue_cfa_asset_view_model.issue_cfa_asset = MagicMock()
# Set input values
widget.asset_description_input.setText('Description')
@@ -59,31 +59,31 @@ def test_on_issue_rgb25(issue_rgb25_widget: IssueRGB25Widget, qtbot):
widget.amount_input.setText('1000')
# Simulate the button click
- widget.on_issue_rgb25()
+ widget.on_issue_cfa()
# Verify that the view model method is called with the correct arguments
- widget._view_model.issue_rgb25_asset_view_model.issue_rgb25_asset.assert_called_once_with(
+ widget._view_model.issue_cfa_asset_view_model.issue_cfa_asset.assert_called_once_with(
'Description', 'Asset Name', '1000',
)
-def test_on_upload_asset_file(issue_rgb25_widget: IssueRGB25Widget, qtbot):
+def test_on_upload_asset_file(issue_cfa_widget: IssueCFAWidget, qtbot):
"""Test the on_upload_asset_file method."""
- widget = issue_rgb25_widget
+ widget = issue_cfa_widget
# Mock the view model method
- widget._view_model.issue_rgb25_asset_view_model.open_file_dialog = MagicMock()
+ widget._view_model.issue_cfa_asset_view_model.open_file_dialog = MagicMock()
# Simulate the button click
widget.on_upload_asset_file()
# Verify that the file dialog is opened
- widget._view_model.issue_rgb25_asset_view_model.open_file_dialog.assert_called_once()
+ widget._view_model.issue_cfa_asset_view_model.open_file_dialog.assert_called_once()
-def test_on_close(issue_rgb25_widget: IssueRGB25Widget, qtbot):
+def test_on_close(issue_cfa_widget: IssueCFAWidget, qtbot):
"""Test the on_close method."""
- widget = issue_rgb25_widget
+ widget = issue_cfa_widget
# Mock the page navigation method
widget._view_model.page_navigation.collectibles_asset_page = MagicMock()
@@ -95,15 +95,15 @@ def test_on_close(issue_rgb25_widget: IssueRGB25Widget, qtbot):
widget._view_model.page_navigation.collectibles_asset_page.assert_called_once()
-def test_handle_button_enabled(issue_rgb25_widget: IssueRGB25Widget, qtbot):
+def test_handle_button_enabled(issue_cfa_widget: IssueCFAWidget, qtbot):
"""Test the handle_button_enabled method."""
- widget = issue_rgb25_widget
+ widget = issue_cfa_widget
# Mock the inputs and button
widget.amount_input = MagicMock()
widget.asset_description_input = MagicMock()
widget.name_of_the_asset_input = MagicMock()
- widget.issue_rgb25_button = MagicMock()
+ widget.issue_cfa_button = MagicMock()
# Case when all fields are filled
widget.amount_input.text.return_value = '1000'
@@ -111,38 +111,38 @@ def test_handle_button_enabled(issue_rgb25_widget: IssueRGB25Widget, qtbot):
widget.name_of_the_asset_input.text.return_value = 'Asset Name'
widget.handle_button_enabled()
- widget.issue_rgb25_button.setDisabled.assert_called_once_with(False)
+ widget.issue_cfa_button.setDisabled.assert_called_once_with(False)
# Case when one of the fields is empty
widget.name_of_the_asset_input.text.return_value = ''
widget.handle_button_enabled()
- assert widget.issue_rgb25_button.isEnabled()
+ assert widget.issue_cfa_button.isEnabled()
-def test_update_loading_state(issue_rgb25_widget: IssueRGB25Widget, qtbot):
+def test_update_loading_state(issue_cfa_widget: IssueCFAWidget, qtbot):
"""Test the update_loading_state method."""
- widget = issue_rgb25_widget
+ widget = issue_cfa_widget
# Mock the button's loading methods
- widget.issue_rgb25_button.start_loading = MagicMock()
- widget.issue_rgb25_button.stop_loading = MagicMock()
+ widget.issue_cfa_button.start_loading = MagicMock()
+ widget.issue_cfa_button.stop_loading = MagicMock()
# Test loading state true
widget.update_loading_state(True)
- widget.issue_rgb25_button.start_loading.assert_called_once()
- widget.issue_rgb25_button.stop_loading.assert_not_called()
+ widget.issue_cfa_button.start_loading.assert_called_once()
+ widget.issue_cfa_button.stop_loading.assert_not_called()
# Test loading state false
widget.update_loading_state(False)
# still called once from previous
- widget.issue_rgb25_button.start_loading.assert_called_once()
- widget.issue_rgb25_button.stop_loading.assert_called_once()
+ widget.issue_cfa_button.start_loading.assert_called_once()
+ widget.issue_cfa_button.stop_loading.assert_called_once()
-def test_show_asset_issued(issue_rgb25_widget: IssueRGB25Widget, qtbot):
+def test_show_asset_issued(issue_cfa_widget: IssueCFAWidget, qtbot):
"""Test the show_asset_issued method."""
- widget = issue_rgb25_widget
+ widget = issue_cfa_widget
# Mock the success page method
widget._view_model.page_navigation.show_success_page = MagicMock()
@@ -160,11 +160,11 @@ def test_show_asset_issued(issue_rgb25_widget: IssueRGB25Widget, qtbot):
assert params.callback == widget._view_model.page_navigation.collectibles_asset_page
-@patch('src.views.ui_issue_rgb25.os.path.getsize')
-@patch('src.views.ui_issue_rgb25.resize_image')
-@patch('src.views.ui_issue_rgb25.QPixmap')
-@patch('src.views.ui_issue_rgb25.NodeInfoModel')
-def test_show_file_preview(mock_node_info_model, mock_qpix_map, mock_resize_image, mock_getsize, issue_rgb25_widget):
+@patch('src.views.ui_issue_cfa.os.path.getsize')
+@patch('src.views.ui_issue_cfa.resize_image')
+@patch('src.views.ui_issue_cfa.QPixmap')
+@patch('src.views.ui_issue_cfa.NodeInfoModel')
+def test_show_file_preview(mock_node_info_model, mock_qpix_map, mock_resize_image, mock_getsize, issue_cfa_widget):
"""Test the show_file_preview method."""
# Mock the NodeInfoModel to return a max file size of 10MB
@@ -173,10 +173,10 @@ def test_show_file_preview(mock_node_info_model, mock_qpix_map, mock_resize_imag
mock_node_info.node_info = MagicMock(spec=NodeInfoResponseModel)
mock_node_info.node_info.max_media_upload_size_mb = 10 # 10MB max size
- issue_rgb25_widget.file_path = MagicMock()
- issue_rgb25_widget.issue_rgb25_button = MagicMock()
- issue_rgb25_widget.issue_rgb_25_card = MagicMock()
- issue_rgb25_widget.upload_file = MagicMock()
+ issue_cfa_widget.file_path = MagicMock()
+ issue_cfa_widget.issue_cfa_button = MagicMock()
+ issue_cfa_widget.issue_cfa_card = MagicMock()
+ issue_cfa_widget.upload_file = MagicMock()
# Mock the file size returned by os.path.getsize
# 15MB (larger than the allowed 10MB)
@@ -190,22 +190,22 @@ def test_show_file_preview(mock_node_info_model, mock_qpix_map, mock_resize_imag
file_upload_message = 'path/to/file.jpg'
# Call the method under test
- issue_rgb25_widget.show_file_preview(file_upload_message)
+ issue_cfa_widget.show_file_preview(file_upload_message)
# Assert that the validation message is shown for large files
expected_validation_text = QCoreApplication.translate(
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'image_validation', None,
).format(mock_node_info.node_info.max_media_upload_size_mb)
- issue_rgb25_widget.file_path.setText.assert_called_once_with(
+ issue_cfa_widget.file_path.setText.assert_called_once_with(
expected_validation_text,
)
- issue_rgb25_widget.issue_rgb25_button.setDisabled.assert_any_call(
+ issue_cfa_widget.issue_cfa_button.setDisabled.assert_any_call(
True,
) # Assert it was disabled first
# Assert that the card's maximum size is set to (499, 608)
- issue_rgb25_widget.issue_rgb_25_card.setMaximumSize.assert_called_once_with(
+ issue_cfa_widget.issue_cfa_card.setMaximumSize.assert_called_once_with(
QSize(499, 608),
)
@@ -213,19 +213,19 @@ def test_show_file_preview(mock_node_info_model, mock_qpix_map, mock_resize_imag
mock_getsize.return_value = 5 * 1024 * 1024 # 5MB (valid size)
# Call the method again with a smaller file size
- issue_rgb25_widget.show_file_preview(file_upload_message)
+ issue_cfa_widget.show_file_preview(file_upload_message)
# Assert that the file path is displayed as the uploaded file
- issue_rgb25_widget.file_path.setText.assert_called_with(
+ issue_cfa_widget.file_path.setText.assert_called_with(
file_upload_message,
)
- issue_rgb25_widget.issue_rgb25_button.setDisabled.assert_any_call(
+ issue_cfa_widget.issue_cfa_button.setDisabled.assert_any_call(
False,
) # Assert it was enabled later
# Assert that the card's maximum size is set to (499, 808)
- issue_rgb25_widget.issue_rgb_25_card.setMaximumSize.assert_called_with(
+ issue_cfa_widget.issue_cfa_card.setMaximumSize.assert_called_with(
QSize(499, 808),
)
@@ -233,12 +233,12 @@ def test_show_file_preview(mock_node_info_model, mock_qpix_map, mock_resize_imag
mock_resize_image.assert_called_once_with(file_upload_message, 242, 242)
# Assert that the resized image is set to the file path as a pixmap
- issue_rgb25_widget.file_path.setPixmap.assert_called_once_with(
+ issue_cfa_widget.file_path.setPixmap.assert_called_once_with(
mock_qpix_map.return_value,
)
# Assert that the "change uploaded file" text is set
- issue_rgb25_widget.upload_file.setText.assert_called_once_with(
+ issue_cfa_widget.upload_file.setText.assert_called_once_with(
QCoreApplication.translate(
IRIS_WALLET_TRANSLATIONS_CONTEXT, 'change_uploaded_file', 'CHANGE UPLOADED FILE',
),
diff --git a/unit_tests/tests/ui_tests/ui_issue_nia_test.py b/unit_tests/tests/ui_tests/ui_issue_nia_test.py
new file mode 100644
index 00000000..3147d9c3
--- /dev/null
+++ b/unit_tests/tests/ui_tests/ui_issue_nia_test.py
@@ -0,0 +1,155 @@
+"""Unit test for Issue NIA UI."""
+# Disable the redefined-outer-name warning as
+# it's normal to pass mocked objects in test functions
+# pylint: disable=redefined-outer-name,unused-argument,protected-access
+from __future__ import annotations
+
+from unittest.mock import MagicMock
+
+import pytest
+
+from src.viewmodels.main_view_model import MainViewModel
+from src.views.ui_issue_nia import IssueNIAWidget
+from unit_tests.tests.ui_tests.ui_helper_test.issue_asset_helper_test import assert_success_page_called
+
+
+@pytest.fixture
+def issue_nia_page_navigation():
+ """Fixture to create a mocked page navigation object."""
+ mock_navigation = MagicMock()
+ return mock_navigation
+
+
+@pytest.fixture
+def mock_issue_nia_view_model(issue_nia_page_navigation: MagicMock):
+ """Fixture to create a MainViewModel instance with mocked page navigation."""
+ return MainViewModel(issue_nia_page_navigation)
+
+
+@pytest.fixture
+def issue_nia_widget(mock_issue_nia_view_model: MainViewModel):
+ """Fixture to create a IssueNIAWidget instance."""
+ return IssueNIAWidget(mock_issue_nia_view_model)
+
+
+def test_retranslate_ui(issue_nia_widget: IssueNIAWidget):
+ """Test that the UI strings are correctly translated."""
+ issue_nia_widget.retranslate_ui()
+
+ assert issue_nia_widget.asset_ticker_label.text() == 'asset_ticker'
+ assert issue_nia_widget.asset_name_label.text() == 'asset_name'
+
+
+def test_on_issue_nia_click(issue_nia_widget: IssueNIAWidget, qtbot):
+ """Test the on_issue_nia_click method."""
+ widget = issue_nia_widget
+
+ # Mock the input fields
+ widget.short_identifier_input = MagicMock()
+ widget.short_identifier_input.text.return_value = 'TTK'
+
+ widget.asset_name_input = MagicMock()
+ widget.asset_name_input.text.return_value = 'NIA'
+
+ widget.amount_input = MagicMock()
+ widget.amount_input.text.return_value = '100'
+
+ # Mock the view model method
+ widget._view_model.issue_nia_asset_view_model.on_issue_click = MagicMock()
+
+ # Simulate the click event
+ widget.on_issue_nia_click()
+
+ # Verify that the view model method was called with the correct arguments
+ widget._view_model.issue_nia_asset_view_model.on_issue_click.assert_called_once_with(
+ 'TTK', 'NIA', '100',
+ )
+
+
+def test_handle_button_enabled(issue_nia_widget: IssueNIAWidget, qtbot):
+ """Test the handle_button_enabled method."""
+ widget = issue_nia_widget
+
+ # Mock the input fields
+ widget.short_identifier_input = MagicMock()
+ widget.amount_input = MagicMock()
+ widget.asset_name_input = MagicMock()
+ widget.issue_nia_btn = MagicMock()
+
+ # Case when all fields are filled
+ widget.short_identifier_input.text.return_value = 'TTK'
+ widget.amount_input.text.return_value = '100'
+ widget.asset_name_input.text.return_value = 'NIA'
+
+ widget.handle_button_enabled()
+ widget.issue_nia_btn.setDisabled.assert_called_once_with(False)
+
+ # Case when one of the fields is empty
+ widget.short_identifier_input.text.return_value = ''
+
+ widget.handle_button_enabled()
+ widget.issue_nia_btn.setDisabled.assert_called_with(True)
+
+
+def test_asset_issued(issue_nia_widget: IssueNIAWidget, qtbot):
+ """Test the asset_issued method."""
+ widget = issue_nia_widget
+
+ # Mock the view model's navigation
+ widget._view_model.page_navigation.show_success_page = MagicMock()
+ widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
+
+ # Simulate asset issuance
+ asset_name = 'NIA'
+ widget.asset_issued(asset_name)
+
+ # Verify that the success page is shown with correct parameters
+ widget._view_model.page_navigation.show_success_page.assert_called_once()
+
+ params = widget._view_model.page_navigation.show_success_page.call_args[0][0]
+ assert_success_page_called(widget, asset_name)
+ assert params.callback == widget._view_model.page_navigation.fungibles_asset_page
+
+
+def test_update_loading_state_true(issue_nia_widget: IssueNIAWidget):
+ """Test the update_loading_state method when is_loading is True."""
+
+ issue_nia_widget.render_timer = MagicMock()
+ issue_nia_widget.issue_nia_btn = MagicMock()
+ issue_nia_widget.nia_close_btn = MagicMock()
+
+ # Call the method with is_loading=True
+ issue_nia_widget.update_loading_state(True)
+
+ # Assert that the render_timer starts
+ issue_nia_widget.render_timer.start.assert_called_once()
+
+ # Assert that the issue_nia_btn starts loading
+ issue_nia_widget.issue_nia_btn.start_loading.assert_called_once()
+
+ # Assert that the nia_close_btn is disabled
+ issue_nia_widget.nia_close_btn.setDisabled.assert_called_once_with(
+ True,
+ )
+
+
+def test_update_loading_state_false(issue_nia_widget: IssueNIAWidget):
+ """Test the update_loading_state method when is_loading is False."""
+
+ issue_nia_widget.render_timer = MagicMock()
+ issue_nia_widget.issue_nia_btn = MagicMock()
+ issue_nia_widget.nia_close_btn = MagicMock()
+
+ # Call the method with is_loading=False
+ issue_nia_widget.update_loading_state(False)
+
+ # Assert that the render_timer stops
+ issue_nia_widget.render_timer.stop.assert_called_once()
+
+ # Assert that the issue_nia_btn stops loading
+ issue_nia_widget.issue_nia_btn.stop_loading.assert_called_once()
+
+ # Assert that the nia_close_btn is enabled
+ issue_nia_widget.nia_close_btn.setDisabled.assert_called_once_with(
+ False,
+ )
diff --git a/unit_tests/tests/ui_tests/ui_issue_rgb20_test.py b/unit_tests/tests/ui_tests/ui_issue_rgb20_test.py
deleted file mode 100644
index f5e76320..00000000
--- a/unit_tests/tests/ui_tests/ui_issue_rgb20_test.py
+++ /dev/null
@@ -1,155 +0,0 @@
-"""Unit test for Issue RGB20 UI."""
-# Disable the redefined-outer-name warning as
-# it's normal to pass mocked objects in test functions
-# pylint: disable=redefined-outer-name,unused-argument,protected-access
-from __future__ import annotations
-
-from unittest.mock import MagicMock
-
-import pytest
-
-from src.viewmodels.main_view_model import MainViewModel
-from src.views.ui_issue_rgb20 import IssueRGB20Widget
-from unit_tests.tests.ui_tests.ui_helper_test.issue_asset_helper_test import assert_success_page_called
-
-
-@pytest.fixture
-def issue_rgb20_page_navigation():
- """Fixture to create a mocked page navigation object."""
- mock_navigation = MagicMock()
- return mock_navigation
-
-
-@pytest.fixture
-def mock_issue_rgb20_view_model(issue_rgb20_page_navigation: MagicMock):
- """Fixture to create a MainViewModel instance with mocked page navigation."""
- return MainViewModel(issue_rgb20_page_navigation)
-
-
-@pytest.fixture
-def issue_rgb20_widget(mock_issue_rgb20_view_model: MainViewModel):
- """Fixture to create a IssueRGB20Widget instance."""
- return IssueRGB20Widget(mock_issue_rgb20_view_model)
-
-
-def test_retranslate_ui(issue_rgb20_widget: IssueRGB20Widget):
- """Test that the UI strings are correctly translated."""
- issue_rgb20_widget.retranslate_ui()
-
- assert issue_rgb20_widget.asset_ticker_label.text() == 'asset_ticker'
- assert issue_rgb20_widget.asset_name_label.text() == 'asset_name'
-
-
-def test_on_issue_rgb20_click(issue_rgb20_widget: IssueRGB20Widget, qtbot):
- """Test the on_issue_rgb20_click method."""
- widget = issue_rgb20_widget
-
- # Mock the input fields
- widget.short_identifier_input = MagicMock()
- widget.short_identifier_input.text.return_value = 'TTK'
-
- widget.asset_name_input = MagicMock()
- widget.asset_name_input.text.return_value = 'RGB20'
-
- widget.amount_input = MagicMock()
- widget.amount_input.text.return_value = '100'
-
- # Mock the view model method
- widget._view_model.issue_rgb20_asset_view_model.on_issue_click = MagicMock()
-
- # Simulate the click event
- widget.on_issue_rgb20_click()
-
- # Verify that the view model method was called with the correct arguments
- widget._view_model.issue_rgb20_asset_view_model.on_issue_click.assert_called_once_with(
- 'TTK', 'RGB20', '100',
- )
-
-
-def test_handle_button_enabled(issue_rgb20_widget: IssueRGB20Widget, qtbot):
- """Test the handle_button_enabled method."""
- widget = issue_rgb20_widget
-
- # Mock the input fields
- widget.short_identifier_input = MagicMock()
- widget.amount_input = MagicMock()
- widget.asset_name_input = MagicMock()
- widget.issue_rgb20_btn = MagicMock()
-
- # Case when all fields are filled
- widget.short_identifier_input.text.return_value = 'TTK'
- widget.amount_input.text.return_value = '100'
- widget.asset_name_input.text.return_value = 'RGB20'
-
- widget.handle_button_enabled()
- widget.issue_rgb20_btn.setDisabled.assert_called_once_with(False)
-
- # Case when one of the fields is empty
- widget.short_identifier_input.text.return_value = ''
-
- widget.handle_button_enabled()
- widget.issue_rgb20_btn.setDisabled.assert_called_with(True)
-
-
-def test_asset_issued(issue_rgb20_widget: IssueRGB20Widget, qtbot):
- """Test the asset_issued method."""
- widget = issue_rgb20_widget
-
- # Mock the view model's navigation
- widget._view_model.page_navigation.show_success_page = MagicMock()
- widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
-
- # Simulate asset issuance
- asset_name = 'RGB20'
- widget.asset_issued(asset_name)
-
- # Verify that the success page is shown with correct parameters
- widget._view_model.page_navigation.show_success_page.assert_called_once()
-
- params = widget._view_model.page_navigation.show_success_page.call_args[0][0]
- assert_success_page_called(widget, asset_name)
- assert params.callback == widget._view_model.page_navigation.fungibles_asset_page
-
-
-def test_update_loading_state_true(issue_rgb20_widget: IssueRGB20Widget):
- """Test the update_loading_state method when is_loading is True."""
-
- issue_rgb20_widget.render_timer = MagicMock()
- issue_rgb20_widget.issue_rgb20_btn = MagicMock()
- issue_rgb20_widget.rgb_20_close_btn = MagicMock()
-
- # Call the method with is_loading=True
- issue_rgb20_widget.update_loading_state(True)
-
- # Assert that the render_timer starts
- issue_rgb20_widget.render_timer.start.assert_called_once()
-
- # Assert that the issue_rgb20_btn starts loading
- issue_rgb20_widget.issue_rgb20_btn.start_loading.assert_called_once()
-
- # Assert that the rgb_20_close_btn is disabled
- issue_rgb20_widget.rgb_20_close_btn.setDisabled.assert_called_once_with(
- True,
- )
-
-
-def test_update_loading_state_false(issue_rgb20_widget: IssueRGB20Widget):
- """Test the update_loading_state method when is_loading is False."""
-
- issue_rgb20_widget.render_timer = MagicMock()
- issue_rgb20_widget.issue_rgb20_btn = MagicMock()
- issue_rgb20_widget.rgb_20_close_btn = MagicMock()
-
- # Call the method with is_loading=False
- issue_rgb20_widget.update_loading_state(False)
-
- # Assert that the render_timer stops
- issue_rgb20_widget.render_timer.stop.assert_called_once()
-
- # Assert that the issue_rgb20_btn stops loading
- issue_rgb20_widget.issue_rgb20_btn.stop_loading.assert_called_once()
-
- # Assert that the rgb_20_close_btn is enabled
- issue_rgb20_widget.rgb_20_close_btn.setDisabled.assert_called_once_with(
- False,
- )
diff --git a/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py b/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py
index e86f5d61..54346ec2 100644
--- a/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py
+++ b/unit_tests/tests/ui_tests/ui_receive_rgb_asset_test.py
@@ -24,7 +24,7 @@ def receive_rgb_asset_widget(qtbot):
"""Fixture to create and return an instance of ReceiveRGBAssetWidget."""
mock_navigation = MagicMock()
mock_view_model = MagicMock(MainViewModel(mock_navigation))
- asset_data = AssetDataModel(asset_type='RGB25', asset_id='test_asset_id')
+ asset_data = AssetDataModel(asset_type='CFA', asset_id='test_asset_id')
widget = ReceiveRGBAssetWidget(mock_view_model, asset_data)
qtbot.addWidget(widget)
return widget
@@ -32,11 +32,11 @@ def receive_rgb_asset_widget(qtbot):
def test_generate_invoice(receive_rgb_asset_widget: ReceiveRGBAssetWidget):
"""Test that generate_invoice calls get_rgb_invoice for specific asset types."""
- receive_rgb_asset_widget._view_model.receive_rgb25_view_model.get_rgb_invoice = MagicMock()
+ receive_rgb_asset_widget._view_model.receive_cfa_view_model.get_rgb_invoice = MagicMock()
receive_rgb_asset_widget.generate_invoice()
- receive_rgb_asset_widget._view_model.receive_rgb25_view_model.get_rgb_invoice.assert_called_once_with(
+ receive_rgb_asset_widget._view_model.receive_cfa_view_model.get_rgb_invoice.assert_called_once_with(
1, 'test_asset_id',
)
@@ -54,10 +54,10 @@ def test_setup_ui_connection(receive_rgb_asset_widget: ReceiveRGBAssetWidget):
receive_rgb_asset_widget.receive_rgb_asset_page.copy_button.clicked.emit()
# Verify UI connections are set up
- receive_rgb_asset_widget._view_model.receive_rgb25_view_model.address.connect.assert_called_once()
+ receive_rgb_asset_widget._view_model.receive_cfa_view_model.address.connect.assert_called_once()
receive_rgb_asset_widget._view_model.ln_offchain_view_model.invoice_get_event.connect.assert_called()
- receive_rgb_asset_widget._view_model.receive_rgb25_view_model.message.connect.assert_called()
- receive_rgb_asset_widget._view_model.receive_rgb25_view_model.hide_loading.connect.assert_called()
+ receive_rgb_asset_widget._view_model.receive_cfa_view_model.message.connect.assert_called()
+ receive_rgb_asset_widget._view_model.receive_cfa_view_model.hide_loading.connect.assert_called()
# Verify address label text is set correctly
assert receive_rgb_asset_widget.receive_rgb_asset_page.address_label.text() == QCoreApplication.translate(
@@ -80,22 +80,22 @@ def test_close_button_navigation(receive_rgb_asset_widget):
# Mock ToastManager for error messages
with patch.object(ToastManager, 'error') as mock_error:
- # Test case 1: Navigation for AssetType.RGB25
- receive_rgb_asset_widget.close_page_navigation = AssetType.RGB25.value
+ # Test case 1: Navigation for AssetType.CFA
+ receive_rgb_asset_widget.close_page_navigation = AssetType.CFA.value
receive_rgb_asset_widget.close_button_navigation()
receive_rgb_asset_widget._view_model.page_navigation.collectibles_asset_page.assert_called_once()
receive_rgb_asset_widget._view_model.page_navigation.collectibles_asset_page.reset_mock()
- # Test case 2: Navigation for AssetType.RGB20
- receive_rgb_asset_widget.close_page_navigation = AssetType.RGB20.value
+ # Test case 2: Navigation for AssetType.NIA
+ receive_rgb_asset_widget.close_page_navigation = AssetType.NIA.value
receive_rgb_asset_widget.close_button_navigation()
receive_rgb_asset_widget._view_model.page_navigation.fungibles_asset_page.assert_called_once()
receive_rgb_asset_widget._view_model.page_navigation.fungibles_asset_page.reset_mock()
- # Test case 3: Specific originating page (e.g., 'RGB20')
+ # Test case 3: Specific originating page (e.g., 'NIA')
# Simulate no specific asset navigation
receive_rgb_asset_widget.close_page_navigation = None
- receive_rgb_asset_widget.originating_page = 'RGB20'
+ receive_rgb_asset_widget.originating_page = 'NIA'
receive_rgb_asset_widget.close_button_navigation()
receive_rgb_asset_widget._view_model.page_navigation.fungibles_asset_page.assert_called_once()
receive_rgb_asset_widget._view_model.page_navigation.fungibles_asset_page.reset_mock()
diff --git a/unit_tests/tests/ui_tests/ui_rgb_asset_detail_test.py b/unit_tests/tests/ui_tests/ui_rgb_asset_detail_test.py
index 158a2357..58ab2037 100644
--- a/unit_tests/tests/ui_tests/ui_rgb_asset_detail_test.py
+++ b/unit_tests/tests/ui_tests/ui_rgb_asset_detail_test.py
@@ -24,6 +24,7 @@
from src.model.selection_page_model import AssetDataModel
from src.model.transaction_detail_page_model import TransactionDetailPageModel
from src.utils.constant import IRIS_WALLET_TRANSLATIONS_CONTEXT
+from src.utils.rgb_asset_helpers import set_asset_image
from src.viewmodels.main_view_model import MainViewModel
from src.views.components.transaction_detail_frame import TransactionDetailFrame
from src.views.ui_rgb_asset_detail import RGBAssetDetailWidget
@@ -44,7 +45,7 @@ def rgb_asset_detail_widget(qtbot):
# Mock the params as an instance of RgbAssetPageLoadModel
mock_params = MagicMock()
- mock_params.asset_type = 'RGB20' # Set the asset_type as needed
+ mock_params.asset_type = 'NIA' # Set the asset_type as needed
widget = RGBAssetDetailWidget(view_model, mock_params)
qtbot.addWidget(widget)
@@ -59,76 +60,16 @@ def test_retranslate_ui(rgb_asset_detail_widget: RGBAssetDetailWidget):
assert rgb_asset_detail_widget.transactions_label.text() == 'transfers'
-def test_valid_hex_string(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test with valid hex strings."""
- valid_hex_strings = [
- '00', # simple hex
- '0a1b2c3d4e5f', # longer valid hex
- 'AABBCCDDEE', # uppercase hex
- '1234567890abcdef', # mixed lower and uppercase
- ]
- for hex_string in valid_hex_strings:
- assert rgb_asset_detail_widget.is_hex_string(hex_string) is True
-
-
-def test_invalid_hex_string(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test with invalid hex strings."""
- invalid_hex_strings = [
- '00G1', # contains non-hex character 'G'
- '123z', # contains non-hex character 'z'
- '12345', # odd length
- '0x1234', # prefixed with '0x'
- ' ', # empty or space character
- ]
- for hex_string in invalid_hex_strings:
- assert rgb_asset_detail_widget.is_hex_string(hex_string) is False
-
-
-def test_empty_string(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test with an empty string."""
- assert rgb_asset_detail_widget.is_hex_string('') is False
-
-
-def test_odd_length_string(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test with a string of odd length."""
- odd_length_hex_strings = [
- '1', # single character
- '123', # three characters
- '12345', # five characters
- ]
- for hex_string in odd_length_hex_strings:
- assert rgb_asset_detail_widget.is_hex_string(hex_string) is False
-
-
-def test_is_path(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test the is_path method with various file paths."""
-
- # Test valid Unix-like paths
- assert rgb_asset_detail_widget.is_path('/path/to/file') is True
- assert rgb_asset_detail_widget.is_path('/usr/local/bin/') is True
- assert rgb_asset_detail_widget.is_path('/home/user/doc-1.txt') is True
-
- # Test invalid paths
- assert rgb_asset_detail_widget.is_path(
- 'invalid/path',
- ) is False # No leading slash
- assert rgb_asset_detail_widget.is_path(123) is False # Non-string input
- assert rgb_asset_detail_widget.is_path('') is False # Empty string
- assert rgb_asset_detail_widget.is_path(
- 'C:\\Windows\\Path',
- ) is False # Windows path format
-
-
-def test_handle_page_navigation_rgb_20(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test page navigation handling when asset type is RGB20."""
- rgb_asset_detail_widget.asset_type = AssetType.RGB20.value
+def test_handle_page_navigation_nia(rgb_asset_detail_widget: RGBAssetDetailWidget):
+ """Test page navigation handling when asset type is NIA."""
+ rgb_asset_detail_widget.asset_type = AssetType.NIA.value
rgb_asset_detail_widget.handle_page_navigation()
rgb_asset_detail_widget._view_model.page_navigation.fungibles_asset_page.assert_called_once()
-def test_handle_page_navigation_rgb_25(rgb_asset_detail_widget: RGBAssetDetailWidget):
- """Test page navigation handling when asset type is RGB25."""
- rgb_asset_detail_widget.asset_type = AssetType.RGB25.value
+def test_handle_page_navigation_cfa(rgb_asset_detail_widget: RGBAssetDetailWidget):
+ """Test page navigation handling when asset type is CFA."""
+ rgb_asset_detail_widget.asset_type = AssetType.CFA.value
rgb_asset_detail_widget.handle_page_navigation()
rgb_asset_detail_widget._view_model.page_navigation.collectibles_asset_page.assert_called_once()
@@ -140,7 +81,7 @@ def test_set_transaction_detail_frame(rgb_asset_detail_widget: RGBAssetDetailWid
asset_id = 'test_asset_id'
asset_name = 'Test Asset'
image_path = asset_image_path
- asset_type = AssetType.RGB20.value
+ asset_type = AssetType.NIA.value
# Mock the transaction details
mock_transaction = MagicMock()
@@ -162,7 +103,7 @@ def test_set_transaction_detail_frame(rgb_asset_detail_widget: RGBAssetDetailWid
mock_transactions.off_chain_transfers = [mock_transaction]
mock_transactions.transfers = [mock_transaction]
- rgb_asset_detail_widget._view_model.rgb25_view_model.txn_list = mock_transactions
+ rgb_asset_detail_widget._view_model.cfa_view_model.txn_list = mock_transactions
# Call the method to test
rgb_asset_detail_widget.set_transaction_detail_frame(
@@ -212,7 +153,7 @@ def test_set_transaction_detail_frame(rgb_asset_detail_widget: RGBAssetDetailWid
mock_transactions.onchain_transfers = [mock_transaction]
mock_transactions.off_chain_transfers = [mock_transaction]
- rgb_asset_detail_widget._view_model.rgb25_view_model.txn_list = mock_transactions
+ rgb_asset_detail_widget._view_model.cfa_view_model.txn_list = mock_transactions
# Call the method to test
rgb_asset_detail_widget.set_transaction_detail_frame(
@@ -236,8 +177,8 @@ def test_set_transaction_detail_frame(rgb_asset_detail_widget: RGBAssetDetailWid
) == mock_transaction.amount_status
-@patch('src.views.ui_rgb_asset_detail.convert_hex_to_image')
-@patch('src.views.ui_rgb_asset_detail.resize_image')
+@patch('src.utils.rgb_asset_helpers.convert_hex_to_image')
+@patch('src.utils.rgb_asset_helpers.resize_image')
def test_set_asset_image(mock_resize_image, mock_convert_hex_to_image, rgb_asset_detail_widget: RGBAssetDetailWidget):
"""Test setting the asset image with mocked image conversion and resizing."""
@@ -255,7 +196,7 @@ def test_set_asset_image(mock_resize_image, mock_convert_hex_to_image, rgb_asset
mock_resize_image.return_value = mock_resized_pixmap
# Test with hex string
- rgb_asset_detail_widget.set_asset_image(mock_hex_image)
+ set_asset_image(rgb_asset_detail_widget.label_asset_name, mock_hex_image)
# Verify that the convert_hex_to_image was called with the correct hex string
mock_convert_hex_to_image.assert_called_once_with(mock_hex_image)
@@ -340,7 +281,7 @@ def test_select_receive_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWid
# Set up mock data for the test
asset_id = 'test_asset_id'
- asset_type = AssetType.RGB20.value
+ asset_type = AssetType.NIA.value
rgb_asset_detail_widget.asset_id_detail.setPlainText(
asset_id,
) # Mock asset_id in widget
@@ -356,9 +297,9 @@ def test_select_receive_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWid
rgb_asset_detail_widget,
'navigate_to_selection_page',
)
- mock_receive_rgb25 = mocker.patch.object(
+ mock_receive_cfa = mocker.patch.object(
rgb_asset_detail_widget._view_model.page_navigation,
- 'receive_rgb25_page',
+ 'receive_cfa_page',
)
# Case 1: Channel is open for the asset (is_channel_open_for_asset returns True)
@@ -371,11 +312,11 @@ def test_select_receive_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWid
mock_navigate.assert_called_once_with(
TransferStatusEnumModel.RECEIVE.value,
)
- mock_receive_rgb25.assert_not_called()
+ mock_receive_cfa.assert_not_called()
# Reset the mocks to ensure clean state for the next test case
mock_navigate.reset_mock()
- mock_receive_rgb25.reset_mock()
+ mock_receive_cfa.reset_mock()
# Case 2: Channel is not open for the asset (is_channel_open_for_asset returns False)
mock_is_channel_open.return_value = False
@@ -384,7 +325,7 @@ def test_select_receive_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWid
rgb_asset_detail_widget.select_receive_transfer_type()
# Assertions for when the channel is not open
- mock_receive_rgb25.assert_called_once_with(
+ mock_receive_cfa.assert_called_once_with(
params=AssetDataModel(
asset_type=asset_type,
asset_id=asset_id,
@@ -413,9 +354,9 @@ def test_select_send_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWidget
rgb_asset_detail_widget,
'navigate_to_selection_page',
)
- mock_send_rgb25 = mocker.patch.object(
+ mock_send_cfa = mocker.patch.object(
rgb_asset_detail_widget._view_model.page_navigation,
- 'send_rgb25_page',
+ 'send_cfa_page',
)
# Case 1: Channel is open for the asset (is_channel_open_for_asset returns True)
@@ -428,11 +369,11 @@ def test_select_send_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWidget
mock_navigate.assert_called_once_with(
TransferStatusEnumModel.SEND.value,
)
- mock_send_rgb25.assert_not_called()
+ mock_send_cfa.assert_not_called()
# Reset the mocks to ensure clean state for the next test case
mock_navigate.reset_mock()
- mock_send_rgb25.reset_mock()
+ mock_send_cfa.reset_mock()
# Case 2: Channel is not open for the asset (is_channel_open_for_asset returns False)
mock_is_channel_open.return_value = False
@@ -441,7 +382,7 @@ def test_select_send_transfer_type(rgb_asset_detail_widget: RGBAssetDetailWidget
rgb_asset_detail_widget.select_send_transfer_type()
# Assertions for when the channel is not open
- mock_send_rgb25.assert_called_once()
+ mock_send_cfa.assert_called_once()
mock_navigate.assert_not_called()
@@ -450,7 +391,7 @@ def test_refresh_transaction(rgb_asset_detail_widget: RGBAssetDetailWidget):
# Mock the render timer and the refresh function
rgb_asset_detail_widget.render_timer = MagicMock()
- rgb_asset_detail_widget._view_model.rgb25_view_model.on_refresh_click = MagicMock()
+ rgb_asset_detail_widget._view_model.cfa_view_model.on_refresh_click = MagicMock()
# Call the method
rgb_asset_detail_widget.refresh_transaction()
@@ -459,7 +400,7 @@ def test_refresh_transaction(rgb_asset_detail_widget: RGBAssetDetailWidget):
# Verify render_timer.start was called once
rgb_asset_detail_widget.render_timer.start.assert_called_once()
# Verify on_refresh_click was called once
- rgb_asset_detail_widget._view_model.rgb25_view_model.on_refresh_click.assert_called_once()
+ rgb_asset_detail_widget._view_model.cfa_view_model.on_refresh_click.assert_called_once()
def test_handle_asset_frame_click(rgb_asset_detail_widget: RGBAssetDetailWidget):
@@ -474,13 +415,13 @@ def test_handle_asset_frame_click(rgb_asset_detail_widget: RGBAssetDetailWidget)
)
# Mock the navigation method
- rgb_asset_detail_widget._view_model.page_navigation.rgb25_transaction_detail_page = MagicMock()
+ rgb_asset_detail_widget._view_model.page_navigation.cfa_transaction_detail_page = MagicMock()
# Call the method
rgb_asset_detail_widget.handle_asset_frame_click(params)
# Assertions to check if the navigation method was called with the correct parameters
- rgb_asset_detail_widget._view_model.page_navigation.rgb25_transaction_detail_page.assert_called_once_with(
+ rgb_asset_detail_widget._view_model.page_navigation.cfa_transaction_detail_page.assert_called_once_with(
params,
)
@@ -720,9 +661,13 @@ def test_handle_fail_transfer(rgb_asset_detail_widget: RGBAssetDetailWidget):
# Verify dialog was created with correct message containing tx_id
mock_confirmation_dialog.assert_called_with(
parent=rgb_asset_detail_widget,
- message=f"{QCoreApplication.translate(IRIS_WALLET_TRANSLATIONS_CONTEXT, 'transaction_id', None)}: {
+ message=f"""{
+ QCoreApplication.translate(
+ IRIS_WALLET_TRANSLATIONS_CONTEXT, 'transaction_id', None,
+ )
+ }: {
tx_id
- }\n\n {QCoreApplication.translate(IRIS_WALLET_TRANSLATIONS_CONTEXT, 'cancel_transfer', None)}",
+ }\n\n {QCoreApplication.translate(IRIS_WALLET_TRANSLATIONS_CONTEXT, 'cancel_transfer', None)}""",
)
# Reset mock for next test
@@ -768,7 +713,7 @@ def test_confirm_fail_transfer(rgb_asset_detail_widget: RGBAssetDetailWidget):
rgb_asset_detail_widget._confirm_fail_transfer(idx)
# Verify the view model method was called with correct index
- rgb_asset_detail_widget._view_model.rgb25_view_model.on_fail_transfer.assert_called_once_with(
+ rgb_asset_detail_widget._view_model.cfa_view_model.on_fail_transfer.assert_called_once_with(
idx,
)
diff --git a/unit_tests/tests/ui_tests/ui_rgb_asset_transaction_detail_test.py b/unit_tests/tests/ui_tests/ui_rgb_asset_transaction_detail_test.py
index c0eb977a..34194afb 100644
--- a/unit_tests/tests/ui_tests/ui_rgb_asset_transaction_detail_test.py
+++ b/unit_tests/tests/ui_tests/ui_rgb_asset_transaction_detail_test.py
@@ -55,7 +55,7 @@ def rgb_asset_transaction_detail_widget(qtbot):
params.recipient_id = 'recipient_124'
params.receive_utxo = 'utxo_1234'
params.change_utxo = 'utxo_4567'
- params.asset_type = 'RGB20'
+ params.asset_type = 'NIA'
params.is_off_chain = False # Add the missing attribute
# Initialize the widget
@@ -222,18 +222,18 @@ def test_handle_close(rgb_asset_transaction_detail_widget: RGBAssetTransactionDe
rgb_asset_transaction_detail_widget.params.asset_type = 'Test Type'
# Mock the view model methods (signal and navigation)
- rgb_asset_transaction_detail_widget._view_model.rgb25_view_model.asset_info = MagicMock()
- rgb_asset_transaction_detail_widget._view_model.page_navigation.rgb25_detail_page = MagicMock()
+ rgb_asset_transaction_detail_widget._view_model.cfa_view_model.asset_info = MagicMock()
+ rgb_asset_transaction_detail_widget._view_model.page_navigation.cfa_detail_page = MagicMock()
# Call the method to test
rgb_asset_transaction_detail_widget.handle_close()
# Assert that the signal is emitted with the correct parameters
- rgb_asset_transaction_detail_widget._view_model.rgb25_view_model.asset_info.emit.assert_called_once_with(
+ rgb_asset_transaction_detail_widget._view_model.cfa_view_model.asset_info.emit.assert_called_once_with(
'123', 'Test Asset', 'path/to/image', 'Test Type',
)
# Assert that the navigation method is called with the correct argument
- rgb_asset_transaction_detail_widget._view_model.page_navigation.rgb25_detail_page.assert_called_once_with(
+ rgb_asset_transaction_detail_widget._view_model.page_navigation.cfa_detail_page.assert_called_once_with(
RgbAssetPageLoadModel(asset_type='Test Type'),
)
diff --git a/unit_tests/tests/ui_tests/ui_send_ln_invoice_test.py b/unit_tests/tests/ui_tests/ui_send_ln_invoice_test.py
index 6916f3aa..1d676396 100644
--- a/unit_tests/tests/ui_tests/ui_send_ln_invoice_test.py
+++ b/unit_tests/tests/ui_tests/ui_send_ln_invoice_test.py
@@ -20,7 +20,7 @@ def send_ln_invoice_widget(qtbot):
"""Fixture to create and return an instance of SendLnInvoiceWidget."""
mock_navigation = MagicMock()
view_model = MagicMock(MainViewModel(mock_navigation))
- asset_type = 'RGB20'
+ asset_type = 'NIA'
widget = SendLnInvoiceWidget(view_model, asset_type)
qtbot.addWidget(widget)
return widget
@@ -30,7 +30,6 @@ def test_show_invoice_detail(send_ln_invoice_widget: SendLnInvoiceWidget):
"""Test the show_invoice_detail method with a valid invoice detail."""
invoice_detail_mock = MagicMock()
invoice_detail_mock.recipient_id = 'utxob:2PoDFyk-8aegNHZE4-inHHn4nWz-rNtAX3MWv-sTiVPQYrF-ed2bXM'
- invoice_detail_mock.asset_iface = 'RGB20'
invoice_detail_mock.asset_id = 'rgb:2eVw8uw-8G88LQ2tQ-kexM12SoD-nCX8DmQrw-yLMu6JDfK-xx1SCfc'
invoice_detail_mock.amount = 69
invoice_detail_mock.network = 'Regtest'
@@ -373,15 +372,15 @@ def test_send_asset(send_ln_invoice_widget):
def test_on_success_sent_navigation_collectibles(send_ln_invoice_widget):
- """Test the on_success_sent_navigation method when asset type is RGB25 (collectibles)."""
+ """Test the on_success_sent_navigation method when asset type is CFA (collectibles)."""
# Mocking the widget's properties and methods
send_ln_invoice_widget._view_model = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.collectibles_asset_page = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
- # Set the asset type to RGB25 (collectibles)
- send_ln_invoice_widget.asset_type = AssetType.RGB25.value
+ # Set the asset type to CFA (collectibles)
+ send_ln_invoice_widget.asset_type = AssetType.CFA.value
# Call the method
send_ln_invoice_widget.on_success_sent_navigation()
@@ -394,14 +393,14 @@ def test_on_success_sent_navigation_collectibles(send_ln_invoice_widget):
def test_on_success_sent_navigation_fungibles(send_ln_invoice_widget):
- """Test the on_success_sent_navigation method when asset type is not RGB25 (fungibles)."""
+ """Test the on_success_sent_navigation method when asset type is not CFA (fungibles)."""
# Mocking the widget's properties and methods
send_ln_invoice_widget._view_model = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.collectibles_asset_page = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
- # Set the asset type to a non-RGB25 value (fungibles)
+ # Set the asset type to a non-CFA value (fungibles)
send_ln_invoice_widget.asset_type = 'some_other_asset_type'
# Call the method
@@ -453,15 +452,15 @@ def test_update_loading_state_not_loading(send_ln_invoice_widget):
def test_on_click_close_button_collectibles(send_ln_invoice_widget):
- """Test the on_click_close_button method when asset type is RGB25 (collectibles)."""
+ """Test the on_click_close_button method when asset type is CFA (collectibles)."""
# Mocking the widget's properties and methods
send_ln_invoice_widget._view_model = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.collectibles_asset_page = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
- # Set the asset type to RGB25 (collectibles)
- send_ln_invoice_widget.asset_type = AssetType.RGB25.value
+ # Set the asset type to CFA (collectibles)
+ send_ln_invoice_widget.asset_type = AssetType.CFA.value
# Call the method
send_ln_invoice_widget.on_click_close_button()
@@ -474,14 +473,14 @@ def test_on_click_close_button_collectibles(send_ln_invoice_widget):
def test_on_click_close_button_fungibles(send_ln_invoice_widget):
- """Test the on_click_close_button method when asset type is not RGB25 (fungibles)."""
+ """Test the on_click_close_button method when asset type is not CFA (fungibles)."""
# Mocking the widget's properties and methods
send_ln_invoice_widget._view_model = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.collectibles_asset_page = MagicMock()
send_ln_invoice_widget._view_model.page_navigation.fungibles_asset_page = MagicMock()
- # Set the asset type to a non-RGB25 value (fungibles)
+ # Set the asset type to a non-CFA value (fungibles)
send_ln_invoice_widget.asset_type = 'some_other_asset_type'
# Call the method
diff --git a/unit_tests/tests/ui_tests/ui_send_rgb_asset_test.py b/unit_tests/tests/ui_tests/ui_send_rgb_asset_test.py
index 2f39c4a2..030b42d3 100644
--- a/unit_tests/tests/ui_tests/ui_send_rgb_asset_test.py
+++ b/unit_tests/tests/ui_tests/ui_send_rgb_asset_test.py
@@ -36,11 +36,11 @@ def send_rgb_asset_widget(qtbot):
asset_balance = Balance(future=100, spendable=50, settled=100)
txn_list = MagicMock(ListTransferAssetWithBalanceResponseModel)
txn_list.asset_balance = asset_balance
- view_model.rgb25_view_model.txn_list = txn_list
+ view_model.cfa_view_model.txn_list = txn_list
- # Mock the attributes of rgb25_view_model
- view_model.rgb25_view_model.is_loading = MagicMock()
- view_model.rgb25_view_model.stop_loading = MagicMock()
+ # Mock the attributes of cfa_view_model
+ view_model.cfa_view_model.is_loading = MagicMock()
+ view_model.cfa_view_model.stop_loading = MagicMock()
widget = SendRGBAssetWidget(view_model)
qtbot.addWidget(widget)
@@ -108,7 +108,7 @@ def test_set_asset_balance(send_rgb_asset_widget: SendRGBAssetWidget, qtbot):
assert send_rgb_asset_widget.send_rgb_asset_page.send_btn.isEnabled()
# Test with zero spendable balance
- send_rgb_asset_widget._view_model.rgb25_view_model.txn_list.asset_balance.spendable = 0
+ send_rgb_asset_widget._view_model.cfa_view_model.txn_list.asset_balance.spendable = 0
send_rgb_asset_widget.set_asset_balance()
assert not send_rgb_asset_widget.send_rgb_asset_page.send_btn.isEnabled()
@@ -134,13 +134,13 @@ def test_handle_show_message(send_rgb_asset_widget: SendRGBAssetWidget):
def test_handle_message(send_rgb_asset_widget: SendRGBAssetWidget):
"""Test the handle_message method of WelcomeWidget."""
with patch('src.views.ui_send_rgb_asset.ToastManager') as mock_toast_manager:
- send_rgb_asset_widget.show_rgb25_message(
+ send_rgb_asset_widget.show_cfa_message(
ToastPreset.ERROR, 'Test Error Message',
)
mock_toast_manager.error.assert_called_once_with('Test Error Message')
mock_toast_manager.success.assert_not_called()
- send_rgb_asset_widget.show_rgb25_message(
+ send_rgb_asset_widget.show_cfa_message(
ToastPreset.SUCCESS, 'Test Success Message',
)
mock_toast_manager.error.assert_called_once()
@@ -153,21 +153,21 @@ def test_refresh_asset(send_rgb_asset_widget: SendRGBAssetWidget, mocker):
"""Test the refresh_asset method of the widget."""
# Mock the view model and its methods
- mock_rgb25_view_model = MagicMock()
+ mock_cfa_view_model = MagicMock()
mock_view_model = MagicMock()
send_rgb_asset_widget._view_model = mock_view_model
- send_rgb_asset_widget._view_model.rgb25_view_model = mock_rgb25_view_model
+ send_rgb_asset_widget._view_model.cfa_view_model = mock_cfa_view_model
# Mock the values that should be set on the view model
- mock_rgb25_view_model.asset_id = '123'
- mock_rgb25_view_model.asset_name = 'Asset Name'
- mock_rgb25_view_model.image_path = 'path/to/image'
- mock_rgb25_view_model.asset_type = 'type'
+ mock_cfa_view_model.asset_id = '123'
+ mock_cfa_view_model.asset_name = 'Asset Name'
+ mock_cfa_view_model.image_path = 'path/to/image'
+ mock_cfa_view_model.asset_type = 'type'
- # Mock the get_rgb25_asset_detail method
- mock_get_rgb25_asset_detail = MagicMock()
- mock_rgb25_view_model.get_rgb25_asset_detail = mock_get_rgb25_asset_detail
+ # Mock the get_cfa_asset_detail method
+ mock_get_cfa_asset_detail = MagicMock()
+ mock_cfa_view_model.get_cfa_asset_detail = mock_get_cfa_asset_detail
# Call the refresh_asset method
send_rgb_asset_widget.refresh_asset()
@@ -176,7 +176,7 @@ def test_refresh_asset(send_rgb_asset_widget: SendRGBAssetWidget, mocker):
assert send_rgb_asset_widget.loading_performer == 'REFRESH_BUTTON'
# Verify that on_refresh_click was called
- mock_rgb25_view_model.on_refresh_click.assert_called_once()
+ mock_cfa_view_model.on_refresh_click.assert_called_once()
# Verify that the asset details were correctly assigned
assert send_rgb_asset_widget.asset_id == '123'
@@ -184,8 +184,8 @@ def test_refresh_asset(send_rgb_asset_widget: SendRGBAssetWidget, mocker):
assert send_rgb_asset_widget.image_path == 'path/to/image'
assert send_rgb_asset_widget.asset_type == 'type'
- # Verify that get_rgb25_asset_detail was called with the correct arguments
- mock_rgb25_view_model.get_rgb25_asset_detail.assert_called_once_with(
+ # Verify that get_cfa_asset_detail was called with the correct arguments
+ mock_cfa_view_model.get_cfa_asset_detail.assert_called_once_with(
asset_id='123',
asset_name='Asset Name',
image_path='path/to/image',
@@ -196,11 +196,11 @@ def test_refresh_asset(send_rgb_asset_widget: SendRGBAssetWidget, mocker):
def test_set_originating_page(send_rgb_asset_widget: SendRGBAssetWidget):
"""Test the set_originating_page method of the widget."""
- # Test when asset_type is 'RGB20'
- send_rgb_asset_widget.set_originating_page('RGB20')
+ # Test when asset_type is 'NIA'
+ send_rgb_asset_widget.set_originating_page('NIA')
- # Verify the asset_type is set to 'RGB20'
- assert send_rgb_asset_widget.asset_type == 'RGB20'
+ # Verify the asset_type is set to 'NIA'
+ assert send_rgb_asset_widget.asset_type == 'NIA'
def test_rgb_asset_page_navigation(send_rgb_asset_widget: SendRGBAssetWidget, mocker):
@@ -214,8 +214,8 @@ def test_rgb_asset_page_navigation(send_rgb_asset_widget: SendRGBAssetWidget, mo
send_rgb_asset_widget._view_model.page_navigation = mock_page_navigation
mock_page_navigation.sidebar.return_value = mock_sidebar
- # Test when asset_type is 'RGB20'
- send_rgb_asset_widget.asset_type = 'RGB20'
+ # Test when asset_type is 'NIA'
+ send_rgb_asset_widget.asset_type = 'NIA'
# Call the rgb_asset_page_navigation method
send_rgb_asset_widget.rgb_asset_page_navigation()
@@ -226,7 +226,7 @@ def test_rgb_asset_page_navigation(send_rgb_asset_widget: SendRGBAssetWidget, mo
# Verify that fungibles_asset_page was called
mock_page_navigation.fungibles_asset_page.assert_called_once()
- # Test when asset_type is not 'RGB20' (e.g., 'OtherType')
+ # Test when asset_type is not 'NIA' (e.g., 'OtherType')
send_rgb_asset_widget.asset_type = 'OtherType'
# Call the rgb_asset_page_navigation method again
@@ -270,7 +270,7 @@ def test_send_rgb_asset_button_success(send_rgb_asset_widget: SendRGBAssetWidget
# Mock the on_send_click method
mock_on_send_click = MagicMock()
- send_rgb_asset_widget._view_model.rgb25_view_model.on_send_click = mock_on_send_click
+ send_rgb_asset_widget._view_model.cfa_view_model.on_send_click = mock_on_send_click
# Mock ToastManager to prevent actual toast displays
mock_toast_manager = MagicMock()
@@ -352,7 +352,7 @@ def test_send_rgb_asset_button_send_error(send_rgb_asset_widget: SendRGBAssetWid
# Mock on_send_click to raise CommonException
mock_error = CommonException('Send failed')
mock_on_send_click = MagicMock(side_effect=mock_error)
- send_rgb_asset_widget._view_model.rgb25_view_model.on_send_click = mock_on_send_click
+ send_rgb_asset_widget._view_model.cfa_view_model.on_send_click = mock_on_send_click
# Use patch to mock ToastManager
with patch('src.views.ui_send_rgb_asset.ToastManager') as mock_toast_manager:
diff --git a/unit_tests/tests/ui_tests/ui_settings_test.py b/unit_tests/tests/ui_tests/ui_settings_test.py
index 719c6e0c..dca1fa41 100644
--- a/unit_tests/tests/ui_tests/ui_settings_test.py
+++ b/unit_tests/tests/ui_tests/ui_settings_test.py
@@ -9,22 +9,10 @@
import pytest
from PySide6.QtGui import QIntValidator
-from PySide6.QtWidgets import QDialog
from src.model.enums.enums_model import NetworkEnumModel
-from src.utils.constant import BITCOIND_RPC_HOST_MAINNET
-from src.utils.constant import BITCOIND_RPC_HOST_REGTEST
-from src.utils.constant import BITCOIND_RPC_HOST_TESTNET
-from src.utils.constant import BITCOIND_RPC_PORT_MAINNET
-from src.utils.constant import BITCOIND_RPC_PORT_REGTEST
-from src.utils.constant import BITCOIND_RPC_PORT_TESTNET
-from src.utils.constant import INDEXER_URL_MAINNET
-from src.utils.constant import INDEXER_URL_REGTEST
-from src.utils.constant import INDEXER_URL_TESTNET
-from src.utils.constant import PROXY_ENDPOINT_MAINNET
-from src.utils.constant import PROXY_ENDPOINT_REGTEST
-from src.utils.constant import PROXY_ENDPOINT_TESTNET
from src.viewmodels.main_view_model import MainViewModel
+from src.views.components.settings_helpers import check_keyring_state_for_password
from src.views.ui_settings import SettingsWidget
@@ -214,12 +202,17 @@ def test_set_expiry_time(setting_widget):
)
-def test_set_indexer_url(setting_widget):
+def test_set_indexer_url(setting_widget, mocker):
"""Test setting indexer URL."""
- # Mock components and check_keyring_state
+ # Mock components
setting_widget.set_indexer_url_frame = MagicMock()
setting_widget.set_indexer_url_frame.input_value.text.return_value = 'http://example.com'
- setting_widget._check_keyring_state = MagicMock(return_value='password123')
+
+ # Mock check_keyring_state_for_password
+ mocker.patch(
+ 'src.views.ui_settings.check_keyring_state_for_password',
+ return_value='password123',
+ )
# Mock setting view model
setting_widget._view_model.setting_view_model = MagicMock()
@@ -233,12 +226,17 @@ def test_set_indexer_url(setting_widget):
)
-def test_set_proxy_endpoint(setting_widget):
+def test_set_proxy_endpoint(setting_widget, mocker):
"""Test setting proxy endpoint."""
- # Mock components and check_keyring_state
+ # Mock components
setting_widget.set_proxy_endpoint_frame = MagicMock()
setting_widget.set_proxy_endpoint_frame.input_value.text.return_value = 'http://proxy.com'
- setting_widget._check_keyring_state = MagicMock(return_value='password123')
+
+ # Mock check_keyring_state_for_password
+ mocker.patch(
+ 'src.views.ui_settings.check_keyring_state_for_password',
+ return_value='password123',
+ )
# Mock setting view model
setting_widget._view_model.setting_view_model = MagicMock()
@@ -252,12 +250,17 @@ def test_set_proxy_endpoint(setting_widget):
)
-def test_set_bitcoind_host(setting_widget):
+def test_set_bitcoind_host(setting_widget, mocker):
"""Test setting bitcoind host."""
- # Mock components and check_keyring_state
+ # Mock components
setting_widget.set_bitcoind_rpc_host_frame = MagicMock()
setting_widget.set_bitcoind_rpc_host_frame.input_value.text.return_value = 'localhost'
- setting_widget._check_keyring_state = MagicMock(return_value='password123')
+
+ # Mock check_keyring_state_for_password
+ mocker.patch(
+ 'src.views.ui_settings.check_keyring_state_for_password',
+ return_value='password123',
+ )
# Mock setting view model
setting_widget._view_model.setting_view_model = MagicMock()
@@ -271,12 +274,17 @@ def test_set_bitcoind_host(setting_widget):
)
-def test_set_bitcoind_port(setting_widget):
+def test_set_bitcoind_port(setting_widget, mocker):
"""Test setting bitcoind port."""
- # Mock components and check_keyring_state
+ # Mock components
setting_widget.set_bitcoind_rpc_port_frame = MagicMock()
setting_widget.set_bitcoind_rpc_port_frame.input_value.text.return_value = '8332'
- setting_widget._check_keyring_state = MagicMock(return_value='password123')
+
+ # Mock check_keyring_state_for_password
+ mocker.patch(
+ 'src.views.ui_settings.check_keyring_state_for_password',
+ return_value='password123',
+ )
# Mock setting view model
setting_widget._view_model.setting_view_model = MagicMock()
@@ -290,12 +298,17 @@ def test_set_bitcoind_port(setting_widget):
)
-def test_set_announce_address(setting_widget):
+def test_set_announce_address(setting_widget, mocker):
"""Test setting announce address."""
- # Mock components and check_keyring_state
+ # Mock components
setting_widget.set_announce_address_frame = MagicMock()
setting_widget.set_announce_address_frame.input_value.text.return_value = 'example.com'
- setting_widget._check_keyring_state = MagicMock(return_value='password123')
+
+ # Mock check_keyring_state_for_password
+ mocker.patch(
+ 'src.views.ui_settings.check_keyring_state_for_password',
+ return_value='password123',
+ )
# Mock setting view model
setting_widget._view_model.setting_view_model = MagicMock()
@@ -309,12 +322,17 @@ def test_set_announce_address(setting_widget):
)
-def test_set_announce_alias(setting_widget):
+def test_set_announce_alias(setting_widget, mocker):
"""Test setting announce alias."""
- # Mock components and check_keyring_state
+ # Mock components
setting_widget.set_announce_alias_frame = MagicMock()
setting_widget.set_announce_alias_frame.input_value.text.return_value = 'my-node'
- setting_widget._check_keyring_state = MagicMock(return_value='password123')
+
+ # Mock check_keyring_state_for_password
+ mocker.patch(
+ 'src.views.ui_settings.check_keyring_state_for_password',
+ return_value='password123',
+ )
# Mock setting view model
setting_widget._view_model.setting_view_model = MagicMock()
@@ -539,67 +557,6 @@ def test_handle_on_error_empty_message(setting_widget, mocker):
mock_toast.error.assert_called_once_with('')
-def test_set_frame_content(setting_widget, mocker):
- """Test setting frame content with various configurations."""
- # Create mock frame and components
- mock_frame = MagicMock()
- mock_frame.input_value = MagicMock()
- mock_frame.suggestion_desc = MagicMock()
- mock_frame.time_unit_combobox = MagicMock()
- mock_frame.save_button = MagicMock()
-
- # Test with float input that's an integer
- setting_widget._set_frame_content(mock_frame, 10.0)
- mock_frame.input_value.setText.assert_called_with('10')
- mock_frame.input_value.setPlaceholderText.assert_called_with('10')
- mock_frame.suggestion_desc.hide.assert_called_once()
- mock_frame.time_unit_combobox.hide.assert_called_once()
-
- # Reset mocks
- mock_frame.reset_mock()
-
- # Test with validator
- mock_validator = MagicMock()
- setting_widget._set_frame_content(mock_frame, 10, validator=mock_validator)
- mock_frame.input_value.setValidator.assert_called_with(mock_validator)
-
- # Reset mocks
- mock_frame.reset_mock()
-
- # Test with time unit combobox
- mock_combobox = MagicMock()
- setting_widget.expiry_time_unit = 'hours'
- mock_combobox.findText.return_value = 1
- setting_widget._set_frame_content(
- mock_frame, 10, time_unit_combobox=mock_combobox,
- )
- mock_combobox.setCurrentIndex.assert_called_with(1)
-
-
-def test_update_save_button(setting_widget):
- """Test updating save button state."""
- # Create mock frame
- mock_frame = MagicMock()
- mock_frame.input_value.text.return_value = '20'
- mock_frame.time_unit_combobox.currentText.return_value = 'hours'
-
- # Test when input value has changed
- setting_widget._update_save_button(mock_frame, '10')
- mock_frame.save_button.setDisabled.assert_called_with(False)
-
- # Test when input value hasn't changed
- mock_frame.input_value.text.return_value = '10'
- setting_widget._update_save_button(mock_frame, '10')
- mock_frame.save_button.setDisabled.assert_called_with(True)
-
- # Test with time unit change
- setting_widget.expiry_time_unit = 'minutes'
- setting_widget._update_save_button(
- mock_frame, '10', mock_frame.time_unit_combobox,
- )
- mock_frame.save_button.setDisabled.assert_called_with(False)
-
-
def test_handle_fee_rate_frame(setting_widget):
"""Test handling fee rate frame."""
# Mock frame and components
@@ -698,83 +655,6 @@ def test_handle_other_frames(setting_widget):
frame.input_value.setPlaceholderText.assert_called_with(str(value))
-def test_check_keyring_state_disabled(setting_widget, mocker):
- """Test checking keyring state when keyring is disabled."""
- # Mock dependencies
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_keyring_status', return_value=False,
- )
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_wallet_network',
- return_value=NetworkEnumModel.MAINNET,
- )
- mock_get_value = mocker.patch(
- 'src.views.ui_settings.get_value', return_value='test_password',
- )
-
- # Call method
- result = setting_widget._check_keyring_state()
-
- # Verify password was retrieved from storage
- mock_get_value.assert_called_once_with('wallet_password', 'mainnet')
- assert result == 'test_password'
-
-
-def test_check_keyring_state_enabled_accepted(setting_widget, mocker):
- """Test checking keyring state when keyring is enabled and dialog is accepted."""
- # Mock dependencies
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_keyring_status', return_value=True,
- )
- mock_dialog = MagicMock()
- mock_dialog.exec.return_value = QDialog.Accepted
- mock_dialog.password_input.text.return_value = 'dialog_password'
- mock_dialog_class = mocker.patch(
- 'src.views.ui_settings.RestoreMnemonicWidget',
- return_value=mock_dialog,
- )
-
- # Call method
- result = setting_widget._check_keyring_state()
-
- # Verify dialog was created with correct parameters
- mock_dialog_class.assert_called_once_with(
- parent=setting_widget,
- view_model=setting_widget._view_model,
- origin_page='setting_card',
- mnemonic_visibility=False,
- )
-
- # Verify dialog was configured correctly
- mock_dialog.mnemonic_detail_text_label.setText.assert_called_once()
- mock_dialog.mnemonic_detail_text_label.setFixedHeight.assert_called_once_with(
- 40,
- )
-
- # Verify password was retrieved from dialog
- assert result == 'dialog_password'
-
-
-def test_check_keyring_state_enabled_rejected(setting_widget, mocker):
- """Test checking keyring state when keyring is enabled and dialog is rejected."""
- # Mock dependencies
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_keyring_status', return_value=True,
- )
- mock_dialog = MagicMock()
- mock_dialog.exec.return_value = QDialog.Rejected
- mocker.patch(
- 'src.views.ui_settings.RestoreMnemonicWidget',
- return_value=mock_dialog,
- )
-
- # Call method
- result = setting_widget._check_keyring_state()
-
- # Verify None is returned when dialog is rejected
- assert result is None
-
-
def test_check_keyring_state_invalid(setting_widget, mocker):
"""Test checking keyring state with invalid keyring status."""
# Mock dependencies
@@ -783,7 +663,9 @@ def test_check_keyring_state_invalid(setting_widget, mocker):
)
# Call method
- result = setting_widget._check_keyring_state()
+ result = check_keyring_state_for_password(
+ setting_widget, setting_widget._view_model,
+ )
# Verify None is returned for invalid keyring status
assert result is None
@@ -839,71 +721,3 @@ def test_update_loading_state_not_loading(setting_widget):
frame = getattr(setting_widget, frame_name)
frame.save_button.stop_loading.assert_called_once()
assert not frame.save_button.start_loading.called
-
-
-def test_set_endpoint_based_on_network_mainnet(setting_widget, mocker):
- """Test setting endpoints for mainnet network."""
- # Mock SettingRepository to return mainnet
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_wallet_network',
- return_value=NetworkEnumModel.MAINNET,
- )
-
- # Call method
- setting_widget._set_endpoint_based_on_network()
-
- # Verify correct endpoints were set
- assert setting_widget.indexer_url == INDEXER_URL_MAINNET
- assert setting_widget.proxy_endpoint == PROXY_ENDPOINT_MAINNET
- assert setting_widget.bitcoind_host == BITCOIND_RPC_HOST_MAINNET
- assert setting_widget.bitcoind_port == BITCOIND_RPC_PORT_MAINNET
-
-
-def test_set_endpoint_based_on_network_testnet(setting_widget, mocker):
- """Test setting endpoints for testnet network."""
- # Mock SettingRepository to return testnet
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_wallet_network',
- return_value=NetworkEnumModel.TESTNET,
- )
-
- # Call method
- setting_widget._set_endpoint_based_on_network()
-
- # Verify correct endpoints were set
- assert setting_widget.indexer_url == INDEXER_URL_TESTNET
- assert setting_widget.proxy_endpoint == PROXY_ENDPOINT_TESTNET
- assert setting_widget.bitcoind_host == BITCOIND_RPC_HOST_TESTNET
- assert setting_widget.bitcoind_port == BITCOIND_RPC_PORT_TESTNET
-
-
-def test_set_endpoint_based_on_network_regtest(setting_widget, mocker):
- """Test setting endpoints for regtest network."""
- # Mock SettingRepository to return regtest
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_wallet_network',
- return_value=NetworkEnumModel.REGTEST,
- )
-
- # Call method
- setting_widget._set_endpoint_based_on_network()
-
- # Verify correct endpoints were set
- assert setting_widget.indexer_url == INDEXER_URL_REGTEST
- assert setting_widget.proxy_endpoint == PROXY_ENDPOINT_REGTEST
- assert setting_widget.bitcoind_host == BITCOIND_RPC_HOST_REGTEST
- assert setting_widget.bitcoind_port == BITCOIND_RPC_PORT_REGTEST
-
-
-def test_set_endpoint_based_on_network_invalid(setting_widget, mocker):
- """Test setting endpoints with invalid network type."""
- # Mock SettingRepository to return invalid network
- mocker.patch(
- 'src.views.ui_settings.SettingRepository.get_wallet_network',
- return_value='invalid_network',
- )
-
- # Verify ValueError is raised
- with pytest.raises(ValueError) as exc_info:
- setting_widget._set_endpoint_based_on_network()
- assert 'Unsupported network type' in str(exc_info.value)
diff --git a/unit_tests/tests/ui_tests/ui_wallet_or_transfer_selection_test.py b/unit_tests/tests/ui_tests/ui_wallet_or_transfer_selection_test.py
index cc3a0776..b0457e0e 100644
--- a/unit_tests/tests/ui_tests/ui_wallet_or_transfer_selection_test.py
+++ b/unit_tests/tests/ui_tests/ui_wallet_or_transfer_selection_test.py
@@ -93,11 +93,11 @@ def test_handle_frame_click_on_chain_receive(wallet_or_transfer_selection_widget
# Mock asset_type if necessary
# Ensure asset_type is set
- wallet_or_transfer_selection_widget.asset_type = AssetType.RGB20.value
+ wallet_or_transfer_selection_widget.asset_type = AssetType.NIA.value
# Mock the methods that should be called
- mock_receive_rgb25_page = mocker.patch.object(
- wallet_or_transfer_selection_widget._view_model.page_navigation, 'receive_rgb25_page',
+ mock_receive_cfa_page = mocker.patch.object(
+ wallet_or_transfer_selection_widget._view_model.page_navigation, 'receive_cfa_page',
)
# Call the method with the on-chain ID
@@ -106,9 +106,9 @@ def test_handle_frame_click_on_chain_receive(wallet_or_transfer_selection_widget
)
# Assertions
- mock_receive_rgb25_page.assert_called_once_with(
+ mock_receive_cfa_page.assert_called_once_with(
params=AssetDataModel(
- asset_type=AssetType.RGB20.value, # Ensure asset_type is passed as expected
+ asset_type=AssetType.NIA.value, # Ensure asset_type is passed as expected
asset_id='test_asset_id',
),
)
@@ -120,8 +120,8 @@ def test_handle_frame_click_on_chain_send(wallet_or_transfer_selection_widget: W
wallet_or_transfer_selection_widget._params.callback = TransferStatusEnumModel.SEND.value
# Mock the methods that should be called
- mock_send_rgb25_page = mocker.patch.object(
- wallet_or_transfer_selection_widget._view_model.page_navigation, 'send_rgb25_page',
+ mock_send_cfa_page = mocker.patch.object(
+ wallet_or_transfer_selection_widget._view_model.page_navigation, 'send_cfa_page',
)
# Call the method with the on-chain ID
@@ -130,7 +130,7 @@ def test_handle_frame_click_on_chain_send(wallet_or_transfer_selection_widget: W
)
# Assertions
- mock_send_rgb25_page.assert_called_once()
+ mock_send_cfa_page.assert_called_once()
def test_handle_frame_click_off_chain_receive(wallet_or_transfer_selection_widget: WalletOrTransferSelectionWidget, mocker):
@@ -227,14 +227,14 @@ def test_close_button_navigation(wallet_or_transfer_selection_widget: WalletOrTr
mock_back_page_navigation = mocker.Mock()
wallet_or_transfer_selection_widget._params.back_page_navigation = mock_back_page_navigation
- # Mock the rgb25_view_model object
- mock_rgb25_view_model = mocker.patch.object(
- wallet_or_transfer_selection_widget._view_model, 'rgb25_view_model', autospec=False,
+ # Mock the cfa_view_model object
+ mock_cfa_view_model = mocker.patch.object(
+ wallet_or_transfer_selection_widget._view_model, 'cfa_view_model', autospec=False,
)
# Create a MagicMock for asset_info and mock its emit method
mock_asset_info = mocker.MagicMock()
- mock_rgb25_view_model.asset_info = mock_asset_info
+ mock_cfa_view_model.asset_info = mock_asset_info
# Set up rgb_asset_page_load_model with test values
wallet_or_transfer_selection_widget._params.rgb_asset_page_load_model = mocker.Mock(
diff --git a/unit_tests/tests/utils_test/helper_test.py b/unit_tests/tests/utils_test/helper_test.py
index 4b014ece..eb3738f4 100644
--- a/unit_tests/tests/utils_test/helper_test.py
+++ b/unit_tests/tests/utils_test/helper_test.py
@@ -24,7 +24,9 @@
from src.utils.helpers import hash_mnemonic
from src.utils.helpers import is_port_available
from src.utils.helpers import load_stylesheet
+from src.utils.helpers import read_ln_node_commit_id_file
from src.utils.helpers import validate_mnemonic
+from src.utils.helpers import write_ln_node_commit_id_file
@pytest.fixture
@@ -255,3 +257,54 @@ def test_handle_generic_error_logs_and_raises(mocker):
handle_generic_error(context, Exception('boom'), message)
assert str(exc_info.value) == message
mock_logger.error.assert_called()
+
+
+@patch('src.utils.helpers.SettingRepository.get_rln_node_commit_id')
+@patch('src.utils.helpers.app_paths')
+def test_write_ln_node_commit_id_file_writes_and_returns_path_and_name(
+ mock_app_paths, mock_get_commit_id,
+):
+ """Test the `write_ln_node_commit_id_file` function to ensure it writes the commit ID to a file and returns the path and name."""
+ mock_app_paths.backup_folder_path = '/fake/backup'
+ mock_get_commit_id.return_value = 'commit123'
+ m = mock_open()
+ with patch('builtins.open', m):
+ path, name = write_ln_node_commit_id_file('wallet')
+
+ expected_name = 'wallet.commit'
+ expected_path = os.path.join('/fake/backup', expected_name)
+
+ assert name == expected_name
+ assert path == expected_path
+ m.assert_called_once_with(expected_path, 'w', encoding='utf-8')
+ handle = m()
+ handle.write.assert_called_once_with('commit123')
+
+
+@patch('src.utils.helpers.app_paths')
+def test_read_ln_node_commit_id_file_returns_content(
+ mock_app_paths,
+):
+ """Test the `read_ln_node_commit_id_file` function to ensure it returns the content of the commit ID file."""
+ mock_app_paths.restore_folder_path = '/fake/restore'
+
+ file_name = 'wallet.commit'
+ full_path = os.path.join('/fake/restore', file_name)
+ m = mock_open(read_data='commitXYZ')
+ with patch('builtins.open', m):
+ result = read_ln_node_commit_id_file(file_name)
+ m.assert_called_once_with(full_path, encoding='utf-8')
+ assert result == 'commitXYZ'
+
+
+@patch('src.utils.helpers.app_paths')
+def test_read_ln_node_commit_id_file_returns_unknown_when_missing(
+ mock_app_paths,
+):
+ """Test the `read_ln_node_commit_id_file` function to ensure it returns 'unknown' when the commit ID file is missing."""
+ mock_app_paths.restore_folder_path = '/fake/restore'
+ m = mock_open()
+ m.side_effect = FileNotFoundError
+ with patch('builtins.open', m):
+ result = read_ln_node_commit_id_file('missing.commit')
+ assert result == 'unknown'
diff --git a/unit_tests/tests/utils_test/page_navigation_test.py b/unit_tests/tests/utils_test/page_navigation_test.py
index 43899023..63580ab8 100644
--- a/unit_tests/tests/utils_test/page_navigation_test.py
+++ b/unit_tests/tests/utils_test/page_navigation_test.py
@@ -130,11 +130,11 @@ def test_enter_wallet_password_page(page_navigation):
assert page_navigation.current_stack['name'] == 'EnterWalletPassword'
-def test_issue_rgb20_asset_page(page_navigation):
- """Test issue_rgb20_asset_page navigation."""
- page_navigation.issue_rgb20_asset_page()
+def test_issue_nia_asset_page(page_navigation):
+ """Test issue_nia_asset_page navigation."""
+ page_navigation.issue_nia_asset_page()
- assert page_navigation.current_stack['name'] == 'IssueRGB20'
+ assert page_navigation.current_stack['name'] == 'IssueNIA'
def test_bitcoin_page(page_navigation):
@@ -144,34 +144,34 @@ def test_bitcoin_page(page_navigation):
assert page_navigation.current_stack['name'] == 'Bitcoin'
-def test_issue_rgb25_asset_page(page_navigation):
- """Test issue_rgb25_asset_page navigation."""
- page_navigation.issue_rgb25_asset_page()
+def test_issue_cfa_asset_page(page_navigation):
+ """Test issue_cfa_asset_page navigation."""
+ page_navigation.issue_cfa_asset_page()
- assert page_navigation.current_stack['name'] == 'IssueRGB25'
+ assert page_navigation.current_stack['name'] == 'IssueCFA'
-def test_send_rgb25_page(page_navigation):
- """Test send_rgb25_page navigation."""
- page_navigation.send_rgb25_page()
+def test_send_cfa_page(page_navigation):
+ """Test send_cfa_page navigation."""
+ page_navigation.send_cfa_page()
- assert page_navigation.current_stack['name'] == 'SendRGB25'
+ assert page_navigation.current_stack['name'] == 'SendCFA'
-def test_receive_rgb25_page(page_navigation):
- """Test receive_rgb25_page navigation."""
+def test_receive_cfa_page(page_navigation):
+ """Test receive_cfa_page navigation."""
params = MagicMock()
- page_navigation.receive_rgb25_page(params)
+ page_navigation.receive_cfa_page(params)
- assert page_navigation.current_stack['name'] == 'ReceiveRGB25'
+ assert page_navigation.current_stack['name'] == 'ReceiveCFA'
-def test_rgb25_detail_page(page_navigation):
- """Test rgb25_detail_page navigation."""
+def test_cfa_detail_page(page_navigation):
+ """Test cfa_detail_page navigation."""
params = MagicMock(spec=RgbAssetPageLoadModel)
- page_navigation.rgb25_detail_page(params)
+ page_navigation.cfa_detail_page(params)
- assert page_navigation.current_stack['name'] == 'RGB25Detail'
+ assert page_navigation.current_stack['name'] == 'CFADetail'
def test_send_bitcoin_page(page_navigation):
@@ -209,12 +209,12 @@ def test_view_unspent_list_page(page_navigation):
assert page_navigation.current_stack['name'] == 'ViewUnspentList'
-def test_rgb25_transaction_detail_page(page_navigation):
- """Test rgb25_transaction_detail_page navigation."""
+def test_cfa_transaction_detail_page(page_navigation):
+ """Test cfa_transaction_detail_page navigation."""
params = MagicMock(spec=TransactionDetailPageModel)
- page_navigation.rgb25_transaction_detail_page(params)
+ page_navigation.cfa_transaction_detail_page(params)
- assert page_navigation.current_stack['name'] == 'RGB25TransactionDetail'
+ assert page_navigation.current_stack['name'] == 'CFATransactionDetail'
def test_bitcoin_transaction_detail_page(page_navigation):
diff --git a/unit_tests/tests/utils_test/render_timer_test.py b/unit_tests/tests/utils_test/render_timer_test.py
index 2e2751d4..6570c67c 100644
--- a/unit_tests/tests/utils_test/render_timer_test.py
+++ b/unit_tests/tests/utils_test/render_timer_test.py
@@ -27,6 +27,8 @@ def render_timer():
def test_singleton_pattern():
"""Test that RenderTimer follows the singleton pattern."""
+ # Clear the singleton instance to ensure test isolation
+ RenderTimer._instance = None
with patch('src.utils.render_timer.logger'):
# Create two instances
timer1 = RenderTimer('Task 1')
@@ -108,6 +110,8 @@ def test_multiple_start_stop_cycles(render_timer):
def test_timer_initialization():
"""Test that timer is properly initialized."""
+ # Clear the singleton instance to ensure test isolation
+ RenderTimer._instance = None
with patch('src.utils.render_timer.logger'):
timer = RenderTimer('Test Task')
assert isinstance(timer.timer, QElapsedTimer)
@@ -126,6 +130,8 @@ def test_stop_resets_rendering_flag(render_timer):
def test_initialization_happens_once():
"""Test that initialization only happens once despite multiple instantiations."""
+ # Clear the singleton instance to ensure test isolation
+ RenderTimer._instance = None
with patch('src.utils.render_timer.logger'):
timer1 = RenderTimer('Task 1')
original_timer = timer1.timer
diff --git a/unit_tests/tests/utils_test/rgb_asset_helpers_test.py b/unit_tests/tests/utils_test/rgb_asset_helpers_test.py
new file mode 100644
index 00000000..9d204404
--- /dev/null
+++ b/unit_tests/tests/utils_test/rgb_asset_helpers_test.py
@@ -0,0 +1,188 @@
+"""Unit tests for RGB asset helper functions."""
+from __future__ import annotations
+
+from unittest.mock import MagicMock
+from unittest.mock import patch
+
+from PySide6.QtWidgets import QLabel
+
+from src.utils.rgb_asset_helpers import handle_img_path
+from src.utils.rgb_asset_helpers import is_hex_string
+from src.utils.rgb_asset_helpers import is_path
+from src.utils.rgb_asset_helpers import set_asset_image
+
+
+def test_valid_hex_string():
+ """Test with valid hex strings."""
+ valid_hex_strings = [
+ '00', # simple hex
+ '0a1b2c3d4e5f', # longer valid hex
+ 'AABBCCDDEE', # uppercase hex
+ '1234567890abcdef', # mixed lower and uppercase
+ ]
+ for hex_string in valid_hex_strings:
+ assert is_hex_string(hex_string) is True
+
+
+def test_invalid_hex_string():
+ """Test with invalid hex strings."""
+ invalid_hex_strings = [
+ '00G1', # contains non-hex character 'G'
+ '123z', # contains non-hex character 'z'
+ '12345', # odd length
+ '0x1234', # prefixed with '0x'
+ ' ', # empty or space character
+ ]
+ for hex_string in invalid_hex_strings:
+ assert is_hex_string(hex_string) is False
+
+
+def test_empty_string():
+ """Test with an empty string."""
+ assert is_hex_string('') is False
+
+
+def test_odd_length_string():
+ """Test with a string of odd length."""
+ odd_length_hex_strings = [
+ '1', # single character
+ '123', # three characters
+ '12345', # five characters
+ ]
+ for hex_string in odd_length_hex_strings:
+ assert is_hex_string(hex_string) is False
+
+
+def test_is_path_valid():
+ """Test the is_path function with valid Unix-like paths."""
+ # Test valid Unix-like paths
+ assert is_path('/path/to/file') is True
+ assert is_path('/usr/local/bin/') is True
+ assert is_path('/home/user/doc-1.txt') is True
+
+
+def test_is_path_invalid():
+ """Test the is_path function with invalid paths."""
+ # Test invalid paths
+ assert is_path('invalid/path') is False # No leading slash
+ assert is_path(123) is False # Non-string input
+ assert is_path('') is False # Empty string
+ assert is_path('C:\\Windows\\Path') is False # Windows path format
+
+
+@patch('src.utils.rgb_asset_helpers.convert_hex_to_image')
+@patch('src.utils.rgb_asset_helpers.resize_image')
+def test_set_asset_image_with_hex(mock_resize_image, mock_convert_hex_to_image):
+ """Test setting asset image from hex string."""
+ # Setup
+ mock_label = MagicMock(spec=QLabel)
+ mock_pixmap = MagicMock()
+ mock_resized = MagicMock()
+ mock_convert_hex_to_image.return_value = mock_pixmap
+ mock_resize_image.return_value = mock_resized
+
+ # Call with hex string
+ set_asset_image(mock_label, '1234567890abcdef')
+
+ # Verify
+ mock_convert_hex_to_image.assert_called_once_with('1234567890abcdef')
+ mock_resize_image.assert_called_once_with(mock_pixmap, 335, 335)
+ mock_label.setPixmap.assert_called_once_with(mock_resized)
+
+
+@patch('src.utils.rgb_asset_helpers.resize_image')
+def test_set_asset_image_with_path(mock_resize_image):
+ """Test setting asset image from file path."""
+ # Setup
+ mock_label = MagicMock(spec=QLabel)
+ mock_resized = MagicMock()
+ mock_resize_image.return_value = mock_resized
+
+ # Call with file path (not hex string)
+ set_asset_image(mock_label, '/path/to/image.png')
+
+ # Verify
+ mock_resize_image.assert_called_once_with('/path/to/image.png', 335, 335)
+
+
+@patch('src.utils.rgb_asset_helpers.set_asset_image')
+@patch('src.utils.rgb_asset_helpers.QLabel')
+def test_handle_img_path_with_path(mock_qlabel_class, mock_set_asset_image):
+ """Test handle_img_path with a valid image path."""
+ # Setup mocks
+ mock_widget = MagicMock()
+ mock_layout = MagicMock()
+ mock_frame = MagicMock()
+ mock_label = MagicMock(spec=QLabel)
+ mock_qlabel_class.return_value = mock_label
+ image_path = '/path/to/image.png'
+
+ # Call
+ result = handle_img_path(
+ mock_widget,
+ image_path,
+ mock_layout,
+ mock_frame,
+ )
+
+ # Verify widget configuration
+ mock_widget.setMinimumSize.assert_called_once()
+ mock_widget.setFixedWidth.assert_called_once_with(499)
+
+ # Verify label was created
+ mock_qlabel_class.assert_called_once_with(mock_widget)
+ assert result == mock_label
+
+ # Verify set_asset_image was called
+ mock_set_asset_image.assert_called_once_with(
+ mock_label, image_hex=image_path,
+ )
+ # Setup
+ mock_widget = MagicMock()
+ mock_layout = MagicMock()
+ mock_frame = MagicMock()
+ existing_label = MagicMock(spec=QLabel)
+
+ # Call with no path
+ result = handle_img_path(
+ mock_widget,
+ '',
+ mock_layout,
+ mock_frame,
+ existing_label,
+ )
+
+ # Verify nothing was configured and existing label was returned
+ assert result == existing_label
+ mock_widget.setMinimumSize.assert_not_called()
+ mock_layout.addWidget.assert_not_called()
+
+
+@patch('src.utils.rgb_asset_helpers.set_asset_image')
+def test_handle_img_path_with_existing_label(mock_set_asset_image):
+ """Test handle_img_path with an existing label."""
+ # Setup
+ mock_widget = MagicMock()
+ mock_layout = MagicMock()
+ mock_frame = MagicMock()
+ existing_label = MagicMock(spec=QLabel)
+ image_path = '/path/to/image.png'
+
+ # Call with existing label
+ result = handle_img_path(
+ mock_widget,
+ image_path,
+ mock_layout,
+ mock_frame,
+ existing_label,
+ )
+
+ # Verify existing label was used and configured
+ assert result == existing_label
+ existing_label.setMaximumSize.assert_called_once()
+ existing_label.setStyleSheet.assert_called_once()
+
+ # Verify set_asset_image was called with existing label
+ mock_set_asset_image.assert_called_once_with(
+ existing_label, image_hex=image_path,
+ )
diff --git a/unit_tests/tests/viewmodel_tests/rgb_25_view_model_test.py b/unit_tests/tests/viewmodel_tests/cfa_view_model_test.py
similarity index 52%
rename from unit_tests/tests/viewmodel_tests/rgb_25_view_model_test.py
rename to unit_tests/tests/viewmodel_tests/cfa_view_model_test.py
index a73a547a..6b057473 100644
--- a/unit_tests/tests/viewmodel_tests/rgb_25_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/cfa_view_model_test.py
@@ -1,4 +1,4 @@
-"""Unit test for rgb25 view model"""
+"""Unit test for cfa view model"""
# Disable the redefined-outer-name warning as
# it's normal to pass mocked object in tests function
# pylint: disable=redefined-outer-name,unused-argument,too-many-statements,protected-access
@@ -12,8 +12,10 @@
import pytest
from src.model.enums.enums_model import AssetType
+from src.model.enums.enums_model import AssignmentEnumModel
from src.model.enums.enums_model import TransferStatusEnumModel
from src.model.rgb_model import AssetBalanceResponseModel
+from src.model.rgb_model import AssignmentModel
from src.model.rgb_model import FailTransferResponseModel
from src.model.rgb_model import ListTransferAssetWithBalanceResponseModel
from src.model.rgb_model import SendAssetResponseModel
@@ -25,7 +27,7 @@
from src.utils.info_message import INFO_FAIL_TRANSFER_SUCCESSFULLY
from src.utils.info_message import INFO_REFRESH_SUCCESSFULLY
from src.utils.page_navigation import PageNavigation
-from src.viewmodels.rgb_25_view_model import RGB25ViewModel
+from src.viewmodels.cfa_view_model import CFAViewModel
from unit_tests.factories.transfer_assets import make_transfer_asset
@@ -38,9 +40,9 @@ def setup_navigation():
@pytest.fixture
-def rgb25_view_model(setup_navigation):
- """Fixture to create an instance of the RGB25ViewModel class."""
- return RGB25ViewModel(setup_navigation)
+def cfa_view_model(setup_navigation):
+ """Fixture to create an instance of the CFAViewModel class."""
+ return CFAViewModel(setup_navigation)
@pytest.fixture
@@ -57,7 +59,12 @@ def mock_asset_details_response():
update_at_date='2024-06-13',
updated_at_time='17:35:42',
status='Settled',
- amount=69,
+ assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=69,
+ ),
+ requested_assignment=AssignmentModel(
+ type=AssignmentEnumModel.FUNGIBLE, value=69,
+ ),
amount_status='+69',
kind='Issuance',
transfer_Status=TransferStatusEnumModel.INTERNAL,
@@ -74,12 +81,12 @@ def mock_asset_details_response():
@patch('src.data.service.asset_detail_page_services.AssetDetailPageService.get_asset_transactions')
-def test_get_rgb25_asset_detail_success(mock_get_asset_transactions, rgb25_view_model, mock_asset_details_response):
- """Test for successfully retrieving RGB25 asset detail."""
+def test_get_cfa_asset_detail_success(mock_get_asset_transactions, cfa_view_model, mock_asset_details_response):
+ """Test for successfully retrieving CFA asset detail."""
mock_txn_list_loaded_signal = Mock()
mock_is_loading_signal = Mock()
- rgb25_view_model.txn_list_loaded.connect(mock_txn_list_loaded_signal)
- rgb25_view_model.is_loading.connect(mock_is_loading_signal)
+ cfa_view_model.txn_list_loaded.connect(mock_txn_list_loaded_signal)
+ cfa_view_model.is_loading.connect(mock_is_loading_signal)
mock_get_asset_transactions.return_value = mock_asset_details_response
@@ -88,24 +95,24 @@ def test_get_rgb25_asset_detail_success(mock_get_asset_transactions, rgb25_view_
image_path = 'test_image_path'
asset_type = 'test_asset_type'
- rgb25_view_model.get_rgb25_asset_detail(
+ cfa_view_model.get_cfa_asset_detail(
asset_id, asset_name, image_path, asset_type,
)
- rgb25_view_model.worker.result.emit(mock_asset_details_response)
+ cfa_view_model.worker.result.emit(mock_asset_details_response)
mock_txn_list_loaded_signal.assert_called_once_with(
asset_id, asset_name, image_path, asset_type,
)
mock_is_loading_signal.assert_called_with(False)
- assert rgb25_view_model.asset_id == asset_id
- assert rgb25_view_model.asset_name == asset_name
- assert rgb25_view_model.image_path == image_path
- assert rgb25_view_model.asset_type == asset_type
+ assert cfa_view_model.asset_id == asset_id
+ assert cfa_view_model.asset_name == asset_name
+ assert cfa_view_model.image_path == image_path
+ assert cfa_view_model.asset_type == asset_type
@patch('src.utils.worker.ThreadManager.run_in_thread', autospec=True)
-def test_on_send_click(mock_run_in_thread, rgb25_view_model):
- """Test the on_send_click method of RGB25ViewModel without executing the actual method."""
+def test_on_send_click(mock_run_in_thread, cfa_view_model):
+ """Test the on_send_click method of CFAViewModel without executing the actual method."""
# Setup test parameters
amount = 100
blinded_utxo = 'test_blinded_utxo'
@@ -114,33 +121,33 @@ def test_on_send_click(mock_run_in_thread, rgb25_view_model):
min_confirmation = 1
# Ensure asset_id is set before calling the method
- rgb25_view_model.asset_id = 'test_asset_id'
+ cfa_view_model.asset_id = 'test_asset_id'
# Mock the worker object
mock_worker = MagicMock()
- rgb25_view_model.worker = mock_worker
+ cfa_view_model.worker = mock_worker
# Call the method
- rgb25_view_model.on_send_click(
+ cfa_view_model.on_send_click(
amount, blinded_utxo, transport_endpoints, fee_rate, min_confirmation,
)
# Verify the state changes
- assert rgb25_view_model.amount == amount
- assert rgb25_view_model.blinded_utxo == blinded_utxo
- assert rgb25_view_model.transport_endpoints == transport_endpoints
- assert rgb25_view_model.fee_rate == fee_rate
- assert rgb25_view_model.min_confirmation == min_confirmation
+ assert cfa_view_model.amount == amount
+ assert cfa_view_model.blinded_utxo == blinded_utxo
+ assert cfa_view_model.transport_endpoints == transport_endpoints
+ assert cfa_view_model.fee_rate == fee_rate
+ assert cfa_view_model.min_confirmation == min_confirmation
@patch('src.data.repository.rgb_repository.RgbRepository.fail_transfer')
@patch('src.views.components.toast.ToastManager.success')
@patch('src.views.components.toast.ToastManager.error')
-def test_on_fail_transfer(mock_toast_error, mock_toast_success, mock_fail_transfer, rgb25_view_model):
+def test_on_fail_transfer(mock_toast_error, mock_toast_success, mock_fail_transfer, cfa_view_model):
"""Test for handling fail transfer operation."""
# Mock necessary attributes and methods
- rgb25_view_model.is_loading = MagicMock()
- rgb25_view_model.get_rgb25_asset_detail = MagicMock()
+ cfa_view_model.is_loading = MagicMock()
+ cfa_view_model.get_cfa_asset_detail = MagicMock()
# Mock the repository method return value
mock_fail_transfer.return_value = FailTransferResponseModel(
@@ -152,24 +159,24 @@ def mock_run_in_thread(func, args):
# Simulate the success callback being called
args['callback'](mock_fail_transfer.return_value)
- rgb25_view_model.run_in_thread = MagicMock(side_effect=mock_run_in_thread)
+ cfa_view_model.run_in_thread = MagicMock(side_effect=mock_run_in_thread)
# Set up the asset ID
- rgb25_view_model.asset_id = 'test_asset_id'
+ cfa_view_model.asset_id = 'test_asset_id'
# Call the method
batch_transfer_idx = 1
- rgb25_view_model.on_fail_transfer(batch_transfer_idx)
+ cfa_view_model.on_fail_transfer(batch_transfer_idx)
# Verify the behavior
- rgb25_view_model.is_loading.emit.assert_called_with(
+ cfa_view_model.is_loading.emit.assert_called_with(
True,
) # Loading state is set
mock_toast_success.assert_called_once_with(
description=INFO_FAIL_TRANSFER_SUCCESSFULLY,
) # Success toast is shown
- rgb25_view_model.get_rgb25_asset_detail.assert_called_once_with(
- rgb25_view_model.asset_id, rgb25_view_model.asset_name, None, rgb25_view_model.asset_type,
+ cfa_view_model.get_cfa_asset_detail.assert_called_once_with(
+ cfa_view_model.asset_id, cfa_view_model.asset_name, None, cfa_view_model.asset_type,
)
# Test when transfers_changed=False
@@ -179,13 +186,13 @@ def mock_run_in_thread_fail(func, args):
# Simulate the error callback being called
args['callback'](mock_fail_transfer.return_value)
- rgb25_view_model.run_in_thread = MagicMock(
+ cfa_view_model.run_in_thread = MagicMock(
side_effect=mock_run_in_thread_fail,
)
- rgb25_view_model.on_fail_transfer(batch_transfer_idx)
+ cfa_view_model.on_fail_transfer(batch_transfer_idx)
- rgb25_view_model.is_loading.emit.assert_called_with(
+ cfa_view_model.is_loading.emit.assert_called_with(
False,
) # Loading state is unset
mock_toast_error.assert_called_once_with(
@@ -195,13 +202,13 @@ def mock_run_in_thread_fail(func, args):
# Test for handling error while failing a transfer
mock_exception = CommonException('Error sending asset')
mock_fail_transfer.side_effect = mock_exception
- rgb25_view_model.run_in_thread = MagicMock(
+ cfa_view_model.run_in_thread = MagicMock(
side_effect=lambda func, args: args['error_callback'](mock_exception),
)
- rgb25_view_model.on_fail_transfer(batch_transfer_idx)
+ cfa_view_model.on_fail_transfer(batch_transfer_idx)
- rgb25_view_model.is_loading.emit.assert_called_with(
+ cfa_view_model.is_loading.emit.assert_called_with(
False,
) # Loading state is unset
mock_toast_error.assert_called_with(
@@ -210,13 +217,13 @@ def mock_run_in_thread_fail(func, args):
# Test for handling generic exception while failing a transfer
mock_fail_transfer.side_effect = Exception('Generic error')
- rgb25_view_model.run_in_thread = MagicMock(
+ cfa_view_model.run_in_thread = MagicMock(
side_effect=lambda func, args: func(),
)
- rgb25_view_model.on_fail_transfer(batch_transfer_idx)
+ cfa_view_model.on_fail_transfer(batch_transfer_idx)
- rgb25_view_model.is_loading.emit.assert_called_with(
+ cfa_view_model.is_loading.emit.assert_called_with(
False,
) # Loading state is unset
mock_toast_error.assert_called_with(
@@ -226,10 +233,10 @@ def mock_run_in_thread_fail(func, args):
# Test on_refresh_click functionality
-@patch('src.viewmodels.rgb_25_view_model.Cache')
-@patch('src.viewmodels.rgb_25_view_model.ToastManager')
-@patch('src.viewmodels.rgb_25_view_model.RgbRepository')
-def test_on_refresh_click(mock_rgb_repository, mock_toast_manager, mock_cache, rgb25_view_model):
+@patch('src.viewmodels.cfa_view_model.Cache')
+@patch('src.viewmodels.cfa_view_model.ToastManager')
+@patch('src.viewmodels.cfa_view_model.RgbRepository')
+def test_on_refresh_click(mock_rgb_repository, mock_toast_manager, mock_cache, cfa_view_model):
"""Test the on_refresh_click method behavior."""
# Set up mocks
mock_cache_session = MagicMock()
@@ -238,60 +245,60 @@ def test_on_refresh_click(mock_rgb_repository, mock_toast_manager, mock_cache, r
mock_toast_error = mock_toast_manager.error
# Set up the view model
- rgb25_view_model.refresh = MagicMock()
- rgb25_view_model.is_loading = MagicMock()
- rgb25_view_model.send_rgb25_button_clicked = MagicMock()
- rgb25_view_model.get_rgb25_asset_detail = MagicMock()
- rgb25_view_model.asset_id = 'test_asset_id'
- rgb25_view_model.asset_name = 'test_asset'
- rgb25_view_model.asset_type = 'RGB25'
+ cfa_view_model.refresh = MagicMock()
+ cfa_view_model.is_loading = MagicMock()
+ cfa_view_model.send_cfa_button_clicked = MagicMock()
+ cfa_view_model.get_cfa_asset_detail = MagicMock()
+ cfa_view_model.asset_id = 'test_asset_id'
+ cfa_view_model.asset_name = 'test_asset'
+ cfa_view_model.asset_type = 'CFA'
# Test successful refresh
def mock_run_in_thread(func, args):
args['callback']()
- rgb25_view_model.run_in_thread = MagicMock(side_effect=mock_run_in_thread)
+ cfa_view_model.run_in_thread = MagicMock(side_effect=mock_run_in_thread)
# Call the method
- rgb25_view_model.on_refresh_click()
+ cfa_view_model.on_refresh_click()
# Verify behavior for successful case
mock_cache_session.invalidate_cache.assert_called_once()
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_called_once_with(
+ cfa_view_model.send_cfa_button_clicked.emit.assert_called_once_with(
True,
)
- rgb25_view_model.is_loading.emit.assert_has_calls(
+ cfa_view_model.is_loading.emit.assert_has_calls(
[call(True), call(False)],
)
- rgb25_view_model.refresh.emit.assert_called_once_with(True)
+ cfa_view_model.refresh.emit.assert_called_once_with(True)
mock_toast_success.assert_called_once_with(
description=INFO_REFRESH_SUCCESSFULLY,
)
- rgb25_view_model.get_rgb25_asset_detail.assert_called_once_with(
- rgb25_view_model.asset_id,
- rgb25_view_model.asset_name,
+ cfa_view_model.get_cfa_asset_detail.assert_called_once_with(
+ cfa_view_model.asset_id,
+ cfa_view_model.asset_name,
None,
- rgb25_view_model.asset_type,
+ cfa_view_model.asset_type,
)
# Test error case with CommonException
mock_exception = CommonException('Refresh error')
- rgb25_view_model.run_in_thread = MagicMock(
+ cfa_view_model.run_in_thread = MagicMock(
side_effect=lambda func, args: args['error_callback'](mock_exception),
)
# Reset mocks
- rgb25_view_model.refresh.reset_mock()
- rgb25_view_model.is_loading.reset_mock()
+ cfa_view_model.refresh.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
mock_toast_error.reset_mock()
mock_cache_session.invalidate_cache.reset_mock()
# Call the method
- rgb25_view_model.on_refresh_click()
+ cfa_view_model.on_refresh_click()
# Verify behavior for error case
- rgb25_view_model.refresh.emit.assert_called_once_with(False)
- rgb25_view_model.is_loading.emit.assert_has_calls(
+ cfa_view_model.refresh.emit.assert_called_once_with(False)
+ cfa_view_model.is_loading.emit.assert_has_calls(
[call(True), call(False)],
)
mock_toast_error.assert_called_once_with(
@@ -300,20 +307,20 @@ def mock_run_in_thread(func, args):
# Test generic exception case
generic_exception = Exception('Generic error')
- rgb25_view_model.run_in_thread = MagicMock(side_effect=generic_exception)
+ cfa_view_model.run_in_thread = MagicMock(side_effect=generic_exception)
# Reset mocks
- rgb25_view_model.refresh.reset_mock()
- rgb25_view_model.is_loading.reset_mock()
+ cfa_view_model.refresh.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
mock_toast_error.reset_mock()
mock_cache_session.invalidate_cache.reset_mock()
# Call the method
- rgb25_view_model.on_refresh_click()
+ cfa_view_model.on_refresh_click()
# Verify behavior for generic exception case
- rgb25_view_model.refresh.emit.assert_called_once_with(False)
- rgb25_view_model.is_loading.emit.assert_has_calls(
+ cfa_view_model.refresh.emit.assert_called_once_with(False)
+ cfa_view_model.is_loading.emit.assert_has_calls(
[call(True), call(False)],
)
mock_toast_error.assert_called_once_with(
@@ -322,183 +329,183 @@ def mock_run_in_thread(func, args):
# Test when cache is None
mock_cache.get_cache_session.return_value = None
- rgb25_view_model.run_in_thread = MagicMock(side_effect=mock_run_in_thread)
+ cfa_view_model.run_in_thread = MagicMock(side_effect=mock_run_in_thread)
# Reset mocks
- rgb25_view_model.refresh.reset_mock()
- rgb25_view_model.is_loading.reset_mock()
+ cfa_view_model.refresh.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
mock_toast_success.reset_mock()
mock_cache_session.invalidate_cache.reset_mock()
# Call the method
- rgb25_view_model.on_refresh_click()
+ cfa_view_model.on_refresh_click()
# Verify behavior when cache is None
# Should not be called when cache is None
mock_cache_session.invalidate_cache.assert_not_called()
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_called_with(True)
- rgb25_view_model.is_loading.emit.assert_has_calls(
+ cfa_view_model.send_cfa_button_clicked.emit.assert_called_with(True)
+ cfa_view_model.is_loading.emit.assert_has_calls(
[call(True), call(False)],
)
- rgb25_view_model.refresh.emit.assert_called_once_with(True)
+ cfa_view_model.refresh.emit.assert_called_once_with(True)
mock_toast_success.assert_called_once_with(
description=INFO_REFRESH_SUCCESSFULLY,
)
-def test_on_success_send_rgb_asset(rgb25_view_model, mocker):
+def test_on_success_send_rgb_asset(cfa_view_model, mocker):
"""Test on_success_send_rgb_asset behavior"""
- rgb25_view_model.send_rgb25_button_clicked = MagicMock()
- rgb25_view_model.is_loading = MagicMock()
- rgb25_view_model.on_error = MagicMock() # Mock on_error as MagicMock
+ cfa_view_model.send_cfa_button_clicked = MagicMock()
+ cfa_view_model.is_loading = MagicMock()
+ cfa_view_model.on_error = MagicMock() # Mock on_error as MagicMock
# Mock toast_error
mock_toast_error = mocker.patch(
- 'src.viewmodels.rgb_25_view_model.ToastManager.error',
+ 'src.viewmodels.cfa_view_model.ToastManager.error',
)
# Test successful case
- rgb25_view_model.asset_id = 'test_asset_id'
- rgb25_view_model.amount = 100
- rgb25_view_model.blinded_utxo = 'test_blinded_utxo'
- rgb25_view_model.transport_endpoints = ['endpoint1', 'endpoint2']
- rgb25_view_model.fee_rate = 1.0
- rgb25_view_model.min_confirmation = 1
- rgb25_view_model.run_in_thread = MagicMock()
+ cfa_view_model.asset_id = 'test_asset_id'
+ cfa_view_model.amount = 100
+ cfa_view_model.blinded_utxo = 'test_blinded_utxo'
+ cfa_view_model.transport_endpoints = ['endpoint1', 'endpoint2']
+ cfa_view_model.fee_rate = 1.0
+ cfa_view_model.min_confirmation = 1
+ cfa_view_model.run_in_thread = MagicMock()
# Call method with success=True
- rgb25_view_model.on_success_send_rgb_asset(True)
+ cfa_view_model.on_success_send_rgb_asset(True)
# Verify behavior for success case
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_called_once_with(
+ cfa_view_model.send_cfa_button_clicked.emit.assert_called_once_with(
True,
)
- rgb25_view_model.is_loading.emit.assert_called_once_with(True)
- rgb25_view_model.run_in_thread.assert_called_once()
+ cfa_view_model.is_loading.emit.assert_called_once_with(True)
+ cfa_view_model.run_in_thread.assert_called_once()
# Verify run_in_thread arguments
- call_args = rgb25_view_model.run_in_thread.call_args[0][1]
- assert call_args['args'][0].asset_id == rgb25_view_model.asset_id
- assert call_args['args'][0].amount == rgb25_view_model.amount
- assert call_args['args'][0].recipient_id == rgb25_view_model.blinded_utxo
- assert call_args['args'][0].transport_endpoints == rgb25_view_model.transport_endpoints
- assert call_args['args'][0].fee_rate == rgb25_view_model.fee_rate
- assert call_args['args'][0].min_confirmations == rgb25_view_model.min_confirmation
- assert call_args['callback'] == rgb25_view_model.on_success_rgb25
- assert call_args['error_callback'] == rgb25_view_model.on_error
+ call_args = cfa_view_model.run_in_thread.call_args[0][1]
+ assert call_args['args'][0].asset_id == cfa_view_model.asset_id
+ assert call_args['args'][0].assignment.value == cfa_view_model.amount
+ assert call_args['args'][0].recipient_id == cfa_view_model.blinded_utxo
+ assert call_args['args'][0].transport_endpoints == cfa_view_model.transport_endpoints
+ assert call_args['args'][0].fee_rate == cfa_view_model.fee_rate
+ assert call_args['args'][0].min_confirmations == cfa_view_model.min_confirmation
+ assert call_args['callback'] == cfa_view_model.on_success_cfa
+ assert call_args['error_callback'] == cfa_view_model.on_error
# Test exception case
mock_exception = Exception('Test error')
- rgb25_view_model.run_in_thread = MagicMock(side_effect=mock_exception)
+ cfa_view_model.run_in_thread = MagicMock(side_effect=mock_exception)
# Reset mocks
- rgb25_view_model.send_rgb25_button_clicked.reset_mock()
- rgb25_view_model.is_loading.reset_mock()
- rgb25_view_model.on_error.reset_mock()
+ cfa_view_model.send_cfa_button_clicked.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
+ cfa_view_model.on_error.reset_mock()
mock_toast_error.reset_mock()
# Call method with success=True (will raise exception)
- rgb25_view_model.on_success_send_rgb_asset(True)
+ cfa_view_model.on_success_send_rgb_asset(True)
# Verify behavior for exception case
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_has_calls([
+ cfa_view_model.send_cfa_button_clicked.emit.assert_has_calls([
call(True),
])
- rgb25_view_model.is_loading.emit.assert_has_calls([call(True)])
- rgb25_view_model.on_error.assert_called_once()
+ cfa_view_model.is_loading.emit.assert_has_calls([call(True)])
+ cfa_view_model.on_error.assert_called_once()
assert isinstance(
- rgb25_view_model.on_error.call_args[0][0], CommonException,
+ cfa_view_model.on_error.call_args[0][0], CommonException,
)
assert str(mock_exception) in str(
- rgb25_view_model.on_error.call_args[0][0],
+ cfa_view_model.on_error.call_args[0][0],
)
# Test authentication cancelled case
# Reset mocks
- rgb25_view_model.send_rgb25_button_clicked.reset_mock()
- rgb25_view_model.is_loading.reset_mock()
+ cfa_view_model.send_cfa_button_clicked.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
mock_toast_error.reset_mock()
# Call method with success=False
- rgb25_view_model.on_success_send_rgb_asset(False)
+ cfa_view_model.on_success_send_rgb_asset(False)
# Verify behavior for cancelled case
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_not_called()
- rgb25_view_model.is_loading.emit.assert_not_called()
+ cfa_view_model.send_cfa_button_clicked.emit.assert_not_called()
+ cfa_view_model.is_loading.emit.assert_not_called()
mock_toast_error.assert_called_once_with(
description=ERROR_AUTHENTICATION_CANCELLED,
)
-def test_on_error(rgb25_view_model, mocker):
- """Test the on_error method of RGB25ViewModel."""
- rgb25_view_model.is_loading = MagicMock()
- rgb25_view_model.send_rgb25_button_clicked = MagicMock()
+def test_on_error(cfa_view_model, mocker):
+ """Test the on_error method of CFAViewModel."""
+ cfa_view_model.is_loading = MagicMock()
+ cfa_view_model.send_cfa_button_clicked = MagicMock()
# Create test error
mock_error = CommonException('Test error message')
mock_toast_error = mocker.patch(
- 'src.viewmodels.rgb_25_view_model.ToastManager.error',
+ 'src.viewmodels.cfa_view_model.ToastManager.error',
)
# Reset mocks
- rgb25_view_model.is_loading.reset_mock()
- rgb25_view_model.send_rgb25_button_clicked.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
+ cfa_view_model.send_cfa_button_clicked.reset_mock()
mock_toast_error.reset_mock()
# Call on_error method
- rgb25_view_model.on_error(mock_error)
+ cfa_view_model.on_error(mock_error)
# Verify behavior
- rgb25_view_model.is_loading.emit.assert_called_once_with(False)
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_called_once_with(
+ cfa_view_model.is_loading.emit.assert_called_once_with(False)
+ cfa_view_model.send_cfa_button_clicked.emit.assert_called_once_with(
False,
)
mock_toast_error.assert_called_once_with(description=mock_error.message)
-def test_on_success_rgb25(rgb25_view_model, mocker):
- """Test the on_success_rgb25 method of RGB25ViewModel."""
+def test_on_success_cfa(cfa_view_model, mocker):
+ """Test the on_success_cfa method of CFAViewModel."""
# Setup mocks
- rgb25_view_model.is_loading = MagicMock()
- rgb25_view_model.send_rgb25_button_clicked = MagicMock()
- rgb25_view_model._page_navigation = MagicMock()
+ cfa_view_model.is_loading = MagicMock()
+ cfa_view_model.send_cfa_button_clicked = MagicMock()
+ cfa_view_model._page_navigation = MagicMock()
mock_toast_success = mocker.patch(
- 'src.viewmodels.rgb_25_view_model.ToastManager.success',
+ 'src.viewmodels.cfa_view_model.ToastManager.success',
)
# Create test tx_id
mock_tx_id = SendAssetResponseModel(txid='test_txid_123')
- # Test RGB25 asset type
- rgb25_view_model.asset_type = AssetType.RGB25.value
- rgb25_view_model.on_success_rgb25(mock_tx_id)
+ # Test CFA asset type
+ cfa_view_model.asset_type = AssetType.CFA.value
+ cfa_view_model.on_success_cfa(mock_tx_id)
- # Verify behavior for RGB25
- rgb25_view_model.is_loading.emit.assert_called_once_with(False)
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_called_once_with(
+ # Verify behavior for CFA
+ cfa_view_model.is_loading.emit.assert_called_once_with(False)
+ cfa_view_model.send_cfa_button_clicked.emit.assert_called_once_with(
False,
)
mock_toast_success.assert_called_once_with(
description=INFO_ASSET_SENT.format(mock_tx_id.txid),
)
- rgb25_view_model._page_navigation.collectibles_asset_page.assert_called_once()
+ cfa_view_model._page_navigation.collectibles_asset_page.assert_called_once()
# Reset mocks
- rgb25_view_model.is_loading.reset_mock()
- rgb25_view_model.send_rgb25_button_clicked.reset_mock()
- rgb25_view_model._page_navigation.reset_mock()
+ cfa_view_model.is_loading.reset_mock()
+ cfa_view_model.send_cfa_button_clicked.reset_mock()
+ cfa_view_model._page_navigation.reset_mock()
mock_toast_success.reset_mock()
- # Test RGB20 asset type
- rgb25_view_model.asset_type = AssetType.RGB20.value
- rgb25_view_model.on_success_rgb25(mock_tx_id)
+ # Test NIA asset type
+ cfa_view_model.asset_type = AssetType.NIA.value
+ cfa_view_model.on_success_cfa(mock_tx_id)
- # Verify behavior for RGB20
- rgb25_view_model.is_loading.emit.assert_called_once_with(False)
- rgb25_view_model.send_rgb25_button_clicked.emit.assert_called_once_with(
+ # Verify behavior for NIA
+ cfa_view_model.is_loading.emit.assert_called_once_with(False)
+ cfa_view_model.send_cfa_button_clicked.emit.assert_called_once_with(
False,
)
mock_toast_success.assert_called_once_with(
description=INFO_ASSET_SENT.format(mock_tx_id.txid),
)
- rgb25_view_model._page_navigation.fungibles_asset_page.assert_called_once()
+ cfa_view_model._page_navigation.fungibles_asset_page.assert_called_once()
diff --git a/unit_tests/tests/viewmodel_tests/channel_management_viewmodel_test.py b/unit_tests/tests/viewmodel_tests/channel_management_viewmodel_test.py
index 0ffa6997..74a7e6c4 100644
--- a/unit_tests/tests/viewmodel_tests/channel_management_viewmodel_test.py
+++ b/unit_tests/tests/viewmodel_tests/channel_management_viewmodel_test.py
@@ -18,7 +18,9 @@
from src.model.rgb_model import GetAssetResponseModel
from src.utils.custom_exception import CommonException
from src.utils.error_message import ERROR_CREATE_UTXO
-from src.utils.error_message import ERROR_INSUFFICIENT_ALLOCATION_SLOT
+from src.utils.error_message import ERROR_INSUFFICIENT_ASSET
+from src.utils.error_message import ERROR_INVALID_PUBKEY_TYPE
+from src.utils.error_message import ERROR_NO_UNCOLORED_UTXOS_AVAILABLE
from src.utils.error_message import ERROR_SOMETHING_WENT_WRONG
from src.utils.info_message import INFO_CHANNEL_DELETED
from src.viewmodels.channel_management_viewmodel import ChannelManagementViewModel
@@ -256,7 +258,6 @@ def test_get_asset_name(channel_view_model):
mock_nia_asset = AssetModel(
asset_id='1',
name='NIA Asset',
- asset_iface='interface1',
details=None,
precision=2,
issued_supply=4000,
@@ -269,7 +270,6 @@ def test_get_asset_name(channel_view_model):
mock_cfa_asset = AssetModel(
asset_id='2',
name='CFA Asset',
- asset_iface='interface2',
details=None,
precision=2,
issued_supply=1000,
@@ -308,7 +308,6 @@ def test_get_asset_list_success_with_both_assets(channel_view_model, mocker):
mock_nia_asset = AssetModel(
asset_id='1',
name='Asset1',
- asset_iface='interface1',
details=None,
precision=2,
issued_supply=1000,
@@ -322,7 +321,6 @@ def test_get_asset_list_success_with_both_assets(channel_view_model, mocker):
mock_cfa_asset = AssetModel(
asset_id='2',
name='Asset2',
- asset_iface='interface2',
details=None,
precision=2,
issued_supply=2000,
@@ -453,7 +451,7 @@ def test_create_rgb_channel_insufficient_allocation_error(channel_view_model, mo
channel_view_model.handle_insufficient_allocation = MagicMock()
channel_view_model.run_in_thread = MagicMock()
- error_message = ERROR_INSUFFICIENT_ALLOCATION_SLOT
+ error_message = ERROR_NO_UNCOLORED_UTXOS_AVAILABLE
mock_error = CommonException(error_message)
# Act
@@ -491,6 +489,64 @@ def test_navigate_to_create_channel_page(channel_view_model, mocker):
mock_page_navigation.create_channel_page.assert_called_once()
+def test_create_rgb_channel_hard_stop_invalid_pubkey(channel_view_model, mocker):
+ """Ensure invalid pubkey error shows toast and does not retry creating UTXOs."""
+ # Arrange
+ channel_view_model.is_loading = MagicMock()
+ channel_view_model.handle_insufficient_allocation = MagicMock()
+ channel_view_model.run_in_thread = MagicMock()
+ mock_toast_error = mocker.patch(
+ 'src.views.components.toast.ToastManager.error',
+ )
+
+ mock_error = CommonException(ERROR_INVALID_PUBKEY_TYPE)
+
+ # Act
+ channel_view_model.create_rgb_channel(
+ pub_key='pub_key', asset_id='asset_id', amount=1000, capacity_sat='30000', push_msat='0',
+ )
+
+ run_thread_kwargs = channel_view_model.run_in_thread.call_args[0][1]
+ on_error = run_thread_kwargs['error_callback']
+ on_error(mock_error)
+
+ # Assert
+ channel_view_model.is_loading.emit.assert_called_with(False)
+ mock_toast_error.assert_called_once_with(
+ description=ERROR_INVALID_PUBKEY_TYPE,
+ )
+ channel_view_model.handle_insufficient_allocation.assert_not_called()
+
+
+def test_create_rgb_channel_hard_stop_insufficient_asset(channel_view_model, mocker):
+ """Ensure not enough assets error shows toast and does not retry creating UTXOs."""
+ # Arrange
+ channel_view_model.is_loading = MagicMock()
+ channel_view_model.handle_insufficient_allocation = MagicMock()
+ channel_view_model.run_in_thread = MagicMock()
+ mock_toast_error = mocker.patch(
+ 'src.views.components.toast.ToastManager.error',
+ )
+
+ mock_error = CommonException(ERROR_INSUFFICIENT_ASSET)
+
+ # Act
+ channel_view_model.create_rgb_channel(
+ pub_key='pub_key', asset_id='asset_id', amount=1000, capacity_sat='30000', push_msat='0',
+ )
+
+ run_thread_kwargs = channel_view_model.run_in_thread.call_args[0][1]
+ on_error = run_thread_kwargs['error_callback']
+ on_error(mock_error)
+
+ # Assert
+ channel_view_model.is_loading.emit.assert_called_with(False)
+ mock_toast_error.assert_called_once_with(
+ description=ERROR_INSUFFICIENT_ASSET,
+ )
+ channel_view_model.handle_insufficient_allocation.assert_not_called()
+
+
def test_handle_insufficient_allocation(channel_view_model, mocker):
"""Test handle_insufficient_allocation method."""
# Arrange
diff --git a/unit_tests/tests/viewmodel_tests/header_frame_view_model_test.py b/unit_tests/tests/viewmodel_tests/header_frame_view_model_test.py
index 18f0f61a..4486d150 100644
--- a/unit_tests/tests/viewmodel_tests/header_frame_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/header_frame_view_model_test.py
@@ -85,6 +85,9 @@ def test_header_frame_view_model_init(mocker):
def test_header_frame_view_model_network_check(mocker):
"""Test that start_network_check creates a NetworkCheckerThread and starts it."""
+ # Mock QTimer to prevent side effects during init
+ mocker.patch('src.viewmodels.header_frame_view_model.QTimer')
+
view_model = HeaderFrameViewModel()
mock_thread = mocker.patch(
@@ -101,8 +104,11 @@ def test_header_frame_view_model_network_check(mocker):
mock_instance.start.assert_called_once()
-def test_header_frame_view_model_handle_network_status():
+def test_header_frame_view_model_handle_network_status(mocker):
"""Test that handle_network_status emits the correct signal."""
+ # Mock QTimer to prevent it from starting and creating circular references
+ mocker.patch('src.viewmodels.header_frame_view_model.QTimer')
+
view_model = HeaderFrameViewModel()
received_signals = []
@@ -119,10 +125,17 @@ def signal_received(value):
def test_header_frame_view_model_stop_network_checker(mocker):
"""Test that stop_network_checker stops the timer."""
+ # Mock QTimer so we can verify stop() is called without starting real timer
+ mock_timer_class = mocker.patch(
+ 'src.viewmodels.header_frame_view_model.QTimer',
+ )
+ mock_timer_instance = mock_timer_class.return_value
+
view_model = HeaderFrameViewModel()
- mock_timer = mocker.patch.object(view_model.timer, 'stop')
+ # Reset mock to clear init calls
+ mock_timer_instance.stop.reset_mock()
view_model.stop_network_checker()
- mock_timer.assert_called_once()
+ mock_timer_instance.stop.assert_called_once()
diff --git a/unit_tests/tests/viewmodel_tests/issue_cfa_view_model_test.py b/unit_tests/tests/viewmodel_tests/issue_cfa_view_model_test.py
new file mode 100644
index 00000000..a9e7c5c5
--- /dev/null
+++ b/unit_tests/tests/viewmodel_tests/issue_cfa_view_model_test.py
@@ -0,0 +1,289 @@
+"""Unit test for Issue CFA view model.
+
+This module contains tests for the IssueCFAViewModel class, which represents the view model
+for the Issue CFA Asset page activities.
+"""
+# Disable the redefined-outer-name warning as
+# it's normal to pass mocked object in tests function
+# pylint: disable=redefined-outer-name,unused-argument
+from __future__ import annotations
+
+from unittest.mock import MagicMock
+from unittest.mock import Mock
+from unittest.mock import patch
+
+import pytest
+from PySide6.QtWidgets import QFileDialog
+
+from src.model.rgb_model import AssetBalanceResponseModel
+from src.model.rgb_model import IssueAssetResponseModel
+from src.utils.custom_exception import CommonException
+from src.utils.error_message import ERROR_AUTHENTICATION
+from src.utils.error_message import ERROR_FIELD_MISSING
+from src.utils.error_message import ERROR_SOMETHING_WENT_WRONG
+from src.utils.info_message import INFO_ASSET_ISSUED
+from src.utils.info_message import INFO_NO_FILE
+from src.viewmodels.issue_cfa_view_model import IssueCFAViewModel
+
+
+@pytest.fixture
+def mock_page_navigation(mocker):
+ """Fixture to create a mock page navigation object."""
+ return mocker.MagicMock()
+
+
+@pytest.fixture
+def issue_cfa_view_model(mock_page_navigation):
+ """Fixture to create an instance of the IssueCFAViewModel class."""
+ return IssueCFAViewModel(mock_page_navigation)
+
+
+@patch('src.views.components.toast.ToastManager')
+def test_open_file_dialog_normal_execution(mock_toast_manager, issue_cfa_view_model):
+ """Test open_file_dialog method when a file is selected"""
+ with patch.object(QFileDialog, 'exec_', return_value=True), \
+ patch.object(QFileDialog, 'selectedFiles', return_value=['/path/to/file.png']):
+
+ # Create a mock signal to check if the file_upload_message signal is emitted
+ mock_signal = Mock()
+ issue_cfa_view_model.file_upload_message.connect(mock_signal)
+
+ issue_cfa_view_model.open_file_dialog()
+
+ # Verify that the signal is emitted with the correct file path
+ mock_signal.assert_called_once_with('/path/to/file.png')
+
+
+@patch('src.views.components.toast.ToastManager.error')
+@patch('src.data.repository.rgb_repository.RgbRepository.issue_asset_cfa')
+@patch('src.utils.worker.ThreadManager.run_in_thread')
+def test_issue_cfa_asset_failure(
+ mock_run_in_thread, mock_issue_asset_cfa, mock_toast_error,
+ issue_cfa_view_model, mock_page_navigation,
+):
+ """Test for failure to issue CFA asset."""
+ # Simulate failure in issuing the asset
+ mock_issue_asset_cfa.side_effect = CommonException('Failed to issue asset')
+
+ # Mock the worker
+ mock_worker = MagicMock()
+ issue_cfa_view_model.worker = mock_worker
+
+ # Provide required input data
+ issue_cfa_view_model.uploaded_file_path = 'path/to/file.png'
+
+ # Perform the action
+ issue_cfa_view_model.issue_cfa_asset('ticker', 'asset_name', '100')
+
+ # Simulate the error callback
+ mock_worker.error.emit(mock_issue_asset_cfa.side_effect)
+
+ mock_page_navigation.collectibles_asset_page.assert_not_called()
+
+
+@patch('src.views.components.toast.ToastManager')
+def test_open_file_dialog_success(mock_toast_manager, issue_cfa_view_model, mocker):
+ """Test for open_file_dialog method when file is successfully selected"""
+ with patch('PySide6.QtWidgets.QFileDialog.exec_', return_value=True), \
+ patch('PySide6.QtWidgets.QFileDialog.selectedFiles', return_value=['/path/to/selected/image.png']):
+ issue_cfa_view_model.open_file_dialog()
+
+ assert issue_cfa_view_model.uploaded_file_path == '/path/to/selected/image.png'
+
+
+@patch('src.views.components.toast.ToastManager')
+def test_open_file_dialog_no_selection(mock_toast_manager, issue_cfa_view_model, mocker):
+ """Test for open_file_dialog method when no file is selected"""
+ with patch('PySide6.QtWidgets.QFileDialog.exec_', return_value=False):
+ issue_cfa_view_model.open_file_dialog()
+
+ assert issue_cfa_view_model.uploaded_file_path is None
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_open_file_dialog_exception(mock_toast_manager, issue_cfa_view_model, mocker):
+ """Test for open_file_dialog method when an exception is raised"""
+ with patch('PySide6.QtWidgets.QFileDialog.exec_', side_effect=CommonException('Test error')):
+ issue_cfa_view_model.open_file_dialog()
+
+ mock_toast_manager.assert_called_once_with(
+ description='An unexpected error occurred: Test error',
+ )
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_on_success_native_auth_cfa_missing_fields(mock_toast_manager, issue_cfa_view_model):
+ """Test on_success_native_auth_cfa with missing required fields"""
+ issue_cfa_view_model.is_loading = MagicMock()
+
+ issue_cfa_view_model.amount = None
+ issue_cfa_view_model.asset_name = None
+ issue_cfa_view_model.asset_ticker = None
+
+ issue_cfa_view_model.on_success_native_auth_cfa(True)
+
+ mock_toast_manager.assert_called_once_with(description=ERROR_FIELD_MISSING)
+ issue_cfa_view_model.is_loading.emit.assert_called_with(False)
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_on_error_native_auth_cfa(mock_toast_manager, issue_cfa_view_model):
+ """Test on_error_native_auth_cfa with different error types"""
+ # Test with CommonException
+ common_error = CommonException('Test error')
+ issue_cfa_view_model.on_error_native_auth_cfa(common_error)
+ mock_toast_manager.assert_called_with(description='Test error')
+
+ # Test with generic Exception
+ generic_error = Exception('Generic error')
+ issue_cfa_view_model.on_error_native_auth_cfa(generic_error)
+ mock_toast_manager.assert_called_with(
+ description=ERROR_SOMETHING_WENT_WRONG,
+ )
+
+
+@patch('src.views.components.toast.ToastManager.success')
+def test_on_success(mock_toast_manager, issue_cfa_view_model):
+ """Test on_success callback"""
+ issue_cfa_view_model.success_page_message = MagicMock()
+ issue_cfa_view_model.is_loading = MagicMock()
+
+ response = IssueAssetResponseModel(
+ asset_id='test_id',
+ name='Test Asset',
+ ticker='TEST',
+ details='details',
+ precision=2,
+ issued_supply=1000,
+ timestamp=123456789,
+ added_at=123456789,
+ balance=AssetBalanceResponseModel(
+ spendable=10, future=10, settled=12, offchain_outbound=0, offchain_inbound=0,
+ ),
+ )
+
+ issue_cfa_view_model.on_success(response)
+
+ mock_toast_manager.assert_called_once_with(
+ description=INFO_ASSET_ISSUED.format('test_id'),
+ )
+ issue_cfa_view_model.success_page_message.emit.assert_called_once_with(
+ 'Test Asset',
+ )
+ issue_cfa_view_model.is_loading.emit.assert_called_once_with(False)
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_on_error(mock_toast_manager, issue_cfa_view_model):
+ """Test on_error callback"""
+ issue_cfa_view_model.is_loading = MagicMock()
+ error = CommonException('Test error')
+
+ issue_cfa_view_model.on_error(error)
+
+ mock_toast_manager.assert_called_once_with(description='Test error')
+ issue_cfa_view_model.is_loading.emit.assert_called_once_with(False)
+
+ @patch('src.views.components.toast.ToastManager.error')
+ def test_on_success_native_auth_cfa_with_generic_exception(mock_toast_manager, issue_cfa_view_model):
+ """Test on_success_native_auth_cfa with generic exception"""
+ issue_cfa_view_model.is_loading = MagicMock()
+ issue_cfa_view_model.run_in_thread = MagicMock(
+ side_effect=Exception('Unexpected error'),
+ )
+
+ # Set required attributes
+ issue_cfa_view_model.amount = '100'
+ issue_cfa_view_model.asset_name = 'Test Asset'
+ issue_cfa_view_model.asset_ticker = 'TEST'
+ issue_cfa_view_model.uploaded_file_path = '/path/to/file.png'
+
+ issue_cfa_view_model.on_success_native_auth_cfa(True)
+
+ mock_toast_manager.assert_called_once_with(
+ description=ERROR_SOMETHING_WENT_WRONG,
+ )
+ issue_cfa_view_model.is_loading.emit.assert_called_once_with(False)
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_on_success_native_auth_cfa_auth_failed(mock_toast_manager, issue_cfa_view_model):
+ """Test on_success_native_auth_cfa when authentication fails"""
+ issue_cfa_view_model.is_loading = MagicMock()
+
+ # Set required attributes
+ issue_cfa_view_model.amount = '100'
+ issue_cfa_view_model.asset_name = 'Test Asset'
+ issue_cfa_view_model.asset_ticker = 'TEST'
+
+ issue_cfa_view_model.on_success_native_auth_cfa(False)
+
+ mock_toast_manager.assert_called_once_with(
+ description=ERROR_AUTHENTICATION,
+ )
+ issue_cfa_view_model.is_loading.emit.assert_called_once_with(False)
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_on_success_native_auth_cfa_no_file(mock_toast_manager, issue_cfa_view_model):
+ """Test on_success_native_auth_cfa when no file is uploaded"""
+ issue_cfa_view_model.is_loading = MagicMock()
+
+ # Set required attributes except file path
+ issue_cfa_view_model.amount = '100'
+ issue_cfa_view_model.asset_name = 'Test Asset'
+ issue_cfa_view_model.asset_ticker = 'TEST'
+ issue_cfa_view_model.uploaded_file_path = None
+
+ issue_cfa_view_model.on_success_native_auth_cfa(True)
+
+ mock_toast_manager.assert_called_once_with(description=INFO_NO_FILE)
+ issue_cfa_view_model.is_loading.emit.assert_called_once_with(False)
+
+
+@patch('src.viewmodels.issue_cfa_view_model.IssueAssetService')
+def test_on_success_native_auth_cfa_success(mock_issue_asset_service, issue_cfa_view_model):
+ """Test on_success_native_auth_cfa successful execution"""
+ issue_cfa_view_model.run_in_thread = MagicMock()
+
+ # Set all required attributes
+ issue_cfa_view_model.amount = '100'
+ issue_cfa_view_model.asset_name = 'Test Asset'
+ issue_cfa_view_model.asset_ticker = 'TEST'
+ issue_cfa_view_model.uploaded_file_path = '/path/to/file.png'
+
+ issue_cfa_view_model.on_success_native_auth_cfa(True)
+
+ # Verify run_in_thread was called with correct arguments
+ issue_cfa_view_model.run_in_thread.assert_called_once()
+ call_args = issue_cfa_view_model.run_in_thread.call_args[0][1]
+
+ assert call_args['callback'] == issue_cfa_view_model.on_success
+ assert call_args['error_callback'] == issue_cfa_view_model.on_error
+ assert len(call_args['args']) == 1
+
+ request_model = call_args['args'][0]
+ assert request_model.amounts == [100]
+ assert request_model.ticker == 'TEST'
+ assert request_model.name == 'Test Asset'
+ assert request_model.file_path == '/path/to/file.png'
+
+
+@patch('src.views.components.toast.ToastManager.error')
+def test_on_success_native_auth_cfa_exception(mock_toast_manager, issue_cfa_view_model):
+ """Test on_success_native_auth_cfa when an unexpected exception occurs"""
+ issue_cfa_view_model.is_loading = MagicMock()
+
+ # Set all required attributes
+ # This will cause an exception when converting to int
+ issue_cfa_view_model.amount = 'invalid_amount'
+ issue_cfa_view_model.asset_name = 'Test Asset'
+ issue_cfa_view_model.asset_ticker = 'TEST'
+ issue_cfa_view_model.uploaded_file_path = '/path/to/file.png'
+
+ issue_cfa_view_model.on_success_native_auth_cfa(True)
+
+ mock_toast_manager.assert_called_once_with(
+ description=ERROR_SOMETHING_WENT_WRONG,
+ )
+ issue_cfa_view_model.is_loading.emit.assert_called_once_with(False)
diff --git a/unit_tests/tests/viewmodel_tests/issue_rgb20_view_model_test.py b/unit_tests/tests/viewmodel_tests/issue_nia_view_model_test.py
similarity index 61%
rename from unit_tests/tests/viewmodel_tests/issue_rgb20_view_model_test.py
rename to unit_tests/tests/viewmodel_tests/issue_nia_view_model_test.py
index 9cb98c5f..27e75d22 100644
--- a/unit_tests/tests/viewmodel_tests/issue_rgb20_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/issue_nia_view_model_test.py
@@ -1,4 +1,4 @@
-"""Unit test for issue RGB20 view model"""
+"""Unit test for issue NIA view model"""
# Disable the redefined-outer-name warning as
# it's normal to pass mocked object in tests function
# pylint: disable=redefined-outer-name,unused-argument
@@ -14,7 +14,7 @@
from src.model.rgb_model import IssueAssetResponseModel
from src.utils.custom_exception import CommonException
from src.utils.error_message import ERROR_SOMETHING_WENT_WRONG
-from src.viewmodels.issue_rgb20_view_model import IssueRGB20ViewModel
+from src.viewmodels.issue_nia_view_model import IssueNIAViewModel
@pytest.fixture
@@ -24,9 +24,9 @@ def mock_page_navigation(mocker):
@pytest.fixture
-def issue_rgb20_view_model(mock_page_navigation):
- """Fixture to create an instance of the IssueRGB20ViewModel class."""
- return IssueRGB20ViewModel(mock_page_navigation)
+def issue_nia_view_model(mock_page_navigation):
+ """Fixture to create an instance of the IssueNIAViewModel class."""
+ return IssueNIAViewModel(mock_page_navigation)
@patch('src.views.components.toast.ToastManager.error')
@@ -34,13 +34,12 @@ def issue_rgb20_view_model(mock_page_navigation):
@patch('src.utils.worker.ThreadManager.run_in_thread')
def test_on_issue_click_success(
mock_run_in_thread, mock_issue_asset_nia, mock_toast_error,
- issue_rgb20_view_model, mock_page_navigation,
+ issue_nia_view_model, mock_page_navigation,
):
- """Test for successful issuing of RGB20 asset."""
+ """Test for successful issuing of NIA asset."""
# Mock the asset issuance response
mock_issue_asset_nia.return_value = IssueAssetResponseModel(
asset_id='asset_id',
- asset_iface='interface',
ticker='ticker',
name='name',
details='details',
@@ -55,17 +54,17 @@ def test_on_issue_click_success(
# Mock signals
mock_issue_button_clicked = Mock()
- issue_rgb20_view_model.issue_button_clicked.connect(
+ issue_nia_view_model.issue_button_clicked.connect(
mock_issue_button_clicked,
)
# Mock worker
mock_worker = MagicMock()
- issue_rgb20_view_model.worker = mock_worker
+ issue_nia_view_model.worker = mock_worker
mock_worker.result.emit = Mock()
# Perform the action
- issue_rgb20_view_model.on_issue_click(
+ issue_nia_view_model.on_issue_click(
'short_identifier', 'asset_name', '100',
)
@@ -83,14 +82,13 @@ def test_on_issue_click_success(
@patch('src.utils.worker.ThreadManager.run_in_thread')
def test_on_success_native_auth(
mock_run_in_thread, mock_native_authentication, mock_issue_asset_nia,
- mock_toast_error, issue_rgb20_view_model, mock_page_navigation,
+ mock_toast_error, issue_nia_view_model, mock_page_navigation,
):
"""Test for successful native authentication and asset issuance."""
# Mock native authentication and asset issuance
mock_native_authentication.return_value = True
mock_issue_asset_nia.return_value = IssueAssetResponseModel(
asset_id='asset_id',
- asset_iface='interface',
ticker='ticker',
name='name',
details='details',
@@ -105,37 +103,37 @@ def test_on_success_native_auth(
# Connect signals to mocks
mock_issue_button_clicked = MagicMock()
- issue_rgb20_view_model.issue_button_clicked.connect(
+ issue_nia_view_model.issue_button_clicked.connect(
mock_issue_button_clicked,
)
mock_is_issued = MagicMock()
- issue_rgb20_view_model.is_issued.connect(mock_is_issued)
+ issue_nia_view_model.is_issued.connect(mock_is_issued)
# Set test data
- issue_rgb20_view_model.token_amount = '100'
- issue_rgb20_view_model.asset_name = 'asset_name'
- issue_rgb20_view_model.short_identifier = 'short_identifier'
+ issue_nia_view_model.token_amount = '100'
+ issue_nia_view_model.asset_name = 'asset_name'
+ issue_nia_view_model.short_identifier = 'short_identifier'
# Simulate success callback for native authentication
- issue_rgb20_view_model.on_success_native_auth_rgb20(success=True)
+ issue_nia_view_model.on_success_native_auth_nia(success=True)
# Simulate worker behavior
mock_worker = MagicMock()
- issue_rgb20_view_model.worker = mock_worker
+ issue_nia_view_model.worker = mock_worker
mock_worker.result.emit(mock_issue_asset_nia.return_value)
mock_toast_error.assert_not_called()
def test_on_success_native_auth_generic_exception(
- issue_rgb20_view_model,
+ issue_nia_view_model,
):
"""Test for handling generic Exception in on_success_native_auth."""
# Setup
with patch('src.views.components.toast.ToastManager.error') as mock_show_toast:
# Trigger the exception
- issue_rgb20_view_model.on_success_native_auth_rgb20(success=False)
+ issue_nia_view_model.on_success_native_auth_nia(success=False)
# Verify the call to show_toast
mock_show_toast.assert_called_once_with(
@@ -144,16 +142,16 @@ def test_on_success_native_auth_generic_exception(
@patch('src.views.components.toast.ToastManager.error')
-def test_on_success_native_auth_rgb20_missing_value(mock_toast_manager, issue_rgb20_view_model):
- """Test on_success_native_auth_rgb25 when an unexpected exception occurs"""
+def test_on_success_native_auth_nia_missing_value(mock_toast_manager, issue_nia_view_model):
+ """Test on_success_native_auth_cfa when an unexpected exception occurs"""
# Set all required attributes
# This will cause an exception when converting to int
- issue_rgb20_view_model.amount = ''
- issue_rgb20_view_model.asset_name = 'Test Asset'
- issue_rgb20_view_model.asset_ticker = 'TEST'
+ issue_nia_view_model.amount = ''
+ issue_nia_view_model.asset_name = 'Test Asset'
+ issue_nia_view_model.asset_ticker = 'TEST'
- issue_rgb20_view_model.on_success_native_auth_rgb20(True)
+ issue_nia_view_model.on_success_native_auth_nia(True)
mock_toast_manager.assert_called_once_with(
description='Few fields missing',
@@ -161,76 +159,76 @@ def test_on_success_native_auth_rgb20_missing_value(mock_toast_manager, issue_rg
@patch('src.views.components.toast.ToastManager.error')
-def test_on_success_native_auth_rgb20_exception(mock_toast_manager, issue_rgb20_view_model):
- """Test on_success_native_auth_rgb25 when an unexpected exception occurs"""
- issue_rgb20_view_model.issue_button_clicked = MagicMock()
+def test_on_success_native_auth_nia_exception(mock_toast_manager, issue_nia_view_model):
+ """Test on_success_native_auth_cfa when an unexpected exception occurs"""
+ issue_nia_view_model.issue_button_clicked = MagicMock()
# Set required attributes
- issue_rgb20_view_model.token_amount = '100'
- issue_rgb20_view_model.asset_name = 'Test Asset'
- issue_rgb20_view_model.short_identifier = 'TEST'
+ issue_nia_view_model.token_amount = '100'
+ issue_nia_view_model.asset_name = 'Test Asset'
+ issue_nia_view_model.short_identifier = 'TEST'
# Mock run_in_thread to raise an exception
def mock_run_in_thread(*args, **kwargs):
raise RuntimeError('Test exception')
# Patch run_in_thread method
- with patch.object(issue_rgb20_view_model, 'run_in_thread', side_effect=mock_run_in_thread):
- issue_rgb20_view_model.on_success_native_auth_rgb20(True)
+ with patch.object(issue_nia_view_model, 'run_in_thread', side_effect=mock_run_in_thread):
+ issue_nia_view_model.on_success_native_auth_nia(True)
mock_toast_manager.assert_called_once_with(
description=ERROR_SOMETHING_WENT_WRONG,
)
- issue_rgb20_view_model.issue_button_clicked.emit.assert_called_once_with(
+ issue_nia_view_model.issue_button_clicked.emit.assert_called_once_with(
False,
)
@patch('src.views.components.toast.ToastManager.error')
-def test_on_error_native_auth_rgb20_common_exception(mock_toast_manager, issue_rgb20_view_model):
- """Test on_error_native_auth_rgb20 with CommonException"""
- issue_rgb20_view_model.issue_button_clicked = MagicMock()
+def test_on_error_native_auth_nia_common_exception(mock_toast_manager, issue_nia_view_model):
+ """Test on_error_native_auth_nia with CommonException"""
+ issue_nia_view_model.issue_button_clicked = MagicMock()
test_message = 'Test error message'
test_error = CommonException(message=test_message)
- issue_rgb20_view_model.on_error_native_auth_rgb20(test_error)
+ issue_nia_view_model.on_error_native_auth_nia(test_error)
mock_toast_manager.assert_called_once_with(description=test_message)
- issue_rgb20_view_model.issue_button_clicked.emit.assert_called_once_with(
+ issue_nia_view_model.issue_button_clicked.emit.assert_called_once_with(
False,
)
@patch('src.views.components.toast.ToastManager.error')
-def test_on_error_native_auth_rgb20_generic_exception(mock_toast_manager, issue_rgb20_view_model):
- """Test on_error_native_auth_rgb20 with generic Exception"""
- issue_rgb20_view_model.issue_button_clicked = MagicMock()
+def test_on_error_native_auth_nia_generic_exception(mock_toast_manager, issue_nia_view_model):
+ """Test on_error_native_auth_nia with generic Exception"""
+ issue_nia_view_model.issue_button_clicked = MagicMock()
test_error = Exception('Test error')
- issue_rgb20_view_model.on_error_native_auth_rgb20(test_error)
+ issue_nia_view_model.on_error_native_auth_nia(test_error)
mock_toast_manager.assert_called_once_with(
description=ERROR_SOMETHING_WENT_WRONG,
)
- issue_rgb20_view_model.issue_button_clicked.emit.assert_called_once_with(
+ issue_nia_view_model.issue_button_clicked.emit.assert_called_once_with(
False,
)
@patch('src.views.components.toast.ToastManager.error')
-def test_on_error(mock_toast_manager, issue_rgb20_view_model):
- """Test on_error method for RGB20 issue page"""
+def test_on_error(mock_toast_manager, issue_nia_view_model):
+ """Test on_error method for NIA issue page"""
# Setup
- issue_rgb20_view_model.issue_button_clicked = MagicMock()
+ issue_nia_view_model.issue_button_clicked = MagicMock()
test_message = 'Test error message'
test_error = MagicMock()
test_error.message = test_message
# Execute
- issue_rgb20_view_model.on_error(test_error)
+ issue_nia_view_model.on_error(test_error)
# Assert
mock_toast_manager.assert_called_once_with(description=test_message)
- issue_rgb20_view_model.issue_button_clicked.emit.assert_called_once_with(
+ issue_nia_view_model.issue_button_clicked.emit.assert_called_once_with(
False,
)
diff --git a/unit_tests/tests/viewmodel_tests/issue_rgb25_view_model_test.py b/unit_tests/tests/viewmodel_tests/issue_rgb25_view_model_test.py
deleted file mode 100644
index 3ce20707..00000000
--- a/unit_tests/tests/viewmodel_tests/issue_rgb25_view_model_test.py
+++ /dev/null
@@ -1,290 +0,0 @@
-"""Unit test for Issue RGB25 view model.
-
-This module contains tests for the IssueRGB25ViewModel class, which represents the view model
-for the Issue RGB25 Asset page activities.
-"""
-# Disable the redefined-outer-name warning as
-# it's normal to pass mocked object in tests function
-# pylint: disable=redefined-outer-name,unused-argument
-from __future__ import annotations
-
-from unittest.mock import MagicMock
-from unittest.mock import Mock
-from unittest.mock import patch
-
-import pytest
-from PySide6.QtWidgets import QFileDialog
-
-from src.model.rgb_model import AssetBalanceResponseModel
-from src.model.rgb_model import IssueAssetResponseModel
-from src.utils.custom_exception import CommonException
-from src.utils.error_message import ERROR_AUTHENTICATION
-from src.utils.error_message import ERROR_FIELD_MISSING
-from src.utils.error_message import ERROR_SOMETHING_WENT_WRONG
-from src.utils.info_message import INFO_ASSET_ISSUED
-from src.utils.info_message import INFO_NO_FILE
-from src.viewmodels.issue_rgb25_view_model import IssueRGB25ViewModel
-
-
-@pytest.fixture
-def mock_page_navigation(mocker):
- """Fixture to create a mock page navigation object."""
- return mocker.MagicMock()
-
-
-@pytest.fixture
-def issue_rgb25_view_model(mock_page_navigation):
- """Fixture to create an instance of the IssueRGB25ViewModel class."""
- return IssueRGB25ViewModel(mock_page_navigation)
-
-
-@patch('src.views.components.toast.ToastManager')
-def test_open_file_dialog_normal_execution(mock_toast_manager, issue_rgb25_view_model):
- """Test open_file_dialog method when a file is selected"""
- with patch.object(QFileDialog, 'exec_', return_value=True), \
- patch.object(QFileDialog, 'selectedFiles', return_value=['/path/to/file.png']):
-
- # Create a mock signal to check if the file_upload_message signal is emitted
- mock_signal = Mock()
- issue_rgb25_view_model.file_upload_message.connect(mock_signal)
-
- issue_rgb25_view_model.open_file_dialog()
-
- # Verify that the signal is emitted with the correct file path
- mock_signal.assert_called_once_with('/path/to/file.png')
-
-
-@patch('src.views.components.toast.ToastManager.error')
-@patch('src.data.repository.rgb_repository.RgbRepository.issue_asset_cfa')
-@patch('src.utils.worker.ThreadManager.run_in_thread')
-def test_issue_rgb25_asset_failure(
- mock_run_in_thread, mock_issue_asset_cfa, mock_toast_error,
- issue_rgb25_view_model, mock_page_navigation,
-):
- """Test for failure to issue RGB25 asset."""
- # Simulate failure in issuing the asset
- mock_issue_asset_cfa.side_effect = CommonException('Failed to issue asset')
-
- # Mock the worker
- mock_worker = MagicMock()
- issue_rgb25_view_model.worker = mock_worker
-
- # Provide required input data
- issue_rgb25_view_model.uploaded_file_path = 'path/to/file.png'
-
- # Perform the action
- issue_rgb25_view_model.issue_rgb25_asset('ticker', 'asset_name', '100')
-
- # Simulate the error callback
- mock_worker.error.emit(mock_issue_asset_cfa.side_effect)
-
- mock_page_navigation.collectibles_asset_page.assert_not_called()
-
-
-@patch('src.views.components.toast.ToastManager')
-def test_open_file_dialog_success(mock_toast_manager, issue_rgb25_view_model, mocker):
- """Test for open_file_dialog method when file is successfully selected"""
- with patch('PySide6.QtWidgets.QFileDialog.exec_', return_value=True), \
- patch('PySide6.QtWidgets.QFileDialog.selectedFiles', return_value=['/path/to/selected/image.png']):
- issue_rgb25_view_model.open_file_dialog()
-
- assert issue_rgb25_view_model.uploaded_file_path == '/path/to/selected/image.png'
-
-
-@patch('src.views.components.toast.ToastManager')
-def test_open_file_dialog_no_selection(mock_toast_manager, issue_rgb25_view_model, mocker):
- """Test for open_file_dialog method when no file is selected"""
- with patch('PySide6.QtWidgets.QFileDialog.exec_', return_value=False):
- issue_rgb25_view_model.open_file_dialog()
-
- assert issue_rgb25_view_model.uploaded_file_path is None
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_open_file_dialog_exception(mock_toast_manager, issue_rgb25_view_model, mocker):
- """Test for open_file_dialog method when an exception is raised"""
- with patch('PySide6.QtWidgets.QFileDialog.exec_', side_effect=CommonException('Test error')):
- issue_rgb25_view_model.open_file_dialog()
-
- mock_toast_manager.assert_called_once_with(
- description='An unexpected error occurred: Test error',
- )
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_on_success_native_auth_rgb25_missing_fields(mock_toast_manager, issue_rgb25_view_model):
- """Test on_success_native_auth_rgb25 with missing required fields"""
- issue_rgb25_view_model.is_loading = MagicMock()
-
- issue_rgb25_view_model.amount = None
- issue_rgb25_view_model.asset_name = None
- issue_rgb25_view_model.asset_ticker = None
-
- issue_rgb25_view_model.on_success_native_auth_rgb25(True)
-
- mock_toast_manager.assert_called_once_with(description=ERROR_FIELD_MISSING)
- issue_rgb25_view_model.is_loading.emit.assert_called_with(False)
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_on_error_native_auth_rgb25(mock_toast_manager, issue_rgb25_view_model):
- """Test on_error_native_auth_rgb25 with different error types"""
- # Test with CommonException
- common_error = CommonException('Test error')
- issue_rgb25_view_model.on_error_native_auth_rgb25(common_error)
- mock_toast_manager.assert_called_with(description='Test error')
-
- # Test with generic Exception
- generic_error = Exception('Generic error')
- issue_rgb25_view_model.on_error_native_auth_rgb25(generic_error)
- mock_toast_manager.assert_called_with(
- description=ERROR_SOMETHING_WENT_WRONG,
- )
-
-
-@patch('src.views.components.toast.ToastManager.success')
-def test_on_success(mock_toast_manager, issue_rgb25_view_model):
- """Test on_success callback"""
- issue_rgb25_view_model.success_page_message = MagicMock()
- issue_rgb25_view_model.is_loading = MagicMock()
-
- response = IssueAssetResponseModel(
- asset_id='test_id',
- name='Test Asset',
- asset_iface='interface',
- ticker='TEST',
- details='details',
- precision=2,
- issued_supply=1000,
- timestamp=123456789,
- added_at=123456789,
- balance=AssetBalanceResponseModel(
- spendable=10, future=10, settled=12, offchain_outbound=0, offchain_inbound=0,
- ),
- )
-
- issue_rgb25_view_model.on_success(response)
-
- mock_toast_manager.assert_called_once_with(
- description=INFO_ASSET_ISSUED.format('test_id'),
- )
- issue_rgb25_view_model.success_page_message.emit.assert_called_once_with(
- 'Test Asset',
- )
- issue_rgb25_view_model.is_loading.emit.assert_called_once_with(False)
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_on_error(mock_toast_manager, issue_rgb25_view_model):
- """Test on_error callback"""
- issue_rgb25_view_model.is_loading = MagicMock()
- error = CommonException('Test error')
-
- issue_rgb25_view_model.on_error(error)
-
- mock_toast_manager.assert_called_once_with(description='Test error')
- issue_rgb25_view_model.is_loading.emit.assert_called_once_with(False)
-
- @patch('src.views.components.toast.ToastManager.error')
- def test_on_success_native_auth_rgb25_with_generic_exception(mock_toast_manager, issue_rgb25_view_model):
- """Test on_success_native_auth_rgb25 with generic exception"""
- issue_rgb25_view_model.is_loading = MagicMock()
- issue_rgb25_view_model.run_in_thread = MagicMock(
- side_effect=Exception('Unexpected error'),
- )
-
- # Set required attributes
- issue_rgb25_view_model.amount = '100'
- issue_rgb25_view_model.asset_name = 'Test Asset'
- issue_rgb25_view_model.asset_ticker = 'TEST'
- issue_rgb25_view_model.uploaded_file_path = '/path/to/file.png'
-
- issue_rgb25_view_model.on_success_native_auth_rgb25(True)
-
- mock_toast_manager.assert_called_once_with(
- description=ERROR_SOMETHING_WENT_WRONG,
- )
- issue_rgb25_view_model.is_loading.emit.assert_called_once_with(False)
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_on_success_native_auth_rgb25_auth_failed(mock_toast_manager, issue_rgb25_view_model):
- """Test on_success_native_auth_rgb25 when authentication fails"""
- issue_rgb25_view_model.is_loading = MagicMock()
-
- # Set required attributes
- issue_rgb25_view_model.amount = '100'
- issue_rgb25_view_model.asset_name = 'Test Asset'
- issue_rgb25_view_model.asset_ticker = 'TEST'
-
- issue_rgb25_view_model.on_success_native_auth_rgb25(False)
-
- mock_toast_manager.assert_called_once_with(
- description=ERROR_AUTHENTICATION,
- )
- issue_rgb25_view_model.is_loading.emit.assert_called_once_with(False)
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_on_success_native_auth_rgb25_no_file(mock_toast_manager, issue_rgb25_view_model):
- """Test on_success_native_auth_rgb25 when no file is uploaded"""
- issue_rgb25_view_model.is_loading = MagicMock()
-
- # Set required attributes except file path
- issue_rgb25_view_model.amount = '100'
- issue_rgb25_view_model.asset_name = 'Test Asset'
- issue_rgb25_view_model.asset_ticker = 'TEST'
- issue_rgb25_view_model.uploaded_file_path = None
-
- issue_rgb25_view_model.on_success_native_auth_rgb25(True)
-
- mock_toast_manager.assert_called_once_with(description=INFO_NO_FILE)
- issue_rgb25_view_model.is_loading.emit.assert_called_once_with(False)
-
-
-@patch('src.viewmodels.issue_rgb25_view_model.IssueAssetService')
-def test_on_success_native_auth_rgb25_success(mock_issue_asset_service, issue_rgb25_view_model):
- """Test on_success_native_auth_rgb25 successful execution"""
- issue_rgb25_view_model.run_in_thread = MagicMock()
-
- # Set all required attributes
- issue_rgb25_view_model.amount = '100'
- issue_rgb25_view_model.asset_name = 'Test Asset'
- issue_rgb25_view_model.asset_ticker = 'TEST'
- issue_rgb25_view_model.uploaded_file_path = '/path/to/file.png'
-
- issue_rgb25_view_model.on_success_native_auth_rgb25(True)
-
- # Verify run_in_thread was called with correct arguments
- issue_rgb25_view_model.run_in_thread.assert_called_once()
- call_args = issue_rgb25_view_model.run_in_thread.call_args[0][1]
-
- assert call_args['callback'] == issue_rgb25_view_model.on_success
- assert call_args['error_callback'] == issue_rgb25_view_model.on_error
- assert len(call_args['args']) == 1
-
- request_model = call_args['args'][0]
- assert request_model.amounts == [100]
- assert request_model.ticker == 'TEST'
- assert request_model.name == 'Test Asset'
- assert request_model.file_path == '/path/to/file.png'
-
-
-@patch('src.views.components.toast.ToastManager.error')
-def test_on_success_native_auth_rgb25_exception(mock_toast_manager, issue_rgb25_view_model):
- """Test on_success_native_auth_rgb25 when an unexpected exception occurs"""
- issue_rgb25_view_model.is_loading = MagicMock()
-
- # Set all required attributes
- # This will cause an exception when converting to int
- issue_rgb25_view_model.amount = 'invalid_amount'
- issue_rgb25_view_model.asset_name = 'Test Asset'
- issue_rgb25_view_model.asset_ticker = 'TEST'
- issue_rgb25_view_model.uploaded_file_path = '/path/to/file.png'
-
- issue_rgb25_view_model.on_success_native_auth_rgb25(True)
-
- mock_toast_manager.assert_called_once_with(
- description=ERROR_SOMETHING_WENT_WRONG,
- )
- issue_rgb25_view_model.is_loading.emit.assert_called_once_with(False)
diff --git a/unit_tests/tests/viewmodel_tests/main_asset_view_model_test.py b/unit_tests/tests/viewmodel_tests/main_asset_view_model_test.py
index a0c3b8ed..324f591f 100644
--- a/unit_tests/tests/viewmodel_tests/main_asset_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/main_asset_view_model_test.py
@@ -45,11 +45,10 @@ def mock_main_page_data_response():
nia=[
AssetModel(
asset_id='1',
- asset_iface='interface1',
- name='Asset1',
details=None,
- precision=2,
+ name='Asset1',
issued_supply=1000,
+ precision=2,
timestamp=1620000000,
added_at=1620001000,
balance=AssetBalanceResponseModel(
@@ -58,11 +57,10 @@ def mock_main_page_data_response():
),
AssetModel(
asset_id='2',
- asset_iface='interface2',
- name='Asset2',
details=None,
- precision=2,
+ name='Asset2',
issued_supply=2000,
+ precision=2,
timestamp=1620002000,
added_at=1620003000,
balance=AssetBalanceResponseModel(
@@ -73,7 +71,6 @@ def mock_main_page_data_response():
uda=[
AssetModel(
asset_id='3',
- asset_iface='interface3',
name='Asset3',
details=None,
precision=2,
@@ -86,7 +83,6 @@ def mock_main_page_data_response():
),
AssetModel(
asset_id='4',
- asset_iface='interface4',
name='Asset4',
details=None,
precision=2,
@@ -101,7 +97,6 @@ def mock_main_page_data_response():
cfa=[
AssetModel(
asset_id='5',
- asset_iface='interface5',
name='Asset5',
details=None,
precision=2,
@@ -114,7 +109,6 @@ def mock_main_page_data_response():
),
AssetModel(
asset_id='6',
- asset_iface='interface6',
name='Asset6',
details=None,
precision=2,
@@ -215,13 +209,13 @@ def mock_run_in_thread(func, kwargs):
list_loaded_mock.assert_called_once_with(False)
-def test_navigate_issue_rgb20_with_enough_balance(
+def test_navigate_issue_nia_with_enough_balance(
main_asset_view_model,
mock_btc_repository,
mock_btc_balance_response_with_positive,
mock_page_navigation,
):
- """Test navigation to issue rgb20 page with enough balance."""
+ """Test navigation to issue nia page with enough balance."""
# Mock signals
message_mock = Mock()
main_asset_view_model.message.connect(message_mock)
diff --git a/unit_tests/tests/viewmodel_tests/main_view_model_test.py b/unit_tests/tests/viewmodel_tests/main_view_model_test.py
index c1eaf87d..7b459eb4 100644
--- a/unit_tests/tests/viewmodel_tests/main_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/main_view_model_test.py
@@ -22,18 +22,18 @@ def test_main_view_model_initialization():
assert view_model.welcome_view_model is not None
assert view_model.terms_view_model is not None
assert view_model.main_asset_view_model is not None
- assert view_model.issue_rgb20_asset_view_model is not None
+ assert view_model.issue_nia_asset_view_model is not None
assert view_model.set_wallet_password_view_model is not None
assert view_model.bitcoin_view_model is not None
assert view_model.receive_bitcoin_view_model is not None
assert view_model.send_bitcoin_view_model is not None
assert view_model.channel_view_model is not None
assert view_model.unspent_view_model is not None
- assert view_model.issue_rgb25_asset_view_model is not None
+ assert view_model.issue_cfa_asset_view_model is not None
assert view_model.ln_endpoint_view_model is not None
- assert view_model.rgb25_view_model is not None
+ assert view_model.cfa_view_model is not None
assert view_model.enter_wallet_password_view_model is not None
- assert view_model.receive_rgb25_view_model is not None
+ assert view_model.receive_cfa_view_model is not None
assert view_model.backup_view_model is not None
assert view_model.setting_view_model is not None
assert view_model.ln_offchain_view_model is not None
@@ -53,18 +53,18 @@ def test_main_view_model_initialization():
assert view_model.welcome_view_model._page_navigation == mock_page_navigation
assert view_model.terms_view_model._page_navigation == mock_page_navigation
assert view_model.main_asset_view_model._page_navigation == mock_page_navigation
- assert view_model.issue_rgb20_asset_view_model._page_navigation == mock_page_navigation
+ assert view_model.issue_nia_asset_view_model._page_navigation == mock_page_navigation
assert view_model.set_wallet_password_view_model._page_navigation == mock_page_navigation
assert view_model.bitcoin_view_model._page_navigation == mock_page_navigation
assert view_model.receive_bitcoin_view_model._page_navigation == mock_page_navigation
assert view_model.send_bitcoin_view_model._page_navigation == mock_page_navigation
assert view_model.channel_view_model._page_navigation == mock_page_navigation
assert view_model.unspent_view_model._page_navigation == mock_page_navigation
- assert view_model.issue_rgb25_asset_view_model._page_navigation == mock_page_navigation
+ assert view_model.issue_cfa_asset_view_model._page_navigation == mock_page_navigation
assert view_model.ln_endpoint_view_model._page_navigation == mock_page_navigation
- assert view_model.rgb25_view_model._page_navigation == mock_page_navigation
+ assert view_model.cfa_view_model._page_navigation == mock_page_navigation
assert view_model.enter_wallet_password_view_model._page_navigation == mock_page_navigation
- assert view_model.receive_rgb25_view_model._page_navigation == mock_page_navigation
+ assert view_model.receive_cfa_view_model._page_navigation == mock_page_navigation
assert view_model.backup_view_model._page_navigation == mock_page_navigation
assert view_model.setting_view_model._page_navigation == mock_page_navigation
assert view_model.ln_offchain_view_model._page_navigation == mock_page_navigation
diff --git a/unit_tests/tests/viewmodel_tests/receive_rgb25_view_model_test.py b/unit_tests/tests/viewmodel_tests/receive_cfa_view_model_test.py
similarity index 66%
rename from unit_tests/tests/viewmodel_tests/receive_rgb25_view_model_test.py
rename to unit_tests/tests/viewmodel_tests/receive_cfa_view_model_test.py
index 322297af..3821df79 100644
--- a/unit_tests/tests/viewmodel_tests/receive_rgb25_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/receive_cfa_view_model_test.py
@@ -1,4 +1,4 @@
-"""Unit test for ReceiveRGB25ViewModel"""
+"""Unit test for ReceiveCFAViewModel"""
# Disable the redefined-outer-name warning as
# it's normal to pass mocked object in tests function
# pylint: disable=redefined-outer-name,unused-argument,protected-access
@@ -11,7 +11,7 @@
from src.model.rgb_model import RgbInvoiceDataResponseModel
from src.utils.custom_exception import CommonException
-from src.viewmodels.receive_rgb25_view_model import ReceiveRGB25ViewModel
+from src.viewmodels.receive_cfa_view_model import ReceiveCFAViewModel
@pytest.fixture
@@ -21,18 +21,18 @@ def mock_page_navigation(mocker):
@pytest.fixture
-def receive_rgb25_view_model(mock_page_navigation):
- """Fixture to create an instance of the ReceiveRGB25ViewModel class."""
- return ReceiveRGB25ViewModel(mock_page_navigation)
+def receive_cfa_view_model(mock_page_navigation):
+ """Fixture to create an instance of the ReceiveCFAViewModel class."""
+ return ReceiveCFAViewModel(mock_page_navigation)
@patch('src.data.repository.rgb_repository.RgbRepository.rgb_invoice')
-def test_get_rgb_invoice_success(mock_rgb_invoice, receive_rgb25_view_model):
+def test_get_rgb_invoice_success(mock_rgb_invoice, receive_cfa_view_model):
"""Test for successfully retrieving an RGB invoice."""
mock_address_signal = Mock()
- receive_rgb25_view_model.address.connect(mock_address_signal)
+ receive_cfa_view_model.address.connect(mock_address_signal)
mock_loading_signal = Mock()
- receive_rgb25_view_model.hide_loading.connect(mock_loading_signal)
+ receive_cfa_view_model.hide_loading.connect(mock_loading_signal)
mock_invoice_response = RgbInvoiceDataResponseModel(
recipient_id='recipient_id',
@@ -44,24 +44,24 @@ def test_get_rgb_invoice_success(mock_rgb_invoice, receive_rgb25_view_model):
mock_rgb_invoice.return_value = mock_invoice_response
# Call get_rgb_invoice with a minimum confirmation argument
- receive_rgb25_view_model.get_rgb_invoice(minimum_confirmations=1)
- receive_rgb25_view_model.worker.result.emit(mock_invoice_response)
+ receive_cfa_view_model.get_rgb_invoice(minimum_confirmations=1)
+ receive_cfa_view_model.worker.result.emit(mock_invoice_response)
mock_address_signal.assert_called_once_with(mock_invoice_response.invoice)
mock_loading_signal.assert_called_once_with(False)
@patch('src.views.components.toast.ToastManager.error')
-def test_on_error_shows_error_and_navigates(mock_toast, receive_rgb25_view_model):
+def test_on_error_shows_error_and_navigates(mock_toast, receive_cfa_view_model):
"""Test for handling errors in on_error method."""
mock_loading_signal = Mock()
- receive_rgb25_view_model.hide_loading.connect(mock_loading_signal)
+ receive_cfa_view_model.hide_loading.connect(mock_loading_signal)
# Create a mock error
mock_error = CommonException('Error occurred')
# Call the on_error method with the mock error
- receive_rgb25_view_model.on_error(mock_error)
+ receive_cfa_view_model.on_error(mock_error)
# Assert that the error toast was shown with the correct message
mock_toast.assert_called_once_with(
@@ -72,10 +72,10 @@ def test_on_error_shows_error_and_navigates(mock_toast, receive_rgb25_view_model
mock_loading_signal.assert_called_once_with(False)
# Assert that the navigation to fungibles asset page occurred
- receive_rgb25_view_model._page_navigation.fungibles_asset_page.assert_called_once()
+ receive_cfa_view_model._page_navigation.fungibles_asset_page.assert_called_once()
# Assert that the sidebar is checked
sidebar_mock = Mock()
- receive_rgb25_view_model._page_navigation.sidebar.return_value = sidebar_mock
- receive_rgb25_view_model.on_error(mock_error)
+ receive_cfa_view_model._page_navigation.sidebar.return_value = sidebar_mock
+ receive_cfa_view_model.on_error(mock_error)
sidebar_mock.my_fungibles.setChecked.assert_called_once_with(True)
diff --git a/unit_tests/tests/viewmodel_tests/view_unspent_view_model_test.py b/unit_tests/tests/viewmodel_tests/view_unspent_view_model_test.py
index fecd4196..58f4bf77 100644
--- a/unit_tests/tests/viewmodel_tests/view_unspent_view_model_test.py
+++ b/unit_tests/tests/viewmodel_tests/view_unspent_view_model_test.py
@@ -40,7 +40,10 @@ def mock_list_unspents_response():
'rgb_allocations': [
{
'asset_id': 'rgb:2dkSTbr-jFhznbPmo-TQafzswCN-av4gTsJjX-ttx6CNou5-M98k8Zd',
- 'amount': 42,
+ 'assignment': {
+ 'type': 'Fungible',
+ 'value': 42,
+ },
'settled': False,
},
],