browser-driver.ts 8.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/**
 * Wechat for Bot. Connecting ChatBots
 *
 * BrowserDriver
 *
 * Licenst: ISC
 * https://github.com/wechaty/wechaty
 *
 */
import {
    Builder
  , Capabilities
  , WebDriver
}               from 'selenium-webdriver'

import {
    Config
  , HeadName
}               from '../config'
import log      from '../brolog-env'

export class BrowserDriver {
  private driver: WebDriver

  constructor(private head: HeadName) {
    log.verbose('PuppetWebBrowserDriver', 'constructor(%s)', head)
  }

  public async init(): Promise<this> {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
30
    log.verbose('PuppetWebBrowserDriver', 'init() for head: %s', this.head)
31 32

    if (this.driver) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
33 34 35 36 37 38 39 40
      try {
        await this.driver.close()
        await this.driver.quit()
      } catch (e) {
        log.warn('PuppetWebBrowserDriver', 'init() this.driver.{close,quit}() exception: %s'
                                          , e.message
        )
      }
41 42
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
43 44
    switch (this.head) {
      case 'phantomjs':
45 46 47
        this.driver = this.getPhantomJsDriver()
        break

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
48
      case 'firefox':
49 50 51 52 53 54
        this.driver = new Builder()
                            .setAlertBehavior('ignore')
                            .forBrowser('firefox')
                            .build()
        break

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
55
      case 'chrome':
56 57 58 59
        this.driver = this.getChromeDriver()
        break

      default: // unsupported browser head
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
60
        throw new Error('unsupported head: ' + this.head)
61 62
    }

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
63 64 65
    await this.driver.manage()
                      .timeouts()
                      .setScriptTimeout(10000)
66 67 68 69 70

    return this
  }

  private getChromeDriver(): WebDriver {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
71
    log.verbose('PuppetWebBrowserDriver', 'getChromeDriver()')
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

    /**
     * http://stackoverflow.com/a/27733960/1123955
     * issue #56
     * only need under win32 with cygwin
     * and will cause strange error:
     * `The previously configured ChromeDriver service is still running. You must shut it down before you may adjust its configuration.`

    const chrome = require('selenium-webdriver/chrome')
    const path = require('chromedriver').path

    const service = new chrome.ServiceBuilder(path).build()
    chrome.setDefaultService(service)

     */

    const options = {
      args: ['--no-sandbox']  // issue #26 for run inside docker
      // , binary: require('chromedriver').path
    }
    if (Config.isDocker) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
93
      log.verbose('PuppetWebBrowserDriver', 'getChromeDriver() wechaty in docker confirmed(should not show this in CI)')
94 95 96 97 98 99
      options['binary'] = Config.CMD_CHROMIUM
    }

    const customChrome = Capabilities.chrome()
                                    .set('chromeOptions', options)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
100 101 102 103 104
    return new Builder()
                .setAlertBehavior('ignore')
                .forBrowser('chrome')
                .withCapabilities(customChrome)
                .build()
105 106 107
    /**
     * XXX when will Builder().build() throw exception???
     */
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    // let driver
    // let ttl = 3
    // let err

    // while (!driver && ttl--) {
    //   try {
    //     driver = new Builder()
    //                   .setAlertBehavior('ignore')
    //                   .forBrowser('chrome')
    //                   .withCapabilities(customChrome)
    //                   .build()
    //   } catch (e) {
    //     log.warn('PuppetWebBrowserDriver', 'getChromeDriver() exception: %s, retry ttl: %d', e.message, ttl)
    //     err = e
    //   }
    // }

    // if (driver) {
    //   return driver
    // }

    // throw err
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
  }

  private getPhantomJsDriver(): WebDriver {
    // setup custom phantomJS capability https://github.com/SeleniumHQ/selenium/issues/2069
    const phantomjsExe = require('phantomjs-prebuilt').path
    if (!phantomjsExe) {
      throw new Error('phantomjs binary path not found')
    }
    // const phantomjsExe = require('phantomjs2').path

    const phantomjsArgs = [
      '--load-images=false'
      , '--ignore-ssl-errors=true'  // this help socket.io connect with localhost
      , '--web-security=false'      // https://github.com/ariya/phantomjs/issues/12440#issuecomment-52155299
      , '--ssl-protocol=any'        // http://stackoverflow.com/a/26503588/1123955
      // , '--ssl-protocol=TLSv1'    // https://github.com/ariya/phantomjs/issues/11239#issuecomment-42362211

      // issue: Secure WebSocket(wss) do not work with Self Signed Certificate in PhantomJS #12
      // , '--ssl-certificates-path=D:\\cygwin64\\home\\zixia\\git\\wechaty' // http://stackoverflow.com/a/32690349/1123955
      // , '--ssl-client-certificate-file=cert.pem' //
    ]

    if (Config.debug) {
      phantomjsArgs.push('--remote-debugger-port=8080') // XXX: be careful when in production env.
      phantomjsArgs.push('--webdriver-loglevel=DEBUG')
      // phantomjsArgs.push('--webdriver-logfile=webdriver.debug.log')
    } else {
      if (log && log.level() === 'silent') {
        phantomjsArgs.push('--webdriver-loglevel=NONE')
      } else {
        phantomjsArgs.push('--webdriver-loglevel=ERROR')
      }
    }

    const customPhantom = Capabilities.phantomjs()
                                      .setAlertBehavior('ignore')
                                      .set('phantomjs.binary.path', phantomjsExe)
                                      .set('phantomjs.cli.args', phantomjsArgs)

Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
169 170
    log.silly('PuppetWebBrowserDriver', 'phantomjs binary: ' + phantomjsExe)
    log.silly('PuppetWebBrowserDriver', 'phantomjs args: ' + phantomjsArgs.join(' '))
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

    const driver = new Builder()
                        .withCapabilities(customPhantom)
                        .build()

    /* tslint:disable:jsdoc-format */
		/**
		 *  FIXME: ISSUE #21 - https://github.com/zixia/wechaty/issues/21
	 	 *
 	 	 *	http://phantomjs.org/api/webpage/handler/on-resource-requested.html
		 *	http://stackoverflow.com/a/29544970/1123955
		 *  https://github.com/geeeeeeeeek/electronic-wechat/pull/319
		 *
		 */
    //   	driver.executePhantomJS(`
    // this.onResourceRequested = function(request, net) {
    //    console.log('REQUEST ' + request.url);
    //    blockRe = /wx\.qq\.com\/\?t=v2\/fake/i
    //    if (blockRe.test(request.url)) {
    //        console.log('Abort ' + request.url);
    //        net.abort();
    //    }
    // }
    // `)

    // https://github.com/detro/ghostdriver/blob/f976007a431e634a3ca981eea743a2686ebed38e/src/session.js#L233
    // driver.manage().timeouts().pageLoadTimeout(2000)

    return driver
  }

  // public driver1(): WebDriver
  // public driver1(empty: null): void
  // public driver1(newDriver: WebDriver): WebDriver

  // public driver1(newDriver?: WebDriver | null): WebDriver | void {
  //   if (newDriver !== undefined) {
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
208
  //     log.verbose('PuppetWebBrowserDriver', 'driver(%s)'
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
  //                                   , newDriver
  //                                     ? newDriver.constructor.name
  //                                     : null
  //     )
  //   }

  //   if (newDriver !== undefined) {
  //     if (newDriver) {
  //       this.driver = newDriver
  //       return this.driver
  //     } else { // null
  //       if (this.driver && this.driver.getSession()) {
  //         throw new Error('driver still has session, can not set null')
  //       }
  //       this.driver = null
  //       return
  //     }
  //   }

  //   if (!this.driver) {
  //     const e = new Error('no driver')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
230
  //     log.warn('PuppetWebBrowserDriver', 'driver() exception: %s', e.message)
231 232 233 234
  //     throw e
  //   }
  //   // if (!this.driver.getSession()) {
  //   //   const e = new Error('no driver session')
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
235
  //   //   log.warn('PuppetWebBrowserDriver', 'driver() exception: %s', e.message)
236 237 238 239 240 241 242 243 244
  //   //   this.driver.quit()
  //   //   throw e
  //   // }

  //   return this.driver
  // }

  public close()              { return this.driver.close() }
  public executeAsyncScript() { return this.driver.executeAsyncScript.apply(this.driver, arguments) }
Huan (李卓桓)'s avatar
Huan (李卓桓) 已提交
245
  public executeScript()      { return this.driver.executeScript.apply(this.driver, arguments) }
246 247 248 249 250 251 252 253
  public get(url: string)     { return this.driver.get(url) }
  public getSession()         { return this.driver.getSession() }
  public manage()             { return this.driver.manage() }
  public navigate()           { return this.driver.navigate() }
  public quit()               { return this.driver.quit() }
}

export default BrowserDriver