@l4yercak3/cli 1.0.6 → 1.1.1
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.
- package/bin/cli.js +6 -0
- package/docs/microsass_production_machine/CLI_API_REFERENCE.md +1197 -0
- package/docs/microsass_production_machine/CLI_PRODUCT_VISION.md +676 -0
- package/docs/microsass_production_machine/CLI_REQUIREMENTS.md +606 -0
- package/docs/microsass_production_machine/CONNECTED_APPLICATIONS_SPEC.md +390 -0
- package/docs/microsass_production_machine/IMPLEMENTATION_ROADMAP.md +725 -0
- package/docs/microsass_production_machine/OBJECT_MAPPINGS.md +808 -0
- package/docs/microsass_production_machine/REFERENCE_IMPLEMENTATION.md +532 -0
- package/package.json +1 -1
- package/src/api/backend-client.js +62 -0
- package/src/commands/spread.js +137 -12
- package/src/generators/api-client-generator.js +13 -6
- package/src/generators/env-generator.js +14 -1
- package/src/generators/index.js +4 -4
- package/src/generators/nextauth-generator.js +14 -9
- package/src/utils/file-utils.js +117 -0
- package/tests/api-client-generator.test.js +20 -13
- package/tests/backend-client.test.js +167 -0
- package/tests/file-utils.test.js +194 -0
- package/tests/generators-index.test.js +8 -0
- package/tests/nextauth-generator.test.js +38 -14
|
@@ -378,4 +378,171 @@ describe('BackendClient', () => {
|
|
|
378
378
|
expect(result.name).toBe('New Org');
|
|
379
379
|
});
|
|
380
380
|
});
|
|
381
|
+
|
|
382
|
+
// ============================================
|
|
383
|
+
// Connected Applications API Tests
|
|
384
|
+
// ============================================
|
|
385
|
+
|
|
386
|
+
describe('checkExistingApplication', () => {
|
|
387
|
+
it('returns found=true when application exists', async () => {
|
|
388
|
+
const mockResponse = {
|
|
389
|
+
ok: true,
|
|
390
|
+
json: jest.fn().mockResolvedValue({
|
|
391
|
+
found: true,
|
|
392
|
+
application: {
|
|
393
|
+
id: 'app-123',
|
|
394
|
+
name: 'My App',
|
|
395
|
+
},
|
|
396
|
+
}),
|
|
397
|
+
};
|
|
398
|
+
fetch.mockResolvedValue(mockResponse);
|
|
399
|
+
|
|
400
|
+
const result = await BackendClient.checkExistingApplication('org-123', 'hash123');
|
|
401
|
+
|
|
402
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
403
|
+
expect.stringContaining('/api/v1/cli/applications/by-path?organizationId=org-123&hash=hash123'),
|
|
404
|
+
expect.objectContaining({ method: 'GET' })
|
|
405
|
+
);
|
|
406
|
+
expect(result.found).toBe(true);
|
|
407
|
+
expect(result.application.id).toBe('app-123');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('returns found=false when 404', async () => {
|
|
411
|
+
const mockResponse = {
|
|
412
|
+
ok: false,
|
|
413
|
+
status: 404,
|
|
414
|
+
json: jest.fn().mockResolvedValue({ error: 'Not found' }),
|
|
415
|
+
};
|
|
416
|
+
fetch.mockResolvedValue(mockResponse);
|
|
417
|
+
|
|
418
|
+
const result = await BackendClient.checkExistingApplication('org-123', 'hash456');
|
|
419
|
+
|
|
420
|
+
expect(result.found).toBe(false);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('throws error on non-404 errors', async () => {
|
|
424
|
+
const mockResponse = {
|
|
425
|
+
ok: false,
|
|
426
|
+
status: 500,
|
|
427
|
+
json: jest.fn().mockResolvedValue({ message: 'Server error' }),
|
|
428
|
+
};
|
|
429
|
+
fetch.mockResolvedValue(mockResponse);
|
|
430
|
+
|
|
431
|
+
await expect(BackendClient.checkExistingApplication('org-123', 'hash789'))
|
|
432
|
+
.rejects.toThrow('Server error');
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
describe('registerApplication', () => {
|
|
437
|
+
it('registers new application', async () => {
|
|
438
|
+
const mockResponse = {
|
|
439
|
+
ok: true,
|
|
440
|
+
json: jest.fn().mockResolvedValue({
|
|
441
|
+
applicationId: 'app-new-123',
|
|
442
|
+
backendUrl: 'https://api.l4yercak3.com',
|
|
443
|
+
}),
|
|
444
|
+
};
|
|
445
|
+
fetch.mockResolvedValue(mockResponse);
|
|
446
|
+
|
|
447
|
+
const registrationData = {
|
|
448
|
+
organizationId: 'org-123',
|
|
449
|
+
name: 'My New App',
|
|
450
|
+
source: {
|
|
451
|
+
type: 'cli',
|
|
452
|
+
projectPathHash: 'hash123',
|
|
453
|
+
framework: 'nextjs',
|
|
454
|
+
},
|
|
455
|
+
connection: {
|
|
456
|
+
features: ['crm', 'events'],
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const result = await BackendClient.registerApplication(registrationData);
|
|
461
|
+
|
|
462
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
463
|
+
expect.stringContaining('/api/v1/cli/applications'),
|
|
464
|
+
expect.objectContaining({
|
|
465
|
+
method: 'POST',
|
|
466
|
+
body: JSON.stringify(registrationData),
|
|
467
|
+
})
|
|
468
|
+
);
|
|
469
|
+
expect(result.applicationId).toBe('app-new-123');
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
describe('updateApplication', () => {
|
|
474
|
+
it('updates existing application', async () => {
|
|
475
|
+
const mockResponse = {
|
|
476
|
+
ok: true,
|
|
477
|
+
json: jest.fn().mockResolvedValue({
|
|
478
|
+
success: true,
|
|
479
|
+
applicationId: 'app-123',
|
|
480
|
+
}),
|
|
481
|
+
};
|
|
482
|
+
fetch.mockResolvedValue(mockResponse);
|
|
483
|
+
|
|
484
|
+
const updates = {
|
|
485
|
+
connection: {
|
|
486
|
+
features: ['crm', 'events', 'invoicing'],
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const result = await BackendClient.updateApplication('app-123', updates);
|
|
491
|
+
|
|
492
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
493
|
+
expect.stringContaining('/api/v1/cli/applications/app-123'),
|
|
494
|
+
expect.objectContaining({
|
|
495
|
+
method: 'PATCH',
|
|
496
|
+
body: JSON.stringify(updates),
|
|
497
|
+
})
|
|
498
|
+
);
|
|
499
|
+
expect(result.success).toBe(true);
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
describe('getApplication', () => {
|
|
504
|
+
it('fetches application details', async () => {
|
|
505
|
+
const mockResponse = {
|
|
506
|
+
ok: true,
|
|
507
|
+
json: jest.fn().mockResolvedValue({
|
|
508
|
+
id: 'app-123',
|
|
509
|
+
name: 'My App',
|
|
510
|
+
status: 'active',
|
|
511
|
+
}),
|
|
512
|
+
};
|
|
513
|
+
fetch.mockResolvedValue(mockResponse);
|
|
514
|
+
|
|
515
|
+
const result = await BackendClient.getApplication('app-123');
|
|
516
|
+
|
|
517
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
518
|
+
expect.stringContaining('/api/v1/cli/applications/app-123'),
|
|
519
|
+
expect.objectContaining({ method: 'GET' })
|
|
520
|
+
);
|
|
521
|
+
expect(result.id).toBe('app-123');
|
|
522
|
+
expect(result.status).toBe('active');
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
describe('listApplications', () => {
|
|
527
|
+
it('lists applications for organization', async () => {
|
|
528
|
+
const mockResponse = {
|
|
529
|
+
ok: true,
|
|
530
|
+
json: jest.fn().mockResolvedValue({
|
|
531
|
+
applications: [
|
|
532
|
+
{ id: 'app-1', name: 'App One' },
|
|
533
|
+
{ id: 'app-2', name: 'App Two' },
|
|
534
|
+
],
|
|
535
|
+
}),
|
|
536
|
+
};
|
|
537
|
+
fetch.mockResolvedValue(mockResponse);
|
|
538
|
+
|
|
539
|
+
const result = await BackendClient.listApplications('org-123');
|
|
540
|
+
|
|
541
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
542
|
+
expect.stringContaining('/api/v1/cli/applications?organizationId=org-123'),
|
|
543
|
+
expect.objectContaining({ method: 'GET' })
|
|
544
|
+
);
|
|
545
|
+
expect(result.applications).toHaveLength(2);
|
|
546
|
+
});
|
|
547
|
+
});
|
|
381
548
|
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for File Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
jest.mock('fs');
|
|
9
|
+
jest.mock('inquirer');
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const inquirer = require('inquirer');
|
|
13
|
+
|
|
14
|
+
// Must require after mocks are set up
|
|
15
|
+
const {
|
|
16
|
+
GENERATED_HEADER,
|
|
17
|
+
isGeneratedFile,
|
|
18
|
+
checkFileOverwrite,
|
|
19
|
+
writeFileWithBackup,
|
|
20
|
+
ensureDir,
|
|
21
|
+
generateProjectPathHash,
|
|
22
|
+
} = require('../src/utils/file-utils');
|
|
23
|
+
|
|
24
|
+
describe('File Utilities', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
jest.clearAllMocks();
|
|
27
|
+
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
console.log.mockRestore();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('generateProjectPathHash', () => {
|
|
35
|
+
it('generates SHA256 hash of absolute project path', () => {
|
|
36
|
+
const projectPath = '/test/project/path';
|
|
37
|
+
const expectedHash = crypto.createHash('sha256')
|
|
38
|
+
.update(path.resolve(projectPath))
|
|
39
|
+
.digest('hex');
|
|
40
|
+
|
|
41
|
+
const result = generateProjectPathHash(projectPath);
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(expectedHash);
|
|
44
|
+
expect(result).toHaveLength(64); // SHA256 produces 64 hex chars
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('generates same hash for same path', () => {
|
|
48
|
+
const projectPath = '/consistent/path';
|
|
49
|
+
|
|
50
|
+
const hash1 = generateProjectPathHash(projectPath);
|
|
51
|
+
const hash2 = generateProjectPathHash(projectPath);
|
|
52
|
+
|
|
53
|
+
expect(hash1).toBe(hash2);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('generates different hashes for different paths', () => {
|
|
57
|
+
const path1 = '/path/one';
|
|
58
|
+
const path2 = '/path/two';
|
|
59
|
+
|
|
60
|
+
const hash1 = generateProjectPathHash(path1);
|
|
61
|
+
const hash2 = generateProjectPathHash(path2);
|
|
62
|
+
|
|
63
|
+
expect(hash1).not.toBe(hash2);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('handles relative paths by resolving to absolute', () => {
|
|
67
|
+
const relativePath = './my-project';
|
|
68
|
+
const absolutePath = path.resolve(relativePath);
|
|
69
|
+
const expectedHash = crypto.createHash('sha256')
|
|
70
|
+
.update(absolutePath)
|
|
71
|
+
.digest('hex');
|
|
72
|
+
|
|
73
|
+
const result = generateProjectPathHash(relativePath);
|
|
74
|
+
|
|
75
|
+
expect(result).toBe(expectedHash);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('isGeneratedFile', () => {
|
|
80
|
+
it('returns false if file does not exist', () => {
|
|
81
|
+
fs.existsSync.mockReturnValue(false);
|
|
82
|
+
|
|
83
|
+
expect(isGeneratedFile('/nonexistent/file.js')).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('returns true if file contains generated header', () => {
|
|
87
|
+
fs.existsSync.mockReturnValue(true);
|
|
88
|
+
fs.readFileSync.mockReturnValue(`// ${GENERATED_HEADER}\n// Some content`);
|
|
89
|
+
|
|
90
|
+
expect(isGeneratedFile('/test/file.js')).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns false if file does not contain generated header', () => {
|
|
94
|
+
fs.existsSync.mockReturnValue(true);
|
|
95
|
+
fs.readFileSync.mockReturnValue('// Manual file content\nconst x = 1;');
|
|
96
|
+
|
|
97
|
+
expect(isGeneratedFile('/test/file.js')).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('returns false if file read throws error', () => {
|
|
101
|
+
fs.existsSync.mockReturnValue(true);
|
|
102
|
+
fs.readFileSync.mockImplementation(() => {
|
|
103
|
+
throw new Error('Read error');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(isGeneratedFile('/test/file.js')).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('checkFileOverwrite', () => {
|
|
111
|
+
it('returns write if file does not exist', async () => {
|
|
112
|
+
fs.existsSync.mockReturnValue(false);
|
|
113
|
+
|
|
114
|
+
const result = await checkFileOverwrite('/new/file.js');
|
|
115
|
+
|
|
116
|
+
expect(result).toBe('write');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('returns write if file is generated by us', async () => {
|
|
120
|
+
fs.existsSync.mockReturnValue(true);
|
|
121
|
+
fs.readFileSync.mockReturnValue(`// ${GENERATED_HEADER}\ncontent`);
|
|
122
|
+
|
|
123
|
+
const result = await checkFileOverwrite('/test/file.js');
|
|
124
|
+
|
|
125
|
+
expect(result).toBe('write');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('returns skip if defaultAction is skip', async () => {
|
|
129
|
+
fs.existsSync.mockReturnValue(true);
|
|
130
|
+
fs.readFileSync.mockReturnValue('manual content');
|
|
131
|
+
|
|
132
|
+
const result = await checkFileOverwrite('/test/file.js', { defaultAction: 'skip' });
|
|
133
|
+
|
|
134
|
+
expect(result).toBe('skip');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('prompts user if file exists and is not generated', async () => {
|
|
138
|
+
fs.existsSync.mockReturnValue(true);
|
|
139
|
+
fs.readFileSync.mockReturnValue('manual content');
|
|
140
|
+
inquirer.prompt.mockResolvedValue({ action: 'backup' });
|
|
141
|
+
|
|
142
|
+
const result = await checkFileOverwrite('/test/file.js');
|
|
143
|
+
|
|
144
|
+
expect(inquirer.prompt).toHaveBeenCalled();
|
|
145
|
+
expect(result).toBe('backup');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('writeFileWithBackup', () => {
|
|
150
|
+
it('returns null if action is skip', () => {
|
|
151
|
+
const result = writeFileWithBackup('/test/file.js', 'content', 'skip');
|
|
152
|
+
|
|
153
|
+
expect(result).toBeNull();
|
|
154
|
+
expect(fs.writeFileSync).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('creates backup if action is backup', () => {
|
|
158
|
+
fs.existsSync.mockReturnValue(true);
|
|
159
|
+
|
|
160
|
+
writeFileWithBackup('/test/file.js', 'new content', 'backup');
|
|
161
|
+
|
|
162
|
+
expect(fs.copyFileSync).toHaveBeenCalledWith('/test/file.js', '/test/file.js.backup');
|
|
163
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith('/test/file.js', 'new content', 'utf8');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('writes file directly if action is write', () => {
|
|
167
|
+
fs.existsSync.mockReturnValue(true);
|
|
168
|
+
|
|
169
|
+
const result = writeFileWithBackup('/test/file.js', 'content', 'write');
|
|
170
|
+
|
|
171
|
+
expect(fs.copyFileSync).not.toHaveBeenCalled();
|
|
172
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith('/test/file.js', 'content', 'utf8');
|
|
173
|
+
expect(result).toBe('/test/file.js');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('ensureDir', () => {
|
|
178
|
+
it('creates directory if it does not exist', () => {
|
|
179
|
+
fs.existsSync.mockReturnValue(false);
|
|
180
|
+
|
|
181
|
+
ensureDir('/test/dir');
|
|
182
|
+
|
|
183
|
+
expect(fs.mkdirSync).toHaveBeenCalledWith('/test/dir', { recursive: true });
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('does nothing if directory exists', () => {
|
|
187
|
+
fs.existsSync.mockReturnValue(true);
|
|
188
|
+
|
|
189
|
+
ensureDir('/test/dir');
|
|
190
|
+
|
|
191
|
+
expect(fs.mkdirSync).not.toHaveBeenCalled();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
|
|
7
7
|
jest.mock('fs');
|
|
8
|
+
jest.mock('../src/utils/file-utils', () => ({
|
|
9
|
+
checkFileOverwrite: jest.fn().mockResolvedValue('write'),
|
|
10
|
+
writeFileWithBackup: jest.fn((filePath, content, action) => {
|
|
11
|
+
if (action === 'skip') return null;
|
|
12
|
+
return filePath;
|
|
13
|
+
}),
|
|
14
|
+
ensureDir: jest.fn(),
|
|
15
|
+
}));
|
|
8
16
|
|
|
9
17
|
const FileGenerator = require('../src/generators/index');
|
|
10
18
|
|
|
@@ -6,8 +6,17 @@ const fs = require('fs');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
|
|
8
8
|
jest.mock('fs');
|
|
9
|
+
jest.mock('../src/utils/file-utils', () => ({
|
|
10
|
+
checkFileOverwrite: jest.fn().mockResolvedValue('write'),
|
|
11
|
+
writeFileWithBackup: jest.fn((filePath, content, action) => {
|
|
12
|
+
if (action === 'skip') return null;
|
|
13
|
+
return filePath;
|
|
14
|
+
}),
|
|
15
|
+
ensureDir: jest.fn(),
|
|
16
|
+
}));
|
|
9
17
|
|
|
10
18
|
const NextAuthGenerator = require('../src/generators/nextauth-generator');
|
|
19
|
+
const { checkFileOverwrite, writeFileWithBackup, ensureDir } = require('../src/utils/file-utils');
|
|
11
20
|
|
|
12
21
|
describe('NextAuthGenerator', () => {
|
|
13
22
|
const mockProjectPath = '/test/project';
|
|
@@ -17,10 +26,11 @@ describe('NextAuthGenerator', () => {
|
|
|
17
26
|
fs.existsSync.mockReturnValue(false);
|
|
18
27
|
fs.mkdirSync.mockReturnValue(undefined);
|
|
19
28
|
fs.writeFileSync.mockReturnValue(undefined);
|
|
29
|
+
checkFileOverwrite.mockResolvedValue('write');
|
|
20
30
|
});
|
|
21
31
|
|
|
22
32
|
describe('generate', () => {
|
|
23
|
-
it('creates route.js in app/api/auth/[...nextauth] for App Router', () => {
|
|
33
|
+
it('creates route.js in app/api/auth/[...nextauth] for App Router', async () => {
|
|
24
34
|
const options = {
|
|
25
35
|
projectPath: mockProjectPath,
|
|
26
36
|
backendUrl: 'https://backend.test.com',
|
|
@@ -29,22 +39,20 @@ describe('NextAuthGenerator', () => {
|
|
|
29
39
|
isTypeScript: false,
|
|
30
40
|
};
|
|
31
41
|
|
|
32
|
-
const result = NextAuthGenerator.generate(options);
|
|
42
|
+
const result = await NextAuthGenerator.generate(options);
|
|
33
43
|
|
|
34
44
|
expect(result).toBe(
|
|
35
45
|
path.join(mockProjectPath, 'app', 'api', 'auth', '[...nextauth]', 'route.js')
|
|
36
46
|
);
|
|
37
|
-
expect(
|
|
38
|
-
path.join(mockProjectPath, 'app', 'api', 'auth')
|
|
39
|
-
{ recursive: true }
|
|
47
|
+
expect(ensureDir).toHaveBeenCalledWith(
|
|
48
|
+
path.join(mockProjectPath, 'app', 'api', 'auth')
|
|
40
49
|
);
|
|
41
|
-
expect(
|
|
42
|
-
path.join(mockProjectPath, 'app', 'api', 'auth', '[...nextauth]')
|
|
43
|
-
{ recursive: true }
|
|
50
|
+
expect(ensureDir).toHaveBeenCalledWith(
|
|
51
|
+
path.join(mockProjectPath, 'app', 'api', 'auth', '[...nextauth]')
|
|
44
52
|
);
|
|
45
53
|
});
|
|
46
54
|
|
|
47
|
-
it('creates [...nextauth].ts in pages/api/auth for Pages Router', () => {
|
|
55
|
+
it('creates [...nextauth].ts in pages/api/auth for Pages Router', async () => {
|
|
48
56
|
const options = {
|
|
49
57
|
projectPath: mockProjectPath,
|
|
50
58
|
backendUrl: 'https://backend.test.com',
|
|
@@ -53,14 +61,30 @@ describe('NextAuthGenerator', () => {
|
|
|
53
61
|
isTypeScript: true,
|
|
54
62
|
};
|
|
55
63
|
|
|
56
|
-
const result = NextAuthGenerator.generate(options);
|
|
64
|
+
const result = await NextAuthGenerator.generate(options);
|
|
57
65
|
|
|
58
66
|
expect(result).toBe(
|
|
59
67
|
path.join(mockProjectPath, 'pages', 'api', 'auth', '[...nextauth].ts')
|
|
60
68
|
);
|
|
61
69
|
});
|
|
62
70
|
|
|
63
|
-
it('
|
|
71
|
+
it('returns null when user skips overwrite', async () => {
|
|
72
|
+
checkFileOverwrite.mockResolvedValue('skip');
|
|
73
|
+
|
|
74
|
+
const options = {
|
|
75
|
+
projectPath: mockProjectPath,
|
|
76
|
+
backendUrl: 'https://backend.test.com',
|
|
77
|
+
oauthProviders: ['google'],
|
|
78
|
+
routerType: 'app',
|
|
79
|
+
isTypeScript: false,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const result = await NextAuthGenerator.generate(options);
|
|
83
|
+
|
|
84
|
+
expect(result).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('does not create [...nextauth] dir for Pages Router', async () => {
|
|
64
88
|
const options = {
|
|
65
89
|
projectPath: mockProjectPath,
|
|
66
90
|
backendUrl: 'https://backend.test.com',
|
|
@@ -69,11 +93,11 @@ describe('NextAuthGenerator', () => {
|
|
|
69
93
|
isTypeScript: false,
|
|
70
94
|
};
|
|
71
95
|
|
|
72
|
-
NextAuthGenerator.generate(options);
|
|
96
|
+
await NextAuthGenerator.generate(options);
|
|
73
97
|
|
|
74
98
|
// Should only create pages/api/auth, not [...nextauth] dir
|
|
75
|
-
const
|
|
76
|
-
expect(
|
|
99
|
+
const ensureDirCalls = ensureDir.mock.calls.map((c) => c[0]);
|
|
100
|
+
expect(ensureDirCalls).not.toContain(
|
|
77
101
|
path.join(mockProjectPath, 'pages', 'api', 'auth', '[...nextauth]')
|
|
78
102
|
);
|
|
79
103
|
});
|