import { Options as ChromeOptions } from 'selenium-webdriver/chrome'; import { Options as FirefoxOptions } from 'selenium-webdriver/firefox'; import * as util from 'util'; import { EnableLog } from '../common-types'; // TODO: does not driver creates profile dir itself ? // function createBrowserProfile() { // fileUtils.mkdir(gT.config.browserProfilePath); // } const cleanedProfilePaths: string[] = []; export async function init(cleanProfile?: boolean, enableLog?: EnableLog) { // if (typeof enableLog === 'undefined' && !gT.config.browserProfileDir) { // enableLog = false; // } gIn.tracer.msg3(`browserProfilePath: ${gT.config.browserProfilePath}`); gIn.tracer.msg3(`shareBrowser: ${gT.cLParams.shareBrowser}`); gIn.tracer.msg3(`sharedBrowserInitiated: ${gIn.sharedBrowserInitiated}`); // If directory has private profile, --share-browser is ignored. if (!gT.config.browserProfileDir && gT.cLParams.shareBrowser) { if (gIn.sharedBrowserInitiated) { gIn.tracer.msg3('Initialization is not needed'); return Promise.resolve('Initialization is not needed'); } gIn.sharedBrowserInitiated = true; } let profileInfo; if (gT.config.browserProfileDir) { profileInfo = `(with dir-config defined ${cleanProfile ? 'empty' : 'saved'} profile)`; } else { profileInfo = '(with default profile)'; } return gIn.wrap({ msg: `Initialization ${profileInfo} ... `, enableLog, act: async () => { if (cleanProfile) { gT.s.browser.cleanProfile(false); } else if ( gT.cLParams.clearProfiles && !cleanedProfilePaths.includes(gT.config.browserProfilePath) ) { gT.s.browser.cleanProfile(true); cleanedProfilePaths.push(gT.config.browserProfilePath); } // createBrowserProfile(); let options: ChromeOptions | FirefoxOptions; switch (gT.cLParams.browser) { case 'chrome': options = new gT.sOrig.chrome.Options(); options.addArguments('--dns-prefetch-disable'); options.addArguments('--no-sandbox'); // Without this there is a fail with xvfb on Ubuntu 16. options.addArguments('--disable-infobars'); if (gT.cLParams.headless) { options.addArguments('--headless'); options.addArguments('--window-size=2560,1024'); if (gT.u.misc.isWindows()) { options.addArguments('--disable-gpu'); // Temporary fix for Windows. } } // options.addArguments('--start-maximized'); // if (gT.config.browserProfileDir) { options.addArguments(`--user-data-dir=${gT.config.browserProfilePath}`); // } // options.excludeSwitches(); options.setUserPreferences({ credentials_enable_service: false, 'profile.password_manager_enabled': false, }); break; case 'firefox': { options = new gT.sOrig.firefox.Options(); // const binary = new gT.sOrig.firefox.Binary(); // if (gT.config.browserProfileDir) { // Profile name should be alphanumeric only. // Checked on linux. It does set -profile option. // binary.addArguments('-profile "' + gT.config.browserProfilePath + '"'); options.setProfile(gT.config.browserProfilePath); // Checked on linux. Does NOT set -profile option. // http://selenium.googlecode.com/git/docs/api/javascript/module_selenium-webdriver_firefox.html // "The FirefoxDriver will never modify a pre-existing profile; instead it will create // a copy for it to modify." // http://stackoverflow.com/questions/6787095/how-to-stop-selenium-from-creating-temporary-firefox-profiles-using-web-driver // webdriver.firefox.profile (name of the profile). // Also there is info that gT.sOrig.driver.quit() deletes tmp profile, butgT.sOrig.driver.close() // - does not. // profile.setPreference ? // browser.sessionstore.resume_from_crash // writeToDisk ? // } if (gT.cLParams.headless) { options.headless(); } // options.setBinary(binary); // gT.sOrig.wdModule.Capabilities.firefox(); // capabilities = new gT.sOrig.wdModule.Capabilities(); } break; } const prefs = new gT.sOrig.wdModule.logging.Preferences(); // TODO: this parameter correctly works only for chrome. // phantomjs gets all messages, independent on choosen level. // Mozilla gets no messages. prefs.setLevel(gT.sOrig.browserLogType, gT.cLParams.browserLogLevel); prefs.setLevel(gT.sOrig.driverLogType, gT.cLParams.driverLogLevel); const capabilities = new gT.sOrig.wdModule.Capabilities(); // Waiting to my PR approve for DefenitelyTyped. // @ts-ignore capabilities.setBrowserName(gT.cLParams.browser); capabilities.setLoggingPrefs(prefs); gIn.tracer.msg3(util.inspect(capabilities, { depth: 4 })); if (gT.cLParams.useRemoteDriver) { const sid = gIn.remoteDriverUtils.getSid(); const remoteDriverConnectionStr = `${gT.globalConfig.remoteDriverUrl}:${gT.globalConfig.remoteDriverPort}`; if (sid) { gIn.tracer.msg3('There is current SID'); gT.firstRunWithRemoteDriver = false; const client = new gT.sOrig.Client(remoteDriverConnectionStr); const executor = new gT.sOrig.Executor(client); // @ts-ignore executor.w3c = true; // gT.sOrig.driver = new gT.sOrig.wdModule.WebDriver(sid, executor); const session = new gT.sOrig.wdModule.Session(sid, {}); gT.sOrig.driver = new gT.sOrig.wdModule.WebDriver(session, executor); } else { gIn.tracer.msg3('There is not current SID'); gT.firstRunWithRemoteDriver = true; gT.sOrig.driver = new gT.sOrig.wdModule.Builder() .forBrowser(gT.cLParams.browser) .setChromeOptions(options! as ChromeOptions) .setFirefoxOptions(options! as FirefoxOptions) .withCapabilities(capabilities) // As an alternative to this method, you may also set the SELENIUM_REMOTE_URL environment variable. // TODO: magic constant. .usingServer(remoteDriverConnectionStr) .build(); gT.sOrig.driver .getSession() .then(res => { const sid = gIn.remoteDriverUtils.saveSid(res.getId()); gIn.tracer.msg3(`Saved session id: ${sid}`); }) .catch(e => { gIn.logger.exception('Error at getSession: ', e); }); } // ============================== // TODO: // Using undocumented property to trace all selenium commands. // let executorRef = gT.sOrig.driver.executor_; // executorRef.executeOrig = executorRef.execute; // executorRef.execute = function (command) { // gIn.tracer.trace3('COMMAND: ' + command.getName()); // // TODO: tracing, let params = command.getParameters(); // return executorRef.executeOrig(command) // .then(function (res) { // // TODO: tracing, command result. // return res; // }) // .catch(function (e) { // // TODO: tracing, command fail. // throw e; // }); // }; // ============================== } else { // Temporary driver gT.sOrig.driver = new gT.sOrig.wdModule.Builder() .forBrowser(gT.cLParams.browser) .setChromeOptions(options! as ChromeOptions) .setFirefoxOptions(options! as FirefoxOptions) .withCapabilities(capabilities) .build(); } gT.sOrig.logs = gT.sOrig.driver.manage().logs(); if (gT.cLParams.useRemoteDriver) { return; } // Trying to fix chromedriver issue 817 by delay. // https://bugs.chromium.org/p/chromedriver/issues/detail?id=817#c21 return gT.u.promise.delayed(gT.engineConsts.defaultDelayAfterDriverCreate); }, }); } export function sleep(ms: number, enableLog?: EnableLog) { return gIn.wrap({ msg: `Sleep ${ms} ms ... `, enableLog, act: () => gT.u.promise.delayed(ms, true), }); } const stupidSleep = 400; /** * This function creates function for stupid sleep. * It is stupid sleep instead of smart waiting for something. */ export function getStupidSleepFunc() { return function() { return sleep(stupidSleep, false); }; } export function quit(enableLog?: EnableLog) { if (gT.cLParams.ejExplore) { gIn.tracer.msg3('quit: ejExplore, no quit'); return Promise.resolve('ejExplore, no quit'); } // if (typeof enableLog === 'undefined' && !gT.config.browserProfileDir) { // enableLog = false; // } if (gIn.sharedBrowserInitiated) { gIn.tracer.msg3('quit: Shared browser, no quit'); return Promise.resolve('Shared browser, no quit'); } return gIn.wrap({ msg: 'Quiting ... ', enableLog, act: () => gT.sOrig.driver.quit().then(() => { gIn.tracer.msg3('Quit: Driver is deleted'); delete gT.sOrig.driver; }), noConsoleAndExceptions: true, }); } export function quitIfInited() { if (gT.cLParams.ejExplore) { gIn.tracer.msg3('quitIfInited: ejExplore, no quit'); return Promise.resolve('ejExplore, no quit'); } if (gT.sOrig.driver) { gIn.tracer.msg3('quitIfInited: before quit call'); return gT.sOrig.driver.quit().then(() => { delete gT.sOrig.driver; gIn.tracer.msg3('quitIfInited: Driver is deleted'); }); } gIn.tracer.msg3('quitIfInited: no driver, no quit'); return Promise.resolve('No driver, no quit'); } export function printSelDriverLogs(minLevel: number) { return gT.sOrig.logs.get(gT.sOrig.driverLogType).then(entries => { gIn.tracer.msg3('Start of printSelDriverLogs'); for (const entry of entries) { if ( entry.level.value >= minLevel && !entry.message.includes( 'This version of ChromeDriver has not been tested with Chrome version' ) ) { const logStr = `SEL.DR.LOG: ${entry.level.name} (${entry.level.value}), Message:\n ${entry.message}`; gIn.logger.logln(logStr); } } gIn.tracer.msg3('End of printSelDriverLogs'); }); }