testing library

The half of the audit that needs Flutter: does the app actually render what the route table promises?

Everything in package:esen_seo/audit.dart reads the route table, so it can only ever confirm that the table agrees with itself. This library compares it against the widget tree a visitor sees — the one question that matters most, because serving crawlers a separately built body is only defensible while the two say the same thing.

Import it from a widget test:

import 'package:esen_seo/testing.dart';

testWidgets('bots and users see the same pages', (tester) async {
  final report = await auditSeoParity(
    routes: seoRoutes,
    siteBase: siteBase,
    paths: const ['/', '/docs'],
    pump: (path) async {
      await tester.pumpWidget(MyApp(initialRoute: path));
      await tester.pumpAndSettle();
    },
  );
  expect(report.passes(), isTrue, reason: '\n${report.describe()}');
});

This library also carries the theme bridge's drift guard, checkOrUpdateSeoThemeCss. The guard needs dart:io, so it is exported conditionally: on the host VM (the normal place for it) you get the real one; compiled for the web — someone running their parity suite with --platform chrome — the entry point still compiles, and only actually calling the guard throws. An unconditional export would have made every consumer of this library VM-only for the sake of one function they may never use.

Kept out of esen_seo.dart on purpose: this is test-time scaffolding and has no business in an app's release build.

Classes

SeoAuditPolicy
What the audit considers a problem.
SeoAuditReport
The result of one audit run.
SeoFinding
One problem, on one page.
SeoParityPolicy
How strict the comparison is.

Enums

SeoSeverity
How much a finding matters.

Extension Types

SeoCheck
The identity of a check, e.g. title.missing.

Functions

assertSeoHealthy(SeoAuditReport report, {SeoSeverity threshold = SeoSeverity.error}) → void
Throws SeoAuditFailure unless report passes at threshold.
auditSeoPages({required List<SeoResolvedPage> pages, required List<SeoRoute> routes, required String siteBase, SeoAuditPolicy policy = const SeoAuditPolicy(), Map<String, Object> resolverFailures = const {}}) SeoAuditReport
Audits an already-resolved set of pages.
auditSeoParity({required List<SeoRoute> routes, required String siteBase, required List<String> paths, required Future<void> pump(String path), SeoParityPolicy policy = const SeoParityPolicy(), List<String> additionalPaths = const []}) Future<SeoAuditReport>
Checks that the app renders what the route table promises.
auditSeoRoutes({required List<SeoRoute> routes, required String siteBase, List<String> additionalPaths = const [], SeoAuditPolicy policy = const SeoAuditPolicy()}) Future<SeoAuditReport>
Audits a route table for the mistakes the package cannot prevent.
captureSeoNodes() List<SeoNode>
Captures the semantic mirror of whatever is currently mounted.
checkOrUpdateSeoThemeCss(String css, {String path = 'lib/seo_theme.g.dart', String variable = 'seoThemeCss', bool? update}) → void
See theme_guard.dart — this platform cannot run the guard.
compareSeoTrees({required String path, required List<SeoNode> ssr, required List<SeoNode> app, SeoParityPolicy policy = const SeoParityPolicy()}) List<SeoFinding>
Compares the server-rendered ssr body against the app mirror.
enableSeoForParity() → void
Prepares the SEO pipeline for a parity test.

Exceptions / Errors

SeoAuditFailure
Thrown by assertSeoHealthy when a report does not pass.