claude-dev-env 1.80.0 → 1.82.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/_shared/pr-loop/scripts/CLAUDE.md +2 -1
- package/_shared/pr-loop/scripts/code_rules_gate.py +116 -30
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +13 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +113 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +467 -0
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +339 -0
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +297 -0
- package/audit-rubrics/CLAUDE.md +3 -2
- package/audit-rubrics/category_rubrics/CLAUDE.md +2 -1
- package/audit-rubrics/category_rubrics/category-a-api-contracts.md +2 -1
- package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +13 -1
- package/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md +19 -0
- package/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md +46 -0
- package/audit-rubrics/prompts/CLAUDE.md +2 -1
- package/audit-rubrics/prompts/category-a-api-contracts.md +1 -0
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +19 -7
- package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +14 -0
- package/audit-rubrics/prompts/category-q-cross-surface-claims.md +51 -0
- package/docs/CODE_RULES.md +2 -2
- package/hooks/blocking/CLAUDE.md +3 -0
- package/hooks/blocking/code_rules_annotations_length.py +59 -11
- package/hooks/blocking/code_rules_banned_identifiers.py +48 -9
- package/hooks/blocking/code_rules_command_dispatch.py +140 -0
- package/hooks/blocking/code_rules_docstrings.py +93 -0
- package/hooks/blocking/code_rules_enforcer.py +54 -4
- package/hooks/blocking/code_rules_js_conventions.py +246 -0
- package/hooks/blocking/code_rules_naming_collection.py +5 -0
- package/hooks/blocking/code_rules_test_assertions.py +3 -0
- package/hooks/blocking/code_rules_unused_imports.py +0 -3
- package/hooks/blocking/conventional_pr_title_gate.py +444 -0
- package/hooks/blocking/duplicate_rmtree_helper_blocker.py +7 -0
- package/hooks/blocking/test_code_rules_command_dispatch.py +95 -0
- package/hooks/blocking/test_code_rules_enforcer_annotations.py +20 -2
- package/hooks/blocking/test_code_rules_enforcer_banned_identifier.py +10 -2
- package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +8 -4
- package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +9 -3
- package/hooks/blocking/test_code_rules_enforcer_docstring_type_checking_gate.py +164 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +16 -3
- package/hooks/blocking/test_code_rules_js_conventions.py +167 -0
- package/hooks/blocking/test_conventional_pr_title_gate.py +640 -0
- package/hooks/hooks.json +5 -0
- package/hooks/hooks_constants/CLAUDE.md +3 -0
- package/hooks/hooks_constants/blocking_check_limits.py +9 -0
- package/hooks/hooks_constants/command_dispatch_constants.py +28 -0
- package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +58 -0
- package/hooks/hooks_constants/js_conventions_constants.py +54 -0
- package/package.json +1 -1
- package/rules/docstring-prose-matches-implementation.md +2 -1
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +43 -1
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +2 -2
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +1 -4
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +100 -4
- package/skills/autoconverge/SKILL.md +92 -7
- package/skills/autoconverge/reference/convergence.md +3 -3
- package/skills/autoconverge/reference/gotchas.md +5 -5
- package/skills/autoconverge/reference/stop-conditions.md +1 -1
- package/skills/autoconverge/workflow/converge.contract.test.mjs +161 -29
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +342 -1
- package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +2 -2
- package/skills/autoconverge/workflow/converge.mjs +178 -32
- package/skills/autoconverge/workflow/converge_multi.mjs +6 -2
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +26 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Tests for check_docstring_names_absent_type_checking_gate — Category O6.
|
|
2
|
+
|
|
3
|
+
A module or function docstring that names a ``TYPE_CHECKING`` gate-detection step,
|
|
4
|
+
or a ``type-checking-gate`` helper family, drifts once no identifier in the body
|
|
5
|
+
handles TYPE_CHECKING: the prose points a reader at machinery the module does not
|
|
6
|
+
hold. This is the deterministic slice of Category O6 (docstring prose versus
|
|
7
|
+
implementation drift) for a TYPE_CHECKING gate claim. The check covers hook
|
|
8
|
+
infrastructure, where the import-scan gates that carry this drift class live.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import importlib.util
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from types import ModuleType
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _load_enforcer_module() -> ModuleType:
|
|
19
|
+
module_path = Path(__file__).parent / "code_rules_enforcer.py"
|
|
20
|
+
spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
|
|
21
|
+
assert spec is not None
|
|
22
|
+
assert spec.loader is not None
|
|
23
|
+
module = importlib.util.module_from_spec(spec)
|
|
24
|
+
spec.loader.exec_module(module)
|
|
25
|
+
return module
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
code_rules_enforcer = _load_enforcer_module()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def check_type_checking_gate(content: str, file_path: str) -> list[str]:
|
|
32
|
+
return code_rules_enforcer.check_docstring_names_absent_type_checking_gate(content, file_path)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
PRODUCTION_HOOK_PATH = "/home/user/.claude/hooks/blocking/code_rules_unused_imports.py"
|
|
36
|
+
TEST_FILE_PATH = "/home/user/.claude/hooks/blocking/test_code_rules_unused_imports.py"
|
|
37
|
+
MYPY_GATE_PRODUCTION_PATH = "/home/user/project/scripts/run_mypy_gate.py"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_flags_module_docstring_naming_type_checking_gate_helpers() -> None:
|
|
41
|
+
content = (
|
|
42
|
+
'"""Unused module-level import check and its import-range and '
|
|
43
|
+
'type-checking-gate helpers."""\n'
|
|
44
|
+
"\n"
|
|
45
|
+
"import ast\n"
|
|
46
|
+
"\n"
|
|
47
|
+
"\n"
|
|
48
|
+
"def check_unused(content: str) -> list[str]:\n"
|
|
49
|
+
' """Flag unused imports."""\n'
|
|
50
|
+
" return []\n"
|
|
51
|
+
)
|
|
52
|
+
issues = check_type_checking_gate(content, PRODUCTION_HOOK_PATH)
|
|
53
|
+
assert len(issues) == 1
|
|
54
|
+
assert issues[0].startswith("Line 1:")
|
|
55
|
+
assert "type-checking-gate" in issues[0]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_flags_function_docstring_naming_type_checking_gate_detection() -> None:
|
|
59
|
+
content = (
|
|
60
|
+
"import ast\n"
|
|
61
|
+
"\n"
|
|
62
|
+
"\n"
|
|
63
|
+
"def check_unused_module_level_imports(content: str) -> list[str]:\n"
|
|
64
|
+
' """Flag module-level imports never referenced.\n'
|
|
65
|
+
"\n"
|
|
66
|
+
" When ``full_file_content`` is provided, the ``__all__`` /\n"
|
|
67
|
+
" ``TYPE_CHECKING`` gate detection and reference scanning run against\n"
|
|
68
|
+
" ``full_file_content``.\n"
|
|
69
|
+
' """\n'
|
|
70
|
+
" return []\n"
|
|
71
|
+
)
|
|
72
|
+
issues = check_type_checking_gate(content, PRODUCTION_HOOK_PATH)
|
|
73
|
+
assert len(issues) == 1
|
|
74
|
+
assert "type_checking gate" in issues[0]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_flags_both_module_and_function_docstrings_on_the_real_drift() -> None:
|
|
78
|
+
content = (
|
|
79
|
+
'"""Unused module-level import check and its import-range and '
|
|
80
|
+
'type-checking-gate helpers."""\n'
|
|
81
|
+
"\n"
|
|
82
|
+
"import ast\n"
|
|
83
|
+
"\n"
|
|
84
|
+
"\n"
|
|
85
|
+
"def check_unused_module_level_imports(content: str) -> list[str]:\n"
|
|
86
|
+
' """Flag module-level imports never referenced.\n'
|
|
87
|
+
"\n"
|
|
88
|
+
" The ``__all__`` / ``TYPE_CHECKING`` gate detection and reference\n"
|
|
89
|
+
" scanning run against ``full_file_content``.\n"
|
|
90
|
+
' """\n'
|
|
91
|
+
" return []\n"
|
|
92
|
+
)
|
|
93
|
+
issues = check_type_checking_gate(content, PRODUCTION_HOOK_PATH)
|
|
94
|
+
assert len(issues) == 2
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_passes_when_code_declares_a_type_checking_gate_helper() -> None:
|
|
98
|
+
content = (
|
|
99
|
+
'"""Unused module-level import check and its type-checking-gate helpers."""\n'
|
|
100
|
+
"\n"
|
|
101
|
+
"import ast\n"
|
|
102
|
+
"\n"
|
|
103
|
+
"\n"
|
|
104
|
+
"def _module_body_declares_type_checking_gate(tree: ast.Module) -> bool:\n"
|
|
105
|
+
' """Return True when the module guards imports behind TYPE_CHECKING."""\n'
|
|
106
|
+
" return False\n"
|
|
107
|
+
)
|
|
108
|
+
assert check_type_checking_gate(content, PRODUCTION_HOOK_PATH) == []
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_passes_when_body_names_type_checking_guard() -> None:
|
|
112
|
+
content = (
|
|
113
|
+
"import ast\n"
|
|
114
|
+
"from typing import TYPE_CHECKING\n"
|
|
115
|
+
"\n"
|
|
116
|
+
"\n"
|
|
117
|
+
"def check_unused(content: str) -> list[str]:\n"
|
|
118
|
+
' """Skip imports the ``TYPE_CHECKING`` gate detection guards."""\n'
|
|
119
|
+
" if TYPE_CHECKING:\n"
|
|
120
|
+
" return []\n"
|
|
121
|
+
" return []\n"
|
|
122
|
+
)
|
|
123
|
+
assert check_type_checking_gate(content, PRODUCTION_HOOK_PATH) == []
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_passes_when_no_docstring_names_the_gate() -> None:
|
|
127
|
+
content = (
|
|
128
|
+
'"""Unused module-level import check and its import-range helpers."""\n'
|
|
129
|
+
"\n"
|
|
130
|
+
"import ast\n"
|
|
131
|
+
"\n"
|
|
132
|
+
"\n"
|
|
133
|
+
"def check_unused(content: str) -> list[str]:\n"
|
|
134
|
+
' """Flag module-level imports never referenced."""\n'
|
|
135
|
+
" return []\n"
|
|
136
|
+
)
|
|
137
|
+
assert check_type_checking_gate(content, PRODUCTION_HOOK_PATH) == []
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_passes_on_mypy_static_type_checker_gate_docstring() -> None:
|
|
141
|
+
content = (
|
|
142
|
+
"\"\"\"Run the project's type-checking gate and report failures.\"\"\"\n"
|
|
143
|
+
"\n"
|
|
144
|
+
"import subprocess\n"
|
|
145
|
+
"\n"
|
|
146
|
+
"\n"
|
|
147
|
+
"def run_gate() -> int:\n"
|
|
148
|
+
' return subprocess.run(["mypy", "hooks"]).returncode\n'
|
|
149
|
+
)
|
|
150
|
+
assert check_type_checking_gate(content, MYPY_GATE_PRODUCTION_PATH) == []
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_test_files_are_exempt() -> None:
|
|
154
|
+
content = (
|
|
155
|
+
'"""Unused module-level import check and its type-checking-gate helpers."""\n'
|
|
156
|
+
"\n"
|
|
157
|
+
"import ast\n"
|
|
158
|
+
"\n"
|
|
159
|
+
"\n"
|
|
160
|
+
"def check_unused(content: str) -> list[str]:\n"
|
|
161
|
+
' """Flag unused imports."""\n'
|
|
162
|
+
" return []\n"
|
|
163
|
+
)
|
|
164
|
+
assert check_type_checking_gate(content, TEST_FILE_PATH) == []
|
|
@@ -190,15 +190,28 @@ def test_should_flag_dead_import_in_workflow_handler_file() -> None:
|
|
|
190
190
|
)
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
def
|
|
193
|
+
def test_should_flag_unused_import_in_test_file() -> None:
|
|
194
194
|
source = (
|
|
195
|
-
"
|
|
195
|
+
"import asyncio\n"
|
|
196
196
|
"\n"
|
|
197
197
|
"def test_thing() -> None:\n"
|
|
198
198
|
" assert True\n"
|
|
199
199
|
)
|
|
200
200
|
issues = check_unused_module_level_imports(source, TEST_FILE_PATH)
|
|
201
|
-
assert
|
|
201
|
+
assert any("asyncio" in each_issue for each_issue in issues), (
|
|
202
|
+
f"Unused import in a test file must be flagged, got: {issues}"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def test_should_not_flag_used_import_in_test_file() -> None:
|
|
207
|
+
source = (
|
|
208
|
+
"import asyncio\n"
|
|
209
|
+
"\n"
|
|
210
|
+
"def test_thing() -> None:\n"
|
|
211
|
+
" asyncio.run(main())\n"
|
|
212
|
+
)
|
|
213
|
+
issues = check_unused_module_level_imports(source, TEST_FILE_PATH)
|
|
214
|
+
assert issues == [], f"Used import in a test file must not be flagged, got: {issues}"
|
|
202
215
|
|
|
203
216
|
|
|
204
217
|
def test_should_handle_syntax_error_gracefully() -> None:
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Behavioral tests for the JavaScript boolean-naming and banned-identifier checks."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
_blocking_directory = str(Path(__file__).resolve().parent)
|
|
7
|
+
if _blocking_directory not in sys.path:
|
|
8
|
+
sys.path.insert(0, _blocking_directory)
|
|
9
|
+
|
|
10
|
+
from code_rules_js_conventions import ( # noqa: E402
|
|
11
|
+
check_js_banned_identifiers,
|
|
12
|
+
check_js_boolean_naming,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
CONVERGE_PATH = "packages/claude-dev-env/skills/autoconverge/workflow/converge.mjs"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_flags_unprefixed_boolean_let_declaration() -> None:
|
|
19
|
+
content = "let hardeningPrOpened = false\n"
|
|
20
|
+
issues = check_js_boolean_naming(content, CONVERGE_PATH)
|
|
21
|
+
assert len(issues) == 1
|
|
22
|
+
assert "hardeningPrOpened" in issues[0]
|
|
23
|
+
assert "Line 1" in issues[0]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_flags_unprefixed_boolean_negation_declaration() -> None:
|
|
27
|
+
content = "const readyState = !pending\n"
|
|
28
|
+
issues = check_js_boolean_naming(content, CONVERGE_PATH)
|
|
29
|
+
assert len(issues) == 1
|
|
30
|
+
assert "readyState" in issues[0]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_flags_pure_negation_declaration() -> None:
|
|
34
|
+
content = "const ready = !pending;\n"
|
|
35
|
+
issues = check_js_boolean_naming(content, CONVERGE_PATH)
|
|
36
|
+
assert len(issues) == 1
|
|
37
|
+
assert "ready" in issues[0]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_does_not_flag_ternary_negation_string_declaration() -> None:
|
|
41
|
+
content = 'const label = !isActive ? "on" : "off";\n'
|
|
42
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_does_not_flag_ternary_negation_number_declaration() -> None:
|
|
46
|
+
content = "const count = !arr.length ? 0 : arr.length;\n"
|
|
47
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_does_not_flag_negation_logical_and_declaration() -> None:
|
|
51
|
+
content = "const name = !err && getName();\n"
|
|
52
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_does_not_flag_negation_logical_or_declaration() -> None:
|
|
56
|
+
content = "const port = !custom || 8080;\n"
|
|
57
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_flags_unprefixed_boolean_jsdoc_param() -> None:
|
|
61
|
+
content = (
|
|
62
|
+
"/**\n"
|
|
63
|
+
" * @param {boolean} alreadyOpened whether the PR is open\n"
|
|
64
|
+
" */\n"
|
|
65
|
+
"function note(alreadyOpened) { return alreadyOpened }\n"
|
|
66
|
+
)
|
|
67
|
+
issues = check_js_boolean_naming(content, CONVERGE_PATH)
|
|
68
|
+
assert len(issues) == 1
|
|
69
|
+
assert "alreadyOpened" in issues[0]
|
|
70
|
+
assert "parameter" in issues[0].lower()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_accepts_prefixed_boolean_declarations() -> None:
|
|
74
|
+
content = (
|
|
75
|
+
"let isReady = true\n"
|
|
76
|
+
"const hasItems = false\n"
|
|
77
|
+
"let shouldRetry = !done\n"
|
|
78
|
+
"var wasOpened = true\n"
|
|
79
|
+
)
|
|
80
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_accepts_prefixed_boolean_jsdoc_param() -> None:
|
|
84
|
+
content = (
|
|
85
|
+
"/**\n"
|
|
86
|
+
" * @param {boolean} isOpen whether the PR is open\n"
|
|
87
|
+
" */\n"
|
|
88
|
+
"function note(isOpen) { return isOpen }\n"
|
|
89
|
+
)
|
|
90
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_ignores_boolean_declaration_inside_string_literal() -> None:
|
|
94
|
+
content = 'const label = "let flagged = false"\n'
|
|
95
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_ignores_boolean_declaration_inside_line_comment() -> None:
|
|
99
|
+
content = "// let flagged = false\nconst isReady = true\n"
|
|
100
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_does_not_flag_falsely_named_non_boolean_declaration() -> None:
|
|
104
|
+
content = "const counted = falsely()\n"
|
|
105
|
+
assert check_js_boolean_naming(content, CONVERGE_PATH) == []
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_boolean_naming_scoped_to_changed_lines() -> None:
|
|
109
|
+
content = "let alreadyRun = false\nconst isReady = true\n"
|
|
110
|
+
unchanged_only = check_js_boolean_naming(content, CONVERGE_PATH, {2})
|
|
111
|
+
assert unchanged_only == []
|
|
112
|
+
changed = check_js_boolean_naming(content, CONVERGE_PATH, {1})
|
|
113
|
+
assert len(changed) == 1
|
|
114
|
+
assert "alreadyRun" in changed[0]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_boolean_naming_defer_scope_returns_all() -> None:
|
|
118
|
+
content = "let alreadyRun = false\n"
|
|
119
|
+
deferred = check_js_boolean_naming(content, CONVERGE_PATH, None, True)
|
|
120
|
+
assert len(deferred) == 1
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_boolean_naming_exempts_test_files() -> None:
|
|
124
|
+
content = "let alreadyRun = false\n"
|
|
125
|
+
assert check_js_boolean_naming(content, "workflow/converge.test.mjs") == []
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_flags_banned_identifier_declaration() -> None:
|
|
129
|
+
content = "let data = fetchThings()\n"
|
|
130
|
+
issues = check_js_banned_identifiers(content, CONVERGE_PATH)
|
|
131
|
+
assert len(issues) == 1
|
|
132
|
+
assert "data" in issues[0]
|
|
133
|
+
assert "Line 1" in issues[0]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_accepts_descriptive_identifier_declaration() -> None:
|
|
137
|
+
content = "let allOrders = fetchThings()\n"
|
|
138
|
+
assert check_js_banned_identifiers(content, CONVERGE_PATH) == []
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_banned_identifier_message_carries_descriptive_name_guidance() -> None:
|
|
142
|
+
content = "let data = fetchThings()\n"
|
|
143
|
+
issues = check_js_banned_identifiers(content, CONVERGE_PATH)
|
|
144
|
+
assert "use descriptive name (see CODE_RULES Naming section)" in issues[0]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_flags_each_banned_identifier_name() -> None:
|
|
148
|
+
content = "const result = compute()\nlet response = call()\nvar ctx = build()\n"
|
|
149
|
+
issues = check_js_banned_identifiers(content, CONVERGE_PATH)
|
|
150
|
+
assert len(issues) == 3
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_banned_identifier_ignores_string_literal() -> None:
|
|
154
|
+
content = 'const label = "let data = 1"\n'
|
|
155
|
+
assert check_js_banned_identifiers(content, CONVERGE_PATH) == []
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_banned_identifier_scoped_to_changed_lines() -> None:
|
|
159
|
+
content = "let data = one()\nconst allOrders = two()\n"
|
|
160
|
+
assert check_js_banned_identifiers(content, CONVERGE_PATH, {2}) == []
|
|
161
|
+
changed = check_js_banned_identifiers(content, CONVERGE_PATH, {1})
|
|
162
|
+
assert len(changed) == 1
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_banned_identifier_exempts_test_files() -> None:
|
|
166
|
+
content = "let data = one()\n"
|
|
167
|
+
assert check_js_banned_identifiers(content, "workflow/converge.test.mjs") == []
|