feat: complete modular plugin refactor with 100% E2E coverage for interactions

This commit is contained in:
2026-04-25 20:58:07 +02:00
parent 77e8251aa7
commit 144d6401b5
232 changed files with 66259 additions and 5410 deletions

View File

@@ -1,40 +1,41 @@
#!/usr/bin/env python3
import argparse
import logging
import os
import sys
import argparse
import time
import logging
# Ensure GramAddict is in PYTHONPATH if script is run directly
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from GramAddict.core.device_facade import create_device
from GramAddict.core.config import Config
# Setup a clean logger for the toolkit
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(levelname)s | %(message)s', datefmt='%H:%M:%S')
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s | %(message)s", datefmt="%H:%M:%S")
logger = logging.getLogger("TestingToolkit")
def _save_dump(device, fixture_dir, filename, description):
logger.info(f"⏳ Waiting for UI to settle for [{description}]...")
time.sleep(3.5) # ensure animations finish
time.sleep(3.5) # ensure animations finish
xml_data = device.dump_hierarchy()
if not xml_data or len(xml_data) < 100:
logger.warning(f"⚠️ Received empty or exceptionally small XML dump for {filename}. Is the app open?")
path = os.path.join(fixture_dir, filename)
with open(path, "w", encoding="utf-8") as f:
f.write(xml_data)
logger.info(f"✅ Saved REAL DUMP to {filename} ({len(xml_data)} bytes)")
def run_interactive_guide(device, fixture_dir):
print("\n" + "="*60)
print("\n" + "=" * 60)
print("🤖 AUTOMATED E2E DUMP CAPTURE GUIDE")
print("="*60)
print("=" * 60)
print("This guide will help you refresh the golden fixtures for the E2E suite.")
print("Please follow the instructions on your phone/emulator.")
print("="*60 + "\n")
print("=" * 60 + "\n")
steps = [
("comment_sheet.xml", "Post Comment Sheet", "Open any post and open its comment section."),
@@ -50,7 +51,7 @@ def run_interactive_guide(device, fixture_dir):
("followers_list_dump.xml", "Followers List", "On that user's profile, tap 'Followers'."),
("carousel_post_dump.xml", "Carousel Post", "Find a post with multiple images on your feed."),
("home_feed_with_ad.xml", "Sponsored Ad", "Scroll until you see a 'Sponsored' post."),
("dm_thread_dump.xml", "DM Chat Thread", "Open any specific chat conversation.")
("dm_thread_dump.xml", "DM Chat Thread", "Open any specific chat conversation."),
]
for i, (fname, desc, instr) in enumerate(steps, 1):
@@ -63,9 +64,10 @@ def run_interactive_guide(device, fixture_dir):
print("\n🛑 Guide interrupted.")
return
print("\n" + "="*60)
print("\n" + "=" * 60)
logger.info("🎉 Capture Sequence Complete! All fixtures have been updated.")
print("="*60 + "\n")
print("=" * 60 + "\n")
def main():
parser = argparse.ArgumentParser(description="Instagram Bot Testing Toolkit - Fixture Management")
@@ -76,14 +78,15 @@ def main():
args = parser.parse_args()
device_id = args.device
# Try to extract device from config if provided
if args.config:
try:
import yaml
with open(args.config, 'r', encoding='utf-8') as f:
with open(args.config, "r", encoding="utf-8") as f:
config_data = yaml.safe_load(f)
device_id = config_data.get('device') or device_id
device_id = config_data.get("device") or device_id
logger.info(f"Loaded device ID from config: {device_id}")
except Exception as e:
logger.warning(f"Could not read config file {args.config}: {e}")
@@ -108,11 +111,13 @@ def main():
run_interactive_guide(device, fixture_dir)
elif args.fixture:
fname = args.fixture
if not fname.endswith(".xml"): fname += ".xml"
if not fname.endswith(".xml"):
fname += ".xml"
_save_dump(device, fixture_dir, fname, f"Single Fixture: {fname}")
else:
logger.info("Nothing to do. Use --interactive or --fixture <name>.")
parser.print_help()
if __name__ == "__main__":
main()