Skip to content

Commit

Permalink
feat: Allow user to set browser preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-bromann committed Sep 7, 2021
1 parent 12b2c8e commit 1a6fea4
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ npm install chrome-launcher
// Do note, many flags are set by default: https://github.com/GoogleChrome/chrome-launcher/blob/master/src/flags.ts
chromeFlags: Array<string>;

// (optional) Additional preferences to be set in Chrome, for example: {'download.default_directory': __dirname}
// See: https://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/pref_names.cc?view=markup
// Do note, if you set preferences when using your default profile it will overwrite these
prefs: {[key: string]: Object};

// (optional) Close the Chrome process on `Ctrl-C`
// Default: true
handleSIGINT: boolean;
Expand Down
37 changes: 35 additions & 2 deletions src/chrome-launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ type SupportedPlatforms = 'darwin'|'linux'|'win32'|'wsl';

const instances = new Set<Launcher>();

type JSONLike =|{[property: string]: JSONLike}|readonly JSONLike[]|string|number|boolean|null;

export interface Options {
startingUrl?: string;
chromeFlags?: Array<string>;
prefs?: Record<string, JSONLike>;
port?: number;
handleSIGINT?: boolean;
chromePath?: string;
Expand Down Expand Up @@ -105,6 +108,7 @@ class Launcher {
private chromePath?: string;
private ignoreDefaultFlags?: boolean;
private chromeFlags: string[];
private prefs: Record<string, JSONLike>;
private requestedPort?: number;
private connectionPollInterval: number;
private maxConnectionRetries: number;
Expand All @@ -127,6 +131,7 @@ class Launcher {
// choose the first one (default)
this.startingUrl = defaults(this.opts.startingUrl, 'about:blank');
this.chromeFlags = defaults(this.opts.chromeFlags, []);
this.prefs = defaults(this.opts.prefs, {});
this.requestedPort = defaults(this.opts.port, 0);
this.chromePath = this.opts.chromePath;
this.ignoreDefaultFlags = defaults(this.opts.ignoreDefaultFlags, false);
Expand Down Expand Up @@ -197,6 +202,8 @@ class Launcher {
this.outFile = this.fs.openSync(`${this.userDataDir}/chrome-out.log`, 'a');
this.errFile = this.fs.openSync(`${this.userDataDir}/chrome-err.log`, 'a');

this.setBrowserPrefs();

// fix for Node4
// you can't pass a fd to fs.writeFileSync
this.pidFile = `${this.userDataDir}/chrome.pid`;
Expand All @@ -206,6 +213,28 @@ class Launcher {
this.tmpDirandPidFileReady = true;
}

private setBrowserPrefs() {
// don't set prefs if not defined
if (Object.keys(this.prefs).length) {
return
}

const preferenceFile = `${this.userDataDir}/Preferences`;
try {
if (this.fs.existsSync(preferenceFile)) {
// overwrite existing file
const file = this.fs.readFileSync(preferenceFile, 'utf-8');
const content = JSON.parse(file);
this.fs.writeFileSync(preferenceFile, JSON.stringify({...content, ...this.prefs}), 'utf-8');
} else {
// create new Preference file
this.fs.writeFileSync(preferenceFile, JSON.stringify({...this.prefs}), 'utf-8');
}
} catch (err) {
log.log('ChromeLauncher', `Failed to set browser prefs: ${err.message}`);
}
}

async launch() {
if (this.requestedPort !== 0) {
this.port = this.requestedPort;
Expand Down Expand Up @@ -259,7 +288,9 @@ class Launcher {
{detached: true, stdio: ['ignore', this.outFile, this.errFile], env: this.envVars});
this.chrome = chrome;

this.fs.writeFileSync(this.pidFile, chrome.pid.toString());
if (chrome.pid) {
this.fs.writeFileSync(this.pidFile, chrome.pid.toString());
}

log.verbose('ChromeLauncher', `Chrome running with pid ${chrome.pid} on port ${this.port}.`);
return chrome.pid;
Expand Down Expand Up @@ -347,7 +378,9 @@ class Launcher {
// if you don't explicitly set `stdio`
execSync(`taskkill /pid ${this.chrome.pid} /T /F`, {stdio: 'pipe'});
} else {
process.kill(-this.chrome.pid);
if (this.chrome.pid) {
process.kill(-this.chrome.pid);
}
}
} catch (err) {
const message = `Chrome could not be killed ${err.message}`;
Expand Down
31 changes: 31 additions & 0 deletions test/chrome-launcher-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,37 @@ describe('Launcher', () => {
assert.strictEqual(fs.rmdir.callCount, 0);
});

it('allows to overwrite browser prefs', async () => {
const existStub = stub().returns(true)
const readFileStub = stub().returns(Buffer.from(JSON.stringify({ some: 'prefs' })))
const writeFileStub = stub()
const fs = {...fsMock, rmdir: spy(), readFileSync: readFileStub, writeFileSync: writeFileStub, existsSync: existStub };
const chromeInstance =
new Launcher({prefs: {'download.default_directory': '/some/dir'}}, {fs: fs as any});

chromeInstance.prepare();
assert.equal(
writeFileStub.getCall(0).args[1],
'{"some":"prefs","download.default_directory":"/some/dir"}'
)
});

it('allows to set browser prefs', async () => {
const existStub = stub().returns(false)
const readFileStub = stub().returns(Buffer.from(JSON.stringify({ some: 'prefs' })))
const writeFileStub = stub()
const fs = {...fsMock, rmdir: spy(), readFileSync: readFileStub, writeFileSync: writeFileStub, existsSync: existStub };
const chromeInstance =
new Launcher({prefs: {'download.default_directory': '/some/dir'}}, {fs: fs as any});

chromeInstance.prepare();
assert.equal(readFileStub.getCalls().length, 0)
assert.equal(
writeFileStub.getCall(0).args[1],
'{"download.default_directory":"/some/dir"}'
)
});

it('cleans up the tmp dir after closing', async () => {
const rmdirMock = stub().callsFake((_path, _options, done) => done());
const fs = {...fsMock, rmdir: rmdirMock};
Expand Down

0 comments on commit 1a6fea4

Please sign in to comment.