Run nightly automated screenshots. Closes #2699
This commit is contained in:
parent
398bbef396
commit
de534632cd
43
.github/workflows/screenshots.yml
vendored
Normal file
43
.github/workflows/screenshots.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
name: Take nightly screenshots
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
|
||||
env:
|
||||
BROWSERSTACK_KEY: ${{ secrets.BROWSERSTACK_KEY }}
|
||||
BROWSERSTACK_PASSWORD: ${{ secrets.BROWSERSTACK_PASSWORD }}
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
|
||||
jobs:
|
||||
Screenshots:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18.8'
|
||||
|
||||
- name: Automate screenshots
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 4
|
||||
command: cd test/automated/screenshots && ./run.sh
|
||||
|
||||
- name: Commit changes
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
author_name: Owncast
|
||||
author_email: owncast@owncast.online
|
||||
message: 'Commit screenshots'
|
||||
pull: '--rebase --autostash'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Dispatch event to web site
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
token: ${{ secrets.BUNDLE_STORYBOOK_OWNCAST_ONLINE }}
|
||||
repository: owncast/owncast.github.io
|
||||
event-type: bundle-components-library
|
18202
test/automated/screenshots/allBrowsers.json
Normal file
18202
test/automated/screenshots/allBrowsers.json
Normal file
File diff suppressed because it is too large
Load Diff
279
test/automated/screenshots/index.js
Normal file
279
test/automated/screenshots/index.js
Normal file
@ -0,0 +1,279 @@
|
||||
const fs = require('fs');
|
||||
const BrowserStack = require('browserstack');
|
||||
const https = require('https');
|
||||
|
||||
let portraitJobId = null;
|
||||
let landscapeJobId = null;
|
||||
let finished = false;
|
||||
|
||||
const testBrowsersPortrait = [
|
||||
{
|
||||
os: 'OS X',
|
||||
os_version: 'Ventura',
|
||||
browser: 'chrome',
|
||||
device: null,
|
||||
browser_version: '71.0',
|
||||
real_mobile: null,
|
||||
},
|
||||
{
|
||||
os: 'OS X',
|
||||
os_version: 'Ventura',
|
||||
browser: 'safari',
|
||||
device: null,
|
||||
browser_version: '16.0',
|
||||
real_mobile: null,
|
||||
},
|
||||
// {
|
||||
// os: 'OS X',
|
||||
// os_version: 'Ventura',
|
||||
// browser: 'firefox',
|
||||
// device: null,
|
||||
// browser_version: '90.0',
|
||||
// real_mobile: null,
|
||||
// },
|
||||
// {
|
||||
// os: 'Windows',
|
||||
// os_version: '10',
|
||||
// browser: 'firefox',
|
||||
// device: null,
|
||||
// browser_version: '90.0',
|
||||
// real_mobile: null,
|
||||
// },
|
||||
{
|
||||
os: 'Windows',
|
||||
os_version: '11',
|
||||
browser: 'chrome',
|
||||
device: null,
|
||||
browser_version: '71.0',
|
||||
real_mobile: null,
|
||||
},
|
||||
{
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
browser: 'edge',
|
||||
device: null,
|
||||
browser_version: '18.0',
|
||||
real_mobile: null,
|
||||
},
|
||||
{
|
||||
os: 'ios',
|
||||
os_version: '16',
|
||||
browser: 'Mobile Safari',
|
||||
device: 'iPhone 14 Pro',
|
||||
browser_version: null,
|
||||
real_mobile: true,
|
||||
},
|
||||
{
|
||||
os: 'ios',
|
||||
os_version: '16',
|
||||
browser: 'Mobile Safari',
|
||||
device: 'iPad Pro 11 2022',
|
||||
browser_version: null,
|
||||
real_mobile: true,
|
||||
},
|
||||
{
|
||||
os: 'android',
|
||||
os_version: '13.0',
|
||||
browser: 'Android Browser',
|
||||
device: 'Google Pixel 7 Pro',
|
||||
browser_version: null,
|
||||
real_mobile: true,
|
||||
},
|
||||
];
|
||||
|
||||
const testBrowsersLandscape = [
|
||||
// {
|
||||
// os: 'android',
|
||||
// os_version: '13.0',
|
||||
// browser: 'Android Browser',
|
||||
// device: 'Google Pixel 7 Pro',
|
||||
// browser_version: null,
|
||||
// real_mobile: true,
|
||||
// },
|
||||
{
|
||||
os: 'ios',
|
||||
os_version: '16',
|
||||
browser: 'Mobile Safari',
|
||||
device: 'iPad Pro 11 2022',
|
||||
browser_version: null,
|
||||
real_mobile: true,
|
||||
},
|
||||
// {
|
||||
// os: 'ios',
|
||||
// os_version: '16',
|
||||
// browser: 'Mobile Safari',
|
||||
// device: 'iPhone 14 Pro',
|
||||
// browser_version: null,
|
||||
// real_mobile: true,
|
||||
// },
|
||||
];
|
||||
|
||||
const USERNAME = process.env.BROWSERSTACK_USERNAME;
|
||||
const PASSWORD = process.env.BROWSERSTACK_PASSWORD;
|
||||
const URL = process.env.TEST_URL;
|
||||
const FILE_SUFFIX = process.env.FILE_SUFFIX;
|
||||
|
||||
if (!URL) {
|
||||
console.error('Missing URL');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!USERNAME || !PASSWORD) {
|
||||
console.error('Missing BrowserStack credentials');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!FILE_SUFFIX) {
|
||||
console.error('Missing file suffix');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { exit } = require('process');
|
||||
var browserStackCredentials = {
|
||||
username: USERNAME,
|
||||
password: PASSWORD,
|
||||
};
|
||||
|
||||
let screenshotClient = BrowserStack.createScreenshotClient(
|
||||
browserStackCredentials
|
||||
);
|
||||
|
||||
function start() {
|
||||
console.log(
|
||||
`creating screenshots for ${URL} using ${
|
||||
[...testBrowsersPortrait, ...testBrowsersLandscape].length
|
||||
} browsers...`
|
||||
);
|
||||
|
||||
const portraitOptions = {
|
||||
url: URL,
|
||||
orientation: 'portrait',
|
||||
mac_res: '1920x1080',
|
||||
win_res: '1280x1024',
|
||||
browsers: testBrowsersPortrait,
|
||||
wait_time: 20,
|
||||
local: true,
|
||||
};
|
||||
|
||||
const landscapeOptions = {
|
||||
url: URL,
|
||||
orientation: 'landscape',
|
||||
mac_res: '1920x1080',
|
||||
win_res: '1280x1024',
|
||||
browsers: testBrowsersLandscape,
|
||||
wait_time: 20,
|
||||
local: true,
|
||||
};
|
||||
|
||||
screenshotClient.generateScreenshots(
|
||||
portraitOptions,
|
||||
function (error, response) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
exit(0);
|
||||
} else {
|
||||
const { job_id } = response;
|
||||
portraitJobId = job_id;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
screenshotClient.generateScreenshots(
|
||||
landscapeOptions,
|
||||
function (error, response) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
exit(0);
|
||||
} else {
|
||||
const { job_id } = response;
|
||||
landscapeJobId = job_id;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
setTimeout(check, 30000);
|
||||
}
|
||||
|
||||
async function checkJob(jobId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
screenshotClient.getJob(jobId, function (error, job) {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
return resolve(job);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function check() {
|
||||
if (finished) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!portraitJobId || !landscapeJobId) {
|
||||
setTimeout(check, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const portraitStatus = await checkJob(portraitJobId);
|
||||
const landscapeStatus = await checkJob(landscapeJobId);
|
||||
|
||||
const { screenshots: portraitScreenshots } = portraitStatus;
|
||||
const { screenshots: landscapeScreenshots } = landscapeStatus;
|
||||
const screenshots = [...portraitScreenshots, ...landscapeScreenshots];
|
||||
|
||||
const completed = screenshots.filter((s) => s.state === 'done');
|
||||
console.log(`completed ${completed.length} of ${screenshots.length}...`);
|
||||
console.log(screenshots.filter((s) => s.state !== 'done'));
|
||||
|
||||
if (completed.length === screenshots.length) {
|
||||
complete(screenshots);
|
||||
}
|
||||
|
||||
const timedOut = screenshots.filter((s) => s.state === 'timed-out');
|
||||
if (timedOut.length > 0) {
|
||||
console.log('timed out');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
setTimeout(check, 5000);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
start();
|
||||
|
||||
function complete(screenshots) {
|
||||
console.log('downloading screenshots...');
|
||||
var saveCounter = 0;
|
||||
for (const screenshot of screenshots) {
|
||||
const { os, os_version, browser, device, orientation, image_url } =
|
||||
screenshot;
|
||||
const name = `${os} ${os_version} ${browser} ${device || 'desktop'} ${
|
||||
orientation || 'default'
|
||||
} ${FILE_SUFFIX}`;
|
||||
|
||||
const filename = name.replace(/ /g, '-').toLowerCase();
|
||||
|
||||
const file = fs.createWriteStream(`./screenshots/${filename}.png`);
|
||||
https
|
||||
.get(image_url, function (response) {
|
||||
response.pipe(file);
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
saveCounter++;
|
||||
if (saveCounter === screenshots.length) {
|
||||
finished = true;
|
||||
}
|
||||
});
|
||||
})
|
||||
.on('error', function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
126
test/automated/screenshots/package-lock.json
generated
Normal file
126
test/automated/screenshots/package-lock.json
generated
Normal file
@ -0,0 +1,126 @@
|
||||
{
|
||||
"name": "screenshots",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "screenshots",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"browserstack": "^1.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
||||
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
|
||||
"dependencies": {
|
||||
"es6-promisify": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/browserstack": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz",
|
||||
"integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==",
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"node_modules/es6-promisify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
|
||||
"dependencies": {
|
||||
"es6-promise": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
|
||||
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
|
||||
"dependencies": {
|
||||
"agent-base": "^4.3.0",
|
||||
"debug": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"agent-base": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
|
||||
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
|
||||
"requires": {
|
||||
"es6-promisify": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"browserstack": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz",
|
||||
"integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==",
|
||||
"requires": {
|
||||
"https-proxy-agent": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
|
||||
"requires": {
|
||||
"es6-promise": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
|
||||
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
|
||||
"requires": {
|
||||
"agent-base": "^4.3.0",
|
||||
"debug": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
}
|
||||
}
|
||||
}
|
14
test/automated/screenshots/package.json
Normal file
14
test/automated/screenshots/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "screenshots",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"browserstack": "^1.6.1"
|
||||
}
|
||||
}
|
43
test/automated/screenshots/run.sh
Executable file
43
test/automated/screenshots/run.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
rm -rf ./screenshots
|
||||
mkdir -p ./screenshots
|
||||
|
||||
curl -o ./BrowserStackLocal-linux-x64.zip https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip
|
||||
unzip -o ./BrowserStackLocal-linux-x64.zip
|
||||
./BrowserStackLocal --key $BROWSERSTACK_KEY &
|
||||
|
||||
finish() {
|
||||
killall BrowserStackLocal
|
||||
kill_with_kids "$STREAM_PID"
|
||||
}
|
||||
|
||||
trap finish EXIT TERM INT
|
||||
|
||||
npm install --silent >/dev/null
|
||||
source ../tools.sh
|
||||
install_ffmpeg
|
||||
start_owncast
|
||||
|
||||
# Offline screenshots
|
||||
FILE_SUFFIX="offline" node index.js
|
||||
|
||||
# Online screenshots
|
||||
start_stream
|
||||
sleep 20
|
||||
|
||||
FILE_SUFFIX="online" node index.js
|
||||
|
||||
SCREENSHOTS="$(pwd)/screenshots"
|
||||
echo $SCREENSHOTS
|
||||
|
||||
# Change to the root directory of the repository
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
cd web/.storybook/story-assets
|
||||
rm -rf ./screenshots
|
||||
mv "$SCREENSHOTS" .
|
Loading…
Reference in New Issue
Block a user