@l4yercak3/cli 1.0.0 → 1.0.2
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/.claude/settings.local.json +3 -1
- package/CLAUDE.md +100 -0
- package/package.json +12 -6
- package/src/api/backend-client.js +18 -7
- package/src/commands/login.js +36 -13
- package/src/config/config-manager.js +2 -2
- package/src/generators/oauth-guide-generator.js +1 -1
- package/templates/CLAUDE.md +86 -0
- package/tests/backend-client.test.js +25 -5
- package/tests/config-manager.test.js +10 -2
- package/tests/oauth-guide-generator.test.js +1 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# L4YERCAK3 CLI - Claude Code Configuration
|
|
2
|
+
|
|
3
|
+
## About This Project
|
|
4
|
+
|
|
5
|
+
This is **L4YERCAK3 CLI** (`@l4yercak3/cli`) - the official CLI tool for integrating Next.js projects with the Layer Cake platform. It handles authentication, project setup, and generates boilerplate code for L4YERCAK3 features.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @l4yercak3/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## CLI Commands
|
|
14
|
+
|
|
15
|
+
| Command | Description |
|
|
16
|
+
|---------|-------------|
|
|
17
|
+
| `l4yercak3 login` | Authenticate with the L4YERCAK3 platform (opens browser) |
|
|
18
|
+
| `l4yercak3 logout` | Log out and clear session |
|
|
19
|
+
| `l4yercak3 status` | Show authentication status and session info |
|
|
20
|
+
| `l4yercak3 spread` | Initialize L4YERCAK3 in a Next.js project (interactive setup) |
|
|
21
|
+
| `icing` | Alias for `l4yercak3` command |
|
|
22
|
+
|
|
23
|
+
## Project Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
src/
|
|
27
|
+
├── api/ # Backend API client
|
|
28
|
+
│ └── backend-client.js
|
|
29
|
+
├── commands/ # CLI command handlers
|
|
30
|
+
│ ├── login.js # Browser-based OAuth login
|
|
31
|
+
│ ├── logout.js # Session cleanup
|
|
32
|
+
│ ├── spread.js # Project initialization wizard
|
|
33
|
+
│ └── status.js # Auth status display
|
|
34
|
+
├── config/ # Configuration management
|
|
35
|
+
│ └── config-manager.js
|
|
36
|
+
├── detectors/ # Project analysis
|
|
37
|
+
│ ├── nextjs-detector.js
|
|
38
|
+
│ ├── github-detector.js
|
|
39
|
+
│ ├── oauth-detector.js
|
|
40
|
+
│ └── api-client-detector.js
|
|
41
|
+
├── generators/ # Code generators
|
|
42
|
+
│ ├── api-client-generator.js
|
|
43
|
+
│ ├── env-generator.js
|
|
44
|
+
│ ├── nextauth-generator.js
|
|
45
|
+
│ ├── oauth-guide-generator.js
|
|
46
|
+
│ └── gitignore-generator.js
|
|
47
|
+
├── index.js # Main entry point
|
|
48
|
+
└── logo.js # ASCII art branding
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Development Commands
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm run build # Build for production
|
|
55
|
+
npm run lint # Run ESLint
|
|
56
|
+
npm run lint:fix # Fix ESLint issues
|
|
57
|
+
npm run type-check # TypeScript type checking (via JSDoc)
|
|
58
|
+
npm test # Run Jest tests
|
|
59
|
+
npm run test:watch # Run tests in watch mode
|
|
60
|
+
npm run test:coverage # Run tests with coverage report
|
|
61
|
+
npm run verify # Run lint + type-check + test + build
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Code Style
|
|
65
|
+
|
|
66
|
+
- JavaScript with JSDoc type annotations
|
|
67
|
+
- ESLint with Prettier formatting
|
|
68
|
+
- Jest for testing with mocks for fs, fetch, and external dependencies
|
|
69
|
+
- Keep files focused and under 500 lines
|
|
70
|
+
- Use async/await for asynchronous operations
|
|
71
|
+
|
|
72
|
+
## Testing Patterns
|
|
73
|
+
|
|
74
|
+
When writing tests:
|
|
75
|
+
- Mock `fs` module for file operations
|
|
76
|
+
- Mock `node-fetch` for API calls
|
|
77
|
+
- Mock `chalk` to return plain strings
|
|
78
|
+
- Use `jest.spyOn(console, 'error').mockImplementation(() => {})` to silence expected errors
|
|
79
|
+
- For singleton modules that instantiate on require, set up mocks BEFORE requiring the module
|
|
80
|
+
|
|
81
|
+
## Configuration Storage
|
|
82
|
+
|
|
83
|
+
User config is stored at `~/.l4yercak3/config.json` with:
|
|
84
|
+
- Session tokens and expiration
|
|
85
|
+
- Organization memberships
|
|
86
|
+
- Project configurations by path
|
|
87
|
+
- Backend URL settings
|
|
88
|
+
|
|
89
|
+
## Available Features
|
|
90
|
+
|
|
91
|
+
The `spread` command can set up:
|
|
92
|
+
- **CRM** - Customer relationship management integration
|
|
93
|
+
- **OAuth** - Social login with Google, GitHub, Discord, etc.
|
|
94
|
+
- **Stripe** - Payment processing integration
|
|
95
|
+
- **Analytics** - Usage tracking and metrics
|
|
96
|
+
|
|
97
|
+
## Links
|
|
98
|
+
|
|
99
|
+
- npm: https://www.npmjs.com/package/@l4yercak3/cli
|
|
100
|
+
- GitHub: https://github.com/voundbrand/l4yercak3-cli
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@l4yercak3/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Icing on the L4yercak3 - The sweet finishing touch for your Layer Cake integration",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"l4yercak3": "
|
|
8
|
-
"icing": "
|
|
7
|
+
"l4yercak3": "bin/cli.js",
|
|
8
|
+
"icing": "bin/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node bin/cli.js",
|
|
@@ -21,9 +21,15 @@
|
|
|
21
21
|
},
|
|
22
22
|
"jest": {
|
|
23
23
|
"testEnvironment": "node",
|
|
24
|
-
"testMatch": [
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
"testMatch": [
|
|
25
|
+
"**/tests/**/*.test.js"
|
|
26
|
+
],
|
|
27
|
+
"collectCoverageFrom": [
|
|
28
|
+
"src/**/*.js"
|
|
29
|
+
],
|
|
30
|
+
"coveragePathIgnorePatterns": [
|
|
31
|
+
"/node_modules/"
|
|
32
|
+
]
|
|
27
33
|
},
|
|
28
34
|
"keywords": [
|
|
29
35
|
"l4yercak3",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Handles communication with L4YERCAK3 backend API
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const crypto = require('crypto');
|
|
6
7
|
const fetch = require('node-fetch');
|
|
7
8
|
const configManager = require('../config/config-manager');
|
|
8
9
|
|
|
@@ -11,6 +12,13 @@ class BackendClient {
|
|
|
11
12
|
this.baseUrl = configManager.getBackendUrl();
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Generate a cryptographically secure state token for CSRF protection
|
|
17
|
+
*/
|
|
18
|
+
generateState() {
|
|
19
|
+
return crypto.randomBytes(32).toString('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
/**
|
|
15
23
|
* Get headers for API requests
|
|
16
24
|
*/
|
|
@@ -98,18 +106,21 @@ class BackendClient {
|
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
/**
|
|
101
|
-
* Get CLI login URL
|
|
109
|
+
* Get CLI login URL with state parameter for CSRF protection
|
|
110
|
+
* @param {string} state - The state token generated by the CLI
|
|
111
|
+
* @param {string|null} provider - Optional OAuth provider for direct auth
|
|
112
|
+
* @returns {string} The login URL
|
|
102
113
|
*/
|
|
103
|
-
getLoginUrl(provider = null) {
|
|
114
|
+
getLoginUrl(state, provider = null) {
|
|
104
115
|
const backendUrl = configManager.getBackendUrl();
|
|
105
|
-
const callbackUrl = 'http://localhost:
|
|
106
|
-
|
|
116
|
+
const callbackUrl = 'http://localhost:3000/callback';
|
|
117
|
+
|
|
107
118
|
if (provider) {
|
|
108
119
|
// Direct OAuth provider URL
|
|
109
|
-
return `${backendUrl}/api/auth/oauth-signup?provider=${provider}&sessionType=cli&callback=${encodeURIComponent(callbackUrl)}`;
|
|
120
|
+
return `${backendUrl}/api/auth/oauth-signup?provider=${provider}&sessionType=cli&state=${state}&callback=${encodeURIComponent(callbackUrl)}`;
|
|
110
121
|
} else {
|
|
111
|
-
// Provider selection page
|
|
112
|
-
return `${backendUrl}/auth/cli-login?callback=${encodeURIComponent(callbackUrl)}`;
|
|
122
|
+
// Provider selection page
|
|
123
|
+
return `${backendUrl}/auth/cli-login?state=${state}&callback=${encodeURIComponent(callbackUrl)}`;
|
|
113
124
|
}
|
|
114
125
|
}
|
|
115
126
|
|
package/src/commands/login.js
CHANGED
|
@@ -10,17 +10,37 @@ const chalk = require('chalk');
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Start local server to receive OAuth callback
|
|
13
|
+
* @param {string} expectedState - The state token to verify against
|
|
13
14
|
*/
|
|
14
|
-
function startCallbackServer() {
|
|
15
|
+
function startCallbackServer(expectedState) {
|
|
15
16
|
return new Promise((resolve, reject) => {
|
|
16
17
|
const http = require('http');
|
|
17
|
-
|
|
18
|
+
|
|
18
19
|
const server = http.createServer((req, res) => {
|
|
19
|
-
const url = new URL(req.url, 'http://localhost:
|
|
20
|
-
|
|
20
|
+
const url = new URL(req.url, 'http://localhost:3000');
|
|
21
|
+
|
|
21
22
|
if (url.pathname === '/callback') {
|
|
22
23
|
const token = url.searchParams.get('token');
|
|
23
|
-
|
|
24
|
+
const returnedState = url.searchParams.get('state');
|
|
25
|
+
|
|
26
|
+
// Verify state to prevent CSRF attacks
|
|
27
|
+
if (returnedState !== expectedState) {
|
|
28
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
29
|
+
res.end(`
|
|
30
|
+
<html>
|
|
31
|
+
<head><title>CLI Login Error</title></head>
|
|
32
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
33
|
+
<h1 style="color: #EF4444;">❌ Security Error</h1>
|
|
34
|
+
<p>State mismatch - possible CSRF attack. Please try again.</p>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
37
|
+
`);
|
|
38
|
+
|
|
39
|
+
server.close();
|
|
40
|
+
reject(new Error('State mismatch - security validation failed'));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
if (token) {
|
|
25
45
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
26
46
|
res.end(`
|
|
@@ -32,7 +52,7 @@ function startCallbackServer() {
|
|
|
32
52
|
</body>
|
|
33
53
|
</html>
|
|
34
54
|
`);
|
|
35
|
-
|
|
55
|
+
|
|
36
56
|
server.close();
|
|
37
57
|
resolve(token);
|
|
38
58
|
} else {
|
|
@@ -46,7 +66,7 @@ function startCallbackServer() {
|
|
|
46
66
|
</body>
|
|
47
67
|
</html>
|
|
48
68
|
`);
|
|
49
|
-
|
|
69
|
+
|
|
50
70
|
server.close();
|
|
51
71
|
reject(new Error('No token received'));
|
|
52
72
|
}
|
|
@@ -56,7 +76,7 @@ function startCallbackServer() {
|
|
|
56
76
|
}
|
|
57
77
|
});
|
|
58
78
|
|
|
59
|
-
server.listen(
|
|
79
|
+
server.listen(3000, 'localhost', () => {
|
|
60
80
|
console.log(chalk.gray(' Waiting for authentication...'));
|
|
61
81
|
});
|
|
62
82
|
|
|
@@ -85,13 +105,16 @@ async function handleLogin() {
|
|
|
85
105
|
|
|
86
106
|
console.log(chalk.cyan(' 🔐 Opening browser for authentication...\n'));
|
|
87
107
|
|
|
88
|
-
//
|
|
89
|
-
const
|
|
108
|
+
// Generate state for CSRF protection
|
|
109
|
+
const state = backendClient.generateState();
|
|
110
|
+
|
|
111
|
+
// Start callback server with expected state
|
|
112
|
+
const callbackPromise = startCallbackServer(state);
|
|
90
113
|
|
|
91
|
-
// Open browser
|
|
92
|
-
const loginUrl = backendClient.getLoginUrl();
|
|
114
|
+
// Open browser with state parameter
|
|
115
|
+
const loginUrl = backendClient.getLoginUrl(state);
|
|
93
116
|
console.log(chalk.gray(` Login URL: ${loginUrl}\n`));
|
|
94
|
-
|
|
117
|
+
|
|
95
118
|
await open(loginUrl);
|
|
96
119
|
|
|
97
120
|
// Wait for callback
|
|
@@ -33,7 +33,7 @@ class ConfigManager {
|
|
|
33
33
|
session: null,
|
|
34
34
|
organizations: [],
|
|
35
35
|
settings: {
|
|
36
|
-
backendUrl: process.env.L4YERCAK3_BACKEND_URL || 'https://
|
|
36
|
+
backendUrl: process.env.L4YERCAK3_BACKEND_URL || 'https://app.l4yercak3.com',
|
|
37
37
|
},
|
|
38
38
|
};
|
|
39
39
|
}
|
|
@@ -118,7 +118,7 @@ class ConfigManager {
|
|
|
118
118
|
*/
|
|
119
119
|
getBackendUrl() {
|
|
120
120
|
const config = this.getConfig();
|
|
121
|
-
return config.settings?.backendUrl || process.env.L4YERCAK3_BACKEND_URL || 'https://
|
|
121
|
+
return config.settings?.backendUrl || process.env.L4YERCAK3_BACKEND_URL || 'https://app.l4yercak3.com';
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
@@ -267,7 +267,7 @@ Once OAuth is set up:
|
|
|
267
267
|
|
|
268
268
|
---
|
|
269
269
|
|
|
270
|
-
**Need Help?** Check the [L4YERCAK3 Documentation](https://
|
|
270
|
+
**Need Help?** Check the [L4YERCAK3 Documentation](https://www.l4yercak3.com/docs) or contact support.
|
|
271
271
|
`;
|
|
272
272
|
|
|
273
273
|
return content;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# L4YERCAK3 Integration - Claude Code Configuration
|
|
2
|
+
|
|
3
|
+
## About This Integration
|
|
4
|
+
|
|
5
|
+
This project uses **L4YERCAK3** for backend services. The integration was set up using `@l4yercak3/cli`.
|
|
6
|
+
|
|
7
|
+
## L4YERCAK3 CLI
|
|
8
|
+
|
|
9
|
+
Install the CLI globally to manage your L4YERCAK3 integration:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @l4yercak3/cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Commands
|
|
16
|
+
|
|
17
|
+
| Command | Description |
|
|
18
|
+
|---------|-------------|
|
|
19
|
+
| `l4yercak3 login` | Authenticate with L4YERCAK3 platform |
|
|
20
|
+
| `l4yercak3 logout` | Log out from L4YERCAK3 |
|
|
21
|
+
| `l4yercak3 status` | Check authentication and session status |
|
|
22
|
+
| `l4yercak3 spread` | Re-run setup wizard to add/update features |
|
|
23
|
+
| `icing` | Shorthand alias for `l4yercak3` |
|
|
24
|
+
|
|
25
|
+
## Generated Files
|
|
26
|
+
|
|
27
|
+
The CLI generates these files based on selected features:
|
|
28
|
+
|
|
29
|
+
| File | Purpose |
|
|
30
|
+
|------|---------|
|
|
31
|
+
| `.env.local` | Environment variables (API keys, secrets) |
|
|
32
|
+
| `src/lib/api-client.js` | API client for L4YERCAK3 backend (or `.ts` for TypeScript) |
|
|
33
|
+
| `src/app/api/auth/[...nextauth]/route.js` | NextAuth.js configuration (if OAuth enabled) |
|
|
34
|
+
| `OAUTH_SETUP_GUIDE.md` | OAuth provider setup instructions (if OAuth enabled) |
|
|
35
|
+
|
|
36
|
+
## Environment Variables
|
|
37
|
+
|
|
38
|
+
Required variables in `.env.local`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
L4YERCAK3_API_KEY= # Your L4YERCAK3 API key
|
|
42
|
+
L4YERCAK3_BACKEND_URL= # Backend API URL
|
|
43
|
+
L4YERCAK3_ORG_ID= # Your organization ID
|
|
44
|
+
NEXTAUTH_SECRET= # NextAuth secret (if using OAuth)
|
|
45
|
+
NEXTAUTH_URL= # Your app URL (if using OAuth)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Using the API Client
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import L4YERCAK3Client from '@/lib/api-client';
|
|
52
|
+
|
|
53
|
+
// Create client instance (uses env defaults if no args)
|
|
54
|
+
const client = new L4YERCAK3Client();
|
|
55
|
+
|
|
56
|
+
// CRM Methods
|
|
57
|
+
const contacts = await client.getContacts();
|
|
58
|
+
const contact = await client.getContact('contact-id');
|
|
59
|
+
const newContact = await client.createContact({ email: 'user@example.com', name: 'John Doe' });
|
|
60
|
+
await client.updateContact('contact-id', { name: 'Jane Doe' });
|
|
61
|
+
await client.deleteContact('contact-id');
|
|
62
|
+
|
|
63
|
+
// Projects Methods
|
|
64
|
+
const projects = await client.getProjects();
|
|
65
|
+
const project = await client.createProject({ name: 'New Project' });
|
|
66
|
+
|
|
67
|
+
// Invoices Methods
|
|
68
|
+
const invoices = await client.getInvoices();
|
|
69
|
+
const invoice = await client.createInvoice({ amount: 100, contactId: 'contact-id' });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Re-running Setup
|
|
73
|
+
|
|
74
|
+
To add new features or update configuration:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
l4yercak3 spread
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This will detect existing setup and allow you to add additional features without overwriting current configuration.
|
|
81
|
+
|
|
82
|
+
## Support
|
|
83
|
+
|
|
84
|
+
- Documentation: https://docs.l4yercak3.com
|
|
85
|
+
- CLI GitHub: https://github.com/voundbrand/l4yercak3-cli
|
|
86
|
+
- npm: https://www.npmjs.com/package/@l4yercak3/cli
|
|
@@ -242,27 +242,47 @@ describe('BackendClient', () => {
|
|
|
242
242
|
});
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
+
describe('generateState', () => {
|
|
246
|
+
it('generates a 64-character hex string', () => {
|
|
247
|
+
const state = BackendClient.generateState();
|
|
248
|
+
|
|
249
|
+
expect(state).toMatch(/^[a-f0-9]{64}$/);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('generates unique values each time', () => {
|
|
253
|
+
const state1 = BackendClient.generateState();
|
|
254
|
+
const state2 = BackendClient.generateState();
|
|
255
|
+
|
|
256
|
+
expect(state1).not.toBe(state2);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
245
260
|
describe('getLoginUrl', () => {
|
|
246
|
-
it('returns provider selection URL when no provider specified', () => {
|
|
247
|
-
const
|
|
261
|
+
it('returns provider selection URL with state when no provider specified', () => {
|
|
262
|
+
const state = 'test-state-token';
|
|
263
|
+
const url = BackendClient.getLoginUrl(state);
|
|
248
264
|
|
|
249
265
|
expect(url).toContain('https://backend.test.com');
|
|
250
266
|
expect(url).toContain('/auth/cli-login');
|
|
267
|
+
expect(url).toContain('state=test-state-token');
|
|
251
268
|
expect(url).toContain('callback=');
|
|
252
269
|
});
|
|
253
270
|
|
|
254
271
|
it('returns direct OAuth URL when provider specified', () => {
|
|
255
|
-
const
|
|
272
|
+
const state = 'test-state-token';
|
|
273
|
+
const url = BackendClient.getLoginUrl(state, 'google');
|
|
256
274
|
|
|
257
275
|
expect(url).toContain('/api/auth/oauth-signup');
|
|
258
276
|
expect(url).toContain('provider=google');
|
|
259
277
|
expect(url).toContain('sessionType=cli');
|
|
278
|
+
expect(url).toContain('state=test-state-token');
|
|
260
279
|
});
|
|
261
280
|
|
|
262
281
|
it('includes encoded callback URL', () => {
|
|
263
|
-
const
|
|
282
|
+
const state = 'test-state-token';
|
|
283
|
+
const url = BackendClient.getLoginUrl(state, 'github');
|
|
264
284
|
|
|
265
|
-
expect(url).toContain(encodeURIComponent('http://localhost:
|
|
285
|
+
expect(url).toContain(encodeURIComponent('http://localhost:3000/callback'));
|
|
266
286
|
});
|
|
267
287
|
});
|
|
268
288
|
|
|
@@ -36,7 +36,7 @@ describe('ConfigManager', () => {
|
|
|
36
36
|
session: null,
|
|
37
37
|
organizations: [],
|
|
38
38
|
settings: {
|
|
39
|
-
backendUrl: 'https://
|
|
39
|
+
backendUrl: 'https://app.l4yercak3.com',
|
|
40
40
|
},
|
|
41
41
|
});
|
|
42
42
|
});
|
|
@@ -61,6 +61,8 @@ describe('ConfigManager', () => {
|
|
|
61
61
|
fs.existsSync.mockReturnValue(true);
|
|
62
62
|
fs.readFileSync.mockReturnValue('invalid json');
|
|
63
63
|
|
|
64
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
65
|
+
|
|
64
66
|
const config = ConfigManager.getConfig();
|
|
65
67
|
|
|
66
68
|
expect(config).toEqual({
|
|
@@ -68,6 +70,8 @@ describe('ConfigManager', () => {
|
|
|
68
70
|
organizations: [],
|
|
69
71
|
settings: {},
|
|
70
72
|
});
|
|
73
|
+
|
|
74
|
+
consoleSpy.mockRestore();
|
|
71
75
|
});
|
|
72
76
|
});
|
|
73
77
|
|
|
@@ -106,9 +110,13 @@ describe('ConfigManager', () => {
|
|
|
106
110
|
throw new Error('Write failed');
|
|
107
111
|
});
|
|
108
112
|
|
|
113
|
+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
114
|
+
|
|
109
115
|
const result = ConfigManager.saveConfig({ test: true });
|
|
110
116
|
|
|
111
117
|
expect(result).toBe(false);
|
|
118
|
+
|
|
119
|
+
consoleSpy.mockRestore();
|
|
112
120
|
});
|
|
113
121
|
});
|
|
114
122
|
|
|
@@ -196,7 +204,7 @@ describe('ConfigManager', () => {
|
|
|
196
204
|
|
|
197
205
|
const url = ConfigManager.getBackendUrl();
|
|
198
206
|
|
|
199
|
-
expect(url).toBe('https://
|
|
207
|
+
expect(url).toBe('https://app.l4yercak3.com');
|
|
200
208
|
});
|
|
201
209
|
|
|
202
210
|
it('returns configured URL from settings', () => {
|
|
@@ -266,7 +266,7 @@ describe('OAuthGuideGenerator', () => {
|
|
|
266
266
|
|
|
267
267
|
expect(guide).toContain('## Next Steps');
|
|
268
268
|
expect(guide).toContain('L4YERCAK3 Documentation');
|
|
269
|
-
expect(guide).toContain('
|
|
269
|
+
expect(guide).toContain('www.l4yercak3.com/docs');
|
|
270
270
|
});
|
|
271
271
|
});
|
|
272
272
|
});
|