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.
Files changed (244) hide show
  1. package/.eslintrc.json +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +94 -0
  4. package/bin/base.js +494 -0
  5. package/dist/ai/fix-manager.d.ts +67 -0
  6. package/dist/ai/fix-manager.d.ts.map +1 -0
  7. package/dist/ai/fix-manager.js +326 -0
  8. package/dist/ai/fix-manager.js.map +1 -0
  9. package/dist/ai/gemini-analyzer.d.ts +116 -0
  10. package/dist/ai/gemini-analyzer.d.ts.map +1 -0
  11. package/dist/ai/gemini-analyzer.js +572 -0
  12. package/dist/ai/gemini-analyzer.js.map +1 -0
  13. package/dist/ai/index.d.ts +4 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +5 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/ai/jules-implementer.d.ts +115 -0
  18. package/dist/ai/jules-implementer.d.ts.map +1 -0
  19. package/dist/ai/jules-implementer.js +387 -0
  20. package/dist/ai/jules-implementer.js.map +1 -0
  21. package/dist/commands/automation.d.ts +5 -0
  22. package/dist/commands/automation.d.ts.map +1 -0
  23. package/dist/commands/automation.js +305 -0
  24. package/dist/commands/automation.js.map +1 -0
  25. package/dist/commands/check.d.ts +9 -0
  26. package/dist/commands/check.d.ts.map +1 -0
  27. package/dist/commands/check.js +113 -0
  28. package/dist/commands/check.js.map +1 -0
  29. package/dist/commands/config.d.ts +11 -0
  30. package/dist/commands/config.d.ts.map +1 -0
  31. package/dist/commands/config.js +324 -0
  32. package/dist/commands/config.js.map +1 -0
  33. package/dist/commands/fix.d.ts +9 -0
  34. package/dist/commands/fix.d.ts.map +1 -0
  35. package/dist/commands/fix.js +207 -0
  36. package/dist/commands/fix.js.map +1 -0
  37. package/dist/commands/index.d.ts +6 -0
  38. package/dist/commands/index.d.ts.map +1 -0
  39. package/dist/commands/index.js +7 -0
  40. package/dist/commands/index.js.map +1 -0
  41. package/dist/commands/init.d.ts +9 -0
  42. package/dist/commands/init.d.ts.map +1 -0
  43. package/dist/commands/init.js +125 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/core/api-key-manager.d.ts +83 -0
  46. package/dist/core/api-key-manager.d.ts.map +1 -0
  47. package/dist/core/api-key-manager.js +244 -0
  48. package/dist/core/api-key-manager.js.map +1 -0
  49. package/dist/core/baseguard.d.ts +46 -0
  50. package/dist/core/baseguard.d.ts.map +1 -0
  51. package/dist/core/baseguard.js +132 -0
  52. package/dist/core/baseguard.js.map +1 -0
  53. package/dist/core/baseline-checker.d.ts +63 -0
  54. package/dist/core/baseline-checker.d.ts.map +1 -0
  55. package/dist/core/baseline-checker.js +502 -0
  56. package/dist/core/baseline-checker.js.map +1 -0
  57. package/dist/core/cache-manager.d.ts +88 -0
  58. package/dist/core/cache-manager.d.ts.map +1 -0
  59. package/dist/core/cache-manager.js +213 -0
  60. package/dist/core/cache-manager.js.map +1 -0
  61. package/dist/core/configuration.d.ts +140 -0
  62. package/dist/core/configuration.d.ts.map +1 -0
  63. package/dist/core/configuration.js +474 -0
  64. package/dist/core/configuration.js.map +1 -0
  65. package/dist/core/directory-filter.d.ts +90 -0
  66. package/dist/core/directory-filter.d.ts.map +1 -0
  67. package/dist/core/directory-filter.js +319 -0
  68. package/dist/core/directory-filter.js.map +1 -0
  69. package/dist/core/error-handler.d.ts +110 -0
  70. package/dist/core/error-handler.d.ts.map +1 -0
  71. package/dist/core/error-handler.js +392 -0
  72. package/dist/core/error-handler.js.map +1 -0
  73. package/dist/core/file-processor.d.ts +80 -0
  74. package/dist/core/file-processor.d.ts.map +1 -0
  75. package/dist/core/file-processor.js +259 -0
  76. package/dist/core/file-processor.js.map +1 -0
  77. package/dist/core/gitignore-manager.d.ts +44 -0
  78. package/dist/core/gitignore-manager.d.ts.map +1 -0
  79. package/dist/core/gitignore-manager.js +147 -0
  80. package/dist/core/gitignore-manager.js.map +1 -0
  81. package/dist/core/index.d.ts +13 -0
  82. package/dist/core/index.d.ts.map +1 -0
  83. package/dist/core/index.js +13 -0
  84. package/dist/core/index.js.map +1 -0
  85. package/dist/core/lazy-loader.d.ts +68 -0
  86. package/dist/core/lazy-loader.d.ts.map +1 -0
  87. package/dist/core/lazy-loader.js +260 -0
  88. package/dist/core/lazy-loader.js.map +1 -0
  89. package/dist/core/memory-manager.d.ts +1 -0
  90. package/dist/core/memory-manager.d.ts.map +1 -0
  91. package/dist/core/memory-manager.js +2 -0
  92. package/dist/core/memory-manager.js.map +1 -0
  93. package/dist/core/startup-optimizer.d.ts +45 -0
  94. package/dist/core/startup-optimizer.d.ts.map +1 -0
  95. package/dist/core/startup-optimizer.js +140 -0
  96. package/dist/core/startup-optimizer.js.map +1 -0
  97. package/dist/git/automation-engine.d.ts +58 -0
  98. package/dist/git/automation-engine.d.ts.map +1 -0
  99. package/dist/git/automation-engine.js +318 -0
  100. package/dist/git/automation-engine.js.map +1 -0
  101. package/dist/git/github-manager.d.ts +71 -0
  102. package/dist/git/github-manager.d.ts.map +1 -0
  103. package/dist/git/github-manager.js +226 -0
  104. package/dist/git/github-manager.js.map +1 -0
  105. package/dist/git/hook-manager.d.ts +43 -0
  106. package/dist/git/hook-manager.d.ts.map +1 -0
  107. package/dist/git/hook-manager.js +191 -0
  108. package/dist/git/hook-manager.js.map +1 -0
  109. package/dist/git/index.d.ts +4 -0
  110. package/dist/git/index.d.ts.map +1 -0
  111. package/dist/git/index.js +5 -0
  112. package/dist/git/index.js.map +1 -0
  113. package/dist/index.d.ts +8 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +9 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/parsers/feature-validator.d.ts +60 -0
  118. package/dist/parsers/feature-validator.d.ts.map +1 -0
  119. package/dist/parsers/feature-validator.js +483 -0
  120. package/dist/parsers/feature-validator.js.map +1 -0
  121. package/dist/parsers/index.d.ts +8 -0
  122. package/dist/parsers/index.d.ts.map +1 -0
  123. package/dist/parsers/index.js +9 -0
  124. package/dist/parsers/index.js.map +1 -0
  125. package/dist/parsers/parser-manager.d.ts +103 -0
  126. package/dist/parsers/parser-manager.d.ts.map +1 -0
  127. package/dist/parsers/parser-manager.js +321 -0
  128. package/dist/parsers/parser-manager.js.map +1 -0
  129. package/dist/parsers/parser.d.ts +23 -0
  130. package/dist/parsers/parser.d.ts.map +1 -0
  131. package/dist/parsers/parser.js +6 -0
  132. package/dist/parsers/parser.js.map +1 -0
  133. package/dist/parsers/react-parser.d.ts +22 -0
  134. package/dist/parsers/react-parser.d.ts.map +1 -0
  135. package/dist/parsers/react-parser.js +307 -0
  136. package/dist/parsers/react-parser.js.map +1 -0
  137. package/dist/parsers/svelte-parser.d.ts +33 -0
  138. package/dist/parsers/svelte-parser.d.ts.map +1 -0
  139. package/dist/parsers/svelte-parser.js +408 -0
  140. package/dist/parsers/svelte-parser.js.map +1 -0
  141. package/dist/parsers/vanilla-parser.d.ts +31 -0
  142. package/dist/parsers/vanilla-parser.d.ts.map +1 -0
  143. package/dist/parsers/vanilla-parser.js +590 -0
  144. package/dist/parsers/vanilla-parser.js.map +1 -0
  145. package/dist/parsers/vue-parser.d.ts +9 -0
  146. package/dist/parsers/vue-parser.d.ts.map +1 -0
  147. package/dist/parsers/vue-parser.js +16 -0
  148. package/dist/parsers/vue-parser.js.map +1 -0
  149. package/dist/terminal-header.d.ts +12 -0
  150. package/dist/terminal-header.js +45 -0
  151. package/dist/types/index.d.ts +83 -0
  152. package/dist/types/index.d.ts.map +1 -0
  153. package/dist/types/index.js +5 -0
  154. package/dist/types/index.js.map +1 -0
  155. package/dist/ui/components.d.ts +133 -0
  156. package/dist/ui/components.d.ts.map +1 -0
  157. package/dist/ui/components.js +482 -0
  158. package/dist/ui/components.js.map +1 -0
  159. package/dist/ui/help.d.ts +11 -0
  160. package/dist/ui/help.d.ts.map +1 -0
  161. package/dist/ui/help.js +161 -0
  162. package/dist/ui/help.js.map +1 -0
  163. package/dist/ui/index.d.ts +5 -0
  164. package/dist/ui/index.d.ts.map +1 -0
  165. package/dist/ui/index.js +5 -0
  166. package/dist/ui/index.js.map +1 -0
  167. package/dist/ui/prompts.d.ts +63 -0
  168. package/dist/ui/prompts.d.ts.map +1 -0
  169. package/dist/ui/prompts.js +611 -0
  170. package/dist/ui/prompts.js.map +1 -0
  171. package/dist/ui/terminal-header.d.ts +13 -0
  172. package/dist/ui/terminal-header.d.ts.map +1 -0
  173. package/dist/ui/terminal-header.js +46 -0
  174. package/dist/ui/terminal-header.js.map +1 -0
  175. package/package.json +80 -0
  176. package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
  177. package/src/ai/fix-manager.ts +362 -0
  178. package/src/ai/gemini-analyzer.ts +671 -0
  179. package/src/ai/index.ts +4 -0
  180. package/src/ai/jules-implementer.ts +459 -0
  181. package/src/commands/automation.ts +344 -0
  182. package/src/commands/check.ts +299 -0
  183. package/src/commands/config.ts +365 -0
  184. package/src/commands/fix.ts +234 -0
  185. package/src/commands/index.ts +6 -0
  186. package/src/commands/init.ts +142 -0
  187. package/src/commands/status.ts +0 -0
  188. package/src/core/api-key-manager.ts +298 -0
  189. package/src/core/baseguard.ts +742 -0
  190. package/src/core/baseline-checker.ts +563 -0
  191. package/src/core/cache-manager.ts +270 -0
  192. package/src/core/configuration-recovery.ts +676 -0
  193. package/src/core/configuration.ts +559 -0
  194. package/src/core/debug-logger.ts +590 -0
  195. package/src/core/directory-filter.ts +421 -0
  196. package/src/core/error-handler.ts +517 -0
  197. package/src/core/file-processor.ts +331 -0
  198. package/src/core/gitignore-manager.ts +169 -0
  199. package/src/core/graceful-degradation-manager.ts +596 -0
  200. package/src/core/index.ts +13 -0
  201. package/src/core/lazy-loader.ts +307 -0
  202. package/src/core/logger.ts +0 -0
  203. package/src/core/memory-manager.ts +294 -0
  204. package/src/core/startup-optimizer.ts +173 -0
  205. package/src/core/system-error-handler.ts +746 -0
  206. package/src/git/automation-engine.ts +361 -0
  207. package/src/git/github-manager.ts +260 -0
  208. package/src/git/hook-manager.ts +210 -0
  209. package/src/git/index.ts +4 -0
  210. package/src/index.ts +8 -0
  211. package/src/parsers/feature-validator.ts +559 -0
  212. package/src/parsers/index.ts +8 -0
  213. package/src/parsers/parser-manager.ts +419 -0
  214. package/src/parsers/parser.ts +26 -0
  215. package/src/parsers/react-parser-optimized.ts +161 -0
  216. package/src/parsers/react-parser.ts +359 -0
  217. package/src/parsers/svelte-parser.ts +506 -0
  218. package/src/parsers/vanilla-parser.ts +682 -0
  219. package/src/parsers/vue-parser.ts +472 -0
  220. package/src/types/index.ts +92 -0
  221. package/src/ui/components.ts +567 -0
  222. package/src/ui/help.ts +193 -0
  223. package/src/ui/index.ts +4 -0
  224. package/src/ui/prompts.ts +688 -0
  225. package/src/ui/terminal-header.ts +59 -0
  226. package/test-config-commands.js +56 -0
  227. package/test-header-simple.js +33 -0
  228. package/test-terminal-header.js +12 -0
  229. package/test-ui.js +29 -0
  230. package/tests/e2e/baseguard.e2e.test.ts +516 -0
  231. package/tests/e2e/cross-platform.e2e.test.ts +420 -0
  232. package/tests/e2e/git-integration.e2e.test.ts +487 -0
  233. package/tests/fixtures/react-project/package.json +14 -0
  234. package/tests/fixtures/react-project/src/App.css +76 -0
  235. package/tests/fixtures/react-project/src/App.tsx +77 -0
  236. package/tests/fixtures/svelte-project/package.json +11 -0
  237. package/tests/fixtures/svelte-project/src/App.svelte +369 -0
  238. package/tests/fixtures/vanilla-project/index.html +76 -0
  239. package/tests/fixtures/vanilla-project/script.js +331 -0
  240. package/tests/fixtures/vanilla-project/styles.css +359 -0
  241. package/tests/fixtures/vue-project/package.json +12 -0
  242. package/tests/fixtures/vue-project/src/App.vue +216 -0
  243. package/tsconfig.json +36 -0
  244. 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;
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "svelte-test-project",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "dependencies": {
6
+ "svelte": "^4.2.0"
7
+ },
8
+ "devDependencies": {
9
+ "typescript": "^5.0.0"
10
+ }
11
+ }