@lbruton/specflow 3.4.1 → 3.5.0

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.
Files changed (47) hide show
  1. package/dist/core/__tests__/config-loader.test.d.ts +2 -0
  2. package/dist/core/__tests__/config-loader.test.d.ts.map +1 -0
  3. package/dist/core/__tests__/config-loader.test.js +184 -0
  4. package/dist/core/__tests__/config-loader.test.js.map +1 -0
  5. package/dist/core/__tests__/index-updater.test.d.ts +2 -0
  6. package/dist/core/__tests__/index-updater.test.d.ts.map +1 -0
  7. package/dist/core/__tests__/index-updater.test.js +122 -0
  8. package/dist/core/__tests__/index-updater.test.js.map +1 -0
  9. package/dist/core/__tests__/migration.test.d.ts +2 -0
  10. package/dist/core/__tests__/migration.test.d.ts.map +1 -0
  11. package/dist/core/__tests__/migration.test.js +151 -0
  12. package/dist/core/__tests__/migration.test.js.map +1 -0
  13. package/dist/core/__tests__/path-utils.test.js +101 -12
  14. package/dist/core/__tests__/path-utils.test.js.map +1 -1
  15. package/dist/core/archive-service.d.ts.map +1 -1
  16. package/dist/core/archive-service.js +22 -0
  17. package/dist/core/archive-service.js.map +1 -1
  18. package/dist/core/config-loader.d.ts +31 -0
  19. package/dist/core/config-loader.d.ts.map +1 -0
  20. package/dist/core/config-loader.js +162 -0
  21. package/dist/core/config-loader.js.map +1 -0
  22. package/dist/core/index-updater.d.ts +17 -0
  23. package/dist/core/index-updater.d.ts.map +1 -0
  24. package/dist/core/index-updater.js +86 -0
  25. package/dist/core/index-updater.js.map +1 -0
  26. package/dist/core/migration.d.ts +30 -0
  27. package/dist/core/migration.d.ts.map +1 -0
  28. package/dist/core/migration.js +99 -0
  29. package/dist/core/migration.js.map +1 -0
  30. package/dist/core/path-utils.d.ts +7 -0
  31. package/dist/core/path-utils.d.ts.map +1 -1
  32. package/dist/core/path-utils.js +22 -10
  33. package/dist/core/path-utils.js.map +1 -1
  34. package/dist/core/template-generator.d.ts.map +1 -1
  35. package/dist/core/template-generator.js +32 -11
  36. package/dist/core/template-generator.js.map +1 -1
  37. package/dist/core/workspace-initializer.d.ts +6 -1
  38. package/dist/core/workspace-initializer.d.ts.map +1 -1
  39. package/dist/core/workspace-initializer.js +69 -9
  40. package/dist/core/workspace-initializer.js.map +1 -1
  41. package/dist/server.d.ts.map +1 -1
  42. package/dist/server.js +15 -2
  43. package/dist/server.js.map +1 -1
  44. package/dist/tools/approvals.d.ts.map +1 -1
  45. package/dist/tools/approvals.js +14 -0
  46. package/dist/tools/approvals.js.map +1 -1
  47. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config-loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.test.d.ts","sourceRoot":"","sources":["../../../src/core/__tests__/config-loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,184 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join, resolve } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { loadConfig, createDefaultConfig, loadOrCreateConfig, } from '../config-loader.js';
6
+ describe('loadConfig', () => {
7
+ let tempDir;
8
+ afterEach(() => {
9
+ if (tempDir) {
10
+ rmSync(tempDir, { recursive: true, force: true });
11
+ }
12
+ });
13
+ function setupProject(config, opts) {
14
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-cfg-'));
15
+ const specflowDir = join(tempDir, '.specflow');
16
+ mkdirSync(specflowDir, { recursive: true });
17
+ writeFileSync(join(specflowDir, 'config.json'), JSON.stringify(config, null, 2));
18
+ if (opts?.createDocVault !== false) {
19
+ // Create the DocVault directory that the config points to
20
+ const dvPath = config.docvault;
21
+ const abs = resolve(tempDir, dvPath);
22
+ mkdirSync(abs, { recursive: true });
23
+ }
24
+ }
25
+ it('returns correct ResolvedConfig with valid config.json', async () => {
26
+ setupProject({
27
+ project: 'MyProject',
28
+ docvault: '../DocVault',
29
+ issue_prefix: 'MP',
30
+ });
31
+ const result = await loadConfig(tempDir);
32
+ expect(result.project).toBe('MyProject');
33
+ expect(result.docvault).toBe('../DocVault');
34
+ expect(result.issue_prefix).toBe('MP');
35
+ expect(result.docvaultAbsolute).toBe(resolve(tempDir, '../DocVault'));
36
+ expect(result.specflowRoot).toBe(join(resolve(tempDir, '../DocVault'), 'specflow', 'MyProject'));
37
+ expect(result.globalTemplatesPath).toBe(join(resolve(tempDir, '../DocVault'), 'specflow', 'templates'));
38
+ });
39
+ it('resolves relative docvault path against projectPath', async () => {
40
+ setupProject({
41
+ project: 'TestApp',
42
+ docvault: '../DocVault',
43
+ issue_prefix: 'TA',
44
+ });
45
+ const result = await loadConfig(tempDir);
46
+ expect(result.docvaultAbsolute).toBe(resolve(tempDir, '../DocVault'));
47
+ });
48
+ it('resolves absolute docvault path as-is', async () => {
49
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-cfg-'));
50
+ const absDocVault = mkdtempSync(join(tmpdir(), 'specflow-dv-'));
51
+ const specflowDir = join(tempDir, '.specflow');
52
+ mkdirSync(specflowDir, { recursive: true });
53
+ writeFileSync(join(specflowDir, 'config.json'), JSON.stringify({
54
+ project: 'AbsTest',
55
+ docvault: absDocVault,
56
+ issue_prefix: 'AT',
57
+ }));
58
+ const result = await loadConfig(tempDir);
59
+ expect(result.docvaultAbsolute).toBe(absDocVault);
60
+ // Clean up extra temp dir
61
+ rmSync(absDocVault, { recursive: true, force: true });
62
+ });
63
+ it('throws on missing config.json (ENOENT)', async () => {
64
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-cfg-'));
65
+ // No .specflow/ directory at all
66
+ await expect(loadConfig(tempDir)).rejects.toThrow();
67
+ });
68
+ it('throws on invalid project name', async () => {
69
+ setupProject({
70
+ project: 'My Project!',
71
+ docvault: '../DocVault',
72
+ issue_prefix: 'MP',
73
+ });
74
+ await expect(loadConfig(tempDir)).rejects.toThrow(/Invalid project name/);
75
+ });
76
+ it('throws on empty project field', async () => {
77
+ setupProject({
78
+ project: '',
79
+ docvault: '../DocVault',
80
+ issue_prefix: 'MP',
81
+ });
82
+ await expect(loadConfig(tempDir)).rejects.toThrow(/Invalid project name/);
83
+ });
84
+ it('throws on empty docvault field', async () => {
85
+ setupProject({
86
+ project: 'TestApp',
87
+ docvault: '',
88
+ issue_prefix: 'TA',
89
+ });
90
+ await expect(loadConfig(tempDir)).rejects.toThrow(/docvault path must be a non-empty string/);
91
+ });
92
+ it('throws on empty issue_prefix field', async () => {
93
+ setupProject({
94
+ project: 'TestApp',
95
+ docvault: '../DocVault',
96
+ issue_prefix: '',
97
+ });
98
+ await expect(loadConfig(tempDir)).rejects.toThrow(/Invalid issue_prefix/);
99
+ });
100
+ it('throws on inaccessible docvault path', async () => {
101
+ setupProject({
102
+ project: 'TestApp',
103
+ docvault: '../NonExistent',
104
+ issue_prefix: 'TA',
105
+ }, { createDocVault: false });
106
+ await expect(loadConfig(tempDir)).rejects.toThrow(/DocVault path does not exist/);
107
+ });
108
+ });
109
+ describe('createDefaultConfig', () => {
110
+ let tempDir;
111
+ let parentDir;
112
+ afterEach(() => {
113
+ if (parentDir) {
114
+ rmSync(parentDir, { recursive: true, force: true });
115
+ }
116
+ else if (tempDir) {
117
+ rmSync(tempDir, { recursive: true, force: true });
118
+ }
119
+ });
120
+ it('auto-detects project name from dirname', async () => {
121
+ parentDir = mkdtempSync(join(tmpdir(), 'specflow-parent-'));
122
+ const projectDir = join(parentDir, 'MyProject');
123
+ const docVaultDir = join(parentDir, 'DocVault');
124
+ mkdirSync(projectDir, { recursive: true });
125
+ mkdirSync(docVaultDir, { recursive: true });
126
+ tempDir = projectDir;
127
+ const result = await createDefaultConfig(projectDir);
128
+ expect(result.project).toBe('MyProject');
129
+ });
130
+ it('detects ../DocVault sibling', async () => {
131
+ parentDir = mkdtempSync(join(tmpdir(), 'specflow-parent-'));
132
+ const projectDir = join(parentDir, 'TestApp');
133
+ const docVaultDir = join(parentDir, 'DocVault');
134
+ mkdirSync(projectDir, { recursive: true });
135
+ mkdirSync(docVaultDir, { recursive: true });
136
+ tempDir = projectDir;
137
+ const result = await createDefaultConfig(projectDir);
138
+ expect(result.docvault).toBe('../DocVault');
139
+ expect(result.docvaultAbsolute).toBe(resolve(projectDir, '../DocVault'));
140
+ });
141
+ it('throws when no DocVault found', async () => {
142
+ parentDir = mkdtempSync(join(tmpdir(), 'specflow-parent-'));
143
+ const projectDir = join(parentDir, 'Lonely');
144
+ mkdirSync(projectDir, { recursive: true });
145
+ tempDir = projectDir;
146
+ await expect(createDefaultConfig(projectDir)).rejects.toThrow(/Could not auto-detect DocVault/);
147
+ });
148
+ });
149
+ describe('loadOrCreateConfig', () => {
150
+ let parentDir;
151
+ afterEach(() => {
152
+ if (parentDir) {
153
+ rmSync(parentDir, { recursive: true, force: true });
154
+ }
155
+ });
156
+ it('loads existing config', async () => {
157
+ parentDir = mkdtempSync(join(tmpdir(), 'specflow-parent-'));
158
+ const projectDir = join(parentDir, 'ExistingProject');
159
+ const docVaultDir = join(parentDir, 'DocVault');
160
+ mkdirSync(projectDir, { recursive: true });
161
+ mkdirSync(docVaultDir, { recursive: true });
162
+ const specflowDir = join(projectDir, '.specflow');
163
+ mkdirSync(specflowDir, { recursive: true });
164
+ writeFileSync(join(specflowDir, 'config.json'), JSON.stringify({
165
+ project: 'ExistingProject',
166
+ docvault: '../DocVault',
167
+ issue_prefix: 'EP',
168
+ }));
169
+ const result = await loadOrCreateConfig(projectDir);
170
+ expect(result.project).toBe('ExistingProject');
171
+ expect(result.issue_prefix).toBe('EP');
172
+ });
173
+ it('creates config when none exists', async () => {
174
+ parentDir = mkdtempSync(join(tmpdir(), 'specflow-parent-'));
175
+ const projectDir = join(parentDir, 'NewProject');
176
+ const docVaultDir = join(parentDir, 'DocVault');
177
+ mkdirSync(projectDir, { recursive: true });
178
+ mkdirSync(docVaultDir, { recursive: true });
179
+ const result = await loadOrCreateConfig(projectDir);
180
+ expect(result.project).toBe('NewProject');
181
+ expect(result.docvault).toBe('../DocVault');
182
+ });
183
+ });
184
+ //# sourceMappingURL=config-loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.test.js","sourceRoot":"","sources":["../../../src/core/__tests__/config-loader.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index-updater.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-updater.test.d.ts","sourceRoot":"","sources":["../../../src/core/__tests__/index-updater.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,122 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync, existsSync, } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { ensureIndexExists, addSpecToIndex, removeSpecFromIndex, } from '../index-updater.js';
6
+ function makeConfig(specflowRoot) {
7
+ return {
8
+ project: 'TestProject',
9
+ docvault: '../DocVault',
10
+ issue_prefix: 'TP',
11
+ docvaultAbsolute: join(specflowRoot, '..', '..'),
12
+ specflowRoot,
13
+ globalTemplatesPath: join(specflowRoot, '..', 'templates'),
14
+ };
15
+ }
16
+ describe('ensureIndexExists', () => {
17
+ let tempDir;
18
+ afterEach(() => {
19
+ if (tempDir) {
20
+ rmSync(tempDir, { recursive: true, force: true });
21
+ }
22
+ });
23
+ it('creates _Index.md with heading and table', async () => {
24
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
25
+ const dir = join(tempDir, 'specs');
26
+ await ensureIndexExists(dir, 'Specs');
27
+ const content = readFileSync(join(dir, '_Index.md'), 'utf-8');
28
+ expect(content).toContain('# Specs');
29
+ expect(content).toContain('| Page | Description |');
30
+ expect(content).toContain('|------|-------------|');
31
+ });
32
+ it('is idempotent — does not overwrite existing', async () => {
33
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
34
+ const dir = join(tempDir, 'specs');
35
+ mkdirSync(dir, { recursive: true });
36
+ writeFileSync(join(dir, '_Index.md'), '# Custom Content\n');
37
+ await ensureIndexExists(dir, 'Specs');
38
+ const content = readFileSync(join(dir, '_Index.md'), 'utf-8');
39
+ expect(content).toBe('# Custom Content\n');
40
+ });
41
+ });
42
+ describe('addSpecToIndex', () => {
43
+ let tempDir;
44
+ afterEach(() => {
45
+ if (tempDir) {
46
+ rmSync(tempDir, { recursive: true, force: true });
47
+ }
48
+ });
49
+ it('adds table row with wikilink', async () => {
50
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
51
+ const specflowRoot = join(tempDir, 'specflow', 'TestProject');
52
+ const specsDir = join(specflowRoot, 'specs');
53
+ mkdirSync(specsDir, { recursive: true });
54
+ writeFileSync(join(specsDir, '_Index.md'), '# Specs\n\n| Page | Description |\n|------|-------------|\n');
55
+ const config = makeConfig(specflowRoot);
56
+ await addSpecToIndex(config, 'SWF-1-auth', 'Authentication feature');
57
+ const content = readFileSync(join(specsDir, '_Index.md'), 'utf-8');
58
+ expect(content).toContain('| [[SWF-1-auth]] | Authentication feature |');
59
+ });
60
+ it('does not duplicate existing entries', async () => {
61
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
62
+ const specflowRoot = join(tempDir, 'specflow', 'TestProject');
63
+ const specsDir = join(specflowRoot, 'specs');
64
+ mkdirSync(specsDir, { recursive: true });
65
+ writeFileSync(join(specsDir, '_Index.md'), '# Specs\n\n| Page | Description |\n|------|-------------|\n| [[SWF-1-auth]] | Auth |\n');
66
+ const config = makeConfig(specflowRoot);
67
+ await addSpecToIndex(config, 'SWF-1-auth', 'Updated description');
68
+ const content = readFileSync(join(specsDir, '_Index.md'), 'utf-8');
69
+ const matches = content.match(/\[\[SWF-1-auth\]\]/g);
70
+ expect(matches).toHaveLength(1);
71
+ });
72
+ it('creates index if missing', async () => {
73
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
74
+ const specflowRoot = join(tempDir, 'specflow', 'TestProject');
75
+ const config = makeConfig(specflowRoot);
76
+ await addSpecToIndex(config, 'SWF-2-dashboard', 'Dashboard feature');
77
+ const specsDir = join(specflowRoot, 'specs');
78
+ expect(existsSync(join(specsDir, '_Index.md'))).toBe(true);
79
+ const content = readFileSync(join(specsDir, '_Index.md'), 'utf-8');
80
+ expect(content).toContain('[[SWF-2-dashboard]]');
81
+ });
82
+ });
83
+ describe('removeSpecFromIndex', () => {
84
+ let tempDir;
85
+ afterEach(() => {
86
+ if (tempDir) {
87
+ rmSync(tempDir, { recursive: true, force: true });
88
+ }
89
+ });
90
+ it('removes the correct entry', async () => {
91
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
92
+ const specflowRoot = join(tempDir, 'specflow', 'TestProject');
93
+ const specsDir = join(specflowRoot, 'specs');
94
+ mkdirSync(specsDir, { recursive: true });
95
+ writeFileSync(join(specsDir, '_Index.md'), '# Specs\n\n| Page | Description |\n|------|-------------|\n| [[SWF-1-auth]] | Auth |\n| [[SWF-2-dash]] | Dashboard |\n');
96
+ const config = makeConfig(specflowRoot);
97
+ await removeSpecFromIndex(config, 'SWF-1-auth');
98
+ const content = readFileSync(join(specsDir, '_Index.md'), 'utf-8');
99
+ expect(content).not.toContain('[[SWF-1-auth]]');
100
+ expect(content).toContain('[[SWF-2-dash]]');
101
+ });
102
+ it('is a no-op when spec not found', async () => {
103
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
104
+ const specflowRoot = join(tempDir, 'specflow', 'TestProject');
105
+ const specsDir = join(specflowRoot, 'specs');
106
+ mkdirSync(specsDir, { recursive: true });
107
+ const original = '# Specs\n\n| Page | Description |\n|------|-------------|\n| [[SWF-1-auth]] | Auth |\n';
108
+ writeFileSync(join(specsDir, '_Index.md'), original);
109
+ const config = makeConfig(specflowRoot);
110
+ await removeSpecFromIndex(config, 'SWF-99-missing');
111
+ const content = readFileSync(join(specsDir, '_Index.md'), 'utf-8');
112
+ expect(content).toBe(original);
113
+ });
114
+ it('is a no-op when file does not exist', async () => {
115
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-idx-'));
116
+ const specflowRoot = join(tempDir, 'specflow', 'TestProject');
117
+ const config = makeConfig(specflowRoot);
118
+ // Should not throw
119
+ await expect(removeSpecFromIndex(config, 'SWF-1-auth')).resolves.toBeUndefined();
120
+ });
121
+ });
122
+ //# sourceMappingURL=index-updater.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-updater.test.js","sourceRoot":"","sources":["../../../src/core/__tests__/index-updater.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=migration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.test.d.ts","sourceRoot":"","sources":["../../../src/core/__tests__/migration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,151 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync, } from 'fs';
3
+ import { join } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { needsMigration, migrateToDocVault } from '../migration.js';
6
+ function makeConfig(tempDir, specflowRoot) {
7
+ return {
8
+ project: 'TestProject',
9
+ docvault: '../DocVault',
10
+ issue_prefix: 'TP',
11
+ docvaultAbsolute: join(tempDir, 'DocVault'),
12
+ specflowRoot,
13
+ globalTemplatesPath: join(tempDir, 'DocVault', 'specflow', 'templates'),
14
+ };
15
+ }
16
+ describe('needsMigration', () => {
17
+ let tempDir;
18
+ afterEach(() => {
19
+ if (tempDir) {
20
+ rmSync(tempDir, { recursive: true, force: true });
21
+ }
22
+ });
23
+ it('returns true when .specflow/ has specs/', async () => {
24
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
25
+ const specsDir = join(tempDir, '.specflow', 'specs');
26
+ mkdirSync(specsDir, { recursive: true });
27
+ writeFileSync(join(specsDir, 'some-spec.md'), '# Spec');
28
+ const config = makeConfig(tempDir, join(tempDir, 'DocVault', 'specflow', 'TestProject'));
29
+ expect(await needsMigration(tempDir, config)).toBe(true);
30
+ });
31
+ it('returns true when .specflow/ has steering/', async () => {
32
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
33
+ const steeringDir = join(tempDir, '.specflow', 'steering');
34
+ mkdirSync(steeringDir, { recursive: true });
35
+ writeFileSync(join(steeringDir, 'product.md'), '# Product');
36
+ const config = makeConfig(tempDir, join(tempDir, 'DocVault', 'specflow', 'TestProject'));
37
+ expect(await needsMigration(tempDir, config)).toBe(true);
38
+ });
39
+ it('returns false when .specflow/ is empty (only config.json)', async () => {
40
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
41
+ const specflowDir = join(tempDir, '.specflow');
42
+ mkdirSync(specflowDir, { recursive: true });
43
+ writeFileSync(join(specflowDir, 'config.json'), '{}');
44
+ const config = makeConfig(tempDir, join(tempDir, 'DocVault', 'specflow', 'TestProject'));
45
+ expect(await needsMigration(tempDir, config)).toBe(false);
46
+ });
47
+ it('returns false when .specflow/ does not exist', async () => {
48
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
49
+ const config = makeConfig(tempDir, join(tempDir, 'DocVault', 'specflow', 'TestProject'));
50
+ expect(await needsMigration(tempDir, config)).toBe(false);
51
+ });
52
+ });
53
+ describe('migrateToDocVault', () => {
54
+ let tempDir;
55
+ afterEach(() => {
56
+ if (tempDir) {
57
+ rmSync(tempDir, { recursive: true, force: true });
58
+ }
59
+ });
60
+ function setupLocalSpecflow(dirs) {
61
+ for (const [dirName, files] of Object.entries(dirs)) {
62
+ const dirPath = join(tempDir, '.specflow', dirName);
63
+ mkdirSync(dirPath, { recursive: true });
64
+ for (const [fileName, content] of Object.entries(files)) {
65
+ writeFileSync(join(dirPath, fileName), content);
66
+ }
67
+ }
68
+ }
69
+ it('copies all 5 directories correctly', async () => {
70
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
71
+ const specflowRoot = join(tempDir, 'DocVault', 'specflow', 'TestProject');
72
+ setupLocalSpecflow({
73
+ steering: { 'product.md': '# Product' },
74
+ specs: { 'spec-1.md': '# Spec 1' },
75
+ 'user-templates': { 'tmpl.md': '# Template' },
76
+ approvals: { 'approval.md': '# Approval' },
77
+ archive: { 'old.md': '# Old' },
78
+ });
79
+ const config = makeConfig(tempDir, specflowRoot);
80
+ const result = await migrateToDocVault(tempDir, config);
81
+ expect(result.migratedDirs).toContain('steering');
82
+ expect(result.migratedDirs).toContain('specs');
83
+ expect(result.migratedDirs).toContain('user-templates');
84
+ expect(result.migratedDirs).toContain('approvals');
85
+ expect(result.migratedDirs).toContain('archive');
86
+ expect(result.errors).toHaveLength(0);
87
+ // Verify files were actually copied
88
+ expect(readFileSync(join(specflowRoot, 'steering', 'product.md'), 'utf-8')).toBe('# Product');
89
+ expect(readFileSync(join(specflowRoot, 'specs', 'spec-1.md'), 'utf-8')).toBe('# Spec 1');
90
+ expect(readFileSync(join(specflowRoot, 'approvals', 'approval.md'), 'utf-8')).toBe('# Approval');
91
+ expect(readFileSync(join(specflowRoot, 'archive', 'old.md'), 'utf-8')).toBe('# Old');
92
+ });
93
+ it('renames user-templates/ to templates/ in destination', async () => {
94
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
95
+ const specflowRoot = join(tempDir, 'DocVault', 'specflow', 'TestProject');
96
+ setupLocalSpecflow({
97
+ 'user-templates': { 'custom.md': '# Custom Template' },
98
+ });
99
+ const config = makeConfig(tempDir, specflowRoot);
100
+ await migrateToDocVault(tempDir, config);
101
+ // Should be at templates/, not user-templates/
102
+ expect(readFileSync(join(specflowRoot, 'templates', 'custom.md'), 'utf-8')).toBe('# Custom Template');
103
+ expect(existsSync(join(specflowRoot, 'user-templates'))).toBe(false);
104
+ });
105
+ it('skips destinations that already have content', async () => {
106
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
107
+ const specflowRoot = join(tempDir, 'DocVault', 'specflow', 'TestProject');
108
+ // Create existing content at destination
109
+ const destSpecs = join(specflowRoot, 'specs');
110
+ mkdirSync(destSpecs, { recursive: true });
111
+ writeFileSync(join(destSpecs, 'existing.md'), '# Existing');
112
+ setupLocalSpecflow({
113
+ specs: { 'new-spec.md': '# New Spec' },
114
+ });
115
+ const config = makeConfig(tempDir, specflowRoot);
116
+ const result = await migrateToDocVault(tempDir, config);
117
+ expect(result.skippedDirs).toContain('specs');
118
+ expect(result.migratedDirs).not.toContain('specs');
119
+ // Original destination content should be preserved
120
+ expect(readFileSync(join(destSpecs, 'existing.md'), 'utf-8')).toBe('# Existing');
121
+ });
122
+ it('handles missing source dirs gracefully', async () => {
123
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
124
+ const specflowRoot = join(tempDir, 'DocVault', 'specflow', 'TestProject');
125
+ mkdirSync(join(tempDir, '.specflow'), { recursive: true });
126
+ const config = makeConfig(tempDir, specflowRoot);
127
+ const result = await migrateToDocVault(tempDir, config);
128
+ // Nothing to migrate, nothing should error
129
+ expect(result.migratedDirs).toHaveLength(0);
130
+ expect(result.skippedDirs).toHaveLength(0);
131
+ expect(result.errors).toHaveLength(0);
132
+ });
133
+ it('returns accurate MigrationResult', async () => {
134
+ tempDir = mkdtempSync(join(tmpdir(), 'specflow-mig-'));
135
+ const specflowRoot = join(tempDir, 'DocVault', 'specflow', 'TestProject');
136
+ // Pre-populate one destination
137
+ const destSteering = join(specflowRoot, 'steering');
138
+ mkdirSync(destSteering, { recursive: true });
139
+ writeFileSync(join(destSteering, 'existing.md'), '# Existing');
140
+ setupLocalSpecflow({
141
+ steering: { 'product.md': '# Product' },
142
+ specs: { 'spec.md': '# Spec' },
143
+ });
144
+ const config = makeConfig(tempDir, specflowRoot);
145
+ const result = await migrateToDocVault(tempDir, config);
146
+ expect(result.migratedDirs).toEqual(['specs']);
147
+ expect(result.skippedDirs).toEqual(['steering']);
148
+ expect(result.errors).toHaveLength(0);
149
+ });
150
+ });
151
+ //# sourceMappingURL=migration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.test.js","sourceRoot":"","sources":["../../../src/core/__tests__/migration.test.ts"],"names":[],"mappings":"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"}