Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | export interface GotoStep {
goto: string;
}
export interface ClickStep {
click: {
selector: string;
};
}
export interface TypeStep {
type: {
selector: string;
text: string;
};
}
export interface RetryStep {
// Runs `steps`, then waits for `until`; if it never shows, reloads the page and tries again (flaky org widgets).
retry: {
steps: Step[];
until: string;
timeout?: number;
attempts?: number;
};
}
export interface BlurStep {
// Moves focus off the element, closing focus-driven popups such as a date picker.
blur: {
selector: string;
};
}
export interface WaitStep {
wait: number;
}
export interface WaitForSelectorStep {
waitForSelector: {
selector: string;
timeout?: number;
};
}
export interface ScreenshotStep {
screenshot: string;
// Full-page scroll-and-stitch capture (vs viewport-only), so tall gallery pages aren't cut off. Defaults to false.
fullPage?: boolean;
// SLDS components visible on this screen, by plain name; shown under the screenshot in the report.
components?: string[];
// For transient UI (e.g. toasts): before each A/B shot, reload the page and replay these steps so both shots see the state.
replay?: Step[];
}
export interface StyleStep {
style: string;
}
export interface CssFileStep {
cssFile: {
path: string;
aggressive?: boolean;
};
}
export interface DismissStep {
// Best-effort dismissal of overlays that would occlude components; never throws since dialogs appear inconsistently.
dismiss: {
// CSS selectors to try; each visible match is clicked (e.g. close buttons).
selectors?: string[];
// Press Escape first — closes most SLDS modals without a selector.
pressEscape?: boolean;
};
}
export type Step =
| GotoStep
| ClickStep
| BlurStep
| RetryStep
| TypeStep
| WaitStep
| WaitForSelectorStep
| ScreenshotStep
| StyleStep
| CssFileStep
| DismissStep;
export interface Flow {
name: string;
steps: Step[];
}
export interface RunnerConfig {
baseUrl: string;
headless?: boolean;
screenshotDir?: string;
}
export interface ScreenshotResult {
name: string;
path: string;
timestamp: Date;
flowName?: string;
}
// A (baseline) and B (candidate) are injected into the SAME page load, so anything besides the CSS cancels out of the diff — the core idea of differential VRT.
export interface DiffConfig {
cssAPath: string;
cssBPath: string;
// Labels purely for logging / output filenames.
labelA?: string;
labelB?: string;
// Tier-2 input: selectors the PR's CSS changed; runner checks which match DOM elements per page. Optional.
changedSelectors?: string[];
// Changed selector -> the props the PR declares on it; lets authored geometry changes count as direct.
changedRules?: Record<string, string[]>;
}
// One screenshot point, captured twice (A and B) and diffed.
export interface DiffScreenshotResult {
name: string;
flowName?: string;
shotAPath: string;
shotBPath: string;
diffPath: string | null;
changedPixels: number;
totalPixels: number;
// changedPixels / totalPixels (0..1); CSS-attributable change, >0 means bundles render this page differently.
ratio: number;
// True when A/B dimensions differ (page reflowed); pixel diff isn't meaningful then, flag for human review.
dimensionMismatch?: { a: [number, number]; b: [number, number] };
// Tier-2: changed selectors that actually matched elements on this page, with match counts. Empty/undefined otherwise.
matchedSelectors?: { selector: string; count: number }[];
// Components the flow author lists as visible on this screen (see ScreenshotStep.components).
components?: string[];
// Computed-style diff between A and B; absent when not collected.
styleDiff?: StyleDiff;
// Pixels differ but SLDS can't be the cause (see isNoise in differ.ts).
noise?: boolean;
noiseReason?: 'no-css' | 'unreachable' | 'style';
// Set when the page wasn't captured: its own step error, or the earlier failed page it depends on.
missed?: { error?: string; dependsOn?: string };
}
// One element whose computed style differs between bundle A and B on the same page load.
export interface StyleChange {
element: string;
components: string[];
props: { prop: string; a: string; b: string }[];
more: number;
// The PR's changed selectors this element matches.
rules?: string[];
}
// Computed-style evidence that the CSS (not rendering noise) moved pixels.
export interface StyleDiff {
// False when A/B DOMs could not be aligned (e.g. replayed pages); callers fall back to pixels.
comparable: boolean;
// Elements with at least one changed computed property.
total: number;
// Elements with a direct (non size/position) change; these are listed and attributed.
listed: number;
// Elements that only moved or resized as a knock-on effect of a direct change.
shifted: number;
// Declared-component names whose elements changed.
components: string[];
// First changed elements, capped.
elements: StyleChange[];
}
|