Skip to content

Commit 2b8e5b3

Browse files
author
tianyi
committed
add test for iflow
1 parent 9385a3a commit 2b8e5b3

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

test/core/init.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,61 @@ describe('InitCommand', () => {
13471347
expect(qoderChoice.configured).toBe(true);
13481348
});
13491349

1350+
it('should create IFlow slash command files with templates', async () => {
1351+
queueSelections('iflow', DONE);
1352+
1353+
await initCommand.execute(testDir);
1354+
1355+
const iflowProposal = path.join(
1356+
testDir,
1357+
'.iflow/commands/openspec-proposal.md'
1358+
);
1359+
const iflowApply = path.join(
1360+
testDir,
1361+
'.iflow/commands/openspec-apply.md'
1362+
);
1363+
const iflowArchive = path.join(
1364+
testDir,
1365+
'.iflow/commands/openspec-archive.md'
1366+
);
1367+
1368+
expect(await fileExists(iflowProposal)).toBe(true);
1369+
expect(await fileExists(iflowApply)).toBe(true);
1370+
expect(await fileExists(iflowArchive)).toBe(true);
1371+
1372+
const proposalContent = await fs.readFile(iflowProposal, 'utf-8');
1373+
expect(proposalContent).toContain('---');
1374+
expect(proposalContent).toContain('name: /openspec-proposal');
1375+
expect(proposalContent).toContain('description: Scaffold a new OpenSpec change and validate strictly.');
1376+
expect(proposalContent).toContain('category: OpenSpec');
1377+
expect(proposalContent).toContain('<!-- OPENSPEC:START -->');
1378+
expect(proposalContent).toContain('**Guardrails**');
1379+
1380+
const applyContent = await fs.readFile(iflowApply, 'utf-8');
1381+
expect(applyContent).toContain('---');
1382+
expect(applyContent).toContain('name: /openspec-apply');
1383+
expect(applyContent).toContain('description: Implement an approved OpenSpec change and keep tasks in sync.');
1384+
expect(applyContent).toContain('Work through tasks sequentially');
1385+
1386+
const archiveContent = await fs.readFile(iflowArchive, 'utf-8');
1387+
expect(archiveContent).toContain('---');
1388+
expect(archiveContent).toContain('name: /openspec-archive');
1389+
expect(archiveContent).toContain('description: Archive a deployed OpenSpec change and update specs.');
1390+
expect(archiveContent).toContain('openspec archive <id> --yes');
1391+
});
1392+
1393+
it('should mark iFlow as already configured during extend mode', async () => {
1394+
queueSelections('iflow', DONE, 'iflow', DONE);
1395+
await initCommand.execute(testDir);
1396+
await initCommand.execute(testDir);
1397+
1398+
const secondRunArgs = mockPrompt.mock.calls[1][0];
1399+
const iflowChoice = secondRunArgs.choices.find(
1400+
(choice: any) => choice.value === 'iflow'
1401+
);
1402+
expect(iflowChoice.configured).toBe(true);
1403+
});
1404+
13501405
it('should create COSTRICT.md when CoStrict is selected', async () => {
13511406
queueSelections('costrict', DONE);
13521407

@@ -1376,6 +1431,22 @@ describe('InitCommand', () => {
13761431
expect(content).toContain('openspec update');
13771432
expect(content).toContain('<!-- OPENSPEC:END -->');
13781433
});
1434+
1435+
it('should create IFLOW.md when iFlow is selected', async () => {
1436+
queueSelections('iflow', DONE);
1437+
1438+
await initCommand.execute(testDir);
1439+
1440+
const iflowPath = path.join(testDir, 'IFLOW.md');
1441+
expect(await fileExists(iflowPath)).toBe(true);
1442+
1443+
const content = await fs.readFile(iflowPath, 'utf-8');
1444+
expect(content).toContain('<!-- OPENSPEC:START -->');
1445+
expect(content).toContain("@/openspec/AGENTS.md");
1446+
expect(content).toContain('openspec update');
1447+
expect(content).toContain('<!-- OPENSPEC:END -->');
1448+
});
1449+
13791450
it('should update existing COSTRICT.md with markers', async () => {
13801451
queueSelections('costrict', DONE);
13811452

@@ -1409,6 +1480,25 @@ describe('InitCommand', () => {
14091480
expect(updatedContent).toContain('<!-- OPENSPEC:END -->');
14101481
expect(updatedContent).toContain('Custom instructions here');
14111482
});
1483+
1484+
it('should update existing IFLOW.md with markers', async () => {
1485+
queueSelections('iflow', DONE);
1486+
1487+
const iflowPath = path.join(testDir, 'IFLOW.md');
1488+
const existingContent =
1489+
'# My iFlow Instructions\nCustom instructions here';
1490+
await fs.writeFile(iflowPath, existingContent);
1491+
1492+
await initCommand.execute(testDir);
1493+
1494+
const updatedContent = await fs.readFile(iflowPath, 'utf-8');
1495+
expect(updatedContent).toContain('<!-- OPENSPEC:START -->');
1496+
expect(updatedContent).toContain("@/openspec/AGENTS.md");
1497+
expect(updatedContent).toContain('openspec update');
1498+
expect(updatedContent).toContain('<!-- OPENSPEC:END -->');
1499+
expect(updatedContent).toContain('Custom instructions here');
1500+
});
1501+
14121502
});
14131503

14141504
describe('non-interactive mode', () => {

test/core/update.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,47 @@ Old slash content
10991099
consoleSpy.mockRestore();
11001100
});
11011101

1102+
it('should refresh existing iFlow slash command files', async () => {
1103+
const iflowPath = path.join(
1104+
testDir,
1105+
'.iflow/commands/openspec-proposal.md'
1106+
);
1107+
await fs.mkdir(path.dirname(iflowPath), { recursive: true });
1108+
const initialContent = `---
1109+
name: /openspec-proposal
1110+
description: Old description
1111+
category: OpenSpec
1112+
tags: [openspec, change]
1113+
---
1114+
<!-- OPENSPEC:START -->
1115+
Old slash content
1116+
<!-- OPENSPEC:END -->`;
1117+
await fs.writeFile(iflowPath, initialContent);
1118+
1119+
const consoleSpy = vi.spyOn(console, 'log');
1120+
1121+
await updateCommand.execute(testDir);
1122+
1123+
const updated = await fs.readFile(iflowPath, 'utf-8');
1124+
expect(updated).toContain('name: /openspec-proposal');
1125+
expect(updated).toContain('**Guardrails**');
1126+
expect(updated).toContain(
1127+
'Validate with `openspec validate <id> --strict`'
1128+
);
1129+
expect(updated).not.toContain('Old slash content');
1130+
1131+
const [logMessage] = consoleSpy.mock.calls[0];
1132+
expect(logMessage).toContain(
1133+
'Updated OpenSpec instructions (openspec/AGENTS.md'
1134+
);
1135+
expect(logMessage).toContain('AGENTS.md (created)');
1136+
expect(logMessage).toContain(
1137+
'Updated slash commands: .iflow/commands/openspec-proposal.md'
1138+
);
1139+
1140+
consoleSpy.mockRestore();
1141+
});
1142+
11021143
it('should refresh existing RooCode slash command files', async () => {
11031144
const rooPath = path.join(
11041145
testDir,
@@ -1244,6 +1285,43 @@ Old body
12441285
await expect(FileSystemUtils.fileExists(qoderArchive)).resolves.toBe(false);
12451286
});
12461287

1288+
it('should not create missing iFlow slash command files on update', async () => {
1289+
const iflowApply = path.join(
1290+
testDir,
1291+
'.iflow/commands/openspec-apply.md'
1292+
);
1293+
1294+
// Only create apply; leave proposal and archive missing
1295+
await fs.mkdir(path.dirname(iflowApply), { recursive: true });
1296+
await fs.writeFile(
1297+
iflowApply,
1298+
`---
1299+
name: /openspec-apply
1300+
description: Old description
1301+
category: OpenSpec
1302+
tags: [openspec, apply]
1303+
---
1304+
<!-- OPENSPEC:START -->
1305+
Old body
1306+
<!-- OPENSPEC:END -->`
1307+
);
1308+
1309+
await updateCommand.execute(testDir);
1310+
1311+
const iflowProposal = path.join(
1312+
testDir,
1313+
'.iflow/commands/openspec-proposal.md'
1314+
);
1315+
const iflowArchive = path.join(
1316+
testDir,
1317+
'.iflow/commands/openspec-archive.md'
1318+
);
1319+
1320+
// Confirm they weren't created by update
1321+
await expect(FileSystemUtils.fileExists(iflowProposal)).resolves.toBe(false);
1322+
await expect(FileSystemUtils.fileExists(iflowArchive)).resolves.toBe(false);
1323+
});
1324+
12471325
it('should update only existing COSTRICT.md file', async () => {
12481326
// Create COSTRICT.md file with initial content
12491327
const costrictPath = path.join(testDir, 'COSTRICT.md');

0 commit comments

Comments
 (0)