Skip to content

Commit

Permalink
Websockets sample + add tests (#1001)
Browse files Browse the repository at this point in the history
* Websockets sample + add tests

* Fix lint + update copyright
  • Loading branch information
Ace Nassri authored Dec 5, 2018
1 parent a5ac074 commit 2524c3a
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 0 deletions.
54 changes: 54 additions & 0 deletions appengine/websockets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Node.js websockets sample for Google App Engine

This sample demonstrates how to use websockets on
[Google App Engine Flexible Environment][appengine] with Node.js.

* [Setup](#setup)
* [Running locally](#running-locally)
* [Deploying to App Engine](#deploying-to-app-engine)
* [Running the tests](#running-the-tests)

## Setup

Before you can run or deploy the sample, you need to do the following:

1. Refer to the [appengine/README.md][readme] file for instructions on
running and deploying.
1. Install dependencies:

With `npm`:

npm install

or with `yarn`:

yarn install

## Running locally

With `npm`:

npm start

or with `yarn`:

yarn start

## Deploying to App Engine

With `npm`:

npm run deploy

or with `yarn`:

yarn run deploy

## Running the tests

See [Contributing][contributing].

[appengine]: https://cloud.google.com/appengine/docs/flexible/nodejs
[readme]: ../README.md
[contributing]: https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/master/CONTRIBUTING.md

42 changes: 42 additions & 0 deletions appengine/websockets/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright 2018, Google LLC.
* 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';

// [START appengine_websockets_app]
const app = require('express')();
app.set('view engine', 'pug');

const server = require('http').Server(app);
const io = require('socket.io')(server);

app.get('/', (req, res) => {
res.render('index.pug');
});

io.on('connection', socket => {
socket.on('chat message', msg => {
io.emit('chat message', msg);
});
});

if (module === require.main) {
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
}
// [END appengine_websockets_app]
14 changes: 14 additions & 0 deletions appengine/websockets/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# [START appengine_websockets_yaml]
runtime: nodejs
env: flex

# Use only a single instance, so that this local-memory-only chat app will work
# consistently with multiple users. To work across multiple instances, an
# extra-instance messaging system or data store would be needed.
manual_scaling:
instances: 1

network:
session_affinity: true
# [END appengine_websockets_yaml]

37 changes: 37 additions & 0 deletions appengine/websockets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "appengine-websockets",
"description": "Node.js websockets sample for Google App Engine",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"engines": {
"node": ">=8"
},
"scripts": {
"deploy": "gcloud app deploy",
"start": "node app.js",
"lint": "samples lint",
"pretest": "npm run lint",
"test": "node app.js & ava -T 30s test/*.js; killall node",
"e2e-test": "samples test deploy"
},
"dependencies": {
"express": "4.15.4",
"pug": "2.0.0-rc.3",
"socket.io": "2.0.3"
},
"devDependencies": {
"@google-cloud/nodejs-repo-tools": "1.4.17",
"puppeteer": "^1.11.0"
},
"cloud-repo-tools": {
"test": {
"app": {
"msg": "messages"
}
},
"requiresKeyFile": true,
"requiresProjectId": true
}
}
50 changes: 50 additions & 0 deletions appengine/websockets/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright 2018, Google LLC.
* 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';

/* eslint node/no-extraneous-require: "off" */

const test = require('ava');
const puppeteer = require('puppeteer');
/* global document */

let browser, browserPage;

test.before(async () => {
browser = await puppeteer.launch();
browserPage = await browser.newPage();
});

test.after.always(async () => {
await browser.close();
});

test('should process chat message', async t => {
await browserPage.goto('http://localhost:8080');

await browserPage.evaluate(() => {
document.querySelector('input').value = 'test';
document.querySelector('button').click();
});

await new Promise(resolve => setTimeout(resolve, 100));

const itemText = await browserPage.evaluate(
() => document.querySelector('li').textContent
);

t.is(itemText, 'test');
});
58 changes: 58 additions & 0 deletions appengine/websockets/views/index.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//- Copyright 2018 Google LLC.
//-
//- 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.
//- [START appengine_websockets_index]
doctype html
html(lang="en")
head
title Socket.IO chat on App Engine
meta(charset="utf-8")
style.
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
//- [START appengine_websockets_form]
body
ul(id="messages")
form(action="")
input(id="m" autocomplete="off")
button Send
//- [END appengine_websockets_form]
script(src="/socket.io/socket.io.js")
script(src="https://code.jquery.com/jquery-1.11.1.js")
script.
// [START appengine_websockets_js]
$(function () {
var socket = io();
$('form').submit(function(){
console.log($('#m').val());
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
console.log(msg);
$('#messages').append($('<li>').text(msg));
window.scrollTo(0, document.body.scrollHeight);
});
});
// [END appengine_websockets_js]
//- [END appengine_websockets_index]

0 comments on commit 2524c3a

Please sign in to comment.