Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run extension with a custom Firefox binary #105

Merged
merged 1 commit into from
Mar 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions src/cli.js

This file was deleted.

3 changes: 1 addition & 2 deletions src/cmd/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import streamToPromise from 'stream-to-promise';
import {onlyErrorsWithCode} from '../errors';
import fs from 'mz/fs';
import {zipDir} from '../util/zip-dir';
import {ProgramOptions} from '../program';
import getValidatedManifest from '../util/manifest';


export default function build(
{sourceDir, buildDir}: ProgramOptions,
{sourceDir, buildDir}: Object,
{manifestData}: Object = {}): Promise {

console.log(`Building web extension from ${sourceDir}`);
Expand Down
4 changes: 4 additions & 0 deletions src/cmd/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* @flow */
import build from './build';
import run from './run';
export default {build, run};
5 changes: 2 additions & 3 deletions src/cmd/run.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/* @flow */
import buildExtension from './build';
import {ProgramOptions} from '../program';
import * as defaultFirefox from '../firefox';
import {withTempDir} from '../util/temp-dir';
import getValidatedManifest from '../util/manifest';


export default function run(
{sourceDir}: ProgramOptions,
{sourceDir, firefoxBinary}: Object,
{firefox=defaultFirefox}: Object = {}): Promise {

console.log(`Running web extension from ${sourceDir}`);
Expand All @@ -30,6 +29,6 @@ export default function run(
})
.then(() => profile);
})
.then((profile) => firefox.run(profile))
.then((profile) => firefox.run(profile, {firefoxBinary}))
));
}
8 changes: 5 additions & 3 deletions src/firefox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ export const defaultFirefoxEnv = {
};

export function run(
profile: FirefoxProfile, {fxRunner=defaultFxRunner}: Object = {}): Promise {
profile: FirefoxProfile,
{fxRunner=defaultFxRunner, firefoxBinary}: Object = {}): Promise {

console.log(`Running Firefox with profile at ${profile.path()}`);
return fxRunner(
{
'binary': null,
// if this is falsey, fxRunner tries to find the default one.
'binary': firefoxBinary,
'binary-args': null,
'no-remote': true,
'foreground': true,
Expand Down Expand Up @@ -50,7 +52,7 @@ export function run(
console.error(`stderr: ${data.toString().trim()}`);
});

firefox.stdout.on('data', function(data) {
firefox.stdout.on('data', (data) => {
console.log(`stdout: ${data.toString().trim()}`);
});

Expand Down
4 changes: 2 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/* @flow */
import * as cli from './cli';
export const main = cli.main;
import {main} from './program';
export {main};
114 changes: 92 additions & 22 deletions src/program.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,52 @@
/* @flow */
import path from 'path';
import {readFileSync} from 'fs';
import yargs from 'yargs';

import defaultCommands from './cmd';
import {WebExtError} from './errors';


/*
* Pseudo-class for all global program options.
*
* This is used to validate the definition of command handlers.
* Each class instance variable is a camel case expanded program
* option. Each option is defined in program.yargs.option(...).
*/
export class ProgramOptions {
sourceDir: string;
buildDir: string;
}


/*
* The command line program.
*/
export class Program {
yargs: any;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this because it wasn't working (too many errors I could not resolve). I filed #106 to look for a solution.

commands: { [key: string]: Function };

constructor(argv: ?Array<string>) {
constructor(argv: ?Array<string>, {yargsInstance=yargs}: Object = {}) {
if (argv !== undefined) {
this.yargs = yargs(argv);
} else {
this.yargs = yargs;
// This allows us to override the process argv which is useful for
// testing.
yargsInstance = yargs(argv);
}
this.yargs = yargsInstance;
this.commands = {};
}

command(name: string, description: string, executor: Function,
configureCommand: ?Function): Program {
this.yargs.command(name, description, configureCommand);
commandOptions: ?Object): Program {
this.yargs.command(name, description, (yargs) => {
if (!commandOptions) {
return;
}
return yargs.options(commandOptions);
});
this.commands[name] = executor;
return this;
}

setGlobalOptions(options: Object): Program {
// This is a convenience for setting global options.
// An option is only global (i.e. available to all sub commands)
// with the `global` flag so this makes sure every option has it.
Object.keys(options).forEach((key) => {
options[key].global = true;
});
this.yargs.options(options);
return this;
}

run({throwError=false, systemProcess=process}: Object = {}): Promise {
let argv = this.yargs.argv;
let cmd = argv._[0];
Expand All @@ -48,12 +55,13 @@ export class Program {
if (cmd === undefined) {
throw new WebExtError('No sub-command was specified in the args');
}
if (!this.commands[cmd]) {
let runCommand = this.commands[cmd];
if (!runCommand) {
throw new WebExtError(`unknown command: ${cmd}`);
}
resolve();
resolve(runCommand);
})
.then(() => this.commands[cmd](argv))
.then((runCommand) => runCommand(argv))
.catch((error) => {
let prefix = '';
if (cmd) {
Expand All @@ -72,3 +80,65 @@ export class Program {
});
}
}


export function version(absolutePackageDir: string): string {
let packageData: any = readFileSync(
path.join(absolutePackageDir, 'package.json'));
return JSON.parse(packageData).version;
}


export function main(
absolutePackageDir: string,
{commands=defaultCommands, argv, runOptions={}}: Object = {}): Promise {
let program = new Program(argv);
// yargs uses magic camel case expansion to expose options on the
// final argv object. For example, the 'build-dir' option is alternatively
// available as argv.buildDir.
program.yargs
.usage(`Usage: $0 [options] command

Option values can also be set by declaring an environment variable prefixed
with \$WEB_EXT_. For example: $WEB_EXT_SOURCE_DIR=/path is the same as
--source-dir=/path.
`)
.help('help')
.alias('h', 'help')
.env('WEB_EXT')
.version(() => version(absolutePackageDir));

program.setGlobalOptions({
'source-dir': {
alias: 's',
describe: 'Web extension source directory.',
default: process.cwd(),
requiresArg: true,
demand: true,
type: 'string',
},
});

program
.command('build', 'Create a web extension package from source',
commands.build, {
'build-dir': {
alias: 'b',
describe: 'Directory where built artifacts will be saved.',
default: path.join(process.cwd(), 'web-ext-build'),
requiresArg: true,
demand: true,
type: 'string',
},
})
.command('run', 'Run the web extension', commands.run, {
'firefox-binary': {
describe: 'Path to a Firefox executable such as firefox-bin. ' +
'If not specified, the default Firefox will be used.',
demand: false,
type: 'string',
},
});

return program.run(runOptions);
}
5 changes: 4 additions & 1 deletion src/util/temp-dir.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ export class TempDir {
* Promise().then(tmp.successHandler())
*/
successHandler(): Function {
return () => this.remove();
return (promiseResult) => {
this.remove();
return promiseResult;
};
}

/*
Expand Down
12 changes: 6 additions & 6 deletions tests/test-cmd/test-run/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import runCommand from '../../../src/cmd/run';


export function run(sourceDir: string, firefox: FirefoxProfile): Promise {
return runCommand(
{
sourceDir,
buildDir: '/dev/null', // this should never be used.
},
{firefox});
return runCommand({sourceDir}, {firefox});
}

export function runWithFirefox(sourceDir: string, firefox: FirefoxProfile,
firefoxBinary: string): Promise {
return runCommand({sourceDir, firefoxBinary}, {firefox});
}
28 changes: 25 additions & 3 deletions tests/test-cmd/test-run/test.run.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ import * as adapter from './adapter';

describe('run', () => {

function getFakeFirefox(implementations={}) {
let allImplementations = {
createProfile: () => {
let profile = {}; // empty object just to avoid errors.
return Promise.resolve(profile);
},
installExtension: () => Promise.resolve(),
...implementations,
};
return fake(firefox, allImplementations);
}

it('installs and runs the extension', () => {

let profile = {};

let fakeFirefox = fake(firefox, {
let fakeFirefox = getFakeFirefox({
createProfile: () => Promise.resolve(profile),
installExtension: () => Promise.resolve(),
});

return adapter.run(fixturePath('minimal-web-ext'), fakeFirefox)
Expand All @@ -35,4 +45,16 @@ describe('run', () => {
});
});

it('passes a custom Firefox binary when specified', () => {
let firefoxBinary = '/pretend/path/to/Firefox/firefox-bin';
let fakeFirefox = getFakeFirefox();
return adapter.runWithFirefox(
fixturePath('minimal-web-ext'), fakeFirefox, firefoxBinary)
.then(() => {
assert.equal(fakeFirefox.run.called, true);
assert.equal(fakeFirefox.run.firstCall.args[1].firefoxBinary,
firefoxBinary);
});
});

});
7 changes: 6 additions & 1 deletion tests/test-firefox/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import * as preferences from '../../src/firefox/preferences';


export function run(profile: Object, fxRunner: Function): Promise {
return firefox.run(profile, {fxRunner: fxRunner});
return firefox.run(profile, {fxRunner});
}

export function runWithFirefox(profile: Object, fxRunner: Function,
firefoxBinary: string): Promise {
return firefox.run(profile, {fxRunner, firefoxBinary});
}


Expand Down
11 changes: 11 additions & 0 deletions tests/test-firefox/test.firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ describe('firefox', () => {
});
});

it('passes a custom Firefox binary when specified', () => {
let runner = createFakeFxRunner();
let firefoxBinary = '/pretend/path/to/firefox-bin';
return adapter.runWithFirefox(fakeProfile, runner, firefoxBinary)
.then(() => {
assert.equal(runner.called, true);
assert.equal(runner.firstCall.args[0].binary,
firefoxBinary);
});
});

});

describe('createProfile', () => {
Expand Down
Loading