Skip to content

Commit 4d424b0

Browse files
committed
fix(tests): discover central mocks from their declarations and accept the async-import form
1 parent c9a9663 commit 4d424b0

2 files changed

Lines changed: 43 additions & 6 deletions

File tree

‎scripts/check-test-patterns.ts‎

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* Run: `bun run check:test-patterns`
2020
*/
2121
import { execFileSync } from 'node:child_process'
22-
import { readFileSync, writeFileSync } from 'node:fs'
22+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs'
2323
import path from 'node:path'
2424
import { parse } from '@babel/parser'
2525

@@ -97,7 +97,10 @@ function mockedId(node: Node): string | undefined {
9797
return first?.type === 'StringLiteral' ? (first as { value: string }).value : undefined
9898
}
9999

100-
/** A factory of the form `() => fooMock` (or `() => ({ ...fooMock })`) using a central mock. */
100+
/**
101+
* A factory that uses a central mock as-is: `() => fooMock`, `() => ({ ...fooMock, override })`, or
102+
* `async () => (await import('@sim/testing/mocks/foo.mock')).fooMock`.
103+
*/
101104
function centralMockName(
102105
factory: Node | undefined,
103106
testingImports: Set<string>
@@ -112,9 +115,27 @@ function centralMockName(
112115
if (spread?.type === 'Identifier' && testingImports.has((spread as { name: string }).name)) {
113116
return (spread as { name: string }).name
114117
}
118+
const awaited = body.type === 'MemberExpression' ? (body.object as Node) : undefined
119+
if (awaited?.type === 'AwaitExpression' && importsTesting(awaited.argument as Node)) {
120+
return (body.property as { name: string }).name
121+
}
115122
return undefined
116123
}
117124

125+
/** A dynamic `import('@sim/testing/…')`. */
126+
function importsTesting(node: Node | undefined): boolean {
127+
const source =
128+
node?.type === 'CallExpression' && (node.callee as Node).type === 'Import'
129+
? (node.arguments as Node[])[0]
130+
: node?.type === 'ImportExpression'
131+
? (node.source as Node)
132+
: undefined
133+
return (
134+
source?.type === 'StringLiteral' &&
135+
(source as { value: string }).value.startsWith('@sim/testing')
136+
)
137+
}
138+
118139
function testFiles(): string[] {
119140
return execFileSync('git', ['ls-files', '*.test.ts', '*.test.tsx', '*.integration.ts'], {
120141
cwd: ROOT,
@@ -153,13 +174,32 @@ function canUseSharedMocks(file: string): boolean {
153174
return uses
154175
}
155176

177+
/**
178+
* Module ids the central mocks declare — each `packages/testing/src/mocks/*.mock.ts` documents its
179+
* target in an `@example` `vi.mock('<id>', () => xMock)` line. Reading the declarations (not just
180+
* current usages) keeps a module covered after its last conforming usage disappears.
181+
*/
182+
function declaredCentralMockIds(): Set<string> {
183+
const ids = new Set<string>()
184+
const dir = path.join(ROOT, 'packages/testing/src/mocks')
185+
for (const name of readdirSync(dir)) {
186+
if (!name.endsWith('.mock.ts')) continue
187+
for (const match of readFileSync(path.join(dir, name), 'utf8').matchAll(
188+
/vi\.mock\(\s*['"]([^'"]+)['"],\s*(?:async\s*)?\(\)\s*=>\s*(?:\(\{\s*\.\.\.)?(?:\(await import\([^)]*\)\)\.)?\w+Mock\b/g
189+
)) {
190+
ids.add(match[1])
191+
}
192+
}
193+
return ids
194+
}
195+
156196
function collect(): Violation[] {
157197
const files = testFiles()
158198
const globals = globalMockIds()
159199
const parsed = files.map((file) => ({ file, program: parseFile(file) }))
160200

161201
const testingImportsByFile = new Map<string, Set<string>>()
162-
const centralIds = new Set<string>(globals)
202+
const centralIds = new Set<string>([...globals, ...declaredCentralMockIds()])
163203
for (const { file, program } of parsed) {
164204
const imports = new Set<string>()
165205
for (const statement of program.body) {

‎scripts/test-patterns-baseline.json‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
"local-factory\tapps/sim/app/o/[organizationId]/settings/integrations/providers/[connectorType]/page.test.tsx\t@/lib/sim-search/connectors",
2626
"local-factory\tapps/sim/app/slack-search/connect/[token]/page.test.tsx\t@sim/emcn",
2727
"local-factory\tapps/sim/app/slack-search/install/[teamId]/page.test.tsx\t@sim/emcn",
28-
"local-factory\tapps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/editor-lifecycle.test.tsx\tnext/navigation",
2928
"local-factory\tapps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/terminal-session/use-terminal-close-confirmation.test.tsx\t@sim/emcn",
3029
"local-factory\tapps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.dom.test.tsx\t@/lib/api/client/request",
3130
"local-factory\tapps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connector-selector-field/connector-selector-field.test.tsx\t@sim/emcn",
@@ -45,13 +44,11 @@
4544
"local-factory\tapps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block-password.test.tsx\t@sim/emcn",
4645
"local-factory\tapps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils.test.ts\t@/blocks",
4746
"local-factory\tapps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.test.tsx\t@/blocks",
48-
"local-factory\tapps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/components/pending-invitations/view-invitations-modal.test.tsx\tnext/navigation",
4947
"local-factory\tapps/sim/background/cleanup-table-row-ttl.test.ts\t@/lib/table/constants",
5048
"local-factory\tapps/sim/blocks/agent-evaluation.test.ts\t@/blocks",
5149
"local-factory\tapps/sim/blocks/blocks/agent.test.ts\t@/blocks",
5250
"local-factory\tapps/sim/blocks/utils.test.ts\t@/providers/models",
5351
"local-factory\tapps/sim/components/mcp/operation-policy-editor.test.tsx\t@sim/emcn",
54-
"local-factory\tapps/sim/components/secrets/secrets-editor.test.tsx\tnext/navigation",
5552
"local-factory\tapps/sim/components/ui/generated-password-input.test.tsx\t@sim/emcn",
5653
"local-factory\tapps/sim/connectors/confluence/attachments.test.ts\t@/lib/knowledge/documents/secure-fetch.server",
5754
"local-factory\tapps/sim/ee/access-requests/components/permission-access-boundary.test.tsx\t@sim/emcn",

0 commit comments

Comments
 (0)