test: BrowserWindow backgroundColor and transparency (#31017)
* test: BrowserWindow backgroundColor * test: allow similar colors * test: disable linux capturing * refactor: split screen capture from reading pixel color
This commit is contained in:
parent
283fa2b79d
commit
7cb62bfc22
3 changed files with 172 additions and 0 deletions
|
@ -12,6 +12,7 @@ import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersLi
|
|||
import { emittedOnce, emittedUntil, emittedNTimes } from './events-helpers';
|
||||
import { ifit, ifdescribe, defer, delay } from './spec-helpers';
|
||||
import { closeWindow, closeAllWindows } from './window-helpers';
|
||||
import { areColorsSimilar, captureScreen, CHROMA_COLOR_HEX, getPixelColor } from './screen-helpers';
|
||||
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
|
||||
|
@ -4872,5 +4873,69 @@ describe('BrowserWindow module', () => {
|
|||
w.setBounds(newBounds);
|
||||
expect(w.getBounds()).to.deep.equal(newBounds);
|
||||
});
|
||||
|
||||
// Linux doesn't return any capture sources.
|
||||
ifit(process.platform !== 'linux')('should not display a visible background', async () => {
|
||||
const display = screen.getPrimaryDisplay();
|
||||
|
||||
const backgroundWindow = new BrowserWindow({
|
||||
...display.bounds,
|
||||
frame: false,
|
||||
backgroundColor: CHROMA_COLOR_HEX,
|
||||
hasShadow: false
|
||||
});
|
||||
|
||||
await backgroundWindow.loadURL('about:blank');
|
||||
|
||||
const foregroundWindow = new BrowserWindow({
|
||||
...display.bounds,
|
||||
show: true,
|
||||
transparent: true,
|
||||
frame: false,
|
||||
hasShadow: false
|
||||
});
|
||||
|
||||
foregroundWindow.loadFile(path.join(__dirname, 'fixtures', 'pages', 'half-background-color.html'));
|
||||
await emittedOnce(foregroundWindow, 'ready-to-show');
|
||||
|
||||
const screenCapture = await captureScreen();
|
||||
const leftHalfColor = getPixelColor(screenCapture, {
|
||||
x: display.size.width / 4,
|
||||
y: display.size.height / 2
|
||||
});
|
||||
const rightHalfColor = getPixelColor(screenCapture, {
|
||||
x: display.size.width - (display.size.width / 4),
|
||||
y: display.size.height / 2
|
||||
});
|
||||
|
||||
expect(areColorsSimilar(leftHalfColor, CHROMA_COLOR_HEX)).to.be.true();
|
||||
expect(areColorsSimilar(rightHalfColor, '#ff0000')).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('"backgroundColor" option', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
// Linux doesn't return any capture sources.
|
||||
ifit(process.platform !== 'linux')('should display the set color', async () => {
|
||||
const display = screen.getPrimaryDisplay();
|
||||
|
||||
const w = new BrowserWindow({
|
||||
...display.bounds,
|
||||
show: true,
|
||||
backgroundColor: CHROMA_COLOR_HEX
|
||||
});
|
||||
|
||||
w.loadURL('about:blank');
|
||||
await emittedOnce(w, 'ready-to-show');
|
||||
|
||||
const screenCapture = await captureScreen();
|
||||
const centerColor = getPixelColor(screenCapture, {
|
||||
x: display.size.width / 2,
|
||||
y: display.size.height / 2
|
||||
});
|
||||
|
||||
expect(areColorsSimilar(centerColor, CHROMA_COLOR_HEX)).to.be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
20
spec-main/fixtures/pages/half-background-color.html
Normal file
20
spec-main/fixtures/pages/half-background-color.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
#main {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
</body>
|
||||
</html>
|
87
spec-main/screen-helpers.ts
Normal file
87
spec-main/screen-helpers.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { screen, desktopCapturer, NativeImage } from 'electron';
|
||||
|
||||
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
|
||||
|
||||
/** Chroma key green. */
|
||||
export const CHROMA_COLOR_HEX = '#00b140';
|
||||
|
||||
/**
|
||||
* Capture the screen at the given point.
|
||||
*
|
||||
* NOTE: Not yet supported on Linux in CI due to empty sources list.
|
||||
*/
|
||||
export const captureScreen = async (point: Electron.Point = { x: 0, y: 0 }): Promise<NativeImage> => {
|
||||
const display = screen.getDisplayNearestPoint(point);
|
||||
const sources = await desktopCapturer.getSources({ types: ['screen'], thumbnailSize: display.size });
|
||||
// Toggle to save screen captures for debugging.
|
||||
const DEBUG_CAPTURE = false;
|
||||
if (DEBUG_CAPTURE) {
|
||||
for (const source of sources) {
|
||||
await fs.promises.writeFile(path.join(fixtures, `screenshot_${source.display_id}.png`), source.thumbnail.toPNG());
|
||||
}
|
||||
}
|
||||
const screenCapture = sources.find(source => source.display_id === `${display.id}`);
|
||||
// Fails when HDR is enabled on Windows.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1247730
|
||||
if (!screenCapture) {
|
||||
const displayIds = sources.map(source => source.display_id);
|
||||
throw new Error(`Unable to find screen capture for display '${display.id}'\n\tAvailable displays: ${displayIds.join(', ')}`);
|
||||
}
|
||||
return screenCapture.thumbnail;
|
||||
};
|
||||
|
||||
const formatHexByte = (val: number): string => {
|
||||
const str = val.toString(16);
|
||||
return str.length === 2 ? str : `0${str}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the hex color at the given pixel coordinate in an image.
|
||||
*/
|
||||
export const getPixelColor = (image: Electron.NativeImage, point: Electron.Point): string => {
|
||||
const pixel = image.crop({ ...point, width: 1, height: 1 });
|
||||
// TODO(samuelmaddock): NativeImage.toBitmap() should return the raw pixel
|
||||
// color, but it sometimes differs. Why is that?
|
||||
const [b, g, r] = pixel.toBitmap();
|
||||
return `#${formatHexByte(r)}${formatHexByte(g)}${formatHexByte(b)}`;
|
||||
};
|
||||
|
||||
const hexToRgba = (hexColor: string) => {
|
||||
const match = hexColor.match(/^#([0-9a-fA-F]{6,8})$/);
|
||||
if (!match) return;
|
||||
|
||||
const colorStr = match[1];
|
||||
return [
|
||||
parseInt(colorStr.substring(0, 2), 16),
|
||||
parseInt(colorStr.substring(2, 4), 16),
|
||||
parseInt(colorStr.substring(4, 6), 16),
|
||||
parseInt(colorStr.substring(6, 8), 16) || 0xFF
|
||||
];
|
||||
};
|
||||
|
||||
/** Calculate euclidian distance between colors. */
|
||||
const colorDistance = (hexColorA: string, hexColorB: string) => {
|
||||
const colorA = hexToRgba(hexColorA);
|
||||
const colorB = hexToRgba(hexColorB);
|
||||
if (!colorA || !colorB) return -1;
|
||||
return Math.sqrt(
|
||||
Math.pow(colorB[0] - colorA[0], 2) +
|
||||
Math.pow(colorB[1] - colorA[1], 2) +
|
||||
Math.pow(colorB[2] - colorA[2], 2)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if colors are similar based on distance. This can be useful when
|
||||
* comparing colors which may differ based on lossy compression.
|
||||
*/
|
||||
export const areColorsSimilar = (
|
||||
hexColorA: string,
|
||||
hexColorB: string,
|
||||
distanceThreshold = 90
|
||||
): boolean => {
|
||||
const distance = colorDistance(hexColorA, hexColorB);
|
||||
return distance <= distanceThreshold;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue