synapse-hub 0.0.0-stage → 2.0.0
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.
- package/.agents/agents/failure-analyst.md +47 -0
- package/.agents/agents/live-explorer.md +41 -0
- package/.agents/agents/orchestrator.md +43 -0
- package/.agents/agents/pom-crafter.md +81 -0
- package/.agents/agents/quality-gatekeeper.md +47 -0
- package/.agents/agents/test-runner.md +43 -0
- package/.agents/rules/context-engineering.md +88 -0
- package/.agents/rules/git-push-protocol.md +101 -0
- package/.agents/rules/graphify.md +14 -0
- package/.agents/rules/lessons-learned.md +23 -0
- package/.agents/rules/mandatory-playwright-live.md +29 -0
- package/.agents/rules/skill-precedence.md +42 -0
- package/.agents/rules/systematic-debugging.md +99 -0
- package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +239 -0
- package/.agents/skills/antigravity-test-sentinel/SKILL.md +64 -0
- package/.agents/skills/api-network-mocking-interception/SKILL.md +142 -0
- package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +70 -0
- package/.agents/skills/architecture-decision-records/SKILL.md +441 -0
- package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +72 -0
- package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +214 -0
- package/.agents/skills/bats-testing-patterns/SKILL.md +100 -0
- package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -0
- package/.agents/skills/clean-pom-crafter/SKILL.md +75 -0
- package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -0
- package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -0
- package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +64 -0
- package/.agents/skills/code-refactoring-refactor-clean/resources/implementation-playbook.md +879 -0
- package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +123 -0
- package/.agents/skills/e2e-testing-patterns/SKILL.md +103 -0
- package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -0
- package/.agents/skills/failure-autopsy-and-healing/SKILL.md +54 -0
- package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -0
- package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -0
- package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -0
- package/.agents/skills/file-and-report-validator/SKILL.md +90 -0
- package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +114 -0
- package/.agents/skills/github-actions-templates/SKILL.md +334 -0
- package/.agents/skills/graphify-impact-analyzer/SKILL.md +78 -0
- package/.agents/skills/katalon-cli-runner/SKILL.md +76 -0
- package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -0
- package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -0
- package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -0
- package/.agents/skills/lam-script-generator/SKILL.md +83 -0
- package/.agents/skills/playwright-live-interaction/SKILL.md +86 -0
- package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -0
- package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -0
- package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -0
- package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -0
- package/.agents/skills/security-dast-sentinel/SKILL.md +176 -0
- package/.agents/skills/self-healing-locator/SKILL.md +73 -0
- package/.agents/skills/synthetic-test-data-generator/SKILL.md +135 -0
- package/.agents/skills/token-and-context-optimizer/SKILL.md +70 -0
- package/.agents/skills/visual-regression-sentinel/SKILL.md +62 -0
- package/.agents/skills/wcag-audit-patterns/SKILL.md +77 -0
- package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -0
- package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +65 -0
- package/.agents/templates/completion_report.md +57 -0
- package/.agents/workflows/graphify.md +10 -0
- package/.agents/workflows/test-impact.md +15 -0
- package/.agents/workflows/verify.md +34 -0
- package/AGENTS.md +115 -0
- package/README.md +394 -2
- package/bin/agent-qa.js +90 -0
- package/package.json +59 -4
- package/skills_manifest.json +262 -0
- package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +97 -0
- package/templates/PROJECT_STATE_TEMPLATE.md +20 -0
- package/tools/TOOLS_RUNBOOK.md +298 -0
- package/tools/agent_canary.py +501 -0
- package/tools/analyze_project.py +242 -0
- package/tools/api_drift_sentinel.py +208 -0
- package/tools/auto_heal.py +189 -0
- package/tools/browser_helpers.js +593 -0
- package/tools/bug_reporter.py +224 -0
- package/tools/cache_resolver.py +846 -0
- package/tools/check_env.py +288 -0
- package/tools/ci_healing_hook.py +128 -0
- package/tools/cli.py +402 -0
- package/tools/config_decoupler.py +253 -0
- package/tools/db_sandbox.py +341 -0
- package/tools/distill_log.py +152 -0
- package/tools/e2e_flow_prober.ps1 +65 -0
- package/tools/flaky_detector.py +222 -0
- package/tools/fuzz_payloads.py +197 -0
- package/tools/generate_allure_report.ps1 +30 -0
- package/tools/generate_api_matrix.py +193 -0
- package/tools/generate_tc_from_story.py +182 -0
- package/tools/generate_test_reports.ps1 +135 -0
- package/tools/git_pre_commit_guard.py +203 -0
- package/tools/heal_pr_automator.py +195 -0
- package/tools/heuristic_locator_matcher.py +202 -0
- package/tools/infer_linter.py +238 -0
- package/tools/lint_ai_docs.py +254 -0
- package/tools/lookup_tc.py +165 -0
- package/tools/lookup_xpath.py +167 -0
- package/tools/matrix_sharder.py +146 -0
- package/tools/mcp_excel_server.ps1 +251 -0
- package/tools/mcp_server.py +490 -0
- package/tools/micro_mutator.py +309 -0
- package/tools/network_mock_shield.py +170 -0
- package/tools/omni_emitter.py +500 -0
- package/tools/omni_parser.py +435 -0
- package/tools/omni_transpiler.py +168 -0
- package/tools/pinpoint_failure.py +88 -0
- package/tools/project_state.py +169 -0
- package/tools/repo_map.json +12 -0
- package/tools/repo_map.py +277 -0
- package/tools/repo_map.txt +7 -0
- package/tools/safety_firewall.py +245 -0
- package/tools/setup_project.py +265 -0
- package/tools/smart_visual_masker.py +134 -0
- package/tools/test_impact.py +321 -0
- package/tools/tms_sync_bridge.py +342 -0
- package/tools/trace_data_flow.py +240 -0
- package/tools/traffic_session_converter.py +256 -0
- package/tools/universal_ir.py +480 -0
- package/tools/validate_groovy_scripts.py +173 -0
- package/tools/verify_all.py +496 -0
- package/tools/verify_all.sh +4 -0
- package/tools/visual_diff_canvas.py +142 -0
- package/tools/visual_som_helper.py +418 -0
- package/tools/wcag_audit.py +214 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
# Bats Testing Patterns Implementation Playbook
|
|
2
|
+
|
|
3
|
+
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
|
4
|
+
|
|
5
|
+
## Bats Fundamentals
|
|
6
|
+
|
|
7
|
+
### What is Bats?
|
|
8
|
+
|
|
9
|
+
Bats (Bash Automated Testing System) is a TAP (Test Anything Protocol) compliant testing framework for shell scripts that provides:
|
|
10
|
+
- Simple, natural test syntax
|
|
11
|
+
- TAP output format compatible with CI systems
|
|
12
|
+
- Fixtures and setup/teardown support
|
|
13
|
+
- Assertion helpers
|
|
14
|
+
- Parallel test execution
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# macOS with Homebrew
|
|
20
|
+
brew install bats-core
|
|
21
|
+
|
|
22
|
+
# Ubuntu/Debian
|
|
23
|
+
git clone https://github.com/bats-core/bats-core.git
|
|
24
|
+
cd bats-core
|
|
25
|
+
./install.sh /usr/local
|
|
26
|
+
|
|
27
|
+
# From npm (Node.js)
|
|
28
|
+
npm install --global bats
|
|
29
|
+
|
|
30
|
+
# Verify installation
|
|
31
|
+
bats --version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### File Structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
project/
|
|
38
|
+
├── bin/
|
|
39
|
+
│ ├── script.sh
|
|
40
|
+
│ └── helper.sh
|
|
41
|
+
├── tests/
|
|
42
|
+
│ ├── test_script.bats
|
|
43
|
+
│ ├── test_helper.sh
|
|
44
|
+
│ ├── fixtures/
|
|
45
|
+
│ │ ├── input.txt
|
|
46
|
+
│ │ └── expected_output.txt
|
|
47
|
+
│ └── helpers/
|
|
48
|
+
│ └── mocks.bash
|
|
49
|
+
└── README.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Basic Test Structure
|
|
53
|
+
|
|
54
|
+
### Simple Test File
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
#!/usr/bin/env bats
|
|
58
|
+
|
|
59
|
+
# Load test helper if present
|
|
60
|
+
load test_helper
|
|
61
|
+
|
|
62
|
+
# Setup runs before each test
|
|
63
|
+
setup() {
|
|
64
|
+
export TMPDIR=$(mktemp -d)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Teardown runs after each test
|
|
68
|
+
teardown() {
|
|
69
|
+
rm -rf "$TMPDIR"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Test: simple assertion
|
|
73
|
+
@test "Function returns 0 on success" {
|
|
74
|
+
run my_function "input"
|
|
75
|
+
[ "$status" -eq 0 ]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Test: output verification
|
|
79
|
+
@test "Function outputs correct result" {
|
|
80
|
+
run my_function "test"
|
|
81
|
+
[ "$output" = "expected output" ]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Test: error handling
|
|
85
|
+
@test "Function returns 1 on missing argument" {
|
|
86
|
+
run my_function
|
|
87
|
+
[ "$status" -eq 1 ]
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Assertion Patterns
|
|
92
|
+
|
|
93
|
+
### Exit Code Assertions
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
#!/usr/bin/env bats
|
|
97
|
+
|
|
98
|
+
@test "Command succeeds" {
|
|
99
|
+
run true
|
|
100
|
+
[ "$status" -eq 0 ]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@test "Command fails as expected" {
|
|
104
|
+
run false
|
|
105
|
+
[ "$status" -ne 0 ]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@test "Command returns specific exit code" {
|
|
109
|
+
run my_function --invalid
|
|
110
|
+
[ "$status" -eq 127 ]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@test "Can capture command result" {
|
|
114
|
+
run echo "hello"
|
|
115
|
+
[ $status -eq 0 ]
|
|
116
|
+
[ "$output" = "hello" ]
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Output Assertions
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
#!/usr/bin/env bats
|
|
124
|
+
|
|
125
|
+
@test "Output matches string" {
|
|
126
|
+
result=$(echo "hello world")
|
|
127
|
+
[ "$result" = "hello world" ]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@test "Output contains substring" {
|
|
131
|
+
result=$(echo "hello world")
|
|
132
|
+
[[ "$result" == *"world"* ]]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@test "Output matches pattern" {
|
|
136
|
+
result=$(date +%Y)
|
|
137
|
+
[[ "$result" =~ ^[0-9]{4}$ ]]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@test "Multi-line output" {
|
|
141
|
+
run printf "line1\nline2\nline3"
|
|
142
|
+
[ "$output" = "line1
|
|
143
|
+
line2
|
|
144
|
+
line3" ]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@test "Lines variable contains output" {
|
|
148
|
+
run printf "line1\nline2\nline3"
|
|
149
|
+
[ "${lines[0]}" = "line1" ]
|
|
150
|
+
[ "${lines[1]}" = "line2" ]
|
|
151
|
+
[ "${lines[2]}" = "line3" ]
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### File Assertions
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
#!/usr/bin/env bats
|
|
159
|
+
|
|
160
|
+
@test "File is created" {
|
|
161
|
+
[ ! -f "$TMPDIR/output.txt" ]
|
|
162
|
+
my_function > "$TMPDIR/output.txt"
|
|
163
|
+
[ -f "$TMPDIR/output.txt" ]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@test "File contents match expected" {
|
|
167
|
+
my_function > "$TMPDIR/output.txt"
|
|
168
|
+
[ "$(cat "$TMPDIR/output.txt")" = "expected content" ]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@test "File is readable" {
|
|
172
|
+
touch "$TMPDIR/test.txt"
|
|
173
|
+
[ -r "$TMPDIR/test.txt" ]
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@test "File has correct permissions" {
|
|
177
|
+
touch "$TMPDIR/test.txt"
|
|
178
|
+
chmod 644 "$TMPDIR/test.txt"
|
|
179
|
+
[ "$(stat -f %OLp "$TMPDIR/test.txt")" = "644" ]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@test "File size is correct" {
|
|
183
|
+
echo -n "12345" > "$TMPDIR/test.txt"
|
|
184
|
+
[ "$(wc -c < "$TMPDIR/test.txt")" -eq 5 ]
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Setup and Teardown Patterns
|
|
189
|
+
|
|
190
|
+
### Basic Setup and Teardown
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
#!/usr/bin/env bats
|
|
194
|
+
|
|
195
|
+
setup() {
|
|
196
|
+
# Create test directory
|
|
197
|
+
TEST_DIR=$(mktemp -d)
|
|
198
|
+
export TEST_DIR
|
|
199
|
+
|
|
200
|
+
# Source script under test
|
|
201
|
+
source "${BATS_TEST_DIRNAME}/../bin/script.sh"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
teardown() {
|
|
205
|
+
# Clean up temporary directory
|
|
206
|
+
rm -rf "$TEST_DIR"
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@test "Test using TEST_DIR" {
|
|
210
|
+
touch "$TEST_DIR/file.txt"
|
|
211
|
+
[ -f "$TEST_DIR/file.txt" ]
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Setup with Resources
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
#!/usr/bin/env bats
|
|
219
|
+
|
|
220
|
+
setup() {
|
|
221
|
+
# Create directory structure
|
|
222
|
+
mkdir -p "$TMPDIR/data/input"
|
|
223
|
+
mkdir -p "$TMPDIR/data/output"
|
|
224
|
+
|
|
225
|
+
# Create test fixtures
|
|
226
|
+
echo "line1" > "$TMPDIR/data/input/file1.txt"
|
|
227
|
+
echo "line2" > "$TMPDIR/data/input/file2.txt"
|
|
228
|
+
|
|
229
|
+
# Initialize environment
|
|
230
|
+
export DATA_DIR="$TMPDIR/data"
|
|
231
|
+
export INPUT_DIR="$DATA_DIR/input"
|
|
232
|
+
export OUTPUT_DIR="$DATA_DIR/output"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
teardown() {
|
|
236
|
+
rm -rf "$TMPDIR/data"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@test "Processes input files" {
|
|
240
|
+
run my_process_script "$INPUT_DIR" "$OUTPUT_DIR"
|
|
241
|
+
[ "$status" -eq 0 ]
|
|
242
|
+
[ -f "$OUTPUT_DIR/file1.txt" ]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Global Setup/Teardown
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
#!/usr/bin/env bats
|
|
250
|
+
|
|
251
|
+
# Load shared setup from test_helper.sh
|
|
252
|
+
load test_helper
|
|
253
|
+
|
|
254
|
+
# setup_file runs once before all tests
|
|
255
|
+
setup_file() {
|
|
256
|
+
export SHARED_RESOURCE=$(mktemp -d)
|
|
257
|
+
echo "Expensive setup" > "$SHARED_RESOURCE/data.txt"
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# teardown_file runs once after all tests
|
|
261
|
+
teardown_file() {
|
|
262
|
+
rm -rf "$SHARED_RESOURCE"
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@test "First test uses shared resource" {
|
|
266
|
+
[ -f "$SHARED_RESOURCE/data.txt" ]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@test "Second test uses shared resource" {
|
|
270
|
+
[ -d "$SHARED_RESOURCE" ]
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Mocking and Stubbing Patterns
|
|
275
|
+
|
|
276
|
+
### Function Mocking
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
#!/usr/bin/env bats
|
|
280
|
+
|
|
281
|
+
# Mock external command
|
|
282
|
+
my_external_tool() {
|
|
283
|
+
echo "mocked output"
|
|
284
|
+
return 0
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@test "Function uses mocked tool" {
|
|
288
|
+
export -f my_external_tool
|
|
289
|
+
run my_function
|
|
290
|
+
[[ "$output" == *"mocked output"* ]]
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Command Stubbing
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
#!/usr/bin/env bats
|
|
298
|
+
|
|
299
|
+
setup() {
|
|
300
|
+
# Create stub directory
|
|
301
|
+
STUBS_DIR="$TMPDIR/stubs"
|
|
302
|
+
mkdir -p "$STUBS_DIR"
|
|
303
|
+
|
|
304
|
+
# Add to PATH
|
|
305
|
+
export PATH="$STUBS_DIR:$PATH"
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
create_stub() {
|
|
309
|
+
local cmd="$1"
|
|
310
|
+
local output="$2"
|
|
311
|
+
local code="${3:-0}"
|
|
312
|
+
|
|
313
|
+
cat > "$STUBS_DIR/$cmd" <<EOF
|
|
314
|
+
#!/bin/bash
|
|
315
|
+
echo "$output"
|
|
316
|
+
exit $code
|
|
317
|
+
EOF
|
|
318
|
+
chmod +x "$STUBS_DIR/$cmd"
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@test "Function works with stubbed curl" {
|
|
322
|
+
create_stub curl "{ \"status\": \"ok\" }" 0
|
|
323
|
+
run my_api_function
|
|
324
|
+
[ "$status" -eq 0 ]
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Variable Stubbing
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
#!/usr/bin/env bats
|
|
332
|
+
|
|
333
|
+
@test "Function handles environment override" {
|
|
334
|
+
export MY_SETTING="override_value"
|
|
335
|
+
run my_function
|
|
336
|
+
[ "$status" -eq 0 ]
|
|
337
|
+
[[ "$output" == *"override_value"* ]]
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@test "Function uses default when var unset" {
|
|
341
|
+
unset MY_SETTING
|
|
342
|
+
run my_function
|
|
343
|
+
[ "$status" -eq 0 ]
|
|
344
|
+
[[ "$output" == *"default"* ]]
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Fixture Management
|
|
349
|
+
|
|
350
|
+
### Using Fixture Files
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
#!/usr/bin/env bats
|
|
354
|
+
|
|
355
|
+
# Fixture directory: tests/fixtures/
|
|
356
|
+
|
|
357
|
+
setup() {
|
|
358
|
+
FIXTURES_DIR="${BATS_TEST_DIRNAME}/fixtures"
|
|
359
|
+
WORK_DIR=$(mktemp -d)
|
|
360
|
+
export WORK_DIR
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
teardown() {
|
|
364
|
+
rm -rf "$WORK_DIR"
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
@test "Process fixture file" {
|
|
368
|
+
# Copy fixture to work directory
|
|
369
|
+
cp "$FIXTURES_DIR/input.txt" "$WORK_DIR/input.txt"
|
|
370
|
+
|
|
371
|
+
# Run function
|
|
372
|
+
run my_process_function "$WORK_DIR/input.txt"
|
|
373
|
+
|
|
374
|
+
# Compare output
|
|
375
|
+
diff "$WORK_DIR/output.txt" "$FIXTURES_DIR/expected_output.txt"
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Dynamic Fixture Generation
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
#!/usr/bin/env bats
|
|
383
|
+
|
|
384
|
+
generate_fixture() {
|
|
385
|
+
local lines="$1"
|
|
386
|
+
local file="$2"
|
|
387
|
+
|
|
388
|
+
for i in $(seq 1 "$lines"); do
|
|
389
|
+
echo "Line $i content" >> "$file"
|
|
390
|
+
done
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
@test "Handle large input file" {
|
|
394
|
+
generate_fixture 1000 "$TMPDIR/large.txt"
|
|
395
|
+
run my_function "$TMPDIR/large.txt"
|
|
396
|
+
[ "$status" -eq 0 ]
|
|
397
|
+
[ "$(wc -l < "$TMPDIR/large.txt")" -eq 1000 ]
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Advanced Patterns
|
|
402
|
+
|
|
403
|
+
### Testing Error Conditions
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
#!/usr/bin/env bats
|
|
407
|
+
|
|
408
|
+
@test "Function fails with missing file" {
|
|
409
|
+
run my_function "/nonexistent/file.txt"
|
|
410
|
+
[ "$status" -ne 0 ]
|
|
411
|
+
[[ "$output" == *"not found"* ]]
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@test "Function fails with invalid input" {
|
|
415
|
+
run my_function ""
|
|
416
|
+
[ "$status" -ne 0 ]
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
@test "Function fails with permission denied" {
|
|
420
|
+
touch "$TMPDIR/readonly.txt"
|
|
421
|
+
chmod 000 "$TMPDIR/readonly.txt"
|
|
422
|
+
run my_function "$TMPDIR/readonly.txt"
|
|
423
|
+
[ "$status" -ne 0 ]
|
|
424
|
+
chmod 644 "$TMPDIR/readonly.txt" # Cleanup
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
@test "Function provides helpful error message" {
|
|
428
|
+
run my_function --invalid-option
|
|
429
|
+
[ "$status" -ne 0 ]
|
|
430
|
+
[[ "$output" == *"Usage:"* ]]
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Testing with Dependencies
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
#!/usr/bin/env bats
|
|
438
|
+
|
|
439
|
+
setup() {
|
|
440
|
+
# Check for required tools
|
|
441
|
+
if ! command -v jq &>/dev/null; then
|
|
442
|
+
skip "jq is not installed"
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
export SCRIPT="${BATS_TEST_DIRNAME}/../bin/script.sh"
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
@test "JSON parsing works" {
|
|
449
|
+
skip_if ! command -v jq &>/dev/null
|
|
450
|
+
run my_json_parser '{"key": "value"}'
|
|
451
|
+
[ "$status" -eq 0 ]
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Testing Shell Compatibility
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
#!/usr/bin/env bats
|
|
459
|
+
|
|
460
|
+
@test "Script works in bash" {
|
|
461
|
+
bash "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@test "Script works in sh (POSIX)" {
|
|
465
|
+
sh "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
@test "Script works in dash" {
|
|
469
|
+
if command -v dash &>/dev/null; then
|
|
470
|
+
dash "${BATS_TEST_DIRNAME}/../bin/script.sh" arg1
|
|
471
|
+
else
|
|
472
|
+
skip "dash not installed"
|
|
473
|
+
fi
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Parallel Execution
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
#!/usr/bin/env bats
|
|
481
|
+
|
|
482
|
+
@test "Multiple independent operations" {
|
|
483
|
+
run bash -c 'for i in {1..10}; do
|
|
484
|
+
my_operation "$i" &
|
|
485
|
+
done
|
|
486
|
+
wait'
|
|
487
|
+
[ "$status" -eq 0 ]
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
@test "Concurrent file operations" {
|
|
491
|
+
for i in {1..5}; do
|
|
492
|
+
my_function "$TMPDIR/file$i" &
|
|
493
|
+
done
|
|
494
|
+
wait
|
|
495
|
+
[ -f "$TMPDIR/file1" ]
|
|
496
|
+
[ -f "$TMPDIR/file5" ]
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
## Test Helper Pattern
|
|
501
|
+
|
|
502
|
+
### test_helper.sh
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
#!/usr/bin/env bash
|
|
506
|
+
|
|
507
|
+
# Source script under test
|
|
508
|
+
export SCRIPT_DIR="${BATS_TEST_DIRNAME%/*}/bin"
|
|
509
|
+
|
|
510
|
+
# Common test utilities
|
|
511
|
+
assert_file_exists() {
|
|
512
|
+
if [ ! -f "$1" ]; then
|
|
513
|
+
echo "Expected file to exist: $1"
|
|
514
|
+
return 1
|
|
515
|
+
fi
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
assert_file_equals() {
|
|
519
|
+
local file="$1"
|
|
520
|
+
local expected="$2"
|
|
521
|
+
|
|
522
|
+
if [ ! -f "$file" ]; then
|
|
523
|
+
echo "File does not exist: $file"
|
|
524
|
+
return 1
|
|
525
|
+
fi
|
|
526
|
+
|
|
527
|
+
local actual=$(cat "$file")
|
|
528
|
+
if [ "$actual" != "$expected" ]; then
|
|
529
|
+
echo "File contents do not match"
|
|
530
|
+
echo "Expected: $expected"
|
|
531
|
+
echo "Actual: $actual"
|
|
532
|
+
return 1
|
|
533
|
+
fi
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
# Create temporary test directory
|
|
537
|
+
setup_test_dir() {
|
|
538
|
+
export TEST_DIR=$(mktemp -d)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
cleanup_test_dir() {
|
|
542
|
+
rm -rf "$TEST_DIR"
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## Integration with CI/CD
|
|
547
|
+
|
|
548
|
+
### GitHub Actions Workflow
|
|
549
|
+
|
|
550
|
+
```yaml
|
|
551
|
+
name: Tests
|
|
552
|
+
|
|
553
|
+
on: [push, pull_request]
|
|
554
|
+
|
|
555
|
+
jobs:
|
|
556
|
+
test:
|
|
557
|
+
runs-on: ubuntu-latest
|
|
558
|
+
|
|
559
|
+
steps:
|
|
560
|
+
- uses: actions/checkout@v3
|
|
561
|
+
|
|
562
|
+
- name: Install Bats
|
|
563
|
+
run: |
|
|
564
|
+
npm install --global bats
|
|
565
|
+
|
|
566
|
+
- name: Run Tests
|
|
567
|
+
run: |
|
|
568
|
+
bats tests/*.bats
|
|
569
|
+
|
|
570
|
+
- name: Run Tests with Tap Reporter
|
|
571
|
+
run: |
|
|
572
|
+
bats tests/*.bats --tap | tee test_output.tap
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Makefile Integration
|
|
576
|
+
|
|
577
|
+
```makefile
|
|
578
|
+
.PHONY: test test-verbose test-tap
|
|
579
|
+
|
|
580
|
+
test:
|
|
581
|
+
bats tests/*.bats
|
|
582
|
+
|
|
583
|
+
test-verbose:
|
|
584
|
+
bats tests/*.bats --verbose
|
|
585
|
+
|
|
586
|
+
test-tap:
|
|
587
|
+
bats tests/*.bats --tap
|
|
588
|
+
|
|
589
|
+
test-parallel:
|
|
590
|
+
bats tests/*.bats --parallel 4
|
|
591
|
+
|
|
592
|
+
coverage: test
|
|
593
|
+
# Optional: Generate coverage reports
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## Best Practices
|
|
597
|
+
|
|
598
|
+
1. **Test one thing per test** - Single responsibility principle
|
|
599
|
+
2. **Use descriptive test names** - Clearly states what is being tested
|
|
600
|
+
3. **Clean up after tests** - Always remove temporary files in teardown
|
|
601
|
+
4. **Test both success and failure paths** - Don't just test happy path
|
|
602
|
+
5. **Mock external dependencies** - Isolate unit under test
|
|
603
|
+
6. **Use fixtures for complex data** - Makes tests more readable
|
|
604
|
+
7. **Run tests in CI/CD** - Catch regressions early
|
|
605
|
+
8. **Test across shell dialects** - Ensure portability
|
|
606
|
+
9. **Keep tests fast** - Run in parallel when possible
|
|
607
|
+
10. **Document complex test setup** - Explain unusual patterns
|
|
608
|
+
|
|
609
|
+
## Resources
|
|
610
|
+
|
|
611
|
+
- **Bats GitHub**: https://github.com/bats-core/bats-core
|
|
612
|
+
- **Bats Documentation**: https://bats-core.readthedocs.io/
|
|
613
|
+
- **TAP Protocol**: https://testanything.org/
|
|
614
|
+
- **Test-Driven Development**: https://en.wikipedia.org/wiki/Test-driven_development
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clean-pom-crafter
|
|
3
|
+
description: >-
|
|
4
|
+
Standardized Clean Page Object Model (POM) development and Flat Architecture enforcement across enterprise automation test suites.
|
|
5
|
+
Ensures 100% UTF-8 No BOM, zero hardcoded XPaths in scripts, dynamic test data generation via uniqueCode,
|
|
6
|
+
and standardized two-phase search, edit, calendar, multiselect, and deletion behaviors.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Clean POM Crafter — Enterprise Clean POM Architecture Standards
|
|
10
|
+
|
|
11
|
+
Specialized skill for creating, refactoring, and standardizing test automation scripts using **Clean Page Object Model (POM)** and **Flat Architecture** across enterprise test suites.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Core Architectural Principles
|
|
16
|
+
|
|
17
|
+
1. **Flat Architecture:**
|
|
18
|
+
- All `Test Cases/`, `Scripts/`, and `Test Suites/` reside directly in their parent directory (no module subfolders).
|
|
19
|
+
- Naming convention: `TC-<Number>_<FeatureName>`, e.g. `TC-101_CreateUserAccount.groovy` or `tc-101-create-user.spec.ts`.
|
|
20
|
+
2. **Unified Inheritance from `BasePage`:**
|
|
21
|
+
- All Page Classes in `Keywords/<project_namespace>/pages/` (or `pages/*.ts` in Playwright/TS) inherit from `BasePage`.
|
|
22
|
+
3. **Zero Raw XPaths in Test Scripts:**
|
|
23
|
+
- Scripts (`Scripts/TC-xxx_*.groovy` or `tests/*.spec.ts`) only orchestrate business workflows. All locators and UI interactions are encapsulated in Page Classes.
|
|
24
|
+
4. **Mandatory 100% UTF-8 No BOM:**
|
|
25
|
+
- All `.groovy`, `.java`, `.ts`, `.tc` files must NEVER contain UTF-8 BOM bytes (`0xEF, 0xBB, 0xBF`).
|
|
26
|
+
5. **Context Window Protection (Zero-Token Economy):**
|
|
27
|
+
- 🚫 **NEVER read entire monolithic Page Class files (> 100KB) into LLM context.**
|
|
28
|
+
- Always use `python3 tools/lookup_xpath.py <keyword>` or line-range targeted reads (`read_file` with `StartLine`/`EndLine`) to find selectors and methods on demand.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Master Locator Priority Map (6 Tiers)
|
|
33
|
+
|
|
34
|
+
All Page Classes **MUST** follow this strict locator hierarchy:
|
|
35
|
+
1. **Tier 1 — Angular Semantic / ARIA:** `[formcontrolname="..."]`, `[aria-label="..."]`, `[role="..."]`.
|
|
36
|
+
2. **Tier 2 — Dedicated Test Attributes:** `[data-testid="..."]`, `[data-test="..."]`.
|
|
37
|
+
3. **Tier 3 — HTML Identifier:** `id`, `name`.
|
|
38
|
+
4. **Tier 4 — PrimeNG Business Attributes:** `p-dropdown[formcontrolname="..."]`, `button[icon*="pencil"]`.
|
|
39
|
+
5. **Tier 5 — Resilient CSS Selectors:** `.card.popup-data input.p-inputtext`.
|
|
40
|
+
6. **Tier 6 — Relative XPath (Last Resort):** `//div[contains(@class, 'card')]//button[contains(., 'Confirm')]`.
|
|
41
|
+
|
|
42
|
+
> 🚫 **FORBIDDEN:** Absolute positional XPaths (`//tbody/tr[1]/td[6]/button[2]`) and dynamic hash classes (`.p-element-1n2xyz`).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 3. Test Data Management & Cross-Module Data Chain
|
|
47
|
+
|
|
48
|
+
- **Supporting Field:** Generate dynamically using `BasePage.uniqueCode(prefix)` (e.g. `BasePage.uniqueCode("AUTO_")`).
|
|
49
|
+
- **Dimension Field:** Fixed category values selected from dropdowns (`"Active"`, `"Standard"`).
|
|
50
|
+
- **Reference Field:** Pass output records from prior TCs into subsequent TCs (Create -> Edit -> Delete).
|
|
51
|
+
- **Computed Field:** Auto-calculated system metrics; assert 100% exact match.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 4. Universal Page Object Architecture Model
|
|
56
|
+
|
|
57
|
+
| No. | Page Class Role | Functional Module | Typical Test Scope | Core Methods & Responsibilities |
|
|
58
|
+
|:---:|---|---|:---:|---|
|
|
59
|
+
| 1 | `BasePage` | Core Engine, Driver, JS Helpers, Smart Wait | Global | `openUrl()`, `smartWait()`, `clickSafe()`, `uniqueCode()` |
|
|
60
|
+
| 2 | `LoginPage` | Authentication & User Profile | Auth & Session | `login()`, `logout()`, `verifyUserProfile()` |
|
|
61
|
+
| 3 | `UserAdminPage` | System Administration & Users | Admin / RBAC | `createUser()`, `assignRole()`, `filterUsers()` |
|
|
62
|
+
| 4 | `CommonCategoryPage` | Common Categories & System Catalogs | Catalogs & Lookups | `addCategory()`, `editCategory()`, `verifyCatalog()` |
|
|
63
|
+
| 5 | `WorkflowPlanningPage`| Sector Planning & Approvals | Workflows & Approvals | `createPlan()`, `submitForReview()`, `approvePlan()` |
|
|
64
|
+
| 6 | `DataManagementPage` | Enterprise CRUD & Search Tables | Master Data | `searchRecord()`, `editRecord()`, `deleteRecord()` |
|
|
65
|
+
| 7 | `ReportAnalyticsPage` | Statistical Reporting & Dashboards | Reporting | `generateReport()`, `exportExcel()`, `verifyKPI()` |
|
|
66
|
+
| 8 | `ApiTokenMgmtPage` | Data Sharing & API Tokens | API & Integration | `generateToken()`, `revokeToken()`, `verifyScopes()` |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 5. Implementation References & Examples
|
|
71
|
+
|
|
72
|
+
- [**Standard Clean POM Methods (`pom_patterns.groovy`)**](references/pom_patterns.groovy): Full implementation of `searchRecord`, `editRecord`, `deleteRecord`, `setCalendarDate`, `selectMultiOptions`, `verifyRecordExists`, `verifyRecordNotExists`, `jsSetInput`.
|
|
73
|
+
- [**Standard Test Script Template (`standard_test_case.groovy`)**](examples/standard_test_case.groovy): 7-step complete script template from Login -> Create -> Search -> Edit -> Delete.
|
|
74
|
+
- [**Zero-Token XPath Lookup (`tools/lookup_xpath.py`)**](tools/lookup_xpath.py): Command `python3 tools/lookup_xpath.py <PageClass>` returns exact locators in <200 tokens without parsing full files.
|
|
75
|
+
- [**Browser Runtime Helper (`tools/browser_helpers.js`)**](tools/browser_helpers.js): In-browser validation engine for form audits, safe clicks, and batch verification.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import com.enterprise.pages.BasePage
|
|
2
|
+
import com.enterprise.pages.LoginPage
|
|
3
|
+
import com.enterprise.pages.DataMiningPage
|
|
4
|
+
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
|
|
5
|
+
|
|
6
|
+
// Bước 1: Đảm bảo phiên đăng nhập hợp lệ
|
|
7
|
+
LoginPage.ensureLoggedIn()
|
|
8
|
+
|
|
9
|
+
// Bước 2: Điều hướng đến phân hệ mục tiêu
|
|
10
|
+
DataMiningPage.navigateToCumCongNghiep()
|
|
11
|
+
|
|
12
|
+
// Bước 3: Chuẩn bị dữ liệu kiểm thử duy nhất
|
|
13
|
+
String testCode = BasePage.uniqueCode("CCN_")
|
|
14
|
+
String testName = "CCN Thử Nghiệm " + testCode
|
|
15
|
+
|
|
16
|
+
// Bước 4: Thực thi tạo mới
|
|
17
|
+
DataMiningPage.createCluster(testCode, testName, "Hà Nội", "Đang hoạt động")
|
|
18
|
+
|
|
19
|
+
// Bước 5: Tìm kiếm 2 pha và xác thực theo Mã
|
|
20
|
+
DataMiningPage.searchRecord(testCode)
|
|
21
|
+
DataMiningPage.verifyRecordExists(testCode)
|
|
22
|
+
|
|
23
|
+
// Bước 6: Chỉnh sửa và xác thực cập nhật
|
|
24
|
+
String updatedName = testName + " [ĐÃ CẬP NHẬT]"
|
|
25
|
+
DataMiningPage.editRecord(testCode, updatedName)
|
|
26
|
+
|
|
27
|
+
// Bước 7: Dọn dẹp dữ liệu (Xóa & Verify Không còn tồn tại)
|
|
28
|
+
DataMiningPage.deleteRecord(testCode)
|