diff --git a/README.md b/README.md index fc937552cd..93501ddc8e 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,28 @@ webExt.cmd.run({ }); ``` +If you would like to run an extension on Firefox for Android: + +```js +// Path to adb binary (optional parameter, auto-detected if missing) +const adbBin = "/path/to/adb"; +// Get an array of device ids (Array) +const deviceIds = await webExt.util.adb.listADBDevices(adbBin); +const adbDevice = ... +// Get an array of Firefox APKs (Array) +const firefoxAPKs = await webExt.util.adb.listADBFirefoxAPKs( + deviceId, adbBin +); +const firefoxApk = ... + +webExt.cmd.run({ + target: 'firefox-android', + firefoxApk, + adbDevice, + sourceDir: ... +}).then((extensionRunner) => {...}); +``` + If you would like to control logging, you can access the logger object. Here is an example of turning on verbose logging: ```js diff --git a/src/main.js b/src/main.js index 3a1a456550..d2c9d8d74d 100644 --- a/src/main.js +++ b/src/main.js @@ -3,8 +3,17 @@ import {main} from './program'; import cmd from './cmd'; import * as logger from './util/logger'; -// This only exposes util/logger so far. +// This only exposes util/logger and a subset of util/adb so far. // Do we need anything else? -const util = {logger}; +const util = { + logger, + // Lazy load the adb module when util.adb is accessed for the first time, to defer loading + // the npm dependencies used by the adb module to when actually needed. + // This is being done to continue the courtesy of web-ext issue #1301 + get adb(): Object { + const {listADBDevices, listADBFirefoxAPKs} = require('./util/adb'); + return {listADBDevices, listADBFirefoxAPKs}; + }, +}; export default {main, cmd, util}; diff --git a/src/util/adb.js b/src/util/adb.js index 213f0134fb..2aa667a05e 100644 --- a/src/util/adb.js +++ b/src/util/adb.js @@ -418,3 +418,16 @@ export default class ADBUtils { }); } } + +export async function listADBDevices(adbBin?: string): Promise> { + const adbClient = new ADBUtils({adbBin}); + return adbClient.discoverDevices(); +} + +export async function listADBFirefoxAPKs( + deviceId: string, + adbBin?: string +): Promise> { + const adbClient = new ADBUtils({adbBin}); + return adbClient.discoverInstalledFirefoxAPKs(deviceId); +} diff --git a/tests/fixtures/import-as-esm/test-import.mjs b/tests/fixtures/import-as-esm/test-import.mjs deleted file mode 100644 index 0b1a856b20..0000000000 --- a/tests/fixtures/import-as-esm/test-import.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import assert from 'assert'; - -// eslint-disable-next-line import/no-unresolved -import webExt from 'web-ext'; - -assert.deepEqual(Object.keys(webExt).sort(), ['cmd', 'main', 'util'].sort()); -assert.equal(typeof webExt.cmd.run, 'function'); diff --git a/tests/fixtures/require-as-cjs/test-require.js b/tests/fixtures/require-as-cjs/test-require.js deleted file mode 100644 index a9a51b81a6..0000000000 --- a/tests/fixtures/require-as-cjs/test-require.js +++ /dev/null @@ -1,6 +0,0 @@ -const assert = require('assert'); - -const webExt = require('web-ext'); - -assert.deepEqual(Object.keys(webExt).sort(), ['cmd', 'main', 'util'].sort()); -assert.equal(typeof webExt.cmd.run, 'function'); diff --git a/tests/fixtures/webext-as-library/helpers.js b/tests/fixtures/webext-as-library/helpers.js new file mode 100644 index 0000000000..513fa70a3f --- /dev/null +++ b/tests/fixtures/webext-as-library/helpers.js @@ -0,0 +1,32 @@ +const assert = require('assert'); +const path = require('path'); + +function testModuleExports(webExt) { + assert.deepEqual(Object.keys(webExt).sort(), ['cmd', 'main', 'util'].sort()); + assert.deepEqual(Object.keys(webExt.util).sort(), ['logger', 'adb'].sort()); + assert.equal(typeof webExt.cmd.run, 'function'); + + assertImportedADB({expectLoaded: false}); + assert.deepEqual( + Object.keys(webExt.util.adb).sort(), + ['listADBDevices', 'listADBFirefoxAPKs'].sort(), + ); + assertImportedADB({expectLoaded: true}); +} + +function assertImportedADB({expectLoaded}) { + const adbPathString = path.join('@devicefarmer', 'adbkit'); + const hasAdbDeps = Object.keys(require.cache).filter( + (filePath) => filePath.includes(adbPathString) + ).length > 0; + + const msg = expectLoaded + ? 'adb module should have been loaded' + : 'adb module should not be loaded yet'; + + assert.equal(hasAdbDeps, expectLoaded, msg); +} + +module.exports = { + testModuleExports, +}; diff --git a/tests/fixtures/webext-as-library/test-import.mjs b/tests/fixtures/webext-as-library/test-import.mjs new file mode 100644 index 0000000000..8d9cf34636 --- /dev/null +++ b/tests/fixtures/webext-as-library/test-import.mjs @@ -0,0 +1,9 @@ +// eslint-disable-next-line import/no-unresolved +import webExt from 'web-ext'; + +// eslint-disable-next-line import/extensions +import helpers from './helpers.js'; + +const {testModuleExports} = helpers; + +testModuleExports(webExt); diff --git a/tests/fixtures/webext-as-library/test-require.js b/tests/fixtures/webext-as-library/test-require.js new file mode 100644 index 0000000000..afc2f39874 --- /dev/null +++ b/tests/fixtures/webext-as-library/test-require.js @@ -0,0 +1,5 @@ +const webExt = require('web-ext'); + +const {testModuleExports} = require('./helpers.js'); + +testModuleExports(webExt); diff --git a/tests/functional/common.js b/tests/functional/common.js index e78d6bedee..00e8fe5561 100644 --- a/tests/functional/common.js +++ b/tests/functional/common.js @@ -20,10 +20,8 @@ export const fixturesDir: string = path.join(functionalTestsDir, '..', 'fixtures'); export const minimalAddonPath: string = path.join(fixturesDir, 'minimal-web-ext'); -export const fixtureEsmImport: string = - path.join(fixturesDir, 'import-as-esm'); -export const fixtureCjsRequire: string = - path.join(fixturesDir, 'require-as-cjs'); +export const fixturesUseAsLibrary: string = + path.join(fixturesDir, 'webext-as-library'); export const fakeFirefoxPath: string = path.join( functionalTestsDir, process.platform === 'win32' ? diff --git a/tests/functional/test.lib.imports.js b/tests/functional/test.lib.imports.js index ce23a4ec40..0c0bbd45ea 100644 --- a/tests/functional/test.lib.imports.js +++ b/tests/functional/test.lib.imports.js @@ -6,7 +6,7 @@ import {describe, it, before, after} from 'mocha'; import shell from 'shelljs'; import { - withTempDir, fixtureEsmImport, fixtureCjsRequire, + withTempDir, fixturesUseAsLibrary, } from './common'; const npm = shell.which('npm').toString(); @@ -35,7 +35,7 @@ describe('web-ext imported as a library', () => { it('can be imported as an ESM module', async () => { await withTempDir(async (tmpDir) => { execFileSync(npm, ['link', 'web-ext'], {cwd: tmpDir.path()}); - shell.cp('-rf', `${fixtureEsmImport}/*`, tmpDir.path()); + shell.cp('-rf', `${fixturesUseAsLibrary}/*`, tmpDir.path()); execFileSync(node, ['--experimental-modules', 'test-import.mjs'], { cwd: tmpDir.path(), }); @@ -45,7 +45,7 @@ describe('web-ext imported as a library', () => { it('can be imported as a CommonJS module', async () => { await withTempDir(async (tmpDir) => { execFileSync(npm, ['link', 'web-ext'], {cwd: tmpDir.path()}); - shell.cp('-rf', `${fixtureCjsRequire}/*`, tmpDir.path()); + shell.cp('-rf', `${fixturesUseAsLibrary}/*`, tmpDir.path()); execFileSync(node, ['--experimental-modules', 'test-require.js'], { cwd: tmpDir.path(), }); diff --git a/tests/unit/test-util/test.adb.js b/tests/unit/test-util/test.adb.js index ac97324431..60b25e74c8 100644 --- a/tests/unit/test-util/test.adb.js +++ b/tests/unit/test-util/test.adb.js @@ -13,6 +13,7 @@ import { import ADBUtils, { ARTIFACTS_DIR_PREFIX, DEVICE_DIR_BASE, + listADBDevices, listADBFirefoxAPKs, } from '../../../src/util/adb'; import { consoleStream, // instance is imported to inspect logged messages @@ -1310,4 +1311,31 @@ describe('utils/adb', () => { }); }); + describe('exports exposed in util.adb', () => { + it('should export a listADBDevices method', async () => { + const stubDiscoverDevices = sinon.stub( + ADBUtils.prototype, 'discoverDevices' + ); + stubDiscoverDevices.resolves(['emulator1', 'device2']); + const promise = listADBDevices(); + const devices = await assert.isFulfilled(promise); + assert.deepEqual(devices, ['emulator1', 'device2']); + }); + + it('should export a listADBFirefoxAPKs method', async () => { + const stubDiscoverInstalledFirefoxAPKs = sinon.stub( + ADBUtils.prototype, 'discoverInstalledFirefoxAPKs' + ); + stubDiscoverInstalledFirefoxAPKs + .resolves(['package1', 'package2', 'package3']); + const promise = listADBFirefoxAPKs('device1'); + const packages = await assert.isFulfilled(promise); + sinon.assert.calledWith( + stubDiscoverInstalledFirefoxAPKs, + 'device1' + ); + assert.deepEqual(packages, ['package1', 'package2', 'package3']); + }); + }); + }); diff --git a/tests/unit/test.web-ext.js b/tests/unit/test.web-ext.js index e40f6fbb3f..9bdd75dbb1 100644 --- a/tests/unit/test.web-ext.js +++ b/tests/unit/test.web-ext.js @@ -6,6 +6,7 @@ import sinon from 'sinon'; import webExt from '../../src/main'; import {main} from '../../src/program'; import {consoleStream} from '../../src/util/logger'; +import {listADBDevices, listADBFirefoxAPKs} from '../../src/util/adb'; describe('webExt', () => { @@ -17,6 +18,16 @@ describe('webExt', () => { assert.equal(webExt.util.logger.consoleStream, consoleStream); }); + describe('exposes adb utils', () => { + it('gives access to listADBDevices', () => { + assert.equal(webExt.util.adb.listADBDevices, listADBDevices); + }); + + it('gives access to listADBFirefoxAPKs', () => { + assert.equal(webExt.util.adb.listADBFirefoxAPKs, listADBFirefoxAPKs); + }); + }); + describe('exposes commands', () => { let stub: any; afterEach(() => {