diff --git a/.gitignore b/.gitignore index cc0263aa813349b6aa27451f443f8e4d8f5202b4..46670a251ecc9d50fe321eeafd4ed75fcae4dd41 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ t.* .DS_Store /dist/ yarn.lock +.yarn/ diff --git a/Dockerfile b/Dockerfile index d8152893f508ebe45c3081567499eeeaaa3905bc..18fafc017e1aa80fb07fff057a1480af6268e556 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,11 +6,12 @@ # # Docker image for Alpine Linux with latest ShellCheck, a static analysis tool for shell scripts. # https://hub.docker.com/r/nlknguyen/alpine-shellcheck/ -FROM nlknguyen/alpine-shellcheck +# FROM nlknguyen/alpine-shellcheck +FROM mhart/alpine-node:7 MAINTAINER Zhuophuan LI RUN apk update && apk upgrade \ - && apk add nodejs \ + && apk add \ bash \ ca-certificates \ chromium-chromedriver \ diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 8c603f28186c67cc4593cf4d3a7002f0a8bf7e93..693128ab06562b9c864d1d090af669f9bf18af76 100755 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -113,6 +113,8 @@ function wechaty::runBot() { echo "Install dependencies modules ..." yarn < /dev/null # yarn will close stdin??? cause `read` command fail after yarn } + npm --progress=false install @types/node > /dev/null + # echo -n "Linking Wechaty module to bot ... " # npm link wechaty < /dev/null > /dev/null 2>&1 @@ -137,6 +139,17 @@ function wechaty::runBot() { return "$ret" } +function wechaty::help() { + echo < + + 1. mybot.js: a JavaScript program for your bot. will run by node v6 + 2. mybot.ts: a TypeScript program for your bot. will run by ts-node + 3. command: demo, test, doctor + +HELP +} + function main() { wechaty::banner figlet Connecting @@ -175,6 +188,14 @@ function main() { npm "$@" || ret=$? ;; + '') + if [ -n "$WECHATY_TOKEN" ]; then + npm start + else + wechaty::help + fi + ;; + # # 4. Default to execute npm run ... # diff --git a/circle.yml b/circle.yml index 65461cd465d282ee7f827b938131971d98e492f3..037ceeca32982e1089c0d95685f024d6c7019141 100644 --- a/circle.yml +++ b/circle.yml @@ -8,6 +8,7 @@ machine: test: override: - docker info + - docker run -ti -v "$(pwd)":/mnt nlknguyen/alpine-shellcheck bin/*.sh - ./script/docker.sh build - ./script/docker.sh run shlint - ./script/docker.sh test diff --git a/index.ts b/index.ts index fd9b8768491e917bb9652f05c6bd40b9f99d47fe..a92ef9949d2ce3c1508f44ba6ce77cfa78bbe979 100644 --- a/index.ts +++ b/index.ts @@ -30,3 +30,4 @@ export { , Wechaty , log // for convenionce use npmlog with environment variable LEVEL } + diff --git a/package.json b/package.json index 03c119c36b72d29510d1e678ff3d4b193ac2f0c6..f3cc3bdcce1b2f8ffb60a6961eaccfb07401c84a 100755 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "lint": "npm run eslint && npm run tslint", "eslint": "eslint \"{bin,example,src,test}/**/*.js\"", "tslint": "tslint \"{bin,example,src,test}/**/*.ts\" && tsc --noEmit", - "shlint": "bash -n bin/*.sh && shellcheck bin/*.sh", + "shlint": "bash -n bin/*.sh", "pretest": "npm run clean && npm run lint && npm run build", "sloc": "sloc bin example src test index.ts --details --format cli-table --keys total,source,comment && sloc bin example src test index.ts", "test": "npm run test:chrome", @@ -34,7 +34,7 @@ "test:chrome": "cross-env LC_ALL=C WECHATY_LOG=verbose WECHATY_HEAD=chrome ava --serial --fail-fast --verbose --ext js --timeout=7m \"dist/{src,test}/**/*.spec.js\"", "testdev": "cross-env LC_ALL=C WECHATY_LOG=silly ava --ext ts --serial --verbose --fail-fast --timeout=3m", "testdist": "cross-env WECHATY_LOG=SILLY WECHATY_HEAD=chrome ava --ext ts --verbose --fail-fast --timeout=2m", - "ava": "cross-env LC_ALL=C WECHATY_LOG=verbose ts-node node_modules/.bin/ava \"{src,test}/**/*.spec.js\"", + "ava": "cross-env LC_ALL=C WECHATY_LOG=verbose node_modules/.bin/ava --ext js", "start": "ts-node bin/client", "dev": "ts-node dev.ts", "demo": "ts-node example/ding-dong-bot.ts" diff --git a/script/docker.sh b/script/docker.sh index 98de4c4f075ff9169e0061b5f978ae24cefb134b..acb8e6d5bd68a81b2e2597333c0c935fd0869711 100755 --- a/script/docker.sh +++ b/script/docker.sh @@ -1,25 +1,41 @@ -#!/bin/sh +#!/bin/bash # # 1. CircleCI with `Btrfs volume error` # https://circleci.com/docs/docker-btrfs-error/ # +set -e + imageName='wechaty:test' optRm='--rm' [ -n "$CIRCLECI" ] && optRm='--rm=false' -if [ "$1" = "build" ] || [ "$1" = "" ]; then - echo docker build "$optRm" -t "$imageName" . - exec docker build "$optRm" -t "$imageName" . +declare -i ret=0 +case "$1" in + build | '') + echo docker build "$optRm" -t "$imageName" . + exec docker build "$optRm" -t "$imageName" . + ret=$? + ;; + + test) + echo docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/js-bot.js + docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/js-bot.js - ret=$? - echo "ERROR: exec return $ret ???" - exit $ret -fi + echo docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/ts-bot.ts + docker run -ti "$optRm" -v /dev/shm:/dev/shm -v "$(pwd)":/bot "$imageName" test/docker-bot/ts-bot.ts -echo docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" $@ -exec docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" $@ + echo docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" "$@" + exec docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" "$@" + ret=$? + ;; + + *) + echo docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" "$@" + exec docker run -ti "$optRm" -v /dev/shm:/dev/shm "$imageName" "$@" + ret=$? + ;; +esac -ret=$? echo "ERROR: exec return $ret ???" exit $ret diff --git a/src/puppet-web/browser.spec.ts b/src/puppet-web/browser.spec.ts new file mode 100755 index 0000000000000000000000000000000000000000..30bd92f73b37676d83aebebfa03d3709862ef0ca --- /dev/null +++ b/src/puppet-web/browser.spec.ts @@ -0,0 +1,41 @@ +/** + * Wechaty - Wechat for Bot. Connecting ChatBots + * + * Licenst: ISC + * https://github.com/wechaty/wechaty + * + */ +import { test } from 'ava' + +import { + // Config + // , log +} from '../config' + +import { + Browser +} from './browser' + +test('quit()', async t => { + const browser = new Browser() + await browser.driver.init() // init driver, not init browser + + t.throws(browser.quit(), Error, 'should throw on an un-inited browser') + + browser.state.current('open', false) + t.notThrows(browser.quit(), 'should not throw exception when call quit() on an `inprocess` `open` state browser') + + browser.state.current('close') + t.throws(browser.quit(), Error, 'should throw exception when call quit() twice on browser') + +}) + +test('init()', async t => { + const browser = new Browser() + + browser.state.current('open') + t.throws(browser.init(), Error, 'should throw exception when call init() on an `open` state browser') + + browser.state.current('open', false) + t.throws(browser.init(), Error, 'should throw exception when call init() on a `open`-`ing` state browser') +}) diff --git a/src/puppet-web/browser.ts b/src/puppet-web/browser.ts index 4bba1a70ca3aa13a2498457b75a70f6ef97e2bf4..2f3d8cd4074dd118903d22e1cf270dd17776ffaa 100644 --- a/src/puppet-web/browser.ts +++ b/src/puppet-web/browser.ts @@ -53,6 +53,17 @@ export class Browser extends EventEmitter { public async init(): Promise { log.verbose('PuppetWebBrowser', 'init()') + if (this.state.current() === 'open') { + let e: Error + if (this.state.inprocess()) { + e = new Error('init() fail: current state is `open`-`ing`') + } else { + e = new Error('init() fail: current state is `open`') + } + log.error('PuppetWebBrowser', e.message) + throw e + } + this.state.target('open') this.state.current('open', false) @@ -134,15 +145,14 @@ export class Browser extends EventEmitter { log.verbose('PuppetWebBrowser', 'quit()') if (this.state.current() === 'close') { + let e: Error if (this.state.inprocess()) { - const e = new Error('quit() on a browser with state.current():`close` and inprocess():`true` ?') - log.warn('PuppetWebBrowser', e.message) - throw e + e = new Error('quit() fail: on a browser with state.current():`close` and inprocess():`true` ?') } else { // stable - const e = new Error('quit() on a already quit-ed browser') - log.warn('PuppetWebBrowser', e.message) - throw e + e = new Error('quit() fail: on a already quit-ed browser') } + log.warn('PuppetWebBrowser', e.message) + throw e } this.state.current('close', false) @@ -275,6 +285,7 @@ export class Browser extends EventEmitter { return await this.driver.executeScript.apply(this.driver, arguments) } catch (e) { // this.dead(e) + log.verbose('PuppetWebBrowser', 'execute() script: %s', script) log.warn('PuppetWebBrowser', 'execute() exception: %s, %s', e.message.substr(0, 99), e.stack) throw e } diff --git a/test/docker-bot/js-bot.js b/test/docker-bot/js-bot.js new file mode 100644 index 0000000000000000000000000000000000000000..3b446b2cfea25939aa0cc11e06145c464b9c68d3 --- /dev/null +++ b/test/docker-bot/js-bot.js @@ -0,0 +1,4 @@ +const { Wechaty } = require('wechaty') + +const bot = Wechaty.instance() +console.log(bot.version()) diff --git a/test/docker-bot/ts-bot.ts b/test/docker-bot/ts-bot.ts new file mode 100644 index 0000000000000000000000000000000000000000..db4b7422b286bf33a172153814f96720e493ea50 --- /dev/null +++ b/test/docker-bot/ts-bot.ts @@ -0,0 +1,4 @@ +import { Wechaty } from 'wechaty' + +const bot = Wechaty.instance() +console.log(bot.version()) diff --git a/test/puppet-web/browser.spec.ts b/test/puppet-web/browser.spec.ts index 72666b1a4ec010aa8aa0cea4fdf3a5d1921af01e..637a565240cf32454e1f2999e4999ccbb08850db 100644 --- a/test/puppet-web/browser.spec.ts +++ b/test/puppet-web/browser.spec.ts @@ -20,7 +20,7 @@ import { const PROFILE = Config.DEFAULT_PROFILE + '-' + process.pid + '-' let profileCounter = 1 -test('Browser Cookie smoking test', async t => { +test('Cookie smoking test', async t => { const browser = new Browser() t.truthy(browser, 'should instanciate a browser instance') @@ -83,10 +83,10 @@ test('Browser Cookie smoking test', async t => { await browser.driver.quit() }) -test('Browser Cookie save/load test', async t => { +test('Cookie save/load', async t => { const profileName = PROFILE + profileCounter++ + 'wechaty.json' - let b = new Browser({ + let browser = new Browser({ head: Config.head , sessionFile: profileName }) @@ -95,14 +95,14 @@ test('Browser Cookie save/load test', async t => { * use exception to call b.quit() to clean up */ try { - t.truthy(b, 'should get a new Browser') + t.truthy(browser, 'should get a new Browser') - b.state.target('open') + browser.state.target('open') - await b.driver.init() + await browser.driver.init() t.pass('should init driver') - await b.open() + await browser.open() t.pass('opened') const EXPECTED_COOKIE = { @@ -115,43 +115,43 @@ test('Browser Cookie save/load test', async t => { } const EXPECTED_NAME_REGEX = new RegExp('^' + EXPECTED_COOKIE.name + '$') - await b.driver.manage().deleteAllCookies() - let cookies = await b.driver.manage().getCookies() + await browser.driver.manage().deleteAllCookies() + let cookies = await browser.driver.manage().getCookies() t.is(cookies.length, 0, 'should no cookie after deleteAllCookies()') - await b.addCookie(EXPECTED_COOKIE) - const cookieFromBrowser = await b.driver.manage().getCookie(EXPECTED_COOKIE.name) + await browser.addCookie(EXPECTED_COOKIE) + const cookieFromBrowser = await browser.driver.manage().getCookie(EXPECTED_COOKIE.name) t.is(cookieFromBrowser.name, EXPECTED_COOKIE.name, 'cookie from getCookie() should be same as we just set') - let cookiesFromCheck = await b.readCookie() + let cookiesFromCheck = await browser.readCookie() t.truthy(cookiesFromCheck.length, 'should get cookies from checkSession() after addCookies()') let cookieFromCheck = cookiesFromCheck.filter(c => EXPECTED_NAME_REGEX.test(c['name'])) t.is(cookieFromCheck[0]['name'], EXPECTED_COOKIE.name, 'cookie from checkSession() return should be same as we just set by addCookies()') - await b.saveCookie() - const cookiesFromSave = await b.readCookie() + await browser.saveCookie() + const cookiesFromSave = await browser.readCookie() t.truthy(cookiesFromSave.length, 'should get cookies from saveSession()') const cookieFromSave = cookiesFromSave.filter(c => EXPECTED_NAME_REGEX.test(c['name'])) t.is(cookieFromSave.length, 1, 'should has the cookie we just set') t.is(cookieFromSave[0]['name'], EXPECTED_COOKIE.name, 'cookie from saveSession() return should be same as we just set') - await b.driver.manage().deleteAllCookies() - cookiesFromCheck = await b.readCookie() + await browser.driver.manage().deleteAllCookies() + cookiesFromCheck = await browser.readCookie() t.is(cookiesFromCheck.length, 0, 'should no cookie from checkSession() after deleteAllCookies()') - await b.loadCookie().catch(() => { /* fail safe */ }) - const cookiesFromLoad = await b.readCookie() + await browser.loadCookie().catch(() => { /* fail safe */ }) + const cookiesFromLoad = await browser.readCookie() t.truthy(cookiesFromLoad.length, 'should get cookies after loadSession()') const cookieFromLoad = cookiesFromLoad.filter(c => EXPECTED_NAME_REGEX.test(c.name)) t.is(cookieFromLoad[0].name, EXPECTED_COOKIE.name, 'cookie from loadSession() should has expected cookie') - cookiesFromCheck = await b.readCookie() + cookiesFromCheck = await browser.readCookie() t.truthy(cookiesFromCheck.length, 'should get cookies from checkSession() after loadSession()') cookieFromCheck = cookiesFromCheck.filter(c => EXPECTED_NAME_REGEX.test(c['name'])) t.truthy(cookieFromCheck.length, 'should has cookie after filtered after loadSession()') t.is(cookieFromCheck[0]['name'], EXPECTED_COOKIE.name, 'cookie from checkSession() return should has expected cookie after loadSession') - await b.quit() + await browser.driver.quit() t.pass('quited') /** @@ -159,24 +159,24 @@ test('Browser Cookie save/load test', async t => { * with the same sessionFile: profileName */ - b = new Browser({ + browser = new Browser({ head: Config.head , sessionFile: profileName }) t.pass('should started a new Browser') - b.state.target('open') + browser.state.target('open') - await b.driver.init() + await browser.driver.init() t.pass('should inited the new Browser') - await b.open() + await browser.open() t.pass('should opened') - await b.loadCookie() + await browser.loadCookie() t.pass('should loadSession for new Browser(process)') - const cookieAfterQuit = await b.driver.manage().getCookie(EXPECTED_COOKIE.name) + const cookieAfterQuit = await browser.driver.manage().getCookie(EXPECTED_COOKIE.name) t.truthy(cookieAfterQuit, 'should get cookie from getCookie()') t.is(cookieAfterQuit.name, EXPECTED_COOKIE.name, 'cookie from getCookie() after browser quit, should load the right cookie back') @@ -186,10 +186,10 @@ test('Browser Cookie save/load test', async t => { } }) - await b.quit() + await browser.driver.quit() } catch (e) { - if (b) { - await b.quit() + if (browser) { + await browser.driver.quit() } t.fail('exception: ' + e.message) }