aboutsummaryrefslogtreecommitdiffstats
path: root/tests/runner/browserstack
diff options
context:
space:
mode:
Diffstat (limited to 'tests/runner/browserstack')
-rw-r--r--tests/runner/browserstack/api.js332
-rw-r--r--tests/runner/browserstack/buildBrowserFromString.js20
-rw-r--r--tests/runner/browserstack/createAuthHeader.js7
-rw-r--r--tests/runner/browserstack/local.js34
4 files changed, 0 insertions, 393 deletions
diff --git a/tests/runner/browserstack/api.js b/tests/runner/browserstack/api.js
deleted file mode 100644
index 632f90c3b..000000000
--- a/tests/runner/browserstack/api.js
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * Browserstack API is documented at
- * https://github.com/browserstack/api
- */
-
-import { createAuthHeader } from "./createAuthHeader.js";
-
-const browserstackApi = "https://api.browserstack.com";
-const apiVersion = 5;
-
-const username = process.env.BROWSERSTACK_USERNAME;
-const accessKey = process.env.BROWSERSTACK_ACCESS_KEY;
-
-// iOS has null for version numbers,
-// and we do not need a similar check for OS versions.
-const rfinalVersion = /(?:^[0-9\.]+$)|(?:^null$)/;
-const rlatest = /^latest-(\d+)$/;
-
-const rnonDigits = /(?:[^\d\.]+)|(?:20\d{2})/g;
-
-async function fetchAPI( path, options = {}, versioned = true ) {
- if ( !username || !accessKey ) {
- throw new Error(
- "BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY environment variables must be set."
- );
- }
- const init = {
- method: "GET",
- ...options,
- headers: {
- authorization: createAuthHeader( username, accessKey ),
- accept: "application/json",
- "content-type": "application/json",
- ...options.headers
- }
- };
- const response = await fetch(
- `${ browserstackApi }/${ versioned ? `${ apiVersion }/` : "" }${ path }`,
- init
- );
- if ( !response.ok ) {
- console.log(
- `\n${ init.method } ${ path }`,
- response.status,
- response.statusText
- );
- throw new Error( `Error fetching ${ path }` );
- }
- return response.json();
-}
-
-/**
- * =============================
- * Browsers API
- * =============================
- */
-
-function compareVersionNumbers( a, b ) {
- if ( a != null && b == null ) {
- return -1;
- }
- if ( a == null && b != null ) {
- return 1;
- }
- if ( a == null && b == null ) {
- return 0;
- }
- const aParts = a.replace( rnonDigits, "" ).split( "." );
- const bParts = b.replace( rnonDigits, "" ).split( "." );
-
- if ( aParts.length > bParts.length ) {
- return -1;
- }
- if ( aParts.length < bParts.length ) {
- return 1;
- }
-
- for ( let i = 0; i < aParts.length; i++ ) {
- const aPart = Number( aParts[ i ] );
- const bPart = Number( bParts[ i ] );
- if ( aPart < bPart ) {
- return -1;
- }
- if ( aPart > bPart ) {
- return 1;
- }
- }
-
- if ( rnonDigits.test( a ) && !rnonDigits.test( b ) ) {
- return -1;
- }
- if ( !rnonDigits.test( a ) && rnonDigits.test( b ) ) {
- return 1;
- }
-
- return 0;
-}
-
-function sortBrowsers( a, b ) {
- if ( a.browser < b.browser ) {
- return -1;
- }
- if ( a.browser > b.browser ) {
- return 1;
- }
- const browserComparison = compareVersionNumbers(
- a.browser_version,
- b.browser_version
- );
- if ( browserComparison ) {
- return browserComparison;
- }
- if ( a.os < b.os ) {
- return -1;
- }
- if ( a.os > b.os ) {
- return 1;
- }
- const osComparison = compareVersionNumbers( a.os_version, b.os_version );
- if ( osComparison ) {
- return osComparison;
- }
- const deviceComparison = compareVersionNumbers( a.device, b.device );
- if ( deviceComparison ) {
- return deviceComparison;
- }
- return 0;
-}
-
-export async function getBrowsers( { flat = false } = {} ) {
- const query = new URLSearchParams();
- if ( flat ) {
- query.append( "flat", true );
- }
- const browsers = await fetchAPI( `/browsers?${ query }` );
- return browsers.sort( sortBrowsers );
-}
-
-function matchVersion( browserVersion, version ) {
- if ( !version ) {
- return false;
- }
- const regex = new RegExp(
- `^${ version.replace( /\\/g, "\\\\" ).replace( /\./g, "\\." ) }\\b`,
- "i"
- );
- return regex.test( browserVersion );
-}
-
-export async function filterBrowsers( filter ) {
- const browsers = await getBrowsers( { flat: true } );
- if ( !filter ) {
- return browsers;
- }
- const filterBrowser = ( filter.browser ?? "" ).toLowerCase();
- const filterVersion = ( filter.browser_version ?? "" ).toLowerCase();
- const filterOs = ( filter.os ?? "" ).toLowerCase();
- const filterOsVersion = ( filter.os_version ?? "" ).toLowerCase();
- const filterDevice = ( filter.device ?? "" ).toLowerCase();
-
- const filteredWithoutVersion = browsers.filter( ( browser ) => {
- return (
- ( !filterBrowser || filterBrowser === browser.browser.toLowerCase() ) &&
- ( !filterOs || filterOs === browser.os.toLowerCase() ) &&
- ( !filterOsVersion || matchVersion( browser.os_version, filterOsVersion ) ) &&
- ( !filterDevice || filterDevice === ( browser.device || "" ).toLowerCase() )
- );
- } );
-
- if ( !filterVersion ) {
- return filteredWithoutVersion;
- }
-
- if ( filterVersion.startsWith( "latest" ) ) {
- const groupedByName = filteredWithoutVersion
- .filter( ( b ) => rfinalVersion.test( b.browser_version ) )
- .reduce( ( acc, browser ) => {
- acc[ browser.browser ] = acc[ browser.browser ] ?? [];
- acc[ browser.browser ].push( browser );
- return acc;
- }, Object.create( null ) );
-
- const filtered = [];
- for ( const group of Object.values( groupedByName ) ) {
- const latest = group[ group.length - 1 ];
-
- // Mobile devices do not have browser version.
- // Skip the version check for these,
- // but include the latest in the list if it made it
- // through filtering.
- if ( !latest.browser_version ) {
-
- // Do not include in the list for latest-n.
- if ( filterVersion === "latest" ) {
- filtered.push( latest );
- }
- continue;
- }
-
- // Get the latest version and subtract the number from the filter,
- // ignoring any patch versions, which may differ between major versions.
- const num = rlatest.exec( filterVersion );
- const version = parseInt( latest.browser_version ) - ( num ? num[ 1 ] : 0 );
- const match = group.findLast( ( browser ) => {
- return matchVersion( browser.browser_version, version.toString() );
- } );
- if ( match ) {
- filtered.push( match );
- }
- }
- return filtered;
- }
-
- return filteredWithoutVersion.filter( ( browser ) => {
- return matchVersion( browser.browser_version, filterVersion );
- } );
-}
-
-export async function listBrowsers( filter ) {
- const browsers = await filterBrowsers( filter );
- console.log( "Available browsers:" );
- for ( const browser of browsers ) {
- let message = ` ${ browser.browser }_`;
- if ( browser.device ) {
- message += `:${ browser.device }_`;
- } else {
- message += `${ browser.browser_version }_`;
- }
- message += `${ browser.os }_${ browser.os_version }`;
- console.log( message );
- }
-}
-
-export async function getLatestBrowser( filter ) {
- if ( !filter.browser_version ) {
- filter.browser_version = "latest";
- }
- const browsers = await filterBrowsers( filter );
- return browsers[ browsers.length - 1 ];
-}
-
-/**
- * =============================
- * Workers API
- * =============================
- */
-
-/**
- * A browser object may only have one of `browser` or `device` set;
- * which property is set will depend on `os`.
- *
- * `options`: is an object with the following properties:
- * `os`: The operating system.
- * `os_version`: The operating system version.
- * `browser`: The browser name.
- * `browser_version`: The browser version.
- * `device`: The device name.
- * `url` (optional): Which URL to navigate to upon creation.
- * `timeout` (optional): Maximum life of the worker (in seconds). Maximum value of `1800`. Specifying `0` will use the default of `300`.
- * `name` (optional): Provide a name for the worker.
- * `build` (optional): Group workers into a build.
- * `project` (optional): Provide the project the worker belongs to.
- * `resolution` (optional): Specify the screen resolution (e.g. "1024x768").
- * `browserstack.local` (optional): Set to `true` to mark as local testing.
- * `browserstack.video` (optional): Set to `false` to disable video recording.
- * `browserstack.localIdentifier` (optional): ID of the local tunnel.
- */
-export function createWorker( options ) {
- return fetchAPI( "/worker", {
- method: "POST",
- body: JSON.stringify( options )
- } );
-}
-
-/**
- * Returns a worker object, if one exists, with the following properties:
- * `id`: The worker id.
- * `status`: A string representing the current status of the worker.
- * Possible statuses: `"running"`, `"queue"`.
- */
-export function getWorker( id ) {
- return fetchAPI( `/worker/${ id }` );
-}
-
-export async function deleteWorker( id ) {
- return fetchAPI( `/worker/${ id }`, { method: "DELETE" } );
-}
-
-export function getWorkers() {
- return fetchAPI( "/workers" );
-}
-
-/**
- * Stop all workers
- */
-export async function stopWorkers() {
- const workers = await getWorkers();
-
- // Run each request on its own
- // to avoid connect timeout errors.
- console.log( `${ workers.length } workers running...` );
- for ( const worker of workers ) {
- try {
- await deleteWorker( worker.id );
- } catch ( error ) {
-
- // Log the error, but continue trying to remove workers.
- console.error( error );
- }
- }
- console.log( "All workers stopped." );
-}
-
-/**
- * =============================
- * Plan API
- * =============================
- */
-
-export function getPlan() {
- return fetchAPI( "/automate/plan.json", {}, false );
-}
-
-export async function getAvailableSessions() {
- try {
- const [ plan, workers ] = await Promise.all( [ getPlan(), getWorkers() ] );
- return plan.parallel_sessions_max_allowed - workers.length;
- } catch ( error ) {
- console.error( error );
- return 0;
- }
-}
diff --git a/tests/runner/browserstack/buildBrowserFromString.js b/tests/runner/browserstack/buildBrowserFromString.js
deleted file mode 100644
index e0d99a039..000000000
--- a/tests/runner/browserstack/buildBrowserFromString.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export function buildBrowserFromString( str ) {
- const [ browser, versionOrDevice, os, osVersion ] = str.split( "_" );
-
- // If the version starts with a colon, it's a device
- if ( versionOrDevice && versionOrDevice.startsWith( ":" ) ) {
- return {
- browser,
- device: versionOrDevice.slice( 1 ),
- os,
- os_version: osVersion
- };
- }
-
- return {
- browser,
- browser_version: versionOrDevice,
- os,
- os_version: osVersion
- };
-}
diff --git a/tests/runner/browserstack/createAuthHeader.js b/tests/runner/browserstack/createAuthHeader.js
deleted file mode 100644
index fe4831e9a..000000000
--- a/tests/runner/browserstack/createAuthHeader.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const textEncoder = new TextEncoder();
-
-export function createAuthHeader( username, accessKey ) {
- const encoded = textEncoder.encode( `${ username }:${ accessKey }` );
- const base64 = btoa( String.fromCodePoint.apply( null, encoded ) );
- return `Basic ${ base64 }`;
-}
diff --git a/tests/runner/browserstack/local.js b/tests/runner/browserstack/local.js
deleted file mode 100644
index c84cf155c..000000000
--- a/tests/runner/browserstack/local.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import browserstackLocal from "browserstack-local";
-
-export async function localTunnel( localIdentifier, opts = {} ) {
- const tunnel = new browserstackLocal.Local();
-
- return new Promise( ( resolve, reject ) => {
-
- // https://www.browserstack.com/docs/local-testing/binary-params
- tunnel.start(
- {
- "enable-logging-for-api": "",
- localIdentifier,
- ...opts
- },
- async( error ) => {
- if ( error || !tunnel.isRunning() ) {
- return reject( error );
- }
- resolve( {
- stop: function stopTunnel() {
- return new Promise( ( resolve, reject ) => {
- tunnel.stop( ( error ) => {
- if ( error ) {
- return reject( error );
- }
- resolve();
- } );
- } );
- }
- } );
- }
- );
- } );
-}