summary property

String? get summary

The one line worth printing after a run, or null when the two runs agree about everything and there is nothing to say.

Breaks the changed count down by facet whenever anything but the pixels moved — not merely when several facets did. "51 steps differ" reads as 51 screenshots that moved, so a drift where all 51 are a settle that ran out of budget on a loaded machine has to say so in the one line anybody reads. Pixels alone need no breakdown: that is what the sentence already means.

Implementation

String? get summary {
  if (isEmpty) return null;
  var facets = byFacet;
  var pixelsOnly =
      facets.length == 1 && facets.keys.single == ScenarioDriftFacet.pixels;
  var breakdown = facets.isEmpty || pixelsOnly
      ? ''
      : ' (${facets.entries.map((e) => '${e.value} ${e.key}').join(' · ')})';
  return [
    if (changed.isNotEmpty)
      '${changed.length} of $compared steps differ$breakdown',
    if (added.isNotEmpty) '${added.length} new',
    if (removed.isNotEmpty) '${removed.length} gone',
    // Only when it is the news. An unnamed step pinned to a name above it is
    // not fragile, and warning about those — most steps in most suites —
    // would train a reader to skip the line that matters.
    if (unanchored > 0) '$unanchored matched by position alone',
    if (pixelsIgnored) 'pictures not compared (real clock)',
  ].join(' · ');
}