superqode 0.1.5__py3-none-any.whl
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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core QE Skills - Essential quality engineering skills.
|
|
3
|
+
|
|
4
|
+
Skills included:
|
|
5
|
+
- testability-scoring: Assess code testability
|
|
6
|
+
- tdd-london-chicago: TDD methodology guidance
|
|
7
|
+
- api-testing-patterns: API testing best practices
|
|
8
|
+
- accessibility-testing: A11y testing patterns
|
|
9
|
+
- shift-left-testing: Early testing strategies
|
|
10
|
+
- chaos-engineering-resilience: Chaos testing
|
|
11
|
+
- visual-testing-advanced: Visual regression
|
|
12
|
+
- compliance-testing: Regulatory compliance
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import ast
|
|
18
|
+
|
|
19
|
+
from .base import Skill, SkillConfig, SkillResult
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestabilityScoring(Skill):
|
|
23
|
+
"""
|
|
24
|
+
Testability Scoring Skill.
|
|
25
|
+
|
|
26
|
+
Assesses code testability before writing tests,
|
|
27
|
+
identifying areas that need refactoring for better testing.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
NAME = "testability-scoring"
|
|
31
|
+
DISPLAY_NAME = "Testability Scoring"
|
|
32
|
+
DESCRIPTION = "Pre-test code testability assessment"
|
|
33
|
+
CATEGORY = "analysis"
|
|
34
|
+
TAGS = ["testability", "code-quality", "pre-test"]
|
|
35
|
+
|
|
36
|
+
# Scoring factors
|
|
37
|
+
FACTORS = {
|
|
38
|
+
"cyclomatic_complexity": {"weight": 0.2, "ideal": 5, "max": 20},
|
|
39
|
+
"dependency_count": {"weight": 0.15, "ideal": 3, "max": 10},
|
|
40
|
+
"method_length": {"weight": 0.15, "ideal": 20, "max": 50},
|
|
41
|
+
"nesting_depth": {"weight": 0.15, "ideal": 2, "max": 5},
|
|
42
|
+
"global_state": {"weight": 0.15, "ideal": 0, "max": 3},
|
|
43
|
+
"side_effects": {"weight": 0.1, "ideal": 0, "max": 5},
|
|
44
|
+
"documentation": {"weight": 0.1, "ideal": 1, "max": 1},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
48
|
+
"""
|
|
49
|
+
Score code testability.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
file_path: Path to file to analyze
|
|
53
|
+
code: Code string to analyze (alternative)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
SkillResult with testability score
|
|
57
|
+
"""
|
|
58
|
+
file_path = kwargs.get("file_path")
|
|
59
|
+
code = kwargs.get("code")
|
|
60
|
+
|
|
61
|
+
if file_path:
|
|
62
|
+
code = Path(file_path).read_text()
|
|
63
|
+
|
|
64
|
+
if not code:
|
|
65
|
+
return SkillResult(skill_name=self.NAME, success=False, errors=["No code provided"])
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
tree = ast.parse(code)
|
|
69
|
+
except SyntaxError as e:
|
|
70
|
+
return SkillResult(skill_name=self.NAME, success=False, errors=[f"Syntax error: {e}"])
|
|
71
|
+
|
|
72
|
+
# Analyze testability
|
|
73
|
+
scores = {}
|
|
74
|
+
recommendations = []
|
|
75
|
+
|
|
76
|
+
for node in ast.walk(tree):
|
|
77
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
78
|
+
func_score = self._score_function(node, code)
|
|
79
|
+
scores[node.name] = func_score
|
|
80
|
+
|
|
81
|
+
if func_score["overall"] < 0.7:
|
|
82
|
+
recommendations.append(
|
|
83
|
+
f"Function '{node.name}' has low testability ({func_score['overall']:.2f}). "
|
|
84
|
+
f"Consider refactoring."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Calculate overall score
|
|
88
|
+
overall = sum(s["overall"] for s in scores.values()) / len(scores) if scores else 0
|
|
89
|
+
|
|
90
|
+
return SkillResult(
|
|
91
|
+
skill_name=self.NAME,
|
|
92
|
+
success=True,
|
|
93
|
+
output=f"Testability Score: {overall:.2f}/1.00",
|
|
94
|
+
metrics={
|
|
95
|
+
"overall_score": overall,
|
|
96
|
+
"function_scores": scores,
|
|
97
|
+
"recommendations": recommendations,
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def _score_function(self, node: ast.FunctionDef, code: str) -> Dict[str, float]:
|
|
102
|
+
"""Score a single function."""
|
|
103
|
+
scores = {}
|
|
104
|
+
|
|
105
|
+
# Cyclomatic complexity
|
|
106
|
+
complexity = self._calculate_complexity(node)
|
|
107
|
+
scores["complexity"] = self._normalize(complexity, self.FACTORS["cyclomatic_complexity"])
|
|
108
|
+
|
|
109
|
+
# Method length
|
|
110
|
+
lines = len(code.splitlines())
|
|
111
|
+
scores["length"] = self._normalize(lines, self.FACTORS["method_length"])
|
|
112
|
+
|
|
113
|
+
# Nesting depth
|
|
114
|
+
depth = self._calculate_nesting(node)
|
|
115
|
+
scores["nesting"] = self._normalize(depth, self.FACTORS["nesting_depth"])
|
|
116
|
+
|
|
117
|
+
# Calculate overall
|
|
118
|
+
overall = sum(scores.values()) / len(scores)
|
|
119
|
+
scores["overall"] = overall
|
|
120
|
+
|
|
121
|
+
return scores
|
|
122
|
+
|
|
123
|
+
def _calculate_complexity(self, node: ast.AST) -> int:
|
|
124
|
+
"""Calculate cyclomatic complexity."""
|
|
125
|
+
complexity = 1
|
|
126
|
+
for child in ast.walk(node):
|
|
127
|
+
if isinstance(child, (ast.If, ast.While, ast.For, ast.ExceptHandler)):
|
|
128
|
+
complexity += 1
|
|
129
|
+
elif isinstance(child, (ast.And, ast.Or)):
|
|
130
|
+
complexity += 1
|
|
131
|
+
return complexity
|
|
132
|
+
|
|
133
|
+
def _calculate_nesting(self, node: ast.AST, depth: int = 0) -> int:
|
|
134
|
+
"""Calculate max nesting depth."""
|
|
135
|
+
max_depth = depth
|
|
136
|
+
for child in ast.iter_child_nodes(node):
|
|
137
|
+
if isinstance(child, (ast.If, ast.While, ast.For, ast.With, ast.Try)):
|
|
138
|
+
child_depth = self._calculate_nesting(child, depth + 1)
|
|
139
|
+
max_depth = max(max_depth, child_depth)
|
|
140
|
+
else:
|
|
141
|
+
child_depth = self._calculate_nesting(child, depth)
|
|
142
|
+
max_depth = max(max_depth, child_depth)
|
|
143
|
+
return max_depth
|
|
144
|
+
|
|
145
|
+
def _normalize(self, value: float, factor: Dict[str, Any]) -> float:
|
|
146
|
+
"""Normalize a value to 0-1 score (1 is best)."""
|
|
147
|
+
ideal = factor["ideal"]
|
|
148
|
+
max_val = factor["max"]
|
|
149
|
+
|
|
150
|
+
if value <= ideal:
|
|
151
|
+
return 1.0
|
|
152
|
+
elif value >= max_val:
|
|
153
|
+
return 0.0
|
|
154
|
+
else:
|
|
155
|
+
return 1.0 - (value - ideal) / (max_val - ideal)
|
|
156
|
+
|
|
157
|
+
def get_prompt(self) -> str:
|
|
158
|
+
return """You are an expert in code testability analysis.
|
|
159
|
+
|
|
160
|
+
Evaluate code for testability by considering:
|
|
161
|
+
1. Cyclomatic complexity (lower is better)
|
|
162
|
+
2. Dependency injection (prefer constructor injection)
|
|
163
|
+
3. Pure functions (no side effects)
|
|
164
|
+
4. Single responsibility (one purpose per function)
|
|
165
|
+
5. Minimal global state
|
|
166
|
+
6. Clear interfaces
|
|
167
|
+
|
|
168
|
+
Provide specific recommendations for improving testability."""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TDDLondonChicago(Skill):
|
|
172
|
+
"""
|
|
173
|
+
TDD London/Chicago Skill.
|
|
174
|
+
|
|
175
|
+
Guides TDD implementation using both London (mockist)
|
|
176
|
+
and Chicago (classicist) schools.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
NAME = "tdd-london-chicago"
|
|
180
|
+
DISPLAY_NAME = "TDD London/Chicago"
|
|
181
|
+
DESCRIPTION = "TDD methodology guidance for both schools"
|
|
182
|
+
CATEGORY = "methodology"
|
|
183
|
+
TAGS = ["tdd", "testing", "methodology", "best-practices"]
|
|
184
|
+
|
|
185
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
186
|
+
"""
|
|
187
|
+
Provide TDD guidance.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
style: "london" or "chicago"
|
|
191
|
+
feature: Feature to implement
|
|
192
|
+
context: Additional context
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
SkillResult with TDD guidance
|
|
196
|
+
"""
|
|
197
|
+
style = kwargs.get("style", "chicago")
|
|
198
|
+
feature = kwargs.get("feature", "")
|
|
199
|
+
|
|
200
|
+
if style == "london":
|
|
201
|
+
guidance = self._london_style_guidance(feature)
|
|
202
|
+
else:
|
|
203
|
+
guidance = self._chicago_style_guidance(feature)
|
|
204
|
+
|
|
205
|
+
return SkillResult(
|
|
206
|
+
skill_name=self.NAME, success=True, output=guidance, metrics={"style": style}
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def _london_style_guidance(self, feature: str) -> str:
|
|
210
|
+
return f"""# TDD London Style (Mockist) for: {feature}
|
|
211
|
+
|
|
212
|
+
## Approach
|
|
213
|
+
The London school focuses on behavior and uses mocks extensively.
|
|
214
|
+
|
|
215
|
+
## RED Phase
|
|
216
|
+
1. Write a failing test that specifies expected behavior
|
|
217
|
+
2. Mock all collaborators
|
|
218
|
+
3. Focus on the unit's interaction with dependencies
|
|
219
|
+
|
|
220
|
+
## GREEN Phase
|
|
221
|
+
1. Implement minimum code to pass
|
|
222
|
+
2. Focus on correct message passing
|
|
223
|
+
3. Don't worry about implementation details
|
|
224
|
+
|
|
225
|
+
## REFACTOR Phase
|
|
226
|
+
1. Improve design while tests pass
|
|
227
|
+
2. Extract interfaces if needed
|
|
228
|
+
3. Consider dependency injection
|
|
229
|
+
|
|
230
|
+
## Key Principles
|
|
231
|
+
- Test behavior, not implementation
|
|
232
|
+
- Mock external dependencies
|
|
233
|
+
- Verify interactions
|
|
234
|
+
- One mock per test (usually)
|
|
235
|
+
|
|
236
|
+
## Example Pattern
|
|
237
|
+
```python
|
|
238
|
+
def test_should_notify_when_order_placed():
|
|
239
|
+
# Arrange
|
|
240
|
+
notifier = Mock(spec=Notifier)
|
|
241
|
+
order_service = OrderService(notifier=notifier)
|
|
242
|
+
|
|
243
|
+
# Act
|
|
244
|
+
order_service.place_order(order)
|
|
245
|
+
|
|
246
|
+
# Assert
|
|
247
|
+
notifier.send.assert_called_once_with(order.customer_email)
|
|
248
|
+
```
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
def _chicago_style_guidance(self, feature: str) -> str:
|
|
252
|
+
return f"""# TDD Chicago Style (Classicist) for: {feature}
|
|
253
|
+
|
|
254
|
+
## Approach
|
|
255
|
+
The Chicago school focuses on state and uses real objects.
|
|
256
|
+
|
|
257
|
+
## RED Phase
|
|
258
|
+
1. Write a failing test with real assertions
|
|
259
|
+
2. Test the final state/outcome
|
|
260
|
+
3. Use real collaborators when possible
|
|
261
|
+
|
|
262
|
+
## GREEN Phase
|
|
263
|
+
1. Implement the simplest thing that works
|
|
264
|
+
2. Let the design emerge
|
|
265
|
+
3. Triangulate with more tests
|
|
266
|
+
|
|
267
|
+
## REFACTOR Phase
|
|
268
|
+
1. Remove duplication
|
|
269
|
+
2. Improve naming
|
|
270
|
+
3. Extract methods/classes
|
|
271
|
+
|
|
272
|
+
## Key Principles
|
|
273
|
+
- Test state, not interactions
|
|
274
|
+
- Use real objects (not mocks)
|
|
275
|
+
- Triangulate to generalize
|
|
276
|
+
- Let design emerge from tests
|
|
277
|
+
|
|
278
|
+
## Example Pattern
|
|
279
|
+
```python
|
|
280
|
+
def test_order_total_includes_tax():
|
|
281
|
+
# Arrange
|
|
282
|
+
order = Order()
|
|
283
|
+
order.add_item(Product("Widget", price=100))
|
|
284
|
+
|
|
285
|
+
# Act
|
|
286
|
+
total = order.calculate_total(tax_rate=0.1)
|
|
287
|
+
|
|
288
|
+
# Assert
|
|
289
|
+
assert total == 110 # Price + 10% tax
|
|
290
|
+
```
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
def get_prompt(self) -> str:
|
|
294
|
+
return """You are a TDD expert familiar with both London and Chicago schools.
|
|
295
|
+
|
|
296
|
+
London School (Mockist):
|
|
297
|
+
- Focus on behavior and interactions
|
|
298
|
+
- Heavy use of mocks
|
|
299
|
+
- Outside-in development
|
|
300
|
+
- Verify messages between objects
|
|
301
|
+
|
|
302
|
+
Chicago School (Classicist):
|
|
303
|
+
- Focus on state and outcomes
|
|
304
|
+
- Use real objects
|
|
305
|
+
- Inside-out development
|
|
306
|
+
- Assert on final state
|
|
307
|
+
|
|
308
|
+
Guide the user through RED/GREEN/REFACTOR cycles."""
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class APITestingPatterns(Skill):
|
|
312
|
+
"""
|
|
313
|
+
API Testing Patterns Skill.
|
|
314
|
+
|
|
315
|
+
Provides patterns for comprehensive API testing.
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
NAME = "api-testing-patterns"
|
|
319
|
+
DISPLAY_NAME = "API Testing Patterns"
|
|
320
|
+
DESCRIPTION = "API testing best practices and patterns"
|
|
321
|
+
CATEGORY = "testing"
|
|
322
|
+
TAGS = ["api", "testing", "rest", "patterns"]
|
|
323
|
+
|
|
324
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
325
|
+
"""
|
|
326
|
+
Provide API testing patterns.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
endpoint: API endpoint to test
|
|
330
|
+
method: HTTP method
|
|
331
|
+
pattern: Specific pattern to apply
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
SkillResult with testing patterns
|
|
335
|
+
"""
|
|
336
|
+
endpoint = kwargs.get("endpoint", "/api/resource")
|
|
337
|
+
method = kwargs.get("method", "GET")
|
|
338
|
+
|
|
339
|
+
patterns = self._get_patterns(endpoint, method)
|
|
340
|
+
|
|
341
|
+
return SkillResult(
|
|
342
|
+
skill_name=self.NAME,
|
|
343
|
+
success=True,
|
|
344
|
+
output=patterns,
|
|
345
|
+
metrics={"endpoint": endpoint, "method": method},
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
def _get_patterns(self, endpoint: str, method: str) -> str:
|
|
349
|
+
return f"""# API Testing Patterns for {method} {endpoint}
|
|
350
|
+
|
|
351
|
+
## 1. Happy Path Testing
|
|
352
|
+
Test successful scenarios with valid inputs.
|
|
353
|
+
|
|
354
|
+
## 2. Validation Testing
|
|
355
|
+
- Missing required fields
|
|
356
|
+
- Invalid data types
|
|
357
|
+
- Boundary values
|
|
358
|
+
- Malformed requests
|
|
359
|
+
|
|
360
|
+
## 3. Authentication/Authorization
|
|
361
|
+
- Missing auth token
|
|
362
|
+
- Invalid token
|
|
363
|
+
- Expired token
|
|
364
|
+
- Insufficient permissions
|
|
365
|
+
|
|
366
|
+
## 4. Error Handling
|
|
367
|
+
- 400 Bad Request
|
|
368
|
+
- 401 Unauthorized
|
|
369
|
+
- 403 Forbidden
|
|
370
|
+
- 404 Not Found
|
|
371
|
+
- 500 Internal Server Error
|
|
372
|
+
|
|
373
|
+
## 5. Performance Testing
|
|
374
|
+
- Response time < 200ms
|
|
375
|
+
- Concurrent requests
|
|
376
|
+
- Rate limiting
|
|
377
|
+
|
|
378
|
+
## 6. Contract Testing
|
|
379
|
+
- Schema validation
|
|
380
|
+
- Field presence
|
|
381
|
+
- Data types
|
|
382
|
+
- Nullable fields
|
|
383
|
+
|
|
384
|
+
## Example Test Structure
|
|
385
|
+
```python
|
|
386
|
+
class Test{method.capitalize()}{endpoint.replace("/", "_").title()}:
|
|
387
|
+
|
|
388
|
+
def test_success_returns_200(self, client):
|
|
389
|
+
response = client.{method.lower()}("{endpoint}")
|
|
390
|
+
assert response.status_code == 200
|
|
391
|
+
|
|
392
|
+
def test_missing_auth_returns_401(self, client):
|
|
393
|
+
response = client.{method.lower()}("{endpoint}", headers={{}})
|
|
394
|
+
assert response.status_code == 401
|
|
395
|
+
|
|
396
|
+
def test_invalid_input_returns_400(self, client):
|
|
397
|
+
response = client.{method.lower()}("{endpoint}", json={{"invalid": True}})
|
|
398
|
+
assert response.status_code == 400
|
|
399
|
+
```
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
def get_prompt(self) -> str:
|
|
403
|
+
return """You are an API testing expert.
|
|
404
|
+
|
|
405
|
+
Key areas to cover:
|
|
406
|
+
1. Happy path scenarios
|
|
407
|
+
2. Error handling (4xx, 5xx)
|
|
408
|
+
3. Input validation
|
|
409
|
+
4. Authentication/authorization
|
|
410
|
+
5. Rate limiting
|
|
411
|
+
6. Pagination
|
|
412
|
+
7. Caching headers
|
|
413
|
+
8. Content negotiation
|
|
414
|
+
|
|
415
|
+
Provide specific test cases for each endpoint."""
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class AccessibilityTesting(Skill):
|
|
419
|
+
"""
|
|
420
|
+
Accessibility Testing Skill.
|
|
421
|
+
|
|
422
|
+
Guides A11y testing for WCAG compliance.
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
NAME = "accessibility-testing"
|
|
426
|
+
DISPLAY_NAME = "Accessibility Testing"
|
|
427
|
+
DESCRIPTION = "A11y testing patterns for WCAG compliance"
|
|
428
|
+
CATEGORY = "testing"
|
|
429
|
+
TAGS = ["accessibility", "a11y", "wcag", "testing"]
|
|
430
|
+
|
|
431
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
432
|
+
"""
|
|
433
|
+
Provide accessibility testing guidance.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
level: WCAG level (A, AA, AAA)
|
|
437
|
+
component: Component type to test
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
SkillResult with A11y guidance
|
|
441
|
+
"""
|
|
442
|
+
level = kwargs.get("level", "AA")
|
|
443
|
+
component = kwargs.get("component", "form")
|
|
444
|
+
|
|
445
|
+
guidance = self._get_guidance(level, component)
|
|
446
|
+
|
|
447
|
+
return SkillResult(
|
|
448
|
+
skill_name=self.NAME,
|
|
449
|
+
success=True,
|
|
450
|
+
output=guidance,
|
|
451
|
+
metrics={"level": level, "component": component},
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
def _get_guidance(self, level: str, component: str) -> str:
|
|
455
|
+
return f"""# Accessibility Testing for {component} (WCAG {level})
|
|
456
|
+
|
|
457
|
+
## Essential Checks
|
|
458
|
+
|
|
459
|
+
### 1. Keyboard Navigation
|
|
460
|
+
- All interactive elements focusable
|
|
461
|
+
- Logical focus order
|
|
462
|
+
- No keyboard traps
|
|
463
|
+
- Visible focus indicators
|
|
464
|
+
|
|
465
|
+
### 2. Screen Reader Compatibility
|
|
466
|
+
- Proper ARIA labels
|
|
467
|
+
- Meaningful alt text
|
|
468
|
+
- Correct heading structure
|
|
469
|
+
- Live regions for dynamic content
|
|
470
|
+
|
|
471
|
+
### 3. Color and Contrast
|
|
472
|
+
- 4.5:1 contrast for text (AA)
|
|
473
|
+
- 3:1 contrast for large text
|
|
474
|
+
- Information not conveyed by color alone
|
|
475
|
+
|
|
476
|
+
### 4. Form Accessibility
|
|
477
|
+
- Labels associated with inputs
|
|
478
|
+
- Error messages identified
|
|
479
|
+
- Required fields indicated
|
|
480
|
+
- Form validation accessible
|
|
481
|
+
|
|
482
|
+
## Testing Tools
|
|
483
|
+
- axe-core for automated testing
|
|
484
|
+
- NVDA/VoiceOver for screen reader testing
|
|
485
|
+
- Keyboard-only navigation testing
|
|
486
|
+
- Color contrast analyzers
|
|
487
|
+
|
|
488
|
+
## Example Test
|
|
489
|
+
```python
|
|
490
|
+
def test_form_has_accessible_labels():
|
|
491
|
+
page.goto("/form")
|
|
492
|
+
inputs = page.locator("input")
|
|
493
|
+
|
|
494
|
+
for input_elem in inputs.all():
|
|
495
|
+
# Check for associated label
|
|
496
|
+
label = page.locator(f'label[for="{{input_elem.get_attribute("id")}}"]')
|
|
497
|
+
assert label.count() > 0, f"Input missing label"
|
|
498
|
+
```
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
def get_prompt(self) -> str:
|
|
502
|
+
return """You are an accessibility expert.
|
|
503
|
+
|
|
504
|
+
Test for WCAG compliance:
|
|
505
|
+
1. Perceivable - content accessible to all senses
|
|
506
|
+
2. Operable - keyboard navigable
|
|
507
|
+
3. Understandable - clear and consistent
|
|
508
|
+
4. Robust - works with assistive technology
|
|
509
|
+
|
|
510
|
+
Use axe-core for automated testing."""
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class ShiftLeftTesting(Skill):
|
|
514
|
+
"""
|
|
515
|
+
Shift-Left Testing Skill.
|
|
516
|
+
|
|
517
|
+
Promotes early testing practices.
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
NAME = "shift-left-testing"
|
|
521
|
+
DISPLAY_NAME = "Shift-Left Testing"
|
|
522
|
+
DESCRIPTION = "Early testing strategies and practices"
|
|
523
|
+
CATEGORY = "methodology"
|
|
524
|
+
TAGS = ["shift-left", "early-testing", "prevention"]
|
|
525
|
+
|
|
526
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
527
|
+
"""
|
|
528
|
+
Provide shift-left testing guidance.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
phase: Development phase
|
|
532
|
+
context: Project context
|
|
533
|
+
|
|
534
|
+
Returns:
|
|
535
|
+
SkillResult with guidance
|
|
536
|
+
"""
|
|
537
|
+
phase = kwargs.get("phase", "design")
|
|
538
|
+
|
|
539
|
+
guidance = self._get_guidance(phase)
|
|
540
|
+
|
|
541
|
+
return SkillResult(
|
|
542
|
+
skill_name=self.NAME, success=True, output=guidance, metrics={"phase": phase}
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
def _get_guidance(self, phase: str) -> str:
|
|
546
|
+
return f"""# Shift-Left Testing for {phase.title()} Phase
|
|
547
|
+
|
|
548
|
+
## Core Principle
|
|
549
|
+
Find defects earlier when they're cheaper to fix.
|
|
550
|
+
|
|
551
|
+
## Activities by Phase
|
|
552
|
+
|
|
553
|
+
### Requirements Phase
|
|
554
|
+
- Requirements review
|
|
555
|
+
- Testability assessment
|
|
556
|
+
- Risk analysis
|
|
557
|
+
- Acceptance criteria definition
|
|
558
|
+
|
|
559
|
+
### Design Phase
|
|
560
|
+
- Design review
|
|
561
|
+
- API contract definition
|
|
562
|
+
- Test strategy creation
|
|
563
|
+
- Architecture testing
|
|
564
|
+
|
|
565
|
+
### Development Phase
|
|
566
|
+
- Unit testing (TDD)
|
|
567
|
+
- Static analysis
|
|
568
|
+
- Code review
|
|
569
|
+
- Pair programming
|
|
570
|
+
|
|
571
|
+
### Integration Phase
|
|
572
|
+
- Integration testing
|
|
573
|
+
- Contract testing
|
|
574
|
+
- Performance testing
|
|
575
|
+
- Security scanning
|
|
576
|
+
|
|
577
|
+
## Benefits
|
|
578
|
+
- 10x cheaper to fix bugs in design vs production
|
|
579
|
+
- Faster feedback loops
|
|
580
|
+
- Better code quality
|
|
581
|
+
- Reduced technical debt
|
|
582
|
+
|
|
583
|
+
## Implementation
|
|
584
|
+
1. Include QE in planning
|
|
585
|
+
2. Define test cases during design
|
|
586
|
+
3. Automate early and often
|
|
587
|
+
4. Continuous integration
|
|
588
|
+
5. Quality gates at each phase
|
|
589
|
+
"""
|
|
590
|
+
|
|
591
|
+
def get_prompt(self) -> str:
|
|
592
|
+
return """You are a shift-left testing advocate.
|
|
593
|
+
|
|
594
|
+
Key principles:
|
|
595
|
+
1. Test early and often
|
|
596
|
+
2. Prevention over detection
|
|
597
|
+
3. Collaboration between dev and QE
|
|
598
|
+
4. Automation from the start
|
|
599
|
+
5. Continuous feedback
|
|
600
|
+
|
|
601
|
+
Help integrate testing into every phase."""
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
class ChaosEngineeringResilience(Skill):
|
|
605
|
+
"""
|
|
606
|
+
Chaos Engineering Skill.
|
|
607
|
+
|
|
608
|
+
Guides chaos testing for system resilience.
|
|
609
|
+
"""
|
|
610
|
+
|
|
611
|
+
NAME = "chaos-engineering-resilience"
|
|
612
|
+
DISPLAY_NAME = "Chaos Engineering"
|
|
613
|
+
DESCRIPTION = "Chaos testing for system resilience"
|
|
614
|
+
CATEGORY = "testing"
|
|
615
|
+
TAGS = ["chaos", "resilience", "reliability"]
|
|
616
|
+
|
|
617
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
618
|
+
"""
|
|
619
|
+
Provide chaos engineering guidance.
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
target: System component to test
|
|
623
|
+
experiment: Type of chaos experiment
|
|
624
|
+
|
|
625
|
+
Returns:
|
|
626
|
+
SkillResult with chaos testing guidance
|
|
627
|
+
"""
|
|
628
|
+
target = kwargs.get("target", "service")
|
|
629
|
+
experiment = kwargs.get("experiment", "latency")
|
|
630
|
+
|
|
631
|
+
guidance = self._get_guidance(target, experiment)
|
|
632
|
+
|
|
633
|
+
return SkillResult(
|
|
634
|
+
skill_name=self.NAME,
|
|
635
|
+
success=True,
|
|
636
|
+
output=guidance,
|
|
637
|
+
metrics={"target": target, "experiment": experiment},
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
def _get_guidance(self, target: str, experiment: str) -> str:
|
|
641
|
+
return f"""# Chaos Engineering for {target}
|
|
642
|
+
|
|
643
|
+
## Experiment: {experiment.title()} Injection
|
|
644
|
+
|
|
645
|
+
### Hypothesis
|
|
646
|
+
"When {experiment} is introduced to {target}, the system should..."
|
|
647
|
+
|
|
648
|
+
### Steady State
|
|
649
|
+
Define normal behavior metrics:
|
|
650
|
+
- Response time < 200ms
|
|
651
|
+
- Error rate < 0.1%
|
|
652
|
+
- Throughput > 1000 req/s
|
|
653
|
+
|
|
654
|
+
### Experiment Types
|
|
655
|
+
1. **Latency**: Add network delays
|
|
656
|
+
2. **Failure**: Kill processes/containers
|
|
657
|
+
3. **Resource**: CPU/memory stress
|
|
658
|
+
4. **Network**: Partition, packet loss
|
|
659
|
+
5. **Dependency**: External service failure
|
|
660
|
+
|
|
661
|
+
### Safety Measures
|
|
662
|
+
- Start in staging environment
|
|
663
|
+
- Use canary deployments
|
|
664
|
+
- Have rollback plan
|
|
665
|
+
- Monitor closely
|
|
666
|
+
- Limit blast radius
|
|
667
|
+
|
|
668
|
+
### Tools
|
|
669
|
+
- Chaos Monkey (Netflix)
|
|
670
|
+
- Gremlin
|
|
671
|
+
- Litmus
|
|
672
|
+
- Chaos Toolkit
|
|
673
|
+
|
|
674
|
+
### Example Experiment
|
|
675
|
+
```yaml
|
|
676
|
+
experiment:
|
|
677
|
+
name: "{target}-{experiment}-test"
|
|
678
|
+
hypothesis:
|
|
679
|
+
title: "Service remains available under {experiment}"
|
|
680
|
+
method:
|
|
681
|
+
- type: action
|
|
682
|
+
name: inject-{experiment}
|
|
683
|
+
provider:
|
|
684
|
+
type: python
|
|
685
|
+
module: chaoslib.{experiment}
|
|
686
|
+
rollbacks:
|
|
687
|
+
- type: action
|
|
688
|
+
name: restore-normal
|
|
689
|
+
```
|
|
690
|
+
"""
|
|
691
|
+
|
|
692
|
+
def get_prompt(self) -> str:
|
|
693
|
+
return """You are a chaos engineering expert.
|
|
694
|
+
|
|
695
|
+
Guide chaos experiments:
|
|
696
|
+
1. Define steady state
|
|
697
|
+
2. Form hypothesis
|
|
698
|
+
3. Run experiment
|
|
699
|
+
4. Measure impact
|
|
700
|
+
5. Improve resilience
|
|
701
|
+
|
|
702
|
+
Always prioritize safety and have rollback plans."""
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
class VisualTestingAdvanced(Skill):
|
|
706
|
+
"""
|
|
707
|
+
Visual Testing Skill.
|
|
708
|
+
|
|
709
|
+
Advanced visual regression testing patterns.
|
|
710
|
+
"""
|
|
711
|
+
|
|
712
|
+
NAME = "visual-testing-advanced"
|
|
713
|
+
DISPLAY_NAME = "Visual Testing Advanced"
|
|
714
|
+
DESCRIPTION = "Advanced visual comparison techniques"
|
|
715
|
+
CATEGORY = "testing"
|
|
716
|
+
TAGS = ["visual", "regression", "ui", "screenshot"]
|
|
717
|
+
|
|
718
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
719
|
+
"""
|
|
720
|
+
Provide visual testing guidance.
|
|
721
|
+
|
|
722
|
+
Args:
|
|
723
|
+
component: Component to test
|
|
724
|
+
strategy: Testing strategy
|
|
725
|
+
|
|
726
|
+
Returns:
|
|
727
|
+
SkillResult with visual testing guidance
|
|
728
|
+
"""
|
|
729
|
+
component = kwargs.get("component", "page")
|
|
730
|
+
strategy = kwargs.get("strategy", "full-page")
|
|
731
|
+
|
|
732
|
+
guidance = self._get_guidance(component, strategy)
|
|
733
|
+
|
|
734
|
+
return SkillResult(
|
|
735
|
+
skill_name=self.NAME,
|
|
736
|
+
success=True,
|
|
737
|
+
output=guidance,
|
|
738
|
+
metrics={"component": component, "strategy": strategy},
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
def _get_guidance(self, component: str, strategy: str) -> str:
|
|
742
|
+
return f"""# Visual Testing for {component}
|
|
743
|
+
|
|
744
|
+
## Strategy: {strategy.title()}
|
|
745
|
+
|
|
746
|
+
### Comparison Algorithms
|
|
747
|
+
1. **Pixel Diff**: Direct pixel comparison
|
|
748
|
+
2. **Perceptual**: Human-perception based
|
|
749
|
+
3. **AI-based**: ML for smart comparison
|
|
750
|
+
4. **Layout**: Structural comparison
|
|
751
|
+
|
|
752
|
+
### Best Practices
|
|
753
|
+
- Consistent viewport sizes
|
|
754
|
+
- Stable test data
|
|
755
|
+
- Font loading wait
|
|
756
|
+
- Animation handling
|
|
757
|
+
- Cross-browser testing
|
|
758
|
+
|
|
759
|
+
### Handling Flakiness
|
|
760
|
+
- Ignore dynamic regions
|
|
761
|
+
- Wait for stability
|
|
762
|
+
- Use tolerance thresholds
|
|
763
|
+
- Retry on failure
|
|
764
|
+
|
|
765
|
+
### Tools
|
|
766
|
+
- Percy
|
|
767
|
+
- Applitools
|
|
768
|
+
- BackstopJS
|
|
769
|
+
- Playwright visual comparisons
|
|
770
|
+
|
|
771
|
+
### Example Implementation
|
|
772
|
+
```python
|
|
773
|
+
def test_visual_regression():
|
|
774
|
+
page.goto("/component")
|
|
775
|
+
page.wait_for_load_state("networkidle")
|
|
776
|
+
|
|
777
|
+
# Capture screenshot
|
|
778
|
+
screenshot = page.screenshot()
|
|
779
|
+
|
|
780
|
+
# Compare with baseline
|
|
781
|
+
assert_visual_match(
|
|
782
|
+
screenshot,
|
|
783
|
+
baseline="component-baseline.png",
|
|
784
|
+
threshold=0.98 # 98% similarity required
|
|
785
|
+
)
|
|
786
|
+
```
|
|
787
|
+
"""
|
|
788
|
+
|
|
789
|
+
def get_prompt(self) -> str:
|
|
790
|
+
return """You are a visual testing expert.
|
|
791
|
+
|
|
792
|
+
Cover:
|
|
793
|
+
1. Baseline management
|
|
794
|
+
2. Diff handling
|
|
795
|
+
3. Cross-browser testing
|
|
796
|
+
4. Responsive testing
|
|
797
|
+
5. Dynamic content handling
|
|
798
|
+
|
|
799
|
+
Use appropriate tools and thresholds."""
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
class ComplianceTesting(Skill):
|
|
803
|
+
"""
|
|
804
|
+
Compliance Testing Skill.
|
|
805
|
+
|
|
806
|
+
Regulatory compliance testing patterns.
|
|
807
|
+
"""
|
|
808
|
+
|
|
809
|
+
NAME = "compliance-testing"
|
|
810
|
+
DISPLAY_NAME = "Compliance Testing"
|
|
811
|
+
DESCRIPTION = "Regulatory compliance test patterns"
|
|
812
|
+
CATEGORY = "testing"
|
|
813
|
+
TAGS = ["compliance", "regulatory", "gdpr", "hipaa"]
|
|
814
|
+
|
|
815
|
+
async def execute(self, **kwargs) -> SkillResult:
|
|
816
|
+
"""
|
|
817
|
+
Provide compliance testing guidance.
|
|
818
|
+
|
|
819
|
+
Args:
|
|
820
|
+
regulation: Regulation to comply with
|
|
821
|
+
area: Area of compliance
|
|
822
|
+
|
|
823
|
+
Returns:
|
|
824
|
+
SkillResult with compliance guidance
|
|
825
|
+
"""
|
|
826
|
+
regulation = kwargs.get("regulation", "GDPR")
|
|
827
|
+
area = kwargs.get("area", "data-privacy")
|
|
828
|
+
|
|
829
|
+
guidance = self._get_guidance(regulation, area)
|
|
830
|
+
|
|
831
|
+
return SkillResult(
|
|
832
|
+
skill_name=self.NAME,
|
|
833
|
+
success=True,
|
|
834
|
+
output=guidance,
|
|
835
|
+
metrics={"regulation": regulation, "area": area},
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
def _get_guidance(self, regulation: str, area: str) -> str:
|
|
839
|
+
return f"""# Compliance Testing for {regulation}
|
|
840
|
+
|
|
841
|
+
## Focus Area: {area.title()}
|
|
842
|
+
|
|
843
|
+
### {regulation} Requirements
|
|
844
|
+
- Data subject rights
|
|
845
|
+
- Consent management
|
|
846
|
+
- Data minimization
|
|
847
|
+
- Right to erasure
|
|
848
|
+
- Data portability
|
|
849
|
+
|
|
850
|
+
### Test Categories
|
|
851
|
+
|
|
852
|
+
#### 1. Data Privacy Tests
|
|
853
|
+
- PII identification
|
|
854
|
+
- Data encryption
|
|
855
|
+
- Access controls
|
|
856
|
+
- Audit logging
|
|
857
|
+
|
|
858
|
+
#### 2. Consent Tests
|
|
859
|
+
- Consent collection
|
|
860
|
+
- Consent withdrawal
|
|
861
|
+
- Preference center
|
|
862
|
+
- Cookie consent
|
|
863
|
+
|
|
864
|
+
#### 3. Security Tests
|
|
865
|
+
- Authentication
|
|
866
|
+
- Authorization
|
|
867
|
+
- Data encryption
|
|
868
|
+
- Secure transmission
|
|
869
|
+
|
|
870
|
+
#### 4. Audit Tests
|
|
871
|
+
- Access logs
|
|
872
|
+
- Change history
|
|
873
|
+
- Data lineage
|
|
874
|
+
- Retention policies
|
|
875
|
+
|
|
876
|
+
### Example Tests
|
|
877
|
+
```python
|
|
878
|
+
def test_pii_is_encrypted_at_rest():
|
|
879
|
+
user = create_user(email="test@example.com")
|
|
880
|
+
raw_data = database.get_raw_record(user.id)
|
|
881
|
+
assert user.email not in raw_data # Should be encrypted
|
|
882
|
+
|
|
883
|
+
def test_user_can_request_data_deletion():
|
|
884
|
+
user = create_user()
|
|
885
|
+
request_deletion(user.id)
|
|
886
|
+
assert user_data_deleted(user.id)
|
|
887
|
+
```
|
|
888
|
+
"""
|
|
889
|
+
|
|
890
|
+
def get_prompt(self) -> str:
|
|
891
|
+
return """You are a compliance testing expert.
|
|
892
|
+
|
|
893
|
+
Cover regulations:
|
|
894
|
+
- GDPR (EU data protection)
|
|
895
|
+
- HIPAA (healthcare)
|
|
896
|
+
- SOC 2 (security)
|
|
897
|
+
- PCI DSS (payment cards)
|
|
898
|
+
|
|
899
|
+
Ensure tests validate compliance requirements."""
|