testql 1.2.63__tar.gz → 1.2.65__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {testql-1.2.63/testql.egg-info → testql-1.2.65}/PKG-INFO +4 -4
- {testql-1.2.63 → testql-1.2.65}/README.md +3 -3
- {testql-1.2.63 → testql-1.2.65}/pyproject.toml +1 -1
- {testql-1.2.63 → testql-1.2.65}/testql/__init__.py +1 -1
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_api_runner.py +2 -2
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_gui.py +20 -1
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_testtoon_parser.py +59 -5
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/engine.py +8 -1
- {testql-1.2.63 → testql-1.2.65/testql.egg-info}/PKG-INFO +4 -4
- {testql-1.2.63 → testql-1.2.65}/tests/test_gui_execution.py +71 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_interpreter.py +44 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir_runner_captures.py +20 -0
- {testql-1.2.63 → testql-1.2.65}/LICENSE +0 -0
- {testql-1.2.63 → testql-1.2.65}/setup.cfg +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/__main__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/_base_fallback.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/entity_extractor.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/grammar.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/intent_recognizer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/lexicon/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/llm_fallback.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nl/nl_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2dsl/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2dsl/client.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2dsl/live_llm.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2dsl/llm_provider.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2dsl/mock_llm.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2dsl/nlp2dsl_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2env/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/nlp2env/nlp2env_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/registry.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/scenario_yaml.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/adapters/testtoon_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/artifacts/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/artifacts/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/artifacts/email_checker.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/artifacts/file_checker.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/artifacts/registry.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/assertion_suite.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/autoloop_runner.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/auto_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/conversation_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/discover_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/context.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/formatters/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/formatters/text.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/parsers/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/parsers/doql.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo/parsers/toon.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/echo_helpers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/encoder_routes.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/endpoints_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/generate_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/generate_from_page_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/generate_ir_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/generate_topology_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/heal_scenario_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/inspect_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/misc_cmds.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/nlp2env_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/run_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/run_ir_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/self_test_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite/cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite/collection.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite/execution.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite/listing.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite/reports.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/suite_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/templates/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/templates/content.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/templates/templates.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/topology_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/commands/watchdog_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/context/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/context/runtime.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/conversation/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/conversation/runner.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/data/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/data/verification-request-v1.schema.json +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/data/verification-result-v1.schema.json +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/config_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/django_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/express_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/fastapi_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/flask_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/graphql_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/openapi_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/test_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/unified.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/detectors/websocket_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/manifest.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/browser/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/browser/playwright_page.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/filesystem/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/filesystem/api_openapi.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/filesystem/container_compose.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/filesystem/container_dockerfile.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/filesystem/package_node.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/filesystem/package_python.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/network/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/probes/network/http_endpoint.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/registry.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/discovery/source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/doql_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/echo_schemas.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/endpoint_detector.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/export/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/export/scenario_builder.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/analyzers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/api_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/convenience.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/conversation_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/generators.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/llm/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/llm/coverage_optimizer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/llm/edge_case_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/multi.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/page_analyzer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/pipeline.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/pytest_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/scenario_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/config_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/conversation.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/nl_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/openapi_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/oql_models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/oql_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/oql_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/page_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/pytest_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/sources/ui_source.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/specialized_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/targets/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/targets/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/targets/nl_target.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/targets/pytest_target.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/targets/testtoon_target.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/generators/test_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/integrations/planfile_hook.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_assertions.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_context.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_converter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_desktop.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_dom_scan.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_encoder.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_flow.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_gui_expand.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_hardware.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_modbus.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_node_playwright.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_node_playwright_bridge.cjs +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_shell.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_unit.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_validation.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/_websockets.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/core.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/dispatcher.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/api.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/assertions.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/encoder.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/flow.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/include.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/navigate.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/record.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/select.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/unknown.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/handlers/wait.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/parsers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/converter/renderer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/dispatcher.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/dom_scan_formatters.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/dom_scan_mixin.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/dom_scan_models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/dom_scanner.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/interpreter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/testtoon_models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter/testtoon_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/interpreter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/assertions.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/captures.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/fixtures.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/metadata.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/plan.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir/steps.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/assertion_eval.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/context.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/api.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/assert_json.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/encoder.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/graphql.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/gui.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/nl.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/proto.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/shell.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/sql.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/executors/unit.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/ir_runner/interpolation.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/mcp/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/mcp/server.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/meta/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/meta/confidence_scorer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/meta/coverage_analyzer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/meta/mutator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/meta/self_test.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/nlp2env/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/nlp2env/llm.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/nlp2env/mcp_client.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/nlp2env/runner.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/nlp2env/scenarios.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/openapi_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/pipeline.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/proto_schema/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/proto_schema/descriptor_loader.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/proto_schema/message_validator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/report_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/reporters/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/reporters/console.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/reporters/json_reporter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/reporters/junit.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/results/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/results/analyzer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/results/artifacts.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/results/models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/results/serializers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/runner.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/runners/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/sql_schema/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/sql_schema/ddl_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/sql_schema/dialect_resolver.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/sumd_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/sumd_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/toon_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/topology/__init__.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/topology/builder.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/topology/generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/topology/models.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/topology/serializers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/topology/sitemap.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql/verification.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql.egg-info/SOURCES.txt +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql.egg-info/dependency_links.txt +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql.egg-info/entry_points.txt +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql.egg-info/requires.txt +0 -0
- {testql-1.2.63 → testql-1.2.65}/testql.egg-info/top_level.txt +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_adapter_capture_syntax.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_adapters_base.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_api_handler.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_assertion_suite.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_browser_discovery.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_cc_refactor_helpers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_cli_no_block.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_conversation_live_llm.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_conversation_nlp2dsl.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_converter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_converter_handlers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_desktop_assert_elements.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_desktop_execution.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_detectors.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_discovery.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_dispatcher.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_doql_parser_sumd_gen.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_echo.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_echo_doql_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_echo_schemas_helpers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_encoder_routes.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_environment_regressions.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_generate_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_generate_from_page_cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_generate_ir_cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_generators.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir_captures.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir_runner_assertion_eval.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir_runner_engine.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir_runner_executors.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_ir_runner_interpolation.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_mcp_autoloop.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_meta_confidence.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_meta_coverage.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_meta_mutator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_meta_self_test.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_misc_cmds.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_modbus_commands.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_navigate_json_path.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_network_discovery.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_nl_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_nl_entity_extractor.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_nl_grammar.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_nl_intent_recognizer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_nl_scenarios_e2e.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_nlp2env_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_node_playwright_fallback.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_openapi_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_page_analyzer.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_pipeline.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_plugin_registry.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_proto_descriptor_loader.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_proto_graphql_scenarios_e2e.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_proto_message_validator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_report_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_reporters.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_results.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_run_cmd.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_run_ir_cli.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_runner.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_runtime_context.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_scenario_yaml_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_shell_execution.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_smoke_decisions.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_sources.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_sql_ddl_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_sql_dialect_resolver.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_suite_cmd_helpers.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_suite_execution.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_suite_listing.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_sumd_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_targets.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_test_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_testtoon_adapter.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_toon_parser.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_topology.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_topology_generator.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_unit_execution.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_validation.py +0 -0
- {testql-1.2.63 → testql-1.2.65}/tests/test_verification_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testql
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.65
|
|
4
4
|
Summary: TestQL — Multi-DSL Test Platform: TestTOON / NL / SQL / Proto / GraphQL adapters with Unified IR, generator engine, and meta-testing
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: <3.14,>=3.11
|
|
@@ -62,10 +62,10 @@ Dynamic: license-file
|
|
|
62
62
|
## AI Cost Tracking
|
|
63
63
|
|
|
64
64
|
   
|
|
65
|
-
  
|
|
66
66
|
|
|
67
|
-
- 🤖 **LLM usage:** $
|
|
68
|
-
- 👤 **Human dev:** ~$
|
|
67
|
+
- 🤖 **LLM usage:** $30.6010 (133 commits)
|
|
68
|
+
- 👤 **Human dev:** ~$7576 (75.8h @ $100/h, 30min dedup)
|
|
69
69
|
|
|
70
70
|
Generated on 2026-07-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
71
71
|
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
6
|
   
|
|
7
|
-
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $30.6010 (133 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$7576 (75.8h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
12
|
Generated on 2026-07-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "testql"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.65"
|
|
8
8
|
description = "TestQL — Multi-DSL Test Platform: TestTOON / NL / SQL / Proto / GraphQL adapters with Unified IR, generator engine, and meta-testing"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11,<3.14"
|
|
@@ -72,7 +72,7 @@ class ApiRunnerMixin:
|
|
|
72
72
|
continue
|
|
73
73
|
# Re-raise non-retryable errors
|
|
74
74
|
raise
|
|
75
|
-
except Exception
|
|
75
|
+
except Exception:
|
|
76
76
|
# Non-retryable errors
|
|
77
77
|
raise
|
|
78
78
|
|
|
@@ -128,7 +128,7 @@ class ApiRunnerMixin:
|
|
|
128
128
|
return
|
|
129
129
|
|
|
130
130
|
method = parts[0].upper()
|
|
131
|
-
url = parts[1].strip("\"'")
|
|
131
|
+
url = self.vars.interpolate(parts[1].strip("\"'"))
|
|
132
132
|
body_str = parts[2] if len(parts) > 2 else ""
|
|
133
133
|
|
|
134
134
|
if url.startswith("/"):
|
|
@@ -1243,10 +1243,29 @@ class GuiMixin:
|
|
|
1243
1243
|
return
|
|
1244
1244
|
|
|
1245
1245
|
try:
|
|
1246
|
-
|
|
1246
|
+
# Form values are often updated after an asynchronous request. Keep
|
|
1247
|
+
# value assertions consistent with GUI_ASSERT_TEXT by polling within
|
|
1248
|
+
# the configured operation budget instead of racing the first read.
|
|
1249
|
+
budget_ms = self._gui_operation_timeout()
|
|
1250
|
+
deadline = time.monotonic() + budget_ms / 1000.0
|
|
1251
|
+
actual = ""
|
|
1252
|
+
last_error: Exception | None = None
|
|
1253
|
+
while True:
|
|
1254
|
+
try:
|
|
1255
|
+
actual = self._read_gui_value(selector)
|
|
1256
|
+
last_error = None
|
|
1257
|
+
except Exception as read_error:
|
|
1258
|
+
last_error = read_error
|
|
1259
|
+
actual = ""
|
|
1260
|
+
if self._compare_value(actual, op.upper(), expected) or time.monotonic() >= deadline:
|
|
1261
|
+
break
|
|
1262
|
+
time.sleep(0.1)
|
|
1263
|
+
|
|
1247
1264
|
if self._compare_value(actual, op.upper(), expected):
|
|
1248
1265
|
self.out.step("✅", f'{name} (actual: "{actual}")')
|
|
1249
1266
|
self.results.append(StepResult(name=name, status=StepStatus.PASSED))
|
|
1267
|
+
elif last_error is not None and not actual:
|
|
1268
|
+
raise last_error
|
|
1250
1269
|
else:
|
|
1251
1270
|
self.out.step("❌", f'{name} (actual: "{actual}")')
|
|
1252
1271
|
self.results.append(StepResult(
|
|
@@ -19,9 +19,6 @@ import json
|
|
|
19
19
|
|
|
20
20
|
from ._gui_expand import expand_gui_row
|
|
21
21
|
from ._parser import OqlLine, OqlScript
|
|
22
|
-
from .testtoon_models import ToonSection, ToonScript
|
|
23
|
-
from .testtoon_parser import parse_testtoon
|
|
24
|
-
|
|
25
22
|
# Backward compatibility: re-export classes that were moved
|
|
26
23
|
from .testtoon_models import ToonSection, ToonScript # noqa: F401
|
|
27
24
|
from .testtoon_parser import parse_testtoon # noqa: F401
|
|
@@ -110,9 +107,38 @@ def _append_api_asserts(row: dict, lines: list[OqlLine], line_num: int) -> int:
|
|
|
110
107
|
return line_num
|
|
111
108
|
|
|
112
109
|
|
|
113
|
-
def
|
|
110
|
+
def _append_api_captures(
|
|
111
|
+
row: dict,
|
|
112
|
+
api_step: int,
|
|
113
|
+
capture_rows: list[dict],
|
|
114
|
+
lines: list[OqlLine],
|
|
115
|
+
line_num: int,
|
|
116
|
+
) -> int:
|
|
117
|
+
"""Append captures assigned to an API row by its 1-based index or name."""
|
|
118
|
+
step_name = str(row.get('name', '')).strip()
|
|
119
|
+
for capture in capture_rows:
|
|
120
|
+
target = str(capture.get('step', '')).strip()
|
|
121
|
+
if target != str(api_step) and (not step_name or target != step_name):
|
|
122
|
+
continue
|
|
123
|
+
var_name = str(capture.get('var', '')).strip()
|
|
124
|
+
from_path = str(capture.get('from', '')).strip()
|
|
125
|
+
if not var_name or not from_path:
|
|
126
|
+
continue
|
|
127
|
+
args = f'{var_name} FROM "{from_path}"'
|
|
128
|
+
line_num = _append_raw_command(lines, line_num, 'CAPTURE', args)
|
|
129
|
+
return line_num
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _expand_api(
|
|
133
|
+
section: ToonSection,
|
|
134
|
+
lines: list[OqlLine],
|
|
135
|
+
line_num: int,
|
|
136
|
+
*,
|
|
137
|
+
capture_rows: list[dict] | None = None,
|
|
138
|
+
step_offset: int = 0,
|
|
139
|
+
) -> int:
|
|
114
140
|
"""Expand API section → API + ASSERT_STATUS commands."""
|
|
115
|
-
for row in section.rows:
|
|
141
|
+
for row_index, row in enumerate(section.rows, 1):
|
|
116
142
|
method = row.get('method', 'GET')
|
|
117
143
|
endpoint = row.get('endpoint', '/')
|
|
118
144
|
body = row.get('body')
|
|
@@ -127,6 +153,13 @@ def _expand_api(section: ToonSection, lines: list[OqlLine], line_num: int) -> in
|
|
|
127
153
|
raw = f'API {method} "{endpoint}"{body_str}'
|
|
128
154
|
lines.append(OqlLine(number=line_num, command='API', args=f'{method} "{endpoint}"{body_str}', raw=raw))
|
|
129
155
|
line_num = _append_api_asserts(row, lines, line_num + 1)
|
|
156
|
+
line_num = _append_api_captures(
|
|
157
|
+
row,
|
|
158
|
+
step_offset + row_index,
|
|
159
|
+
capture_rows or [],
|
|
160
|
+
lines,
|
|
161
|
+
line_num,
|
|
162
|
+
)
|
|
130
163
|
return line_num
|
|
131
164
|
|
|
132
165
|
|
|
@@ -649,8 +682,29 @@ def testtoon_to_oql(text: str, filename: str = "<string>") -> OqlScript:
|
|
|
649
682
|
toon = parse_testtoon(text, filename)
|
|
650
683
|
lines: list[OqlLine] = []
|
|
651
684
|
line_num = 1
|
|
685
|
+
api_step_count = 0
|
|
686
|
+
capture_rows = [
|
|
687
|
+
row
|
|
688
|
+
for section in toon.sections
|
|
689
|
+
if section.type == 'CAPTURE'
|
|
690
|
+
for row in section.rows
|
|
691
|
+
]
|
|
652
692
|
|
|
653
693
|
for section in toon.sections:
|
|
694
|
+
if section.type == 'CAPTURE':
|
|
695
|
+
# Captures must run while the referenced API response is still the
|
|
696
|
+
# current response, so API expansion interleaves them above.
|
|
697
|
+
continue
|
|
698
|
+
if section.type == 'API':
|
|
699
|
+
line_num = _expand_api(
|
|
700
|
+
section,
|
|
701
|
+
lines,
|
|
702
|
+
line_num,
|
|
703
|
+
capture_rows=capture_rows,
|
|
704
|
+
step_offset=api_step_count,
|
|
705
|
+
)
|
|
706
|
+
api_step_count += len(section.rows)
|
|
707
|
+
continue
|
|
654
708
|
expander = _SECTION_EXPANDERS.get(section.type, _expand_generic)
|
|
655
709
|
line_num = expander(section, lines, line_num)
|
|
656
710
|
|
|
@@ -49,7 +49,14 @@ def _apply_captures(step, result: StepResult, ctx: ExecutionContext) -> None:
|
|
|
49
49
|
return
|
|
50
50
|
payload = (result.details or {}).get("payload", {})
|
|
51
51
|
for capture in captures:
|
|
52
|
-
|
|
52
|
+
# API capture paths are relative to the response body (`response.data.id`
|
|
53
|
+
# is normalized to `data.id`). Other executors expose their result fields
|
|
54
|
+
# directly. Fall back to the full envelope so API status/header captures
|
|
55
|
+
# remain possible.
|
|
56
|
+
capture_root = payload.get("data", {}) if getattr(step, "kind", "") == "api" else payload
|
|
57
|
+
value = navigate(capture_root, capture.from_path)
|
|
58
|
+
if value is None and capture_root is not payload:
|
|
59
|
+
value = navigate(payload, capture.from_path)
|
|
53
60
|
if value is None:
|
|
54
61
|
ctx.warnings.append(
|
|
55
62
|
f"capture {capture.var_name!r} from {capture.from_path!r}: path not found"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testql
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.65
|
|
4
4
|
Summary: TestQL — Multi-DSL Test Platform: TestTOON / NL / SQL / Proto / GraphQL adapters with Unified IR, generator engine, and meta-testing
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: <3.14,>=3.11
|
|
@@ -62,10 +62,10 @@ Dynamic: license-file
|
|
|
62
62
|
## AI Cost Tracking
|
|
63
63
|
|
|
64
64
|
   
|
|
65
|
-
  
|
|
66
66
|
|
|
67
|
-
- 🤖 **LLM usage:** $
|
|
68
|
-
- 👤 **Human dev:** ~$
|
|
67
|
+
- 🤖 **LLM usage:** $30.6010 (133 commits)
|
|
68
|
+
- 👤 **Human dev:** ~$7576 (75.8h @ $100/h, 30min dedup)
|
|
69
69
|
|
|
70
70
|
Generated on 2026-07-20 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
71
71
|
|
|
@@ -294,6 +294,77 @@ class TestGuiExecution:
|
|
|
294
294
|
assert interpreter.results[-1].status.value == "failed"
|
|
295
295
|
assert elapsed < 2.0
|
|
296
296
|
|
|
297
|
+
def test_gui_assert_value_polls_until_async_value_appears(self, interpreter):
|
|
298
|
+
"""GUI_ASSERT_VALUE waits for an asynchronously-updated form value."""
|
|
299
|
+
from testql.interpreter._parser import OqlLine
|
|
300
|
+
|
|
301
|
+
interpreter.dry_run = False
|
|
302
|
+
interpreter._gui_driver = "playwright"
|
|
303
|
+
|
|
304
|
+
class _Locator:
|
|
305
|
+
def __init__(self):
|
|
306
|
+
self.reads = 0
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def first(self):
|
|
310
|
+
return self
|
|
311
|
+
|
|
312
|
+
def input_value(self):
|
|
313
|
+
self.reads += 1
|
|
314
|
+
return "Analizowanie…" if self.reads < 3 else '{"kind":"organization_status"}'
|
|
315
|
+
|
|
316
|
+
class _AsyncPage:
|
|
317
|
+
def __init__(self):
|
|
318
|
+
self.value_locator = _Locator()
|
|
319
|
+
|
|
320
|
+
def locator(self, selector):
|
|
321
|
+
return self.value_locator
|
|
322
|
+
|
|
323
|
+
interpreter._gui_page = _AsyncPage()
|
|
324
|
+
interpreter.timeout_ms = 3000
|
|
325
|
+
line = OqlLine(
|
|
326
|
+
number=1, command="GUI_ASSERT_VALUE",
|
|
327
|
+
args='"#llmIntentJson" CONTAINS "organization_status"',
|
|
328
|
+
raw='GUI_ASSERT_VALUE "#llmIntentJson" CONTAINS "organization_status"',
|
|
329
|
+
)
|
|
330
|
+
interpreter._cmd_gui_assert_value(line.args, line)
|
|
331
|
+
|
|
332
|
+
assert interpreter.results[-1].status.value == "passed"
|
|
333
|
+
assert interpreter._gui_page.value_locator.reads >= 3
|
|
334
|
+
|
|
335
|
+
def test_gui_assert_value_fails_bounded_when_value_never_appears(self, interpreter):
|
|
336
|
+
"""A stable wrong form value fails within the operation timeout."""
|
|
337
|
+
import time
|
|
338
|
+
from testql.interpreter._parser import OqlLine
|
|
339
|
+
|
|
340
|
+
interpreter.dry_run = False
|
|
341
|
+
interpreter._gui_driver = "playwright"
|
|
342
|
+
|
|
343
|
+
class _Locator:
|
|
344
|
+
@property
|
|
345
|
+
def first(self):
|
|
346
|
+
return self
|
|
347
|
+
|
|
348
|
+
def input_value(self):
|
|
349
|
+
return "Analizowanie…"
|
|
350
|
+
|
|
351
|
+
class _StablePage:
|
|
352
|
+
def locator(self, selector):
|
|
353
|
+
return _Locator()
|
|
354
|
+
|
|
355
|
+
interpreter._gui_page = _StablePage()
|
|
356
|
+
interpreter.timeout_ms = 300
|
|
357
|
+
line = OqlLine(
|
|
358
|
+
number=1, command="GUI_ASSERT_VALUE",
|
|
359
|
+
args='"#x" CONTAINS "done"', raw='GUI_ASSERT_VALUE "#x" CONTAINS "done"',
|
|
360
|
+
)
|
|
361
|
+
started = time.monotonic()
|
|
362
|
+
interpreter._cmd_gui_assert_value(line.args, line)
|
|
363
|
+
elapsed = time.monotonic() - started
|
|
364
|
+
|
|
365
|
+
assert interpreter.results[-1].status.value == "failed"
|
|
366
|
+
assert elapsed < 2.0
|
|
367
|
+
|
|
297
368
|
def test_gui_start_no_args_error(self, interpreter):
|
|
298
369
|
"""Test GUI_START without arguments."""
|
|
299
370
|
interpreter.dry_run = False
|
|
@@ -103,6 +103,34 @@ class TestTestTOONExpansion:
|
|
|
103
103
|
assert script.lines[3].command == "ASSERT_STATUS"
|
|
104
104
|
assert "201" in script.lines[3].args
|
|
105
105
|
|
|
106
|
+
def test_capture_is_interleaved_after_referenced_api_step(self):
|
|
107
|
+
source = (
|
|
108
|
+
"API[2]{method, endpoint, status}:\n"
|
|
109
|
+
" POST, /devices, 201\n"
|
|
110
|
+
" GET, /devices/${device_id}, 200\n"
|
|
111
|
+
"CAPTURE[1]{step, var, from}:\n"
|
|
112
|
+
" 1, device_id, data.id\n"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
script = _testtoon_to_oql(source, "test.testql.toon.yaml")
|
|
116
|
+
|
|
117
|
+
assert [line.command for line in script.lines] == [
|
|
118
|
+
"API", "ASSERT_STATUS", "CAPTURE", "API", "ASSERT_STATUS",
|
|
119
|
+
]
|
|
120
|
+
assert script.lines[2].args == 'device_id FROM "data.id"'
|
|
121
|
+
|
|
122
|
+
def test_capture_can_reference_named_api_step(self):
|
|
123
|
+
source = (
|
|
124
|
+
"API[1]{method, endpoint, status, name}:\n"
|
|
125
|
+
" POST, /devices, 201, createDevice\n"
|
|
126
|
+
"CAPTURE[1]{step, var, from}:\n"
|
|
127
|
+
" createDevice, device_id, data.id\n"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
script = _testtoon_to_oql(source, "test.testql.toon.yaml")
|
|
131
|
+
|
|
132
|
+
assert script.lines[-1].command == "CAPTURE"
|
|
133
|
+
|
|
106
134
|
def test_encoder_expansion(self):
|
|
107
135
|
source = (
|
|
108
136
|
"ENCODER[2]{action, target, value, wait_ms}:\n"
|
|
@@ -227,6 +255,22 @@ class TestOqlInterpreter:
|
|
|
227
255
|
assert result.ok
|
|
228
256
|
assert result.passed >= 1
|
|
229
257
|
|
|
258
|
+
def test_api_interpolates_captured_variable_in_url(self, monkeypatch):
|
|
259
|
+
requested_urls = []
|
|
260
|
+
|
|
261
|
+
def fake_request(_self, _method, url, _body):
|
|
262
|
+
requested_urls.append(url)
|
|
263
|
+
return 200, {"ok": True}, {}
|
|
264
|
+
|
|
265
|
+
monkeypatch.setattr(OqlInterpreter, "_do_http_request_with_retry", fake_request)
|
|
266
|
+
interp = OqlInterpreter(quiet=True, api_url="http://localhost:8101")
|
|
267
|
+
interp.vars.set("device_id", "dev-7")
|
|
268
|
+
|
|
269
|
+
result = interp.run('API GET "/devices/${device_id}"', "test.tql")
|
|
270
|
+
|
|
271
|
+
assert result.ok
|
|
272
|
+
assert requested_urls == ["http://localhost:8101/devices/dev-7"]
|
|
273
|
+
|
|
230
274
|
def test_set_get(self):
|
|
231
275
|
source = 'SET foo "bar"\nGET foo'
|
|
232
276
|
interp = OqlInterpreter(dry_run=True, quiet=True)
|
|
@@ -77,6 +77,26 @@ class TestShellCaptures:
|
|
|
77
77
|
assert "hello" in runner.ctx.vars.get("out")
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
class TestApiCaptures:
|
|
81
|
+
def test_capture_is_relative_to_response_body(self, monkeypatch):
|
|
82
|
+
from testql.ir_runner.executors import api
|
|
83
|
+
|
|
84
|
+
monkeypatch.setattr(api, "_do_request", lambda *args: (201, {"data": {"id": "sc-1"}}))
|
|
85
|
+
plan = _plan(
|
|
86
|
+
ApiStep(
|
|
87
|
+
method="POST",
|
|
88
|
+
path="/api/v3/scenarios",
|
|
89
|
+
captures=[Capture(var_name="scenario_id", from_path="data.id")],
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
runner = IRRunner()
|
|
93
|
+
|
|
94
|
+
result = runner.run(plan)
|
|
95
|
+
|
|
96
|
+
assert result.ok
|
|
97
|
+
assert runner.ctx.vars.get("scenario_id") == "sc-1"
|
|
98
|
+
|
|
99
|
+
|
|
80
100
|
# ── Missing path → warning, not failure ─────────────────────────────────────
|
|
81
101
|
|
|
82
102
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|