Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to filter scenarios and watch Backstop runs as they execute #354

Merged
merged 12 commits into from
Jul 11, 2024
Merged
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,25 @@ repo.

Please file all bugs, requests, and issues on the [web team's visual regression phabricator board](https://phabricator.wikimedia.org/project/board/5933/)

## Debugging

The following env vars are useful when running and debugging Pixel locally

### Watch mode

To watch BackstopJS taking your screenshots (in its visual-regression container) you can use the `WATCH_MODE` env var:

`WATCH_MODE=1 ./pixel.js reference`

This can be very helpful determining Mediawiki related configuration issues and should work with any command kicking off Pixel BackstopJS runs

### Filter

To quickly restrict which scenario runs without modifying any code, you can use the `FILTER` env var:

`FILTER="Sticky header with unpinned TOC" ./pixel.js reference`
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering changing FILTER to SCENARIO_FILTER to be explicit...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


This is useful when debugging specific scenarios and should work with any command kicking off Pixel BackstopJS runs

## Roadmap

Expand Down
36 changes: 35 additions & 1 deletion config/configCommon.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
const config = {
id: 'MediaWiki',
asyncCaptureLimit: 4,
asyncCompareLimit: 25,
Expand All @@ -17,3 +17,37 @@ module.exports = {
debugWindow: false,
delay: 100
};

const watchConfig = {
asyncCaptureLimit: 1,
engineOptions: {
headless: false,
slowMo: 100,
},
delay: 1000
};

const deepMerge = (target, source) => {
Object.keys(source).forEach(key => {
const sourceValue = source[key];
if (Array.isArray(target[key]) && Array.isArray(sourceValue)) {
target[key] = [...new Set([...target[key], ...sourceValue])];
} else if (sourceValue && typeof sourceValue === 'object') {
target[key] = deepMerge(target[key] ?? {}, sourceValue);
} else {
target[key] = sourceValue;
}
});
return target;
};

if ( process.env.WATCH_MODE === '1' ) {
deepMerge(config, watchConfig);
}

console.log('-----------------------------');
console.log('Current common configuration:');
console.log(JSON.stringify(config, null, 2));
console.log('-----------------------------');

module.exports = config;
19 changes: 19 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,20 @@ services:
retries: 20
networks:
- pixel_network
novnc:
profiles:
- manual
build: novnc
environment:
- DISPLAY_WIDTH=1280
- DISPLAY_HEIGHT=1024
networks:
- pixel_network
ports:
- 8088:8088
visual-regression:
profiles:
- manual
init: true
build:
context: .
Expand All @@ -73,6 +86,8 @@ services:
working_dir: /pixel
env_file:
- .env
environment:
- DISPLAY=novnc:0
Copy link
Collaborator Author

@montehurd montehurd Jul 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a reminder, this line makes the magic happen

It causes the visual-regression container to use the novnc container as its display

The Backstop process (including Chrome) still runs in the visual-regression container, but the display window for Chrome appears on the novnc desktop

We then can see the novnc container's desktop because the novnc program lets make a vnc connection from a any modern browser, in this case most frequently from the host

The novnc container's desktop is using xfce4 atop an xvfb virtual frame buffer

websockify lets the host connect to the novnc container's novnc

Copy link
Collaborator Author

@montehurd montehurd Jul 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a subtle point I needed clarifying on even after getting this all working:

( for context, when I say "this all works" below, I had given Claude this PR's branch as a diff )

346711337-be0f9689-a42e-4e3c-8f13-66d5d27d36eb

volumes:
- ./context.json:/pixel/context.json
- ./viewports.js:/pixel/viewports.js
Expand All @@ -86,6 +101,8 @@ services:
networks:
- pixel_network
a11y-regression:
profiles:
- manual
init: true
build:
context: .
Expand All @@ -95,6 +112,8 @@ services:
working_dir: /pixel
env_file:
- .env
environment:
- DISPLAY=novnc:0
volumes:
- ./context.json:/pixel/context.json
- ./viewports.js:/pixel/viewports.js
Expand Down
24 changes: 24 additions & 0 deletions novnc/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM alpine

RUN set -ex; \
apk add --no-cache \
xfce4 \
novnc \
supervisor \
x11vnc \
xvfb \
&& rm -rf /var/cache/apk/*

ENV HOME=/root \
LANG=en_US.UTF-8 \
LANGUAGE=en_US.UTF-8 \
LC_ALL=C.UTF-8 \
DISPLAY=:0.0 \
DISPLAY_WIDTH=1280 \
DISPLAY_HEIGHT=1024

COPY . /app
COPY background.svg /usr/share/backgrounds/xfce/xfce-shapes.svg

CMD ["/app/entrypoint.sh"]
EXPOSE 8088
81 changes: 81 additions & 0 deletions novnc/background.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions novnc/conf.d/desktop.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[program:xfce4]
command=startxfce4
autorestart=true
3 changes: 3 additions & 0 deletions novnc/conf.d/websockify.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[program:websockify]
command=websockify --web /usr/share/novnc 8088 localhost:5900
autorestart=true
3 changes: 3 additions & 0 deletions novnc/conf.d/x11vnc.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[program:x11vnc]
command=x11vnc -forever -shared
autorestart=true
3 changes: 3 additions & 0 deletions novnc/conf.d/xvfb.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[program:xvfb]
command=Xvfb :0 -screen 0 "%(ENV_DISPLAY_WIDTH)s"x"%(ENV_DISPLAY_HEIGHT)s"x24 -listen tcp -ac
autorestart=true
7 changes: 7 additions & 0 deletions novnc/ensure-novnc-container-up.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

if docker container ls --format '{{.Names}}' | grep -q "pixel-novnc-1"; then
echo "The pixel-novnc-1 container is already running."
else
docker compose up --no-deps --detach novnc
fi
5 changes: 5 additions & 0 deletions novnc/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh

set -eux

exec supervisord -c /app/supervisord.conf
10 changes: 10 additions & 0 deletions novnc/open-watch-url.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh

URL="http://localhost:8088/vnc_lite.html?autoconnect=true"

open "$URL" 2>/dev/null || {
printf "\n\033[0;32mThe 'open' command is not available\033[0m\n"
printf "\n\033[0;33mOpen the following URL in your web browser to watch BackstopJS do screen captures inside its docker container:\033[0m\n"
printf "\n\033[1;33m%s\033[0m\n\n" "$URL"
sleep 5
}
5 changes: 5 additions & 0 deletions novnc/supervisord.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[supervisord]
nodaemon=true

[include]
files = /app/conf.d/*.conf
21 changes: 19 additions & 2 deletions pixel.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ async function processCommand( type, opts, runSilently = false ) {
const { stdout: stdout2 } = await simpleSpawn.exec( './reset-db.sh' );
console.log( stdout2 );

if (process.env.WATCH_MODE === '1') {
await simpleSpawn.spawn( './novnc/open-watch-url.sh' );
}

if ( opts.a11y ) {
return await runA11yRegressionTests( type, configFile, opts.logResults, opts );
} else {
Expand Down Expand Up @@ -266,8 +270,21 @@ async function runVisualRegressionTests( type, config, group, runSilently, confi
writeRunInProgressTemplateToIndexFile( indexFileFullPath, group );

const finished = await simpleSpawn.spawn(
'docker',
[ 'compose', ...getComposeOpts( [ 'run', ...( process.env.NONINTERACTIVE ? [ '--no-TTY' ] : [] ), '--rm', 'visual-regression', type, '--config', configFile ] ) ]
'docker',
[
'compose',
...getComposeOpts( [
'run',
...( process.env.NONINTERACTIVE ? [ '--no-TTY' ] : [] ),
'--rm',
...( process.env.WATCH_MODE ? [ '-e', `WATCH_MODE=${process.env.WATCH_MODE}` ] : [] ),
'visual-regression',
type,
'--config',
configFile,
...( process.env.FILTER ? [ '--filter', `${process.env.FILTER}` ] : [] )
] )
]
).then( async () => {
if ( type !== 'reference' ) {
await addBannerAndIfNecessaryOpenReport(
Expand Down
7 changes: 6 additions & 1 deletion start.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#!/bin/bash

# Start docker containers
docker compose --progress=plain --project-directory . -f ./docker-compose.yml up -d
docker compose --progress=plain --project-directory . -f ./docker-compose.yml up -d

# Start novnc container if needed
if [ "${WATCH_MODE}" = "1" ]; then
./novnc/ensure-novnc-container-up.sh
fi
Loading