diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 76b2f86..b4e8c93 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 0.0.6
+current_version = 0.1.0
commit = False
tag = False
allow_dirty = True
diff --git a/.github/workflows/iris-wallet-desktop.yml b/.github/workflows/iris-wallet-desktop.yml
index 17c42c4..5efae76 100644
--- a/.github/workflows/iris-wallet-desktop.yml
+++ b/.github/workflows/iris-wallet-desktop.yml
@@ -1,49 +1,71 @@
name: Iris Wallet Desktop CI
on:
- workflow_dispatch:
+ push:
+ tags:
+ - '*' # Trigger this workflow on any tag push
jobs:
- build-linux:
+ build-iris-wallet-desktop-linux:
runs-on: ubuntu-22.04
steps:
- - name: Checkout code with submodules
+ - name: Checkout repository with submodules
uses: actions/checkout@v3
with:
- submodules: true
- fetch-depth: 1
- submodule-fetch-depth: 1
+ submodules: true # Include submodules in the checkout
+ fetch-depth: 1 # Fetch the latest commit only
- - name: Install Rust
+ - name: Validate Tag and Code Version
+ run: |
+ TAG_VERSION=$(git describe --tags)
+ echo "TAG_VERSION=${TAG_VERSION}" >> $GITHUB_ENV
+
+ # Extract version from tag
+ echo "Tag version: $TAG_VERSION"
+
+ # Extract version from code
+ CODE_VERSION=$(grep '__version__' ./src/version.py | awk -F'=' '{print $2}' | tr -d ' "' | xargs)
+ echo "Code version: $CODE_VERSION"
+
+ # Compare the tag and code version
+ if [ "$TAG_VERSION" != "$CODE_VERSION" ]; then
+ echo "❌ Tag version ($TAG_VERSION) does not match code version ($CODE_VERSION)."
+ exit 1
+ else
+ echo "✅ Tag version matches code version."
+ fi
+
+ - name: Install Rust Programming Environment
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
source "$HOME/.cargo/env"
- - name: Set up Python 3.12.3
+ - name: Set up Python 3.12.3 environment
uses: actions/setup-python@v5
with:
python-version: "3.12.3"
- - name: Install dependencies
+ - name: Install required system dependencies
run: |
sudo apt update
- sudo apt install libxcb-cursor0 -y
- sudo apt-get install ruby-dev build-essential && sudo gem i fpm -f
+ sudo apt install libxcb-cursor0 -y # Required by the application
+ sudo apt-get install ruby-dev build-essential -y && sudo gem i fpm -f
+ sudo apt-get update && sudo apt-get install -y libfuse2 # Required for AppImage creation
- name: Clone rgb-lightning-node repository with submodules
run: git clone https://github.com/RGB-Tools/rgb-lightning-node --recurse-submodules --shallow-submodules
- name: Build the rgb-lightning-node binary
working-directory: rgb-lightning-node
- run: cargo install --debug --path .
+ run: cargo install --locked --debug --path .
- name: Copy rgb-lightning-node binary to root directory
run: |
mkdir ln_node_binary
cp rgb-lightning-node/target/debug/rgb-lightning-node ln_node_binary
- - name: Set environment variables from secrets and create config.py
+ - name: Set environment variables from GitHub Secrets and generate config.py
env:
CLIENT_ID: ${{ secrets.CLIENT_ID }}
PROJECT_ID: ${{ secrets.PROJECT_ID }}
@@ -55,45 +77,55 @@ jobs:
cd src/utils
python generate_config.py
- - name: Install python dependencies
+ - name: Install Python dependencies
run: |
- pip install poetry
- pip install pyinstaller
- poetry install
+ pip install poetry # Dependency management tool
+ pip install pyinstaller # Required for building executable files
+ poetry install # Install application dependencies
- name: Compile QT resources
run: |
poetry run pyside6-rcc src/resources.qrc -o src/resources_rc.py
- - name: Build the application
- run: |
- chmod +x build_linux.sh
- ./build_linux.sh
-
- - name: Create AppImage
+ - name: Create AppImage for Regtest network
run: |
- chmod +x build_appimage.sh
- ./build_appimage.sh
ARCH=$(uname -m)
- APPIMAGE_NAME="iriswallet-${ARCH}.AppImage"
- echo "APPIMAGE_NAME=${APPIMAGE_NAME}" >> $GITHUB_ENV
- echo $APPIMAGE_NAME
-
- - name: Upload Linux artifact
+ VERSION=$(grep '__version__' src/version.py | cut -d "'" -f 2)
+ poetry run build-iris-wallet --network=regtest --distribution=appimage
+ REGTEST_APPIMAGE_NAME="iriswallet-${VERSION}-${ARCH}.AppImage"
+ RENAME_REGTEST_APPIMAGE_NAME="iriswallet-${VERSION}-regtest-${ARCH}.AppImage"
+ mv ${REGTEST_APPIMAGE_NAME} ${RENAME_REGTEST_APPIMAGE_NAME}
+ echo "RENAME_REGTEST_APPIMAGE_NAME=${RENAME_REGTEST_APPIMAGE_NAME}" >> $GITHUB_ENV
+ chmod +x $RENAME_REGTEST_APPIMAGE_NAME
+ echo "Generated file: $RENAME_REGTEST_APPIMAGE_NAME"
+ shell: bash
+
+ - name: Upload Regtest AppImage artifact
uses: actions/upload-artifact@v4
with:
- name: linux
- path: |
- iriswallet.deb
+ name: linux_appimage_regtest
+ path: ${{ env.RENAME_REGTEST_APPIMAGE_NAME }}
- - name: Upload AppImage artifact
+ - name: Create AppImage for Testnet network
+ run: |
+ ARCH=$(uname -m)
+ VERSION=$(grep '__version__' src/version.py | cut -d "'" -f 2)
+ poetry run build-iris-wallet --network=testnet --distribution=appimage
+ TESTNET_APPIMAGE_NAME="iriswallet-${VERSION}-${ARCH}.AppImage"
+ RENAME_TESTNET_APPIMAGE_NAME="iriswallet-${VERSION}-testnet-${ARCH}.AppImage"
+ mv ${TESTNET_APPIMAGE_NAME} ${RENAME_TESTNET_APPIMAGE_NAME}
+ echo "RENAME_TESTNET_APPIMAGE_NAME=${RENAME_TESTNET_APPIMAGE_NAME}" >> $GITHUB_ENV
+ chmod +x $RENAME_TESTNET_APPIMAGE_NAME
+ echo "Generated file: $RENAME_TESTNET_APPIMAGE_NAME"
+ shell: bash
+
+ - name: Upload Testnet AppImage artifact
uses: actions/upload-artifact@v4
with:
- name: linux_appimage
- path: |
- ${{ env.APPIMAGE_NAME }}
+ name: linux_appimage_testnet
+ path: ${{ env.RENAME_TESTNET_APPIMAGE_NAME }}
- build-macos:
+ build-iris-wallet-desktop-macos:
runs-on: macos-latest
steps:
- name: Checkout code with submodules
@@ -101,7 +133,26 @@ jobs:
with:
submodules: true
fetch-depth: 1
- submodule-fetch-depth: 1
+
+ - name: Validate Tag and Code Version
+ run: |
+ TAG_VERSION=$(git describe --tags)
+ echo "TAG_VERSION=${TAG_VERSION}" >> $GITHUB_ENV
+
+ # Extract version from tag
+ echo "Tag version: $TAG_VERSION"
+
+ # Extract version from code
+ CODE_VERSION=$(grep '__version__' ./src/version.py | awk -F'=' '{print $2}' | tr -d ' "' | xargs)
+ echo "Code version: $CODE_VERSION"
+
+ # Compare the tag and code version
+ if [ "$TAG_VERSION" != "$CODE_VERSION" ]; then
+ echo "❌ Tag version ($TAG_VERSION) does not match code version ($CODE_VERSION)."
+ exit 1
+ else
+ echo "✅ Tag version matches code version."
+ fi
- name: Install Rust
run: |
@@ -119,7 +170,7 @@ jobs:
- name: Build the rgb-lightning-node binary
working-directory: rgb-lightning-node
- run: cargo install --debug --path .
+ run: cargo install --locked --debug --path .
- name: Copy rgb-lightning-node binary to root directory
run: |
@@ -150,195 +201,99 @@ jobs:
- name: Build the application
run: |
- chmod +x build_macos.sh
- ./build_macos.sh
+ # Build the regtest application for macOS
+ poetry run build-iris-wallet --network=regtest
+
+ # Build the testnet application for macOS
+ poetry run build-iris-wallet --network=testnet
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: macos
- path: iriswallet*
-
- build-windows:
- runs-on: windows-latest
- steps:
- - name: Checkout code with submodules
- uses: actions/checkout@v3
- with:
- submodules: true
- fetch-depth: 1
- submodule-fetch-depth: 1
-
- - name: Install Rust
- run: |
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
-
- - name: Set up Python 3.12
- uses: actions/setup-python@v5
- with:
- python-version: "3.12"
-
- - name: Clone rgb-lightning-node repository with submodules
- run: git clone https://github.com/RGB-Tools/rgb-lightning-node --recurse-submodules --shallow-submodules
-
- - name: Build the rgb-lightning-node binary
- working-directory: rgb-lightning-node
- run: cargo install --debug --path .
-
- - name: Copy rgb-lightning-node binary to root directory
- run: |
- mkdir ln_node_binary
- copy rgb-lightning-node\\target\\debug\\rgb-lightning-node.exe ln_node_binary
-
- - name: Generate config.py with secrets and inno Setup script with dynamic version
- env:
- CLIENT_ID: ${{ secrets.CLIENT_ID }}
- PROJECT_ID: ${{ secrets.PROJECT_ID }}
- AUTH_URI: ${{ secrets.AUTH_URI }}
- TOKEN_URI: ${{ secrets.TOKEN_URI }}
- AUTH_PROVIDER_CERT_URL: ${{ secrets.AUTH_PROVIDER_CERT_URL }}
- CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
- run: |
- cd src/utils
- python generate_config.py
- python generate_iss.py
-
- - name: Install python dependencies
- run: |
- pip install poetry
- pip install pyinstaller
- poetry install
+ path: iriswallet-*
- - name: Compile QT resources and build exe
- run: |
- poetry run pyside6-rcc src/resources.qrc -o src/resources_rc.py
- poetry run pyinstaller iris_wallet_desktop.spec
-
- - name: Build the application
- uses: Minionguyjpro/Inno-Setup-Action@v1.2.2
- with:
- path: updated_iriswallet.iss
-
- - name: Upload Windows artifact
- uses: actions/upload-artifact@v4
- with:
- name: windows
- path: iriswallet.exe
-
-
- upload-release:
- if: needs.build-linux.result == 'success' || needs.build-macos.result == 'success' || needs.build-windows.result == 'success'
- runs-on: ubuntu-latest
- needs: [build-linux, build-macos, build-windows]
+ release-artifacts:
+ if: needs.build-iris-wallet-desktop-macos.result == 'success' && needs.build-iris-wallet-desktop-linux.result == 'success'
+ runs-on: ubuntu-24.04
+ needs: [build-iris-wallet-desktop-macos, build-iris-wallet-desktop-linux]
permissions:
contents: write
steps:
- - uses: actions/checkout@v4
+ - name: Checkout repository
+ uses: actions/checkout@v4
- - name: Read version from VERSION.py
+ - name: Retrieve tag name
run: |
- VERSION=$(grep '__version__' src/version.py | cut -d "'" -f 2)
- echo "TAG_NAME=v${VERSION}" >> $GITHUB_ENV
- echo "RELEASE_NAME=Release v${VERSION}" >> $GITHUB_ENV
- ARCH=$(uname -m)
- APPIMAGE_NAME="iriswallet-${ARCH}.AppImage"
- echo "APPIMAGE_NAME=${APPIMAGE_NAME}" >> $GITHUB_ENV
- echo $APPIMAGE_NAME
- APPNAME_MAC="iriswallet ${VERSION}".dmg
- echo "APPNAME_MAC=${APPNAME_MAC}" >> $GITHUB_ENV
- echo $APPNAME_MAC
-
-
- - name: Create GitHub Release
- id: create_release
- uses: actions/create-release@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- tag_name: "${{ env.TAG_NAME }}"
- release_name: "${{ env.RELEASE_NAME }}"
- draft: false
- prerelease: false
+ TAG_NAME=$(git describe --tags)
+ echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_ENV
+ echo "Using tag: $TAG_NAME"
- - name: Create uploads folder
+ - name: Prepare uploads folder
run: mkdir -p ./uploads
- - name: Download Linux artifact
- uses: actions/download-artifact@v4
- with:
- name: linux # Name of the Linux artifact
- path: ./uploads # Destination path folder
-
- - name: Download Linux AppImage artifact
- uses: actions/download-artifact@v4
- with:
- name: linux_appimage # Name of the Linux artifact
- path: ./uploads # Destination path folder
- name: Download macOS artifact
uses: actions/download-artifact@v4
with:
name: macos # Name of the macOS artifact
path: ./uploads # Destination path folder
- - name: Download windows artifact
+ - name: Download Testnet AppImage artifact
uses: actions/download-artifact@v4
with:
- name: windows # Name of the windows artifact
- path: ./uploads # Destination path folder
+ name: linux_appimage_testnet
+ path: ./uploads
- - name: Upload Release Artifact Linux DEB
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Download Regtest AppImage artifact
+ uses: actions/download-artifact@v4
with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./uploads/iriswallet.deb
- asset_name: iris-wallet-desktop-linux-"${{ env.TAG_NAME }}".deb
- asset_content_type: application/vnd.debian.binary-package
+ name: linux_appimage_regtest
+ path: ./uploads
- - name: Upload Release Artifact Linux AppImage
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./uploads/${{ env.APPIMAGE_NAME }}
- asset_name: iris-wallet-desktop-"${{ env.TAG_NAME }}".AppImage
- asset_content_type: application/octet-stream
+ - name: Extract Version and Artifacts Info
+ run: |
+ VERSION=$(grep '__version__' src/version.py | cut -d "'" -f 2)
- - name: Upload Release Artifact windows
- uses: actions/upload-release-asset@v1
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./uploads/iriswallet.exe
- asset_name: iris-wallet-desktop-windows-"${{ env.TAG_NAME }}".exe
- asset_content_type: application/vnd.microsoft.portable-executable
+ echo "TESTNET_APPIMAGE_NAME=${TESTNET_APPIMAGE_NAME}" >> $GITHUB_ENV
+ echo "REGTEST_APPIMAGE_NAME=${REGTEST_APPIMAGE_NAME}" >> $GITHUB_ENV
+
+ cd uploads
+ REGTEST_APPNAME_MAC=$(ls iriswallet-${VERSION}-regtest-*.dmg 2>/dev/null || echo "")
+ TESTNET_APPNAME_MAC=$(ls iriswallet-${VERSION}-testnet-*.dmg 2>/dev/null || echo "")
+
+ TESTNET_APPIMAGE_NAME=$(ls iriswallet-${VERSION}-regtest-*.AppImage 2>/dev/null || echo "")
+ REGTEST_APPIMAGE_NAME=$(ls iriswallet-${VERSION}-testnet-*.AppImage 2>/dev/null || echo "")
+
+ echo "REGTEST_APPNAME_MAC=${REGTEST_APPNAME_MAC}" >> $GITHUB_ENV
+ echo "TESTNET_APPNAME_MAC=${TESTNET_APPNAME_MAC}" >> $GITHUB_ENV
+ echo "REGTEST_APPIMAGE_NAME=${REGTEST_APPIMAGE_NAME}" >> $GITHUB_ENV
+ echo "TESTNET_APPIMAGE_NAME=${TESTNET_APPIMAGE_NAME}" >> $GITHUB_ENV
- - name: Upload Release Artifact macOS
- uses: actions/upload-release-asset@v1
+ - name: Create GitHub Release and Upload Assets
+ uses: softprops/action-gh-release@v1
+ with:
+ tag_name: ${{ env.TAG_NAME }}
+ files: |
+ ./uploads/${{ env.REGTEST_APPNAME_MAC }}
+ ./uploads/${{ env.TESTNET_APPNAME_MAC }}
+ ./uploads/${{ env.TESTNET_APPIMAGE_NAME }}
+ ./uploads/${{ env.REGTEST_APPIMAGE_NAME }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: ${{ steps.create_release.outputs.upload_url }}
- asset_path: ./uploads/${{ env.APPNAME_MAC }}
- asset_name: iris-wallet-desktop-macos-"${{ env.TAG_NAME }}".dmg
- asset_content_type: application/octet-stream
- cleanup:
+ cleanup-artifacts:
if: always()
- runs-on: ubuntu-latest
- needs: [build-linux, build-macos, build-windows,upload-release]
+ runs-on: ubuntu-22.04
+ needs: [build-iris-wallet-desktop-macos, build-iris-wallet-desktop-linux, release-artifacts]
steps:
- - uses: actions/checkout@v4
+ - name: Checkout repository
+ uses: actions/checkout@v4
- - name: Cleanup Artifacts
+ - name: Delete Artifacts from Workflow Run
uses: geekyeggo/delete-artifact@v5
with:
name: |
- linux
- linux_appimage
macos
- windows
+ linux_appimage_regtest
+ linux_appimage_testnet
failOnError: false
diff --git a/README.md b/README.md
index 81844dd..7c3c859 100644
--- a/README.md
+++ b/README.md
@@ -91,14 +91,6 @@ client_config = {
'client_secret': 'your_client_secret',
},
}
-
-# Config for the error report email server
-report_email_server_config = {
- 'email_id': 'your_email_id',
- 'email_token': 'your_email_token',
- 'smtp_host': 'smtp.gmail.com',
- 'smtp_port': '587'
-}
```
#### 8.2 Create Google Drive Credentials
@@ -138,9 +130,6 @@ report_email_server_config = {
6. **Update Your Configuration:**
- Save the modified JSON file and add it to your `config.py` file.
-#### 8.3 Create Email Server Configuration
-Search for **App Passwords** in your Gmail account to create an app password for email access.
-
### 9. Start the Application
You can now start the Iris Wallet application using:
```bash
diff --git a/build_macos.sh b/build_macos.sh
index 58d06e9..b176d5d 100755
--- a/build_macos.sh
+++ b/build_macos.sh
@@ -3,6 +3,8 @@
# Getting the app name and version from version.py and constant.py.
APP_NAME=$(grep 'APP_NAME' ./src/utils/constant.py | awk -F'=' '{print $2}' | tr -d ' "' | xargs)
VERSION=$(grep '__version__' ./src/version.py | awk -F'=' '{print $2}' | tr -d ' "' | xargs)
+BITCOIN_NETWORK=$(grep '__network__' ./src/flavour.py | awk -F'=' '{print $2}' | tr -d ' "' | xargs)
+ARCH=$(uname -m)
# Check if APP_NAME and VERSION are non-empty
@@ -33,9 +35,23 @@ ls -l "${DIST_PATH}"
if [ -d "${APP_BUNDLE}" ]; then
echo "App bundle created successfully."
echo "Creating DMG file..."
+
+ # Create the DMG and capture the output directory
npx create-dmg --dmg-title="${APP_NAME}-${VERSION}" "${APP_BUNDLE}"
- echo "DMG file created successfully."
+ # Locate the newly created DMG file in the current directory
+ DMG_FILE=$(ls -1t *.dmg | head -n 1)
+
+ if [ -f "${DMG_FILE}" ]; then
+ echo "DMG file created: ${DMG_FILE}"
+
+ # Rename the DMG file
+ NEW_DMG_NAME="${APP_NAME}-${VERSION}-${BITCOIN_NETWORK}-${ARCH}.dmg"
+ mv "${DMG_FILE}" "${NEW_DMG_NAME}"
+ echo "DMG file renamed to: ${NEW_DMG_NAME}"
+ else
+ echo "Failed to locate the DMG file."
+ fi
echo "Removing app bundle..."
rm -rf "${DIST_PATH}"
diff --git a/pyproject.toml b/pyproject.toml
index 97f6708..0cea446 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "iris-wallet-desktop"
-version = "0.0.6"
+version = "0.1.0"
description = ""
readme = "README.md"
license = ""
diff --git a/src/translations/en_IN.qm b/src/translations/en_IN.qm
index 0ee3844..47bca71 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 d49474b..42f9bed 100644
--- a/src/translations/en_IN.ts
+++ b/src/translations/en_IN.ts
@@ -1510,5 +1510,29 @@ If you understand the above remarks and wish to proceed, press the button below
Set a minimum confirmation for RGB operation
+
+
+ GitHub Issue
+
+
+
+ Email us at:
+
+
+
+ Note: do not expect a reply.
+
+
+
+ Thank you for your support!
+
+
+
+ Open an issue on GitHub (preferred):
+
+
+
+ Please help us improve by sharing the logs. You can report the issue in one of the following ways:
+
diff --git a/src/utils/common_utils.py b/src/utils/common_utils.py
index 8a774b5..925ff2e 100644
--- a/src/utils/common_utils.py
+++ b/src/utils/common_utils.py
@@ -7,17 +7,10 @@
import base64
import binascii
import os
-import platform
import shutil
-import smtplib
import time
import zipfile
-from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
-from email import encoders
-from email.mime.base import MIMEBase
-from email.mime.multipart import MIMEMultipart
-from email.mime.text import MIMEText
from io import BytesIO
import pydenticon
@@ -42,7 +35,6 @@
from PySide6.QtWidgets import QPlainTextEdit
from PySide6.QtWidgets import QWidget
-from config import report_email_server_config
from src.data.repository.setting_repository import SettingRepository
from src.data.service.helpers.main_asset_page_helper import get_offline_asset_ticker
from src.model.enums.enums_model import AssetType
@@ -61,9 +53,7 @@
from src.utils.constant import SLOW_TRANSACTION_FEE_BLOCKS
from src.utils.custom_exception import CommonException
from src.utils.error_message import ERROR_SAVE_LOGS
-from src.utils.error_message import ERROR_SEND_REPORT_EMAIL
from src.utils.error_message import ERROR_SOMETHING_WENT_WRONG
-from src.utils.error_message import ERROR_TITLE
from src.utils.info_message import INFO_COPY_MESSAGE
from src.utils.info_message import INFO_LOG_SAVE_DESCRIPTION
from src.utils.ln_node_manage import LnNodeServerManager
@@ -472,108 +462,6 @@ def close_button_navigation(parent, back_page_navigation=None):
parent.view_model.page_navigation.settings_page()
-def generate_error_report_email(url, title):
- """Collect system info, format it, and generate the email body."""
- # Collect system information
- network = SettingRepository.get_wallet_network()
- system_info = {
- 'URL': url,
- 'OS': platform.system(),
- 'OS Version': platform.version(),
- 'Wallet Version': __version__,
- 'Wallet Network': network.value,
- 'Architecture': platform.machine(),
- 'Processor': platform.processor(),
- }
-
- # Format system information for the email report
- system_info_formatted = (
- f"System Information Report:\n"
- f"-------------------------\n"
- f"URL: {system_info['URL']}\n"
- f"Operating System: {system_info['OS']}\n"
- f"OS Version: {system_info['OS Version']}\n"
- f"Wallet Version: {system_info['Wallet Version']}\n"
- f"Wallet Network: {system_info['Wallet Network']}\n"
- f"Architecture: {system_info['Architecture']}\n"
- f"Processor: {system_info['Processor']}\n"
- )
-
- # Generate the email body
- email_body = (
- f"{title}\n"
- f"{'=' * len(title)}\n\n"
- f"{system_info_formatted}\n"
- f"Attached logs can be found in the provided ZIP file for further details."
- )
-
- return email_body
-
-
-def send_crash_report_async(email_to, subject, body, zip_file_path):
- """
- Asynchronously sends a crash report email with a ZIP attachment.
- - Initializes a thread pool to run the email sending task asynchronously.
- """
-
- def send_crash_report(email_to, subject, body, zip_file_path):
- """
- - Retrieves the email server configuration (email ID and token).
- - Creates a multipart email message with the subject, sender, and recipient.
- - Attaches the email body and the ZIP file (if it exists).
- - Connects to the SMTP server using TLS, authenticates with the email ID and token, and sends the email.
- - Handles any exceptions that occur during the email sending process by showing an error toast message.
- """
- email_id = report_email_server_config['email_id']
- email_token = report_email_server_config['email_token']
-
- # Create a multipart message
- msg = MIMEMultipart()
- msg['Subject'] = subject
- msg['From'] = email_id
- msg['To'] = email_to
-
- # Attach the body text
- msg.attach(MIMEText(body))
-
- # Attach the ZIP file
- if zip_file_path and os.path.exists(zip_file_path):
- with open(zip_file_path, 'rb') as attachment:
- part = MIMEBase('application', 'octet-stream')
- part.set_payload(attachment.read())
-
- # Encode the attachment
- encoders.encode_base64(part)
-
- # Add headers
- part.add_header(
- 'Content-Disposition',
- f'attachment; filename="{os.path.basename(zip_file_path)}"',
- )
-
- # Attach the part to the message
- msg.attach(part)
-
- try:
- # Connect to the SMTP server
- smtp_host = report_email_server_config.get(
- 'smtp_host', 'smtp.gmail.com',
- )
- smtp_port = report_email_server_config.get('smtp_port', 587)
- with smtplib.SMTP(smtp_host, smtp_port) as server:
- server.starttls()
- server.login(email_id, email_token)
- server.sendmail(email_id, [email_to], msg.as_string())
- except CommonException as e:
- ToastManager.error(
- parent=None, title=ERROR_TITLE,
- description=ERROR_SEND_REPORT_EMAIL.format(e),
- )
-
- executor = ThreadPoolExecutor(max_workers=1)
- executor.submit(send_crash_report, email_to, subject, body, zip_file_path)
-
-
def find_files_with_name(path, keyword):
"""This method finds a file using the provided name."""
found_files = []
diff --git a/src/utils/constant.py b/src/utils/constant.py
index 750f5cc..dabdc0a 100644
--- a/src/utils/constant.py
+++ b/src/utils/constant.py
@@ -66,10 +66,10 @@
BITCOIND_RPC_USER_REGTEST = 'user'
BITCOIND_RPC_PASSWORD_REGTEST = 'password'
-BITCOIND_RPC_HOST_REGTEST = 'localhost'
-BITCOIND_RPC_PORT_REGTEST = 18443
-INDEXER_URL_REGTEST = '127.0.0.1:50001'
-PROXY_ENDPOINT_REGTEST = 'rpc://127.0.0.1:3000/json-rpc'
+BITCOIND_RPC_HOST_REGTEST = 'regtest-bitcoind.rgbtools.org'
+BITCOIND_RPC_PORT_REGTEST = 80
+INDEXER_URL_REGTEST = 'electrum.rgbtools.org:50041'
+PROXY_ENDPOINT_REGTEST = 'rpcs://proxy.iriswallet.com/0.2/json-rpc'
LDK_DATA_NAME_REGTEST = 'dataldkregtest'
BITCOIND_RPC_USER_TESTNET = 'user'
@@ -118,3 +118,7 @@
# Syncing chain info label timer in milliseconds
SYNCING_CHAIN_LABEL_TIMER = 5000
+
+# Email and github issue url for error report
+CONTACT_EMAIL = 'iriswalletdesktop@gmail.com'
+GITHUB_ISSUE_LINK = 'https://github.com/RGB-Tools/iris-wallet-desktop/issues/new?template=Blank+issue'
diff --git a/src/utils/error_message.py b/src/utils/error_message.py
index 0e8a097..e301d92 100644
--- a/src/utils/error_message.py
+++ b/src/utils/error_message.py
@@ -46,7 +46,6 @@
ERROR_ENDPOINT_NOT_ALLOW_TO_CACHE_CHECK_CACHE_LIST = 'Provide endpoint {} not allow to cache please check cache list in endpoints.py file'
ERROR_CREATE_UTXO_FEE_RATE_ISSUE = 'Unexpected error'
ERROR_MESSAGE_TO_CHANGE_FEE_RATE = 'Please change default fee rate from setting page'
-ERROR_SEND_REPORT_EMAIL = 'Failed to send email: {}'
ERROR_OPERATION_CANCELLED = 'Operation cancelled'
ERROR_NEW_CONNECTION_ERROR = 'Failed to connect to the server.because of network connection.'
ERROR_MAX_RETRY_EXITS = 'Max retries exceeded. while checking internet connection.'
diff --git a/src/utils/generate_config.py b/src/utils/generate_config.py
index c3d06a5..91404fb 100644
--- a/src/utils/generate_config.py
+++ b/src/utils/generate_config.py
@@ -12,13 +12,33 @@
import os
+
+def get_env_var(key, default=None):
+ """
+ Returns the value of an environment variable, or a default value if the variable
+ is not set or is an empty string.
+
+ Args:
+ key (str): The name of the environment variable.
+ default (Any): The fallback value if the variable is not set or is empty.
+
+ Returns:
+ Any: The environment variable's value or the default.
+ """
+ value = os.getenv(key, default)
+ # If the value is an empty string, return the default
+ return value if value else default
+
+
# Read environment variables
-client_id = os.getenv('CLIENT_ID')
-project_id = os.getenv('PROJECT_ID')
-auth_uri = os.getenv('AUTH_URI')
-token_uri = os.getenv('TOKEN_URI')
-auth_provider_cert_url = os.getenv('AUTH_PROVIDER_CERT_URL')
-client_secret = os.getenv('CLIENT_SECRET')
+client_id = get_env_var('CLIENT_ID')
+project_id = get_env_var('PROJECT_ID')
+auth_uri = get_env_var('AUTH_URI', 'https://accounts.google.com/o/oauth2/auth')
+token_uri = get_env_var('TOKEN_URI', 'https://oauth2.googleapis.com/token')
+auth_provider_cert_url = get_env_var(
+ 'AUTH_PROVIDER_CERT_URL', 'https://www.googleapis.com/oauth2/v1/certs',
+)
+client_secret = get_env_var('CLIENT_SECRET')
# Create the content of config.py
config_content = f"""
diff --git a/src/utils/handle_exception.py b/src/utils/handle_exception.py
index bd0eaee..bc9413c 100644
--- a/src/utils/handle_exception.py
+++ b/src/utils/handle_exception.py
@@ -38,7 +38,7 @@ def handle_exceptions(exc):
)
if exc.response.status_code == 500:
- PageNavigationEventManager.get_instance().error_report_signal.emit(exc.response.url)
+ PageNavigationEventManager.get_instance().error_report_signal.emit()
raw_exc = exc.response.json()
raise CommonException(error_message, raw_exc) from exc
diff --git a/src/utils/page_navigation.py b/src/utils/page_navigation.py
index f4e401b..9a0e084 100644
--- a/src/utils/page_navigation.py
+++ b/src/utils/page_navigation.py
@@ -411,7 +411,7 @@ def sidebar(self):
"""This method return the sidebar objects."""
return self._ui.sidebar
- def error_report_dialog_box(self, url):
+ def error_report_dialog_box(self):
"""This method display the error report dialog box"""
- error_report_dialog = ErrorReportDialog(url=url)
+ error_report_dialog = ErrorReportDialog()
error_report_dialog.exec()
diff --git a/src/utils/page_navigation_events.py b/src/utils/page_navigation_events.py
index 8916b72..e63c292 100644
--- a/src/utils/page_navigation_events.py
+++ b/src/utils/page_navigation_events.py
@@ -1,3 +1,4 @@
+# pylint: disable = too-few-public-methods
"""Make page navigation global"""
from __future__ import annotations
@@ -45,7 +46,7 @@ class PageNavigationEventManager(QObject):
about_page_signal = Signal()
faucets_page_signal = Signal()
help_page_signal = Signal()
- error_report_signal = Signal(str)
+ error_report_signal = Signal()
def __init__(self):
super().__init__()
diff --git a/src/version.py b/src/version.py
index 7299b46..edadef9 100644
--- a/src/version.py
+++ b/src/version.py
@@ -6,4 +6,4 @@
"""
from __future__ import annotations
-__version__ = '0.0.6'
+__version__ = '0.1.0'
diff --git a/src/views/components/error_report_dialog_box.py b/src/views/components/error_report_dialog_box.py
index 2fb85e9..b6900d6 100644
--- a/src/views/components/error_report_dialog_box.py
+++ b/src/views/components/error_report_dialog_box.py
@@ -1,106 +1,235 @@
-"""
-This module defines the ErrorReportDialog class, which represents a message box
-for sending error reports with translations and error details.
-"""
+# pylint: disable=too-many-instance-attributes, too-many-statements
+"""This module contains the ErrorReportDialog class which contains the UI for the error report dialog box"""
from __future__ import annotations
-import shutil
-
from PySide6.QtCore import QCoreApplication
-from PySide6.QtWidgets import QMessageBox
+from PySide6.QtCore import QSize
+from PySide6.QtCore import Qt
+from PySide6.QtGui import QCursor
+from PySide6.QtGui import QIcon
+from PySide6.QtWidgets import QDialog
+from PySide6.QtWidgets import QDialogButtonBox
+from PySide6.QtWidgets import QFileDialog
+from PySide6.QtWidgets import QGridLayout
+from PySide6.QtWidgets import QHBoxLayout
+from PySide6.QtWidgets import QLabel
+from PySide6.QtWidgets import QPushButton
+from PySide6.QtWidgets import QSizePolicy
+from PySide6.QtWidgets import QSpacerItem
+from PySide6.QtWidgets import QVBoxLayout
-from config import report_email_server_config
-from src.utils.common_utils import generate_error_report_email
-from src.utils.common_utils import send_crash_report_async
+from src.utils.common_utils import copy_text
+from src.utils.common_utils import download_file
from src.utils.common_utils import zip_logger_folder
-from src.utils.error_message import ERROR_OPERATION_CANCELLED
-from src.utils.info_message import INFO_SENDING_ERROR_REPORT
+from src.utils.constant import CONTACT_EMAIL
+from src.utils.constant import GITHUB_ISSUE_LINK
+from src.utils.helpers import load_stylesheet
from src.utils.local_store import local_store
-from src.version import __version__
-from src.views.components.toast import ToastManager
-class ErrorReportDialog(QMessageBox):
- """This class represents the error report dialog in the application."""
+class ErrorReportDialog(QDialog):
+ """This class represents the UI elements of the error report dialog"""
- def __init__(self, url, parent=None):
- """Initialize the ErrorReportDialog message box with translated strings and error details."""
- super().__init__(parent)
- self.url = url
- self.setWindowTitle('Send Error Report')
- # Create the message box
- self.setIcon(QMessageBox.Critical)
- self.setWindowTitle(
- QCoreApplication.translate(
- 'iris_wallet_desktop', 'error_report', None,
+ def __init__(self):
+ super().__init__()
+
+ self.setObjectName('error_report_dialog_box')
+ self.setStyleSheet(
+ load_stylesheet(
+ 'views/qss/error_report_dialog.qss',
),
)
+ self.grid_layout = QGridLayout(self)
+ self.grid_layout.setObjectName('grid_layout')
+ self.grid_layout.setContentsMargins(-1, -1, 33, 25)
+ self.icon_label = QLabel(self)
+ self.icon_label.setObjectName('icon_label')
+ icon = QIcon.fromTheme('dialog-error')
+ self.icon_label.setPixmap(icon.pixmap(70, 70))
+
+ self.grid_layout.addWidget(
+ self.icon_label, 0, 0, 1, 1, Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignHCenter,
+ )
+
+ self.were_sorry_label = QLabel(self)
+ self.were_sorry_label.setObjectName('were_sorry_label')
+ self.were_sorry_label.setContentsMargins(0, 15, 2, 0)
+ self.grid_layout.addWidget(self.were_sorry_label, 0, 1, 1, 2)
+
+ self.help_us_label = QLabel(self)
+ self.help_us_label.setObjectName('help_us_label')
+ self.help_us_label.setWordWrap(True)
+
+ self.grid_layout.addWidget(self.help_us_label, 1, 1, 1, 2)
+
+ self.open_issue_label = QLabel(self)
+ self.open_issue_label.setObjectName('open_issue_label')
+
+ self.grid_layout.addWidget(self.open_issue_label, 2, 1, 1, 1)
+
+ self.github_issue_label = QLabel(self)
+ self.github_issue_label.setObjectName('github_issue_label')
+
+ self.github_issue_label.setOpenExternalLinks(True)
+
+ self.grid_layout.addWidget(self.github_issue_label, 2, 2, 1, 1)
- # Fetch translations
- self.text_sorry = QCoreApplication.translate(
- 'iris_wallet_desktop', 'something_went_wrong_mb', None,
+ self.vertical_layout = QVBoxLayout()
+ self.vertical_layout.setObjectName('vertical_layout')
+ self.vertical_layout.setSpacing(0)
+ self.horizontal_layout = QHBoxLayout()
+ self.horizontal_layout.setObjectName('horizontal_layout')
+ self.horizontal_layout.setContentsMargins(-1, -1, 10, -1)
+ self.email_us_label = QLabel(self)
+ self.email_us_label.setObjectName('email_us_label')
+ self.email_us_label.setMaximumSize(QSize(100, 16777215))
+
+ self.horizontal_layout.addWidget(self.email_us_label)
+
+ self.email_label = QLabel(self)
+ self.email_label.setObjectName('email_label')
+ self.email_label.setText(CONTACT_EMAIL)
+
+ self.horizontal_layout.addWidget(
+ self.email_label, Qt.AlignmentFlag.AlignHCenter,
)
- self.text_help = QCoreApplication.translate(
- 'iris_wallet_desktop', 'error_description_mb', None,
+
+ self.copy_button = QPushButton(self)
+ self.copy_button.setObjectName('copy_button')
+ self.copy_button.setMaximumSize(QSize(16, 16))
+ self.copy_button.setStyleSheet('background:none')
+ icon = QIcon()
+ icon.addFile(
+ ':/assets/copy.png', QSize(),
+ QIcon.Mode.Normal, QIcon.State.Off,
)
- self.text_included = QCoreApplication.translate(
- 'iris_wallet_desktop', 'what_will_be_included', None,
+ self.copy_button.setIcon(icon)
+ self.copy_button.setFlat(True)
+ self.copy_button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
+
+ self.horizontal_layout.addWidget(
+ self.copy_button, Qt.AlignmentFlag.AlignLeft,
)
- self.text_error_details = QCoreApplication.translate(
- 'iris_wallet_desktop', 'error_details_title', None,
+
+ self.horizontal_spacer = QSpacerItem(
+ 20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed,
)
- self.text_app_version = QCoreApplication.translate(
- 'iris_wallet_desktop', 'application_version', None,
+
+ self.horizontal_layout.addItem(self.horizontal_spacer)
+
+ self.vertical_layout.addLayout(self.horizontal_layout)
+
+ self.no_reply_label = QLabel(self)
+ self.no_reply_label.setObjectName('no_reply_label')
+ self.vertical_layout.addWidget(
+ self.no_reply_label, Qt.AlignmentFlag.AlignTop,
)
- self.text_os_info = QCoreApplication.translate(
- 'iris_wallet_desktop', 'os_info', None,
+
+ self.grid_layout.addLayout(self.vertical_layout, 3, 1, 1, 2)
+
+ self.vertical_spacer = QSpacerItem(
+ 20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum,
)
- self.text_send_report = QCoreApplication.translate(
- 'iris_wallet_desktop', 'error_report_permission', None,
+
+ self.grid_layout.addItem(self.vertical_spacer, 4, 1)
+
+ self.thank_you_label = QLabel(self)
+ self.thank_you_label.setObjectName('thank_you_label')
+
+ self.grid_layout.addWidget(self.thank_you_label, 5, 1, 1, 2)
+
+ self.vertical_spacer_2 = QSpacerItem(
+ 20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum,
)
- # Set the text for the message box
- self.setText(self.text_sorry)
- self.setInformativeText(
- f"{self.text_help}\n\n"
- f"{self.text_included}\n"
- f"{self.text_error_details}\n"
- f"{self.text_app_version}\n"
- f"{self.text_os_info}\n\n"
- f"{self.text_send_report}",
+ self.grid_layout.addItem(self.vertical_spacer_2, 6, 1)
+ self.button_box = QDialogButtonBox(self)
+ self.button_box.setObjectName('buttonBox')
+ self.button_box.setOrientation(Qt.Orientation.Horizontal)
+
+ self.download_debug_logs = QPushButton(self)
+ self.download_debug_logs.setObjectName('download_debug_logs')
+ self.download_debug_logs.setMinimumSize(QSize(120, 40))
+ self.download_debug_logs.setCursor(
+ QCursor(Qt.CursorShape.PointingHandCursor),
+ )
+ self.button_box.addButton(
+ self.download_debug_logs, QDialogButtonBox.ActionRole,
)
- self.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
- self.setDefaultButton(QMessageBox.Yes)
- # Connect the buttonClicked signal to a custom slot
- self.buttonClicked.connect(self.on_button_clicked)
+ self.grid_layout.addWidget(self.button_box, 7, 1, 1, 2)
- def on_button_clicked(self, button):
- """
- Handles the button click event to either send an error report or cancel the operation.
+ self.retranslate_ui()
+ self.setup_ui()
+
+ def retranslate_ui(self):
+ """translations for the error report dialog"""
+ self.setWindowTitle(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'error_report', None,
+ ),
+ )
+ self.were_sorry_label.setText(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'something_went_wrong_mb', None,
+ ),
+ )
+ self.help_us_label.setText(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'please_help_us', None,
+ ),
+ )
+ self.open_issue_label.setText(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'open_an_issue', None,
+ ),
+ )
+ self.github_issue_label.setText(
+ f"""
+ {
+ QCoreApplication.translate(
+ "iris_wallet_desktop", "github_issue_label", None
+ )
+ }
+ """,
+ )
+ self.email_us_label.setText(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'email_us_at', None,
+ ),
+ )
+ self.no_reply_label.setText(f"({
+ QCoreApplication.translate(
+ "iris_wallet_desktop", "do_not_expect_reply", None
+ )
+ })")
+ self.thank_you_label.setText(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'thank_you_for_your_support', None,
+ ),
+ )
+ self.download_debug_logs.setText(
+ QCoreApplication.translate(
+ 'iris_wallet_desktop', 'download_debug_log', None,
+ ),
+ )
- If the 'Yes' button is clicked:
- - Shows an info toast notification indicating the report is being sent.
- - Compresses the log files into a ZIP archive.
- - Prepares an error report email with the appropriate subject and body.
- - Sends the error report asynchronously to the specified email ID.
+ def setup_ui(self):
+ """Ui connections for error report dialog"""
+ self.copy_button.clicked.connect(lambda: copy_text(self.email_label))
+ self.download_debug_logs.clicked.connect(
+ self.on_click_download_debug_log,
+ )
- If the 'No' button is clicked:
- - Shows a warning toast notification indicating the operation was cancelled.
- """
- if button == self.button(QMessageBox.Yes):
- ToastManager.info(INFO_SENDING_ERROR_REPORT)
+ def on_click_download_debug_log(self):
+ """This method opens the file dialog box and saves the debug logs to the selected path"""
- base_path = local_store.get_path()
- _, output_dir = zip_logger_folder(base_path)
- zip_file_path = shutil.make_archive(output_dir, 'zip', output_dir)
+ path = local_store.get_path()
+ zip_filename, output_dir = zip_logger_folder(path)
- # Set the subject and formatted body
- subject = f"Iris Wallet Error Report - Version {__version__}"
- title = 'Error Report for Iris Wallet Desktop'
- body = generate_error_report_email(url=self.url, title=title)
- email_id = report_email_server_config['email_id']
+ save_path, _ = QFileDialog.getSaveFileName(
+ self, 'Save logs File', zip_filename, 'Zip Files (*.zip)',
+ )
- send_crash_report_async(email_id, subject, body, zip_file_path)
- elif button == self.button(QMessageBox.No):
- ToastManager.warning(ERROR_OPERATION_CANCELLED)
+ if save_path:
+ download_file(save_path, output_dir)
diff --git a/src/views/components/transaction_detail_frame.py b/src/views/components/transaction_detail_frame.py
index a923b0e..1b2e3e3 100644
--- a/src/views/components/transaction_detail_frame.py
+++ b/src/views/components/transaction_detail_frame.py
@@ -93,6 +93,7 @@ def set_frame(self):
)
self.transaction_amount = QLabel(self)
+ self.transaction_amount.setFixedHeight(18)
self.transaction_amount.setObjectName('label_15')
self.transaction_amount.setStyleSheet(
'font: 15px "Inter";\n'
diff --git a/src/views/qss/error_report_dialog.qss b/src/views/qss/error_report_dialog.qss
new file mode 100644
index 0000000..f08b1e6
--- /dev/null
+++ b/src/views/qss/error_report_dialog.qss
@@ -0,0 +1,23 @@
+/* Styles for the logo title text */
+QLabel {
+ font: 16px "Inter";
+ color: white;
+}
+
+QLabel#email_label{
+ padding-left:5px;
+}
+
+QLabel#icon_label{
+ padding-top: 20px;
+ padding-left: 10px;
+ padding-right: 20px;
+}
+
+QPushButton#download_debug_logs{
+ font:16px "Inter";
+}
+
+QLabel#no_reply_label{
+ font:italic;
+}
diff --git a/unit_tests/tests/ui_tests/components/error_report_dialog_box_test.py b/unit_tests/tests/ui_tests/components/error_report_dialog_box_test.py
index 9e7a5f7..f21d772 100644
--- a/unit_tests/tests/ui_tests/components/error_report_dialog_box_test.py
+++ b/unit_tests/tests/ui_tests/components/error_report_dialog_box_test.py
@@ -8,20 +8,21 @@
import pytest
from PySide6.QtCore import QCoreApplication
-from PySide6.QtWidgets import QMessageBox
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QLabel
+from PySide6.QtWidgets import QPushButton
-from src.utils.error_message import ERROR_OPERATION_CANCELLED
-from src.utils.info_message import INFO_SENDING_ERROR_REPORT
from src.utils.local_store import local_store
from src.version import __version__
from src.views.components.error_report_dialog_box import ErrorReportDialog
+from src.views.components.toast import ToastManager
@pytest.fixture
def error_report_dialog(qtbot):
"""Fixture to create and return an instance of ErrorReportDialog."""
- url = 'http://example.com/error_report'
- dialog = ErrorReportDialog(url)
+ dialog = ErrorReportDialog()
+ qtbot.addWidget(dialog)
return dialog
@@ -29,99 +30,78 @@ def test_dialog_initialization(error_report_dialog):
"""Test if the ErrorReportDialog initializes with correct properties."""
dialog = error_report_dialog
+ # Test window title
expected_title = QCoreApplication.translate(
'iris_wallet_desktop', 'error_report', None,
)
assert dialog.windowTitle() == expected_title
- # Test the text in the dialog
- assert 'something_went_wrong_mb' in dialog.text_sorry
- assert 'error_description_mb' in dialog.text_help
- assert 'what_will_be_included' in dialog.text_included
- # Verify the dialog has 'Yes' and 'No' buttons
- buttons = dialog.buttons()
- assert len(buttons) == 2
- assert buttons[0].text().replace('&', '') == 'Yes'
- assert buttons[1].text().replace('&', '') == 'No'
+ # Test if main components exist
+ assert isinstance(dialog.were_sorry_label, QLabel)
+ assert isinstance(dialog.help_us_label, QLabel)
+ assert isinstance(dialog.download_debug_logs, QPushButton)
+ assert isinstance(dialog.copy_button, QPushButton)
+ # Test labels content
+ assert QCoreApplication.translate(
+ 'iris_wallet_desktop', 'something_went_wrong_mb', None,
+ ) == dialog.were_sorry_label.text()
-def test_send_report_on_yes_button(error_report_dialog):
- """Test behavior when the 'Yes' button is clicked."""
+ # Test button properties
+ assert dialog.download_debug_logs.minimumSize().width() == 120
+ assert dialog.download_debug_logs.minimumSize().height() == 40
+
+
+def test_download_debug_logs(error_report_dialog, qtbot):
+ """Test the download debug logs functionality."""
dialog = error_report_dialog
- # Mock external functions to prevent actual side effects during testing
- with patch('src.views.components.error_report_dialog_box.ToastManager.info') as mock_info, \
+ with patch('src.views.components.error_report_dialog_box.QFileDialog.getSaveFileName') as mock_file_dialog, \
patch('src.views.components.error_report_dialog_box.zip_logger_folder') as mock_zip, \
- patch('src.views.components.error_report_dialog_box.shutil.make_archive') as mock_archive, \
- patch('src.views.components.error_report_dialog_box.generate_error_report_email') as mock_generate_email, \
- patch('src.views.components.error_report_dialog_box.send_crash_report_async') as mock_send_email, \
- patch('src.views.components.error_report_dialog_box.report_email_server_config', {'email_id': 'dummy_email_id'}): # Mock email config dictionary
+ patch('src.views.components.error_report_dialog_box.download_file') as mock_download:
- # Mock return values for external functions
- mock_zip.return_value = ('dummy_dir', 'output_dir')
- mock_archive.return_value = 'dummy_path.zip'
- mock_generate_email.return_value = 'dummy email body'
+ # Mock return values
+ mock_zip.return_value = ('test.zip', 'output_dir')
+ mock_file_dialog.return_value = ('save_path.zip', 'selected_filter')
- # Simulate a button click on "Yes"
- dialog.buttonClicked.emit(dialog.button(QMessageBox.Yes))
+ # Click download button
+ qtbot.mouseClick(dialog.download_debug_logs, Qt.LeftButton)
- # Verify that the correct toast message is shown
- mock_info.assert_called_once_with(INFO_SENDING_ERROR_REPORT)
-
- # Verify that the zip logger folder was called
+ # Verify zip_logger_folder was called with correct path
mock_zip.assert_called_once_with(local_store.get_path())
- # Verify that make_archive was called to create the ZIP file
- # Corrected the argument order to match the actual function call
- mock_archive.assert_called_once_with('output_dir', 'zip', 'output_dir')
-
- # Verify the email generation
- mock_generate_email.assert_called_once_with(
- url='http://example.com/error_report', title='Error Report for Iris Wallet Desktop',
- )
+ # Verify download_file was called with correct parameters
+ mock_download.assert_called_once_with('save_path.zip', 'output_dir')
- # Verify that the email sending function was called with correct parameters
- mock_send_email.assert_called_once_with(
- 'dummy_email_id', # The mocked email ID
- f"Iris Wallet Error Report - Version {__version__}",
- 'dummy email body',
- 'dummy_path.zip',
- )
-
-def test_cancel_report_on_no_button(error_report_dialog):
- """Test behavior when the 'No' button is clicked."""
+def test_download_debug_logs_cancelled(error_report_dialog, qtbot):
+ """Test when debug logs download is cancelled."""
dialog = error_report_dialog
- # Mock ToastManager to avoid actual toast notifications
- with patch('src.views.components.error_report_dialog_box.ToastManager.warning') as mock_warning:
- # Simulate a button click on "No"
- dialog.buttonClicked.emit(dialog.button(QMessageBox.No))
+ with patch('src.views.components.error_report_dialog_box.QFileDialog.getSaveFileName') as mock_file_dialog, \
+ patch('src.views.components.toast.ToastManager.show_toast') as mock_toast:
- # Verify the correct warning toast message is shown
- mock_warning.assert_called_once_with(ERROR_OPERATION_CANCELLED)
+ # Mock cancelled file dialog
+ mock_file_dialog.return_value = ('', '')
+ # Click download button
+ qtbot.mouseClick(dialog.download_debug_logs, Qt.LeftButton)
-def test_dialog_buttons_functionality(error_report_dialog):
- """Test if the 'Yes' and 'No' buttons work correctly."""
- dialog = error_report_dialog
+ # Verify toast was shown with correct message
+ mock_toast.assert_not_called()
- # Mock ToastManager methods to prevent actual toasts from being shown
- with patch('src.views.components.error_report_dialog_box.ToastManager.info') as mock_info, \
- patch('src.views.components.error_report_dialog_box.ToastManager.warning') as mock_warning:
- # Check 'Yes' button functionality
- yes_button = dialog.button(QMessageBox.Yes)
- assert yes_button.text().replace('&', '') == 'Yes'
- dialog.buttonClicked.emit(yes_button)
+def test_copy_button(error_report_dialog, qtbot):
+ """Test if the copy button copies email to clipboard."""
+ dialog = error_report_dialog
- # Verify that the info toast was shown
- mock_info.assert_called_once_with(INFO_SENDING_ERROR_REPORT)
+ with patch('src.views.components.error_report_dialog_box.copy_text') as mock_copy_text, \
+ patch.object(ToastManager, 'success', return_value=None):
+ # Click copy button
+ qtbot.mouseClick(dialog.copy_button, Qt.LeftButton)
- # Check 'No' button functionality
- no_button = dialog.button(QMessageBox.No)
- assert no_button.text().replace('&', '') == 'No'
- dialog.buttonClicked.emit(no_button)
+ # Process events to allow click to propagate
+ qtbot.wait(100)
- # Verify that the warning toast was shown
- mock_warning.assert_called_once_with(ERROR_OPERATION_CANCELLED)
+ # Verify copy_text was called with correct label
+ mock_copy_text.assert_called_once_with(dialog.email_label)
diff --git a/unit_tests/tests/utils_test/common_utils_test.py b/unit_tests/tests/utils_test/common_utils_test.py
index d8ec881..e61194e 100644
--- a/unit_tests/tests/utils_test/common_utils_test.py
+++ b/unit_tests/tests/utils_test/common_utils_test.py
@@ -1,6 +1,6 @@
# 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-lines, no-value-for-parameter
+# pylint: disable=redefined-outer-name,unused-argument, too-many-lines, no-value-for-parameter, use-implicit-booleaness-not-comparison
"""unit tests for common utils"""
from __future__ import annotations
@@ -26,14 +26,12 @@
from src.model.enums.enums_model import NetworkEnumModel
from src.model.enums.enums_model import TokenSymbol
from src.model.selection_page_model import SelectionPageModel
-from src.utils.constant import DEFAULT_LOCALE
from src.utils.common_utils import close_button_navigation
from src.utils.common_utils import convert_hex_to_image
from src.utils.common_utils import convert_timestamp
from src.utils.common_utils import copy_text
from src.utils.common_utils import download_file
from src.utils.common_utils import find_files_with_name
-from src.utils.common_utils import generate_error_report_email
from src.utils.common_utils import generate_identicon
from src.utils.common_utils import get_bitcoin_info_by_network
from src.utils.common_utils import load_translator
@@ -44,6 +42,7 @@
from src.utils.common_utils import translate_value
from src.utils.common_utils import zip_logger_folder
from src.utils.constant import APP_NAME
+from src.utils.constant import DEFAULT_LOCALE
from src.utils.constant import LOG_FOLDER_NAME
from src.utils.custom_exception import CommonException
from src.version import __version__
@@ -259,10 +258,6 @@ def test_convert_hex_to_image_valid_data():
pixmap = convert_hex_to_image(valid_hex)
assert isinstance(pixmap, QPixmap)
-# import time
-# from unittest.mock import patch
-# import os
-
def test_zip_logger_folder():
"""
@@ -943,52 +938,6 @@ def test_resize_image_file_not_found(mock_exists):
) == 'The file /mock/path/nonexistent.png does not exist.'
-def test_generate_error_report_email(mocker):
- """Test generate_error_report_email function"""
- # Mock all dependencies
- mocker.patch(
- 'src.data.repository.setting_repository.SettingRepository.get_wallet_network',
- return_value=mocker.MagicMock(value='Mainnet'),
- )
- mocker.patch('platform.system', return_value='Linux')
- mocker.patch('platform.version', return_value='5.4.0-104-generic')
- mocker.patch('platform.machine', return_value='x86_64')
- mocker.patch(
- 'platform.processor',
- return_value='Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz',
- )
- mocker.patch('src.version.__version__', '0.0.6')
-
- # Mock inputs
- url = 'http://example.com/error'
- title = 'Error Report'
-
- # Call the function
- result = generate_error_report_email(url, title)
-
- # Assert the result matches the expected format
- expected_system_info = (
- f"System Information Report:\n"
- f"-------------------------\n"
- f"URL: {url}\n"
- f"Operating System: Linux\n"
- f"OS Version: 5.4.0-104-generic\n"
- f"Wallet Version: 0.0.6\n"
- f"Wallet Network: Mainnet\n"
- f"Architecture: x86_64\n"
- f"Processor: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz\n"
- )
-
- expected_email_body = (
- f"{title}\n"
- f"{'=' * len(title)}\n\n"
- f"{expected_system_info}\n"
- f"Attached logs can be found in the provided ZIP file for further details."
- )
-
- assert result == expected_email_body
-
-
@patch('PySide6.QtWidgets.QMessageBox.warning')
@patch('src.utils.ln_node_manage.LnNodeServerManager.get_instance')
@patch('PySide6.QtWidgets.QApplication.instance')
diff --git a/unit_tests/tests/utils_test/handle_exception_test.py b/unit_tests/tests/utils_test/handle_exception_test.py
index 1d290ae..0a86238 100644
--- a/unit_tests/tests/utils_test/handle_exception_test.py
+++ b/unit_tests/tests/utils_test/handle_exception_test.py
@@ -49,13 +49,13 @@ def test_http_error_500_with_error_report():
with patch('src.utils.handle_exception.PageNavigationEventManager') as mock_manager:
mock_instance = MagicMock()
mock_manager.get_instance.return_value = mock_instance
+ mock_instance.error_report_signal = MagicMock()
with pytest.raises(CommonException) as exc_info:
handle_exceptions(exc)
- mock_instance.error_report_signal.emit.assert_called_once_with(
- 'http://test.url',
- )
+ # Verify error_report_signal.emit() was called with no arguments
+ mock_instance.error_report_signal.emit.assert_called_once_with()
assert str(exc_info.value) == 'Server error'
diff --git a/unit_tests/tests/utils_test/page_navigation_test.py b/unit_tests/tests/utils_test/page_navigation_test.py
index 3799a8b..4389902 100644
--- a/unit_tests/tests/utils_test/page_navigation_test.py
+++ b/unit_tests/tests/utils_test/page_navigation_test.py
@@ -301,16 +301,15 @@ def test_sidebar(page_navigation, mock_ui):
def test_error_report_dialog_box(page_navigation):
"""Test error_report_dialog_box method."""
- url = 'test_url'
with patch('src.utils.page_navigation.ErrorReportDialog') as mock_dialog:
mock_dialog_instance = MagicMock()
mock_dialog.return_value = mock_dialog_instance
# Call the method we're testing
- page_navigation.error_report_dialog_box(url)
+ page_navigation.error_report_dialog_box()
- # Verify the dialog was created with correct URL
- mock_dialog.assert_called_once_with(url=url)
+ # Verify the dialog was created
+ mock_dialog.assert_called_once()
# Verify the dialog was shown
mock_dialog_instance.exec.assert_called_once()