Skip to content

Commit 8647f8a

Browse files
committed
feat: add tests for the cli
1 parent c651898 commit 8647f8a

9 files changed

Lines changed: 1309 additions & 26 deletions

File tree

packages/brownie/jest.config.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/** @type {import('jest').Config} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
testMatch: ['<rootDir>/scripts/**/*.test.ts'],
6+
moduleFileExtensions: ['ts', 'js'],
7+
clearMocks: true,
8+
transform: {
9+
'^.+\\.ts$': [
10+
'ts-jest',
11+
{
12+
tsconfig: '<rootDir>/scripts/__tests__/tsconfig.json',
13+
},
14+
],
15+
},
16+
};

packages/brownie/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"scripts": {
3030
"lint": "eslint \"**/*.{js,ts,tsx}\"",
3131
"typecheck": "tsc --noEmit",
32-
"build": "bob build && tsc -p scripts/tsconfig.json"
32+
"build": "bob build && tsc -p scripts/tsconfig.json",
33+
"test": "jest"
3334
},
3435
"bin": "./lib/scripts/cli.js",
3536
"keywords": [
@@ -68,11 +69,14 @@
6869
"@babel/runtime": "^7.25.0",
6970
"@react-native/babel-preset": "0.82.1",
7071
"@react-native/eslint-config": "0.82.1",
72+
"@types/jest": "^29.5.14",
7173
"@types/node": "^22.0.0",
7274
"@types/react": "^19.1.1",
75+
"jest": "^29.7.0",
7376
"react": "19.1.1",
7477
"react-native": "0.82.1",
7578
"react-native-builder-bob": "^0.40.14",
79+
"ts-jest": "^29.2.5",
7680
"typescript": "5.8.3"
7781
},
7882
"codegenConfig": {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type SecondStore = {
2+
theme: string;
3+
enabled: boolean;
4+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type TestStore = {
2+
counter: number;
3+
name: string;
4+
isActive: boolean;
5+
};
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import { runCodegen } from '../../commands/codegen';
4+
import * as swiftGenerator from '../../generators/swift';
5+
import * as kotlinGenerator from '../../generators/kotlin';
6+
7+
const FIXTURES_DIR = path.join(__dirname, '../../__fixtures__');
8+
9+
jest.mock('../../generators/swift');
10+
jest.mock('../../generators/kotlin');
11+
12+
const mockGenerateSwift = swiftGenerator.generateSwift as jest.Mock;
13+
const mockGenerateKotlin = kotlinGenerator.generateKotlin as jest.Mock;
14+
const mockCwd = jest.spyOn(process, 'cwd');
15+
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
16+
const mockConsoleError = jest.spyOn(console, 'error').mockImplementation();
17+
jest.spyOn(console, 'warn').mockImplementation();
18+
jest.spyOn(process, 'exit').mockImplementation((code) => {
19+
throw new Error(`process.exit(${code})`);
20+
});
21+
22+
function createTempPackageJson(config: object): string {
23+
const tempDir = fs.mkdtempSync(path.join(FIXTURES_DIR, 'temp-'));
24+
const packageJsonPath = path.join(tempDir, 'package.json');
25+
fs.writeFileSync(packageJsonPath, JSON.stringify(config, null, 2));
26+
return tempDir;
27+
}
28+
29+
function cleanupTempDir(dir: string): void {
30+
fs.rmSync(dir, { recursive: true, force: true });
31+
}
32+
33+
describe('runCodegen', () => {
34+
let tempDir: string | null = null;
35+
36+
beforeEach(() => {
37+
mockGenerateSwift.mockResolvedValue(undefined);
38+
mockGenerateKotlin.mockResolvedValue(undefined);
39+
});
40+
41+
afterEach(() => {
42+
if (tempDir) {
43+
cleanupTempDir(tempDir);
44+
tempDir = null;
45+
}
46+
jest.clearAllMocks();
47+
});
48+
49+
it('prints version when -v flag passed', async () => {
50+
await runCodegen(['-v'], '1.0.0');
51+
expect(mockConsoleLog).toHaveBeenCalledWith('1.0.0');
52+
});
53+
54+
it('prints help when -h flag passed', async () => {
55+
await runCodegen(['-h'], '1.0.0');
56+
expect(mockConsoleLog).toHaveBeenCalled();
57+
const output = mockConsoleLog.mock.calls[0]![0];
58+
expect(output).toContain('brownie codegen');
59+
});
60+
61+
it('generates swift files for single store', async () => {
62+
tempDir = createTempPackageJson({
63+
brownie: {
64+
stores: [
65+
{
66+
schema: './valid.schema.ts',
67+
typeName: 'TestStore',
68+
swift: './Generated/TestStore.swift',
69+
},
70+
],
71+
},
72+
});
73+
mockCwd.mockReturnValue(tempDir);
74+
75+
await runCodegen([], '1.0.0');
76+
77+
expect(mockGenerateSwift).toHaveBeenCalledWith({
78+
schemaPath: './valid.schema.ts',
79+
typeName: 'TestStore',
80+
outputPath: './Generated/TestStore.swift',
81+
});
82+
expect(mockGenerateKotlin).not.toHaveBeenCalled();
83+
});
84+
85+
it('generates kotlin files for single store', async () => {
86+
tempDir = createTempPackageJson({
87+
brownie: {
88+
stores: [
89+
{
90+
schema: './valid.schema.ts',
91+
typeName: 'TestStore',
92+
kotlin: './Generated/TestStore.kt',
93+
kotlinPackageName: 'com.test',
94+
},
95+
],
96+
},
97+
});
98+
mockCwd.mockReturnValue(tempDir);
99+
100+
await runCodegen([], '1.0.0');
101+
102+
expect(mockGenerateKotlin).toHaveBeenCalledWith({
103+
schemaPath: './valid.schema.ts',
104+
typeName: 'TestStore',
105+
outputPath: './Generated/TestStore.kt',
106+
packageName: 'com.test',
107+
});
108+
expect(mockGenerateSwift).not.toHaveBeenCalled();
109+
});
110+
111+
it('generates both swift and kotlin when configured', async () => {
112+
tempDir = createTempPackageJson({
113+
brownie: {
114+
stores: [
115+
{
116+
schema: './valid.schema.ts',
117+
typeName: 'TestStore',
118+
swift: './Generated/TestStore.swift',
119+
kotlin: './Generated/TestStore.kt',
120+
},
121+
],
122+
},
123+
});
124+
mockCwd.mockReturnValue(tempDir);
125+
126+
await runCodegen([], '1.0.0');
127+
128+
expect(mockGenerateSwift).toHaveBeenCalled();
129+
expect(mockGenerateKotlin).toHaveBeenCalled();
130+
});
131+
132+
it('generates only specified platform with -p flag', async () => {
133+
tempDir = createTempPackageJson({
134+
brownie: {
135+
stores: [
136+
{
137+
schema: './valid.schema.ts',
138+
typeName: 'TestStore',
139+
swift: './Generated/TestStore.swift',
140+
kotlin: './Generated/TestStore.kt',
141+
},
142+
],
143+
},
144+
});
145+
mockCwd.mockReturnValue(tempDir);
146+
147+
await runCodegen(['-p', 'swift'], '1.0.0');
148+
149+
expect(mockGenerateSwift).toHaveBeenCalled();
150+
expect(mockGenerateKotlin).not.toHaveBeenCalled();
151+
});
152+
153+
it('generates for multiple stores', async () => {
154+
tempDir = createTempPackageJson({
155+
brownie: {
156+
stores: [
157+
{
158+
schema: './user.schema.ts',
159+
typeName: 'UserStore',
160+
swift: './Generated/UserStore.swift',
161+
},
162+
{
163+
schema: './settings.schema.ts',
164+
typeName: 'SettingsStore',
165+
swift: './Generated/SettingsStore.swift',
166+
},
167+
],
168+
},
169+
});
170+
mockCwd.mockReturnValue(tempDir);
171+
172+
await runCodegen([], '1.0.0');
173+
174+
expect(mockGenerateSwift).toHaveBeenCalledTimes(2);
175+
expect(mockGenerateSwift).toHaveBeenNthCalledWith(1, {
176+
schemaPath: './user.schema.ts',
177+
typeName: 'UserStore',
178+
outputPath: './Generated/UserStore.swift',
179+
});
180+
expect(mockGenerateSwift).toHaveBeenNthCalledWith(2, {
181+
schemaPath: './settings.schema.ts',
182+
typeName: 'SettingsStore',
183+
outputPath: './Generated/SettingsStore.swift',
184+
});
185+
});
186+
187+
it('exits with error for invalid platform', async () => {
188+
tempDir = createTempPackageJson({
189+
brownie: {
190+
stores: [
191+
{
192+
schema: './valid.schema.ts',
193+
typeName: 'TestStore',
194+
swift: './Generated/TestStore.swift',
195+
},
196+
],
197+
},
198+
});
199+
mockCwd.mockReturnValue(tempDir);
200+
201+
await expect(runCodegen(['-p', 'invalid'], '1.0.0')).rejects.toThrow(
202+
'process.exit(1)'
203+
);
204+
expect(mockConsoleError).toHaveBeenCalled();
205+
});
206+
207+
it('exits with error when generator fails', async () => {
208+
tempDir = createTempPackageJson({
209+
brownie: {
210+
stores: [
211+
{
212+
schema: './valid.schema.ts',
213+
typeName: 'TestStore',
214+
swift: './Generated/TestStore.swift',
215+
},
216+
],
217+
},
218+
});
219+
mockCwd.mockReturnValue(tempDir);
220+
mockGenerateSwift.mockRejectedValue(new Error('Generation failed'));
221+
222+
await expect(runCodegen([], '1.0.0')).rejects.toThrow('process.exit(1)');
223+
expect(mockConsoleError).toHaveBeenCalled();
224+
});
225+
226+
it('warns when store has no output paths for selected platform', async () => {
227+
tempDir = createTempPackageJson({
228+
brownie: {
229+
stores: [
230+
{
231+
schema: './valid.schema.ts',
232+
typeName: 'TestStore',
233+
swift: './Generated/TestStore.swift',
234+
},
235+
],
236+
},
237+
});
238+
mockCwd.mockReturnValue(tempDir);
239+
240+
await runCodegen(['-p', 'kotlin'], '1.0.0');
241+
242+
expect(mockGenerateKotlin).not.toHaveBeenCalled();
243+
});
244+
});

0 commit comments

Comments
 (0)