Skip to content

Commit d0f45e0

Browse files
committed
chore: adding tests and normalizing mode and template
1 parent cb0819d commit d0f45e0

File tree

44 files changed

+229
-83
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+229
-83
lines changed

cli/create-tanstack/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ registerSolid()
1010
cli({
1111
name: 'create-tanstack',
1212
appName: 'TanStack',
13+
defaultTemplate: 'file-router',
1314
})

packages/cta-cli/src/cli.ts

+17-15
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,27 @@ import { promptForOptions } from './options.js'
2222
import { normalizeOptions } from './command-line.js'
2323

2424
import { createUIEnvironment } from './ui-environment.js'
25+
import { convertTemplateToMode } from './utils.js'
2526

26-
import type {
27-
Mode,
28-
Options,
29-
PackageManager,
30-
TemplateOptions,
31-
} from '@tanstack/cta-engine'
27+
import type { Mode, Options, PackageManager } from '@tanstack/cta-engine'
3228

33-
import type { CliOptions } from './types.js'
29+
import type { CliOptions, TemplateOptions } from './types.js'
3430

3531
async function listAddOns(
3632
options: CliOptions,
3733
{
3834
forcedMode,
39-
forcedAddOns = [],
35+
forcedAddOns,
36+
defaultTemplate,
4037
}: {
41-
forcedMode?: TemplateOptions
42-
forcedAddOns?: Array<string>
38+
forcedMode?: Mode
39+
forcedAddOns: Array<string>
40+
defaultTemplate: TemplateOptions
4341
},
4442
) {
4543
const addOns = await getAllAddOns(
4644
getFrameworkById(options.framework || 'react-cra')!,
47-
forcedMode || options.template || 'typescript',
45+
forcedMode || convertTemplateToMode(options.template || defaultTemplate),
4846
)
4947
for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
5048
console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`)
@@ -55,12 +53,14 @@ export function cli({
5553
name,
5654
appName,
5755
forcedMode,
58-
forcedAddOns,
56+
forcedAddOns = [],
57+
defaultTemplate = 'javascript',
5958
}: {
6059
name: string
6160
appName: string
6261
forcedMode?: Mode
6362
forcedAddOns?: Array<string>
63+
defaultTemplate?: TemplateOptions
6464
}) {
6565
const environment = createUIEnvironment(appName, false)
6666

@@ -132,6 +132,7 @@ export function cli({
132132
}
133133
return value
134134
},
135+
defaultTemplate,
135136
)
136137
}
137138

@@ -207,12 +208,13 @@ export function cli({
207208
program.action(async (projectName: string, options: CliOptions) => {
208209
if (options.listAddOns) {
209210
await listAddOns(options, {
210-
forcedMode: forcedMode as TemplateOptions,
211+
forcedMode,
211212
forcedAddOns,
213+
defaultTemplate,
212214
})
213215
} else if (options.mcp || options.mcpSse) {
214216
await runMCPServer(!!options.mcpSse, {
215-
forcedMode: forcedMode as TemplateOptions,
217+
forcedMode,
216218
forcedAddOns,
217219
appName,
218220
})
@@ -247,7 +249,7 @@ export function cli({
247249
} else {
248250
intro(`Let's configure your ${appName} application`)
249251
finalOptions = await promptForOptions(cliOptions, {
250-
forcedMode: forcedMode as Mode,
252+
forcedMode,
251253
forcedAddOns,
252254
})
253255
}

packages/cta-cli/src/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import type { PackageManager, TemplateOptions } from '@tanstack/cta-engine'
1+
import type { PackageManager } from '@tanstack/cta-engine'
2+
3+
export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
24

35
export interface CliOptions {
46
template?: TemplateOptions

packages/cta-cli/src/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { CODE_ROUTER, FILE_ROUTER } from '@tanstack/cta-engine'
2+
import type { Mode } from '@tanstack/cta-engine'
3+
4+
import type { TemplateOptions } from './types.js'
5+
6+
export function convertTemplateToMode(template: TemplateOptions): Mode {
7+
if (template === 'typescript' || template === 'javascript') {
8+
return CODE_ROUTER
9+
}
10+
return FILE_ROUTER
11+
}

packages/cta-cli/tests/command-line.test.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ describe('normalizeOptions', () => {
101101
{
102102
id: 'foo',
103103
name: 'foobar',
104-
templates: ['file-router'],
104+
modes: ['file-router'],
105105
},
106106
],
107107
})
@@ -123,12 +123,12 @@ describe('normalizeOptions', () => {
123123
{
124124
id: 'foo',
125125
name: 'foobar',
126-
templates: ['file-router'],
126+
modes: ['file-router'],
127127
},
128128
{
129129
id: 'baz',
130130
name: 'baz',
131-
templates: ['file-router'],
131+
modes: ['file-router'],
132132
},
133133
],
134134
})
@@ -154,7 +154,7 @@ describe('normalizeOptions', () => {
154154
{
155155
id: 'biome',
156156
name: 'Biome',
157-
templates: ['file-router', 'code-router'],
157+
modes: ['file-router', 'code-router'],
158158
},
159159
],
160160
})
@@ -175,12 +175,12 @@ describe('normalizeOptions', () => {
175175
{
176176
id: 'foo',
177177
name: 'foobar',
178-
templates: ['file-router', 'code-router'],
178+
modes: ['file-router', 'code-router'],
179179
},
180180
{
181181
id: 'baz',
182182
name: 'baz',
183-
templates: ['file-router', 'code-router'],
183+
modes: ['file-router', 'code-router'],
184184
},
185185
],
186186
})

packages/cta-cli/tests/index.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { describe, it, expect, vi } from 'vitest'
2+
3+
import { cli } from '../src/index.js'
4+
5+
describe('cli', () => {
6+
it('should call the cli with the correct arguments', async () => {
7+
expect(cli).toBeDefined()
8+
})
9+
})

packages/cta-cli/tests/options.test.ts

+33-3
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ beforeEach(() => {
2323
{
2424
id: 'react-query',
2525
type: 'add-on',
26-
templates: ['file-router', 'code-router'],
26+
modes: ['file-router', 'code-router'],
2727
},
2828
{
2929
id: 'tanstack-chat',
3030
type: 'add-on',
31-
templates: ['file-router', 'code-router'],
31+
modes: ['file-router', 'code-router'],
3232
},
3333
{
3434
id: 'biome',
3535
type: 'toolchain',
36-
templates: ['file-router', 'code-router'],
36+
modes: ['file-router', 'code-router'],
3737
},
3838
],
3939
} as unknown as Framework)
@@ -103,6 +103,36 @@ describe('promptForOptions', () => {
103103
expect(options?.typescript).toBe(true)
104104
})
105105

106+
it('takes template from cli options - code-router', async () => {
107+
setBasicSpies()
108+
109+
vi.spyOn(prompts, 'selectRouterType').mockImplementation(
110+
async () => 'code-router',
111+
)
112+
113+
const options = await promptForOptions(
114+
{ ...baseCliOptions, template: 'javascript' },
115+
{},
116+
)
117+
118+
expect(options?.mode).toBe('code-router')
119+
})
120+
121+
it('takes template from cli options - file-router', async () => {
122+
setBasicSpies()
123+
124+
vi.spyOn(prompts, 'selectRouterType').mockImplementation(
125+
async () => 'code-router',
126+
)
127+
128+
const options = await promptForOptions(
129+
{ ...baseCliOptions, template: 'file-router' },
130+
{},
131+
)
132+
133+
expect(options?.mode).toBe('file-router')
134+
})
135+
106136
it('prompt for router type when unspecified', async () => {
107137
setBasicSpies()
108138

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { beforeEach, describe, it, expect, vi } from 'vitest'
2+
3+
import * as clack from '@clack/prompts'
4+
5+
import { createUIEnvironment } from '../src/ui-environment.js'
6+
7+
vi.mock('@clack/prompts')
8+
9+
// @ts-expect-error
10+
vi.spyOn(process, 'exit').mockImplementation(() => {})
11+
12+
describe('createUIEnvironment', () => {
13+
it('should create a silent UI environment', () => {
14+
const environment = createUIEnvironment('test', true)
15+
expect(environment.appName).toBe('test')
16+
})
17+
18+
it('should handle intro', () => {
19+
const environment = createUIEnvironment('test', false)
20+
const spy = vi
21+
.spyOn(clack, 'intro')
22+
.mockImplementation(async () => undefined)
23+
environment.intro('test')
24+
expect(spy).toHaveBeenCalledWith('test')
25+
})
26+
27+
it('should handle outro', () => {
28+
const environment = createUIEnvironment('test', false)
29+
const spy = vi
30+
.spyOn(clack, 'outro')
31+
.mockImplementation(async () => undefined)
32+
environment.outro('test')
33+
expect(spy).toHaveBeenCalledWith('test')
34+
})
35+
36+
it('should handle info', () => {
37+
const environment = createUIEnvironment('test', false)
38+
const spy = vi
39+
.spyOn(clack.log, 'info')
40+
.mockImplementation(async () => undefined)
41+
environment.info('test')
42+
expect(spy).toHaveBeenCalled()
43+
})
44+
45+
it('should handle error', () => {
46+
const environment = createUIEnvironment('test', false)
47+
const spy = vi
48+
.spyOn(clack.log, 'error')
49+
.mockImplementation(async () => undefined)
50+
environment.error('test')
51+
expect(spy).toHaveBeenCalled()
52+
})
53+
54+
it('should handle warn', () => {
55+
const environment = createUIEnvironment('test', false)
56+
const spy = vi
57+
.spyOn(clack.log, 'warn')
58+
.mockImplementation(async () => undefined)
59+
environment.warn('test')
60+
expect(spy).toHaveBeenCalled()
61+
})
62+
63+
it('should handle confirm', async () => {
64+
const environment = createUIEnvironment('test', false)
65+
const spy = vi.spyOn(clack, 'confirm').mockImplementation(async () => true)
66+
const isCancelSpy = vi
67+
.spyOn(clack, 'isCancel')
68+
.mockImplementation(() => false)
69+
const result = await environment.confirm('test')
70+
expect(spy).toHaveBeenCalled()
71+
expect(isCancelSpy).toHaveBeenCalled()
72+
expect(result).toBe(true)
73+
})
74+
75+
it('should handle confirm', async () => {
76+
const environment = createUIEnvironment('test', false)
77+
const spy = vi.spyOn(clack, 'confirm').mockImplementation(async () => true)
78+
const isCancelSpy = vi
79+
.spyOn(clack, 'isCancel')
80+
.mockImplementation(() => true)
81+
await environment.confirm('test')
82+
expect(spy).toHaveBeenCalled()
83+
expect(isCancelSpy).toHaveBeenCalled()
84+
})
85+
86+
it('should handle spinner', async () => {
87+
const environment = createUIEnvironment('test', false)
88+
// @ts-expect-error
89+
const spy = vi.spyOn(clack, 'spinner').mockImplementation(async () => ({
90+
start: (_msg?: string) => {},
91+
stop: (_msg?: string) => {},
92+
message: (_msg?: string) => {},
93+
}))
94+
const result = await environment.spinner()
95+
expect(spy).toHaveBeenCalled()
96+
})
97+
})

packages/cta-cli/tests/ui-prompts.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('selectAddOns', () => {
107107
name: 'Add-on 1',
108108
description: 'Add-on 1 description',
109109
type: 'add-on',
110-
templates: ['file-router'],
110+
modes: ['file-router'],
111111
},
112112
] as Array<AddOn>,
113113
} as Framework,
@@ -134,7 +134,7 @@ describe('selectAddOns', () => {
134134
name: 'Add-on 1',
135135
description: 'Add-on 1 description',
136136
type: 'add-on',
137-
templates: ['file-router'],
137+
modes: ['file-router'],
138138
},
139139
] as Array<AddOn>,
140140
} as Framework,
@@ -178,7 +178,7 @@ describe('selectToolchain', () => {
178178
name: 'Biome',
179179
description: 'Biome description',
180180
type: 'toolchain',
181-
templates: ['file-router'],
181+
modes: ['file-router'],
182182
},
183183
] as Array<AddOn>,
184184
} as Framework)
@@ -199,7 +199,7 @@ describe('selectToolchain', () => {
199199
name: 'Biome',
200200
description: 'Biome description',
201201
type: 'toolchain',
202-
templates: ['file-router'],
202+
modes: ['file-router'],
203203
},
204204
] as Array<AddOn>,
205205
} as Framework,
@@ -224,7 +224,7 @@ describe('selectToolchain', () => {
224224
name: 'Biome',
225225
description: 'Biome description',
226226
type: 'toolchain',
227-
templates: ['file-router'],
227+
modes: ['file-router'],
228228
},
229229
] as Array<AddOn>,
230230
} as Framework),

packages/cta-custom-add-on/src/custom-add-on.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313

1414
import type {
1515
Environment,
16+
Mode,
1617
Options,
1718
PersistedOptions,
1819
} from '@tanstack/cta-engine'
@@ -115,7 +116,7 @@ export async function createAppOptionsFromPersisted(
115116
...json,
116117
framework,
117118
addOns: true,
118-
chosenAddOns: await finalizeAddOns(framework!, json.mode as string, [
119+
chosenAddOns: await finalizeAddOns(framework!, json.mode as Mode, [
119120
...json.existingAddOns,
120121
]),
121122
} as Required<Options>

0 commit comments

Comments
 (0)