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: '89.0', real_mobile: null, }, { os: 'Windows', os_version: '11', browser: 'firefox', device: null, browser_version: '89.0', real_mobile: null, }, { os: 'Windows', os_version: '11', browser: 'chrome', device: null, browser_version: '71.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: '10.0', browser: 'Android Browser', device: 'Samsung Galaxy S20 Ultra', 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', // 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); }); } }