fix(epg): keep loading shimmer visible in both themes

This commit is contained in:
4gray committed 2026-09-05 14:18:44 +02:00
1 parent 28ece6f514
commit edebe0c019
4 files changed
+78 -4

No files matched your search

@@ -24,6 +24,7 @@ import {
applyTheme,
expectTextContrast,
expectThemeSurface,
expectSkeletonContrast,
} from './theme-contrast';
const epgCredentials = {
@@ -182,6 +183,32 @@ test('@epg @xtream @electron opens the programme dialog from a timeline block an
guide.locator('[data-when="now"] .desc').first()
);
}
// Keep a fresh channel's EPG IPC pending so the real list loading
// template stays mounted through both theme changes.
await app.electronApp.evaluate(({ ipcMain }) => {
ipcMain.removeHandler('XTREAM_REQUEST');
ipcMain.handle(
'XTREAM_REQUEST',
() =>
new Promise(() => {
// Released when this isolated Electron test app closes.
})
);
});
await app.mainWindow
.locator('[data-test-id="channel-item"]')
.nth(1)
.click();
const skeleton = guide.locator('.sk-time').first();
await expect(skeleton).toBeVisible();
for (const theme of ['light', 'dark'] as const) {
await applyTheme(app.mainWindow, theme);
await expectSkeletonContrast(skeleton, guide);
await expectSkeletonContrast(
guide.locator('.sk-title').first(),
guide
);
}
} finally {
await closeElectronApp(app);
}
@@ -153,3 +153,48 @@ export async function expectOverlayContrastOnWhite(
);
}
}
/** Skeletons are decorative, but their faintest gradient stop must remain
* distinguishable from the guide's background in either theme. */
export async function expectSkeletonContrast(
skeleton: Locator,
guide: Locator
) {
const background = await guide.evaluate((element) =>
getComputedStyle(element)
.backgroundColor.match(/[\d.]+/g)!
.slice(0, 3)
.map(Number)
);
const { data, info } = await sharp(
await skeleton.screenshot({ animations: 'disabled' })
)
.removeAlpha()
.raw()
.toBuffer({ resolveWithObject: true });
const luminance = (rgb: number[]) =>
rgb
.map((value) => {
const s = value / 255;
return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
})
.reduce(
(sum, value, index) =>
sum + value * [0.2126, 0.7152, 0.0722][index],
0
);
const bg = luminance(background);
// Middle row avoids the rounded transparent corners.
for (let x = 8; x < info.width - 8; x++) {
const offset =
(Math.floor(info.height / 2) * info.width + x) * info.channels;
const fg = luminance([
data[offset],
data[offset + 1],
data[offset + 2],
]);
expect(
(Math.max(fg, bg) + 0.05) / (Math.min(fg, bg) + 0.05)
).toBeGreaterThanOrEqual(1.3);
}
}