fix flaky reports tests (#6193)

* add isTestEnv hook

* disable all recharts animations in test env

* note

* drop all reports snapshots

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6193

* disable animation instead of zeroing duration

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Matt Fiddaman
2025-11-18 17:35:49 +00:00
committed by GitHub
parent 7a4ec20bac
commit f3419a4ee2
45 changed files with 109 additions and 31 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -15,7 +15,6 @@ import { styles } from '@actual-app/components/styles';
import { View } from '@actual-app/components/view';
import { init as initConnection, send } from 'loot-core/platform/client/fetch';
import * as Platform from 'loot-core/shared/platform';
import { AppBackground } from './AppBackground';
import { BudgetMonthCountProvider } from './budget/BudgetMonthCountContext';
@@ -33,6 +32,7 @@ import {
loadBudget,
} from '@desktop-client/budgetfiles/budgetfilesSlice';
import { handleGlobalEvents } from '@desktop-client/global-events';
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
import { SpreadsheetProvider } from '@desktop-client/hooks/useSpreadsheet';
import { setI18NextLanguage } from '@desktop-client/i18n';
@@ -171,6 +171,7 @@ function ErrorFallback({ error }: FallbackProps) {
export function App() {
const store = useStore();
const isTestEnv = useIsTestEnv();
useEffect(() => handleGlobalEvents(store), [store]);
@@ -233,8 +234,9 @@ export function App() {
}}
>
<ErrorBoundary FallbackComponent={ErrorFallback}>
{process.env.REACT_APP_REVIEW_ID &&
!Platform.isPlaywright && <DevelopmentTopBar />}
{process.env.REACT_APP_REVIEW_ID && !isTestEnv && (
<DevelopmentTopBar />
)}
<AppInner />
</ErrorBoundary>
<ThemeStyle />

View File

@@ -35,6 +35,7 @@ import { ThemeSelector } from './ThemeSelector';
import { sync } from '@desktop-client/app/appSlice';
import { useGlobalPref } from '@desktop-client/hooks/useGlobalPref';
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
import { useNavigate } from '@desktop-client/hooks/useNavigate';
import { useSheetValue } from '@desktop-client/hooks/useSheetValue';
@@ -276,6 +277,7 @@ export function Titlebar({ style }: TitlebarProps) {
const { isNarrowWidth } = useResponsive();
const serverURL = useServerURL();
const [floatingSidebar] = useGlobalPref('floatingSidebar');
const isTestEnv = useIsTestEnv();
return isNarrowWidth ? null : (
<View
@@ -343,9 +345,7 @@ export function Titlebar({ style }: TitlebarProps) {
<View style={{ flex: 1 }} />
<SpaceBetween gap={10}>
<UncategorizedButton />
{isDevelopmentEnvironment() && !Platform.isPlaywright && (
<ThemeSelector />
)}
{isDevelopmentEnvironment() && !isTestEnv && <ThemeSelector />}
<PrivacyButton />
{serverURL ? <SyncButton /> : null}
<LoggedInUser />

View File

@@ -19,6 +19,7 @@ import * as monthUtils from 'loot-core/shared/months';
import { integerToCurrency } from 'loot-core/shared/util';
import { PrivacyFilter } from '@desktop-client/components/PrivacyFilter';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { LoadingIndicator } from '@desktop-client/components/reports/LoadingIndicator';
import { useLocale } from '@desktop-client/hooks/useLocale';
import * as query from '@desktop-client/queries';
@@ -38,6 +39,7 @@ export function BalanceHistoryGraph({
ref,
}: BalanceHistoryGraphProps) {
const locale = useLocale();
const animationProps = useRechartsAnimation({ isAnimationActive: false });
const [balanceData, setBalanceData] = useState<
Array<{ date: string; balance: number }>
>([]);
@@ -281,7 +283,7 @@ export function BalanceHistoryGraph({
dataKey="balance"
stroke={color}
strokeWidth={2}
animationDuration={0}
{...animationProps}
fill={
color === theme.noticeTextLight
? 'url(#fillLight)'

View File

@@ -26,8 +26,7 @@ import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
import { useDrag } from '@use-gesture/react';
import * as Platform from 'loot-core/shared/platform';
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
import { useScrollListener } from '@desktop-client/hooks/useScrollListener';
import { useSyncServerStatus } from '@desktop-client/hooks/useSyncServerStatus';
@@ -45,8 +44,8 @@ export function MobileNavTabs() {
const { t } = useTranslation();
const { isNarrowWidth } = useResponsive();
const syncServerStatus = useSyncServerStatus();
const isUsingServer =
syncServerStatus !== 'no-server' || Platform.isPlaywright;
const isTestEnv = useIsTestEnv();
const isUsingServer = syncServerStatus !== 'no-server' || isTestEnv;
const [navbarState, setNavbarState] = useState<'default' | 'open' | 'hidden'>(
'default',
);

View File

@@ -1,5 +1,7 @@
import { theme } from '@actual-app/components/theme';
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
const colorFades = {
blueFadeStart: 'rgba(229, 245, 255, 1)',
blueFadeEnd: 'rgba(229, 245, 255, 0)',
@@ -135,3 +137,22 @@ export function getColorScale(name: string): string[] {
};
return name ? scales[name] : scales.grayscale;
}
export function useRechartsAnimation(defaults?: {
animationDuration?: number;
isAnimationActive?: boolean;
}) {
const isTestEnv = useIsTestEnv();
if (isTestEnv) {
return {
isAnimationActive: false,
animationDuration: 0,
};
}
return {
isAnimationActive: defaults?.isAnimationActive ?? true,
animationDuration: defaults?.animationDuration,
};
}

View File

@@ -23,6 +23,7 @@ import {
import { adjustTextSize } from './adjustTextSize';
import { renderCustomLabel } from './renderCustomLabel';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { type FormatType, useFormat } from '@desktop-client/hooks/useFormat';
import { usePrivacyMode } from '@desktop-client/hooks/usePrivacyMode';
@@ -161,6 +162,7 @@ export function AreaGraph({
showTooltip = true,
}: AreaGraphProps) {
const format = useFormat();
const animationProps = useRechartsAnimation({ isAnimationActive: false });
const privacyMode = usePrivacyMode();
const dataMax = Math.max(...data.intervalData.map(i => i[balanceTypeOp]));
@@ -303,7 +305,7 @@ export function AreaGraph({
type="linear"
dot={false}
activeDot={false}
animationDuration={0}
{...animationProps}
dataKey={balanceTypeOp}
stroke={`url(#stroke${balanceTypeOp})`}
fill={`url(#fill${balanceTypeOp})`}

View File

@@ -27,6 +27,7 @@ import { adjustTextSize } from './adjustTextSize';
import { renderCustomLabel } from './renderCustomLabel';
import { showActivity } from './showActivity';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { getCustomTick } from '@desktop-client/components/reports/getCustomTick';
import { numberFormatterTooltip } from '@desktop-client/components/reports/numberFormatter';
@@ -173,6 +174,7 @@ export function BarGraph({
showOffBudget,
showTooltip = true,
}: BarGraphProps) {
const animationProps = useRechartsAnimation();
const navigate = useNavigate();
const categories = useCategories();
const accounts = useAccounts();
@@ -270,6 +272,7 @@ export function BarGraph({
<Bar
dataKey={val => getVal(val)}
stackId="a"
{...animationProps}
onMouseLeave={() => setPointer('')}
onMouseEnter={() =>
!['Group', 'Interval'].includes(groupBy) &&

View File

@@ -16,6 +16,7 @@ import {
} from 'recharts';
import { PrivacyFilter } from '@desktop-client/components/PrivacyFilter';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { numberFormatterTooltip } from '@desktop-client/components/reports/numberFormatter';
import { useFormat } from '@desktop-client/hooks/useFormat';
@@ -88,6 +89,7 @@ export function BarLineGraph({
showTooltip = true,
}: BarLineGraphProps) {
const format = useFormat();
const animationProps = useRechartsAnimation();
const tickFormatter = tick => {
return `${format(Math.round(tick), 'financial')}`; // Formats the tick values as strings with commas
};
@@ -120,8 +122,18 @@ export function BarLineGraph({
{!compact && <CartesianGrid strokeDasharray="3 3" />}
{!compact && <XAxis dataKey="x" />}
{!compact && <YAxis dataKey="y" tickFormatter={tickFormatter} />}
<Bar type="monotone" dataKey="y" fill="#8884d8" />
<Line type="monotone" dataKey="y" stroke="#8884d8" />
<Bar
type="monotone"
dataKey="y"
fill="#8884d8"
{...animationProps}
/>
<Line
type="monotone"
dataKey="y"
stroke="#8884d8"
{...animationProps}
/>
</ComposedChart>
</div>
)

View File

@@ -16,7 +16,10 @@ import {
YAxis,
} from 'recharts';
import { chartTheme } from '@desktop-client/components/reports/chart-theme';
import {
chartTheme,
useRechartsAnimation,
} from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { type FormatType, useFormat } from '@desktop-client/hooks/useFormat';
import { useLocale } from '@desktop-client/hooks/useLocale';
@@ -130,6 +133,9 @@ export function CashFlowGraph({
const privacyMode = usePrivacyMode();
const [yAxisIsHovered, setYAxisIsHovered] = useState(false);
const format = useFormat();
const animationProps = useRechartsAnimation({
animationDuration: ANIMATION_DURATION,
});
const data = graphData.expenses.map((row, idx) => ({
date: row.x,
@@ -189,14 +195,14 @@ export function CashFlowGraph({
stackId="a"
fill={chartTheme.colors.blue}
maxBarSize={MAX_BAR_SIZE}
animationDuration={ANIMATION_DURATION}
{...animationProps}
/>
<Bar
dataKey="expenses"
stackId="a"
fill={chartTheme.colors.red}
maxBarSize={MAX_BAR_SIZE}
animationDuration={ANIMATION_DURATION}
{...animationProps}
/>
<Line
type="monotone"
@@ -205,7 +211,7 @@ export function CashFlowGraph({
hide={!showBalance}
stroke={theme.pageTextLight}
strokeWidth={2}
animationDuration={ANIMATION_DURATION}
{...animationProps}
/>
</ComposedChart>
)}

View File

@@ -15,6 +15,7 @@ import {
ReferenceLine,
} from 'recharts';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { useFormat } from '@desktop-client/hooks/useFormat';
import { usePrivacyMode } from '@desktop-client/hooks/usePrivacyMode';
@@ -45,6 +46,7 @@ export function CrossoverGraph({
const { t } = useTranslation();
const privacyMode = usePrivacyMode();
const format = useFormat();
const animationProps = useRechartsAnimation({ isAnimationActive: false });
const tickFormatter = (tick: number) => {
if (privacyMode) {
@@ -176,7 +178,7 @@ export function CrossoverGraph({
dot={false}
stroke={theme.reportsBlue}
strokeWidth={2}
animationDuration={0}
{...animationProps}
/>
<Line
type="monotone"
@@ -184,7 +186,7 @@ export function CrossoverGraph({
dot={false}
stroke={theme.reportsRed}
strokeWidth={2}
animationDuration={0}
{...animationProps}
/>
</LineChart>
</div>

View File

@@ -17,6 +17,7 @@ import { renderCustomLabel } from './renderCustomLabel';
import { showActivity } from './showActivity';
import { PrivacyFilter } from '@desktop-client/components/PrivacyFilter';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { useAccounts } from '@desktop-client/hooks/useAccounts';
import { useCategories } from '@desktop-client/hooks/useCategories';
@@ -238,6 +239,7 @@ export function DonutGraph({
showTooltip = true,
}: DonutGraphProps) {
const format = useFormat();
const animationProps = useRechartsAnimation({ isAnimationActive: false });
const yAxis = groupBy === 'Interval' ? 'date' : 'name';
const splitData = groupBy === 'Interval' ? 'intervalData' : 'data';
@@ -289,7 +291,7 @@ export function DonutGraph({
}
dataKey={val => getVal(val)}
nameKey={yAxis}
isAnimationActive={false}
{...animationProps}
data={
data[splitData]?.map(item => ({
...item,

View File

@@ -22,6 +22,7 @@ import {
import { showActivity } from './showActivity';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { getCustomTick } from '@desktop-client/components/reports/getCustomTick';
import { numberFormatterTooltip } from '@desktop-client/components/reports/numberFormatter';
@@ -147,6 +148,7 @@ export function LineGraph({
showTooltip = true,
interval,
}: LineGraphProps) {
const animationProps = useRechartsAnimation();
const navigate = useNavigate();
const categories = useCategories();
const accounts = useAccounts();
@@ -241,6 +243,7 @@ export function LineGraph({
type="monotone"
dataKey={entry.name}
stroke={entry.color}
{...animationProps}
activeDot={{
r: entry.name === tooltip && !compact ? 8 : 3,
onMouseEnter: () => {

View File

@@ -18,6 +18,7 @@ import {
import { computePadding } from './util/computePadding';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { numberFormatterTooltip } from '@desktop-client/components/reports/numberFormatter';
import { useFormat } from '@desktop-client/hooks/useFormat';
@@ -55,6 +56,7 @@ export function NetWorthGraph({
const privacyMode = usePrivacyMode();
const id = useId();
const format = useFormat();
const animationProps = useRechartsAnimation({ isAnimationActive: false });
// Use more aggressive smoothening for high-frequency data
const interpolationType =
@@ -227,7 +229,7 @@ export function NetWorthGraph({
type={interpolationType}
dot={false}
activeDot={false}
animationDuration={0}
{...animationProps}
dataKey="y"
stroke={theme.reportsBlue}
strokeWidth={2}

View File

@@ -18,6 +18,7 @@ import { type SpendingEntity } from 'loot-core/types/models';
import { computePadding } from './util/computePadding';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { numberFormatterTooltip } from '@desktop-client/components/reports/numberFormatter';
import { useFormat, type FormatType } from '@desktop-client/hooks/useFormat';
@@ -148,6 +149,7 @@ export function SpendingGraph({
compareTo,
}: SpendingGraphProps) {
const privacyMode = usePrivacyMode();
const animationProps = useRechartsAnimation({ isAnimationActive: false });
const balanceTypeOp = 'cumulative';
const format = useFormat();
@@ -302,7 +304,7 @@ export function SpendingGraph({
fillOpacity: 1,
r: 10,
}}
animationDuration={0}
{...animationProps}
dataKey={val => getVal(val, compare)}
stroke={`url(#stroke${balanceTypeOp})`}
strokeWidth={3}
@@ -313,7 +315,7 @@ export function SpendingGraph({
type="linear"
dot={false}
activeDot={false}
animationDuration={0}
{...animationProps}
dataKey={val => getVal(val, selection)}
stroke={theme.reportsGray}
strokeDasharray="10 10"

View File

@@ -24,6 +24,7 @@ import {
import { renderCustomLabel } from './renderCustomLabel';
import { showActivity } from './showActivity';
import { useRechartsAnimation } from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { getCustomTick } from '@desktop-client/components/reports/getCustomTick';
import { numberFormatterTooltip } from '@desktop-client/components/reports/numberFormatter';
@@ -178,6 +179,7 @@ export function StackedBarGraph({
showTooltip = true,
interval,
}: StackedBarGraphProps) {
const animationProps = useRechartsAnimation();
const navigate = useNavigate();
const categories = useCategories();
const accounts = useAccounts();
@@ -257,6 +259,7 @@ export function StackedBarGraph({
dataKey={entry.name}
stackId="a"
fill={entry.color}
{...animationProps}
onMouseLeave={() => {
setPointer('');
setTooltip('');

View File

@@ -19,7 +19,10 @@ import { defaultTimeFrame } from './CashFlow';
import { PrivacyFilter } from '@desktop-client/components/PrivacyFilter';
import { Change } from '@desktop-client/components/reports/Change';
import { chartTheme } from '@desktop-client/components/reports/chart-theme';
import {
chartTheme,
useRechartsAnimation,
} from '@desktop-client/components/reports/chart-theme';
import { Container } from '@desktop-client/components/reports/Container';
import { DateRange } from '@desktop-client/components/reports/DateRange';
import { LoadingIndicator } from '@desktop-client/components/reports/LoadingIndicator';
@@ -111,6 +114,7 @@ export function CashFlowCard({
onRemove,
}: CashFlowCardProps) {
const { t } = useTranslation();
const animationProps = useRechartsAnimation();
const [latestTransaction, setLatestTransaction] = useState<string>('');
const [nameMenuOpen, setNameMenuOpen] = useState(false);
@@ -224,6 +228,7 @@ export function CashFlowCard({
dataKey="income"
fill={chartTheme.colors.blue}
barSize={14}
{...animationProps}
>
<LabelList
dataKey="income"
@@ -236,6 +241,7 @@ export function CashFlowCard({
dataKey="expenses"
fill={chartTheme.colors.red}
barSize={14}
{...animationProps}
>
<LabelList
dataKey="expenses"

View File

@@ -20,7 +20,6 @@ import { Tooltip } from '@actual-app/components/tooltip';
import { View } from '@actual-app/components/view';
import { css, cx } from '@emotion/css';
import * as Platform from 'loot-core/shared/platform';
import { type AccountEntity } from 'loot-core/types/models';
import {
@@ -40,6 +39,7 @@ import {
import { CellValue } from '@desktop-client/components/spreadsheet/CellValue';
import { useContextMenu } from '@desktop-client/hooks/useContextMenu';
import { useDragRef } from '@desktop-client/hooks/useDragRef';
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
import { useNotes } from '@desktop-client/hooks/useNotes';
import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref';
import { openAccountCloseModal } from '@desktop-client/modals/modalsSlice';
@@ -90,6 +90,7 @@ export function Account<FieldName extends SheetFields<'account'>>({
onDrop,
titleAccount,
}: AccountProps<FieldName>) {
const isTestEnv = useIsTestEnv();
const { t } = useTranslation();
const type = account
? account.closed
@@ -281,7 +282,7 @@ export function Account<FieldName extends SheetFields<'account'>>({
</View>
);
if (!needsTooltip || Platform.isPlaywright) {
if (!needsTooltip || isTestEnv) {
return accountRow;
}

View File

@@ -16,11 +16,10 @@ import {
import { SvgCalendar3 } from '@actual-app/components/icons/v2';
import { View } from '@actual-app/components/view';
import * as Platform from 'loot-core/shared/platform';
import { Item } from './Item';
import { SecondaryItem } from './SecondaryItem';
import { useIsTestEnv } from '@desktop-client/hooks/useIsTestEnv';
import { useSyncServerStatus } from '@desktop-client/hooks/useSyncServerStatus';
export function PrimaryButtons() {
@@ -30,8 +29,8 @@ export function PrimaryButtons() {
const location = useLocation();
const syncServerStatus = useSyncServerStatus();
const isUsingServer =
syncServerStatus !== 'no-server' || Platform.isPlaywright;
const isTestEnv = useIsTestEnv();
const isUsingServer = syncServerStatus !== 'no-server' || isTestEnv;
const isActive = [
'/payees',

View File

@@ -0,0 +1,5 @@
import * as Platform from 'loot-core/shared/platform';
export function useIsTestEnv(): boolean {
return Platform.isPlaywright;
}

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [matt-fidd]
---
Fix flaky charts VRT tests