smithers-orchestrator 0.1.13 → 0.1.15
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/bunfig.toml +3 -3
- package/package.json +6 -1
- package/preload.ts +9 -0
- package/src/components/Claude.test.tsx +97 -0
- package/src/components/Git/Commit.test.tsx +109 -0
- package/src/components/Git/Notes.test.tsx +92 -0
- package/src/components/Hooks/OnCIFailure.test.tsx +110 -0
- package/src/components/Hooks/PostCommit.test.tsx +75 -0
- package/src/components/JJ/Commit.test.tsx +56 -0
- package/src/components/JJ/Describe.test.tsx +49 -0
- package/src/components/JJ/Rebase.test.tsx +67 -0
- package/src/components/JJ/Snapshot.test.tsx +42 -0
- package/src/components/JJ/Status.test.tsx +56 -0
- package/src/components/Ralph.test.tsx +111 -0
- package/src/components/Review.test.tsx +231 -0
- package/src/components/components.test.tsx +149 -0
- package/src/core/root.test.ts +83 -0
- package/src/core/serialize.test.ts +205 -0
- package/src/jsx-runtime.test.ts +237 -0
- package/src/orchestrator/monitor/output-parser.test.ts +165 -0
- package/src/orchestrator/monitor/stream-formatter.test.ts +224 -0
- package/src/orchestrator/tools/registry.test.ts +234 -0
- package/src/utils/vcs.test.ts +11 -1
package/bunfig.toml
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Bun configuration for Smithers
|
|
2
2
|
# https://bun.sh/docs/runtime/bunfig
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
# Use bun-plugin-solid for Solid JSX transform
|
|
5
|
+
preload = ["./preload.ts"]
|
|
6
6
|
|
|
7
7
|
[install]
|
|
8
8
|
auto = "fallback"
|
|
9
9
|
|
|
10
10
|
[test]
|
|
11
|
-
preload = ["./test/preload.ts"]
|
|
11
|
+
preload = ["./preload.ts", "./test/preload.ts"]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smithers-orchestrator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Build AI agents with Solid.js - Declarative JSX for Claude orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"skills",
|
|
37
37
|
"templates",
|
|
38
38
|
"bunfig.toml",
|
|
39
|
+
"preload.ts",
|
|
39
40
|
"plugin.json",
|
|
40
41
|
"README.md"
|
|
41
42
|
],
|
|
@@ -48,7 +49,11 @@
|
|
|
48
49
|
"dependencies": {
|
|
49
50
|
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
|
50
51
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
52
|
+
"@babel/core": "^7.28.6",
|
|
53
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
54
|
+
"@dschz/bun-plugin-solid": "^1.0.4",
|
|
51
55
|
"@electric-sql/pglite": "^0.3.15",
|
|
56
|
+
"babel-preset-solid": "^1.9.10",
|
|
52
57
|
"commander": "^12.0.0",
|
|
53
58
|
"solid-js": "^1.9.10",
|
|
54
59
|
"zod": "^4.3.5"
|
package/preload.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Claude.tsx - Claude component interface tests.
|
|
3
|
+
* Tests the component's props and interface, not execution behavior.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
6
|
+
import type { ClaudeProps } from './Claude'
|
|
7
|
+
|
|
8
|
+
describe('ClaudeProps interface', () => {
|
|
9
|
+
test('model is optional string', () => {
|
|
10
|
+
const props: ClaudeProps = {}
|
|
11
|
+
expect(props.model).toBeUndefined()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('model can be set', () => {
|
|
15
|
+
const props: ClaudeProps = { model: 'claude-opus-4' }
|
|
16
|
+
expect(props.model).toBe('claude-opus-4')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('maxTurns is optional number', () => {
|
|
20
|
+
const props: ClaudeProps = { maxTurns: 5 }
|
|
21
|
+
expect(props.maxTurns).toBe(5)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('tools is optional string array', () => {
|
|
25
|
+
const props: ClaudeProps = { tools: ['Read', 'Edit', 'Bash'] }
|
|
26
|
+
expect(props.tools).toHaveLength(3)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('systemPrompt is optional string', () => {
|
|
30
|
+
const props: ClaudeProps = { systemPrompt: 'You are a helpful assistant' }
|
|
31
|
+
expect(props.systemPrompt).toBe('You are a helpful assistant')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('onFinished is optional callback', () => {
|
|
35
|
+
const callback = mock(() => {})
|
|
36
|
+
const props: ClaudeProps = { onFinished: callback }
|
|
37
|
+
|
|
38
|
+
props.onFinished?.('result')
|
|
39
|
+
expect(callback).toHaveBeenCalledWith('result')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('onError is optional callback', () => {
|
|
43
|
+
const callback = mock(() => {})
|
|
44
|
+
const props: ClaudeProps = { onError: callback }
|
|
45
|
+
|
|
46
|
+
const error = new Error('test')
|
|
47
|
+
props.onError?.(error)
|
|
48
|
+
expect(callback).toHaveBeenCalledWith(error)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('validate is optional async function', async () => {
|
|
52
|
+
const validate = mock(async () => true)
|
|
53
|
+
const props: ClaudeProps = { validate }
|
|
54
|
+
|
|
55
|
+
const result = await props.validate?.('test')
|
|
56
|
+
expect(result).toBe(true)
|
|
57
|
+
expect(validate).toHaveBeenCalledWith('test')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('validate can return false', async () => {
|
|
61
|
+
const validate = mock(async () => false)
|
|
62
|
+
const props: ClaudeProps = { validate }
|
|
63
|
+
|
|
64
|
+
const result = await props.validate?.('invalid')
|
|
65
|
+
expect(result).toBe(false)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('allows arbitrary additional props', () => {
|
|
69
|
+
const props: ClaudeProps = {
|
|
70
|
+
customProp: 'value',
|
|
71
|
+
numberProp: 42,
|
|
72
|
+
boolProp: true,
|
|
73
|
+
objectProp: { key: 'value' },
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
expect(props.customProp).toBe('value')
|
|
77
|
+
expect(props.numberProp).toBe(42)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('children is optional', () => {
|
|
81
|
+
const props: ClaudeProps = {}
|
|
82
|
+
expect(props.children).toBeUndefined()
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe('Claude component behavior', () => {
|
|
87
|
+
test('exports Claude function', async () => {
|
|
88
|
+
const { Claude } = await import('./Claude')
|
|
89
|
+
expect(typeof Claude).toBe('function')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('Claude is a valid Solid component', async () => {
|
|
93
|
+
const { Claude } = await import('./Claude')
|
|
94
|
+
// A component should accept props
|
|
95
|
+
expect(Claude.length).toBeLessThanOrEqual(1)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Git/Commit.tsx - Git commit component interface tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
5
|
+
import type { CommitProps, CommitResult } from './Commit'
|
|
6
|
+
|
|
7
|
+
describe('CommitProps interface', () => {
|
|
8
|
+
test('message is optional', () => {
|
|
9
|
+
const props: CommitProps = {}
|
|
10
|
+
expect(props.message).toBeUndefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('message can be set', () => {
|
|
14
|
+
const props: CommitProps = { message: 'Add new feature' }
|
|
15
|
+
expect(props.message).toBe('Add new feature')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('autoGenerate is optional boolean', () => {
|
|
19
|
+
const props: CommitProps = { autoGenerate: true }
|
|
20
|
+
expect(props.autoGenerate).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('notes is optional object', () => {
|
|
24
|
+
const props: CommitProps = { notes: { key: 'value', number: 42 } }
|
|
25
|
+
expect(props.notes?.key).toBe('value')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('files is optional string array', () => {
|
|
29
|
+
const props: CommitProps = { files: ['file1.ts', 'file2.ts'] }
|
|
30
|
+
expect(props.files).toHaveLength(2)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('all is optional boolean for -a flag', () => {
|
|
34
|
+
const props: CommitProps = { all: true }
|
|
35
|
+
expect(props.all).toBe(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('children can be used as message', () => {
|
|
39
|
+
const props: CommitProps = { children: 'Commit message via children' }
|
|
40
|
+
expect(props.children).toBe('Commit message via children')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('onFinished callback receives CommitResult', () => {
|
|
44
|
+
const callback = mock(() => {})
|
|
45
|
+
const props: CommitProps = { onFinished: callback }
|
|
46
|
+
|
|
47
|
+
const result: CommitResult = {
|
|
48
|
+
commitHash: 'abc123',
|
|
49
|
+
message: 'Test commit',
|
|
50
|
+
filesChanged: ['file.ts'],
|
|
51
|
+
insertions: 10,
|
|
52
|
+
deletions: 5,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
props.onFinished?.(result)
|
|
56
|
+
expect(callback).toHaveBeenCalledWith(result)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('onError callback receives Error', () => {
|
|
60
|
+
const callback = mock(() => {})
|
|
61
|
+
const props: CommitProps = { onError: callback }
|
|
62
|
+
|
|
63
|
+
const error = new Error('Commit failed')
|
|
64
|
+
props.onError?.(error)
|
|
65
|
+
expect(callback).toHaveBeenCalledWith(error)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('CommitResult interface', () => {
|
|
70
|
+
test('has all required fields', () => {
|
|
71
|
+
const result: CommitResult = {
|
|
72
|
+
commitHash: 'abc123def456',
|
|
73
|
+
message: 'Feature: add new capability',
|
|
74
|
+
filesChanged: ['src/index.ts', 'src/utils.ts'],
|
|
75
|
+
insertions: 100,
|
|
76
|
+
deletions: 50,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
expect(result.commitHash).toBe('abc123def456')
|
|
80
|
+
expect(result.message).toBe('Feature: add new capability')
|
|
81
|
+
expect(result.filesChanged).toHaveLength(2)
|
|
82
|
+
expect(result.insertions).toBe(100)
|
|
83
|
+
expect(result.deletions).toBe(50)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('filesChanged can be empty', () => {
|
|
87
|
+
const result: CommitResult = {
|
|
88
|
+
commitHash: 'abc123',
|
|
89
|
+
message: 'Empty commit',
|
|
90
|
+
filesChanged: [],
|
|
91
|
+
insertions: 0,
|
|
92
|
+
deletions: 0,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
expect(result.filesChanged).toHaveLength(0)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('Commit component', () => {
|
|
100
|
+
test('exports Commit function', async () => {
|
|
101
|
+
const { Commit } = await import('./Commit')
|
|
102
|
+
expect(typeof Commit).toBe('function')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('Commit is a valid Solid component', async () => {
|
|
106
|
+
const { Commit } = await import('./Commit')
|
|
107
|
+
expect(Commit.length).toBeLessThanOrEqual(1)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Git/Notes.tsx - Git notes component interface tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
5
|
+
import type { NotesProps, NotesResult } from './Notes'
|
|
6
|
+
|
|
7
|
+
describe('NotesProps interface', () => {
|
|
8
|
+
test('commitRef is optional, defaults to HEAD', () => {
|
|
9
|
+
const props: NotesProps = { data: { key: 'value' } }
|
|
10
|
+
expect(props.commitRef).toBeUndefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('commitRef can be set', () => {
|
|
14
|
+
const props: NotesProps = { commitRef: 'abc123', data: {} }
|
|
15
|
+
expect(props.commitRef).toBe('abc123')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('data is required object', () => {
|
|
19
|
+
const props: NotesProps = { data: { smithers: true, version: '1.0' } }
|
|
20
|
+
expect(props.data.smithers).toBe(true)
|
|
21
|
+
expect(props.data.version).toBe('1.0')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('append is optional boolean', () => {
|
|
25
|
+
const props: NotesProps = { data: {}, append: true }
|
|
26
|
+
expect(props.append).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('append defaults to false (replace)', () => {
|
|
30
|
+
const props: NotesProps = { data: {} }
|
|
31
|
+
expect(props.append).toBeUndefined() // Will default to false in component
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('onFinished callback receives NotesResult', () => {
|
|
35
|
+
const callback = mock(() => {})
|
|
36
|
+
const props: NotesProps = { data: {}, onFinished: callback }
|
|
37
|
+
|
|
38
|
+
const result: NotesResult = {
|
|
39
|
+
commitRef: 'HEAD',
|
|
40
|
+
data: { smithers: true },
|
|
41
|
+
previousNotes: null,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
props.onFinished?.(result)
|
|
45
|
+
expect(callback).toHaveBeenCalledWith(result)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('onError callback receives Error', () => {
|
|
49
|
+
const callback = mock(() => {})
|
|
50
|
+
const props: NotesProps = { data: {}, onError: callback }
|
|
51
|
+
|
|
52
|
+
const error = new Error('Notes failed')
|
|
53
|
+
props.onError?.(error)
|
|
54
|
+
expect(callback).toHaveBeenCalledWith(error)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('NotesResult interface', () => {
|
|
59
|
+
test('has all required fields', () => {
|
|
60
|
+
const result: NotesResult = {
|
|
61
|
+
commitRef: 'abc123',
|
|
62
|
+
data: { smithers: true, executionId: 'exec-1' },
|
|
63
|
+
previousNotes: null,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expect(result.commitRef).toBe('abc123')
|
|
67
|
+
expect(result.data.smithers).toBe(true)
|
|
68
|
+
expect(result.previousNotes).toBeNull()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('previousNotes can contain prior notes when appending', () => {
|
|
72
|
+
const result: NotesResult = {
|
|
73
|
+
commitRef: 'HEAD',
|
|
74
|
+
data: { newData: true },
|
|
75
|
+
previousNotes: '{"oldData": true}',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
expect(result.previousNotes).toBe('{"oldData": true}')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('Notes component', () => {
|
|
83
|
+
test('exports Notes function', async () => {
|
|
84
|
+
const { Notes } = await import('./Notes')
|
|
85
|
+
expect(typeof Notes).toBe('function')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('Notes is a valid Solid component', async () => {
|
|
89
|
+
const { Notes } = await import('./Notes')
|
|
90
|
+
expect(Notes.length).toBeLessThanOrEqual(1)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Hooks/OnCIFailure.tsx - CI failure hook component interface tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
5
|
+
import type { OnCIFailureProps, CIFailure } from './OnCIFailure'
|
|
6
|
+
|
|
7
|
+
describe('OnCIFailureProps interface', () => {
|
|
8
|
+
test('provider is required', () => {
|
|
9
|
+
const props: OnCIFailureProps = {
|
|
10
|
+
provider: 'github-actions',
|
|
11
|
+
children: null as any,
|
|
12
|
+
}
|
|
13
|
+
expect(props.provider).toBe('github-actions')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('pollInterval is optional', () => {
|
|
17
|
+
const props: OnCIFailureProps = {
|
|
18
|
+
provider: 'github-actions',
|
|
19
|
+
children: null as any,
|
|
20
|
+
}
|
|
21
|
+
expect(props.pollInterval).toBeUndefined()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('pollInterval can be set', () => {
|
|
25
|
+
const props: OnCIFailureProps = {
|
|
26
|
+
provider: 'github-actions',
|
|
27
|
+
pollInterval: 60000,
|
|
28
|
+
children: null as any,
|
|
29
|
+
}
|
|
30
|
+
expect(props.pollInterval).toBe(60000)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('onFailure is optional callback', () => {
|
|
34
|
+
const callback = mock(() => {})
|
|
35
|
+
const props: OnCIFailureProps = {
|
|
36
|
+
provider: 'github-actions',
|
|
37
|
+
onFailure: callback,
|
|
38
|
+
children: null as any,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const failure: CIFailure = {
|
|
42
|
+
failed: true,
|
|
43
|
+
runId: '12345',
|
|
44
|
+
workflowName: 'CI',
|
|
45
|
+
failedJobs: ['test', 'lint'],
|
|
46
|
+
logs: 'Error: test failed',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
props.onFailure?.(failure)
|
|
50
|
+
expect(callback).toHaveBeenCalledWith(failure)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('children is required', () => {
|
|
54
|
+
const props: OnCIFailureProps = {
|
|
55
|
+
provider: 'github-actions',
|
|
56
|
+
children: null as any,
|
|
57
|
+
}
|
|
58
|
+
expect(props.children).toBeNull()
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('CIFailure interface', () => {
|
|
63
|
+
test('minimal failure', () => {
|
|
64
|
+
const failure: CIFailure = {
|
|
65
|
+
failed: true,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
expect(failure.failed).toBe(true)
|
|
69
|
+
expect(failure.runId).toBeUndefined()
|
|
70
|
+
expect(failure.workflowName).toBeUndefined()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('full failure with all fields', () => {
|
|
74
|
+
const failure: CIFailure = {
|
|
75
|
+
failed: true,
|
|
76
|
+
runId: '123456789',
|
|
77
|
+
workflowName: 'Build and Test',
|
|
78
|
+
failedJobs: ['unit-tests', 'integration-tests'],
|
|
79
|
+
logs: 'Test failed at line 42',
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
expect(failure.failed).toBe(true)
|
|
83
|
+
expect(failure.runId).toBe('123456789')
|
|
84
|
+
expect(failure.workflowName).toBe('Build and Test')
|
|
85
|
+
expect(failure.failedJobs).toHaveLength(2)
|
|
86
|
+
expect(failure.logs).toContain('line 42')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('failure with empty failedJobs', () => {
|
|
90
|
+
const failure: CIFailure = {
|
|
91
|
+
failed: true,
|
|
92
|
+
runId: '123',
|
|
93
|
+
failedJobs: [],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
expect(failure.failedJobs).toHaveLength(0)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('OnCIFailure component', () => {
|
|
101
|
+
test('exports OnCIFailure function', async () => {
|
|
102
|
+
const { OnCIFailure } = await import('./OnCIFailure')
|
|
103
|
+
expect(typeof OnCIFailure).toBe('function')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('OnCIFailure is a valid Solid component', async () => {
|
|
107
|
+
const { OnCIFailure } = await import('./OnCIFailure')
|
|
108
|
+
expect(OnCIFailure.length).toBeLessThanOrEqual(1)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Hooks/PostCommit.tsx - Post commit hook component interface tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
5
|
+
import type { PostCommitProps } from './PostCommit'
|
|
6
|
+
|
|
7
|
+
describe('PostCommitProps interface', () => {
|
|
8
|
+
test('children is required', () => {
|
|
9
|
+
const props: PostCommitProps = {
|
|
10
|
+
children: null as any,
|
|
11
|
+
}
|
|
12
|
+
expect(props.children).toBeNull()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('runOn is optional', () => {
|
|
16
|
+
const props: PostCommitProps = {
|
|
17
|
+
children: null as any,
|
|
18
|
+
}
|
|
19
|
+
expect(props.runOn).toBeUndefined()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('runOn can be all', () => {
|
|
23
|
+
const props: PostCommitProps = {
|
|
24
|
+
children: null as any,
|
|
25
|
+
runOn: 'all',
|
|
26
|
+
}
|
|
27
|
+
expect(props.runOn).toBe('all')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('runOn can be smithers-only', () => {
|
|
31
|
+
const props: PostCommitProps = {
|
|
32
|
+
children: null as any,
|
|
33
|
+
runOn: 'smithers-only',
|
|
34
|
+
}
|
|
35
|
+
expect(props.runOn).toBe('smithers-only')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('async is optional boolean', () => {
|
|
39
|
+
const props: PostCommitProps = {
|
|
40
|
+
children: null as any,
|
|
41
|
+
}
|
|
42
|
+
expect(props.async).toBeUndefined()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('async can be true', () => {
|
|
46
|
+
const props: PostCommitProps = {
|
|
47
|
+
children: null as any,
|
|
48
|
+
async: true,
|
|
49
|
+
}
|
|
50
|
+
expect(props.async).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('all props together', () => {
|
|
54
|
+
const props: PostCommitProps = {
|
|
55
|
+
children: null as any,
|
|
56
|
+
runOn: 'smithers-only',
|
|
57
|
+
async: true,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
expect(props.runOn).toBe('smithers-only')
|
|
61
|
+
expect(props.async).toBe(true)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe('PostCommit component', () => {
|
|
66
|
+
test('exports PostCommit function', async () => {
|
|
67
|
+
const { PostCommit } = await import('./PostCommit')
|
|
68
|
+
expect(typeof PostCommit).toBe('function')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('PostCommit is a valid Solid component', async () => {
|
|
72
|
+
const { PostCommit } = await import('./PostCommit')
|
|
73
|
+
expect(PostCommit.length).toBeLessThanOrEqual(1)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for JJ/Commit.tsx - JJ commit component interface tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
5
|
+
import type { CommitProps } from './Commit'
|
|
6
|
+
|
|
7
|
+
describe('CommitProps interface', () => {
|
|
8
|
+
test('message is optional string', () => {
|
|
9
|
+
const props: CommitProps = {}
|
|
10
|
+
expect(props.message).toBeUndefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('message can be set', () => {
|
|
14
|
+
const props: CommitProps = { message: 'Add new feature' }
|
|
15
|
+
expect(props.message).toBe('Add new feature')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('autoDescribe is optional boolean', () => {
|
|
19
|
+
const props: CommitProps = { autoDescribe: true }
|
|
20
|
+
expect(props.autoDescribe).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('notes is optional string', () => {
|
|
24
|
+
const props: CommitProps = { notes: 'Additional metadata' }
|
|
25
|
+
expect(props.notes).toBe('Additional metadata')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('children is optional', () => {
|
|
29
|
+
const props: CommitProps = {}
|
|
30
|
+
expect(props.children).toBeUndefined()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('all props together', () => {
|
|
34
|
+
const props: CommitProps = {
|
|
35
|
+
message: 'Feature commit',
|
|
36
|
+
autoDescribe: false,
|
|
37
|
+
notes: '{"key": "value"}',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
expect(props.message).toBe('Feature commit')
|
|
41
|
+
expect(props.autoDescribe).toBe(false)
|
|
42
|
+
expect(props.notes).toBe('{"key": "value"}')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('Commit component', () => {
|
|
47
|
+
test('exports Commit function', async () => {
|
|
48
|
+
const { Commit } = await import('./Commit')
|
|
49
|
+
expect(typeof Commit).toBe('function')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('Commit is a valid Solid component', async () => {
|
|
53
|
+
const { Commit } = await import('./Commit')
|
|
54
|
+
expect(Commit.length).toBeLessThanOrEqual(1)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for JJ/Describe.tsx - JJ describe component interface tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, test, expect, mock } from 'bun:test'
|
|
5
|
+
import type { DescribeProps } from './Describe'
|
|
6
|
+
|
|
7
|
+
describe('DescribeProps interface', () => {
|
|
8
|
+
test('useAgent is optional', () => {
|
|
9
|
+
const props: DescribeProps = {}
|
|
10
|
+
expect(props.useAgent).toBeUndefined()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('useAgent can be claude', () => {
|
|
14
|
+
const props: DescribeProps = { useAgent: 'claude' }
|
|
15
|
+
expect(props.useAgent).toBe('claude')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('template is optional string', () => {
|
|
19
|
+
const props: DescribeProps = { template: 'feat: {summary}' }
|
|
20
|
+
expect(props.template).toBe('feat: {summary}')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('children is optional', () => {
|
|
24
|
+
const props: DescribeProps = {}
|
|
25
|
+
expect(props.children).toBeUndefined()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('all props together', () => {
|
|
29
|
+
const props: DescribeProps = {
|
|
30
|
+
useAgent: 'claude',
|
|
31
|
+
template: 'conventional-commits',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
expect(props.useAgent).toBe('claude')
|
|
35
|
+
expect(props.template).toBe('conventional-commits')
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('Describe component', () => {
|
|
40
|
+
test('exports Describe function', async () => {
|
|
41
|
+
const { Describe } = await import('./Describe')
|
|
42
|
+
expect(typeof Describe).toBe('function')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('Describe is a valid Solid component', async () => {
|
|
46
|
+
const { Describe } = await import('./Describe')
|
|
47
|
+
expect(Describe.length).toBeLessThanOrEqual(1)
|
|
48
|
+
})
|
|
49
|
+
})
|