baseguard 1.0.3 → 1.0.5

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 (169) hide show
  1. package/.baseguardrc.example.json +63 -63
  2. package/.eslintrc.json +24 -24
  3. package/.prettierrc +7 -7
  4. package/CHANGELOG.md +195 -195
  5. package/DEPLOYMENT.md +624 -624
  6. package/DEPLOYMENT_CHECKLIST.md +239 -239
  7. package/DEPLOYMENT_SUMMARY_v1.0.2.md +202 -202
  8. package/QUICK_START.md +134 -134
  9. package/README.md +488 -488
  10. package/RELEASE_NOTES_v1.0.2.md +434 -434
  11. package/bin/base.js +627 -627
  12. package/dist/ai/fix-manager.d.ts.map +1 -1
  13. package/dist/ai/fix-manager.js +1 -1
  14. package/dist/ai/fix-manager.js.map +1 -1
  15. package/dist/ai/gemini-analyzer.d.ts.map +1 -1
  16. package/dist/ai/gemini-analyzer.js +29 -35
  17. package/dist/ai/gemini-analyzer.js.map +1 -1
  18. package/dist/ai/gemini-code-fixer.d.ts.map +1 -1
  19. package/dist/ai/gemini-code-fixer.js +58 -58
  20. package/dist/ai/gemini-code-fixer.js.map +1 -1
  21. package/dist/ai/jules-implementer.d.ts +3 -0
  22. package/dist/ai/jules-implementer.d.ts.map +1 -1
  23. package/dist/ai/jules-implementer.js +63 -32
  24. package/dist/ai/jules-implementer.js.map +1 -1
  25. package/dist/ai/unified-code-fixer.js.map +1 -1
  26. package/dist/commands/check.d.ts.map +1 -1
  27. package/dist/commands/check.js +1 -1
  28. package/dist/commands/check.js.map +1 -1
  29. package/dist/commands/config.js +2 -1
  30. package/dist/commands/config.js.map +1 -1
  31. package/dist/commands/fix.d.ts.map +1 -1
  32. package/dist/commands/fix.js +48 -15
  33. package/dist/commands/fix.js.map +1 -1
  34. package/dist/core/api-key-manager.js +2 -2
  35. package/dist/core/api-key-manager.js.map +1 -1
  36. package/dist/core/baseguard.d.ts +1 -0
  37. package/dist/core/baseguard.d.ts.map +1 -1
  38. package/dist/core/baseguard.js +13 -10
  39. package/dist/core/baseguard.js.map +1 -1
  40. package/dist/core/baseline-checker.d.ts.map +1 -1
  41. package/dist/core/baseline-checker.js +8 -5
  42. package/dist/core/baseline-checker.js.map +1 -1
  43. package/dist/core/configuration-recovery.d.ts.map +1 -1
  44. package/dist/core/configuration-recovery.js +1 -1
  45. package/dist/core/configuration-recovery.js.map +1 -1
  46. package/dist/core/debug-logger.d.ts.map +1 -1
  47. package/dist/core/debug-logger.js +1 -1
  48. package/dist/core/debug-logger.js.map +1 -1
  49. package/dist/core/error-handler.d.ts.map +1 -1
  50. package/dist/core/error-handler.js +2 -1
  51. package/dist/core/error-handler.js.map +1 -1
  52. package/dist/core/gitignore-manager.js +5 -5
  53. package/dist/core/graceful-degradation-manager.d.ts.map +1 -1
  54. package/dist/core/graceful-degradation-manager.js +16 -16
  55. package/dist/core/graceful-degradation-manager.js.map +1 -1
  56. package/dist/core/lazy-loader.d.ts.map +1 -1
  57. package/dist/core/lazy-loader.js +9 -2
  58. package/dist/core/lazy-loader.js.map +1 -1
  59. package/dist/core/memory-manager.d.ts +0 -3
  60. package/dist/core/memory-manager.d.ts.map +1 -1
  61. package/dist/core/memory-manager.js.map +1 -1
  62. package/dist/core/parser-worker.d.ts +2 -0
  63. package/dist/core/parser-worker.d.ts.map +1 -0
  64. package/dist/core/parser-worker.js +19 -0
  65. package/dist/core/parser-worker.js.map +1 -0
  66. package/dist/core/startup-optimizer.d.ts.map +1 -1
  67. package/dist/core/startup-optimizer.js +4 -8
  68. package/dist/core/startup-optimizer.js.map +1 -1
  69. package/dist/core/system-error-handler.d.ts.map +1 -1
  70. package/dist/core/system-error-handler.js.map +1 -1
  71. package/dist/git/automation-engine.d.ts.map +1 -1
  72. package/dist/git/automation-engine.js +5 -4
  73. package/dist/git/automation-engine.js.map +1 -1
  74. package/dist/git/github-manager.d.ts.map +1 -1
  75. package/dist/git/github-manager.js.map +1 -1
  76. package/dist/git/hook-manager.js +5 -5
  77. package/dist/git/hook-manager.js.map +1 -1
  78. package/dist/parsers/parser-manager.d.ts.map +1 -1
  79. package/dist/parsers/parser-manager.js +1 -1
  80. package/dist/parsers/parser-manager.js.map +1 -1
  81. package/dist/parsers/svelte-parser.js +1 -1
  82. package/dist/parsers/svelte-parser.js.map +1 -1
  83. package/dist/parsers/vanilla-parser.d.ts.map +1 -1
  84. package/dist/parsers/vanilla-parser.js.map +1 -1
  85. package/dist/parsers/vue-parser.d.ts.map +1 -1
  86. package/dist/parsers/vue-parser.js.map +1 -1
  87. package/dist/ui/components.d.ts +1 -1
  88. package/dist/ui/components.d.ts.map +1 -1
  89. package/dist/ui/components.js +11 -11
  90. package/dist/ui/components.js.map +1 -1
  91. package/dist/ui/terminal-header.js +14 -14
  92. package/package.json +105 -105
  93. package/src/ai/__tests__/gemini-analyzer.test.ts +180 -180
  94. package/src/ai/agentkit-orchestrator.ts +533 -533
  95. package/src/ai/fix-manager.ts +362 -362
  96. package/src/ai/gemini-analyzer.ts +665 -671
  97. package/src/ai/gemini-code-fixer.ts +539 -540
  98. package/src/ai/index.ts +3 -3
  99. package/src/ai/jules-implementer.ts +504 -460
  100. package/src/ai/unified-code-fixer.ts +347 -347
  101. package/src/commands/automation.ts +343 -343
  102. package/src/commands/check.ts +298 -299
  103. package/src/commands/config.ts +584 -583
  104. package/src/commands/fix.ts +269 -238
  105. package/src/commands/index.ts +6 -6
  106. package/src/commands/init.ts +155 -155
  107. package/src/commands/status.ts +306 -306
  108. package/src/core/api-key-manager.ts +298 -298
  109. package/src/core/baseguard.ts +757 -756
  110. package/src/core/baseline-checker.ts +566 -563
  111. package/src/core/cache-manager.ts +271 -271
  112. package/src/core/configuration-recovery.ts +672 -673
  113. package/src/core/configuration.ts +595 -595
  114. package/src/core/debug-logger.ts +590 -590
  115. package/src/core/directory-filter.ts +420 -420
  116. package/src/core/error-handler.ts +518 -517
  117. package/src/core/file-processor.ts +337 -337
  118. package/src/core/gitignore-manager.ts +168 -168
  119. package/src/core/graceful-degradation-manager.ts +596 -596
  120. package/src/core/index.ts +16 -16
  121. package/src/core/lazy-loader.ts +317 -307
  122. package/src/core/memory-manager.ts +290 -295
  123. package/src/core/parser-worker.ts +33 -0
  124. package/src/core/startup-optimizer.ts +246 -255
  125. package/src/core/system-error-handler.ts +755 -756
  126. package/src/git/automation-engine.ts +361 -361
  127. package/src/git/github-manager.ts +190 -192
  128. package/src/git/hook-manager.ts +210 -210
  129. package/src/git/index.ts +3 -3
  130. package/src/index.ts +7 -7
  131. package/src/parsers/feature-validator.ts +558 -558
  132. package/src/parsers/index.ts +7 -7
  133. package/src/parsers/parser-manager.ts +418 -419
  134. package/src/parsers/parser.ts +25 -25
  135. package/src/parsers/react-parser-optimized.ts +160 -160
  136. package/src/parsers/react-parser.ts +358 -358
  137. package/src/parsers/svelte-parser.ts +510 -510
  138. package/src/parsers/vanilla-parser.ts +685 -686
  139. package/src/parsers/vue-parser.ts +476 -478
  140. package/src/types/index.ts +95 -95
  141. package/src/ui/components.ts +567 -567
  142. package/src/ui/help.ts +192 -192
  143. package/src/ui/index.ts +3 -3
  144. package/src/ui/prompts.ts +680 -680
  145. package/src/ui/terminal-header.ts +58 -58
  146. package/test-build.js +40 -40
  147. package/test-config-commands.js +55 -55
  148. package/test-header-simple.js +32 -32
  149. package/test-terminal-header.js +11 -11
  150. package/test-ui.js +28 -28
  151. package/tests/e2e/baseguard.e2e.test.ts +515 -515
  152. package/tests/e2e/cross-platform.e2e.test.ts +419 -419
  153. package/tests/e2e/git-integration.e2e.test.ts +486 -486
  154. package/tests/fixtures/react-project/package.json +13 -13
  155. package/tests/fixtures/react-project/src/App.css +75 -75
  156. package/tests/fixtures/react-project/src/App.tsx +76 -76
  157. package/tests/fixtures/svelte-project/package.json +10 -10
  158. package/tests/fixtures/svelte-project/src/App.svelte +368 -368
  159. package/tests/fixtures/vanilla-project/index.html +75 -75
  160. package/tests/fixtures/vanilla-project/script.js +330 -330
  161. package/tests/fixtures/vanilla-project/styles.css +358 -358
  162. package/tests/fixtures/vue-project/package.json +11 -11
  163. package/tests/fixtures/vue-project/src/App.vue +215 -215
  164. package/tmp-smoke/.baseguard/backups/config-2026-02-19T12-04-11-067Z-auto.json +30 -0
  165. package/tmp-smoke/src/bad.css +3 -0
  166. package/tsconfig.json +34 -34
  167. package/vitest.config.ts +11 -11
  168. package/dist/terminal-header.d.ts +0 -12
  169. package/dist/terminal-header.js +0 -45
@@ -5,13 +5,13 @@ import chalk from 'chalk';
5
5
  export function showTerminalHeader() {
6
6
  console.clear();
7
7
  // Well-spaced ASCII art for "Baseguard" - monochrome for good contrast
8
- const logo = `
9
- ██████╗ █████╗ ███████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗
10
- ██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ██║ ██║ ██╔══██╗ ██╔══██╗ ██╔══██╗
11
- ██████╔╝ ███████║ ███████╗ █████╗ ██║ ███╗ ██║ ██║ ███████║ ██████╔╝ ██║ ██║
12
- ██╔══██╗ ██╔══██║ ╚════██║ ██╔══╝ ██║ ██║ ██║ ██║ ██╔══██║ ██╔══██╗ ██║ ██║
13
- ██████╔╝ ██║ ██║ ███████║ ███████╗ ╚██████╔╝ ╚██████╔╝ ██║ ██║ ██║ ██║ ██████╔╝
14
- ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
8
+ const logo = `
9
+ ██████╗ █████╗ ███████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗
10
+ ██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ██║ ██║ ██╔══██╗ ██╔══██╗ ██╔══██╗
11
+ ██████╔╝ ███████║ ███████╗ █████╗ ██║ ███╗ ██║ ██║ ███████║ ██████╔╝ ██║ ██║
12
+ ██╔══██╗ ██╔══██║ ╚════██║ ██╔══╝ ██║ ██║ ██║ ██║ ██╔══██║ ██╔══██╗ ██║ ██║
13
+ ██████╔╝ ██║ ██║ ███████║ ███████╗ ╚██████╔╝ ╚██████╔╝ ██║ ██║ ██║ ██║ ██████╔╝
14
+ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
15
15
  `;
16
16
  // Use a single color that contrasts well with both light and dark terminals
17
17
  console.log(chalk.bold.white(logo));
@@ -31,13 +31,13 @@ export function showCompactHeader() {
31
31
  */
32
32
  export function showSimpleHeader() {
33
33
  console.clear();
34
- const logo = `
35
- ██████╗ █████╗ ███████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗
36
- ██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ██║ ██║ ██╔══██╗ ██╔══██╗ ██╔══██╗
37
- ██████╔╝ ███████║ ███████╗ █████╗ ██║ ███╗ ██║ ██║ ███████║ ██████╔╝ ██║ ██║
38
- ██╔══██╗ ██╔══██║ ╚════██║ ██╔══╝ ██║ ██║ ██║ ██║ ██╔══██║ ██╔══██╗ ██║ ██║
39
- ██████╔╝ ██║ ██║ ███████║ ███████╗ ╚██████╔╝ ╚██████╔╝ ██║ ██║ ██║ ██║ ██████╔╝
40
- ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
34
+ const logo = `
35
+ ██████╗ █████╗ ███████╗ ███████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗
36
+ ██╔══██╗ ██╔══██╗ ██╔════╝ ██╔════╝ ██╔════╝ ██║ ██║ ██╔══██╗ ██╔══██╗ ██╔══██╗
37
+ ██████╔╝ ███████║ ███████╗ █████╗ ██║ ███╗ ██║ ██║ ███████║ ██████╔╝ ██║ ██║
38
+ ██╔══██╗ ██╔══██║ ╚════██║ ██╔══╝ ██║ ██║ ██║ ██║ ██╔══██║ ██╔══██╗ ██║ ██║
39
+ ██████╔╝ ██║ ██║ ███████║ ███████╗ ╚██████╔╝ ╚██████╔╝ ██║ ██║ ██║ ██║ ██████╔╝
40
+ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
41
41
  `;
42
42
  console.log(chalk.bold.white(logo));
43
43
  console.log(chalk.bold.white(' Ship Modern Code') +
package/package.json CHANGED
@@ -1,105 +1,105 @@
1
- {
2
- "name": "baseguard",
3
- "version": "1.0.3",
4
- "description": "BaseGuard - Never ship incompatible code again. Intelligent browser compatibility enforcement with AI-powered analysis and autonomous fixing.",
5
- "main": "dist/index.js",
6
- "bin": {
7
- "base": "bin/base.js"
8
- },
9
- "type": "module",
10
- "scripts": {
11
- "build": "tsc",
12
- "dev": "tsc --watch",
13
- "test": "vitest --run",
14
- "test:watch": "vitest",
15
- "test:e2e": "vitest --run tests/e2e/",
16
- "lint": "eslint src/**/*.ts",
17
- "lint:fix": "eslint src/**/*.ts --fix",
18
- "format": "prettier --write src/**/*.ts",
19
- "format:check": "prettier --check src/**/*.ts",
20
- "typecheck": "tsc --noEmit",
21
- "prepare": "husky install",
22
- "prepublishOnly": "npm run build && npm run test",
23
- "release": "npm version patch && npm publish",
24
- "clean": "rm -rf dist"
25
- },
26
- "keywords": [
27
- "browser-compatibility",
28
- "baseline",
29
- "web-standards",
30
- "cli",
31
- "ai",
32
- "automation",
33
- "git-hooks",
34
- "compatibility-checker",
35
- "web-features",
36
- "progressive-enhancement",
37
- "polyfills",
38
- "gemini",
39
- "jules",
40
- "code-fixing",
41
- "linting",
42
- "static-analysis",
43
- "developer-tools",
44
- "frontend",
45
- "javascript",
46
- "css",
47
- "html",
48
- "react",
49
- "vue",
50
- "svelte"
51
- ],
52
- "author": "BaseGuard Team",
53
- "license": "MIT",
54
- "dependencies": {
55
- "@babel/parser": "^7.23.6",
56
- "@babel/traverse": "^7.23.6",
57
- "@babel/types": "^7.23.6",
58
- "@vue/compiler-sfc": "^3.3.13",
59
- "boxen": "^7.1.1",
60
- "chalk": "^5.3.0",
61
- "cli-table3": "^0.6.3",
62
- "commander": "^11.1.0",
63
- "figlet": "^1.9.3",
64
- "glob": "^10.3.10",
65
- "gradient-string": "^2.0.2",
66
- "husky": "^8.0.3",
67
- "inquirer": "^9.2.12",
68
- "node-fetch": "^3.3.2",
69
- "open": "^9.1.0",
70
- "ora": "^7.0.1",
71
- "postcss": "^8.4.32",
72
- "postcss-selector-parser": "^6.0.13",
73
- "svelte": "^4.2.8",
74
- "web-features": "^0.8.4"
75
- },
76
- "devDependencies": {
77
- "@types/babel__traverse": "^7.28.0",
78
- "@types/figlet": "^1.7.0",
79
- "@types/glob": "^8.1.0",
80
- "@types/gradient-string": "^1.1.6",
81
- "@types/inquirer": "^9.0.7",
82
- "@types/node": "^20.10.5",
83
- "@typescript-eslint/eslint-plugin": "^6.21.0",
84
- "@typescript-eslint/parser": "^6.21.0",
85
- "eslint": "^8.56.0",
86
- "prettier": "^3.1.1",
87
- "typescript": "^5.3.3",
88
- "vitest": "^1.1.0"
89
- },
90
- "engines": {
91
- "node": ">=18.0.0"
92
- },
93
- "directories": {
94
- "test": "tests"
95
- },
96
- "repository": {
97
- "type": "git",
98
- "url": "git+https://github.com/ebuka1017/baseguard.git"
99
- },
100
- "types": "./dist/index.d.ts",
101
- "bugs": {
102
- "url": "https://github.com/ebuka1017/baseguard/issues"
103
- },
104
- "homepage": "https://github.com/ebuka1017/baseguard#readme"
105
- }
1
+ {
2
+ "name": "baseguard",
3
+ "version": "1.0.5",
4
+ "description": "BaseGuard - Never ship incompatible code again. Intelligent browser compatibility enforcement with AI-powered analysis and autonomous fixing.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "base": "bin/base.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "test": "vitest --run",
14
+ "test:watch": "vitest",
15
+ "test:e2e": "vitest --run tests/e2e/",
16
+ "lint": "eslint src/**/*.ts",
17
+ "lint:fix": "eslint src/**/*.ts --fix",
18
+ "format": "prettier --write src/**/*.ts",
19
+ "format:check": "prettier --check src/**/*.ts",
20
+ "typecheck": "tsc --noEmit",
21
+ "prepare": "husky install",
22
+ "prepublishOnly": "npm run build && npm run test",
23
+ "release": "npm version patch && npm publish",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "keywords": [
27
+ "browser-compatibility",
28
+ "baseline",
29
+ "web-standards",
30
+ "cli",
31
+ "ai",
32
+ "automation",
33
+ "git-hooks",
34
+ "compatibility-checker",
35
+ "web-features",
36
+ "progressive-enhancement",
37
+ "polyfills",
38
+ "gemini",
39
+ "jules",
40
+ "code-fixing",
41
+ "linting",
42
+ "static-analysis",
43
+ "developer-tools",
44
+ "frontend",
45
+ "javascript",
46
+ "css",
47
+ "html",
48
+ "react",
49
+ "vue",
50
+ "svelte"
51
+ ],
52
+ "author": "BaseGuard Team",
53
+ "license": "MIT",
54
+ "dependencies": {
55
+ "@babel/parser": "^7.23.6",
56
+ "@babel/traverse": "^7.23.6",
57
+ "@babel/types": "^7.23.6",
58
+ "@vue/compiler-sfc": "^3.3.13",
59
+ "boxen": "^7.1.1",
60
+ "chalk": "^5.3.0",
61
+ "cli-table3": "^0.6.3",
62
+ "commander": "^11.1.0",
63
+ "figlet": "^1.9.3",
64
+ "glob": "^10.3.10",
65
+ "gradient-string": "^2.0.2",
66
+ "husky": "^8.0.3",
67
+ "inquirer": "^9.2.12",
68
+ "node-fetch": "^3.3.2",
69
+ "open": "^9.1.0",
70
+ "ora": "^7.0.1",
71
+ "postcss": "^8.4.32",
72
+ "postcss-selector-parser": "^6.0.13",
73
+ "svelte": "^4.2.8",
74
+ "web-features": "^0.8.4"
75
+ },
76
+ "devDependencies": {
77
+ "@types/babel__traverse": "^7.28.0",
78
+ "@types/figlet": "^1.7.0",
79
+ "@types/glob": "^8.1.0",
80
+ "@types/gradient-string": "^1.1.6",
81
+ "@types/inquirer": "^9.0.7",
82
+ "@types/node": "^20.10.5",
83
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
84
+ "@typescript-eslint/parser": "^6.21.0",
85
+ "eslint": "^8.56.0",
86
+ "prettier": "^3.1.1",
87
+ "typescript": "^5.3.3",
88
+ "vitest": "^1.1.0"
89
+ },
90
+ "engines": {
91
+ "node": ">=18.0.0"
92
+ },
93
+ "directories": {
94
+ "test": "tests"
95
+ },
96
+ "repository": {
97
+ "type": "git",
98
+ "url": "git+https://github.com/ebuka1017/baseguard.git"
99
+ },
100
+ "types": "./dist/index.d.ts",
101
+ "bugs": {
102
+ "url": "https://github.com/ebuka1017/baseguard/issues"
103
+ },
104
+ "homepage": "https://github.com/ebuka1017/baseguard#readme"
105
+ }
@@ -1,181 +1,181 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { GeminiAnalyzer } from '../gemini-analyzer.js';
3
- import type { Violation } from '../../types/index.js';
4
-
5
- // Mock fetch globally
6
- global.fetch = vi.fn();
7
-
8
- describe('GeminiAnalyzer', () => {
9
- let analyzer: GeminiAnalyzer;
10
- const mockApiKey = 'AIzaSyDummyKeyForTesting1234567890123456789';
11
-
12
- beforeEach(() => {
13
- analyzer = new GeminiAnalyzer(mockApiKey);
14
- vi.clearAllMocks();
15
- });
16
-
17
- const mockViolation: Violation = {
18
- feature: 'container-type',
19
- featureId: 'container-queries',
20
- file: 'src/Card.css',
21
- line: 15,
22
- column: 5,
23
- context: ' container-type: inline-size;',
24
- browser: 'safari',
25
- required: '15',
26
- actual: false,
27
- baselineStatus: 'newly',
28
- reason: 'Not supported in Safari 15'
29
- };
30
-
31
- describe('API Key Validation', () => {
32
- it('should validate correct API key format', () => {
33
- expect(GeminiAnalyzer.validateApiKey(mockApiKey)).toBe(true);
34
- });
35
-
36
- it('should reject invalid API key format', () => {
37
- expect(GeminiAnalyzer.validateApiKey('invalid-key')).toBe(false);
38
- expect(GeminiAnalyzer.validateApiKey('AIza123')).toBe(false); // too short
39
- });
40
- });
41
-
42
- describe('Cache Management', () => {
43
- it('should cache analysis results', async () => {
44
- const mockResponse = {
45
- ok: true,
46
- json: () => Promise.resolve({
47
- candidates: [{
48
- content: {
49
- parts: [{ text: 'Test analysis response with 5% market share impact.' }]
50
- },
51
- groundingMetadata: {
52
- groundingChunks: [
53
- { web: { uri: 'https://web.dev/container-queries' } }
54
- ]
55
- }
56
- }]
57
- })
58
- };
59
-
60
- (global.fetch as any).mockResolvedValue(mockResponse);
61
-
62
- // First call should hit the API
63
- const analysis1 = await analyzer.analyzeViolation(mockViolation);
64
- expect(fetch).toHaveBeenCalledTimes(1);
65
-
66
- // Second call should use cache
67
- const analysis2 = await analyzer.analyzeViolation(mockViolation);
68
- expect(fetch).toHaveBeenCalledTimes(1); // Still only 1 call
69
-
70
- expect(analysis1.violation.feature).toBe(analysis2.violation.feature);
71
- });
72
-
73
- it('should clear cache when requested', async () => {
74
- analyzer.clearCache();
75
- const stats = analyzer.getCacheStats();
76
- expect(stats.size).toBe(0);
77
- });
78
- });
79
-
80
- describe('Error Handling', () => {
81
- it('should handle API errors gracefully', async () => {
82
- (global.fetch as any).mockRejectedValue(new Error('Network error'));
83
-
84
- const analysis = await analyzer.analyzeViolation(mockViolation);
85
-
86
- expect(analysis.confidence).toBe(0.3); // Fallback confidence
87
- expect(analysis.plainEnglish).toContain('Analysis unavailable');
88
- expect(analysis.fixStrategy).toBe('progressive enhancement');
89
- });
90
-
91
- it('should handle invalid API responses', async () => {
92
- const mockResponse = {
93
- ok: true,
94
- json: () => Promise.resolve({ candidates: [] })
95
- };
96
-
97
- (global.fetch as any).mockResolvedValue(mockResponse);
98
-
99
- const analysis = await analyzer.analyzeViolation(mockViolation);
100
-
101
- expect(analysis.confidence).toBe(0.3);
102
- expect(analysis.plainEnglish).toContain('Analysis unavailable');
103
- });
104
- });
105
-
106
- describe('Response Parsing', () => {
107
- it('should extract market share from response', async () => {
108
- const mockResponse = {
109
- ok: true,
110
- json: () => Promise.resolve({
111
- candidates: [{
112
- content: {
113
- parts: [{ text: 'This affects approximately 8.5% of users on Safari 15.' }]
114
- },
115
- groundingMetadata: {
116
- groundingChunks: [
117
- { web: { uri: 'https://caniuse.com/css-container-queries' } }
118
- ]
119
- }
120
- }]
121
- })
122
- };
123
-
124
- (global.fetch as any).mockResolvedValue(mockResponse);
125
-
126
- const analysis = await analyzer.analyzeViolation(mockViolation);
127
-
128
- expect(analysis.marketShare).toBe(0.085);
129
- expect(analysis.sources).toContain('https://caniuse.com/css-container-queries');
130
- });
131
-
132
- it('should extract best practices from response', async () => {
133
- const mockResponse = {
134
- ok: true,
135
- json: () => Promise.resolve({
136
- candidates: [{
137
- content: {
138
- parts: [{
139
- text: 'Use @supports for feature detection. Consider using polyfills. Test across target browsers.'
140
- }]
141
- },
142
- groundingMetadata: { groundingChunks: [] }
143
- }]
144
- })
145
- };
146
-
147
- (global.fetch as any).mockResolvedValue(mockResponse);
148
-
149
- const analysis = await analyzer.analyzeViolation(mockViolation);
150
-
151
- expect(analysis.bestPractices).toContain('Use @supports for feature detection');
152
- expect(analysis.bestPractices).toContain('Consider using polyfills');
153
- expect(analysis.bestPractices).toContain('Test across target browsers');
154
- });
155
- });
156
-
157
- describe('Batch Processing', () => {
158
- it('should process multiple violations with concurrency control', async () => {
159
- const mockResponse = {
160
- ok: true,
161
- json: () => Promise.resolve({
162
- candidates: [{
163
- content: {
164
- parts: [{ text: 'Test analysis response.' }]
165
- },
166
- groundingMetadata: { groundingChunks: [] }
167
- }]
168
- })
169
- };
170
-
171
- (global.fetch as any).mockResolvedValue(mockResponse);
172
-
173
- const violations = [mockViolation, { ...mockViolation, feature: 'dialog' }];
174
- const analyses = await analyzer.analyzeViolations(violations, 2);
175
-
176
- expect(analyses).toHaveLength(2);
177
- expect(analyses[0].violation.feature).toBe('container-type');
178
- expect(analyses[1].violation.feature).toBe('dialog');
179
- });
180
- });
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { GeminiAnalyzer } from '../gemini-analyzer.js';
3
+ import type { Violation } from '../../types/index.js';
4
+
5
+ // Mock fetch globally
6
+ global.fetch = vi.fn();
7
+
8
+ describe('GeminiAnalyzer', () => {
9
+ let analyzer: GeminiAnalyzer;
10
+ const mockApiKey = 'AIzaSyDummyKeyForTesting1234567890123456789';
11
+
12
+ beforeEach(() => {
13
+ analyzer = new GeminiAnalyzer(mockApiKey);
14
+ vi.clearAllMocks();
15
+ });
16
+
17
+ const mockViolation: Violation = {
18
+ feature: 'container-type',
19
+ featureId: 'container-queries',
20
+ file: 'src/Card.css',
21
+ line: 15,
22
+ column: 5,
23
+ context: ' container-type: inline-size;',
24
+ browser: 'safari',
25
+ required: '15',
26
+ actual: false,
27
+ baselineStatus: 'newly',
28
+ reason: 'Not supported in Safari 15'
29
+ };
30
+
31
+ describe('API Key Validation', () => {
32
+ it('should validate correct API key format', () => {
33
+ expect(GeminiAnalyzer.validateApiKey(mockApiKey)).toBe(true);
34
+ });
35
+
36
+ it('should reject invalid API key format', () => {
37
+ expect(GeminiAnalyzer.validateApiKey('invalid-key')).toBe(false);
38
+ expect(GeminiAnalyzer.validateApiKey('AIza123')).toBe(false); // too short
39
+ });
40
+ });
41
+
42
+ describe('Cache Management', () => {
43
+ it('should cache analysis results', async () => {
44
+ const mockResponse = {
45
+ ok: true,
46
+ json: () => Promise.resolve({
47
+ candidates: [{
48
+ content: {
49
+ parts: [{ text: 'Test analysis response with 5% market share impact.' }]
50
+ },
51
+ groundingMetadata: {
52
+ groundingChunks: [
53
+ { web: { uri: 'https://web.dev/container-queries' } }
54
+ ]
55
+ }
56
+ }]
57
+ })
58
+ };
59
+
60
+ (global.fetch as any).mockResolvedValue(mockResponse);
61
+
62
+ // First call should hit the API
63
+ const analysis1 = await analyzer.analyzeViolation(mockViolation);
64
+ expect(fetch).toHaveBeenCalledTimes(1);
65
+
66
+ // Second call should use cache
67
+ const analysis2 = await analyzer.analyzeViolation(mockViolation);
68
+ expect(fetch).toHaveBeenCalledTimes(1); // Still only 1 call
69
+
70
+ expect(analysis1.violation.feature).toBe(analysis2.violation.feature);
71
+ });
72
+
73
+ it('should clear cache when requested', async () => {
74
+ analyzer.clearCache();
75
+ const stats = analyzer.getCacheStats();
76
+ expect(stats.size).toBe(0);
77
+ });
78
+ });
79
+
80
+ describe('Error Handling', () => {
81
+ it('should handle API errors gracefully', async () => {
82
+ (global.fetch as any).mockRejectedValue(new Error('Network error'));
83
+
84
+ const analysis = await analyzer.analyzeViolation(mockViolation);
85
+
86
+ expect(analysis.confidence).toBe(0.3); // Fallback confidence
87
+ expect(analysis.plainEnglish).toContain('Analysis unavailable');
88
+ expect(analysis.fixStrategy).toBe('progressive enhancement');
89
+ });
90
+
91
+ it('should handle invalid API responses', async () => {
92
+ const mockResponse = {
93
+ ok: true,
94
+ json: () => Promise.resolve({ candidates: [] })
95
+ };
96
+
97
+ (global.fetch as any).mockResolvedValue(mockResponse);
98
+
99
+ const analysis = await analyzer.analyzeViolation(mockViolation);
100
+
101
+ expect(analysis.confidence).toBe(0.3);
102
+ expect(analysis.plainEnglish).toContain('Analysis unavailable');
103
+ });
104
+ });
105
+
106
+ describe('Response Parsing', () => {
107
+ it('should extract market share from response', async () => {
108
+ const mockResponse = {
109
+ ok: true,
110
+ json: () => Promise.resolve({
111
+ candidates: [{
112
+ content: {
113
+ parts: [{ text: 'This affects approximately 8.5% of users on Safari 15.' }]
114
+ },
115
+ groundingMetadata: {
116
+ groundingChunks: [
117
+ { web: { uri: 'https://caniuse.com/css-container-queries' } }
118
+ ]
119
+ }
120
+ }]
121
+ })
122
+ };
123
+
124
+ (global.fetch as any).mockResolvedValue(mockResponse);
125
+
126
+ const analysis = await analyzer.analyzeViolation(mockViolation);
127
+
128
+ expect(analysis.marketShare).toBe(0.085);
129
+ expect(analysis.sources).toContain('https://caniuse.com/css-container-queries');
130
+ });
131
+
132
+ it('should extract best practices from response', async () => {
133
+ const mockResponse = {
134
+ ok: true,
135
+ json: () => Promise.resolve({
136
+ candidates: [{
137
+ content: {
138
+ parts: [{
139
+ text: 'Use @supports for feature detection. Consider using polyfills. Test across target browsers.'
140
+ }]
141
+ },
142
+ groundingMetadata: { groundingChunks: [] }
143
+ }]
144
+ })
145
+ };
146
+
147
+ (global.fetch as any).mockResolvedValue(mockResponse);
148
+
149
+ const analysis = await analyzer.analyzeViolation(mockViolation);
150
+
151
+ expect(analysis.bestPractices).toContain('Use @supports for feature detection');
152
+ expect(analysis.bestPractices).toContain('Consider using polyfills');
153
+ expect(analysis.bestPractices).toContain('Test across target browsers');
154
+ });
155
+ });
156
+
157
+ describe('Batch Processing', () => {
158
+ it('should process multiple violations with concurrency control', async () => {
159
+ const mockResponse = {
160
+ ok: true,
161
+ json: () => Promise.resolve({
162
+ candidates: [{
163
+ content: {
164
+ parts: [{ text: 'Test analysis response.' }]
165
+ },
166
+ groundingMetadata: { groundingChunks: [] }
167
+ }]
168
+ })
169
+ };
170
+
171
+ (global.fetch as any).mockResolvedValue(mockResponse);
172
+
173
+ const violations = [mockViolation, { ...mockViolation, feature: 'dialog' }];
174
+ const analyses = await analyzer.analyzeViolations(violations, 2);
175
+
176
+ expect(analyses).toHaveLength(2);
177
+ expect(analyses[0].violation.feature).toBe('container-type');
178
+ expect(analyses[1].violation.feature).toBe('dialog');
179
+ });
180
+ });
181
181
  });