diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a5dec44..051f1f98 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -109,6 +109,7 @@ jobs: node test-issue-1356-map-a11y.js node test-issue-1360-pill-letter-count.js node test-issue-1364-pill-no-clamp.js + node test-issue-1375-scope-stats-fetch.js - name: Verify proto syntax run: | diff --git a/public/analytics.js b/public/analytics.js index d6ec1c9f..a198a6bb 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -3986,7 +3986,7 @@ function destroy() { _stopRolesRefresh(); _stopScopesRefresh(); _analyticsData = if (loadingEl) loadingEl.style.display = ''; try { // Fix 4: use api() instead of raw fetch() - var data = await api('/api/scope-stats?window=' + encodeURIComponent(w), { ttl: 30000 }); + var data = await api('/scope-stats?window=' + encodeURIComponent(w), { ttl: 30000 }); if (loadingEl) loadingEl.style.display = 'none'; if (data.error) { var cardsEl2 = document.getElementById('scopes-cards'); diff --git a/test-issue-1375-scope-stats-fetch.js b/test-issue-1375-scope-stats-fetch.js new file mode 100644 index 00000000..a68b46a4 --- /dev/null +++ b/test-issue-1375-scope-stats-fetch.js @@ -0,0 +1,50 @@ +/** + * #1375 — regression(analytics): Scopes tab fetches `/api/api/scope-stats` + * (duplicate prefix) → 404 → SPA HTML → JSON.parse error. + * + * The `api()` helper already prepends `/api`. Other callers in + * public/analytics.js correctly pass `/scope-stats` style relative paths; + * the Scopes loader was the lone offender passing `/api/scope-stats`, + * producing the doubled prefix at runtime. + * + * Fix: drop the leading `/api` from the Scopes-tab call so the helper + * builds `/api/scope-stats?window=…`. + * + * Originally landed on the PR #915 branch (commit 2fd22cee) but that + * branch never merged, so the bug resurfaced in subsequent rebases. + */ +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +let passed = 0, failed = 0; +function assert(cond, msg) { + if (cond) { passed++; console.log(' ✓ ' + msg); } + else { failed++; console.error(' ✗ ' + msg); } +} + +const src = fs.readFileSync( + path.join(__dirname, 'public', 'analytics.js'), 'utf8'); + +console.log('\n=== #1375: Scopes tab scope-stats fetch path ==='); + +// Regression guard: the buggy doubled-prefix form must never reappear. +const badRe = /api\(\s*['"]\/api\/scope-stats/g; +const badMatches = src.match(badRe) || []; +assert(badMatches.length === 0, + "ZERO `api('/api/scope-stats'` occurrences in analytics.js " + + '(regression guard for doubled /api prefix)'); + +// Positive: the corrected, helper-relative form is present exactly once. +const goodRe = /api\(\s*['"]\/scope-stats/g; +const goodMatches = src.match(goodRe) || []; +assert(goodMatches.length === 1, + "Exactly one `api('/scope-stats'` call exists (the fixed loader) — " + + 'found ' + goodMatches.length); + +console.log('\n=== Summary ==='); +console.log(' Passed: ' + passed); +console.log(' Failed: ' + failed); +console.log('\n#1375 ' + (failed === 0 ? 'PASS' : 'FAIL')); +process.exit(failed === 0 ? 0 : 1);