@rankcli/agent-runtime 0.0.14 → 0.0.16
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/dist/index.d.mts +77 -393
- package/dist/index.d.ts +77 -393
- package/dist/index.js +417 -6443
- package/dist/index.mjs +296 -6313
- package/package.json +6 -1
- package/scripts/build-deno.ts +0 -134
- package/src/analyzers/core-web-vitals-analyzer.test.ts +0 -236
- package/src/analyzers/core-web-vitals-analyzer.ts +0 -557
- package/src/analyzers/geo-analyzer.test.ts +0 -310
- package/src/analyzers/geo-analyzer.ts +0 -814
- package/src/analyzers/image-optimization-analyzer.test.ts +0 -145
- package/src/analyzers/image-optimization-analyzer.ts +0 -348
- package/src/analyzers/index.ts +0 -233
- package/src/analyzers/internal-linking-analyzer.test.ts +0 -141
- package/src/analyzers/internal-linking-analyzer.ts +0 -419
- package/src/analyzers/mobile-seo-analyzer.test.ts +0 -140
- package/src/analyzers/mobile-seo-analyzer.ts +0 -455
- package/src/analyzers/security-headers-analyzer.test.ts +0 -115
- package/src/analyzers/security-headers-analyzer.ts +0 -318
- package/src/analyzers/structured-data-analyzer.test.ts +0 -210
- package/src/analyzers/structured-data-analyzer.ts +0 -590
- package/src/audit/ai/analyzer.ts +0 -347
- package/src/audit/ai/index.ts +0 -29
- package/src/audit/ai/prompts/content-analysis.ts +0 -271
- package/src/audit/ai/types.ts +0 -179
- package/src/audit/checks/additional-checks.ts +0 -439
- package/src/audit/checks/ai-citation-worthiness.ts +0 -399
- package/src/audit/checks/ai-content-structure.ts +0 -325
- package/src/audit/checks/ai-readiness.ts +0 -339
- package/src/audit/checks/anchor-text.ts +0 -179
- package/src/audit/checks/answer-conciseness.ts +0 -322
- package/src/audit/checks/asset-minification.ts +0 -270
- package/src/audit/checks/bing-optimization.ts +0 -206
- package/src/audit/checks/brand-mention-optimization.ts +0 -349
- package/src/audit/checks/caching-headers.ts +0 -305
- package/src/audit/checks/canonical-advanced.ts +0 -150
- package/src/audit/checks/canonical-domain.ts +0 -196
- package/src/audit/checks/citation-quality.ts +0 -358
- package/src/audit/checks/client-rendering.ts +0 -542
- package/src/audit/checks/color-contrast.ts +0 -342
- package/src/audit/checks/content-freshness.ts +0 -170
- package/src/audit/checks/content-science.ts +0 -589
- package/src/audit/checks/conversion-elements.ts +0 -526
- package/src/audit/checks/crawlability.ts +0 -220
- package/src/audit/checks/directory-listing.ts +0 -172
- package/src/audit/checks/dom-analysis.ts +0 -191
- package/src/audit/checks/dom-size.ts +0 -246
- package/src/audit/checks/duplicate-content.ts +0 -194
- package/src/audit/checks/eeat-signals.ts +0 -990
- package/src/audit/checks/entity-seo.ts +0 -396
- package/src/audit/checks/featured-snippet.ts +0 -473
- package/src/audit/checks/freshness-signals.ts +0 -443
- package/src/audit/checks/funnel-intent.ts +0 -463
- package/src/audit/checks/hreflang.ts +0 -174
- package/src/audit/checks/html-compliance.ts +0 -302
- package/src/audit/checks/image-dimensions.ts +0 -167
- package/src/audit/checks/images.ts +0 -160
- package/src/audit/checks/indexnow.ts +0 -275
- package/src/audit/checks/interactive-tools.ts +0 -475
- package/src/audit/checks/internal-link-graph.ts +0 -436
- package/src/audit/checks/keyword-analysis.ts +0 -239
- package/src/audit/checks/keyword-cannibalization.ts +0 -385
- package/src/audit/checks/keyword-placement.ts +0 -471
- package/src/audit/checks/links.ts +0 -273
- package/src/audit/checks/llms-txt.ts +0 -224
- package/src/audit/checks/local-seo.ts +0 -296
- package/src/audit/checks/mobile.ts +0 -167
- package/src/audit/checks/modern-images.ts +0 -226
- package/src/audit/checks/navboost-signals.ts +0 -395
- package/src/audit/checks/on-page.ts +0 -209
- package/src/audit/checks/page-resources.ts +0 -285
- package/src/audit/checks/pagination.ts +0 -180
- package/src/audit/checks/performance.ts +0 -153
- package/src/audit/checks/platform-presence.ts +0 -580
- package/src/audit/checks/redirect-analysis.ts +0 -153
- package/src/audit/checks/redirect-chain.ts +0 -389
- package/src/audit/checks/resource-hints.ts +0 -420
- package/src/audit/checks/responsive-css.ts +0 -247
- package/src/audit/checks/responsive-images.ts +0 -396
- package/src/audit/checks/review-ecosystem.ts +0 -415
- package/src/audit/checks/robots-validation.ts +0 -373
- package/src/audit/checks/security-headers.ts +0 -188
- package/src/audit/checks/security.ts +0 -144
- package/src/audit/checks/serp-preview.ts +0 -251
- package/src/audit/checks/site-maturity.ts +0 -453
- package/src/audit/checks/social-meta.test.ts +0 -275
- package/src/audit/checks/social-meta.ts +0 -134
- package/src/audit/checks/soft-404.ts +0 -151
- package/src/audit/checks/structured-data.ts +0 -238
- package/src/audit/checks/tech-detection.ts +0 -496
- package/src/audit/checks/topical-clusters.ts +0 -435
- package/src/audit/checks/tracker-bloat.ts +0 -462
- package/src/audit/checks/tracking-verification.test.ts +0 -371
- package/src/audit/checks/tracking-verification.ts +0 -636
- package/src/audit/checks/url-safety.ts +0 -682
- package/src/audit/deno-entry.ts +0 -66
- package/src/audit/discovery/index.ts +0 -15
- package/src/audit/discovery/link-crawler.ts +0 -233
- package/src/audit/discovery/repo-routes.ts +0 -347
- package/src/audit/engine.ts +0 -627
- package/src/audit/fixes/index.ts +0 -209
- package/src/audit/fixes/social-meta-fixes.test.ts +0 -329
- package/src/audit/fixes/social-meta-fixes.ts +0 -463
- package/src/audit/index.ts +0 -74
- package/src/audit/runner.test.ts +0 -299
- package/src/audit/runner.ts +0 -130
- package/src/audit/types.ts +0 -1963
- package/src/content/featured-snippet.ts +0 -367
- package/src/content/generator.test.ts +0 -534
- package/src/content/generator.ts +0 -501
- package/src/content/headline.ts +0 -317
- package/src/content/index.ts +0 -62
- package/src/content/intent.ts +0 -258
- package/src/content/keyword-density.ts +0 -349
- package/src/content/readability.ts +0 -262
- package/src/executor.ts +0 -336
- package/src/fixer/framework-fixes.test.ts +0 -489
- package/src/fixer/framework-fixes.ts +0 -5287
- package/src/fixer/index.ts +0 -6
- package/src/fixer/schemas.ts +0 -971
- package/src/fixer.ts +0 -642
- package/src/frameworks/detector.test.ts +0 -248
- package/src/frameworks/detector.ts +0 -899
- package/src/frameworks/index.ts +0 -68
- package/src/frameworks/recipes/angular.yaml +0 -171
- package/src/frameworks/recipes/astro.yaml +0 -206
- package/src/frameworks/recipes/django.yaml +0 -180
- package/src/frameworks/recipes/laravel.yaml +0 -137
- package/src/frameworks/recipes/nextjs.yaml +0 -268
- package/src/frameworks/recipes/nuxt.yaml +0 -175
- package/src/frameworks/recipes/rails.yaml +0 -188
- package/src/frameworks/recipes/react.yaml +0 -202
- package/src/frameworks/recipes/sveltekit.yaml +0 -154
- package/src/frameworks/recipes/vue.yaml +0 -137
- package/src/frameworks/recipes/wordpress.yaml +0 -209
- package/src/frameworks/suggestion-engine.ts +0 -357
- package/src/geo/geo-content.test.ts +0 -305
- package/src/geo/geo-content.ts +0 -266
- package/src/geo/geo-history.test.ts +0 -473
- package/src/geo/geo-history.ts +0 -433
- package/src/geo/geo-tracker.test.ts +0 -359
- package/src/geo/geo-tracker.ts +0 -411
- package/src/geo/index.ts +0 -11
- package/src/geo/llm-citation-checker.ts +0 -188
- package/src/git/commit-helper.test.ts +0 -261
- package/src/git/commit-helper.ts +0 -329
- package/src/git/index.ts +0 -12
- package/src/git/pr-helper.test.ts +0 -284
- package/src/git/pr-helper.ts +0 -307
- package/src/index.ts +0 -76
- package/src/keywords/ai-keyword-engine.ts +0 -1062
- package/src/keywords/ai-summarizer.ts +0 -387
- package/src/keywords/ci-mode.ts +0 -555
- package/src/keywords/engine.ts +0 -359
- package/src/keywords/index.ts +0 -151
- package/src/keywords/llm-judge.ts +0 -357
- package/src/keywords/nlp-analysis.ts +0 -706
- package/src/keywords/prioritizer.ts +0 -295
- package/src/keywords/site-crawler.ts +0 -342
- package/src/keywords/sources/autocomplete.ts +0 -139
- package/src/keywords/sources/competitive-search.ts +0 -450
- package/src/keywords/sources/competitor-analysis.ts +0 -374
- package/src/keywords/sources/dataforseo.ts +0 -206
- package/src/keywords/sources/free-sources.ts +0 -294
- package/src/keywords/sources/gsc.ts +0 -123
- package/src/keywords/topic-grouping.ts +0 -327
- package/src/keywords/types.ts +0 -144
- package/src/keywords/wizard.ts +0 -457
- package/src/loader.ts +0 -40
- package/src/ranking/index.ts +0 -5
- package/src/ranking/serp-client.ts +0 -348
- package/src/ranking/tracker.ts +0 -380
- package/src/ranking/types.ts +0 -123
- package/src/reports/index.ts +0 -7
- package/src/reports/report-generator.test.ts +0 -293
- package/src/reports/report-generator.ts +0 -713
- package/src/scheduler/alerts.test.ts +0 -458
- package/src/scheduler/alerts.ts +0 -328
- package/src/scheduler/index.ts +0 -8
- package/src/scheduler/scheduled-audit.test.ts +0 -377
- package/src/scheduler/scheduled-audit.ts +0 -149
- package/src/test/integration-test.ts +0 -325
- package/src/tools/analyzer.ts +0 -373
- package/src/tools/crawl.ts +0 -293
- package/src/tools/files.ts +0 -301
- package/src/tools/h1-fixer.ts +0 -249
- package/src/tools/index.ts +0 -67
- package/src/tracking/github-action.ts +0 -326
- package/src/tracking/google-analytics.ts +0 -265
- package/src/tracking/index.ts +0 -45
- package/src/tracking/report-generator.ts +0 -386
- package/src/tracking/search-console.ts +0 -335
- package/src/types.ts +0 -148
- package/src/utils/http.ts +0 -302
- package/src/wasm-adapter.ts +0 -297
- package/src/wasm-entry.ts +0 -14
- package/tsconfig.json +0 -17
- package/tsup.wasm.config.ts +0 -26
- package/vitest.config.ts +0 -15
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rankcli/agent-runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "RankCLI agent runtime - executes SEO audits and fixes with AI",
|
|
5
5
|
"homepage": "https://rankcli.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"!dist/wasm"
|
|
12
|
+
],
|
|
9
13
|
"exports": {
|
|
10
14
|
".": {
|
|
11
15
|
"types": "./dist/index.d.ts",
|
|
@@ -33,6 +37,7 @@
|
|
|
33
37
|
"yaml": "^2.3.0"
|
|
34
38
|
},
|
|
35
39
|
"devDependencies": {
|
|
40
|
+
"@rankcli/agent-runtime-premium": "workspace:*",
|
|
36
41
|
"@types/node": "^20.0.0",
|
|
37
42
|
"esbuild": "^0.20.0",
|
|
38
43
|
"tsup": "^8.0.0",
|
package/scripts/build-deno.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
/**
|
|
3
|
-
* Build script to create a Deno-compatible bundle of the audit engine.
|
|
4
|
-
* This bundle can be used by Supabase Edge Functions.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* pnpm build:deno # One-time build
|
|
8
|
-
* pnpm build:deno --watch # Watch mode for development
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { build, context } from 'esbuild';
|
|
12
|
-
import { writeFileSync, readFileSync, mkdirSync } from 'fs';
|
|
13
|
-
import { join } from 'path';
|
|
14
|
-
|
|
15
|
-
const OUTPUT_DIR = join(__dirname, '../../saas/supabase/functions/_shared/audit');
|
|
16
|
-
const WATCH_MODE = process.argv.includes('--watch');
|
|
17
|
-
|
|
18
|
-
const buildOptions = {
|
|
19
|
-
entryPoints: [join(__dirname, '../src/audit/deno-entry.ts')],
|
|
20
|
-
bundle: true,
|
|
21
|
-
format: 'esm' as const,
|
|
22
|
-
platform: 'neutral' as const,
|
|
23
|
-
target: 'es2022',
|
|
24
|
-
outfile: join(OUTPUT_DIR, 'engine.bundle.js'),
|
|
25
|
-
external: ['cheerio'], // Will be imported from esm.sh in Deno
|
|
26
|
-
minify: false,
|
|
27
|
-
sourcemap: false,
|
|
28
|
-
define: {
|
|
29
|
-
'process.env.NODE_ENV': '"production"',
|
|
30
|
-
},
|
|
31
|
-
banner: {
|
|
32
|
-
js: `// Auto-generated Deno-compatible audit bundle
|
|
33
|
-
// Do not edit manually - regenerate with: pnpm build:deno
|
|
34
|
-
// Generated: ${new Date().toISOString()}
|
|
35
|
-
|
|
36
|
-
// Polyfill for Node's Buffer if needed
|
|
37
|
-
if (typeof globalThis.Buffer === 'undefined') {
|
|
38
|
-
globalThis.Buffer = {
|
|
39
|
-
from: (str, encoding) => new TextEncoder().encode(str),
|
|
40
|
-
isBuffer: () => false
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
`,
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
function postProcessBundle() {
|
|
48
|
-
// Replace bare cheerio imports with esm.sh URL
|
|
49
|
-
const bundlePath = join(OUTPUT_DIR, 'engine.bundle.js');
|
|
50
|
-
let content = readFileSync(bundlePath, 'utf-8');
|
|
51
|
-
|
|
52
|
-
// Replace various import patterns for cheerio
|
|
53
|
-
content = content
|
|
54
|
-
.replace(/from\s*["']cheerio["']/g, 'from "https://esm.sh/cheerio@1.0.0"')
|
|
55
|
-
.replace(/import\s*\*\s*as\s*cheerio\s*from\s*["']cheerio["']/g,
|
|
56
|
-
'import * as cheerio from "https://esm.sh/cheerio@1.0.0"')
|
|
57
|
-
.replace(/require\s*\(\s*["']cheerio["']\s*\)/g,
|
|
58
|
-
'await import("https://esm.sh/cheerio@1.0.0")');
|
|
59
|
-
|
|
60
|
-
writeFileSync(bundlePath, content);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function writeWrapper() {
|
|
64
|
-
const wrapper = `// Deno wrapper for audit engine
|
|
65
|
-
// Re-export everything from the bundle
|
|
66
|
-
export * from './engine.bundle.js';
|
|
67
|
-
`;
|
|
68
|
-
writeFileSync(join(OUTPUT_DIR, 'index.ts'), wrapper);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function buildDenoBundle() {
|
|
72
|
-
console.log('Building Deno-compatible audit bundle...');
|
|
73
|
-
|
|
74
|
-
// Ensure output directory exists
|
|
75
|
-
mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
if (WATCH_MODE) {
|
|
79
|
-
// Watch mode using esbuild context
|
|
80
|
-
const ctx = await context({
|
|
81
|
-
...buildOptions,
|
|
82
|
-
plugins: [{
|
|
83
|
-
name: 'rebuild-notify',
|
|
84
|
-
setup(build) {
|
|
85
|
-
build.onEnd(result => {
|
|
86
|
-
if (result.errors.length > 0) {
|
|
87
|
-
console.error(`[${new Date().toLocaleTimeString()}] Deno build failed:`, result.errors);
|
|
88
|
-
} else {
|
|
89
|
-
postProcessBundle();
|
|
90
|
-
writeWrapper();
|
|
91
|
-
console.log(`[${new Date().toLocaleTimeString()}] ✓ Deno bundle rebuilt`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
},
|
|
95
|
-
}],
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
await ctx.watch();
|
|
99
|
-
console.log('Watching for changes in audit module...');
|
|
100
|
-
console.log('Press Ctrl+C to stop.\n');
|
|
101
|
-
|
|
102
|
-
// Keep the process running
|
|
103
|
-
process.on('SIGINT', async () => {
|
|
104
|
-
await ctx.dispose();
|
|
105
|
-
console.log('\nStopped watching.');
|
|
106
|
-
process.exit(0);
|
|
107
|
-
});
|
|
108
|
-
} else {
|
|
109
|
-
// One-time build
|
|
110
|
-
const result = await build(buildOptions);
|
|
111
|
-
|
|
112
|
-
if (result.errors.length > 0) {
|
|
113
|
-
console.error('Build errors:', result.errors);
|
|
114
|
-
process.exit(1);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
console.log(`✓ Bundle created at ${OUTPUT_DIR}/engine.bundle.js`);
|
|
118
|
-
|
|
119
|
-
postProcessBundle();
|
|
120
|
-
console.log(`✓ Cheerio imports replaced with esm.sh URLs`);
|
|
121
|
-
|
|
122
|
-
writeWrapper();
|
|
123
|
-
console.log(`✓ Wrapper created at ${OUTPUT_DIR}/index.ts`);
|
|
124
|
-
|
|
125
|
-
console.log('\nDeno bundle ready for use in edge functions!');
|
|
126
|
-
console.log('Import with: import { runFullAudit } from "../_shared/audit/index.ts"');
|
|
127
|
-
}
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error('Build failed:', error);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
buildDenoBundle();
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { analyzeCoreWebVitals } from './core-web-vitals-analyzer.js';
|
|
3
|
-
|
|
4
|
-
describe('Core Web Vitals Analyzer', () => {
|
|
5
|
-
describe('LCP Analysis', () => {
|
|
6
|
-
it('detects lazy-loaded hero images', () => {
|
|
7
|
-
const html = `
|
|
8
|
-
<!DOCTYPE html>
|
|
9
|
-
<html>
|
|
10
|
-
<head><title>Test</title></head>
|
|
11
|
-
<body>
|
|
12
|
-
<header>
|
|
13
|
-
<img src="/hero.jpg" loading="lazy" width="1200" height="600">
|
|
14
|
-
</header>
|
|
15
|
-
</body>
|
|
16
|
-
</html>
|
|
17
|
-
`;
|
|
18
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
19
|
-
expect(result.lcp.issues.some(i => i.includes('lazy'))).toBe(true);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('detects render-blocking scripts', () => {
|
|
23
|
-
const html = `
|
|
24
|
-
<!DOCTYPE html>
|
|
25
|
-
<html>
|
|
26
|
-
<head>
|
|
27
|
-
<script src="/app.js"></script>
|
|
28
|
-
<script src="/vendor.js"></script>
|
|
29
|
-
</head>
|
|
30
|
-
<body><h1>Test</h1></body>
|
|
31
|
-
</html>
|
|
32
|
-
`;
|
|
33
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
34
|
-
expect(result.lcp.issues.some(i => i.includes('render-blocking scripts'))).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('accepts async/defer scripts', () => {
|
|
38
|
-
const html = `
|
|
39
|
-
<!DOCTYPE html>
|
|
40
|
-
<html>
|
|
41
|
-
<head>
|
|
42
|
-
<script src="/app.js" async></script>
|
|
43
|
-
<script src="/analytics.js" defer></script>
|
|
44
|
-
</head>
|
|
45
|
-
<body><h1>Test</h1></body>
|
|
46
|
-
</html>
|
|
47
|
-
`;
|
|
48
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
49
|
-
expect(result.lcp.issues.some(i => i.includes('render-blocking scripts'))).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('detects multiple render-blocking stylesheets', () => {
|
|
53
|
-
const html = `
|
|
54
|
-
<!DOCTYPE html>
|
|
55
|
-
<html>
|
|
56
|
-
<head>
|
|
57
|
-
<link rel="stylesheet" href="/style1.css">
|
|
58
|
-
<link rel="stylesheet" href="/style2.css">
|
|
59
|
-
<link rel="stylesheet" href="/style3.css">
|
|
60
|
-
<link rel="stylesheet" href="/style4.css">
|
|
61
|
-
</head>
|
|
62
|
-
<body><h1>Test</h1></body>
|
|
63
|
-
</html>
|
|
64
|
-
`;
|
|
65
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
66
|
-
expect(result.lcp.issues.some(i => i.includes('render-blocking stylesheets'))).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('CLS Analysis', () => {
|
|
71
|
-
it('detects images without dimensions', () => {
|
|
72
|
-
const html = `
|
|
73
|
-
<!DOCTYPE html>
|
|
74
|
-
<html>
|
|
75
|
-
<body>
|
|
76
|
-
<img src="/photo1.jpg">
|
|
77
|
-
<img src="/photo2.jpg">
|
|
78
|
-
</body>
|
|
79
|
-
</html>
|
|
80
|
-
`;
|
|
81
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
82
|
-
expect(result.cls.issues.some(i => i.includes('without width/height'))).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('passes images with dimensions', () => {
|
|
86
|
-
const html = `
|
|
87
|
-
<!DOCTYPE html>
|
|
88
|
-
<html>
|
|
89
|
-
<body>
|
|
90
|
-
<img src="/photo.jpg" width="800" height="600">
|
|
91
|
-
</body>
|
|
92
|
-
</html>
|
|
93
|
-
`;
|
|
94
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
95
|
-
expect(result.cls.issues.some(i => i.includes('without width/height'))).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('detects iframes and videos without dimensions', () => {
|
|
99
|
-
const html = `
|
|
100
|
-
<!DOCTYPE html>
|
|
101
|
-
<html>
|
|
102
|
-
<body>
|
|
103
|
-
<iframe src="https://youtube.com/embed/123"></iframe>
|
|
104
|
-
<video src="/video.mp4"></video>
|
|
105
|
-
</body>
|
|
106
|
-
</html>
|
|
107
|
-
`;
|
|
108
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
109
|
-
expect(result.cls.issues.some(i => i.includes('videos/iframes without dimensions'))).toBe(true);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('detects web fonts without font-display', () => {
|
|
113
|
-
const html = `
|
|
114
|
-
<!DOCTYPE html>
|
|
115
|
-
<html>
|
|
116
|
-
<head>
|
|
117
|
-
<link href="https://fonts.googleapis.com/css2?family=Open+Sans" rel="stylesheet">
|
|
118
|
-
</head>
|
|
119
|
-
<body><h1>Test</h1></body>
|
|
120
|
-
</html>
|
|
121
|
-
`;
|
|
122
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
123
|
-
expect(result.cls.issues.some(i => i.includes('font-display'))).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
describe('FID/INP Analysis', () => {
|
|
128
|
-
it('detects many JavaScript bundles', () => {
|
|
129
|
-
const html = `
|
|
130
|
-
<!DOCTYPE html>
|
|
131
|
-
<html>
|
|
132
|
-
<head></head>
|
|
133
|
-
<body>
|
|
134
|
-
<script src="/bundle.1.js"></script>
|
|
135
|
-
<script src="/bundle.2.js"></script>
|
|
136
|
-
<script src="/vendor.bundle.js"></script>
|
|
137
|
-
<script src="/chunk.1.js"></script>
|
|
138
|
-
<script src="/chunk.2.js"></script>
|
|
139
|
-
<script src="/chunk.3.js"></script>
|
|
140
|
-
</body>
|
|
141
|
-
</html>
|
|
142
|
-
`;
|
|
143
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
144
|
-
expect(result.fid.issues.some(i => i.includes('bundle files'))).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('detects large inline JavaScript', () => {
|
|
148
|
-
const largeScript = 'x'.repeat(15000);
|
|
149
|
-
const html = `
|
|
150
|
-
<!DOCTYPE html>
|
|
151
|
-
<html>
|
|
152
|
-
<body>
|
|
153
|
-
<script>${largeScript}</script>
|
|
154
|
-
</body>
|
|
155
|
-
</html>
|
|
156
|
-
`;
|
|
157
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
158
|
-
expect(result.fid.issues.some(i => i.includes('Large inline JavaScript'))).toBe(true);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('detects many inline event handlers', () => {
|
|
162
|
-
const handlers = Array(15).fill('<button onclick="doSomething()">Click</button>').join('');
|
|
163
|
-
const html = `
|
|
164
|
-
<!DOCTYPE html>
|
|
165
|
-
<html>
|
|
166
|
-
<body>${handlers}</body>
|
|
167
|
-
</html>
|
|
168
|
-
`;
|
|
169
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
170
|
-
expect(result.fid.issues.some(i => i.includes('inline event handlers'))).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('TTFB Analysis', () => {
|
|
175
|
-
it('detects missing preconnect hints', () => {
|
|
176
|
-
const html = `
|
|
177
|
-
<!DOCTYPE html>
|
|
178
|
-
<html>
|
|
179
|
-
<head><title>Test</title></head>
|
|
180
|
-
<body><h1>Test</h1></body>
|
|
181
|
-
</html>
|
|
182
|
-
`;
|
|
183
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
184
|
-
expect(result.ttfb.issues.some(i => i.includes('preconnect'))).toBe(true);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('passes with preconnect hints', () => {
|
|
188
|
-
const html = `
|
|
189
|
-
<!DOCTYPE html>
|
|
190
|
-
<html>
|
|
191
|
-
<head>
|
|
192
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
193
|
-
<link rel="dns-prefetch" href="https://api.example.com">
|
|
194
|
-
</head>
|
|
195
|
-
<body><h1>Test</h1></body>
|
|
196
|
-
</html>
|
|
197
|
-
`;
|
|
198
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
199
|
-
expect(result.ttfb.issues.some(i => i.includes('preconnect'))).toBe(false);
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
describe('Overall Score', () => {
|
|
204
|
-
it('calculates overall score', () => {
|
|
205
|
-
const html = `
|
|
206
|
-
<!DOCTYPE html>
|
|
207
|
-
<html>
|
|
208
|
-
<head>
|
|
209
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
210
|
-
</head>
|
|
211
|
-
<body>
|
|
212
|
-
<h1>Well Optimized Page</h1>
|
|
213
|
-
<img src="/photo.jpg" width="800" height="600">
|
|
214
|
-
</body>
|
|
215
|
-
</html>
|
|
216
|
-
`;
|
|
217
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
218
|
-
expect(result.overallScore).toBeGreaterThan(0);
|
|
219
|
-
expect(result.overallScore).toBeLessThanOrEqual(100);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('generates issues array', () => {
|
|
223
|
-
const html = `
|
|
224
|
-
<!DOCTYPE html>
|
|
225
|
-
<html>
|
|
226
|
-
<body>
|
|
227
|
-
<img src="/photo.jpg">
|
|
228
|
-
<script src="/app.js"></script>
|
|
229
|
-
</body>
|
|
230
|
-
</html>
|
|
231
|
-
`;
|
|
232
|
-
const result = analyzeCoreWebVitals(html, 'https://example.com');
|
|
233
|
-
expect(Array.isArray(result.issues)).toBe(true);
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|