Skip to content

Commit

Permalink
fix: Fix compatibility, browser launcher detection in shaka-player
Browse files Browse the repository at this point in the history
This makes these launchers compatible with the older launchers we
replaced, by defining DEFAULT_CMD and ENV_CMD on prototype.  This
allows the detection of available browser launchers in shaka-player
work again.

To do this, this change abstracts away several things that were
previously set in the constructor so that they are now set at the
class-definition level.  A new function called generateSubclass
handles all of these details before any instances are constructed.

Closes #23
  • Loading branch information
joeyparrish committed Feb 25, 2022
1 parent 0c3ebe6 commit b06cb99
Showing 1 changed file with 102 additions and 87 deletions.
189 changes: 102 additions & 87 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,39 +34,23 @@ const PLATFORM_MAP = {
'linux': 'Linux',
};

const LocalWebDriverBase = function(
browserName, name, driverCommand, argsFromPort, baseBrowserDecorator, logger) {
// Subclasses must define these static members:
// - BROWSER_NAME: browser name as presented to WebDriver
// - LAUNCHER_NAME: launcher name as presented to Karma
// - EXTRA_WEBDRIVER_SPECS: an object containing any extra WebDriver specs
// - getDriverArgs(port): take port as string, return driver command arguments
const LocalWebDriverBase = function(baseBrowserDecorator, logger) {
baseBrowserDecorator(this);

this.name = `${name} via WebDriver`;
this.name = `${this.constructor.LAUNCHER_NAME} via WebDriver`;
const log = logger.create(this.name);

this.browserName = browserName;

// An environment variable that can be used to override the command path.
// Must be computed before the cache path is prepended to the driverCommand.
this.ENV_CMD = driverCommand.toUpperCase().replace('-', '_') + '_PATH';

if (driverCommand[0] == '/') {
// Absolute path. Keep it.
} else {
// File name. Assume it's in our driver cache.
driverCommand = path.join(DRIVER_CACHE, driverCommand);
}

log.debug(`Default driver command: ${driverCommand}`);

// Checked by the base class to determine what command to run.
this.DEFAULT_CMD = {
linux: driverCommand,
darwin: driverCommand,
win32: driverCommand + '.exe',
};
this.browserName = this.constructor.BROWSER_NAME;

const port = Math.floor((Math.random() * 1000)) + 4000;

// Called by the base class to get arguments to pass to the driver command.
this._getOptions = () => argsFromPort(port.toString());
this._getOptions = () => this.constructor.getDriverArgs(port.toString());

const config = {
protocol: 'http:',
Expand All @@ -85,6 +69,8 @@ const LocalWebDriverBase = function(
platform: PLATFORM_MAP[os.platform()],
// This is necessary for safaridriver:
allowW3C: true,
// This allows extra configuration for headless variants:
...this.constructor.EXTRA_WEBDRIVER_SPECS,
};

this.browser.on('status', (info) => {
Expand Down Expand Up @@ -193,78 +179,107 @@ const LocalWebDriverBase = function(
};
}

const LocalWebDriverChrome = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'Chrome', 'Chrome', 'chromedriver', (port) => ['--port=' + port],
baseBrowserDecorator, logger);
};
// Generate a subclass of LocalWebDriverBase and return it.
function generateSubclass(
browserName, launcherName, driverCommand, getDriverArgs,
extraWebDriverSpecs={}) {
if (driverCommand[0] == '/') {
// Absolute path. Keep it.
} else {
// File name. Assume it will be found in our driver cache.
driverCommand = path.join(DRIVER_CACHE, driverCommand);
}

const LocalWebDriverChromeHeadless = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'Chrome', 'ChromeHeadless', 'chromedriver', (port) => ['--port=' + port],
baseBrowserDecorator, logger);

this.spec['goog:chromeOptions'] = {
args: [
'--headless',
'--disable-gpu',
'--disable-dev-shm-usage',
],
// Karma will not use "new" to construct our class, so it can't be a true ES6
// class. Use the old function syntax instead.
const subclass = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this, baseBrowserDecorator, logger);
};
};

// TODO: Add Chrome on android?

const LocalWebDriverEdge = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'MSEdge', 'MSEdge', 'msedgedriver', (port) => ['--port=' + port],
baseBrowserDecorator, logger);
};
// These are needed by our base class, LocalWebDriverBase.
subclass.BROWSER_NAME = browserName;
subclass.LAUNCHER_NAME = launcherName;
subclass.EXTRA_WEBDRIVER_SPECS = extraWebDriverSpecs;
subclass.getDriverArgs = getDriverArgs;

// These are needed by Karma's base class for command-based launchers, and
// will also facilitate auto-detection of available browsers by Shaka Player:
const anyPathSeparator = /[\/\\]/; // Windows (backslash) or POSIX (slash)
const driverCommandName = driverCommand.split(anyPathSeparator).pop();
subclass.prototype.ENV_CMD =
driverCommandName.toUpperCase().replace('-', '_') + '_PATH';
subclass.prototype.DEFAULT_CMD = {
linux: driverCommand,
darwin: driverCommand,
win32: driverCommand + '.exe',
};

const LocalWebDriverEdgeHeadless = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'MSEdge', 'MSEdgeHeadless', 'msedgedriver', (port) => ['--port=' + port],
baseBrowserDecorator, logger);
// This configures Karma's dependency injection system:
subclass.$inject = ['baseBrowserDecorator', 'logger'];

this.spec['ms:edgeOptions'] = {
args: [
'--headless',
'--disable-gpu',
],
};
};
return subclass;
}

const LocalWebDriverFirefox = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'Firefox', 'Firefox', 'geckodriver', (port) => ['-p', port],
baseBrowserDecorator, logger);
};
const LocalWebDriverChrome = generateSubclass(
'Chrome', 'Chrome',
'chromedriver',
(port) => ['--port=' + port]);

const LocalWebDriverChromeHeadless = generateSubclass(
'Chrome', 'ChromeHeadless',
'chromedriver',
(port) => ['--port=' + port],
{
'goog:chromeOptions': {
args: [
'--headless',
'--disable-gpu',
'--disable-dev-shm-usage',
],
},
});

const LocalWebDriverFirefoxHeadless = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'Firefox', 'FirefoxHeadless', 'geckodriver', (port) => ['-p', port],
baseBrowserDecorator, logger);
// TODO: Add Chrome on android?

this.spec['moz:firefoxOptions'] = {
args: [
'-headless',
],
};
};
const LocalWebDriverEdge = generateSubclass(
'MSEdge', 'MSEdge',
'msedgedriver',
(port) => ['--port=' + port]);

const LocalWebDriverEdgeHeadless = generateSubclass(
'MSEdge', 'MSEdgeHeadless',
'msedgedriver',
(port) => ['--port=' + port],
{
'ms:edgeOptions': {
args: [
'--headless',
'--disable-gpu',
],
},
});

const LocalWebDriverSafari = function(baseBrowserDecorator, logger) {
LocalWebDriverBase.call(this,
'Safari', 'Safari', '/usr/bin/safaridriver', (port) => ['-p', port],
baseBrowserDecorator, logger);
};
const LocalWebDriverFirefox = generateSubclass(
'Firefox', 'Firefox',
'geckodriver',
(port) => ['-p', port]);

const LocalWebDriverFirefoxHeadless = generateSubclass(
'Firefox', 'FirefoxHeadless',
'geckodriver',
(port) => ['-p', port],
{
'moz:firefoxOptions': {
args: [
'-headless',
],
},
});

LocalWebDriverChrome.$inject = ['baseBrowserDecorator', 'logger'];
LocalWebDriverChromeHeadless.$inject = ['baseBrowserDecorator', 'logger'];
LocalWebDriverEdge.$inject = ['baseBrowserDecorator', 'logger'];
LocalWebDriverEdgeHeadless.$inject = ['baseBrowserDecorator', 'logger'];
LocalWebDriverFirefox.$inject = ['baseBrowserDecorator', 'logger'];
LocalWebDriverFirefoxHeadless.$inject = ['baseBrowserDecorator', 'logger'];
LocalWebDriverSafari.$inject = ['baseBrowserDecorator', 'logger'];
const LocalWebDriverSafari = generateSubclass(
'Safari', 'Safari',
'/usr/bin/safaridriver',
(port) => ['-p', port]);

module.exports = {
'launcher:Chrome': ['type', LocalWebDriverChrome],
Expand Down

0 comments on commit b06cb99

Please sign in to comment.