Run nightly automated screenshots. Closes #2699

This commit is contained in:
Gabe Kangas 2023-02-11 18:41:04 -08:00
parent 398bbef396
commit de534632cd
No known key found for this signature in database
GPG Key ID: 4345B2060657F330
6 changed files with 18707 additions and 0 deletions

43
.github/workflows/screenshots.yml vendored Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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);
});
}
}

View 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=="
}
}
}

View 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"
}
}

View 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" .