@1aboveio/skills 1.0.7 → 1.0.9
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/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/resources.json +1827 -0
- package/README.md +22 -0
- package/package.json +9 -2
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/airflow/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/SKILL.md +92 -34
- package/skills/app-dev/android-foundation/assets/android-ci-job.yml +13 -7
- package/skills/app-dev/android-foundation/references/ci.md +23 -13
- package/skills/app-dev/android-foundation/references/delivery-focus.md +29 -0
- package/skills/app-dev/android-foundation/references/diagnosis.md +136 -0
- package/skills/app-dev/android-foundation/references/gotchas.md +20 -11
- package/skills/app-dev/android-foundation/references/setup.md +15 -6
- package/skills/app-dev/android-foundation/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.sh +148 -42
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/app-debug/release-check.mjs +3 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/better-auth/release-check.mjs +3 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/release-check.mjs +3 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/data-quality/release-check.mjs +3 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/monorepo/release-check.mjs +3 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/pyspark/release-check.mjs +3 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/python-backend/release-check.mjs +3 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/shadcn/release-check.mjs +3 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/release-check.mjs +3 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-build/release-check.mjs +3 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/release-check.mjs +3 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/docker/release-check.mjs +3 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-actions/release-check.mjs +3 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/github-flow/release-check.mjs +3 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/google-cloud/release-check.mjs +3 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/mergify/release-check.mjs +3 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/cicd/podman/release-check.mjs +3 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-review/release-check.mjs +3 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/article-to-image/release-check.mjs +3 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/flowchart-svg/release-check.mjs +3 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/image-gen/release-check.mjs +3 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/mermaid/release-check.mjs +3 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/release-check.mjs +3 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-publisher/release-check.mjs +3 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/wechat-search/release-check.mjs +3 -1
- package/skills/content/xai-search/SKILL.md +88 -13
- package/skills/content/xai-search/release-check.mjs +3 -1
- package/skills/content/xai-search/scripts/xai_search.sh +125 -149
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/distill-writing/release-check.mjs +3 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/doc-to-md/release-check.mjs +3 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/editorial-review/release-check.mjs +3 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/release-check.mjs +3 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/plain-language/release-check.mjs +3 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/polish-cn/release-check.mjs +3 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/reg-parser/release-check.mjs +3 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/release-check.mjs +3 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-doc/release-check.mjs +3 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/better-review/release-check.mjs +3 -1
- package/skills/engineering/contributing/SKILL.md +1 -1
- package/skills/engineering/contributing/release-check.mjs +3 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/e2e-test/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/golden-path/release-check.mjs +3 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/SKILL.md +11 -20
- package/skills/engineering/resolve-issues/references/delivery-progress.md +63 -0
- package/skills/engineering/resolve-issues/references/expire.md +24 -11
- package/skills/engineering/resolve-issues/references/implementation.md +9 -1
- package/skills/engineering/resolve-issues/references/preflight.md +7 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +12 -5
- package/skills/engineering/resolve-issues/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/resolve-release/release-check.mjs +3 -1
- package/skills/engineering/rush-issues/SKILL.md +5 -2
- package/skills/engineering/rush-issues/release-check.mjs +3 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/rush-release/release-check.mjs +3 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/engineering/test-quality/release-check.mjs +3 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
- package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -172
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
- package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
- package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
- package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
- package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
- package/skills/content/image-gen/scripts/options.test.mjs +0 -245
- package/skills/content/xai-search/tests/smoke_test.sh +0 -36
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
- package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
- package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
- package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
- package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
// Tests for the lint-tests gate engine (pure core, no filesystem/git).
|
|
2
|
-
import { test } from 'node:test'
|
|
3
|
-
import assert from 'node:assert/strict'
|
|
4
|
-
|
|
5
|
-
import { scanText, scanDiff, selectRules, RULES, ALL_RULES, isTestFile, stripComment, runCli } from './lint-tests.mjs'
|
|
6
|
-
import { resolveMockPolicy } from './mock-policy-config.mjs'
|
|
7
|
-
|
|
8
|
-
test('default rules = all gate rules incl. the default-on mock-internal-seam', () => {
|
|
9
|
-
assert.deepEqual(
|
|
10
|
-
selectRules().map((r) => r.id).sort(),
|
|
11
|
-
['mock-internal-seam', 'no-empty-test', 'no-fixed-timeout', 'no-skip', 'no-swallowed-assertion'],
|
|
12
|
-
)
|
|
13
|
-
assert.equal(RULES.length, 4) // the four authoring gates; mock-internal-seam lives in ALL_RULES
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
test('no-skip rule flags skip/only/xfail (carries rule + kind)', () => {
|
|
17
|
-
// non-empty bodies so this isolates no-skip from the no-empty-test rule
|
|
18
|
-
const f = scanText('a.spec.ts', 'test.skip("x", () => { go() })\ndescribe.only("y", () => { go() })')
|
|
19
|
-
assert.deepEqual(f.map((x) => [x.rule, x.kind]), [['no-skip', 'skip'], ['no-skip', 'only']])
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
test('no-fixed-timeout flags waitForTimeout / numeric cy.wait / time.sleep', () => {
|
|
23
|
-
const f = scanText('a.spec.ts', [
|
|
24
|
-
'await page.waitForTimeout(500)',
|
|
25
|
-
"cy.wait(1000)",
|
|
26
|
-
'time.sleep(2)',
|
|
27
|
-
].join('\n'))
|
|
28
|
-
assert.deepEqual(f.map((x) => x.kind), ['waitForTimeout', 'cy.wait', 'time.sleep'])
|
|
29
|
-
assert.ok(f.every((x) => x.rule === 'no-fixed-timeout'))
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('no-fixed-timeout does NOT flag an aliased cy.wait (not a fixed sleep)', () => {
|
|
33
|
-
assert.deepEqual(scanText('a.spec.ts', "cy.wait('@getUser')"), [])
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
test('--rules selection runs only the chosen rule', () => {
|
|
37
|
-
const text = 'test.skip("x", () => {})\nawait page.waitForTimeout(500)'
|
|
38
|
-
const onlySkip = scanText('a.spec.ts', text, ['no-skip'])
|
|
39
|
-
assert.deepEqual(onlySkip.map((x) => x.rule), ['no-skip'])
|
|
40
|
-
const onlyTimeout = scanText('a.spec.ts', text, ['no-fixed-timeout'])
|
|
41
|
-
assert.deepEqual(onlyTimeout.map((x) => x.rule), ['no-fixed-timeout'])
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('a single line can trip two different rules', () => {
|
|
45
|
-
// contrived but valid: a skipped test on the same line as a timeout call
|
|
46
|
-
const f = scanText('a.spec.ts', 'test.skip("x", async () => { await page.waitForTimeout(1) })')
|
|
47
|
-
assert.deepEqual(f.map((x) => x.rule).sort(), ['no-fixed-timeout', 'no-skip'])
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('scanDiff flags only ADDED violations in test files, with new-file line numbers', () => {
|
|
51
|
-
const diff = [
|
|
52
|
-
'+++ b/e2e/x.spec.ts',
|
|
53
|
-
'@@ -10,2 +10,3 @@',
|
|
54
|
-
' context',
|
|
55
|
-
'+await page.waitForTimeout(300)',
|
|
56
|
-
'-old line',
|
|
57
|
-
].join('\n')
|
|
58
|
-
const f = scanDiff(diff)
|
|
59
|
-
assert.equal(f.length, 1)
|
|
60
|
-
assert.equal(f[0].rule, 'no-fixed-timeout')
|
|
61
|
-
assert.equal(f[0].line, 11)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test('scanDiff ignores violations added to NON-test files', () => {
|
|
65
|
-
const diff = ['+++ b/src/util.ts', '@@ -1,1 +1,2 @@', '+await sleepThing.waitForTimeout(5)'].join('\n')
|
|
66
|
-
assert.deepEqual(scanDiff(diff), [])
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('isTestFile is shared and recognizes spec/test paths, rejects app code', () => {
|
|
70
|
-
for (const p of ['e2e/a.spec.ts', 'src/x.test.tsx', 'tests/e2e/b.spec.js', 'api/test_views.py', 'pkg/foo_test.py', '__tests__/c.ts']) {
|
|
71
|
-
assert.equal(isTestFile(p), true, p)
|
|
72
|
-
}
|
|
73
|
-
for (const p of ['src/app/page.tsx', 'lib/util.ts', 'README.md']) {
|
|
74
|
-
assert.equal(isTestFile(p), false, p)
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// ---- no-skip rule: richer cases (folded in from the former skip-lint suite) ----
|
|
79
|
-
|
|
80
|
-
test('no-skip leaves conditional skips to test-quality (a lead, not a gate)', () => {
|
|
81
|
-
// A runtime or annotated skip depends on a condition: legitimate for a platform
|
|
82
|
-
// CI still runs elsewhere, a hole otherwise. A gate cannot tell which.
|
|
83
|
-
const f = scanText('a.spec.ts', 'if (!user) test.skip();\ntest.skip(true, "https://github.com/o/r/issues/1")')
|
|
84
|
-
assert.equal(f.filter((x) => x.rule === 'no-skip').length, 0)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test('no-skip flags unconditional Python skips, not skipif/runtime skip/xfail', () => {
|
|
88
|
-
const text = [
|
|
89
|
-
'@pytest.mark.skip(reason="x")',
|
|
90
|
-
'@pytest.mark.skipif(cond)',
|
|
91
|
-
'@unittest.skip("x")',
|
|
92
|
-
' pytest.skip("x")',
|
|
93
|
-
'@pytest.mark.xfail',
|
|
94
|
-
].join('\n')
|
|
95
|
-
assert.deepEqual(scanText('test_x.py', text).filter((f) => f.rule === 'no-skip').map((f) => [f.line, f.kind]), [[1, 'skip'], [3, 'skip']])
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
test('no-skip flags declared-skipped and todo tests', () => {
|
|
99
|
-
const f = scanText('a.spec.ts', 'test.skip("later", () => { go() })\ntest.todo("write me")')
|
|
100
|
-
assert.deepEqual(f.filter((x) => x.rule === 'no-skip').map((x) => x.kind), ['skip', 'todo'])
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
test('no-skip does not flag a commented-out or doc mention', () => {
|
|
104
|
-
const text = [
|
|
105
|
-
'// never use test.skip() — banned',
|
|
106
|
-
'# doc: test.skip is not allowed',
|
|
107
|
-
'const xs = items.skip(2) // unrelated array method',
|
|
108
|
-
].join('\n')
|
|
109
|
-
assert.deepEqual(scanText('a.spec.ts', text), [])
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('no-skip does not flag .only/.skip method calls that are not test forms', () => {
|
|
113
|
-
assert.deepEqual(scanText('a.spec.ts', 'arr.skip(1); foo.only;'), [])
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
// ---- no-empty-test rule: the decidable floor of the Missing-Assertion smell ----
|
|
117
|
-
|
|
118
|
-
test('no-empty-test flags empty arrow/function/playwright/.each/.concurrent bodies', () => {
|
|
119
|
-
const cases = [
|
|
120
|
-
"test('a', () => {})",
|
|
121
|
-
'it("b", async () => {})',
|
|
122
|
-
"test('c', function () {})",
|
|
123
|
-
"test('d', async ({ page }) => {})",
|
|
124
|
-
"it.each([1,2])('e', () => {})",
|
|
125
|
-
"test.concurrent('f', () => {})",
|
|
126
|
-
]
|
|
127
|
-
for (const c of cases) {
|
|
128
|
-
const f = scanText('a.spec.ts', c, ['no-empty-test'])
|
|
129
|
-
assert.equal(f.length, 1, `should flag: ${c}`)
|
|
130
|
-
assert.equal(f[0].kind, 'empty-test')
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test('no-empty-test does NOT flag a test that has a body', () => {
|
|
135
|
-
const cases = [
|
|
136
|
-
"test('a', () => { expect(1).toBe(1) })",
|
|
137
|
-
"it('b', async () => { await verifyDashboard(page) })", // asserts via helper — not empty, not flagged
|
|
138
|
-
"test('c', () => { doThing() })",
|
|
139
|
-
]
|
|
140
|
-
for (const c of cases) assert.deepEqual(scanText('a.spec.ts', c, ['no-empty-test']), [], c)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
test('no-empty-test does NOT flag describe/suite, look-alike identifiers, or 1-arg calls', () => {
|
|
144
|
-
const text = [
|
|
145
|
-
"describe('group', () => {})", // empty suite is not a fake test
|
|
146
|
-
"emit('evt', () => {})", // 'it' inside emit — word boundary protects
|
|
147
|
-
"submit('x', () => {})", // 'it' inside submit
|
|
148
|
-
"it('pending')", // no callback — not an empty-bodied test
|
|
149
|
-
'const t = () => {}', // not a test call
|
|
150
|
-
].join('\n')
|
|
151
|
-
assert.deepEqual(scanText('a.spec.ts', text, ['no-empty-test']), [])
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test('no-empty-test leaves a multi-line empty body to the review lead (no false block)', () => {
|
|
155
|
-
// The opening line has no closing brace, so the single-line gate does not fire.
|
|
156
|
-
assert.deepEqual(scanText('a.spec.ts', "it('x', () => {\n})", ['no-empty-test']), [])
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
// ---- no-swallowed-assertion rule: the vacuous-green form behind #237 ----
|
|
160
|
-
|
|
161
|
-
test('no-swallowed-assertion flags a matcher whose rejection is eaten by .catch()', () => {
|
|
162
|
-
const cases = [
|
|
163
|
-
"await expect(page.locator('#cta')).not.toBeVisible().catch(() => {})", // the #237 form
|
|
164
|
-
"await expect(btn).toHaveText('Sign in').catch(() => null)",
|
|
165
|
-
'await expect(x).toBe(1).catch(() => {})',
|
|
166
|
-
]
|
|
167
|
-
for (const c of cases) {
|
|
168
|
-
const f = scanText('a.spec.ts', c, ['no-swallowed-assertion'])
|
|
169
|
-
assert.equal(f.length, 1, `should flag: ${c}`)
|
|
170
|
-
assert.equal(f[0].kind, 'swallowed-catch')
|
|
171
|
-
assert.equal(f[0].rule, 'no-swallowed-assertion')
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
test('no-swallowed-assertion does NOT flag a legitimate assertion or an unrelated .catch', () => {
|
|
176
|
-
const cases = [
|
|
177
|
-
"await expect(page.locator('#cta')).toBeVisible()", // real assertion, no swallow
|
|
178
|
-
"const v = await fetchThing().catch(() => fallback)", // .catch on setup, not a matcher
|
|
179
|
-
"promise.then(ok).catch(handleError)", // plain promise handling
|
|
180
|
-
]
|
|
181
|
-
for (const c of cases) assert.deepEqual(scanText('a.spec.ts', c, ['no-swallowed-assertion']), [], c)
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
test('no-swallowed-assertion is a default gate and flags it in a PR diff', () => {
|
|
185
|
-
const diff = [
|
|
186
|
-
'+++ b/e2e/login.sweep.spec.ts',
|
|
187
|
-
'@@ -1,1 +1,2 @@',
|
|
188
|
-
' context',
|
|
189
|
-
"+await expect(page.locator('#cta')).not.toBeVisible().catch(() => {})",
|
|
190
|
-
].join('\n')
|
|
191
|
-
const f = scanDiff(diff) // default rule set, no explicit --rules
|
|
192
|
-
assert.equal(f.length, 1)
|
|
193
|
-
assert.equal(f[0].rule, 'no-swallowed-assertion')
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
test('stripComment removes // and # trailing comments', () => {
|
|
197
|
-
assert.equal(stripComment('code // test.skip()').includes('test.skip'), false)
|
|
198
|
-
assert.equal(stripComment('code # pytest.skip').includes('pytest.skip'), false)
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
test('scanDiff ignores a pre-existing skip on an unchanged (context) line', () => {
|
|
202
|
-
const diff = ['+++ b/e2e/x.spec.ts', '@@ -1,2 +1,3 @@', ' test.skip("inherited", () => {})', '+test("added clean", () => { ok() })'].join('\n')
|
|
203
|
-
assert.deepEqual(scanDiff(diff), [])
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
// ---- mock-internal-seam rule: the zero-tolerance, default-on over-mock gate ----
|
|
207
|
-
|
|
208
|
-
const SEAM_CTX = { policy: resolveMockPolicy({ firstPartyPackages: ['@agentier/'] }) }
|
|
209
|
-
|
|
210
|
-
test('mock-internal-seam is default-on: in ALL_RULES and in the default gate set', () => {
|
|
211
|
-
assert.ok(ALL_RULES.some((r) => r.id === 'mock-internal-seam'))
|
|
212
|
-
// zero-tolerance: it runs by default, no opt-in needed
|
|
213
|
-
assert.ok(selectRules().some((r) => r.id === 'mock-internal-seam'))
|
|
214
|
-
assert.deepEqual(
|
|
215
|
-
selectRules().map((r) => r.id).sort(),
|
|
216
|
-
['mock-internal-seam', 'no-empty-test', 'no-fixed-timeout', 'no-skip', 'no-swallowed-assertion'],
|
|
217
|
-
)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
test('mock-internal-seam cannot be opted out via config (enabled:false is ignored)', () => {
|
|
221
|
-
// the gate is defaultOn:true; selectRules does not consult policy.enabled
|
|
222
|
-
assert.ok(selectRules().some((r) => r.id === 'mock-internal-seam'))
|
|
223
|
-
// and naming a subset still resolves it from the registry
|
|
224
|
-
assert.deepEqual(selectRules(['mock-internal-seam']).map((r) => r.id), ['mock-internal-seam'])
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
test('mock-internal-seam flags a first-party alias mock (the studio incident)', () => {
|
|
228
|
-
const f = scanText('a.test.ts', "vi." + "mock('@/lib/org-allowlist-client')", ['mock-internal-seam'], SEAM_CTX)
|
|
229
|
-
assert.equal(f.length, 1)
|
|
230
|
-
assert.equal(f[0].rule, 'mock-internal-seam')
|
|
231
|
-
assert.equal(f[0].detail, '@/lib/org-allowlist-client')
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
test('mock-internal-seam exempts stdlib and third-party packages', () => {
|
|
235
|
-
const text = ["vi.mock('node:fs')", "jest.mock('react')", "vi.mock('@playwright/test')"].join('\n')
|
|
236
|
-
assert.deepEqual(scanText('a.test.ts', text, ['mock-internal-seam'], SEAM_CTX), [])
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
test('mock-internal-seam ignores an inline @mock-ok comment (no waivers)', () => {
|
|
240
|
-
const text = "vi." + "mock('@/lib/cp') // @mock-ok: covered by cp.real.test.ts"
|
|
241
|
-
const f = scanText('a.test.ts', text, ['mock-internal-seam'], SEAM_CTX)
|
|
242
|
-
assert.equal(f.length, 1) // still flagged — zero-tolerance, no escape hatch
|
|
243
|
-
assert.equal(f[0].detail, '@/lib/cp')
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
test('mock-internal-seam flags a declared first-party workspace package', () => {
|
|
247
|
-
const f = scanText('a.test.ts', "vi.mock('@agentier/shared')", ['mock-internal-seam'], SEAM_CTX)
|
|
248
|
-
assert.equal(f.length, 1)
|
|
249
|
-
assert.equal(f[0].detail, '@agentier/shared')
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
test('mock-internal-seam fires with NO ctx policy (never silently inert; @/ defaults apply)', () => {
|
|
253
|
-
// zero-tolerance: omitting config must not disable the gate — it falls back
|
|
254
|
-
// to DEFAULT_MOCK_POLICY, which flags conventional @/ first-party mocks.
|
|
255
|
-
const f = scanText('a.test.ts', "vi." + "mock('@/lib/x')", ['mock-internal-seam'])
|
|
256
|
-
assert.equal(f.length, 1)
|
|
257
|
-
assert.equal(f[0].detail, '@/lib/x')
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
test('mock-internal-seam flags an ADDED first-party mock in a PR diff (PR lane)', () => {
|
|
261
|
-
const diff = ['+++ b/src/__tests__/x.test.ts', '@@ -1,1 +1,2 @@', " import x", "+vi." + "mock('@/lib/cp')"].join('\n')
|
|
262
|
-
const f = scanDiff(diff, ['mock-internal-seam'], SEAM_CTX)
|
|
263
|
-
assert.equal(f.length, 1)
|
|
264
|
-
assert.equal(f[0].rule, 'mock-internal-seam')
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
test('mock-internal-seam flags an INHERITED (un-added) mock in a whole-tree scan (no grandfathering)', () => {
|
|
268
|
-
// whole-tree lane: scanText sees every line, not just diff additions, so the
|
|
269
|
-
// 781-style inherited debt is flagged until removed.
|
|
270
|
-
const f = scanText('src/__tests__/x.test.ts', "import x\nvi.mock('@/lib/cp')\n", ['mock-internal-seam'], SEAM_CTX)
|
|
271
|
-
assert.equal(f.length, 1)
|
|
272
|
-
assert.equal(f[0].detail, '@/lib/cp')
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
test('mock-internal-seam does not fire on a commented-out mock line', () => {
|
|
276
|
-
assert.deepEqual(scanText('a.test.ts', "// vi.mock('@/lib/x')", ['mock-internal-seam'], SEAM_CTX), [])
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
test('--help prints usage and exits 0 without scanning the tree', () => {
|
|
280
|
-
const lines = []
|
|
281
|
-
const orig = console.log
|
|
282
|
-
console.log = (s) => lines.push(String(s))
|
|
283
|
-
try {
|
|
284
|
-
assert.equal(runCli(['--help']), 0)
|
|
285
|
-
} finally {
|
|
286
|
-
console.log = orig
|
|
287
|
-
}
|
|
288
|
-
assert.match(lines.join('\n'), /usage: lint-tests\.mjs/)
|
|
289
|
-
})
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// Tests for the mock-policy classifier (pure; one fs case via a temp dir).
|
|
2
|
-
import { test } from 'node:test'
|
|
3
|
-
import assert from 'node:assert/strict'
|
|
4
|
-
import fs from 'node:fs'
|
|
5
|
-
import os from 'node:os'
|
|
6
|
-
import path from 'node:path'
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
DEFAULT_MOCK_POLICY,
|
|
10
|
-
resolveMockPolicy,
|
|
11
|
-
loadMockPolicy,
|
|
12
|
-
loadMockPolicyFromFile,
|
|
13
|
-
extractMockTarget,
|
|
14
|
-
classifyMockTarget,
|
|
15
|
-
} from './mock-policy-config.mjs'
|
|
16
|
-
|
|
17
|
-
test('default policy is zero-tolerance: default-on, no waiver tag', () => {
|
|
18
|
-
assert.equal(DEFAULT_MOCK_POLICY.enabled, true)
|
|
19
|
-
// the inline-waiver escape hatch is gone
|
|
20
|
-
assert.equal(DEFAULT_MOCK_POLICY.waiverTag, undefined)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('extractMockTarget pulls the specifier from vi/jest/vitest mock+doMock', () => {
|
|
24
|
-
assert.equal(extractMockTarget("vi." + "mock('@/lib/x')"), '@/lib/x')
|
|
25
|
-
assert.equal(extractMockTarget('jest.' + 'mock("pkg")'), 'pkg')
|
|
26
|
-
assert.equal(extractMockTarget('vi.' + 'doMock(`@/a/b`)'), '@/a/b')
|
|
27
|
-
assert.equal(extractMockTarget('vitest.' + 'mock("./rel")'), './rel')
|
|
28
|
-
assert.equal(extractMockTarget('const x = mockThing()'), null) // bare mock() is not a module mock
|
|
29
|
-
assert.equal(extractMockTarget('expect(x).toBe(1)'), null)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('classify: first-party alias @/ and ~/ are internal seams (the incident case)', () => {
|
|
33
|
-
const p = resolveMockPolicy({})
|
|
34
|
-
assert.equal(classifyMockTarget('@/lib/org-allowlist-client', p).internal, true)
|
|
35
|
-
assert.equal(classifyMockTarget('~/services/cp', p).internal, true)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test('classify: stdlib and third-party packages are external (exempt)', () => {
|
|
39
|
-
const p = resolveMockPolicy({})
|
|
40
|
-
assert.equal(classifyMockTarget('node:fs', p).internal, false)
|
|
41
|
-
assert.equal(classifyMockTarget('react', p).internal, false)
|
|
42
|
-
assert.equal(classifyMockTarget('@playwright/test', p).internal, false)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
test('classify: relative import into source is internal; into a test util/fixture is not', () => {
|
|
46
|
-
const p = resolveMockPolicy({})
|
|
47
|
-
assert.equal(classifyMockTarget('../services/payments', p).internal, true)
|
|
48
|
-
assert.equal(classifyMockTarget('./helpers/operator-session-store', p).internal, false)
|
|
49
|
-
assert.equal(classifyMockTarget('../__mocks__/cp', p).internal, false)
|
|
50
|
-
assert.equal(classifyMockTarget('../fixtures/data', p).internal, false)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test('classify: declared first-party workspace package is internal', () => {
|
|
54
|
-
const p = resolveMockPolicy({ firstPartyPackages: ['@acme/'] })
|
|
55
|
-
assert.equal(classifyMockTarget('@acme/db', p).internal, true)
|
|
56
|
-
assert.equal(classifyMockTarget('@acme', p).internal, true)
|
|
57
|
-
assert.equal(classifyMockTarget('@other/db', p).internal, false)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test('classify: declared externals allowlist exempts a first-party-looking target', () => {
|
|
61
|
-
const p = resolveMockPolicy({ externals: ['^@/generated/'] })
|
|
62
|
-
assert.equal(classifyMockTarget('@/generated/openapi', p).internal, false) // exempted
|
|
63
|
-
assert.equal(classifyMockTarget('@/lib/x', p).internal, true) // still flagged
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
test('compiled _externalsRe is non-enumerable: does not leak into JSON / enumeration', () => {
|
|
67
|
-
const p = resolveMockPolicy({ externals: ['^@/generated/'] })
|
|
68
|
-
// the compiled RegExp cache works...
|
|
69
|
-
assert.equal(classifyMockTarget('@/generated/openapi', p).internal, false)
|
|
70
|
-
// ...but is hidden from enumeration and serialization
|
|
71
|
-
assert.ok(!Object.keys(p).includes('_externalsRe'))
|
|
72
|
-
assert.equal(JSON.parse(JSON.stringify(p))._externalsRe, undefined)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('loadMockPolicy reads coverage.config.json topology, defaults to on with no config', () => {
|
|
76
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mpc-'))
|
|
77
|
-
// no config → zero-tolerance default (on), conventional topology
|
|
78
|
-
assert.equal(loadMockPolicy(dir).enabled, true)
|
|
79
|
-
// with config → declared topology is merged in (config declares topology, not on/off)
|
|
80
|
-
fs.writeFileSync(
|
|
81
|
-
path.join(dir, 'coverage.config.json'),
|
|
82
|
-
JSON.stringify({ mockPolicy: { firstPartyPackages: ['@acme/'] } }),
|
|
83
|
-
)
|
|
84
|
-
const p = loadMockPolicy(dir)
|
|
85
|
-
assert.equal(p.enabled, true)
|
|
86
|
-
assert.deepEqual(p.firstPartyPackages, ['@acme/'])
|
|
87
|
-
// inherited default array still present
|
|
88
|
-
assert.ok(p.firstPartyAliasPrefixes.includes('@/'))
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
test('loadMockPolicy never throws on malformed config (and stays on)', () => {
|
|
92
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mpc-'))
|
|
93
|
-
fs.writeFileSync(path.join(dir, 'coverage.config.json'), '{ not json')
|
|
94
|
-
assert.equal(loadMockPolicy(dir).enabled, true)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
test('loadMockPolicyFromFile reads the NAMED file (finding #1: not just its dirname)', () => {
|
|
98
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mpc-'))
|
|
99
|
-
const file = path.join(dir, 'team-coverage.json') // deliberately non-standard name
|
|
100
|
-
fs.writeFileSync(file, JSON.stringify({ mockPolicy: { enabled: true, externals: ['^@/generated/'] } }))
|
|
101
|
-
const p = loadMockPolicyFromFile(file)
|
|
102
|
-
assert.equal(p.enabled, true)
|
|
103
|
-
// the declared externals must actually apply (proof the file was read, not defaults)
|
|
104
|
-
assert.equal(classifyMockTarget('@/generated/openapi', p).internal, false)
|
|
105
|
-
assert.equal(classifyMockTarget('@/lib/auth', p).internal, true)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
test('loadMockPolicyFromFile falls back to defaults when the named file is missing', () => {
|
|
109
|
-
// finding #1: a declared config that does not apply must not look like a clean pass.
|
|
110
|
-
// (It warns on stderr; here we assert the zero-tolerance default — gate stays ON.)
|
|
111
|
-
const p = loadMockPolicyFromFile(path.join(os.tmpdir(), 'definitely-absent-config-xyz.json'))
|
|
112
|
-
assert.equal(p.enabled, true)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
test('loadMockPolicyFromFile with no argument discovers coverage.config.json in cwd', () => {
|
|
116
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mpc-'))
|
|
117
|
-
fs.writeFileSync(path.join(dir, 'coverage.config.json'), JSON.stringify({ mockPolicy: { enabled: true } }))
|
|
118
|
-
assert.equal(loadMockPolicyFromFile(undefined, dir).enabled, true)
|
|
119
|
-
})
|