@operstack/audit 0.16.0 → 0.16.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operstack/audit",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "SEO, AEO and GEO audit generator: collect public signals from a site into audit.json, let an analyst or an agent fill the narrative, render the OperStack report as HTML and PDF.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/collect.mjs CHANGED
@@ -448,11 +448,6 @@ export async function collect(startUrl, { pages = 20, log = () => {}, backlinks
448
448
  const msg = pagesForTrust.filter((p) => p.messengerLinks > 0);
449
449
  checks.push(row('conv-messenger', 'conversion', 'A messenger link', msg.length ? 'ok' : 'warn', msg.length ? `on ${msg.length} of ${pagesForTrust.length} sampled page(s)` : 'none on the sampled pages', msg.length ? '' : 'for an international audience reading on a phone, one messenger link is usually worth more than a form'));
450
450
 
451
- const { scores, scoreBasis } = computeScores(checks);
452
-
453
- // Один балл на весь отчёт: его же берут страница проверки и письмо, чтобы цифры не спорили.
454
-
455
- const overall = computeOverall(checks);
456
451
  // Оценки считаются до перевода: язык на цифры не влияет.
457
452
  // Сколько текста не видно без выполнения скриптов. Меряется настоящим браузером и только по
458
453
  // просьбе: запуск браузера это десятки секунд на страницу, и еженедельному мониторингу это ни к
@@ -473,6 +468,12 @@ export async function collect(startUrl, { pages = 20, log = () => {}, backlinks
473
468
  checks.push(jsBlindnessCheck(measured, lang));
474
469
  }
475
470
 
471
+ // Балл считается ПОСЛЕ всех этапов, включая браузерный: раньше он снимался до него, и на прогоне
472
+ // с `rendered: true` сохранённое поле расходилось с тем, что печатает отчёт. Письмо говорило 61,
473
+ // PDF печатал 59. Одна функция, один набор проверок, один момент: вот этот.
474
+ const { scores, scoreBasis } = computeScores(checks);
475
+ const overall = computeOverall(checks);
476
+
476
477
  const localised = localiseChecks(checks, lang);
477
478
  const critical = checks.filter((c) => c.status === 'bad').map((c) => ({ title: c.label, text: `${c.value}${c.comment ? `. ${c.comment}` : ''}`, level: 'bad' }));
478
479
 
@@ -5,7 +5,8 @@
5
5
  * 44 из 60, то есть 73 процента. Два разных движка в одном документе читаются как выдуманные
6
6
  * цифры. Эти тесты держат единственный источник балла и запрет считать неизмеренное нулём. */
7
7
  import { readFileSync } from 'node:fs';
8
- import { computeOverall, computeScores, verifyScores, SCORE_AREAS } from './collect.mjs';
8
+ import { createServer } from 'node:http';
9
+ import { collect, computeOverall, computeScores, verifyScores, SCORE_AREAS } from './collect.mjs';
9
10
  import { toHtml } from './render.mjs';
10
11
 
11
12
  let bad = 0;
@@ -86,5 +87,25 @@ is('все спорные: половина', computeOverall(every('warn')).scor
86
87
  ok('разбивка по областям осталась под общим баллом', Object.keys(scores).every((k) => html.includes('/10') || scores[k] === null));
87
88
  }
88
89
 
90
+ // Сохранённые оценки обязаны сходиться с итоговым набором проверок того же объекта.
91
+ //
92
+ // 14.09.2026 письмо сказало 61, а PDF напечатал 59: поле снималось до браузерного этапа, а он
93
+ // дописывает проверки. Отчёт считал заново и получал другое число. Тест поднимает маленький сайт
94
+ // и сверяет то, что collect положил в объект, с тем, что даёт функция по его же checks.
95
+ {
96
+ const server = createServer((req, res) => {
97
+ if (req.url === '/robots.txt') return res.end('User-agent: *\nAllow: /\nSitemap: http://127.0.0.1:' + server.address().port + '/sitemap.xml');
98
+ if (req.url === '/sitemap.xml') { res.setHeader('content-type', 'application/xml'); return res.end(`<?xml version="1.0"?><urlset><url><loc>http://127.0.0.1:${server.address().port}/</loc></url></urlset>`); }
99
+ res.setHeader('content-type', 'text/html');
100
+ res.end('<!doctype html><html><head><title>A small test page for the audit collector</title><meta name="description" content="A page that exists only so the collector has something real to read during the test run of the suite."></head><body><h1>Test page</h1><p>Ten pounds a month, measured on our own invoices in 2026.</p><h2>One</h2><p>Text.</p><h2>Two</h2><p>Text.</p><h2>Three</h2><p>Text.</p><a href="mailto:a@b.c">write</a></body></html>');
101
+ });
102
+ await new Promise((r) => server.listen(0, '127.0.0.1', r));
103
+ const audit = await collect(`http://127.0.0.1:${server.address().port}/`, { pages: 2, log: () => {} });
104
+ server.close();
105
+ is('сохранённый общий балл равен пересчитанному по тем же проверкам', audit.overall.score, computeOverall(audit.checks).score);
106
+ is('сохранённые оценки областей тоже сходятся', audit.scores, computeScores(audit.checks).scores);
107
+ is('и собственная проверка отчёта молчит', verifyScores(audit).filter((p) => p.startsWith('overall')), []);
108
+ }
109
+
89
110
  if (bad) { console.error(`\n${bad} тест(ов) упало`); process.exit(1); }
90
111
  console.log('\nвсе тесты общего балла прошли');