Skip to content

Commit 928f972

Browse files
feat: Allow user to set browser preferences
1 parent 12b2c8e commit 928f972

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ npm install chrome-launcher
4040
// Do note, many flags are set by default: https://github.com/GoogleChrome/chrome-launcher/blob/master/src/flags.ts
4141
chromeFlags: Array<string>;
4242

43+
// (optional) Additional preferences to be set in Chrome, for example: {'download.default_directory': __dirname}
44+
// See: https://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/pref_names.cc?view=markup
45+
// Do note, if you set preferences when using your default profile it will overwrite these
46+
prefs: Record<string, string | boolean | number>;
47+
4348
// (optional) Close the Chrome process on `Ctrl-C`
4449
// Default: true
4550
handleSIGINT: boolean;

src/chrome-launcher.ts

+33-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ type SupportedPlatforms = 'darwin'|'linux'|'win32'|'wsl';
2626

2727
const instances = new Set<Launcher>();
2828

29+
type ValidPrefTypes = string|boolean|number
30+
2931
export interface Options {
3032
startingUrl?: string;
3133
chromeFlags?: Array<string>;
34+
prefs?: Record<string, ValidPrefTypes>;
3235
port?: number;
3336
handleSIGINT?: boolean;
3437
chromePath?: string;
@@ -105,6 +108,7 @@ class Launcher {
105108
private chromePath?: string;
106109
private ignoreDefaultFlags?: boolean;
107110
private chromeFlags: string[];
111+
private prefs: Record<string, ValidPrefTypes>;
108112
private requestedPort?: number;
109113
private connectionPollInterval: number;
110114
private maxConnectionRetries: number;
@@ -127,6 +131,7 @@ class Launcher {
127131
// choose the first one (default)
128132
this.startingUrl = defaults(this.opts.startingUrl, 'about:blank');
129133
this.chromeFlags = defaults(this.opts.chromeFlags, []);
134+
this.prefs = defaults(this.opts.prefs, {});
130135
this.requestedPort = defaults(this.opts.port, 0);
131136
this.chromePath = this.opts.chromePath;
132137
this.ignoreDefaultFlags = defaults(this.opts.ignoreDefaultFlags, false);
@@ -197,6 +202,11 @@ class Launcher {
197202
this.outFile = this.fs.openSync(`${this.userDataDir}/chrome-out.log`, 'a');
198203
this.errFile = this.fs.openSync(`${this.userDataDir}/chrome-err.log`, 'a');
199204

205+
// set preferences
206+
if (Object.keys(this.prefs).length) {
207+
this.setBrowserPrefs()
208+
}
209+
200210
// fix for Node4
201211
// you can't pass a fd to fs.writeFileSync
202212
this.pidFile = `${this.userDataDir}/chrome.pid`;
@@ -206,6 +216,23 @@ class Launcher {
206216
this.tmpDirandPidFileReady = true;
207217
}
208218

219+
private setBrowserPrefs() {
220+
const preferenceFile = `${this.userDataDir}/Preferences`;
221+
try {
222+
if (this.fs.existsSync(preferenceFile)) {
223+
// overwrite existing file
224+
const file = this.fs.readFileSync(preferenceFile, 'utf-8');
225+
const content = JSON.parse(file.toString());
226+
this.fs.writeFileSync(preferenceFile, JSON.stringify({...content, ...this.prefs}), 'utf-8');
227+
} else {
228+
// create new Preference file
229+
this.fs.writeFileSync(preferenceFile, JSON.stringify({...this.prefs}), 'utf-8');
230+
}
231+
} catch (err) {
232+
log.log('ChromeLauncher', `Failed to set browser prefs: ${err.message}`);
233+
}
234+
}
235+
209236
async launch() {
210237
if (this.requestedPort !== 0) {
211238
this.port = this.requestedPort;
@@ -259,7 +286,9 @@ class Launcher {
259286
{detached: true, stdio: ['ignore', this.outFile, this.errFile], env: this.envVars});
260287
this.chrome = chrome;
261288

262-
this.fs.writeFileSync(this.pidFile, chrome.pid.toString());
289+
if (chrome.pid) {
290+
this.fs.writeFileSync(this.pidFile, chrome.pid.toString());
291+
}
263292

264293
log.verbose('ChromeLauncher', `Chrome running with pid ${chrome.pid} on port ${this.port}.`);
265294
return chrome.pid;
@@ -347,7 +376,9 @@ class Launcher {
347376
// if you don't explicitly set `stdio`
348377
execSync(`taskkill /pid ${this.chrome.pid} /T /F`, {stdio: 'pipe'});
349378
} else {
350-
process.kill(-this.chrome.pid);
379+
if (this.chrome.pid) {
380+
process.kill(-this.chrome.pid);
381+
}
351382
}
352383
} catch (err) {
353384
const message = `Chrome could not be killed ${err.message}`;

test/chrome-launcher-test.ts

+31
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,37 @@ describe('Launcher', () => {
6464
assert.strictEqual(fs.rmdir.callCount, 0);
6565
});
6666

67+
it('allows to overwrite browser prefs', async () => {
68+
const existStub = stub().returns(true)
69+
const readFileStub = stub().returns(Buffer.from(JSON.stringify({ some: 'prefs' })))
70+
const writeFileStub = stub()
71+
const fs = {...fsMock, rmdir: spy(), readFileSync: readFileStub, writeFileSync: writeFileStub, existsSync: existStub };
72+
const chromeInstance =
73+
new Launcher({prefs: {'download.default_directory': '/some/dir'}}, {fs: fs as any});
74+
75+
chromeInstance.prepare();
76+
assert.equal(
77+
writeFileStub.getCall(0).args[1],
78+
'{"some":"prefs","download.default_directory":"/some/dir"}'
79+
)
80+
});
81+
82+
it('allows to set browser prefs', async () => {
83+
const existStub = stub().returns(false)
84+
const readFileStub = stub().returns(Buffer.from(JSON.stringify({ some: 'prefs' })))
85+
const writeFileStub = stub()
86+
const fs = {...fsMock, rmdir: spy(), readFileSync: readFileStub, writeFileSync: writeFileStub, existsSync: existStub };
87+
const chromeInstance =
88+
new Launcher({prefs: {'download.default_directory': '/some/dir'}}, {fs: fs as any});
89+
90+
chromeInstance.prepare();
91+
assert.equal(readFileStub.getCalls().length, 0)
92+
assert.equal(
93+
writeFileStub.getCall(0).args[1],
94+
'{"download.default_directory":"/some/dir"}'
95+
)
96+
});
97+
6798
it('cleans up the tmp dir after closing', async () => {
6899
const rmdirMock = stub().callsFake((_path, _options, done) => done());
69100
const fs = {...fsMock, rmdir: rmdirMock};

0 commit comments

Comments
 (0)