@rankcli/agent-runtime 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +242 -0
  2. package/dist/analyzer-2CSWIQGD.mjs +6 -0
  3. package/dist/chunk-YNZYHEYM.mjs +774 -0
  4. package/dist/index.d.mts +4012 -0
  5. package/dist/index.d.ts +4012 -0
  6. package/dist/index.js +29672 -0
  7. package/dist/index.mjs +28602 -0
  8. package/package.json +53 -0
  9. package/scripts/build-deno.ts +134 -0
  10. package/src/audit/ai/analyzer.ts +347 -0
  11. package/src/audit/ai/index.ts +29 -0
  12. package/src/audit/ai/prompts/content-analysis.ts +271 -0
  13. package/src/audit/ai/types.ts +179 -0
  14. package/src/audit/checks/additional-checks.ts +439 -0
  15. package/src/audit/checks/ai-citation-worthiness.ts +399 -0
  16. package/src/audit/checks/ai-content-structure.ts +325 -0
  17. package/src/audit/checks/ai-readiness.ts +339 -0
  18. package/src/audit/checks/anchor-text.ts +179 -0
  19. package/src/audit/checks/answer-conciseness.ts +322 -0
  20. package/src/audit/checks/asset-minification.ts +270 -0
  21. package/src/audit/checks/bing-optimization.ts +206 -0
  22. package/src/audit/checks/brand-mention-optimization.ts +349 -0
  23. package/src/audit/checks/caching-headers.ts +305 -0
  24. package/src/audit/checks/canonical-advanced.ts +150 -0
  25. package/src/audit/checks/canonical-domain.ts +196 -0
  26. package/src/audit/checks/citation-quality.ts +358 -0
  27. package/src/audit/checks/client-rendering.ts +542 -0
  28. package/src/audit/checks/color-contrast.ts +342 -0
  29. package/src/audit/checks/content-freshness.ts +170 -0
  30. package/src/audit/checks/content-science.ts +589 -0
  31. package/src/audit/checks/conversion-elements.ts +526 -0
  32. package/src/audit/checks/crawlability.ts +220 -0
  33. package/src/audit/checks/directory-listing.ts +172 -0
  34. package/src/audit/checks/dom-analysis.ts +191 -0
  35. package/src/audit/checks/dom-size.ts +246 -0
  36. package/src/audit/checks/duplicate-content.ts +194 -0
  37. package/src/audit/checks/eeat-signals.ts +990 -0
  38. package/src/audit/checks/entity-seo.ts +396 -0
  39. package/src/audit/checks/featured-snippet.ts +473 -0
  40. package/src/audit/checks/freshness-signals.ts +443 -0
  41. package/src/audit/checks/funnel-intent.ts +463 -0
  42. package/src/audit/checks/hreflang.ts +174 -0
  43. package/src/audit/checks/html-compliance.ts +302 -0
  44. package/src/audit/checks/image-dimensions.ts +167 -0
  45. package/src/audit/checks/images.ts +160 -0
  46. package/src/audit/checks/indexnow.ts +275 -0
  47. package/src/audit/checks/interactive-tools.ts +475 -0
  48. package/src/audit/checks/internal-link-graph.ts +436 -0
  49. package/src/audit/checks/keyword-analysis.ts +239 -0
  50. package/src/audit/checks/keyword-cannibalization.ts +385 -0
  51. package/src/audit/checks/keyword-placement.ts +471 -0
  52. package/src/audit/checks/links.ts +203 -0
  53. package/src/audit/checks/llms-txt.ts +224 -0
  54. package/src/audit/checks/local-seo.ts +296 -0
  55. package/src/audit/checks/mobile.ts +167 -0
  56. package/src/audit/checks/modern-images.ts +226 -0
  57. package/src/audit/checks/navboost-signals.ts +395 -0
  58. package/src/audit/checks/on-page.ts +209 -0
  59. package/src/audit/checks/page-resources.ts +285 -0
  60. package/src/audit/checks/pagination.ts +180 -0
  61. package/src/audit/checks/performance.ts +153 -0
  62. package/src/audit/checks/platform-presence.ts +580 -0
  63. package/src/audit/checks/redirect-analysis.ts +153 -0
  64. package/src/audit/checks/redirect-chain.ts +389 -0
  65. package/src/audit/checks/resource-hints.ts +420 -0
  66. package/src/audit/checks/responsive-css.ts +247 -0
  67. package/src/audit/checks/responsive-images.ts +396 -0
  68. package/src/audit/checks/review-ecosystem.ts +415 -0
  69. package/src/audit/checks/robots-validation.ts +373 -0
  70. package/src/audit/checks/security-headers.ts +172 -0
  71. package/src/audit/checks/security.ts +144 -0
  72. package/src/audit/checks/serp-preview.ts +251 -0
  73. package/src/audit/checks/site-maturity.ts +444 -0
  74. package/src/audit/checks/social-meta.test.ts +275 -0
  75. package/src/audit/checks/social-meta.ts +134 -0
  76. package/src/audit/checks/soft-404.ts +151 -0
  77. package/src/audit/checks/structured-data.ts +238 -0
  78. package/src/audit/checks/tech-detection.ts +496 -0
  79. package/src/audit/checks/topical-clusters.ts +435 -0
  80. package/src/audit/checks/tracker-bloat.ts +462 -0
  81. package/src/audit/checks/tracking-verification.test.ts +371 -0
  82. package/src/audit/checks/tracking-verification.ts +636 -0
  83. package/src/audit/checks/url-safety.ts +682 -0
  84. package/src/audit/deno-entry.ts +66 -0
  85. package/src/audit/discovery/index.ts +15 -0
  86. package/src/audit/discovery/link-crawler.ts +232 -0
  87. package/src/audit/discovery/repo-routes.ts +347 -0
  88. package/src/audit/engine.ts +620 -0
  89. package/src/audit/fixes/index.ts +209 -0
  90. package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
  91. package/src/audit/fixes/social-meta-fixes.ts +463 -0
  92. package/src/audit/index.ts +74 -0
  93. package/src/audit/runner.test.ts +299 -0
  94. package/src/audit/runner.ts +130 -0
  95. package/src/audit/types.ts +1953 -0
  96. package/src/content/featured-snippet.ts +367 -0
  97. package/src/content/generator.test.ts +534 -0
  98. package/src/content/generator.ts +501 -0
  99. package/src/content/headline.ts +317 -0
  100. package/src/content/index.ts +62 -0
  101. package/src/content/intent.ts +258 -0
  102. package/src/content/keyword-density.ts +349 -0
  103. package/src/content/readability.ts +262 -0
  104. package/src/executor.ts +336 -0
  105. package/src/fixer.ts +416 -0
  106. package/src/frameworks/detector.test.ts +248 -0
  107. package/src/frameworks/detector.ts +371 -0
  108. package/src/frameworks/index.ts +68 -0
  109. package/src/frameworks/recipes/angular.yaml +171 -0
  110. package/src/frameworks/recipes/astro.yaml +206 -0
  111. package/src/frameworks/recipes/django.yaml +180 -0
  112. package/src/frameworks/recipes/laravel.yaml +137 -0
  113. package/src/frameworks/recipes/nextjs.yaml +268 -0
  114. package/src/frameworks/recipes/nuxt.yaml +175 -0
  115. package/src/frameworks/recipes/rails.yaml +188 -0
  116. package/src/frameworks/recipes/react.yaml +202 -0
  117. package/src/frameworks/recipes/sveltekit.yaml +154 -0
  118. package/src/frameworks/recipes/vue.yaml +137 -0
  119. package/src/frameworks/recipes/wordpress.yaml +209 -0
  120. package/src/frameworks/suggestion-engine.ts +320 -0
  121. package/src/geo/geo-content.test.ts +305 -0
  122. package/src/geo/geo-content.ts +266 -0
  123. package/src/geo/geo-history.test.ts +473 -0
  124. package/src/geo/geo-history.ts +433 -0
  125. package/src/geo/geo-tracker.test.ts +359 -0
  126. package/src/geo/geo-tracker.ts +411 -0
  127. package/src/geo/index.ts +10 -0
  128. package/src/git/commit-helper.test.ts +261 -0
  129. package/src/git/commit-helper.ts +329 -0
  130. package/src/git/index.ts +12 -0
  131. package/src/git/pr-helper.test.ts +284 -0
  132. package/src/git/pr-helper.ts +307 -0
  133. package/src/index.ts +66 -0
  134. package/src/keywords/ai-keyword-engine.ts +1062 -0
  135. package/src/keywords/ai-summarizer.ts +387 -0
  136. package/src/keywords/ci-mode.ts +555 -0
  137. package/src/keywords/engine.ts +359 -0
  138. package/src/keywords/index.ts +151 -0
  139. package/src/keywords/llm-judge.ts +357 -0
  140. package/src/keywords/nlp-analysis.ts +706 -0
  141. package/src/keywords/prioritizer.ts +295 -0
  142. package/src/keywords/site-crawler.ts +342 -0
  143. package/src/keywords/sources/autocomplete.ts +139 -0
  144. package/src/keywords/sources/competitive-search.ts +450 -0
  145. package/src/keywords/sources/competitor-analysis.ts +374 -0
  146. package/src/keywords/sources/dataforseo.ts +206 -0
  147. package/src/keywords/sources/free-sources.ts +294 -0
  148. package/src/keywords/sources/gsc.ts +123 -0
  149. package/src/keywords/topic-grouping.ts +327 -0
  150. package/src/keywords/types.ts +144 -0
  151. package/src/keywords/wizard.ts +457 -0
  152. package/src/loader.ts +40 -0
  153. package/src/reports/index.ts +7 -0
  154. package/src/reports/report-generator.test.ts +293 -0
  155. package/src/reports/report-generator.ts +713 -0
  156. package/src/scheduler/alerts.test.ts +458 -0
  157. package/src/scheduler/alerts.ts +328 -0
  158. package/src/scheduler/index.ts +8 -0
  159. package/src/scheduler/scheduled-audit.test.ts +377 -0
  160. package/src/scheduler/scheduled-audit.ts +149 -0
  161. package/src/test/integration-test.ts +325 -0
  162. package/src/tools/analyzer.ts +373 -0
  163. package/src/tools/crawl.ts +293 -0
  164. package/src/tools/files.ts +301 -0
  165. package/src/tools/h1-fixer.ts +249 -0
  166. package/src/tools/index.ts +67 -0
  167. package/src/tracking/github-action.ts +326 -0
  168. package/src/tracking/google-analytics.ts +265 -0
  169. package/src/tracking/index.ts +45 -0
  170. package/src/tracking/report-generator.ts +386 -0
  171. package/src/tracking/search-console.ts +335 -0
  172. package/src/types.ts +134 -0
  173. package/src/utils/http.ts +302 -0
  174. package/src/wasm-adapter.ts +297 -0
  175. package/src/wasm-entry.ts +14 -0
  176. package/tsconfig.json +17 -0
  177. package/tsup.wasm.config.ts +26 -0
  178. package/vitest.config.ts +15 -0
@@ -0,0 +1,371 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { analyzeTrackingVerification } from './tracking-verification.js';
3
+
4
+ describe('analyzeTrackingVerification', () => {
5
+ const testUrl = 'https://example.com';
6
+
7
+ describe('Google Analytics 4 detection', () => {
8
+ it('detects GA4 gtag.js tracking code', () => {
9
+ const html = `
10
+ <html>
11
+ <head>
12
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-ABC123XYZ"></script>
13
+ <script>
14
+ window.dataLayer = window.dataLayer || [];
15
+ function gtag(){dataLayer.push(arguments);}
16
+ gtag('js', new Date());
17
+ gtag('config', 'G-ABC123XYZ');
18
+ </script>
19
+ </head>
20
+ </html>
21
+ `;
22
+
23
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
24
+
25
+ expect(data.ga4.detected).toBe(true);
26
+ expect(data.ga4.measurementId).toBe('G-ABC123XYZ');
27
+
28
+ const ga4Issue = issues.find((i) => i.code === 'GA4_MISSING');
29
+ expect(ga4Issue).toBeUndefined();
30
+ });
31
+
32
+ it('detects GA4 from script src only', () => {
33
+ const html = `
34
+ <html>
35
+ <head>
36
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-TESTID123"></script>
37
+ </head>
38
+ </html>
39
+ `;
40
+
41
+ const { data } = analyzeTrackingVerification(html, testUrl);
42
+
43
+ expect(data.ga4.detected).toBe(true);
44
+ expect(data.ga4.measurementId).toBe('G-TESTID123');
45
+ });
46
+
47
+ it('reports missing GA4 when not present', () => {
48
+ const html = `<html><head></head></html>`;
49
+
50
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
51
+
52
+ expect(data.ga4.detected).toBe(false);
53
+ expect(data.ga4.measurementId).toBeUndefined();
54
+
55
+ const ga4Issue = issues.find((i) => i.code === 'GA4_MISSING');
56
+ expect(ga4Issue).toBeDefined();
57
+ expect(ga4Issue?.severity).toBe('warning');
58
+ expect(ga4Issue?.affectedUrls).toContain(testUrl);
59
+ });
60
+
61
+ it('does not report missing GA4 if GTM is present', () => {
62
+ const html = `
63
+ <html>
64
+ <head>
65
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
66
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
67
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
68
+ 'https://www.googletagmanager.com/gtm.js?id=GTM-ABC123';f.parentNode.insertBefore(j,f);
69
+ })(window,document,'script','dataLayer','GTM-ABC123');</script>
70
+ </head>
71
+ </html>
72
+ `;
73
+
74
+ const { issues } = analyzeTrackingVerification(html, testUrl);
75
+
76
+ const ga4Issue = issues.find((i) => i.code === 'GA4_MISSING');
77
+ expect(ga4Issue).toBeUndefined();
78
+ });
79
+ });
80
+
81
+ describe('Google Tag Manager detection', () => {
82
+ it('detects GTM with noscript fallback', () => {
83
+ const html = `
84
+ <html>
85
+ <head>
86
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
87
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
88
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
89
+ 'https://www.googletagmanager.com/gtm.js?id=GTM-TESTID';f.parentNode.insertBefore(j,f);
90
+ })(window,document,'script','dataLayer','GTM-TESTID');</script>
91
+ </head>
92
+ <body>
93
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TESTID"
94
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
95
+ </body>
96
+ </html>
97
+ `;
98
+
99
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
100
+
101
+ expect(data.gtm.detected).toBe(true);
102
+ expect(data.gtm.containerId).toBe('GTM-TESTID');
103
+ expect(data.gtm.hasNoscript).toBe(true);
104
+
105
+ const gtmIssue = issues.find((i) => i.code === 'GTM_MISSING');
106
+ expect(gtmIssue).toBeUndefined();
107
+
108
+ const noscriptIssue = issues.find((i) => i.code === 'GTM_NOSCRIPT_MISSING');
109
+ expect(noscriptIssue).toBeUndefined();
110
+ });
111
+
112
+ it('reports missing noscript when GTM present without it', () => {
113
+ const html = `
114
+ <html>
115
+ <head>
116
+ <script src="https://www.googletagmanager.com/gtm.js?id=GTM-ABC123"></script>
117
+ </head>
118
+ <body></body>
119
+ </html>
120
+ `;
121
+
122
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
123
+
124
+ expect(data.gtm.detected).toBe(true);
125
+ expect(data.gtm.hasNoscript).toBe(false);
126
+
127
+ const noscriptIssue = issues.find((i) => i.code === 'GTM_NOSCRIPT_MISSING');
128
+ expect(noscriptIssue).toBeDefined();
129
+ expect(noscriptIssue?.severity).toBe('notice');
130
+ });
131
+
132
+ it('reports missing GTM as informational', () => {
133
+ const html = `<html><head></head></html>`;
134
+
135
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
136
+
137
+ expect(data.gtm.detected).toBe(false);
138
+
139
+ const gtmIssue = issues.find((i) => i.code === 'GTM_MISSING');
140
+ expect(gtmIssue).toBeDefined();
141
+ expect(gtmIssue?.severity).toBe('notice');
142
+ });
143
+ });
144
+
145
+ describe('Google Search Console verification', () => {
146
+ it('detects GSC verification meta tag', () => {
147
+ const html = `
148
+ <html>
149
+ <head>
150
+ <meta name="google-site-verification" content="abc123xyz789verificationcode" />
151
+ </head>
152
+ </html>
153
+ `;
154
+
155
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
156
+
157
+ expect(data.gsc.verified).toBe(true);
158
+ expect(data.gsc.verificationCode).toBe('abc123xyz789verificationcode');
159
+
160
+ const gscIssue = issues.find((i) => i.code === 'GSC_VERIFICATION_MISSING');
161
+ expect(gscIssue).toBeUndefined();
162
+ });
163
+
164
+ it('reports missing GSC verification', () => {
165
+ const html = `<html><head></head></html>`;
166
+
167
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
168
+
169
+ expect(data.gsc.verified).toBe(false);
170
+
171
+ const gscIssue = issues.find((i) => i.code === 'GSC_VERIFICATION_MISSING');
172
+ expect(gscIssue).toBeDefined();
173
+ expect(gscIssue?.severity).toBe('warning');
174
+ });
175
+
176
+ it('rejects short verification codes as invalid', () => {
177
+ const html = `
178
+ <html>
179
+ <head>
180
+ <meta name="google-site-verification" content="short" />
181
+ </head>
182
+ </html>
183
+ `;
184
+
185
+ const { data } = analyzeTrackingVerification(html, testUrl);
186
+
187
+ expect(data.gsc.verified).toBe(false);
188
+ });
189
+ });
190
+
191
+ describe('Bing Webmaster Tools verification', () => {
192
+ it('detects Bing verification meta tag', () => {
193
+ const html = `
194
+ <html>
195
+ <head>
196
+ <meta name="msvalidate.01" content="BING12345678901234567890" />
197
+ </head>
198
+ </html>
199
+ `;
200
+
201
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
202
+
203
+ expect(data.bing.verified).toBe(true);
204
+ expect(data.bing.verificationCode).toBe('BING12345678901234567890');
205
+
206
+ const bingIssue = issues.find((i) => i.code === 'BING_VERIFICATION_MISSING');
207
+ expect(bingIssue).toBeUndefined();
208
+ });
209
+
210
+ it('reports missing Bing verification as notice', () => {
211
+ const html = `<html><head></head></html>`;
212
+
213
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
214
+
215
+ expect(data.bing.verified).toBe(false);
216
+
217
+ const bingIssue = issues.find((i) => i.code === 'BING_VERIFICATION_MISSING');
218
+ expect(bingIssue).toBeDefined();
219
+ expect(bingIssue?.severity).toBe('notice');
220
+ });
221
+ });
222
+
223
+ describe('Schema.org structured data detection', () => {
224
+ it('detects JSON-LD structured data', () => {
225
+ const html = `
226
+ <html>
227
+ <head>
228
+ <script type="application/ld+json">
229
+ {
230
+ "@context": "https://schema.org",
231
+ "@type": "Organization",
232
+ "name": "Test Company",
233
+ "url": "https://example.com"
234
+ }
235
+ </script>
236
+ </head>
237
+ </html>
238
+ `;
239
+
240
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
241
+
242
+ expect(data.schemaOrg.detected).toBe(true);
243
+ expect(data.schemaOrg.types).toContain('Organization');
244
+ expect(data.schemaOrg.hasValidJson).toBe(true);
245
+
246
+ const schemaIssue = issues.find((i) => i.code === 'SCHEMA_ORG_MISSING');
247
+ expect(schemaIssue).toBeUndefined();
248
+ });
249
+
250
+ it('detects microdata structured data', () => {
251
+ const html = `
252
+ <html>
253
+ <body>
254
+ <div itemscope itemtype="https://schema.org/Product">
255
+ <span itemprop="name">Test Product</span>
256
+ </div>
257
+ </body>
258
+ </html>
259
+ `;
260
+
261
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
262
+
263
+ expect(data.schemaOrg.detected).toBe(true);
264
+
265
+ const schemaIssue = issues.find((i) => i.code === 'SCHEMA_ORG_MISSING');
266
+ expect(schemaIssue).toBeUndefined();
267
+ });
268
+
269
+ it('reports missing structured data', () => {
270
+ const html = `<html><head></head><body></body></html>`;
271
+
272
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
273
+
274
+ expect(data.schemaOrg.detected).toBe(false);
275
+
276
+ const schemaIssue = issues.find((i) => i.code === 'SCHEMA_ORG_MISSING');
277
+ expect(schemaIssue).toBeDefined();
278
+ expect(schemaIssue?.severity).toBe('warning');
279
+ });
280
+
281
+ it('reports invalid JSON-LD', () => {
282
+ const html = `
283
+ <html>
284
+ <head>
285
+ <script type="application/ld+json">
286
+ { invalid json here }
287
+ </script>
288
+ </head>
289
+ </html>
290
+ `;
291
+
292
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
293
+
294
+ expect(data.schemaOrg.detected).toBe(true);
295
+ expect(data.schemaOrg.hasValidJson).toBe(false);
296
+
297
+ const invalidIssue = issues.find((i) => i.code === 'SCHEMA_ORG_INVALID');
298
+ expect(invalidIssue).toBeDefined();
299
+ expect(invalidIssue?.severity).toBe('error');
300
+ });
301
+
302
+ it('extracts multiple schema types', () => {
303
+ const html = `
304
+ <html>
305
+ <head>
306
+ <script type="application/ld+json">
307
+ { "@context": "https://schema.org", "@type": "Organization", "name": "Test" }
308
+ </script>
309
+ <script type="application/ld+json">
310
+ { "@context": "https://schema.org", "@type": "WebSite", "url": "https://example.com" }
311
+ </script>
312
+ </head>
313
+ </html>
314
+ `;
315
+
316
+ const { data } = analyzeTrackingVerification(html, testUrl);
317
+
318
+ expect(data.schemaOrg.types).toContain('Organization');
319
+ expect(data.schemaOrg.types).toContain('WebSite');
320
+ });
321
+ });
322
+
323
+ describe('Complete tracking analysis', () => {
324
+ it('analyzes fully configured page with all tracking', () => {
325
+ const html = `
326
+ <!DOCTYPE html>
327
+ <html lang="en">
328
+ <head>
329
+ <meta name="google-site-verification" content="googleverification123456" />
330
+ <meta name="msvalidate.01" content="bingverification123456789" />
331
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-MEASUREMENT1"></script>
332
+ <script>
333
+ window.dataLayer = window.dataLayer || [];
334
+ function gtag(){dataLayer.push(arguments);}
335
+ gtag('js', new Date());
336
+ gtag('config', 'G-MEASUREMENT1');
337
+ </script>
338
+ <script type="application/ld+json">
339
+ { "@context": "https://schema.org", "@type": "Organization", "name": "Test" }
340
+ </script>
341
+ </head>
342
+ <body></body>
343
+ </html>
344
+ `;
345
+
346
+ const { data, issues } = analyzeTrackingVerification(html, testUrl);
347
+
348
+ expect(data.ga4.detected).toBe(true);
349
+ expect(data.gsc.verified).toBe(true);
350
+ expect(data.bing.verified).toBe(true);
351
+ expect(data.schemaOrg.detected).toBe(true);
352
+
353
+ // Should only have GTM missing (which is informational)
354
+ const criticalIssues = issues.filter((i) => i.severity === 'error' || i.severity === 'warning');
355
+ expect(criticalIssues).toHaveLength(0);
356
+ });
357
+
358
+ it('analyzes page with no tracking configured', () => {
359
+ const html = `<html><head></head><body></body></html>`;
360
+
361
+ const { issues } = analyzeTrackingVerification(html, testUrl);
362
+
363
+ const issueCodes = issues.map((i) => i.code);
364
+ expect(issueCodes).toContain('GA4_MISSING');
365
+ expect(issueCodes).toContain('GSC_VERIFICATION_MISSING');
366
+ expect(issueCodes).toContain('BING_VERIFICATION_MISSING');
367
+ expect(issueCodes).toContain('GTM_MISSING');
368
+ expect(issueCodes).toContain('SCHEMA_ORG_MISSING');
369
+ });
370
+ });
371
+ });