Skip to content

Commit 9f19724

Browse files
Merge pull request finos#1470 from LeighFinegold/1452_cli-updates
Add support for --template and --template-dir (finos#1469)
2 parents fe8edac + 1b370cd commit 9f19724

File tree

17 files changed

+712
-38
lines changed

17 files changed

+712
-38
lines changed

cli/src/cli.e2e.spec.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,47 @@ describe('CLI Integration Tests', () => {
317317
expect(actualContent).toEqual(expectedContent);
318318
});
319319

320+
321+
test('template command works with --template mode', async () => {
322+
const fixtureDir = path.resolve(__dirname, '../test_fixtures/template');
323+
const testModelPath = path.join(fixtureDir, 'model/document-system.json');
324+
const localDirectory = path.join(fixtureDir, 'model/url-to-file-directory.json');
325+
const templatePath = path.join(fixtureDir, 'self-provided/single-template.hbs');
326+
const expectedOutputPath = path.join(fixtureDir, 'expected-output/single-template-output.md');
327+
const outputDir = path.join(tempDir, 'output-single-template');
328+
const outputFile = path.join(outputDir, 'simple-template-output.md');
329+
330+
await run(
331+
calm(
332+
`template --input ${testModelPath} --template ${templatePath} --output ${outputFile} --url-to-local-file-mapping ${localDirectory}`
333+
)
334+
);
335+
336+
expect(fs.existsSync(outputFile)).toBe(true);
337+
const actual = fs.readFileSync(outputFile, 'utf8').trim();
338+
const expected = fs.readFileSync(expectedOutputPath, 'utf8').trim();
339+
expect(actual).toEqual(expected);
340+
});
341+
342+
test('template command works with --template-dir mode', async () => {
343+
const fixtureDir = path.resolve(__dirname, '../test_fixtures/template');
344+
const testModelPath = path.join(fixtureDir, 'model/document-system.json');
345+
const localDirectory = path.join(fixtureDir, 'model/url-to-file-directory.json');
346+
const templateDirPath = path.join(fixtureDir, 'self-provided/template-dir');
347+
const expectedOutputDir = path.join(fixtureDir, 'expected-output/template-dir');
348+
const actualOutputDir = path.join(tempDir, 'output-template-dir');
349+
350+
await run(
351+
calm(
352+
`template --input ${testModelPath} --template-dir ${templateDirPath} --output ${actualOutputDir} --url-to-local-file-mapping ${localDirectory}`
353+
)
354+
);
355+
356+
await expectDirectoryMatch(expectedOutputDir, actualOutputDir);
357+
});
358+
359+
360+
320361
test('docify command generates expected files', async () => {
321362
const fixtureDir = path.resolve(__dirname, '../test_fixtures/template');
322363
const testModelPath = path.join(
@@ -345,6 +386,46 @@ describe('CLI Integration Tests', () => {
345386
);
346387
});
347388

389+
390+
test('docify command works with --template mode', async () => {
391+
const fixtureDir = path.resolve(__dirname, '../test_fixtures/template');
392+
const testModelPath = path.join(fixtureDir, 'model/document-system.json');
393+
const localDirectory = path.join(fixtureDir, 'model/url-to-file-directory.json');
394+
const templatePath = path.join(fixtureDir, 'self-provided/single-template.hbs');
395+
const expectedOutputPath = path.join(fixtureDir, 'expected-output/single-template-output.md');
396+
const outputDir = path.join(tempDir, 'output-single-template');
397+
const outputFile = path.join(outputDir, 'simple-template-output.md');
398+
399+
await run(
400+
calm(
401+
`docify --input ${testModelPath} --template ${templatePath} --output ${outputFile} --url-to-local-file-mapping ${localDirectory}`
402+
)
403+
);
404+
405+
expect(fs.existsSync(outputFile)).toBe(true);
406+
const actual = fs.readFileSync(outputFile, 'utf8').trim();
407+
const expected = fs.readFileSync(expectedOutputPath, 'utf8').trim();
408+
expect(actual).toEqual(expected);
409+
});
410+
411+
test('docify command works with --template-dir mode', async () => {
412+
const fixtureDir = path.resolve(__dirname, '../test_fixtures/template');
413+
const testModelPath = path.join(fixtureDir, 'model/document-system.json');
414+
const localDirectory = path.join(fixtureDir, 'model/url-to-file-directory.json');
415+
const templateDirPath = path.join(fixtureDir, 'self-provided/template-dir');
416+
const expectedOutputDir = path.join(fixtureDir, 'expected-output/template-dir');
417+
const actualOutputDir = path.join(tempDir, 'output-template-dir');
418+
419+
await run(
420+
calm(
421+
`docify --input ${testModelPath} --template-dir ${templateDirPath} --output ${actualOutputDir} --url-to-local-file-mapping ${localDirectory}`
422+
)
423+
);
424+
425+
await expectDirectoryMatch(expectedOutputDir, actualOutputDir);
426+
});
427+
428+
348429
test('Getting Started Verification - CLI Steps', async () => {
349430
const GETTING_STARTED_DIR = join(
350431
__dirname,

cli/src/cli.spec.ts

Lines changed: 171 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import { CALM_META_SCHEMA_DIRECTORY } from '@finos/calm-shared';
2-
import { Command } from 'commander';
1+
import {
2+
CALM_META_SCHEMA_DIRECTORY,
3+
Docifier,
4+
DocifyMode,
5+
TemplateProcessingMode,
6+
TemplateProcessor
7+
} from '@finos/calm-shared';
8+
import {Command} from 'commander';
9+
import {MockInstance} from 'vitest';
310

411
let calmShared: typeof import('@finos/calm-shared');
512
let validateModule: typeof import('./command-helpers/validate');
@@ -100,7 +107,19 @@ describe('CLI Commands', () => {
100107
});
101108

102109
describe('Template Command', () => {
103-
it('should instantiate TemplateProcessor and call processTemplate', async () => {
110+
let processorConstructorSpy: MockInstance<(this: TemplateProcessor, inputPath: string, templateBundlePath: string, outputPath: string, urlToLocalPathMapping: Map<string, string>, mode?: TemplateProcessingMode) => TemplateProcessor>;
111+
112+
beforeEach(() => {
113+
processorConstructorSpy = vi
114+
.spyOn(calmShared, 'TemplateProcessor')
115+
.mockImplementation(() => {
116+
return {
117+
processTemplate: vi.fn().mockResolvedValue(undefined),
118+
} as unknown as TemplateProcessor; //This works to get round any but prob not spying properly (used in other tests)
119+
});
120+
});
121+
122+
it('should handle --bundle mode correctly', async () => {
104123
await program.parseAsync([
105124
'node', 'cli.js', 'template',
106125
'--input', 'model.json',
@@ -109,21 +128,165 @@ describe('CLI Commands', () => {
109128
'--verbose',
110129
]);
111130

112-
expect(calmShared.TemplateProcessor.prototype.processTemplate).toHaveBeenCalled();
131+
expect(processorConstructorSpy).toHaveBeenCalledWith(
132+
'model.json',
133+
'templateDir',
134+
'outDir',
135+
expect.any(Map),
136+
'bundle'
137+
);
138+
});
139+
140+
it('should handle --template mode correctly', async () => {
141+
await program.parseAsync([
142+
'node', 'cli.js', 'template',
143+
'--input', 'model.json',
144+
'--template', 'template.hbs',
145+
'--output', 'outDir',
146+
]);
147+
148+
expect(processorConstructorSpy).toHaveBeenCalledWith(
149+
'model.json',
150+
'template.hbs',
151+
'outDir',
152+
expect.any(Map),
153+
'template'
154+
);
155+
});
156+
157+
it('should handle --template-dir mode correctly', async () => {
158+
await program.parseAsync([
159+
'node', 'cli.js', 'template',
160+
'--input', 'model.json',
161+
'--template-dir', 'templates/',
162+
'--output', 'outDir',
163+
]);
164+
165+
expect(processorConstructorSpy).toHaveBeenCalledWith(
166+
'model.json',
167+
'templates/',
168+
'outDir',
169+
expect.any(Map),
170+
'template-directory'
171+
);
172+
});
173+
174+
it('should exit if multiple template flags are provided', async () => {
175+
const exitSpy = vi.spyOn(process, 'exit').mockImplementationOnce(() => {
176+
throw new Error('process.exit called');
177+
});
178+
179+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
180+
181+
await expect(program.parseAsync([
182+
'node', 'cli.js', 'template',
183+
'--input', 'model.json',
184+
'--template', 't1.hbs',
185+
'--bundle', 'bundle',
186+
'--output', 'outDir'
187+
])).rejects.toThrow('process.exit called');
188+
189+
expect(errorSpy).toHaveBeenCalledWith('❌ Please specify exactly one of --template, --template-dir, or --bundle');
190+
expect(exitSpy).toHaveBeenCalledWith(1);
191+
192+
exitSpy.mockRestore();
193+
errorSpy.mockRestore();
113194
});
114195
});
115196

197+
116198
describe('Docify Command', () => {
117-
it('should instantiate Docifier and call docify', async () => {
199+
let docifierConstructorSpy: MockInstance<
200+
(this: Docifier,
201+
mode: DocifyMode,
202+
inputPath: string,
203+
outputPath: string,
204+
urlToLocalPathMapping: Map<string, string>,
205+
templateProcessingMode?: TemplateProcessingMode,
206+
templatePath?: string) => Docifier
207+
>;
208+
209+
beforeEach(() => {
210+
docifierConstructorSpy = vi
211+
.spyOn(calmShared, 'Docifier')
212+
.mockImplementation(() => ({
213+
docify: vi.fn().mockResolvedValue(undefined),
214+
} as unknown as Docifier));
215+
});
216+
217+
it('should default to WEBSITE mode with bundle', async () => {
118218
await program.parseAsync([
119219
'node', 'cli.js', 'docify',
120220
'--input', 'model.json',
121221
'--output', 'outDir',
122-
'--url-to-local-file-mapping', 'url-to-file-directory.json',
123-
'--verbose',
124222
]);
125223

126-
expect(calmShared.Docifier.prototype.docify).toHaveBeenCalled();
224+
expect(docifierConstructorSpy).toHaveBeenCalledWith(
225+
'WEBSITE',
226+
'model.json',
227+
'outDir',
228+
expect.any(Map),
229+
'bundle',
230+
undefined
231+
);
232+
});
233+
234+
it('should use template mode if --template is specified', async () => {
235+
await program.parseAsync([
236+
'node', 'cli.js', 'docify',
237+
'--input', 'model.json',
238+
'--output', 'outDir',
239+
'--template', 'template.hbs',
240+
]);
241+
242+
expect(docifierConstructorSpy).toHaveBeenCalledWith(
243+
'USER_PROVIDED',
244+
'model.json',
245+
'outDir',
246+
expect.any(Map),
247+
'template',
248+
'template.hbs'
249+
);
250+
});
251+
252+
it('should use template-directory mode if --template-dir is specified', async () => {
253+
await program.parseAsync([
254+
'node', 'cli.js', 'docify',
255+
'--input', 'model.json',
256+
'--output', 'outDir',
257+
'--template-dir', 'templateDir',
258+
]);
259+
260+
expect(docifierConstructorSpy).toHaveBeenCalledWith(
261+
'USER_PROVIDED',
262+
'model.json',
263+
'outDir',
264+
expect.any(Map),
265+
'template-directory',
266+
'templateDir'
267+
);
268+
});
269+
270+
it('should exit if both --template and --template-dir are specified', async () => {
271+
const exitSpy = vi.spyOn(process, 'exit').mockImplementationOnce(() => {
272+
throw new Error('process.exit called');
273+
});
274+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
275+
276+
await expect(program.parseAsync([
277+
'node', 'cli.js', 'docify',
278+
'--input', 'model.json',
279+
'--output', 'outDir',
280+
'--template', 't1.hbs',
281+
'--template-dir', 'templateDir'
282+
])).rejects.toThrow('process.exit called');
283+
284+
expect(errorSpy).toHaveBeenCalledWith('❌ Please specify only one of --template or --template-dir');
285+
expect(exitSpy).toHaveBeenCalledWith(1);
286+
287+
exitSpy.mockRestore();
288+
errorSpy.mockRestore();
127289
});
128290
});
291+
129292
});

0 commit comments

Comments
 (0)