From 8754bf9c685e214670ae3063f5b1eec3e88c387f Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 9 Jan 2026 16:47:05 +0000 Subject: [PATCH] Add comprehensive multilingual UI messages and enhance server test configuration: Update localization files with extensive UI translations in multiple languages, dynamically bind test server port, and refactor UI tests to improve literal text handling. --- src/autometabuilder/messages_es.json | 153 ++++++++++++++++++++++- src/autometabuilder/messages_fr.json | 153 ++++++++++++++++++++++- src/autometabuilder/messages_nl.json | 153 ++++++++++++++++++++++- src/autometabuilder/messages_pirate.json | 153 ++++++++++++++++++++++- tests/ui/conftest.py | 18 ++- tests/ui/test_web_ui.py | 49 ++++---- 6 files changed, 645 insertions(+), 34 deletions(-) diff --git a/src/autometabuilder/messages_es.json b/src/autometabuilder/messages_es.json index 43c7eae..f92cba9 100644 --- a/src/autometabuilder/messages_es.json +++ b/src/autometabuilder/messages_es.json @@ -15,5 +15,156 @@ "confirm_tool_execution": "¿Desea ejecutar {name} con {args}? [y/N]: ", "info_tool_skipped": "Omitiendo herramienta: {name}", "info_dry_run_skipping": "DRY RUN: Omitiendo herramienta de modificación de estado {name}", - "info_second_pass": "Realizando un segundo paso con los resultados de la herramienta..." + "info_second_pass": "Realizando un segundo paso con los resultados de la herramienta...", + "ui.app.title": "Panel de AutoMetabuilder", + "ui.app.name": "AutoMetabuilder", + "ui.nav.dashboard": "Panel", + "ui.nav.workflow": "Flujo de trabajo", + "ui.nav.prompt": "Prompt", + "ui.nav.settings": "Configuración", + "ui.nav.translations": "Traducciones", + "ui.theme_toggle": "Cambiar tema", + "ui.actions.add": "Añadir", + "ui.actions.save": "Guardar", + "ui.actions.reset": "Restablecer", + "ui.actions.delete": "Eliminar", + "ui.actions.edit": "Editar", + "ui.actions.refresh": "Actualizar", + "ui.common.select_placeholder": "Seleccionar...", + "ui.choices.search_placeholder": "Escribe para buscar...", + "ui.choices.no_results": "No se encontraron resultados", + "ui.choices.no_choices": "No hay opciones disponibles", + "ui.dashboard.title": "Panel", + "ui.dashboard.subtitle": "Controla el bot y supervisa la actividad del sistema", + "ui.dashboard.bot_control": "Control del bot", + "ui.dashboard.run_strategy": "Estrategia de ejecución", + "ui.dashboard.run.single.title": "Iteración única", + "ui.dashboard.run.single.desc": "Una pasada completa por el flujo de trabajo.", + "ui.dashboard.run.repeat.title": "Repetir", + "ui.dashboard.run.repeat.desc": "Ejecuta un número fijo de ciclos.", + "ui.dashboard.run.repeat.label": "Iteraciones", + "ui.dashboard.run.yolo.title": "YOLO", + "ui.dashboard.run.yolo.desc": "Sigue iterando hasta que lo detengas.", + "ui.dashboard.automation": "Automatización", + "ui.dashboard.auto_approve.title": "Autoaprobar herramientas", + "ui.dashboard.auto_approve.desc": "Omite confirmaciones para que el bot ejecute más rápido.", + "ui.dashboard.stop_mvp.title": "Detener en MVP", + "ui.dashboard.stop_mvp.desc": "Pausa automáticamente cuando se alcance el hito MVP.", + "ui.dashboard.start_bot": "Iniciar bot", + "ui.dashboard.status.title": "Estado", + "ui.dashboard.status.bot_label": "Estado del bot", + "ui.dashboard.status.running": "En ejecución", + "ui.dashboard.status.idle": "Inactivo", + "ui.dashboard.status.mvp_label": "Hito MVP", + "ui.dashboard.status.mvp_reached": "Alcanzado", + "ui.dashboard.status.mvp_progress": "En progreso", + "ui.dashboard.logs.title": "Registros recientes", + "ui.workflow.title": "Constructor de flujo de trabajo", + "ui.workflow.subtitle": "Diseña el flujo de ejecución de tareas del bot", + "ui.workflow.card.title": "Tareas y pasos", + "ui.workflow.toggle_json": "Alternar JSON", + "ui.workflow.save": "Guardar flujo de trabajo", + "ui.workflow.empty": "Aún no hay tareas. Haz clic en \"Añadir tarea\" para crear tu primera tarea de flujo de trabajo.", + "ui.workflow.task_label": "Tarea {number}", + "ui.workflow.task_name_placeholder": "Nombre de la tarea", + "ui.workflow.type.standard": "Estándar", + "ui.workflow.type.loop": "Bucle", + "ui.workflow.max_label": "Máx.", + "ui.workflow.move_up": "Mover arriba", + "ui.workflow.move_down": "Mover abajo", + "ui.workflow.delete_task": "Eliminar tarea", + "ui.workflow.add_step": "Añadir paso", + "ui.workflow.add_task": "Añadir tarea", + "ui.workflow.select_action": "Selecciona una acción...", + "ui.workflow.step_type_placeholder": "Selecciona el tipo de acción...", + "ui.workflow.field.enable": "Habilitar", + "ui.workflow.remove_step": "Eliminar paso", + "ui.workflow.new_task": "Nueva tarea", + "ui.workflow.delete_task_confirm": "¿Eliminar esta tarea y todos sus pasos?", + "ui.workflow.untitled_task": "Tarea sin título", + "ui.prompt.title": "Constructor de prompts", + "ui.prompt.subtitle": "Define cómo piensa, habla y decide el asistente", + "ui.prompt.card.title": "Constructor de prompts", + "ui.prompt.step1.title": "Define al asistente", + "ui.prompt.step1.desc": "Describe quién es el asistente y cómo debe comportarse.", + "ui.prompt.step1.placeholder": "Eres un ingeniero de software senior que prefiere cambios pequeños y seguros.", + "ui.prompt.chip.senior.label": "Ingeniero senior", + "ui.prompt.chip.senior.snippet": "Eres un ingeniero de software senior centrado en la corrección y la claridad.", + "ui.prompt.chip.ask.label": "Hacer preguntas", + "ui.prompt.chip.ask.snippet": "Haz preguntas aclaratorias antes de hacer cambios arriesgados.", + "ui.prompt.chip.minimal.label": "Cambios mínimos", + "ui.prompt.chip.minimal.snippet": "Prefiere cambios mínimos y explica las compensaciones.", + "ui.prompt.step2.title": "Da la misión", + "ui.prompt.step2.desc": "Explica lo que el bot debe lograr ahora.", + "ui.prompt.step2.placeholder": "Revisa el repositorio, mejora la UI y resume lo que cambió.", + "ui.prompt.chip.ux.label": "Pulido UX", + "ui.prompt.chip.ux.snippet": "Céntrate en pulir la UX y evita grandes refactorizaciones.", + "ui.prompt.chip.tests.label": "Añadir tests", + "ui.prompt.chip.tests.snippet": "Añade tests cuando sea posible, pero evita mucho andamiaje.", + "ui.prompt.chip.summarize.label": "Resumir", + "ui.prompt.chip.summarize.snippet": "Resume los cambios y sugiere próximos pasos.", + "ui.prompt.model.label": "Elige un modelo", + "ui.prompt.model.desc": "Elige el equilibrio entre calidad y velocidad que se ajuste a la tarea.", + "ui.prompt.model.recommended": "Recomendado", + "ui.prompt.model.faster": "Más rápido", + "ui.prompt.raw.label": "YAML avanzado", + "ui.prompt.raw.desc": "Edita el YAML completo solo si necesitas un control fino.", + "ui.prompt.save": "Guardar prompt", + "ui.prompt.advanced_yaml": "YAML avanzado", + "ui.prompt.guidance.title": "Guía", + "ui.prompt.guidance.keep_human.title": "Manténlo humano", + "ui.prompt.guidance.keep_human.desc": "Escribe instrucciones como si estuvieras explicándolas a un compañero.", + "ui.prompt.guidance.be_specific.title": "Sé específico", + "ui.prompt.guidance.be_specific.desc": "Menciona restricciones como tiempo, alcance o expectativas de pruebas.", + "ui.prompt.guidance.raw.title": "Usa YAML avanzado con moderación", + "ui.prompt.guidance.raw.desc": "Cambia a YAML bruto solo si necesitas control total.", + "ui.settings.title": "Configuración", + "ui.settings.subtitle": "Configura servicios, seguridad y preferencias del entorno", + "ui.settings.callout.title": "Configuración clara", + "ui.settings.callout.body": "Cada campo explica qué hace. Añade descripciones en metadata.json para mantener legibles los ajustes personalizados.", + "ui.settings.api_keys": "Claves API", + "ui.settings.configuration": "Configuración", + "ui.settings.web_access": "Acceso a la interfaz web", + "ui.settings.other": "Otros ajustes", + "ui.settings.custom_default_desc": "Ajuste de entorno personalizado. Añade una descripción en metadata.json para verla aquí.", + "ui.settings.add.title": "Añadir nuevo ajuste", + "ui.settings.add.desc": "Usa claves en mayúsculas con guiones bajos, como API_TIMEOUT.", + "ui.settings.add.placeholder_key": "CLAVE", + "ui.settings.add.placeholder_value": "Valor", + "ui.settings.save_all": "Guardar todos los ajustes", + "ui.translations.title": "Traducciones", + "ui.translations.subtitle": "Crea, edita y mantiene archivos de idioma para los mensajes del bot", + "ui.translations.languages": "Idiomas", + "ui.translations.add_language_placeholder": "Añadir idioma...", + "ui.translations.select_language_placeholder": "Selecciona un idioma...", + "ui.translations.editor.title": "Editor de traducciones", + "ui.translations.editing_label": "Editando", + "ui.translations.missing_count": "{count} faltantes", + "ui.translations.all_set": "Todo listo", + "ui.translations.actions.fill_missing": "Rellenar faltantes", + "ui.translations.empty.title": "Elige un idioma", + "ui.translations.empty.body": "Selecciona un idioma de la lista para empezar a editar traducciones.", + "ui.translations.search.placeholder": "Buscar claves o texto...", + "ui.translations.toggle_missing": "Mostrar claves faltantes", + "ui.translations.add.key_placeholder": "nueva.clave", + "ui.translations.add.value_placeholder": "Texto de traducción", + "ui.translations.add.use_english": "Usar inglés", + "ui.translations.add.add_key": "Añadir clave", + "ui.translations.table.key": "Clave", + "ui.translations.table.translation": "Traducción", + "ui.translations.table.delete_title": "Eliminar clave", + "ui.translations.missing_label": "Faltante", + "ui.translations.hint_prefix": "EN:", + "ui.translations.prompt.enter_key": "Introduce primero un nombre de clave.", + "ui.translations.prompt.no_english": "No se encontró texto en inglés para esta clave.", + "ui.translations.confirm.replace_key": "Esta clave ya existe. ¿Reemplazarla?", + "ui.translations.confirm.delete_key": "¿Eliminar la clave de traducción \"{key}\"?", + "ui.translations.confirm.delete_translation": "¿Seguro que quieres eliminar la traducción \"{lang}\"?", + "ui.translations.errors.load": "No se pudo cargar la traducción", + "ui.translations.errors.load_prefix": "Error al cargar la traducción: ", + "ui.translations.errors.save": "No se pudo guardar", + "ui.translations.errors.save_prefix": "Error al guardar la traducción: ", + "ui.translations.errors.delete": "No se pudo eliminar", + "ui.translations.errors.delete_prefix": "Error al eliminar la traducción: ", + "ui.translations.notice.saved": "¡Traducción \"{lang}\" guardada correctamente!" } diff --git a/src/autometabuilder/messages_fr.json b/src/autometabuilder/messages_fr.json index 88312c5..2d55c77 100644 --- a/src/autometabuilder/messages_fr.json +++ b/src/autometabuilder/messages_fr.json @@ -15,5 +15,156 @@ "confirm_tool_execution": "Voulez-vous exécuter {name} avec {args} ? [y/N] : ", "info_tool_skipped": "Omission de l'outil : {name}", "info_dry_run_skipping": "DRY RUN : Omission de l'outil de modification d'état {name}", - "info_second_pass": "Deuxième passage avec les résultats de l'outil..." + "info_second_pass": "Deuxième passage avec les résultats de l'outil...", + "ui.app.title": "Tableau de bord AutoMetabuilder", + "ui.app.name": "AutoMetabuilder", + "ui.nav.dashboard": "Tableau de bord", + "ui.nav.workflow": "Flux de travail", + "ui.nav.prompt": "Prompt", + "ui.nav.settings": "Paramètres", + "ui.nav.translations": "Traductions", + "ui.theme_toggle": "Basculer le thème", + "ui.actions.add": "Ajouter", + "ui.actions.save": "Enregistrer", + "ui.actions.reset": "Réinitialiser", + "ui.actions.delete": "Supprimer", + "ui.actions.edit": "Modifier", + "ui.actions.refresh": "Actualiser", + "ui.common.select_placeholder": "Sélectionner...", + "ui.choices.search_placeholder": "Tapez pour rechercher...", + "ui.choices.no_results": "Aucun résultat", + "ui.choices.no_choices": "Aucune option disponible", + "ui.dashboard.title": "Tableau de bord", + "ui.dashboard.subtitle": "Contrôlez le bot et surveillez l'activité du système", + "ui.dashboard.bot_control": "Contrôle du bot", + "ui.dashboard.run_strategy": "Stratégie d'exécution", + "ui.dashboard.run.single.title": "Itération unique", + "ui.dashboard.run.single.desc": "Un passage complet dans le workflow.", + "ui.dashboard.run.repeat.title": "Répéter", + "ui.dashboard.run.repeat.desc": "Exécuter un nombre fixe de cycles.", + "ui.dashboard.run.repeat.label": "Itérations", + "ui.dashboard.run.yolo.title": "YOLO", + "ui.dashboard.run.yolo.desc": "Continuez à itérer jusqu'à l'arrêt.", + "ui.dashboard.automation": "Automatisation", + "ui.dashboard.auto_approve.title": "Auto-approuver les outils", + "ui.dashboard.auto_approve.desc": "Ignore les confirmations pour accélérer le bot.", + "ui.dashboard.stop_mvp.title": "Arrêter au MVP", + "ui.dashboard.stop_mvp.desc": "Mettre en pause automatiquement lorsque le jalon MVP est atteint.", + "ui.dashboard.start_bot": "Démarrer le bot", + "ui.dashboard.status.title": "Statut", + "ui.dashboard.status.bot_label": "Statut du bot", + "ui.dashboard.status.running": "En cours", + "ui.dashboard.status.idle": "Inactif", + "ui.dashboard.status.mvp_label": "Jalon MVP", + "ui.dashboard.status.mvp_reached": "Atteint", + "ui.dashboard.status.mvp_progress": "En cours", + "ui.dashboard.logs.title": "Logs récents", + "ui.workflow.title": "Constructeur de workflow", + "ui.workflow.subtitle": "Concevez le pipeline d'exécution des tâches du bot", + "ui.workflow.card.title": "Tâches et étapes", + "ui.workflow.toggle_json": "Basculer le JSON", + "ui.workflow.save": "Enregistrer le workflow", + "ui.workflow.empty": "Aucune tâche pour l'instant. Cliquez sur \"Ajouter une tâche\" pour créer votre première tâche de workflow.", + "ui.workflow.task_label": "Tâche {number}", + "ui.workflow.task_name_placeholder": "Nom de la tâche", + "ui.workflow.type.standard": "Standard", + "ui.workflow.type.loop": "Boucle", + "ui.workflow.max_label": "Max", + "ui.workflow.move_up": "Monter", + "ui.workflow.move_down": "Descendre", + "ui.workflow.delete_task": "Supprimer la tâche", + "ui.workflow.add_step": "Ajouter une étape", + "ui.workflow.add_task": "Ajouter une tâche", + "ui.workflow.select_action": "Sélectionnez une action...", + "ui.workflow.step_type_placeholder": "Sélectionnez un type d'action...", + "ui.workflow.field.enable": "Activer", + "ui.workflow.remove_step": "Supprimer l'étape", + "ui.workflow.new_task": "Nouvelle tâche", + "ui.workflow.delete_task_confirm": "Supprimer cette tâche et toutes ses étapes ?", + "ui.workflow.untitled_task": "Tâche sans titre", + "ui.prompt.title": "Constructeur de prompts", + "ui.prompt.subtitle": "Définissez comment l'assistant pense, parle et décide", + "ui.prompt.card.title": "Constructeur de prompts", + "ui.prompt.step1.title": "Définir l'assistant", + "ui.prompt.step1.desc": "Décrivez qui est l'assistant et comment il doit se comporter.", + "ui.prompt.step1.placeholder": "Vous êtes un ingénieur logiciel senior qui préfère des changements petits et sûrs.", + "ui.prompt.chip.senior.label": "Ingénieur senior", + "ui.prompt.chip.senior.snippet": "Vous êtes un ingénieur logiciel senior axé sur la justesse et la clarté.", + "ui.prompt.chip.ask.label": "Poser des questions", + "ui.prompt.chip.ask.snippet": "Posez des questions de clarification avant d'apporter des changements risqués.", + "ui.prompt.chip.minimal.label": "Diffs minimales", + "ui.prompt.chip.minimal.snippet": "Préférez des diffs minimales et expliquez les compromis.", + "ui.prompt.step2.title": "Donner la mission", + "ui.prompt.step2.desc": "Expliquez ce que le bot doit accomplir maintenant.", + "ui.prompt.step2.placeholder": "Examinez le dépôt, améliorez l'UI et résumez ce qui a changé.", + "ui.prompt.chip.ux.label": "Finition UX", + "ui.prompt.chip.ux.snippet": "Concentrez-vous sur la finition UX et évitez les grosses refactorisations.", + "ui.prompt.chip.tests.label": "Ajouter des tests", + "ui.prompt.chip.tests.snippet": "Ajoutez des tests quand c'est possible, mais évitez trop de scaffolding.", + "ui.prompt.chip.summarize.label": "Résumer", + "ui.prompt.chip.summarize.snippet": "Résumez les changements et proposez les prochaines étapes.", + "ui.prompt.model.label": "Choisir un modèle", + "ui.prompt.model.desc": "Choisissez l'équilibre qualité/vitesse adapté à la tâche.", + "ui.prompt.model.recommended": "Recommandé", + "ui.prompt.model.faster": "Plus rapide", + "ui.prompt.raw.label": "YAML avancé", + "ui.prompt.raw.desc": "Modifiez le YAML complet seulement si vous avez besoin d'un contrôle fin.", + "ui.prompt.save": "Enregistrer le prompt", + "ui.prompt.advanced_yaml": "YAML avancé", + "ui.prompt.guidance.title": "Conseils", + "ui.prompt.guidance.keep_human.title": "Restez humain", + "ui.prompt.guidance.keep_human.desc": "Rédigez les instructions comme si vous briefiez un collègue.", + "ui.prompt.guidance.be_specific.title": "Soyez précis", + "ui.prompt.guidance.be_specific.desc": "Mentionnez des contraintes comme le temps, le périmètre ou les attentes de tests.", + "ui.prompt.guidance.raw.title": "Utilisez le YAML avancé avec parcimonie", + "ui.prompt.guidance.raw.desc": "Basculez vers le YAML brut uniquement si vous avez besoin d'un contrôle total.", + "ui.settings.title": "Paramètres", + "ui.settings.subtitle": "Configurez les services, la sécurité et les préférences d'environnement", + "ui.settings.callout.title": "Paramètres compréhensibles", + "ui.settings.callout.body": "Chaque champ explique son rôle. Ajoutez des descriptions dans metadata.json pour rendre les paramètres personnalisés lisibles.", + "ui.settings.api_keys": "Clés API", + "ui.settings.configuration": "Configuration", + "ui.settings.web_access": "Accès à l'interface web", + "ui.settings.other": "Autres paramètres", + "ui.settings.custom_default_desc": "Paramètre d'environnement personnalisé. Ajoutez une description dans metadata.json pour l'afficher ici.", + "ui.settings.add.title": "Ajouter un paramètre", + "ui.settings.add.desc": "Utilisez des clés en majuscules avec des underscores, comme API_TIMEOUT.", + "ui.settings.add.placeholder_key": "CLÉ", + "ui.settings.add.placeholder_value": "Valeur", + "ui.settings.save_all": "Enregistrer tous les paramètres", + "ui.translations.title": "Traductions", + "ui.translations.subtitle": "Créez, éditez et maintenez les fichiers de langue pour les messages du bot", + "ui.translations.languages": "Langues", + "ui.translations.add_language_placeholder": "Ajouter une langue...", + "ui.translations.select_language_placeholder": "Sélectionnez une langue...", + "ui.translations.editor.title": "Éditeur de traductions", + "ui.translations.editing_label": "Modification", + "ui.translations.missing_count": "{count} manquants", + "ui.translations.all_set": "Tout est prêt", + "ui.translations.actions.fill_missing": "Compléter les manquants", + "ui.translations.empty.title": "Choisir une langue", + "ui.translations.empty.body": "Sélectionnez une langue dans la liste pour commencer à éditer les traductions.", + "ui.translations.search.placeholder": "Rechercher des clés ou du texte...", + "ui.translations.toggle_missing": "Afficher les clés manquantes", + "ui.translations.add.key_placeholder": "nouvelle.cle", + "ui.translations.add.value_placeholder": "Texte de traduction", + "ui.translations.add.use_english": "Utiliser l'anglais", + "ui.translations.add.add_key": "Ajouter une clé", + "ui.translations.table.key": "Clé", + "ui.translations.table.translation": "Traduction", + "ui.translations.table.delete_title": "Supprimer la clé", + "ui.translations.missing_label": "Manquant", + "ui.translations.hint_prefix": "EN:", + "ui.translations.prompt.enter_key": "Saisissez d'abord un nom de clé.", + "ui.translations.prompt.no_english": "Aucun texte anglais trouvé pour cette clé.", + "ui.translations.confirm.replace_key": "Cette clé existe déjà. La remplacer ?", + "ui.translations.confirm.delete_key": "Supprimer la clé de traduction \"{key}\" ?", + "ui.translations.confirm.delete_translation": "Voulez-vous vraiment supprimer la traduction \"{lang}\" ?", + "ui.translations.errors.load": "Échec du chargement de la traduction", + "ui.translations.errors.load_prefix": "Erreur lors du chargement de la traduction : ", + "ui.translations.errors.save": "Échec de l'enregistrement", + "ui.translations.errors.save_prefix": "Erreur lors de l'enregistrement : ", + "ui.translations.errors.delete": "Échec de la suppression", + "ui.translations.errors.delete_prefix": "Erreur lors de la suppression : ", + "ui.translations.notice.saved": "Traduction \"{lang}\" enregistrée avec succès !" } diff --git a/src/autometabuilder/messages_nl.json b/src/autometabuilder/messages_nl.json index 25bcbe8..49a665f 100644 --- a/src/autometabuilder/messages_nl.json +++ b/src/autometabuilder/messages_nl.json @@ -15,5 +15,156 @@ "confirm_tool_execution": "Wilt u {name} uitvoeren met {args}? [y/N]: ", "info_tool_skipped": "Overslaan van tool: {name}", "info_dry_run_skipping": "DRY RUN: Overslaan van statuswijzigende tool {name}", - "info_second_pass": "Tweede ronde uitvoeren met tool-resultaten..." + "info_second_pass": "Tweede ronde uitvoeren met tool-resultaten...", + "ui.app.title": "AutoMetabuilder-dashboard", + "ui.app.name": "AutoMetabuilder", + "ui.nav.dashboard": "Dashboard", + "ui.nav.workflow": "Workflow", + "ui.nav.prompt": "Prompt", + "ui.nav.settings": "Instellingen", + "ui.nav.translations": "Vertalingen", + "ui.theme_toggle": "Thema wisselen", + "ui.actions.add": "Toevoegen", + "ui.actions.save": "Opslaan", + "ui.actions.reset": "Resetten", + "ui.actions.delete": "Verwijderen", + "ui.actions.edit": "Bewerken", + "ui.actions.refresh": "Vernieuwen", + "ui.common.select_placeholder": "Selecteer...", + "ui.choices.search_placeholder": "Typ om te zoeken...", + "ui.choices.no_results": "Geen resultaten gevonden", + "ui.choices.no_choices": "Geen opties beschikbaar", + "ui.dashboard.title": "Dashboard", + "ui.dashboard.subtitle": "Beheer de bot en bewaak de systeemactiviteit", + "ui.dashboard.bot_control": "Botbediening", + "ui.dashboard.run_strategy": "Uitvoeringsstrategie", + "ui.dashboard.run.single.title": "Enkele iteratie", + "ui.dashboard.run.single.desc": "Eén volledige ronde door de workflow.", + "ui.dashboard.run.repeat.title": "Herhalen", + "ui.dashboard.run.repeat.desc": "Voer een vast aantal cycli uit.", + "ui.dashboard.run.repeat.label": "Iteraties", + "ui.dashboard.run.yolo.title": "YOLO", + "ui.dashboard.run.yolo.desc": "Blijf itereren totdat je stopt.", + "ui.dashboard.automation": "Automatisering", + "ui.dashboard.auto_approve.title": "Tools automatisch goedkeuren", + "ui.dashboard.auto_approve.desc": "Sla bevestigingen over zodat de bot sneller kan draaien.", + "ui.dashboard.stop_mvp.title": "Stop bij MVP", + "ui.dashboard.stop_mvp.desc": "Pauzeer automatisch wanneer het MVP-mijlpunt is bereikt.", + "ui.dashboard.start_bot": "Start bot", + "ui.dashboard.status.title": "Status", + "ui.dashboard.status.bot_label": "Botstatus", + "ui.dashboard.status.running": "Actief", + "ui.dashboard.status.idle": "Inactief", + "ui.dashboard.status.mvp_label": "MVP-mijlpaal", + "ui.dashboard.status.mvp_reached": "Bereikt", + "ui.dashboard.status.mvp_progress": "Bezig", + "ui.dashboard.logs.title": "Recente logs", + "ui.workflow.title": "Workflowbouwer", + "ui.workflow.subtitle": "Ontwerp de taakuitvoeringspijplijn van de bot", + "ui.workflow.card.title": "Taken en stappen", + "ui.workflow.toggle_json": "JSON wisselen", + "ui.workflow.save": "Workflow opslaan", + "ui.workflow.empty": "Nog geen taken. Klik op \"Taak toevoegen\" om je eerste workflowtaak te maken.", + "ui.workflow.task_label": "Taak {number}", + "ui.workflow.task_name_placeholder": "Taaknaam", + "ui.workflow.type.standard": "Standaard", + "ui.workflow.type.loop": "Lus", + "ui.workflow.max_label": "Max", + "ui.workflow.move_up": "Omhoog", + "ui.workflow.move_down": "Omlaag", + "ui.workflow.delete_task": "Taak verwijderen", + "ui.workflow.add_step": "Stap toevoegen", + "ui.workflow.add_task": "Taak toevoegen", + "ui.workflow.select_action": "Selecteer een actie...", + "ui.workflow.step_type_placeholder": "Selecteer een actietype...", + "ui.workflow.field.enable": "Inschakelen", + "ui.workflow.remove_step": "Stap verwijderen", + "ui.workflow.new_task": "Nieuwe taak", + "ui.workflow.delete_task_confirm": "Deze taak en alle stappen verwijderen?", + "ui.workflow.untitled_task": "Naamloze taak", + "ui.prompt.title": "Promptbouwer", + "ui.prompt.subtitle": "Bepaal hoe de assistent denkt, praat en beslist", + "ui.prompt.card.title": "Promptbouwer", + "ui.prompt.step1.title": "Definieer de assistent", + "ui.prompt.step1.desc": "Beschrijf wie de assistent is en hoe hij zich moet gedragen.", + "ui.prompt.step1.placeholder": "Je bent een senior software engineer die kleine, veilige wijzigingen verkiest.", + "ui.prompt.chip.senior.label": "Senior engineer", + "ui.prompt.chip.senior.snippet": "Je bent een senior software engineer gericht op correctheid en duidelijkheid.", + "ui.prompt.chip.ask.label": "Vragen stellen", + "ui.prompt.chip.ask.snippet": "Stel verduidelijkende vragen voordat je riskante wijzigingen aanbrengt.", + "ui.prompt.chip.minimal.label": "Minimale diffs", + "ui.prompt.chip.minimal.snippet": "Geef de voorkeur aan minimale diffs en leg de afwegingen uit.", + "ui.prompt.step2.title": "Geef de missie", + "ui.prompt.step2.desc": "Leg uit wat de bot nu moet bereiken.", + "ui.prompt.step2.placeholder": "Beoordeel de repo, verbeter de UI en vat samen wat er is veranderd.", + "ui.prompt.chip.ux.label": "UX-polish", + "ui.prompt.chip.ux.snippet": "Richt je op UX-polish en vermijd grote refactors.", + "ui.prompt.chip.tests.label": "Tests toevoegen", + "ui.prompt.chip.tests.snippet": "Voeg tests toe waar mogelijk, maar vermijd zware scaffolding.", + "ui.prompt.chip.summarize.label": "Samenvatten", + "ui.prompt.chip.summarize.snippet": "Vat de wijzigingen samen en stel vervolgstappen voor.", + "ui.prompt.model.label": "Kies een model", + "ui.prompt.model.desc": "Kies de balans tussen kwaliteit en snelheid die bij de taak past.", + "ui.prompt.model.recommended": "Aanbevolen", + "ui.prompt.model.faster": "Sneller", + "ui.prompt.raw.label": "Geavanceerde YAML", + "ui.prompt.raw.desc": "Bewerk de volledige YAML alleen als je fijne controle nodig hebt.", + "ui.prompt.save": "Prompt opslaan", + "ui.prompt.advanced_yaml": "Geavanceerde YAML", + "ui.prompt.guidance.title": "Richtlijnen", + "ui.prompt.guidance.keep_human.title": "Houd het menselijk", + "ui.prompt.guidance.keep_human.desc": "Schrijf instructies alsof je een collega brief.", + "ui.prompt.guidance.be_specific.title": "Wees specifiek", + "ui.prompt.guidance.be_specific.desc": "Noem beperkingen zoals tijd, scope of testverwachtingen.", + "ui.prompt.guidance.raw.title": "Gebruik geavanceerde YAML spaarzaam", + "ui.prompt.guidance.raw.desc": "Schakel alleen over naar ruwe YAML als je volledige controle nodig hebt.", + "ui.settings.title": "Instellingen", + "ui.settings.subtitle": "Configureer services, beveiliging en omgevingsvoorkeuren", + "ui.settings.callout.title": "Mensvriendelijke instellingen", + "ui.settings.callout.body": "Elk veld legt uit wat het doet. Voeg beschrijvingen toe in metadata.json om aangepaste instellingen leesbaar te houden.", + "ui.settings.api_keys": "API-sleutels", + "ui.settings.configuration": "Configuratie", + "ui.settings.web_access": "Toegang tot webinterface", + "ui.settings.other": "Andere instellingen", + "ui.settings.custom_default_desc": "Aangepaste omgevingsinstelling. Voeg een beschrijving toe in metadata.json om dit hier te tonen.", + "ui.settings.add.title": "Nieuwe instelling toevoegen", + "ui.settings.add.desc": "Gebruik hoofdletters met underscores, zoals API_TIMEOUT.", + "ui.settings.add.placeholder_key": "SLEUTEL", + "ui.settings.add.placeholder_value": "Waarde", + "ui.settings.save_all": "Alle instellingen opslaan", + "ui.translations.title": "Vertalingen", + "ui.translations.subtitle": "Maak, bewerk en onderhoud taalbestanden voor botberichten", + "ui.translations.languages": "Talen", + "ui.translations.add_language_placeholder": "Taal toevoegen...", + "ui.translations.select_language_placeholder": "Selecteer taal...", + "ui.translations.editor.title": "Vertalingseditor", + "ui.translations.editing_label": "Bewerken", + "ui.translations.missing_count": "{count} ontbrekend", + "ui.translations.all_set": "Alles geregeld", + "ui.translations.actions.fill_missing": "Ontbrekende invullen", + "ui.translations.empty.title": "Kies een taal", + "ui.translations.empty.body": "Selecteer een taal uit de lijst om vertalingen te bewerken.", + "ui.translations.search.placeholder": "Zoek naar sleutels of tekst...", + "ui.translations.toggle_missing": "Ontbrekende sleutels tonen", + "ui.translations.add.key_placeholder": "nieuwe.sleutel", + "ui.translations.add.value_placeholder": "Vertaaltekst", + "ui.translations.add.use_english": "Gebruik Engels", + "ui.translations.add.add_key": "Sleutel toevoegen", + "ui.translations.table.key": "Sleutel", + "ui.translations.table.translation": "Vertaling", + "ui.translations.table.delete_title": "Sleutel verwijderen", + "ui.translations.missing_label": "Ontbrekend", + "ui.translations.hint_prefix": "EN:", + "ui.translations.prompt.enter_key": "Voer eerst een sleutelnaam in.", + "ui.translations.prompt.no_english": "Geen Engelse tekst gevonden voor deze sleutel.", + "ui.translations.confirm.replace_key": "Deze sleutel bestaat al. Vervangen?", + "ui.translations.confirm.delete_key": "Vertalingssleutel \"{key}\" verwijderen?", + "ui.translations.confirm.delete_translation": "Weet je zeker dat je de vertaling \"{lang}\" wilt verwijderen?", + "ui.translations.errors.load": "Vertaling laden mislukt", + "ui.translations.errors.load_prefix": "Fout bij laden van vertaling: ", + "ui.translations.errors.save": "Opslaan mislukt", + "ui.translations.errors.save_prefix": "Fout bij opslaan: ", + "ui.translations.errors.delete": "Verwijderen mislukt", + "ui.translations.errors.delete_prefix": "Fout bij verwijderen: ", + "ui.translations.notice.saved": "Vertaling \"{lang}\" is succesvol opgeslagen!" } diff --git a/src/autometabuilder/messages_pirate.json b/src/autometabuilder/messages_pirate.json index 39b97a3..44c4468 100644 --- a/src/autometabuilder/messages_pirate.json +++ b/src/autometabuilder/messages_pirate.json @@ -15,5 +15,156 @@ "confirm_tool_execution": "Do ye want to fire {name} with {args}? [y/N]: ", "info_tool_skipped": "Lettin' {name} sail by.", "info_dry_run_skipping": "DRY RUN: Stoppin' the crew from changin' the ship's course {name}", - "info_second_pass": "Making a second tack with the tool's findings..." + "info_second_pass": "Making a second tack with the tool's findings...", + "ui.app.title": "AutoMetabuilder Cap'n's Deck", + "ui.app.name": "AutoMetabuilder", + "ui.nav.dashboard": "Deck", + "ui.nav.workflow": "Flow o' Work", + "ui.nav.prompt": "Prompt", + "ui.nav.settings": "Settings", + "ui.nav.translations": "Tongues", + "ui.theme_toggle": "Hoist the theme", + "ui.actions.add": "Add", + "ui.actions.save": "Stow", + "ui.actions.reset": "Reset", + "ui.actions.delete": "Scuttle", + "ui.actions.edit": "Tweak", + "ui.actions.refresh": "Refresh", + "ui.common.select_placeholder": "Choose...", + "ui.choices.search_placeholder": "Type t' search...", + "ui.choices.no_results": "No loot found", + "ui.choices.no_choices": "No choices aboard", + "ui.dashboard.title": "Deck", + "ui.dashboard.subtitle": "Command the bot and watch the ship's activity", + "ui.dashboard.bot_control": "Bot Helm", + "ui.dashboard.run_strategy": "Run Tactics", + "ui.dashboard.run.single.title": "Single Run", + "ui.dashboard.run.single.desc": "One full pass through the workflow.", + "ui.dashboard.run.repeat.title": "Repeat", + "ui.dashboard.run.repeat.desc": "Run a fixed number o' cycles.", + "ui.dashboard.run.repeat.label": "Iterations", + "ui.dashboard.run.yolo.title": "YOLO", + "ui.dashboard.run.yolo.desc": "Keep iteratin' till ye stop it.", + "ui.dashboard.automation": "Auto-magic", + "ui.dashboard.auto_approve.title": "Auto-approve tools", + "ui.dashboard.auto_approve.desc": "Skip confirmations so the bot sails faster.", + "ui.dashboard.stop_mvp.title": "Stop at MVP", + "ui.dashboard.stop_mvp.desc": "Heave to when the MVP milestone be reached.", + "ui.dashboard.start_bot": "Hoist the Bot", + "ui.dashboard.status.title": "Status", + "ui.dashboard.status.bot_label": "Bot Status", + "ui.dashboard.status.running": "Running", + "ui.dashboard.status.idle": "Idle", + "ui.dashboard.status.mvp_label": "MVP Milestone", + "ui.dashboard.status.mvp_reached": "Reached", + "ui.dashboard.status.mvp_progress": "In Progress", + "ui.dashboard.logs.title": "Recent Logs", + "ui.workflow.title": "Workflow Builder", + "ui.workflow.subtitle": "Chart the bot's task pipeline", + "ui.workflow.card.title": "Tasks & Steps", + "ui.workflow.toggle_json": "Toggle JSON", + "ui.workflow.save": "Stow Workflow", + "ui.workflow.empty": "No tasks yet. Click \"Add Task\" to chart yer first workflow task.", + "ui.workflow.task_label": "Task {number}", + "ui.workflow.task_name_placeholder": "Task Name", + "ui.workflow.type.standard": "Standard", + "ui.workflow.type.loop": "Loop", + "ui.workflow.max_label": "Max", + "ui.workflow.move_up": "Move up", + "ui.workflow.move_down": "Move down", + "ui.workflow.delete_task": "Scuttle task", + "ui.workflow.add_step": "Add Step", + "ui.workflow.add_task": "Add Task", + "ui.workflow.select_action": "Select action...", + "ui.workflow.step_type_placeholder": "Select action type...", + "ui.workflow.field.enable": "Enable", + "ui.workflow.remove_step": "Remove step", + "ui.workflow.new_task": "New Task", + "ui.workflow.delete_task_confirm": "Scuttle this task and all its steps?", + "ui.workflow.untitled_task": "Untitled Task", + "ui.prompt.title": "Prompt Builder", + "ui.prompt.subtitle": "Shape how the matey thinks, speaks, and decides", + "ui.prompt.card.title": "Prompt Builder", + "ui.prompt.step1.title": "Define the matey", + "ui.prompt.step1.desc": "Describe who the matey be and how it should behave.", + "ui.prompt.step1.placeholder": "Ye be a senior software engineer who prefers small, safe changes.", + "ui.prompt.chip.senior.label": "Senior engineer", + "ui.prompt.chip.senior.snippet": "Ye be a senior software engineer focused on correctness and clarity.", + "ui.prompt.chip.ask.label": "Ask questions", + "ui.prompt.chip.ask.snippet": "Ask clarifying questions before makin' risky changes.", + "ui.prompt.chip.minimal.label": "Minimal diffs", + "ui.prompt.chip.minimal.snippet": "Prefer minimal diffs and explain trade-offs.", + "ui.prompt.step2.title": "Give the mission", + "ui.prompt.step2.desc": "Explain what the bot should accomplish right now.", + "ui.prompt.step2.placeholder": "Review the repo, improve the UI, and summarize what changed.", + "ui.prompt.chip.ux.label": "UX polish", + "ui.prompt.chip.ux.snippet": "Focus on UX polish, and avoid major refactors.", + "ui.prompt.chip.tests.label": "Add tests", + "ui.prompt.chip.tests.snippet": "Add tests when possible, but avoid heavy scaffolding.", + "ui.prompt.chip.summarize.label": "Summarize", + "ui.prompt.chip.summarize.snippet": "Summarize changes and suggest next steps.", + "ui.prompt.model.label": "Choose a model", + "ui.prompt.model.desc": "Pick the balance of quality and speed that fits the task.", + "ui.prompt.model.recommended": "Recommended", + "ui.prompt.model.faster": "Faster", + "ui.prompt.raw.label": "Advanced YAML", + "ui.prompt.raw.desc": "Edit the full YAML only if ye need fine control.", + "ui.prompt.save": "Save Prompt", + "ui.prompt.advanced_yaml": "Advanced YAML", + "ui.prompt.guidance.title": "Guidance", + "ui.prompt.guidance.keep_human.title": "Keep it human", + "ui.prompt.guidance.keep_human.desc": "Write instructions the way ye would brief a crewmate.", + "ui.prompt.guidance.be_specific.title": "Be specific", + "ui.prompt.guidance.be_specific.desc": "Mention constraints like time, scope, or testing expectations.", + "ui.prompt.guidance.raw.title": "Use advanced YAML sparingly", + "ui.prompt.guidance.raw.desc": "Only switch to raw YAML if ye need full control.", + "ui.settings.title": "Settings", + "ui.settings.subtitle": "Set services, security, and environment preferences", + "ui.settings.callout.title": "Crew-friendly settings", + "ui.settings.callout.body": "Each field explains what it does. Add descriptions in metadata.json to keep custom settings readable fer the crew.", + "ui.settings.api_keys": "API Keys", + "ui.settings.configuration": "Configuration", + "ui.settings.web_access": "Web UI Access", + "ui.settings.other": "Other Settings", + "ui.settings.custom_default_desc": "Custom environment setting. Add a description in metadata.json to show it here.", + "ui.settings.add.title": "Add New Setting", + "ui.settings.add.desc": "Use uppercase keys with underscores, like API_TIMEOUT.", + "ui.settings.add.placeholder_key": "KEY", + "ui.settings.add.placeholder_value": "Value", + "ui.settings.save_all": "Save All Settings", + "ui.translations.title": "Translations", + "ui.translations.subtitle": "Create, edit, and keep language files fer bot messages", + "ui.translations.languages": "Languages", + "ui.translations.add_language_placeholder": "Add language...", + "ui.translations.select_language_placeholder": "Select language...", + "ui.translations.editor.title": "Translation Editor", + "ui.translations.editing_label": "Editing", + "ui.translations.missing_count": "{count} missing", + "ui.translations.all_set": "All set", + "ui.translations.actions.fill_missing": "Fill Missing", + "ui.translations.empty.title": "Pick a language", + "ui.translations.empty.body": "Select a language from the list to start editing translations.", + "ui.translations.search.placeholder": "Search keys or text...", + "ui.translations.toggle_missing": "Show missing keys", + "ui.translations.add.key_placeholder": "new.key", + "ui.translations.add.value_placeholder": "Translation text", + "ui.translations.add.use_english": "Use English", + "ui.translations.add.add_key": "Add Key", + "ui.translations.table.key": "Key", + "ui.translations.table.translation": "Translation", + "ui.translations.table.delete_title": "Delete key", + "ui.translations.missing_label": "Missing", + "ui.translations.hint_prefix": "EN:", + "ui.translations.prompt.enter_key": "Enter a key name first.", + "ui.translations.prompt.no_english": "No English text found for this key.", + "ui.translations.confirm.replace_key": "This key already exists. Replace it?", + "ui.translations.confirm.delete_key": "Delete translation key \"{key}\"?", + "ui.translations.confirm.delete_translation": "Be ye sure ye want to delete the \"{lang}\" translation?", + "ui.translations.errors.load": "Failed to load translation", + "ui.translations.errors.load_prefix": "Error loading translation: ", + "ui.translations.errors.save": "Failed to save", + "ui.translations.errors.save_prefix": "Error saving translation: ", + "ui.translations.errors.delete": "Failed to delete", + "ui.translations.errors.delete_prefix": "Error deleting translation: ", + "ui.translations.notice.saved": "Translation \"{lang}\" saved successfully!" } diff --git a/tests/ui/conftest.py b/tests/ui/conftest.py index 4493601..716229d 100644 --- a/tests/ui/conftest.py +++ b/tests/ui/conftest.py @@ -1,21 +1,31 @@ import os import time +import socket import multiprocessing import pytest import uvicorn from autometabuilder.web.server import app -def run_server(): +multiprocessing.set_start_method("spawn", force=True) + +def run_server(port): os.environ["MOCK_WEB_UI"] = "true" os.environ["WEB_USER"] = "testuser" os.environ["WEB_PASSWORD"] = "testpass" - uvicorn.run(app, host="127.0.0.1", port=8001, log_level="error") + os.environ["APP_LANG"] = "en" + uvicorn.run(app, host="127.0.0.1", port=port, log_level="error") + +def get_free_port(): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.bind(("127.0.0.1", 0)) + return sock.getsockname()[1] @pytest.fixture(scope="session") def server(): - proc = multiprocessing.Process(target=run_server, daemon=True) + port = get_free_port() + proc = multiprocessing.Process(target=run_server, args=(port,), daemon=True) proc.start() # Give the server a moment to start time.sleep(2) - yield "http://127.0.0.1:8001" + yield f"http://127.0.0.1:{port}" proc.terminate() diff --git a/tests/ui/test_web_ui.py b/tests/ui/test_web_ui.py index 95db42f..f2a4a5c 100644 --- a/tests/ui/test_web_ui.py +++ b/tests/ui/test_web_ui.py @@ -4,13 +4,20 @@ import os import re from playwright.sync_api import Page, expect +UI_MESSAGES_PATH = os.path.join(os.path.dirname(__file__), "../../src/autometabuilder/messages_en.json") +with open(UI_MESSAGES_PATH, "r", encoding="utf-8") as f: + UI_MESSAGES = json.load(f) + +def t(key, fallback=None): + return UI_MESSAGES.get(key, fallback or key) + def test_login_and_dashboard(page: Page, server: str): # Go to the server with auth auth_url = server.replace("http://", "http://testuser:testpass@") page.goto(auth_url) # Check if we are on the dashboard - select h1 in the active section - expect(page.locator("#dashboard.active h1")).to_contain_text("Dashboard") + expect(page.locator("#dashboard.active h1")).to_contain_text(t("ui.dashboard.title")) # User info is now in sidebar footer expect(page.locator(".amb-sidebar-footer")).to_contain_text("testuser") @@ -41,21 +48,20 @@ def test_update_prompt(page: Page, server: str): page.click("[data-section='prompt']") page.wait_for_selector("#prompt.active") - # Find prompt textarea - specifically in the prompt section - textarea = page.locator("#prompt textarea[name='content']") - original_content = textarea.input_value() - - new_content = original_content + "\n# Test Comment" - textarea.fill(new_content) + system_prompt = page.locator("#prompt textarea[name='system_content']") + user_prompt = page.locator("#prompt textarea[name='user_content']") + system_prompt.fill("Test system prompt") + user_prompt.fill("Test user prompt") # Click save prompt - page.click("#prompt button:has-text('Save Prompt')") + page.click(f"#prompt button:has-text('{t('ui.prompt.save')}')") # Verify it updated page.reload() page.click("[data-section='prompt']") page.wait_for_selector("#prompt.active") - expect(page.locator("#prompt textarea[name='content']")).to_have_value(new_content) + expect(page.locator("#prompt textarea[name='system_content']")).to_have_value("Test system prompt") + expect(page.locator("#prompt textarea[name='user_content']")).to_have_value("Test user prompt") def test_update_settings(page: Page, server: str): auth_url = server.replace("http://", "http://testuser:testpass@") @@ -68,26 +74,17 @@ def test_update_settings(page: Page, server: str): # Wait for Choices.js to initialize page.wait_for_timeout(1000) - # Add a new setting using Choices.js select - # Click on the outer .choices wrapper (first match only) - key_choices = page.locator("#settings select[name='new_env_key']").locator("xpath=ancestor::div[@class='choices' or contains(@class, 'choices ')]").first - key_choices.click() - page.keyboard.type("GITHUB_TOKEN") - page.keyboard.press("Enter") + # Add a new setting using text inputs + page.fill("#settings input[name='new_env_key']", "TEST_SETTING") + page.fill("#settings input[name='new_env_value']", "42") - # For new_env_value - value_choices = page.locator("#settings select[name='new_env_value']").locator("xpath=ancestor::div[@class='choices' or contains(@class, 'choices ')]").first - value_choices.click() - page.keyboard.type("DEBUG") - page.keyboard.press("Enter") - - page.click("#settings button:has-text('Save Settings')") + page.click(f"#settings button:has-text('{t('ui.settings.save_all')}')") # Verify it appeared in the table page.reload() page.click("[data-section='settings']") page.wait_for_selector("#settings.active") - expect(page.locator("#settings code:has-text('GITHUB_TOKEN')")).to_be_visible() + expect(page.locator("#settings input[name='env_TEST_SETTING']")).to_be_visible() def test_navigation_sections(page: Page, server: str): """Test that sidebar navigation works correctly""" @@ -205,11 +202,11 @@ def test_workflow_builder_renders(page: Page, server: str): page.wait_for_selector("#workflow-builder") # Should have at least the "Add Task" button - expect(page.locator("#workflow-builder button:has-text('Add Task')")).to_be_visible() + expect(page.locator(f"#workflow-builder button:has-text('{t('ui.workflow.add_task')}')")).to_be_visible() # Toggle raw JSON should work - page.click("#workflow button:has-text('Toggle Raw JSON')") + page.click(f"#workflow button:has-text('{t('ui.workflow.toggle_json')}')") expect(page.locator("#workflow-content")).to_be_visible() - page.click("#workflow button:has-text('Toggle Raw JSON')") + page.click(f"#workflow button:has-text('{t('ui.workflow.toggle_json')}')") expect(page.locator("#workflow-content")).not_to_be_visible()