diff --git a/.env.enc b/.env.enc new file mode 100644 index 0000000..eeb0fd3 Binary files /dev/null and b/.env.enc differ diff --git a/.env.example b/.env.example index 6b480f1..9b4b9c9 100644 --- a/.env.example +++ b/.env.example @@ -2,12 +2,16 @@ WORKSPACE_ID= CONVERSATION_USERNAME= CONVERSATION_PASSWORD= -#Optional params, delete any which are not used! -#Optional params to enable Speech to text + +# Optional params, delete any which are not used! +# To enable Speech to text STT_USERNAME= STT_PASSWORD= -#Optional cloudant URL for loggin + +# Optional Cloudant URL for logging CLOUDANT_URL= -#If cloudant url is specified a user name and password must be specified to secure the logging endpoints + +# If Cloudant url is specified a user name and password must be +# specified to secure the logging endpoints LOG_USER= LOG_PASS= diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index fc5a13c..0000000 --- a/.eslintrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "rules": { - "no-console": 0, - "func-names": 0, - "vars-on-top": 0, - "consistent-return": 0 - }, - "globals": { - describe: true, - it: true - }, - "env": { - "node": true - }, - "extends": "eslint-config-airbnb-es5", -} \ No newline at end of file diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..e7d2922 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,18 @@ +env: + browser: true + node: true + mocha: true +extends: 'eslint:recommended' +rules: + indent: + - error + - 2 + linebreak-style: + - error + - unix + quotes: + - error + - single + semi: + - error + - always diff --git a/.travis.yml b/.travis.yml index 722e672..6558d3c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,25 @@ language: node_js sudo: true +node_js: stable cache: directories: - node_modules -node_js: stable +before_install: +- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && openssl aes-256-cbc -K $encrypted_047d2e62ea22_key -iv $encrypted_047d2e62ea22_iv -in .env.enc -out .env -d || true' script: - - npm run lint - - npm run codecov +- npm test +env: + global: + - CF_APP=conversation-simple + - CF_API=https://api.ng.bluemix.net + - CF_ORGANIZATION=WatsonPlatformServices + - CF_SPACE=appgallery +before_deploy: npm install -g cf-blue-green deploy: - provider: cloudfoundry - api: https://api.ng.bluemix.net - username: $CF_USERNAME - password: $CF_PASSWORD - name: conversation-simple - organization: WatsonPlatformServices - space: demos + provider: script + script: + - cf-blue-green-travis on: - repo: watson-developer-cloud/conversation-simple branch: release + repo: watson-developer-cloud/conversation-simple + skip_cleanup: true diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..e8f79ea --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: npm start \ No newline at end of file diff --git a/README.md b/README.md index 05da3d1..8a3523d 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ The chat interface is on the left, and the JSON that the JavaScript code receive capabilities greetings goodbyes - + Type a request, such as `music on` or `I want to turn on the windshield wipers`. The system understands your intent and responds. You can see the details of how your input was understood by examining the JSON data in the `Watson understands` section on the right side. For example, if you type `Turn on some music`, the JSON data shows that the system understood the `turn_on` intent with a high level of confidence, along with the `appliance` entity with a value of `music`. @@ -142,7 +142,7 @@ After you have the app deployed and running, you can explore the source files an * Modify the .js files to change the app logic. * Modify the .html file to change the appearance of the app page. * Use the Conversation tool to train the service for new intents, or to modify the dialog flow. For more information, see the [Conversation service documentation][docs_landing]. - + ## Deploying to Bluemix You can use Cloud Foundry to deploy your local version of the app to Bluemix. @@ -151,7 +151,6 @@ You can use Cloud Foundry to deploy your local version of the app to Bluemix. * In the `applications` section of the `manifest.yml` file, change the `name` value to a unique name for your version of the demo app. * In the `services` section, specify the name of the Conversation service instance you created for the demo app. If you do not remember the service name, use the `cf services` command to list all services you have created. - * In the `env` section, add `WORKSPACE_ID` and specify the value from the `.env` file. The following example shows a modified `manifest.yml` file: @@ -171,7 +170,6 @@ You can use Cloud Foundry to deploy your local version of the app to Bluemix. - conversation-simple-demo-test1 env: NPM_CONFIG_PRODUCTION: false - WORKSPACE_ID: fdeab5e4-0ebe-4183-8d10-6e5557a6d842 ``` 1. Push the app to Bluemix: @@ -213,4 +211,4 @@ cf logs --recent [docs_landing]: (http://www.ibm.com/watson/developercloud/doc/conversation/index.shtml) [node_link]: (http://nodejs.org/) [npm_link]: (https://www.npmjs.com/) -[sign_up]: https://apps.admin.ibmcloud.com/manage/trial/bluemix.html?cm_mmc=WatsonDeveloperCloud-_-LandingSiteGetStarted-_-x-_-CreateAnAccountOnBluemixCLI +[sign_up]: bluemix.net/registration diff --git a/app.js b/app.js index 182ed96..bfb25d9 100644 --- a/app.js +++ b/app.js @@ -147,9 +147,7 @@ if ( cloudantUrl ) { // add a new API which allows us to retrieve the logs (note this is not secure) nano.db.get( 'car_logs', function(err) { if ( err ) { - console.error(err); - nano.db.create( 'car_logs', function(errCreate) { - console.error(errCreate); + nano.db.create( 'car_logs', function() { logs = nano.db.use( 'car_logs' ); } ); } else { @@ -170,7 +168,6 @@ if ( cloudantUrl ) { // Endpoint which allows conversation logs to be fetched app.get( '/chats', auth, function(req, res) { logs.list( {include_docs: true, 'descending': true}, function(err, body) { - console.error(err); // download as CSV var csv = []; csv.push( ['Question', 'Intent', 'Confidence', 'Entity', 'Output', 'Time'] ); diff --git a/casper-runner.js b/casper-runner.js new file mode 100644 index 0000000..a75c15d --- /dev/null +++ b/casper-runner.js @@ -0,0 +1,55 @@ +/** + * Copyright 2015 IBM Corp. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var fs = require('fs'); +if (!fs.existsSync('.env')) + return; + +var spawn = require('child_process').spawn; + +require('dotenv').config({ silent: true }); + +var app = require('./app'); +var port = 3000; + +var server = app.listen(port, function() { + // eslint-disable-next-line no-console + console.log('Server running on port: %d', port); + + function kill(code) { + server.close(function() { + // eslint-disable-next-line no-process-exit + process.exit(code); + }); + } + + function runTests() { + var casper = spawn('npm', ['run', 'test-integration']); + casper.stdout.pipe(process.stdout); + + casper.on('error', function(error) { + // eslint-disable-next-line + console.error(error); + server.close(function() { + process.exit(1); + }); + }); + + casper.on('close', kill); + } + + runTests(); +}); diff --git a/package.json b/package.json index c94830c..5cee3f1 100644 --- a/package.json +++ b/package.json @@ -5,35 +5,44 @@ "main": "server.js", "scripts": { "start": "node server.js", - "test": "istanbul cover ./node_modules/mocha/bin/_mocha", + "test-integration": "casperjs test ./test/integration/test.*.js", + "test-integration-runner": "NODE_ENV=test node casper-runner.js", + "test": "npm run lint && npm run test-unit && npm run test-integration-runner", + "test-unit": "istanbul cover ./node_modules/mocha/bin/_mocha test/unit", "lint": "eslint .", "autofix": "eslint --fix .", "codecov": "npm run test && (codecov || true)" }, + "repository": { + "type": "git", + "url": "https://github.com/watson-developer-cloud/conversation-simple.git" + }, "license": "Apache-2.0", "dependencies": { - "basic-auth-connect": "1.0.0", + "basic-auth-connect": "^1.0.0", + "body-parser": "^1.15.2", + "cloudant": "^1.5.2", "dotenv": "^2.0.0", - "express": "4.13.4", - "body-parser": "1.15.1", - "watson-developer-cloud": "^2.4.6", - "cloudant": "1.4.1", - "express-csv": "0.6.0", - "nano" : "*", - "uuid": "2.0.2", - "vcap_services": "^0.2.0" + "express": "^4.14.0", + "express-csv": "^0.6.0", + "nano": "^6.2.0", + "uuid": "^2.0.3", + "vcap_services": "^0.2.0", + "watson-developer-cloud": "^2.8.1" }, "devDependencies": { "babel-eslint": "^6.0.4", + "casperjs": "^1.1.3", "codecov": "^1.0.1", "eslint": "^2.8.0", "eslint-config-airbnb-es5": "^1.0.9", - "eslint-plugin-react": "^5.1.1", "istanbul": "^0.4.2", "mocha": "^2.4.5", + "phantomjs-prebuilt": "^2.1.13", "supertest": "^1.2.0" }, "engine": { - "node": ">= 4.2.4" + "node": ">= 6.9.x", + "npm": "> 3.10.x" } } diff --git a/server.js b/server.js index 12549cc..0ed9ab4 100644 --- a/server.js +++ b/server.js @@ -1,4 +1,19 @@ #!/usr/bin/env node +/** + * Copyright 2015 IBM Corp. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ 'use strict'; @@ -6,5 +21,6 @@ var server = require('./app'); var port = process.env.PORT || process.env.VCAP_APP_PORT || 3000; server.listen(port, function() { + // eslint-disable-next-line console.log('Server running on port: %d', port); }); diff --git a/test/integration/test.mainpage.js b/test/integration/test.mainpage.js new file mode 100644 index 0000000..7654f0b --- /dev/null +++ b/test/integration/test.mainpage.js @@ -0,0 +1,51 @@ +/** + * Copyright 2015 IBM Corp. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint no-undef: 0 */ + +casper.test.begin('Conversation simple Demo', 5, function suite(test) { + var baseHost = 'http://localhost:3000'; + + function testWelcomeMessageExists() { + casper.waitForSelector('.from-watson', function () { + test.assertExists('.message-inner', 'Welcome message received'); + }); + } + + function testEnterMessageClick() { + casper.then(function () { + this.sendKeys('#textInput', 'turn the wipers on'); + this.sendKeys('#textInput', casper.page.event.key.Enter); + }); + casper.waitForSelector('.from-user', function () { + test.assertExists('.message-inner', 'Message sent'); + test.assertTextExists('turn the wipers on', 'Message in bubble'); + casper.waitForText('Ok. Turning on the wipers'); + }); + } + + casper.start(baseHost, function () { + casper.test.comment('Starting Testing'); + test.assertHttpStatus(200, 'conversation-simple is up'); + test.assertTitle('Conversation Chat App', 'Title is correct'); + + testWelcomeMessageExists(); + testEnterMessageClick(); + }); + + casper.run(function () { + test.done(); + }); +}); diff --git a/test/test.express.js b/test/test.express.js deleted file mode 100644 index deec0e4..0000000 --- a/test/test.express.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2015 IBM Corp. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -var app = require('../app'); -var bodyParser = require('body-parser'); -var request = require('supertest'); - -app.use(bodyParser.json()); - -describe('Basic API tests', function() { - it('GET to / should load the home page', function(done) { - request(app).get('/').expect(200, done); - }); - -// it('POST to /api/message should return error message', function(done) { -// request(app) -// .post('/api/message') -// .set('Accept', /application\/json/) -// .expect('Content-Type', /application\/json/) -// .send({'input': {'text': 'Turn on the radio'}}) -// .expect(function(res) { -// if (!res.body) throw new Error('Body was not present in response'); -// console.log(res.body); -// if (!res.body.output) throw new Error('\'Output\' was not present in response'); -// if (!res.body.output.text) throw new Error('\'text\' was not present in response'); -// }) -// .expect(200, done); -// }); -}); diff --git a/test/unit/test.express.js b/test/unit/test.express.js new file mode 100644 index 0000000..c392c90 --- /dev/null +++ b/test/unit/test.express.js @@ -0,0 +1,33 @@ +/** + * Copyright 2015 IBM Corp. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var path = require('path'); +// load default variables for testing +require('dotenv').config({ path: path.join(__dirname, '../../.env.example') }); + +var app = require('../../app'); +var request = require('supertest'); + +describe('express', function() { + it('load home page when GET /', function() { + request(app).get('/').expect(200); + }); + + it('404 when page not found', function() { + request(app).get('/foo/bar').expect(404); + }); + +});