Hardening: Streamlined testing infrastructure, unified toolkit, and established English TDD standards
This commit is contained in:
118
scripts/sync_fixtures.py
Executable file
118
scripts/sync_fixtures.py
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
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')
|
||||
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
|
||||
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("🤖 AUTOMATED E2E DUMP CAPTURE GUIDE")
|
||||
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")
|
||||
|
||||
steps = [
|
||||
("comment_sheet.xml", "Post Comment Sheet", "Open any post and open its comment section."),
|
||||
("stories_feed_dump.xml", "Active Story Playback", "Tap any user's story ring on the HomeFeed."),
|
||||
("dm_inbox_dump.xml", "DM Inbox / Threads List", "Go to your DM inbox list."),
|
||||
("scraping_profile_dump.xml", "Own Profile Root", "Go to your OWN profile tab."),
|
||||
("unfollow_list_dump.xml", "Following List", "On your profile, tap 'Following' to open the list."),
|
||||
("search_feed_dump.xml", "Explore Search Focus", "Tap Explore, then tap into the Search bar."),
|
||||
("reels_feed_dump.xml", "Reels Video Feed", "Go to the Reels tab and let a video play."),
|
||||
("notifications_dump.xml", "Activity Feed", "Tap the Heart/Notifications icon."),
|
||||
("explore_feed_dump.xml", "Explore Grid", "Tap the Explore tab (just the grid visible)."),
|
||||
("user_profile_dump.xml", "Alien Profile Root", "Go to ANY OTHER user's profile."),
|
||||
("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.")
|
||||
]
|
||||
|
||||
for i, (fname, desc, instr) in enumerate(steps, 1):
|
||||
try:
|
||||
print(f"\n👉 {i}/{len(steps)}. {desc.upper()}:")
|
||||
print(f" {instr}")
|
||||
input(" Press ENTER when ready to capture...")
|
||||
_save_dump(device, fixture_dir, fname, desc)
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 Guide interrupted.")
|
||||
return
|
||||
|
||||
print("\n" + "="*60)
|
||||
logger.info("🎉 Capture Sequence Complete! All fixtures have been updated.")
|
||||
print("="*60 + "\n")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Instagram Bot Testing Toolkit - Fixture Management")
|
||||
parser.add_argument("--config", help="Path to your bot's .yml config (to auto-extract device ID)")
|
||||
parser.add_argument("--device", help="Manual ADB device ID (e.g., emulator-5554)")
|
||||
parser.add_argument("--fixture", help="Name of a single fixture to sync (e.g., explore_feed_dump.xml)")
|
||||
parser.add_argument("--interactive", action="store_true", help="Launch the full interactive capture guide")
|
||||
args = parser.parse_args()
|
||||
|
||||
device_id = args.device
|
||||
|
||||
# Try to extract device from config if provided
|
||||
if args.config:
|
||||
try:
|
||||
# We use a dummy Config instance to parse the file
|
||||
bot_config = Config(first_run=True, config=args.config)
|
||||
bot_config.parse_args()
|
||||
device_id = bot_config.device_id 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}")
|
||||
|
||||
if not device_id:
|
||||
logger.error("No device ID provided! Use --device or --config to specify which device to connect to.")
|
||||
sys.exit(1)
|
||||
|
||||
fixture_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "tests", "fixtures")
|
||||
os.makedirs(fixture_dir, exist_ok=True)
|
||||
|
||||
print(f"Connecting to {device_id}...")
|
||||
try:
|
||||
device = create_device(device_id, "com.instagram.android")
|
||||
# Quick connectivity check
|
||||
device.get_info()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to device: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if args.interactive:
|
||||
run_interactive_guide(device, fixture_dir)
|
||||
elif args.fixture:
|
||||
fname = args.fixture
|
||||
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()
|
||||
Reference in New Issue
Block a user