spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

mods: the kit's helpers are mock.clock, mock.store and mock.env, MemoryClock is MockClock, and a rendered tree's text is the diff tests' own fixture; the declarations carry 'claude-code/testing' as generated from the kit's sourceBrowse files
poteat authored on Sep 12, 2026 parent ead8806 · commit f56a625702fa
8 changed files+53 -33compared with ead8806
Mmods/README.md
Mmods/diff/tests/fixtures/in-repository.ts
Mmods/diff/tests/fixtures/index.ts
Mmods/diff/tests/fixtures/starts-session.ts
Amods/diff/tests/fixtures/text-of.ts
Mmods/diff/tests/register.test.ts
Mmods/telemetry/tests/register.test.ts
Mmods/types/claude-code.d.ts
@@ -36,14 +36,14 @@
3636 what several tests share sits under `tests/fixtures/`, one export a file.
3737
3838 ```ts
39-import { describe, expect, memoryClock, test, tier } from 'claude-code/testing'
39+import { describe, expect, mock, test, tier } from 'claude-code/testing'
4040
4141 tier('builtin')
4242
4343 describe('register', () => {
4444 test('outside a git repository /diff says so, opens nothing', async ($, on) => {
4545 const opened: string[] = []
46- memoryClock(on)
46+ mock.clock(on)
4747 on('session.start', ($, e) => ({ cwd: e.cwd }))
4848 on('command.register', ($, e) => ({ value: { command: e.name } }))
4949 on('process.run', () => ({
@@ -67,13 +67,13 @@
6767 })
6868 ```
6969
70-The kit's helpers are plain functions over `on`, each answering one noun
71-beneath the mod where the test calls it: `memoryEnv(on, variables)` for
72-`$.env`, `memoryStore(on, entries)` for `$.store`, and `memoryClock(on)` for
73-`$.clock`, whose `advance(ms)` resolves every wait the mod asked for
74-(`$.clock.sleep`, `after`, `every`) as the clock crosses it. `textOf(tree)`
75-reads a rendered tree's text, and `$.ui.press({ plugin, key })` presses a
76-`Button` the test rendered, as a click in the terminal does.
70+The kit's `mock` answers the world beneath the mod from memory, noun by noun,
71+each member a plain function over `on` registering hooks where the test calls
72+it: `mock.env(on, variables)` for `$.env`, `mock.store(on, entries)` for
73+`$.store`, and `mock.clock(on)` for `$.clock`, whose `advance(ms)` resolves
74+every wait the mod asked for (`$.clock.sleep`, `after`, `every`) as the clock
75+crosses it. `$.ui.press({ plugin, key })` presses a `Button` the test
76+rendered, as a click in the terminal does.
7777
7878 `tsc -p mods/tsconfig.json` typechecks every mod's hooks and tests against
7979 `types/`.
@@ -1,5 +1,5 @@
11 import type { Args, On } from 'claude-code'
2-import { memoryStore } from 'claude-code/testing'
2+import { mock } from 'claude-code/testing'
33
44 import { gitIn } from './git-in.js'
55 import { HINT_DRAWN } from './hint-drawn.js'
@@ -30,7 +30,7 @@
3030 on('ui.invalidate', () => ({ value: undefined }))
3131 on('ui.render', { component: 'PromptHint' }, () => HINT_DRAWN)
3232 on('session.messages', () => ({ value: [] }))
33- memoryStore(on)
33+ mock.store(on)
3434
3535 return { runs, opened: opened.kept, closed: closed.kept, clock }
3636 }
@@ -13,5 +13,6 @@
1313 export * from './session.js'
1414 export * from './settle-ms.js'
1515 export * from './starts-session.js'
16+export * from './text-of.js'
1617
1718 export * as default from '.'
@@ -1,6 +1,6 @@
11 import type { On } from 'claude-code'
2-import { memoryClock } from 'claude-code/testing'
3-import type { MemoryClock } from 'claude-code/testing'
2+import { mock } from 'claude-code/testing'
3+import type { MockClock } from 'claude-code/testing'
44
55 /**
66 * Answers what every /diff session asks first: its start, with its own
@@ -9,9 +9,9 @@
99 * @param on the test's `on`
1010 * @returns the clock the session reads, at 0 until the test moves it
1111 */
12-export function startsSession(on: On): MemoryClock {
12+export function startsSession(on: On): MockClock {
1313 on('session.start', ($, e) => ({ cwd: e.cwd }))
1414 on('command.register', ($, e) => ({ value: { command: e.name } }))
1515
16- return memoryClock(on)
16+ return mock.clock(on)
1717 }
@@ -1,0 +1,26 @@
1+/**
2+ * A rendered tree's text as it reads: its strings and its labels, in order.
3+ *
4+ * @param tree what `$.ui.render` resolved to, or a part of it
5+ * @returns the text
6+ */
7+export function textOf(tree: unknown): string {
8+ if (typeof tree === 'string' || typeof tree === 'number') {
9+ return String(tree)
10+ }
11+
12+ if (Array.isArray(tree)) {
13+ return tree.map(textOf).join('')
14+ }
15+
16+ if (typeof tree !== 'object' || !tree) {
17+ return ''
18+ }
19+
20+ const props: unknown = Reflect.get(tree, 'props')
21+ const label: unknown =
22+ typeof props === 'object' && props ? Reflect.get(props, 'label') : undefined
23+ const lead = typeof label === 'string' ? label : ''
24+
25+ return `${lead}${textOf(Reflect.get(tree, 'children') ?? [])}`
26+}
@@ -1,11 +1,4 @@
1-import {
2- describe,
3- expect,
4- memoryClock,
5- test,
6- textOf,
7- tier,
8-} from 'claude-code/testing'
1+import { describe, expect, mock, test, tier } from 'claude-code/testing'
92
103 import Limits from '../hooks/limits'
114 import Fixtures from './fixtures'
@@ -73,7 +66,7 @@
7366
7467 test('when the built-in holds /diff, the mod stands down', async ($, on) => {
7568 const logged: string[] = []
76- memoryClock(on)
69+ mock.clock(on)
7770 on('session.start', ($, e) => ({ cwd: e.cwd }))
7871 on('command.register', () => ({ deny: Fixtures.BUILTIN_HOLDS }))
7972 on('command.run', () => ({ text: 'the built-in /diff ran' }))
@@ -93,7 +86,7 @@
9386
9487 test('a refusal the built-in did not cause is said aloud', async ($, on) => {
9588 const logged: string[] = []
96- memoryClock(on)
89+ mock.clock(on)
9790 on('session.start', ($, e) => ({ cwd: e.cwd }))
9891 on('command.register', () => ({
9992 deny: '32 commands are registered already',
@@ -121,7 +114,7 @@
121114 expect(world.opened.map(pane => pane.id)).toEqual(['diff'])
122115
123116 await world.clock.advance(Fixtures.SETTLE_MS)
124- const drawn = textOf(await $.ui.render(Fixtures.PANE))
117+ const drawn = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
125118
126119 expect(drawn).toContain('1 file changed')
127120 expect(drawn).toContain('app.ts')
@@ -1,4 +1,4 @@
1-import { describe, expect, memoryEnv, test, tier } from 'claude-code/testing'
1+import { describe, expect, mock, test, tier } from 'claude-code/testing'
22
33 import Fixtures from './fixtures'
44
@@ -9,7 +9,7 @@
99 'a $.telemetry.log call from a plugin posts one first-party row',
1010 { plugins: [Fixtures.recording] },
1111 async ($, on) => {
12- memoryEnv(on, { USER_TYPE: 'ant' })
12+ mock.env(on, { USER_TYPE: 'ant' })
1313 const posts = Fixtures.firstPartySession(on)
1414
1515 expect(
@@ -39,7 +39,7 @@
3939 'a row already named tengu_ is sent under its own name',
4040 { plugins: [Fixtures.recording] },
4141 async ($, on) => {
42- memoryEnv(on, {})
42+ mock.env(on, {})
4343 const posts = Fixtures.firstPartySession(on)
4444
4545 await $.command.run(
@@ -68,7 +68,7 @@
6868 'nothing is sent for a person who asked not to be tracked',
6969 { plugins: [Fixtures.recording] },
7070 async ($, on) => {
71- memoryEnv(on, { DO_NOT_TRACK: '1' })
71+ mock.env(on, { DO_NOT_TRACK: '1' })
7272 const posts = Fixtures.firstPartySession(on)
7373 const { text } = await $.command.run(
7474 Fixtures.record(Fixtures.surveyAnswer()),
@@ -82,7 +82,7 @@
8282 'nothing is sent on a third-party provider',
8383 { plugins: [Fixtures.recording] },
8484 async ($, on) => {
85- memoryEnv(on, { CLAUDE_CODE_USE_BEDROCK: '1' })
85+ mock.env(on, { CLAUDE_CODE_USE_BEDROCK: '1' })
8686 const posts = Fixtures.firstPartySession(on)
8787
8888 await $.command.run(Fixtures.record(Fixtures.surveyAnswer()))
@@ -95,7 +95,7 @@
9595 'a session with no first-party credential is refused, nothing sent',
9696 { plugins: [Fixtures.recording] },
9797 async ($, on) => {
98- memoryEnv(on, {})
98+ mock.env(on, {})
9999 const posts = Fixtures.firstPartySession(on, null)
100100 const { text } = await $.command.run(
101101 Fixtures.record(Fixtures.surveyAnswer()),
@@ -113,7 +113,7 @@
113113 'free text in a row is refused, nothing sent',
114114 { plugins: [Fixtures.recording] },
115115 async ($, on) => {
116- memoryEnv(on, {})
116+ mock.env(on, {})
117117 const posts = Fixtures.firstPartySession(on)
118118 const { text } = await $.command.run({
119119 ...Fixtures.record(Fixtures.surveyAnswer()),
Large file, diff not shown.