baseguard 1.0.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.
- package/.eslintrc.json +25 -0
- package/.prettierrc +8 -0
- package/README.md +94 -0
- package/bin/base.js +494 -0
- package/dist/ai/fix-manager.d.ts +67 -0
- package/dist/ai/fix-manager.d.ts.map +1 -0
- package/dist/ai/fix-manager.js +326 -0
- package/dist/ai/fix-manager.js.map +1 -0
- package/dist/ai/gemini-analyzer.d.ts +116 -0
- package/dist/ai/gemini-analyzer.d.ts.map +1 -0
- package/dist/ai/gemini-analyzer.js +572 -0
- package/dist/ai/gemini-analyzer.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/jules-implementer.d.ts +115 -0
- package/dist/ai/jules-implementer.d.ts.map +1 -0
- package/dist/ai/jules-implementer.js +387 -0
- package/dist/ai/jules-implementer.js.map +1 -0
- package/dist/commands/automation.d.ts +5 -0
- package/dist/commands/automation.d.ts.map +1 -0
- package/dist/commands/automation.js +305 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/check.d.ts +9 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +113 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +324 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/fix.d.ts +9 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +207 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/core/api-key-manager.d.ts +83 -0
- package/dist/core/api-key-manager.d.ts.map +1 -0
- package/dist/core/api-key-manager.js +244 -0
- package/dist/core/api-key-manager.js.map +1 -0
- package/dist/core/baseguard.d.ts +46 -0
- package/dist/core/baseguard.d.ts.map +1 -0
- package/dist/core/baseguard.js +132 -0
- package/dist/core/baseguard.js.map +1 -0
- package/dist/core/baseline-checker.d.ts +63 -0
- package/dist/core/baseline-checker.d.ts.map +1 -0
- package/dist/core/baseline-checker.js +502 -0
- package/dist/core/baseline-checker.js.map +1 -0
- package/dist/core/cache-manager.d.ts +88 -0
- package/dist/core/cache-manager.d.ts.map +1 -0
- package/dist/core/cache-manager.js +213 -0
- package/dist/core/cache-manager.js.map +1 -0
- package/dist/core/configuration.d.ts +140 -0
- package/dist/core/configuration.d.ts.map +1 -0
- package/dist/core/configuration.js +474 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/directory-filter.d.ts +90 -0
- package/dist/core/directory-filter.d.ts.map +1 -0
- package/dist/core/directory-filter.js +319 -0
- package/dist/core/directory-filter.js.map +1 -0
- package/dist/core/error-handler.d.ts +110 -0
- package/dist/core/error-handler.d.ts.map +1 -0
- package/dist/core/error-handler.js +392 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/core/file-processor.d.ts +80 -0
- package/dist/core/file-processor.d.ts.map +1 -0
- package/dist/core/file-processor.js +259 -0
- package/dist/core/file-processor.js.map +1 -0
- package/dist/core/gitignore-manager.d.ts +44 -0
- package/dist/core/gitignore-manager.d.ts.map +1 -0
- package/dist/core/gitignore-manager.js +147 -0
- package/dist/core/gitignore-manager.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lazy-loader.d.ts +68 -0
- package/dist/core/lazy-loader.d.ts.map +1 -0
- package/dist/core/lazy-loader.js +260 -0
- package/dist/core/lazy-loader.js.map +1 -0
- package/dist/core/memory-manager.d.ts +1 -0
- package/dist/core/memory-manager.d.ts.map +1 -0
- package/dist/core/memory-manager.js +2 -0
- package/dist/core/memory-manager.js.map +1 -0
- package/dist/core/startup-optimizer.d.ts +45 -0
- package/dist/core/startup-optimizer.d.ts.map +1 -0
- package/dist/core/startup-optimizer.js +140 -0
- package/dist/core/startup-optimizer.js.map +1 -0
- package/dist/git/automation-engine.d.ts +58 -0
- package/dist/git/automation-engine.d.ts.map +1 -0
- package/dist/git/automation-engine.js +318 -0
- package/dist/git/automation-engine.js.map +1 -0
- package/dist/git/github-manager.d.ts +71 -0
- package/dist/git/github-manager.d.ts.map +1 -0
- package/dist/git/github-manager.js +226 -0
- package/dist/git/github-manager.js.map +1 -0
- package/dist/git/hook-manager.d.ts +43 -0
- package/dist/git/hook-manager.d.ts.map +1 -0
- package/dist/git/hook-manager.js +191 -0
- package/dist/git/hook-manager.js.map +1 -0
- package/dist/git/index.d.ts +4 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +5 -0
- package/dist/git/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/feature-validator.d.ts +60 -0
- package/dist/parsers/feature-validator.d.ts.map +1 -0
- package/dist/parsers/feature-validator.js +483 -0
- package/dist/parsers/feature-validator.js.map +1 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +9 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/parser-manager.d.ts +103 -0
- package/dist/parsers/parser-manager.d.ts.map +1 -0
- package/dist/parsers/parser-manager.js +321 -0
- package/dist/parsers/parser-manager.js.map +1 -0
- package/dist/parsers/parser.d.ts +23 -0
- package/dist/parsers/parser.d.ts.map +1 -0
- package/dist/parsers/parser.js +6 -0
- package/dist/parsers/parser.js.map +1 -0
- package/dist/parsers/react-parser.d.ts +22 -0
- package/dist/parsers/react-parser.d.ts.map +1 -0
- package/dist/parsers/react-parser.js +307 -0
- package/dist/parsers/react-parser.js.map +1 -0
- package/dist/parsers/svelte-parser.d.ts +33 -0
- package/dist/parsers/svelte-parser.d.ts.map +1 -0
- package/dist/parsers/svelte-parser.js +408 -0
- package/dist/parsers/svelte-parser.js.map +1 -0
- package/dist/parsers/vanilla-parser.d.ts +31 -0
- package/dist/parsers/vanilla-parser.d.ts.map +1 -0
- package/dist/parsers/vanilla-parser.js +590 -0
- package/dist/parsers/vanilla-parser.js.map +1 -0
- package/dist/parsers/vue-parser.d.ts +9 -0
- package/dist/parsers/vue-parser.d.ts.map +1 -0
- package/dist/parsers/vue-parser.js +16 -0
- package/dist/parsers/vue-parser.js.map +1 -0
- package/dist/terminal-header.d.ts +12 -0
- package/dist/terminal-header.js +45 -0
- package/dist/types/index.d.ts +83 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/components.d.ts +133 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js +482 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/help.d.ts +11 -0
- package/dist/ui/help.d.ts.map +1 -0
- package/dist/ui/help.js +161 -0
- package/dist/ui/help.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/prompts.d.ts +63 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +611 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/terminal-header.d.ts +13 -0
- package/dist/ui/terminal-header.d.ts.map +1 -0
- package/dist/ui/terminal-header.js +46 -0
- package/dist/ui/terminal-header.js.map +1 -0
- package/package.json +80 -0
- package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
- package/src/ai/fix-manager.ts +362 -0
- package/src/ai/gemini-analyzer.ts +671 -0
- package/src/ai/index.ts +4 -0
- package/src/ai/jules-implementer.ts +459 -0
- package/src/commands/automation.ts +344 -0
- package/src/commands/check.ts +299 -0
- package/src/commands/config.ts +365 -0
- package/src/commands/fix.ts +234 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/init.ts +142 -0
- package/src/commands/status.ts +0 -0
- package/src/core/api-key-manager.ts +298 -0
- package/src/core/baseguard.ts +742 -0
- package/src/core/baseline-checker.ts +563 -0
- package/src/core/cache-manager.ts +270 -0
- package/src/core/configuration-recovery.ts +676 -0
- package/src/core/configuration.ts +559 -0
- package/src/core/debug-logger.ts +590 -0
- package/src/core/directory-filter.ts +421 -0
- package/src/core/error-handler.ts +517 -0
- package/src/core/file-processor.ts +331 -0
- package/src/core/gitignore-manager.ts +169 -0
- package/src/core/graceful-degradation-manager.ts +596 -0
- package/src/core/index.ts +13 -0
- package/src/core/lazy-loader.ts +307 -0
- package/src/core/logger.ts +0 -0
- package/src/core/memory-manager.ts +294 -0
- package/src/core/startup-optimizer.ts +173 -0
- package/src/core/system-error-handler.ts +746 -0
- package/src/git/automation-engine.ts +361 -0
- package/src/git/github-manager.ts +260 -0
- package/src/git/hook-manager.ts +210 -0
- package/src/git/index.ts +4 -0
- package/src/index.ts +8 -0
- package/src/parsers/feature-validator.ts +559 -0
- package/src/parsers/index.ts +8 -0
- package/src/parsers/parser-manager.ts +419 -0
- package/src/parsers/parser.ts +26 -0
- package/src/parsers/react-parser-optimized.ts +161 -0
- package/src/parsers/react-parser.ts +359 -0
- package/src/parsers/svelte-parser.ts +506 -0
- package/src/parsers/vanilla-parser.ts +682 -0
- package/src/parsers/vue-parser.ts +472 -0
- package/src/types/index.ts +92 -0
- package/src/ui/components.ts +567 -0
- package/src/ui/help.ts +193 -0
- package/src/ui/index.ts +4 -0
- package/src/ui/prompts.ts +688 -0
- package/src/ui/terminal-header.ts +59 -0
- package/test-config-commands.js +56 -0
- package/test-header-simple.js +33 -0
- package/test-terminal-header.js +12 -0
- package/test-ui.js +29 -0
- package/tests/e2e/baseguard.e2e.test.ts +516 -0
- package/tests/e2e/cross-platform.e2e.test.ts +420 -0
- package/tests/e2e/git-integration.e2e.test.ts +487 -0
- package/tests/fixtures/react-project/package.json +14 -0
- package/tests/fixtures/react-project/src/App.css +76 -0
- package/tests/fixtures/react-project/src/App.tsx +77 -0
- package/tests/fixtures/svelte-project/package.json +11 -0
- package/tests/fixtures/svelte-project/src/App.svelte +369 -0
- package/tests/fixtures/vanilla-project/index.html +76 -0
- package/tests/fixtures/vanilla-project/script.js +331 -0
- package/tests/fixtures/vanilla-project/styles.css +359 -0
- package/tests/fixtures/vue-project/package.json +12 -0
- package/tests/fixtures/vue-project/src/App.vue +216 -0
- package/tsconfig.json +36 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { execSync, spawn } from 'child_process';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
const TEST_TIMEOUT = 45000; // 45 seconds for git operations
|
|
11
|
+
const TEMP_DIR = path.join(__dirname, '../temp-git');
|
|
12
|
+
|
|
13
|
+
// Helper function to run git commands
|
|
14
|
+
function runGit(args: string[], cwd: string): string {
|
|
15
|
+
try {
|
|
16
|
+
return execSync(`git ${args.join(' ')}`, {
|
|
17
|
+
cwd,
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: 'pipe'
|
|
20
|
+
});
|
|
21
|
+
} catch (error: any) {
|
|
22
|
+
throw new Error(`Git command failed: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Helper function to run BaseGuard commands
|
|
27
|
+
async function runBaseGuard(args: string[], cwd: string): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const basePath = path.join(__dirname, '../../bin/base.js');
|
|
30
|
+
const child = spawn('node', [basePath, ...args], {
|
|
31
|
+
cwd,
|
|
32
|
+
stdio: 'pipe',
|
|
33
|
+
env: { ...process.env, NODE_ENV: 'test' }
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
let stdout = '';
|
|
37
|
+
let stderr = '';
|
|
38
|
+
|
|
39
|
+
child.stdout?.on('data', (data) => {
|
|
40
|
+
stdout += data.toString();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
child.stderr?.on('data', (data) => {
|
|
44
|
+
stderr += data.toString();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
child.on('close', (code) => {
|
|
48
|
+
resolve({
|
|
49
|
+
stdout,
|
|
50
|
+
stderr,
|
|
51
|
+
exitCode: code || 0
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
child.kill();
|
|
57
|
+
resolve({ stdout, stderr, exitCode: 1 });
|
|
58
|
+
}, TEST_TIMEOUT);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Helper function to setup git repository
|
|
63
|
+
async function setupGitRepo(repoPath: string): Promise<void> {
|
|
64
|
+
await fs.mkdir(repoPath, { recursive: true });
|
|
65
|
+
|
|
66
|
+
// Initialize git repo
|
|
67
|
+
runGit(['init'], repoPath);
|
|
68
|
+
runGit(['config', 'user.name', 'Test User'], repoPath);
|
|
69
|
+
runGit(['config', 'user.email', 'test@example.com'], repoPath);
|
|
70
|
+
|
|
71
|
+
// Create initial commit
|
|
72
|
+
await fs.writeFile(path.join(repoPath, 'README.md'), '# Test Project\n');
|
|
73
|
+
runGit(['add', 'README.md'], repoPath);
|
|
74
|
+
runGit(['commit', '-m', 'Initial commit'], repoPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Helper function to create test files with violations
|
|
78
|
+
async function createTestFiles(repoPath: string): Promise<void> {
|
|
79
|
+
// Create a file with modern features that will trigger violations
|
|
80
|
+
const jsContent = `
|
|
81
|
+
// Modern JavaScript features
|
|
82
|
+
const data = { test: 'value' };
|
|
83
|
+
const cloned = structuredClone(data);
|
|
84
|
+
const observer = new ResizeObserver(() => {});
|
|
85
|
+
|
|
86
|
+
// Dialog API usage
|
|
87
|
+
const dialog = document.querySelector('dialog');
|
|
88
|
+
if (dialog) {
|
|
89
|
+
dialog.showModal();
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
const cssContent = `
|
|
94
|
+
.container {
|
|
95
|
+
container-type: inline-size;
|
|
96
|
+
aspect-ratio: 16/9;
|
|
97
|
+
backdrop-filter: blur(10px);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@container (min-width: 400px) {
|
|
101
|
+
.container {
|
|
102
|
+
font-size: 1.2rem;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.modern-selector:has(dialog[open]) {
|
|
107
|
+
background: color-mix(in srgb, red 50%, blue);
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
await fs.writeFile(path.join(repoPath, 'test.js'), jsContent);
|
|
112
|
+
await fs.writeFile(path.join(repoPath, 'test.css'), cssContent);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
describe('Git Integration End-to-End Tests', () => {
|
|
116
|
+
beforeEach(async () => {
|
|
117
|
+
try {
|
|
118
|
+
await fs.rm(TEMP_DIR, { recursive: true, force: true });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// Directory might not exist
|
|
121
|
+
}
|
|
122
|
+
await fs.mkdir(TEMP_DIR, { recursive: true });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
afterEach(async () => {
|
|
126
|
+
try {
|
|
127
|
+
await fs.rm(TEMP_DIR, { recursive: true, force: true });
|
|
128
|
+
} catch (error) {
|
|
129
|
+
// Ignore cleanup errors
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('Git Hook Installation', () => {
|
|
134
|
+
it('should install pre-commit hooks successfully', async () => {
|
|
135
|
+
const repoPath = path.join(TEMP_DIR, 'hook-install-test');
|
|
136
|
+
await setupGitRepo(repoPath);
|
|
137
|
+
|
|
138
|
+
// Initialize BaseGuard with automation
|
|
139
|
+
const initResult = await runBaseGuard(['init'], repoPath);
|
|
140
|
+
expect(initResult.exitCode).toBe(0);
|
|
141
|
+
|
|
142
|
+
// Install git hooks
|
|
143
|
+
const hookResult = await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
144
|
+
expect(hookResult.exitCode).toBe(0);
|
|
145
|
+
|
|
146
|
+
// Check if hook files were created
|
|
147
|
+
const hookPath = path.join(repoPath, '.husky/pre-commit');
|
|
148
|
+
const hookExists = await fs.access(hookPath).then(() => true).catch(() => false);
|
|
149
|
+
expect(hookExists).toBe(true);
|
|
150
|
+
|
|
151
|
+
if (hookExists) {
|
|
152
|
+
const hookContent = await fs.readFile(hookPath, 'utf8');
|
|
153
|
+
expect(hookContent).toContain('base automation run');
|
|
154
|
+
}
|
|
155
|
+
}, TEST_TIMEOUT);
|
|
156
|
+
|
|
157
|
+
it('should install pre-push hooks successfully', async () => {
|
|
158
|
+
const repoPath = path.join(TEMP_DIR, 'pre-push-test');
|
|
159
|
+
await setupGitRepo(repoPath);
|
|
160
|
+
|
|
161
|
+
await runBaseGuard(['init'], repoPath);
|
|
162
|
+
|
|
163
|
+
const hookResult = await runBaseGuard(['automation', 'enable', '--trigger', 'pre-push'], repoPath);
|
|
164
|
+
expect(hookResult.exitCode).toBe(0);
|
|
165
|
+
|
|
166
|
+
const hookPath = path.join(repoPath, '.husky/pre-push');
|
|
167
|
+
const hookExists = await fs.access(hookPath).then(() => true).catch(() => false);
|
|
168
|
+
expect(hookExists).toBe(true);
|
|
169
|
+
}, TEST_TIMEOUT);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('Pre-commit Hook Behavior', () => {
|
|
173
|
+
it('should block commits when violations are found', async () => {
|
|
174
|
+
const repoPath = path.join(TEMP_DIR, 'block-commit-test');
|
|
175
|
+
await setupGitRepo(repoPath);
|
|
176
|
+
|
|
177
|
+
// Initialize BaseGuard and enable automation
|
|
178
|
+
await runBaseGuard(['init'], repoPath);
|
|
179
|
+
|
|
180
|
+
// Create configuration that will find violations
|
|
181
|
+
const config = {
|
|
182
|
+
version: '1.0.0',
|
|
183
|
+
targets: [{ browser: 'safari', minVersion: '15' }],
|
|
184
|
+
automation: {
|
|
185
|
+
enabled: true,
|
|
186
|
+
trigger: 'pre-commit',
|
|
187
|
+
autoAnalyze: false,
|
|
188
|
+
autoFix: false,
|
|
189
|
+
blockCommit: true
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
await fs.writeFile(path.join(repoPath, '.baseguardrc.json'), JSON.stringify(config, null, 2));
|
|
193
|
+
|
|
194
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
195
|
+
|
|
196
|
+
// Create files with violations
|
|
197
|
+
await createTestFiles(repoPath);
|
|
198
|
+
runGit(['add', '.'], repoPath);
|
|
199
|
+
|
|
200
|
+
// Try to commit - should fail
|
|
201
|
+
try {
|
|
202
|
+
runGit(['commit', '-m', 'Add files with violations'], repoPath);
|
|
203
|
+
expect.fail('Commit should have been blocked');
|
|
204
|
+
} catch (error: any) {
|
|
205
|
+
expect(error.message).toContain('Git command failed');
|
|
206
|
+
}
|
|
207
|
+
}, TEST_TIMEOUT);
|
|
208
|
+
|
|
209
|
+
it('should allow commits when no violations are found', async () => {
|
|
210
|
+
const repoPath = path.join(TEMP_DIR, 'allow-commit-test');
|
|
211
|
+
await setupGitRepo(repoPath);
|
|
212
|
+
|
|
213
|
+
await runBaseGuard(['init'], repoPath);
|
|
214
|
+
|
|
215
|
+
// Create configuration targeting very old browsers (everything should pass)
|
|
216
|
+
const config = {
|
|
217
|
+
version: '1.0.0',
|
|
218
|
+
targets: [{ browser: 'chrome', minVersion: '120' }], // Very new browser
|
|
219
|
+
automation: {
|
|
220
|
+
enabled: true,
|
|
221
|
+
trigger: 'pre-commit',
|
|
222
|
+
blockCommit: true
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
await fs.writeFile(path.join(repoPath, '.baseguardrc.json'), JSON.stringify(config, null, 2));
|
|
226
|
+
|
|
227
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
228
|
+
|
|
229
|
+
// Create files without violations (for very new browsers)
|
|
230
|
+
await fs.writeFile(path.join(repoPath, 'safe.js'), 'console.log("Hello World");');
|
|
231
|
+
await fs.writeFile(path.join(repoPath, 'safe.css'), '.safe { color: red; }');
|
|
232
|
+
|
|
233
|
+
runGit(['add', '.'], repoPath);
|
|
234
|
+
|
|
235
|
+
// Commit should succeed
|
|
236
|
+
const commitOutput = runGit(['commit', '-m', 'Add safe files'], repoPath);
|
|
237
|
+
expect(commitOutput).toContain('Add safe files');
|
|
238
|
+
}, TEST_TIMEOUT);
|
|
239
|
+
|
|
240
|
+
it('should respect --no-verify flag', async () => {
|
|
241
|
+
const repoPath = path.join(TEMP_DIR, 'no-verify-test');
|
|
242
|
+
await setupGitRepo(repoPath);
|
|
243
|
+
|
|
244
|
+
await runBaseGuard(['init'], repoPath);
|
|
245
|
+
|
|
246
|
+
const config = {
|
|
247
|
+
version: '1.0.0',
|
|
248
|
+
targets: [{ browser: 'safari', minVersion: '15' }],
|
|
249
|
+
automation: {
|
|
250
|
+
enabled: true,
|
|
251
|
+
trigger: 'pre-commit',
|
|
252
|
+
blockCommit: true
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
await fs.writeFile(path.join(repoPath, '.baseguardrc.json'), JSON.stringify(config, null, 2));
|
|
256
|
+
|
|
257
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
258
|
+
|
|
259
|
+
// Create files with violations
|
|
260
|
+
await createTestFiles(repoPath);
|
|
261
|
+
runGit(['add', '.'], repoPath);
|
|
262
|
+
|
|
263
|
+
// Commit with --no-verify should succeed
|
|
264
|
+
const commitOutput = runGit(['commit', '--no-verify', '-m', 'Bypass BaseGuard'], repoPath);
|
|
265
|
+
expect(commitOutput).toContain('Bypass BaseGuard');
|
|
266
|
+
}, TEST_TIMEOUT);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('Auto-fix Mode', () => {
|
|
270
|
+
it('should automatically fix violations and include in commit', async () => {
|
|
271
|
+
const repoPath = path.join(TEMP_DIR, 'auto-fix-test');
|
|
272
|
+
await setupGitRepo(repoPath);
|
|
273
|
+
|
|
274
|
+
await runBaseGuard(['init'], repoPath);
|
|
275
|
+
|
|
276
|
+
// Enable auto-fix mode (would require API keys in real scenario)
|
|
277
|
+
const config = {
|
|
278
|
+
version: '1.0.0',
|
|
279
|
+
targets: [{ browser: 'safari', minVersion: '15' }],
|
|
280
|
+
apiKeys: {
|
|
281
|
+
jules: 'test-key',
|
|
282
|
+
gemini: 'test-key'
|
|
283
|
+
},
|
|
284
|
+
automation: {
|
|
285
|
+
enabled: true,
|
|
286
|
+
trigger: 'pre-commit',
|
|
287
|
+
autoAnalyze: true,
|
|
288
|
+
autoFix: true,
|
|
289
|
+
blockCommit: false
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
await fs.writeFile(path.join(repoPath, '.baseguardrc.json'), JSON.stringify(config, null, 2));
|
|
293
|
+
|
|
294
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
295
|
+
|
|
296
|
+
// Create files with violations
|
|
297
|
+
await createTestFiles(repoPath);
|
|
298
|
+
runGit(['add', '.'], repoPath);
|
|
299
|
+
|
|
300
|
+
// Note: This test would require mock API responses in a real scenario
|
|
301
|
+
// For now, we test that the automation runs without crashing
|
|
302
|
+
try {
|
|
303
|
+
runGit(['commit', '-m', 'Test auto-fix'], repoPath);
|
|
304
|
+
} catch (error: any) {
|
|
305
|
+
// Expected to fail due to missing API keys, but should not crash
|
|
306
|
+
expect(error.message).toContain('API') || expect(error.message).toContain('key');
|
|
307
|
+
}
|
|
308
|
+
}, TEST_TIMEOUT);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('Pre-push Hook Behavior', () => {
|
|
312
|
+
it('should check violations before push', async () => {
|
|
313
|
+
const repoPath = path.join(TEMP_DIR, 'pre-push-test');
|
|
314
|
+
await setupGitRepo(repoPath);
|
|
315
|
+
|
|
316
|
+
await runBaseGuard(['init'], repoPath);
|
|
317
|
+
|
|
318
|
+
const config = {
|
|
319
|
+
version: '1.0.0',
|
|
320
|
+
targets: [{ browser: 'safari', minVersion: '15' }],
|
|
321
|
+
automation: {
|
|
322
|
+
enabled: true,
|
|
323
|
+
trigger: 'pre-push',
|
|
324
|
+
blockCommit: true
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
await fs.writeFile(path.join(repoPath, '.baseguardrc.json'), JSON.stringify(config, null, 2));
|
|
328
|
+
|
|
329
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-push'], repoPath);
|
|
330
|
+
|
|
331
|
+
// Create and commit files with violations (bypassing pre-commit)
|
|
332
|
+
await createTestFiles(repoPath);
|
|
333
|
+
runGit(['add', '.'], repoPath);
|
|
334
|
+
runGit(['commit', '--no-verify', '-m', 'Add files with violations'], repoPath);
|
|
335
|
+
|
|
336
|
+
// Create a remote repository simulation
|
|
337
|
+
const remoteRepo = path.join(TEMP_DIR, 'remote-repo');
|
|
338
|
+
await fs.mkdir(remoteRepo, { recursive: true });
|
|
339
|
+
runGit(['init', '--bare'], remoteRepo);
|
|
340
|
+
runGit(['remote', 'add', 'origin', remoteRepo], repoPath);
|
|
341
|
+
|
|
342
|
+
// Try to push - should fail due to violations
|
|
343
|
+
try {
|
|
344
|
+
runGit(['push', 'origin', 'main'], repoPath);
|
|
345
|
+
expect.fail('Push should have been blocked');
|
|
346
|
+
} catch (error: any) {
|
|
347
|
+
expect(error.message).toContain('Git command failed');
|
|
348
|
+
}
|
|
349
|
+
}, TEST_TIMEOUT);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
describe('Automation Configuration', () => {
|
|
353
|
+
it('should enable and disable automation correctly', async () => {
|
|
354
|
+
const repoPath = path.join(TEMP_DIR, 'automation-config-test');
|
|
355
|
+
await setupGitRepo(repoPath);
|
|
356
|
+
|
|
357
|
+
await runBaseGuard(['init'], repoPath);
|
|
358
|
+
|
|
359
|
+
// Enable automation
|
|
360
|
+
const enableResult = await runBaseGuard(['automation', 'enable'], repoPath);
|
|
361
|
+
expect(enableResult.exitCode).toBe(0);
|
|
362
|
+
|
|
363
|
+
// Check configuration
|
|
364
|
+
const configPath = path.join(repoPath, '.baseguardrc.json');
|
|
365
|
+
const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
|
|
366
|
+
expect(config.automation.enabled).toBe(true);
|
|
367
|
+
|
|
368
|
+
// Disable automation
|
|
369
|
+
const disableResult = await runBaseGuard(['automation', 'disable'], repoPath);
|
|
370
|
+
expect(disableResult.exitCode).toBe(0);
|
|
371
|
+
|
|
372
|
+
// Check configuration updated
|
|
373
|
+
const updatedConfig = JSON.parse(await fs.readFile(configPath, 'utf8'));
|
|
374
|
+
expect(updatedConfig.automation.enabled).toBe(false);
|
|
375
|
+
}, TEST_TIMEOUT);
|
|
376
|
+
|
|
377
|
+
it('should handle different trigger configurations', async () => {
|
|
378
|
+
const repoPath = path.join(TEMP_DIR, 'trigger-config-test');
|
|
379
|
+
await setupGitRepo(repoPath);
|
|
380
|
+
|
|
381
|
+
await runBaseGuard(['init'], repoPath);
|
|
382
|
+
|
|
383
|
+
// Test pre-commit trigger
|
|
384
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
385
|
+
let config = JSON.parse(await fs.readFile(path.join(repoPath, '.baseguardrc.json'), 'utf8'));
|
|
386
|
+
expect(config.automation.trigger).toBe('pre-commit');
|
|
387
|
+
|
|
388
|
+
// Test pre-push trigger
|
|
389
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-push'], repoPath);
|
|
390
|
+
config = JSON.parse(await fs.readFile(path.join(repoPath, '.baseguardrc.json'), 'utf8'));
|
|
391
|
+
expect(config.automation.trigger).toBe('pre-push');
|
|
392
|
+
}, TEST_TIMEOUT);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('Incremental Scanning', () => {
|
|
396
|
+
it('should only scan changed files in git workflow', async () => {
|
|
397
|
+
const repoPath = path.join(TEMP_DIR, 'incremental-test');
|
|
398
|
+
await setupGitRepo(repoPath);
|
|
399
|
+
|
|
400
|
+
await runBaseGuard(['init'], repoPath);
|
|
401
|
+
|
|
402
|
+
// Create multiple files
|
|
403
|
+
await fs.writeFile(path.join(repoPath, 'file1.js'), 'console.log("file1");');
|
|
404
|
+
await fs.writeFile(path.join(repoPath, 'file2.js'), 'console.log("file2");');
|
|
405
|
+
await createTestFiles(repoPath); // Creates files with violations
|
|
406
|
+
|
|
407
|
+
runGit(['add', 'file1.js', 'file2.js'], repoPath);
|
|
408
|
+
runGit(['commit', '-m', 'Add safe files'], repoPath);
|
|
409
|
+
|
|
410
|
+
// Now stage only the files with violations
|
|
411
|
+
runGit(['add', 'test.js', 'test.css'], repoPath);
|
|
412
|
+
|
|
413
|
+
const config = {
|
|
414
|
+
version: '1.0.0',
|
|
415
|
+
targets: [{ browser: 'safari', minVersion: '15' }],
|
|
416
|
+
automation: {
|
|
417
|
+
enabled: true,
|
|
418
|
+
trigger: 'pre-commit',
|
|
419
|
+
blockCommit: true
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
await fs.writeFile(path.join(repoPath, '.baseguardrc.json'), JSON.stringify(config, null, 2));
|
|
423
|
+
|
|
424
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
425
|
+
|
|
426
|
+
// The automation should only check staged files
|
|
427
|
+
try {
|
|
428
|
+
runGit(['commit', '-m', 'Add files with violations'], repoPath);
|
|
429
|
+
expect.fail('Commit should have been blocked');
|
|
430
|
+
} catch (error: any) {
|
|
431
|
+
expect(error.message).toContain('Git command failed');
|
|
432
|
+
}
|
|
433
|
+
}, TEST_TIMEOUT);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
describe('Error Recovery', () => {
|
|
437
|
+
it('should handle corrupted git hooks gracefully', async () => {
|
|
438
|
+
const repoPath = path.join(TEMP_DIR, 'corrupted-hook-test');
|
|
439
|
+
await setupGitRepo(repoPath);
|
|
440
|
+
|
|
441
|
+
await runBaseGuard(['init'], repoPath);
|
|
442
|
+
await runBaseGuard(['automation', 'enable', '--trigger', 'pre-commit'], repoPath);
|
|
443
|
+
|
|
444
|
+
// Corrupt the hook file
|
|
445
|
+
const hookPath = path.join(repoPath, '.husky/pre-commit');
|
|
446
|
+
await fs.writeFile(hookPath, 'invalid shell script content {{{');
|
|
447
|
+
|
|
448
|
+
// Create and stage files
|
|
449
|
+
await fs.writeFile(path.join(repoPath, 'test.js'), 'console.log("test");');
|
|
450
|
+
runGit(['add', '.'], repoPath);
|
|
451
|
+
|
|
452
|
+
// Commit should either succeed (hook fails) or provide clear error
|
|
453
|
+
try {
|
|
454
|
+
const result = runGit(['commit', '-m', 'Test corrupted hook'], repoPath);
|
|
455
|
+
// If it succeeds, that's also acceptable behavior
|
|
456
|
+
expect(result).toContain('Test corrupted hook');
|
|
457
|
+
} catch (error: any) {
|
|
458
|
+
// If it fails, error should be about the hook, not BaseGuard crashing
|
|
459
|
+
expect(error.message).toContain('hook') || expect(error.message).toContain('script');
|
|
460
|
+
}
|
|
461
|
+
}, TEST_TIMEOUT);
|
|
462
|
+
|
|
463
|
+
it('should handle missing BaseGuard configuration in git workflow', async () => {
|
|
464
|
+
const repoPath = path.join(TEMP_DIR, 'missing-config-test');
|
|
465
|
+
await setupGitRepo(repoPath);
|
|
466
|
+
|
|
467
|
+
// Install hooks without proper BaseGuard initialization
|
|
468
|
+
await fs.mkdir(path.join(repoPath, '.husky'), { recursive: true });
|
|
469
|
+
await fs.writeFile(
|
|
470
|
+
path.join(repoPath, '.husky/pre-commit'),
|
|
471
|
+
'#!/bin/sh\n. "$(dirname "$0")/_/husky.sh"\nbase automation run --trigger pre-commit'
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
// Create and stage files
|
|
475
|
+
await fs.writeFile(path.join(repoPath, 'test.js'), 'console.log("test");');
|
|
476
|
+
runGit(['add', '.'], repoPath);
|
|
477
|
+
|
|
478
|
+
// Should handle missing configuration gracefully
|
|
479
|
+
try {
|
|
480
|
+
runGit(['commit', '-m', 'Test missing config'], repoPath);
|
|
481
|
+
} catch (error: any) {
|
|
482
|
+
// Should provide helpful error about missing configuration
|
|
483
|
+
expect(error.message).toContain('configuration') || expect(error.message).toContain('init');
|
|
484
|
+
}
|
|
485
|
+
}, TEST_TIMEOUT);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-test-project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"react": "^18.2.0",
|
|
7
|
+
"react-dom": "^18.2.0"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@types/react": "^18.2.0",
|
|
11
|
+
"@types/react-dom": "^18.2.0",
|
|
12
|
+
"typescript": "^5.0.0"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* Modern CSS features that may have compatibility issues */
|
|
2
|
+
|
|
3
|
+
.app {
|
|
4
|
+
/* Container queries - baseline newly available */
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
container-name: app-container;
|
|
7
|
+
|
|
8
|
+
/* Modern CSS properties */
|
|
9
|
+
aspect-ratio: 16/9;
|
|
10
|
+
accent-color: #007bff;
|
|
11
|
+
color-scheme: light dark;
|
|
12
|
+
|
|
13
|
+
/* Modern CSS functions */
|
|
14
|
+
background: color-mix(in srgb, red 50%, blue);
|
|
15
|
+
|
|
16
|
+
/* Backdrop filter - baseline newly available */
|
|
17
|
+
backdrop-filter: blur(10px);
|
|
18
|
+
|
|
19
|
+
/* CSS Grid gap shorthand */
|
|
20
|
+
display: grid;
|
|
21
|
+
gap: 1rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Container query usage */
|
|
25
|
+
@container app-container (min-width: 400px) {
|
|
26
|
+
.container-query-test {
|
|
27
|
+
font-size: 1.2rem;
|
|
28
|
+
color: green;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Modern CSS selectors */
|
|
33
|
+
.app:has(dialog[open]) {
|
|
34
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* CSS nesting (very new feature) */
|
|
38
|
+
.app {
|
|
39
|
+
.container-query-test {
|
|
40
|
+
padding: 1rem;
|
|
41
|
+
|
|
42
|
+
&:hover {
|
|
43
|
+
background-color: #f0f0f0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Modern CSS at-rules */
|
|
49
|
+
@supports (container-type: inline-size) {
|
|
50
|
+
.app {
|
|
51
|
+
border: 2px solid green;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* CSS custom properties with modern functions */
|
|
56
|
+
:root {
|
|
57
|
+
--primary-color: oklch(0.7 0.15 200);
|
|
58
|
+
--secondary-color: color(display-p3 1 0 0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Modern pseudo-classes */
|
|
62
|
+
.app button:focus-visible {
|
|
63
|
+
outline: 2px solid var(--primary-color);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.app button:is(:hover, :focus) {
|
|
67
|
+
transform: scale(1.05);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Modern CSS units */
|
|
71
|
+
.container-query-test {
|
|
72
|
+
width: min(100%, 50ch);
|
|
73
|
+
height: max(200px, 20vh);
|
|
74
|
+
margin-block: 1rem;
|
|
75
|
+
padding-inline: 2rem;
|
|
76
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import './App.css';
|
|
3
|
+
|
|
4
|
+
// Component using modern web features that may have compatibility issues
|
|
5
|
+
const App: React.FC = () => {
|
|
6
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
7
|
+
const [resizeObserver, setResizeObserver] = useState<ResizeObserver | null>(null);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
// Modern JavaScript API - ResizeObserver (baseline newly available)
|
|
11
|
+
const observer = new ResizeObserver((entries) => {
|
|
12
|
+
console.log('Element resized:', entries);
|
|
13
|
+
});
|
|
14
|
+
setResizeObserver(observer);
|
|
15
|
+
|
|
16
|
+
// Modern JavaScript API - structuredClone (baseline newly available)
|
|
17
|
+
const data = { name: 'test', nested: { value: 42 } };
|
|
18
|
+
const cloned = structuredClone(data);
|
|
19
|
+
console.log('Cloned data:', cloned);
|
|
20
|
+
|
|
21
|
+
// Modern Array method - Array.at() (baseline newly available)
|
|
22
|
+
const items = [1, 2, 3, 4, 5];
|
|
23
|
+
const lastItem = items.at(-1);
|
|
24
|
+
console.log('Last item:', lastItem);
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
observer.disconnect();
|
|
28
|
+
};
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const openDialog = () => {
|
|
32
|
+
// Modern HTML API - HTMLDialogElement.showModal() (baseline newly available)
|
|
33
|
+
const dialog = document.querySelector('dialog');
|
|
34
|
+
if (dialog) {
|
|
35
|
+
dialog.showModal();
|
|
36
|
+
}
|
|
37
|
+
setDialogOpen(true);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const closeDialog = () => {
|
|
41
|
+
const dialog = document.querySelector('dialog');
|
|
42
|
+
if (dialog) {
|
|
43
|
+
dialog.close();
|
|
44
|
+
}
|
|
45
|
+
setDialogOpen(false);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Canvas API usage (widely supported but good test case)
|
|
49
|
+
const drawCanvas = () => {
|
|
50
|
+
const canvas = document.createElement('canvas');
|
|
51
|
+
const ctx = canvas.getContext('2d');
|
|
52
|
+
if (ctx) {
|
|
53
|
+
// Modern Canvas API - filter property (baseline newly available)
|
|
54
|
+
ctx.filter = 'blur(5px)';
|
|
55
|
+
ctx.fillRect(0, 0, 100, 100);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="app">
|
|
61
|
+
<h1>React Test App</h1>
|
|
62
|
+
<button onClick={openDialog}>Open Dialog</button>
|
|
63
|
+
<button onClick={drawCanvas}>Draw Canvas</button>
|
|
64
|
+
|
|
65
|
+
<dialog open={dialogOpen}>
|
|
66
|
+
<p>This is a modern dialog element</p>
|
|
67
|
+
<button onClick={closeDialog}>Close</button>
|
|
68
|
+
</dialog>
|
|
69
|
+
|
|
70
|
+
<div className="container-query-test">
|
|
71
|
+
<p>Container with modern CSS</p>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default App;
|