Popup Fenster nach Vorne
Dieser Commit ist enthalten in:
@ -15,7 +15,10 @@
|
|||||||
"Bash(claude mcp)",
|
"Bash(claude mcp)",
|
||||||
"Bash(claude mcp:*)",
|
"Bash(claude mcp:*)",
|
||||||
"Bash(sqlite3:*)",
|
"Bash(sqlite3:*)",
|
||||||
"Bash(python -m pip install:*)"
|
"Bash(python -m pip install:*)",
|
||||||
|
"Bash(python -m pytest teststest_generator_tab_factory.py -v)",
|
||||||
|
"Bash(python tests:*)",
|
||||||
|
"Bash(python tests/test_generator_tab_factory.py)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"defaultMode": "acceptEdits",
|
"defaultMode": "acceptEdits",
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
- **Path**: `A:\GiTea\AccountForger`
|
- **Path**: `A:\GiTea\AccountForger`
|
||||||
- **Files**: 990 files
|
- **Files**: 1024 files
|
||||||
- **Size**: 182.0 MB
|
- **Size**: 184.1 MB
|
||||||
- **Last Modified**: 2025-09-14 18:54
|
- **Last Modified**: 2025-10-03 01:25
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
@ -296,6 +296,7 @@ styles/
|
|||||||
│ ├── modal_styles.py
|
│ ├── modal_styles.py
|
||||||
│ └── __init__.py
|
│ └── __init__.py
|
||||||
tests/
|
tests/
|
||||||
|
│ ├── test_generator_tab_factory.py
|
||||||
│ ├── test_method_rotation.py
|
│ ├── test_method_rotation.py
|
||||||
│ ├── bright_data/
|
│ ├── bright_data/
|
||||||
│ │ └── logs/
|
│ │ └── logs/
|
||||||
@ -383,6 +384,7 @@ views/
|
|||||||
│ ├── accounts_tab.py
|
│ ├── accounts_tab.py
|
||||||
│ ├── facebook_generator_tab.py
|
│ ├── facebook_generator_tab.py
|
||||||
│ ├── generator_tab.py
|
│ ├── generator_tab.py
|
||||||
|
│ ├── generator_tab_factory.py
|
||||||
│ └── settings_tab.py
|
│ └── settings_tab.py
|
||||||
└── widgets/
|
└── widgets/
|
||||||
├── account_card.py
|
├── account_card.py
|
||||||
@ -423,3 +425,7 @@ This project is managed with Claude Project Manager. To work with this project:
|
|||||||
- README updated on 2025-09-13 20:45:18
|
- README updated on 2025-09-13 20:45:18
|
||||||
- README updated on 2025-09-14 11:33:43
|
- README updated on 2025-09-14 11:33:43
|
||||||
- README updated on 2025-09-14 18:54:48
|
- README updated on 2025-09-14 18:54:48
|
||||||
|
- README updated on 2025-09-17 19:46:22
|
||||||
|
- README updated on 2025-09-21 10:31:22
|
||||||
|
- README updated on 2025-09-24 19:25:49
|
||||||
|
- README updated on 2025-10-03 20:29:19
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from PyQt5.QtCore import QObject
|
|||||||
from typing import Dict, Any, Optional, Tuple
|
from typing import Dict, Any, Optional, Tuple
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from views.tabs.generator_tab import GeneratorTab
|
from views.tabs.generator_tab_factory import GeneratorTabFactory
|
||||||
from views.tabs.accounts_tab import AccountsTab
|
from views.tabs.accounts_tab import AccountsTab
|
||||||
# SettingsTab import entfernt - wird nicht mehr verwendet
|
# SettingsTab import entfernt - wird nicht mehr verwendet
|
||||||
|
|
||||||
@ -86,10 +86,13 @@ class BasePlatformController(QObject):
|
|||||||
|
|
||||||
def create_generator_tab(self):
|
def create_generator_tab(self):
|
||||||
"""
|
"""
|
||||||
Erstellt den Generator-Tab.
|
Erstellt den Generator-Tab mit der Factory.
|
||||||
Diese Methode sollte von Unterklassen überschrieben werden.
|
Unterklassen können diese Methode überschreiben für spezielle Anforderungen.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QWidget: Plattform-spezifischer oder generischer Generator-Tab
|
||||||
"""
|
"""
|
||||||
return GeneratorTab(self.platform_name, self.language_manager)
|
return GeneratorTabFactory.create_tab(self.platform_name, self.language_manager)
|
||||||
|
|
||||||
def create_accounts_tab(self):
|
def create_accounts_tab(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from typing import Dict, Any
|
|||||||
|
|
||||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||||
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
||||||
from views.tabs.generator_tab import GeneratorTab # Verwende generischen GeneratorTab
|
from views.tabs.generator_tab_factory import GeneratorTabFactory # Factory Pattern für Tab-Erstellung
|
||||||
from views.widgets.forge_animation_widget import ForgeAnimationDialog # Forge Dialog für Animation
|
from views.widgets.forge_animation_widget import ForgeAnimationDialog # Forge Dialog für Animation
|
||||||
|
|
||||||
from social_networks.facebook.facebook_automation import FacebookAutomation
|
from social_networks.facebook.facebook_automation import FacebookAutomation
|
||||||
@ -127,38 +127,39 @@ class FacebookController(BasePlatformController):
|
|||||||
def get_generator_tab(self):
|
def get_generator_tab(self):
|
||||||
"""
|
"""
|
||||||
Erstellt und konfiguriert den Generator-Tab für Facebook.
|
Erstellt und konfiguriert den Generator-Tab für Facebook.
|
||||||
|
Verwendet die Factory für saubere Tab-Erstellung.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
GeneratorTab: Konfigurierter Tab für Facebook mit Geschlechtsauswahl
|
QWidget: Facebook-spezifischer Tab mit Geschlechtsauswahl
|
||||||
"""
|
"""
|
||||||
# Erstelle generischen Generator-Tab
|
# Verwende Factory Pattern für Tab-Erstellung
|
||||||
generator_tab = GeneratorTab(
|
# Die Factory entscheidet, welche Tab-Implementierung verwendet wird
|
||||||
|
generator_tab = GeneratorTabFactory.create_tab(
|
||||||
"Facebook",
|
"Facebook",
|
||||||
self.language_manager
|
self.language_manager
|
||||||
)
|
)
|
||||||
|
|
||||||
# Facebook-spezifische Konfiguration
|
# Verbinde Signale unabhängig von der Tab-Implementierung
|
||||||
self._configure_facebook_fields(generator_tab)
|
# Alle Tabs müssen diese Signale unterstützen (Interface-Kontrakt)
|
||||||
|
if hasattr(generator_tab, 'start_requested'):
|
||||||
# Verbinde Signale
|
|
||||||
generator_tab.start_requested.connect(self.handle_generation_request)
|
generator_tab.start_requested.connect(self.handle_generation_request)
|
||||||
|
if hasattr(generator_tab, 'stop_requested'):
|
||||||
generator_tab.stop_requested.connect(self.stop_generation)
|
generator_tab.stop_requested.connect(self.stop_generation)
|
||||||
|
|
||||||
return generator_tab
|
return generator_tab
|
||||||
|
|
||||||
def _configure_facebook_fields(self, generator_tab):
|
def _configure_facebook_fields(self, generator_tab):
|
||||||
"""
|
"""
|
||||||
Konfiguriert Facebook-spezifische Felder im Generator-Tab.
|
Legacy-Methode für Abwärtskompatibilität.
|
||||||
|
Die Konfiguration erfolgt jetzt in FacebookGeneratorTab.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
generator_tab: Der zu konfigurierende Tab
|
generator_tab: Der zu konfigurierende Tab
|
||||||
"""
|
"""
|
||||||
# Facebook-spezifische Konfiguration
|
# Diese Methode ist nicht mehr nötig, da FacebookGeneratorTab
|
||||||
# Der GeneratorTab kann für Facebook-spezifische Felder erweitert werden
|
# bereits alle Facebook-spezifischen Felder enthält
|
||||||
# Geschlecht wird aus den Parametern extrahiert
|
# Behalten für Abwärtskompatibilität
|
||||||
# Vor- und Nachnamen werden im Worker-Thread aus full_name extrahiert
|
pass
|
||||||
|
|
||||||
logger.debug("Facebook-spezifische Felder konfiguriert (inkl. Geschlechtsauswahl)")
|
|
||||||
|
|
||||||
def handle_generation_request(self, params: Dict[str, Any]):
|
def handle_generation_request(self, params: Dict[str, Any]):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -89,8 +89,8 @@ class GmailController(BasePlatformController):
|
|||||||
|
|
||||||
def create_generator_tab(self):
|
def create_generator_tab(self):
|
||||||
"""Erstellt den Generator-Tab und verbindet die Signale"""
|
"""Erstellt den Generator-Tab und verbindet die Signale"""
|
||||||
from views.tabs.generator_tab import GeneratorTab
|
# Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt
|
||||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
generator_tab = super().create_generator_tab()
|
||||||
|
|
||||||
# Signal verbinden
|
# Signal verbinden
|
||||||
generator_tab.start_requested.connect(self.start_account_creation)
|
generator_tab.start_requested.connect(self.start_account_creation)
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from typing import Dict, Any
|
|||||||
|
|
||||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||||
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
||||||
from views.tabs.generator_tab import GeneratorTab
|
from views.tabs.generator_tab_factory import GeneratorTabFactory
|
||||||
from views.tabs.accounts_tab import AccountsTab
|
from views.tabs.accounts_tab import AccountsTab
|
||||||
from views.tabs.settings_tab import SettingsTab
|
from views.tabs.settings_tab import SettingsTab
|
||||||
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
||||||
@ -178,7 +178,8 @@ class InstagramController(BasePlatformController):
|
|||||||
|
|
||||||
def create_generator_tab(self):
|
def create_generator_tab(self):
|
||||||
"""Erstellt den Instagram-Generator-Tab."""
|
"""Erstellt den Instagram-Generator-Tab."""
|
||||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
# Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt
|
||||||
|
generator_tab = super().create_generator_tab()
|
||||||
|
|
||||||
# Instagram-spezifische Anpassungen
|
# Instagram-spezifische Anpassungen
|
||||||
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
||||||
|
|||||||
@ -7,7 +7,6 @@ from PyQt5.QtCore import QThread, pyqtSignal
|
|||||||
|
|
||||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||||
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
||||||
from views.tabs.generator_tab import GeneratorTab
|
|
||||||
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
||||||
|
|
||||||
from social_networks.ok_ru.ok_ru_automation import OkRuAutomation
|
from social_networks.ok_ru.ok_ru_automation import OkRuAutomation
|
||||||
@ -44,7 +43,8 @@ class OkRuController(BasePlatformController):
|
|||||||
|
|
||||||
def create_generator_tab(self):
|
def create_generator_tab(self):
|
||||||
"""Erstellt den Generator-Tab für OK.ru."""
|
"""Erstellt den Generator-Tab für OK.ru."""
|
||||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
# Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt
|
||||||
|
generator_tab = super().create_generator_tab()
|
||||||
|
|
||||||
# OK.ru verwendet nur Telefon-Registrierung
|
# OK.ru verwendet nur Telefon-Registrierung
|
||||||
# Keine spezielle Konfiguration nötig, da GeneratorTab standardmäßig alle Felder hat
|
# Keine spezielle Konfiguration nötig, da GeneratorTab standardmäßig alle Felder hat
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from typing import Dict, Any
|
|||||||
|
|
||||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||||
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
||||||
from views.tabs.generator_tab import GeneratorTab
|
from views.tabs.generator_tab_factory import GeneratorTabFactory
|
||||||
from views.tabs.accounts_tab import AccountsTab
|
from views.tabs.accounts_tab import AccountsTab
|
||||||
from views.tabs.settings_tab import SettingsTab
|
from views.tabs.settings_tab import SettingsTab
|
||||||
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
||||||
@ -183,7 +183,8 @@ class TikTokController(BasePlatformController):
|
|||||||
|
|
||||||
def create_generator_tab(self):
|
def create_generator_tab(self):
|
||||||
"""Erstellt den TikTok-Generator-Tab."""
|
"""Erstellt den TikTok-Generator-Tab."""
|
||||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
# Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt
|
||||||
|
generator_tab = super().create_generator_tab()
|
||||||
|
|
||||||
# TikTok-spezifische Anpassungen
|
# TikTok-spezifische Anpassungen
|
||||||
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from typing import Dict, Any, Tuple
|
|||||||
|
|
||||||
from controllers.platform_controllers.base_controller import BasePlatformController
|
from controllers.platform_controllers.base_controller import BasePlatformController
|
||||||
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread
|
||||||
from views.tabs.generator_tab import GeneratorTab
|
from views.tabs.generator_tab_factory import GeneratorTabFactory
|
||||||
from views.tabs.accounts_tab import AccountsTab
|
from views.tabs.accounts_tab import AccountsTab
|
||||||
from views.tabs.settings_tab import SettingsTab
|
from views.tabs.settings_tab import SettingsTab
|
||||||
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
from views.widgets.forge_animation_widget import ForgeAnimationDialog
|
||||||
@ -180,7 +180,8 @@ class XController(BasePlatformController):
|
|||||||
|
|
||||||
def create_generator_tab(self):
|
def create_generator_tab(self):
|
||||||
"""Erstellt den X-Generator-Tab."""
|
"""Erstellt den X-Generator-Tab."""
|
||||||
generator_tab = GeneratorTab(self.platform_name, self.language_manager)
|
# Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt
|
||||||
|
generator_tab = super().create_generator_tab()
|
||||||
|
|
||||||
# X-spezifische Anpassungen
|
# X-spezifische Anpassungen
|
||||||
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
# Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden
|
||||||
|
|||||||
Binäre Datei nicht angezeigt.
150
tests/test_generator_tab_factory.py
Normale Datei
150
tests/test_generator_tab_factory.py
Normale Datei
@ -0,0 +1,150 @@
|
|||||||
|
"""
|
||||||
|
Unit-Tests für GeneratorTabFactory.
|
||||||
|
Validiert die Factory-Implementierung und plattform-spezifische Tab-Erstellung.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
# Füge Projekt-Root zum Path hinzu
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QApplication, QWidget
|
||||||
|
from views.tabs.generator_tab_factory import GeneratorTabFactory, create_generator_tab
|
||||||
|
from views.tabs.generator_tab import GeneratorTab
|
||||||
|
|
||||||
|
|
||||||
|
class TestGeneratorTabFactory(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Test-Suite für GeneratorTabFactory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
"""Erstelle QApplication für Qt-Widgets."""
|
||||||
|
if not QApplication.instance():
|
||||||
|
cls.app = QApplication([])
|
||||||
|
else:
|
||||||
|
cls.app = QApplication.instance()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Setup vor jedem Test."""
|
||||||
|
# Registry zurücksetzen
|
||||||
|
GeneratorTabFactory.clear_registry()
|
||||||
|
self.language_manager = MagicMock()
|
||||||
|
|
||||||
|
def test_create_generic_tab_for_unknown_platform(self):
|
||||||
|
"""Test: Factory erstellt generischen Tab für unbekannte Plattform."""
|
||||||
|
tab = GeneratorTabFactory.create_tab("unknown_platform", self.language_manager)
|
||||||
|
|
||||||
|
self.assertIsInstance(tab, QWidget)
|
||||||
|
self.assertIsInstance(tab, GeneratorTab)
|
||||||
|
|
||||||
|
def test_create_facebook_tab(self):
|
||||||
|
"""Test: Factory erstellt FacebookGeneratorTab für Facebook."""
|
||||||
|
tab = GeneratorTabFactory.create_tab("facebook", self.language_manager)
|
||||||
|
|
||||||
|
self.assertIsInstance(tab, QWidget)
|
||||||
|
# Prüfe ob es der Facebook-spezifische Tab ist
|
||||||
|
# FacebookGeneratorTab hat gender_male, gender_female, gender_custom Attribute
|
||||||
|
self.assertTrue(hasattr(tab, 'gender_male'), "Facebook-Tab sollte gender_male haben")
|
||||||
|
self.assertTrue(hasattr(tab, 'gender_female'), "Facebook-Tab sollte gender_female haben")
|
||||||
|
self.assertTrue(hasattr(tab, 'gender_custom'), "Facebook-Tab sollte gender_custom haben")
|
||||||
|
|
||||||
|
def test_case_insensitive_platform_names(self):
|
||||||
|
"""Test: Plattform-Namen sind case-insensitive."""
|
||||||
|
tab1 = GeneratorTabFactory.create_tab("FACEBOOK", self.language_manager)
|
||||||
|
tab2 = GeneratorTabFactory.create_tab("Facebook", self.language_manager)
|
||||||
|
tab3 = GeneratorTabFactory.create_tab("facebook", self.language_manager)
|
||||||
|
|
||||||
|
# Alle sollten Facebook-Tabs sein
|
||||||
|
for tab in [tab1, tab2, tab3]:
|
||||||
|
self.assertTrue(hasattr(tab, 'gender_male'))
|
||||||
|
|
||||||
|
def test_registry_functionality(self):
|
||||||
|
"""Test: Tab-Registry funktioniert korrekt."""
|
||||||
|
# Erstelle Mock-Tab-Klasse
|
||||||
|
class MockTab(QWidget):
|
||||||
|
def __init__(self, platform, language_manager):
|
||||||
|
super().__init__()
|
||||||
|
self.platform = platform
|
||||||
|
self.language_manager = language_manager
|
||||||
|
|
||||||
|
# Registriere Mock-Tab
|
||||||
|
GeneratorTabFactory.register_tab("test_platform", MockTab)
|
||||||
|
|
||||||
|
# Erstelle Tab
|
||||||
|
tab = GeneratorTabFactory.create_tab("test_platform", self.language_manager)
|
||||||
|
|
||||||
|
self.assertIsInstance(tab, MockTab)
|
||||||
|
self.assertEqual(tab.platform, "test_platform")
|
||||||
|
|
||||||
|
def test_lazy_loading(self):
|
||||||
|
"""Test: Tabs werden lazy geladen."""
|
||||||
|
# Registry sollte initial leer sein
|
||||||
|
self.assertEqual(len(GeneratorTabFactory._tab_registry), 0)
|
||||||
|
|
||||||
|
# Erstelle Facebook-Tab
|
||||||
|
tab = GeneratorTabFactory.create_tab("facebook", self.language_manager)
|
||||||
|
|
||||||
|
# Jetzt sollte Facebook in Registry sein
|
||||||
|
self.assertIn("facebook", GeneratorTabFactory._tab_registry)
|
||||||
|
|
||||||
|
def test_get_supported_platforms(self):
|
||||||
|
"""Test: Liste der unterstützten Plattformen."""
|
||||||
|
platforms = GeneratorTabFactory.get_supported_platforms()
|
||||||
|
|
||||||
|
# Sollte bekannte Plattformen enthalten
|
||||||
|
self.assertIn("facebook", platforms)
|
||||||
|
self.assertIn("instagram", platforms)
|
||||||
|
self.assertIn("tiktok", platforms)
|
||||||
|
self.assertIn("x", platforms)
|
||||||
|
|
||||||
|
def test_is_platform_supported(self):
|
||||||
|
"""Test: Plattform-Support-Prüfung."""
|
||||||
|
self.assertTrue(GeneratorTabFactory.is_platform_supported("facebook"))
|
||||||
|
self.assertTrue(GeneratorTabFactory.is_platform_supported("FACEBOOK"))
|
||||||
|
self.assertFalse(GeneratorTabFactory.is_platform_supported("unknown"))
|
||||||
|
|
||||||
|
def test_convenience_function(self):
|
||||||
|
"""Test: Convenience-Funktion create_generator_tab."""
|
||||||
|
tab = create_generator_tab("facebook", self.language_manager)
|
||||||
|
|
||||||
|
self.assertIsInstance(tab, QWidget)
|
||||||
|
self.assertTrue(hasattr(tab, 'gender_male'))
|
||||||
|
|
||||||
|
def test_error_handling_fallback(self):
|
||||||
|
"""Test: Factory fällt auf generischen Tab zurück bei Fehlern."""
|
||||||
|
# Simuliere einen Fehler beim Tab-Erstellen
|
||||||
|
with patch('views.tabs.facebook_generator_tab.FacebookGeneratorTab.__init__',
|
||||||
|
side_effect=Exception("Test error")):
|
||||||
|
|
||||||
|
tab = GeneratorTabFactory.create_tab("facebook", self.language_manager)
|
||||||
|
|
||||||
|
# Sollte auf generischen Tab zurückfallen
|
||||||
|
self.assertIsInstance(tab, GeneratorTab)
|
||||||
|
|
||||||
|
def test_signal_compatibility(self):
|
||||||
|
"""Test: Alle Tabs haben die erforderlichen Signale."""
|
||||||
|
platforms = ["facebook", "instagram", "tiktok", "x"]
|
||||||
|
|
||||||
|
for platform in platforms:
|
||||||
|
tab = GeneratorTabFactory.create_tab(platform, self.language_manager)
|
||||||
|
|
||||||
|
# Prüfe erforderliche Signale
|
||||||
|
self.assertTrue(hasattr(tab, 'start_requested'),
|
||||||
|
f"{platform}-Tab sollte start_requested Signal haben")
|
||||||
|
self.assertTrue(hasattr(tab, 'stop_requested'),
|
||||||
|
f"{platform}-Tab sollte stop_requested Signal haben")
|
||||||
|
self.assertTrue(hasattr(tab, 'account_created'),
|
||||||
|
f"{platform}-Tab sollte account_created Signal haben")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Cleanup nach jedem Test."""
|
||||||
|
GeneratorTabFactory.clear_registry()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@ -30,6 +30,8 @@ class FacebookGeneratorTab(QWidget):
|
|||||||
self.init_ui()
|
self.init_ui()
|
||||||
|
|
||||||
if self.language_manager:
|
if self.language_manager:
|
||||||
|
# Nur echte LanguageManager haben language_changed Signal
|
||||||
|
if hasattr(self.language_manager, 'language_changed'):
|
||||||
self.language_manager.language_changed.connect(self.update_texts)
|
self.language_manager.language_changed.connect(self.update_texts)
|
||||||
self.update_texts()
|
self.update_texts()
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,8 @@ class GeneratorTab(QWidget):
|
|||||||
self.language_manager = language_manager
|
self.language_manager = language_manager
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
if self.language_manager:
|
if self.language_manager:
|
||||||
|
# Nur echte LanguageManager haben language_changed Signal
|
||||||
|
if hasattr(self.language_manager, 'language_changed'):
|
||||||
self.language_manager.language_changed.connect(self.update_texts)
|
self.language_manager.language_changed.connect(self.update_texts)
|
||||||
self.update_texts()
|
self.update_texts()
|
||||||
|
|
||||||
|
|||||||
185
views/tabs/generator_tab_factory.py
Normale Datei
185
views/tabs/generator_tab_factory.py
Normale Datei
@ -0,0 +1,185 @@
|
|||||||
|
"""
|
||||||
|
Factory Pattern für die Erstellung plattform-spezifischer Generator-Tabs.
|
||||||
|
Clean Architecture Implementation für erweiterbare Tab-Generierung.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional, Dict, Type
|
||||||
|
from PyQt5.QtWidgets import QWidget
|
||||||
|
|
||||||
|
# Base imports
|
||||||
|
from views.tabs.generator_tab import GeneratorTab
|
||||||
|
|
||||||
|
logger = logging.getLogger("generator_tab_factory")
|
||||||
|
|
||||||
|
|
||||||
|
class GeneratorTabFactory:
|
||||||
|
"""
|
||||||
|
Factory-Klasse zur Erstellung plattform-spezifischer Generator-Tabs.
|
||||||
|
|
||||||
|
Diese Factory verwendet das Strategy Pattern intern, um verschiedene
|
||||||
|
Tab-Implementierungen basierend auf der Plattform zu erstellen.
|
||||||
|
Neue Plattformen können einfach durch Hinzufügen zur Registry erweitert werden.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Registry für plattform-spezifische Tabs
|
||||||
|
# Key: Plattform-Name (lowercase), Value: Tab-Klasse
|
||||||
|
_tab_registry: Dict[str, Type[QWidget]] = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_tab(cls, platform: str, tab_class: Type[QWidget]) -> None:
|
||||||
|
"""
|
||||||
|
Registriert eine Tab-Klasse für eine bestimmte Plattform.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
platform: Name der Plattform (wird zu lowercase konvertiert)
|
||||||
|
tab_class: Die Tab-Klasse für diese Plattform
|
||||||
|
"""
|
||||||
|
platform_lower = platform.lower()
|
||||||
|
cls._tab_registry[platform_lower] = tab_class
|
||||||
|
logger.info(f"Tab-Klasse {tab_class.__name__} für Plattform '{platform}' registriert")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_tab(cls, platform: str, language_manager: Optional[object] = None) -> QWidget:
|
||||||
|
"""
|
||||||
|
Erstellt einen Generator-Tab für die angegebene Plattform.
|
||||||
|
|
||||||
|
Diese Methode verwendet Lazy Loading für plattform-spezifische Tabs,
|
||||||
|
um zirkuläre Imports zu vermeiden und die Performance zu optimieren.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
platform: Name der Plattform
|
||||||
|
language_manager: Optionaler Language Manager für Internationalisierung
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QWidget: Der erstellte Generator-Tab
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Wenn die Plattform nicht unterstützt wird
|
||||||
|
"""
|
||||||
|
platform_lower = platform.lower()
|
||||||
|
|
||||||
|
# Lazy Loading und Registrierung für bekannte Plattformen
|
||||||
|
if platform_lower not in cls._tab_registry:
|
||||||
|
cls._lazy_load_platform_tab(platform_lower)
|
||||||
|
|
||||||
|
# Tab-Klasse aus Registry holen
|
||||||
|
tab_class = cls._tab_registry.get(platform_lower)
|
||||||
|
|
||||||
|
if tab_class:
|
||||||
|
logger.info(f"Erstelle {tab_class.__name__} für Plattform '{platform}'")
|
||||||
|
try:
|
||||||
|
return tab_class(platform, language_manager)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Erstellen des Tabs für '{platform}': {e}")
|
||||||
|
logger.info(f"Fallback auf generischen GeneratorTab für '{platform}'")
|
||||||
|
return GeneratorTab(platform, language_manager)
|
||||||
|
else:
|
||||||
|
# Fallback auf generischen Tab
|
||||||
|
logger.info(f"Verwende generischen GeneratorTab für Plattform '{platform}'")
|
||||||
|
return GeneratorTab(platform, language_manager)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _lazy_load_platform_tab(cls, platform: str) -> None:
|
||||||
|
"""
|
||||||
|
Lädt plattform-spezifische Tab-Klassen lazy (bei Bedarf).
|
||||||
|
|
||||||
|
Dies vermeidet zirkuläre Imports und verbessert die Startzeit,
|
||||||
|
indem nur benötigte Module geladen werden.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
platform: Name der Plattform (lowercase)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if platform == "facebook":
|
||||||
|
from views.tabs.facebook_generator_tab import FacebookGeneratorTab
|
||||||
|
cls.register_tab("facebook", FacebookGeneratorTab)
|
||||||
|
|
||||||
|
elif platform == "instagram":
|
||||||
|
# Instagram verwendet möglicherweise einen spezifischen Tab
|
||||||
|
# Vorerst den generischen Tab verwenden
|
||||||
|
cls.register_tab("instagram", GeneratorTab)
|
||||||
|
|
||||||
|
elif platform == "tiktok":
|
||||||
|
# TikTok könnte einen eigenen Tab haben
|
||||||
|
# Prüfe ob TikTokGeneratorTab existiert
|
||||||
|
try:
|
||||||
|
from views.tabs.tiktok_generator_tab import TikTokGeneratorTab
|
||||||
|
cls.register_tab("tiktok", TikTokGeneratorTab)
|
||||||
|
except ImportError:
|
||||||
|
# Fallback auf generischen Tab
|
||||||
|
cls.register_tab("tiktok", GeneratorTab)
|
||||||
|
|
||||||
|
elif platform == "x":
|
||||||
|
# X (Twitter) Tab
|
||||||
|
try:
|
||||||
|
from views.tabs.x_generator_tab import XGeneratorTab
|
||||||
|
cls.register_tab("x", XGeneratorTab)
|
||||||
|
except ImportError:
|
||||||
|
cls.register_tab("x", GeneratorTab)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Unbekannte Plattform - verwende generischen Tab
|
||||||
|
cls.register_tab(platform, GeneratorTab)
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
logger.warning(f"Konnte spezifischen Tab für '{platform}' nicht laden: {e}")
|
||||||
|
# Registriere generischen Tab als Fallback
|
||||||
|
cls.register_tab(platform, GeneratorTab)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_supported_platforms(cls) -> list:
|
||||||
|
"""
|
||||||
|
Gibt eine Liste der unterstützten Plattformen zurück.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: Liste der Plattform-Namen
|
||||||
|
"""
|
||||||
|
# Bekannte Plattformen, die lazy geladen werden können
|
||||||
|
known_platforms = ["facebook", "instagram", "tiktok", "x"]
|
||||||
|
|
||||||
|
# Bereits registrierte Plattformen hinzufügen
|
||||||
|
registered = list(cls._tab_registry.keys())
|
||||||
|
|
||||||
|
# Kombiniere und entferne Duplikate
|
||||||
|
all_platforms = list(set(known_platforms + registered))
|
||||||
|
all_platforms.sort()
|
||||||
|
|
||||||
|
return all_platforms
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_platform_supported(cls, platform: str) -> bool:
|
||||||
|
"""
|
||||||
|
Prüft, ob eine Plattform unterstützt wird.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
platform: Name der Plattform
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True wenn unterstützt, sonst False
|
||||||
|
"""
|
||||||
|
return platform.lower() in cls.get_supported_platforms()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_registry(cls) -> None:
|
||||||
|
"""
|
||||||
|
Löscht die Tab-Registry.
|
||||||
|
Hauptsächlich für Tests und Cleanup.
|
||||||
|
"""
|
||||||
|
cls._tab_registry.clear()
|
||||||
|
logger.debug("Tab-Registry gelöscht")
|
||||||
|
|
||||||
|
|
||||||
|
# Convenience-Funktion für direkten Import
|
||||||
|
def create_generator_tab(platform: str, language_manager: Optional[object] = None) -> QWidget:
|
||||||
|
"""
|
||||||
|
Convenience-Funktion zum Erstellen eines Generator-Tabs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
platform: Name der Plattform
|
||||||
|
language_manager: Optionaler Language Manager
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QWidget: Der erstellte Generator-Tab
|
||||||
|
"""
|
||||||
|
return GeneratorTabFactory.create_tab(platform, language_manager)
|
||||||
@ -23,13 +23,13 @@ class ForgeAnimationDialog(QDialog):
|
|||||||
# Timer für das regelmäßige Nach-vorne-Holen
|
# Timer für das regelmäßige Nach-vorne-Holen
|
||||||
self.raise_timer = QTimer()
|
self.raise_timer = QTimer()
|
||||||
self.raise_timer.timeout.connect(self._raise_to_front)
|
self.raise_timer.timeout.connect(self._raise_to_front)
|
||||||
self.raise_timer.setInterval(500) # Alle 500ms
|
self.raise_timer.setInterval(1000) # Alle 1000ms (weniger aggressiv)
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
"""Initialisiert die UI mit verbessertem Design"""
|
"""Initialisiert die UI mit verbessertem Design"""
|
||||||
# Dialog-Fenster ohne Stay-on-Top, um Browser nicht zu blockieren
|
# Dialog-Fenster mit Stay-on-Top für Sichtbarkeit
|
||||||
# Entfernt Qt.WindowStaysOnTopHint, da dies den Browser-Fokus stören kann
|
# WindowStaysOnTopHint sorgt dafür, dass die Warnung immer sichtbar bleibt
|
||||||
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
|
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||||
self.setModal(False) # Nicht modal - blockiert nicht das Hauptfenster
|
self.setModal(False) # Nicht modal - blockiert nicht das Hauptfenster
|
||||||
self.setFixedSize(650, 600) # Ursprüngliche Größe beibehalten
|
self.setFixedSize(650, 600) # Ursprüngliche Größe beibehalten
|
||||||
|
|
||||||
@ -187,8 +187,8 @@ class ForgeAnimationDialog(QDialog):
|
|||||||
def start_animation(self):
|
def start_animation(self):
|
||||||
"""Zeigt den Dialog an"""
|
"""Zeigt den Dialog an"""
|
||||||
self.status_label.setText("Initialisiere...")
|
self.status_label.setText("Initialisiere...")
|
||||||
# Timer deaktiviert - verhindert Fokus-Probleme mit dem Browser
|
# Timer aktiviert als Fallback für Window-Manager die TopHint ignorieren
|
||||||
# self.raise_timer.start() # Deaktiviert: Stört Browser-Interaktion
|
self.raise_timer.start() # Fallback: Hält Dialog zuverlässig im Vordergrund
|
||||||
|
|
||||||
def stop_animation(self):
|
def stop_animation(self):
|
||||||
"""Stoppt die Animation und den Timer"""
|
"""Stoppt die Animation und den Timer"""
|
||||||
@ -227,9 +227,9 @@ class ForgeAnimationDialog(QDialog):
|
|||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
def _raise_to_front(self):
|
def _raise_to_front(self):
|
||||||
"""Holt den Dialog in den Vordergrund"""
|
"""Holt den Dialog in den Vordergrund ohne Fokus zu stehlen"""
|
||||||
self.raise_()
|
self.raise_()
|
||||||
# Nicht activateWindow() aufrufen - das holt das Hauptfenster mit
|
# Nicht activateWindow() aufrufen - das würde den Fokus vom Browser stehlen
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
"""Überschreibt show() um den Dialog richtig zu positionieren"""
|
"""Überschreibt show() um den Dialog richtig zu positionieren"""
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren