Skip to content

Commit

Permalink
Start browsers with clean profile by default (DevExpress#1792)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyBelym authored and AlexanderMoskovkin committed Sep 26, 2017
1 parent 83f41b6 commit 4d66894
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 101 deletions.
103 changes: 22 additions & 81 deletions src/browser/provider/built-in/chrome/config.js
Original file line number Diff line number Diff line change
@@ -1,100 +1,41 @@
import emulatedDevices from 'chrome-emulated-devices-list';
import OS from 'os-family';
import { find as findElement, pickBy as filterProperties } from 'lodash';
import { pickBy as filterProperties } from 'lodash';
import {
hasMatch, findMatch, isMatchTrue, getModes, splitEscaped, getPathFromParsedModes, parseConfig
} from '../../utils/argument-parsing';


const HEADLESS_DEFAULT_WIDTH = 1280;
const HEADLESS_DEFAULT_HEIGHT = 800;

const CONFIG_TERMINATOR_RE = /(\s+|^)-/;
const AVAILABLE_MODES = ['userProfile', 'headless', 'emulation'];

var configCache = {};

function hasMatch (array, re) {
return !!findElement(array, el => el.match(re));
function hasCustomProfile (userArgs) {
return !!userArgs.match(/--user-data-dir=/);
}

function findMatch (array, re) {
var element = findElement(array, el => el.match(re));
function parseModes (modesStr, userArgs) {
var parsed = splitEscaped(modesStr, ':');
var path = getPathFromParsedModes(parsed, AVAILABLE_MODES);
var detectedModes = getModes(parsed, AVAILABLE_MODES);
var optionsString = '';

return element ? element.match(re)[1] : '';
}

function isMatchTrue (array, re) {
var match = findMatch(array, re);

return match && match !== '0' && match !== 'false';
}

function splitEscaped (str, splitterChar) {
var result = [''];

for (var i = 0; i < str.length; i++) {
if (str[i] === splitterChar) {
result.push('');
continue;
}

if (str[i] === '\\' && (str[i + 1] === '\\' || str [i + 1] === splitterChar))
i++;

result[result.length - 1] += str[i];
}

return result;
}

function parseConfig (str) {
var configTerminatorMatch = str.match(CONFIG_TERMINATOR_RE);

if (!configTerminatorMatch)
return { modesString: str, userArgs: '' };

return {
modesString: str.substr(0, configTerminatorMatch.index),
userArgs: str.substr(configTerminatorMatch.index + configTerminatorMatch[1].length)
};
}

function getPathFromParsedModes (modesList) {
if (!modesList.length)
return '';

if (modesList[0] === 'headless' || modesList[0] === 'emulation')
return '';

var path = modesList.shift();

if (OS.win && modesList.length && path.match(/^[A-Za-z]$/))
path += ':' + modesList.shift();

return path;
}

function parseModes (str) {
var parsed = splitEscaped(str, ':');
var path = getPathFromParsedModes(parsed);
var nextMode = parsed.shift();
var hasHeadless = nextMode === 'headless';

if (hasHeadless)
nextMode = parsed.shift();

var hasEmulation = nextMode === 'emulation';

if (hasEmulation)
nextMode = parsed.shift();
if (parsed.length)
optionsString = parsed.shift();

while (parsed.length)
nextMode += ':' + parsed.shift();
optionsString += ':' + parsed.shift();

var modes = {
path: path,
headless: hasHeadless,
emulation: hasEmulation || hasHeadless
path: path,
userProfile: detectedModes.userProfile || hasCustomProfile(userArgs),
headless: detectedModes.headless,
emulation: detectedModes.emulation || detectedModes.headless
};

return { modes, optionsString: nextMode || '' };
return { modes, optionsString };
}

function simplifyDeviceName (deviceName) {
Expand Down Expand Up @@ -154,7 +95,7 @@ function parseOptions (str, modes) {
width: Number(findMatch(parsed, /^width=(.*)/) || NaN),
height: Number(findMatch(parsed, /^height=(.*)/) || NaN),
scaleFactor: Number(findMatch(parsed, /^scaleFactor=(.*)/) || NaN),
userAgent: findMatch(parsed, /^userAgent=(.*)/),
userAgent: findMatch(parsed, /^userAgent=(.*)/)
};

specifiedDeviceOptions = filterProperties(specifiedDeviceOptions, optionValue => {
Expand All @@ -167,7 +108,7 @@ function parseOptions (str, modes) {

function getNewConfig (configString) {
var { userArgs, modesString } = parseConfig(configString);
var { modes, optionsString } = parseModes(modesString);
var { modes, optionsString } = parseModes(modesString, userArgs);
var options = parseOptions(optionsString, modes);

return Object.assign({ userArgs }, modes, options);
Expand Down
3 changes: 2 additions & 1 deletion src/browser/provider/built-in/chrome/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export default {

runtimeInfo.viewportSize = await this.runInitScript(browserId, GET_WINDOW_DIMENSIONS_INFO_SCRIPT);

await cdp.createClient(runtimeInfo);
if (runtimeInfo.config.headless || runtimeInfo.config.emulation)
await cdp.createClient(runtimeInfo);

this.openedBrowsers[browserId] = runtimeInfo;
},
Expand Down
19 changes: 7 additions & 12 deletions src/browser/provider/built-in/chrome/local-chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { findProcess, killProcess } from '../../../../utils/promisified-function

const BROWSER_CLOSING_TIMEOUT = 5;


function buildChromeArgs (config, cdpPort, platformArgs, userDataDir) {
return [`--remote-debugging-port=${cdpPort}`, `--user-data-dir=${userDataDir.name}`]
function buildChromeArgs (config, cdpPort, platformArgs, profileDir) {
return []
.concat(
config.headless || config.emulation ? [`--remote-debugging-port=${cdpPort}`] : [],
!config.userProfile ? [`--user-data-dir=${profileDir.name}`] : [],
config.headless ? ['--headless'] : [],
config.userArgs ? [config.userArgs] : [],
platformArgs ? [platformArgs] : []
Expand All @@ -32,17 +33,11 @@ async function killChrome (cdpPort) {
}
}

export async function start (pageUrl, { browserName, config, cdpPort, tempUserDataDir }) {
var chromeInfo = null;

if (config.path)
chromeInfo = await browserTools.getBrowserInfo(config.path);
else
chromeInfo = await browserTools.getBrowserInfo(browserName);

export async function start (pageUrl, { browserName, config, cdpPort, tempProfileDir }) {
var chromeInfo = await browserTools.getBrowserInfo(config.path || browserName);
var chromeOpenParameters = Object.assign({}, chromeInfo);

chromeOpenParameters.cmd = buildChromeArgs(config, cdpPort, chromeOpenParameters.cmd, tempUserDataDir);
chromeOpenParameters.cmd = buildChromeArgs(config, cdpPort, chromeOpenParameters.cmd, tempProfileDir);

await browserTools.open(chromeOpenParameters, pageUrl);
}
Expand Down
6 changes: 3 additions & 3 deletions src/browser/provider/built-in/chrome/runtime-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import { getFreePort } from 'endpoint-utils';
import getConfig from './config';


function createTempUserDataDir () {
function createTempProfileDir () {
tmp.setGracefulCleanup();

return tmp.dirSync({ unsafeCleanup: true });
}

export default async function (configString) {
var config = getConfig(configString);
var tempUserDataDir = createTempUserDataDir();
var tempProfileDir = !config.userProfile ? createTempProfileDir() : null;
var cdpPort = config.cdpPort || await getFreePort();

return { config, cdpPort, tempUserDataDir };
return { config, cdpPort, tempProfileDir };
}
36 changes: 36 additions & 0 deletions src/browser/provider/built-in/firefox/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { splitEscaped, parseConfig, getModes, getPathFromParsedModes } from '../../utils/argument-parsing';


const AVAILABLE_MODES = ['userProfile'];

var configCache = {};

function hasCustomProfile (userArgs) {
return !!(userArgs.match(/-P\s/) || userArgs.match(/-profile\s/));
}

function parseModes (modesStr, userArgs) {
var parsed = splitEscaped(modesStr, ':');
var path = getPathFromParsedModes(parsed, AVAILABLE_MODES);
var detectedModes = getModes(parsed, AVAILABLE_MODES);

return {
path: path,
userProfile: detectedModes.userProfile || hasCustomProfile(userArgs)
};
}


function getNewConfig (configString) {
var { userArgs, modesString } = parseConfig(configString);
var modes = parseModes(modesString, userArgs);

return Object.assign({ userArgs }, modes);
}

export default function (configString) {
if (!configCache[configString])
configCache[configString] = getNewConfig(configString);

return configCache[configString];
}
28 changes: 28 additions & 0 deletions src/browser/provider/built-in/firefox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import browserTools from 'testcafe-browser-tools';
import getRuntimeInfo from './runtime-info';
import { start as startLocalFirefox } from './local-firefox';


export default {
openedBrowsers: {},

isMultiBrowser: false,

async openBrowser (browserId, pageUrl, configString) {
var runtimeInfo = await getRuntimeInfo(configString);
var browserName = this.providerName.replace(':', '');

runtimeInfo.browserId = browserId;
runtimeInfo.browserName = browserName;

await startLocalFirefox(pageUrl, runtimeInfo);
},

async closeBrowser (browserId) {
await browserTools.close(browserId);
},

async isLocalBrowser () {
return true;
},
};
22 changes: 22 additions & 0 deletions src/browser/provider/built-in/firefox/local-firefox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import browserTools from 'testcafe-browser-tools';


function buildFirefoxArgs (config, platformArgs, profileDir) {
return []
.concat(
!config.userProfile ? ['-no-remote', '-new-instance', `-profile "${profileDir.name}"`] : [],
config.userArgs ? [config.userArgs] : [],
platformArgs ? [platformArgs] : []
)
.join(' ');
}


export async function start (pageUrl, { browserName, config, tempProfileDir }) {
var firefoxInfo = await browserTools.getBrowserInfo(config.path || browserName);
var firefoxOpenParameters = Object.assign({}, firefoxInfo);

firefoxOpenParameters.cmd = buildFirefoxArgs(config, firefoxOpenParameters.cmd, tempProfileDir);

await browserTools.open(firefoxOpenParameters, pageUrl);
}
16 changes: 16 additions & 0 deletions src/browser/provider/built-in/firefox/runtime-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import tmp from 'tmp';
import getConfig from './config';


function createTempProfileDir () {
tmp.setGracefulCleanup();

return tmp.dirSync({ unsafeCleanup: true });
}

export default async function (configString) {
var config = getConfig(configString);
var tempProfileDir = !config.userProfile ? createTempProfileDir() : null;

return { config, tempProfileDir };
}
10 changes: 7 additions & 3 deletions src/browser/provider/built-in/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ import nodeVersion from 'node-version';
import pathBrowserProvider from './path';
import locallyInstalledBrowserProvider from './locally-installed';
import remoteBrowserProvider from './remote';
import firefoxProvider from './firefox';


const chromeProvider = nodeVersion.major !== '0' ? require('./chrome') : null;


export default Object.assign(
{
'locally-installed': locallyInstalledBrowserProvider,
'path': pathBrowserProvider,
'remote': remoteBrowserProvider
'remote': remoteBrowserProvider,
'firefox': firefoxProvider
},
chromeProvider && {
'chrome:': chromeProvider,
'chromium:': chromeProvider
'chrome': chromeProvider,
'chromium': chromeProvider,
'chrome-canary': chromeProvider,
}
);
2 changes: 1 addition & 1 deletion src/browser/provider/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { GeneralError } from '../../errors/runtime';
import MESSAGE from '../../errors/runtime/message';


const BROWSER_PROVIDER_RE = /^([^:]+)(?::(.*))?$/;
const BROWSER_PROVIDER_RE = /^([^:\s]+):?(.*)?$/;

export default {
providersCache: {},
Expand Down
Loading

0 comments on commit 4d66894

Please sign in to comment.