This commit is contained in:
Martin Donnelly 2017-10-27 21:26:27 +01:00
commit d332140d32
26 changed files with 28303 additions and 0 deletions

55
.eslintrc.json Normal file
View File

@ -0,0 +1,55 @@
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": false
}
},
"env": {
"browser": true,
"node": true,
"es6": true
},
"rules": {
"arrow-spacing": "error",
"block-scoped-var": "error",
"block-spacing": "error",
"brace-style": ["error", "stroustrup", {}],
"camelcase": "error",
"comma-dangle": ["error", "never"],
"comma-spacing": ["error", { "before": false, "after": true }],
"comma-style": [1, "last"],
"consistent-this": [1, "_this"],
"curly": [1, "multi"],
"eol-last": 1,
"eqeqeq": 1,
"func-names": 1,
"indent": ["error", 2, { "SwitchCase": 1 }],
"lines-around-comment": ["error", { "beforeBlockComment": true, "allowArrayStart": true }],
"max-len": [1, 120, 2], // 2 spaces per tab, max 80 chars per line
"new-cap": 1,
"newline-before-return": "error",
"no-array-constructor": 1,
"no-inner-declarations": [1, "both"],
"no-mixed-spaces-and-tabs": 1,
"no-multi-spaces": 2,
"no-new-object": 1,
"no-shadow-restricted-names": 1,
"object-curly-spacing": ["error", "always"],
"padded-blocks": ["error", { "blocks": "never", "switches": "always" }],
"prefer-const": "error",
"prefer-template": "error",
"one-var": 0,
"quote-props": ["error", "always"],
"quotes": [1, "single"],
"radix": 1,
"semi": [1, "always"],
"space-before-blocks": [1, "always"],
"space-infix-ops": 1,
"vars-on-top": 1,
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
"spaced-comment": ["error", "always", { "markers": ["/"] }]
}
}

146
.gitignore vendored Normal file
View File

@ -0,0 +1,146 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
/src/bundle.js
/src/bundle.js.map

69
Readme.md Normal file
View File

@ -0,0 +1,69 @@
# Wipro OpenWeatherMap App
## Intro
An application written in Javascript, utilising Backbone on the front end and Express on the back end.
The application is a single HTML page displaying the 5 day weather forecast for a location of my choice. In this case it is [Glasgow City](http://nurl.co/QD).
A live version of the application can be viewed at: [http://wiprotest-dev.eu-west-1.elasticbeanstalk.com](http://wiprotest-dev.eu-west-1.elasticbeanstalk.com)
## Installation
To install this application, it should be cloned from the repo:
```
git clone https://github.com/martind2000/wiprotest.git
```
Once the code has been retrieved you have to install the node packages:
```
npm install
```
This will install and build the app.
## How to use
Once the node packages have been installed the application is almost ready to run.
An OpenWeatherMap API key is required, if you do not already have one, then you can get one for free by going to this address: [http://openweathermap.org/appid](http://openweathermap.org/appid)
Once an APPID key has been obtained the application can be run by using the following
```
openweatherAPI=<APPID> npm start
```
replace \<APPID\> with your appid, such as:
```
openweatherAPI=b1b15e88fa797225412429c1 npm start
```
## Test
This application is currently tested using Mocha and Expect.js. The test is in the /test folder.
To run the test, run the following command.
```
npm test
```
## Todo
* Enhance the build process to merge the font and weather icon css, the Material UI css and the colour css into one file to reduce the number of server requests from 3 to 1.
* Better testing. Currently the tests are simple for brevity, however I would like to implement Browser testing to ensure the view portion of the code is working as intended.

29
gulp/backbone.js Normal file
View File

@ -0,0 +1,29 @@
'use strict';
const gulp = require('gulp');
const browserify = require('gulp-browserify');
const rename = require('gulp-rename');
const buffer = require('vinyl-buffer');
const sourcemaps = require('gulp-sourcemaps');
gulp.task('bundleBackbone', function() {
// Single entry point to browserify
gulp.src(['./src/backbone/js/app.js'])
.pipe(browserify({
'insertGlobals' : true,
'debug' : true
}))
.pipe(buffer())
// optional, remove if you dont want sourcemaps
.pipe(sourcemaps.init({ 'loadMaps': true })) // loads map from browserify file
// Add transformation tasks to the pipeline here.
.pipe(sourcemaps.write('./src/backbone/')) // writes .map file
.pipe(rename('bundle.js'))
// .pipe(uglify())
.pipe(gulp.dest('./src/backbone/'));
});
gulp.task('buildBackbone', ['bundleBackbone'], function() {
gulp.watch('src/backbone/js/**/*.js', ['bundleBackbone']);
});

5
gulpfile.js Normal file
View File

@ -0,0 +1,5 @@
const gulp = require('gulp');
const requireDir = require('require-dir');
requireDir('./gulp');

39
old-gulpfile.js Normal file
View File

@ -0,0 +1,39 @@
'use strict';
const watchify = require('watchify');
const browserify = require('browserify');
const gulp = require('gulp');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const gutil = require('gulp-util');
const sourcemaps = require('gulp-sourcemaps');
const assign = require('lodash.assign');
// add custom browserify options here
const customOpts = {
entries: ['./src/js/app.js'],
debug: true
};
const opts = assign({}, watchify.args, customOpts);
const b = watchify(browserify(opts));
// add transformations here
// i.e. b.transform(coffeeify);
gulp.task('js', bundle); // so you can run `gulp js` to build the file
b.on('update', bundle); // on any dep update, runs the bundler
b.on('log', gutil.log); // output build logs to terminal
function bundle() {
return b.bundle()
// log errors if they happen
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
// optional, remove if you don't need to buffer file contents
.pipe(buffer())
// optional, remove if you dont want sourcemaps
.pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
// Add transformation tasks to the pipeline here.
.pipe(sourcemaps.write('./')) // writes .map file
.pipe(gulp.dest('./src'));
}

7651
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

52
package.json Normal file
View File

@ -0,0 +1,52 @@
{
"name": "wiprotest",
"version": "1.0.0",
"description": "Wipro Weather Test",
"main": "server.js",
"scripts": {
"build": "gulp js",
"prestart": "browserify -g uglifyify src/backbone/js/app.js -o src/backbone/bundle.js",
"test": "./node_modules/mocha/bin/mocha --reporter spec",
"postinstall": "browserify -g uglifyify src/backbone/js/app.js -o src/backbone/bundle.js",
"start": "node server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/martind2000/wiprotest.git"
},
"author": "Martin Donnelly <martind2000@gmail.com>",
"license": "ISC",
"bugs": {
"url": "https://github.com/martind2000/wiprotest/issues"
},
"homepage": "https://github.com/martind2000/wiprotest#readme",
"dependencies": {
"apicache": "^1.1.0",
"backbone": "^1.3.3",
"browserify": "^14.4.0",
"express": "^4.16.1",
"jquery": "^3.2.1",
"log4js": "^2.3.4",
"moment": "^2.18.1",
"openweather-apis": "^3.3.2",
"react": "^16.0.0",
"uglifyify": "^4.0.4",
"underscore": "^1.8.3"
},
"devDependencies": {
"expect.js": "^0.3.1",
"gulp": "^3.9.1",
"gulp-browserify": "^0.5.1",
"gulp-rename": "^1.2.2",
"gulp-sourcemaps": "^2.6.1",
"gulp-tasks": "0.0.2",
"gulp-uglify": "^3.0.0",
"lodash.assign": "^4.2.0",
"mocha": "^3.5.3",
"require-dir": "^0.3.2",
"sinon": "^4.0.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.9.0"
}
}

34
server.js Normal file
View File

@ -0,0 +1,34 @@
const express = require('express');
const path = require('path');
const apicache = require('apicache');
const logger = require('log4js').getLogger('Server');
const weather = require('./server/weather');
logger.level = 'debug';
const app = express();
const port = process.env.PORT || 3000;
const sitePath = 'src';
apicache.options({ 'debug': true });
const cache = apicache.middleware;
app.use(express.static(path.join(__dirname, sitePath)));
app.get('/weather', cache('20 minutes'), (req, res) => {
weather.doGetOpenWeather()
.then((d) => {
res.send(d);
}).catch((e) => {
logger.error(e);
res.status(500).send('There was an error!');
});
});
app.listen(port, (err) => {
if (err)
return logger.error('Server error:', err);
logger.info(`Weather Server is listening on ${port}`);
});

25
server/weather.js Normal file
View File

@ -0,0 +1,25 @@
const logger = require('log4js').getLogger('Weather');
const weather = require('openweather-apis');
logger.level = 'debug';
const openWeatherApiKey = process.env.openweatherAPI || '';
weather.setAPPID(openWeatherApiKey);
weather.setLang('en');
weather.setCity('Glasgow City');
function doGetOpenWeather() {
return new Promise((resolve, reject) => {
weather.getWeatherForecastForDays(5, function(err, wData) {
if (err)
return reject(err);
else
return resolve(wData);
});
});
}
module.exports.doGetOpenWeather = doGetOpenWeather;

24
src/angular/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - Angular</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather Angular</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

24
src/aurelia/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - Aurelia</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather Aurelia</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

19690
src/backbone/bundle.js Normal file

File diff suppressed because it is too large Load Diff

24
src/backbone/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - Backbone</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
<script type='module' src="bundle.js"></script>
</body>
</html>

4
src/backbone/js/app.js Normal file
View File

@ -0,0 +1,4 @@
const WView = require('./view/weather');
window.wview = new WView({ 'el': document.getElementById('weather') });

View File

@ -0,0 +1,34 @@
const moment = require('moment');
const Backbone = require('backbone');
function reduceOpenWeather(item) {
// Openweather returns timestamps in seconds. Moment requires them in milliseconds.
const ts = moment(item.dt * 1000);
const weatherBlock = item.weather[0];
return {
'timestamp': item.dt,
'icon': `wi-owm-${weatherBlock.id}`,
'summary': weatherBlock.description,
'tempHigh': parseInt(item.temp.max, 10),
'tempLow': parseInt(item.temp.min, 10),
'datelong': ts.format(),
'time': item.dt,
'date': ts.format('D/M'),
'day': ts.format('ddd')
};
}
const WCollection = Backbone.Collection.extend({
'url': '/weather',
'parse': function(data) {
return data.list.map((item) => {
// Reduce the data
return reduceOpenWeather(item);
});
}
});
module.exports = WCollection;

View File

@ -0,0 +1,41 @@
const $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const WCollection = require('../collection/weather');
const WView = Backbone.View.extend({
'tagName': 'div',
'template': _.template(`
<% _.each(data, function(item) {%>
<div class="card mui--z1 mui-col-md-6 mui-col-lg-4">
<div class="mui-col-md-3">
<div class="mui--text-accent mui--text-title day mui--text-center"><%= item.day %></div>
<div class="mui--text-dark-secondary mui--text-subhead mui--text-center"><%= item.date %></div>
</div>
<div class="mui-col-md-9">
<div>
<i class="mui--text-headline wi <%= item.icon %>"></i>
<span class="mui--text-display1 temp<%=item.tempHigh %>"><%= item.tempHigh %>°</span> /
<span class="mui--text-headline temp<%=item.tempLow %>"><%= item.tempLow %>°</span></div>
<div class="mui--text-caption summary"><%= item.summary %></div>
</div>
</div>
<% });
%>`),
'initialize': function() {
_.bindAll(this, 'render');
this.collection = new WCollection();
this.listenTo(this.collection, 'reset sync', _.debounce(_.bind(this.render), 128));
this.collection.fetch();
},
'render': function() {
if (this.collection.length !== 0) {
const data = { 'data':this.collection.toJSON() };
this.$el.html(this.template(data));
}
}
});
module.exports = WView;

85
src/css/common.css Normal file
View File

@ -0,0 +1,85 @@
body {
background-color: #eee;
}
.card {
position: relative;
background-color: #fff;
min-height:72px;
}
.mui--text-display4, .mui--text-display3 {
font-family: "Roboto Slab", "Helvetica Neue", Helvetica, Arial;
}
.temp0, .temp1, .temp2, .temp3, .temp4, .temp5 {
color: rgb(80,181,221)
}
.temp6 {
color: rgb(78,178,206)
}
.temp7 {
color: rgb(76, 176, 190)
}
.temp8 {
color: rgb(73, 173, 175)
}
.temp9 {
color: rgb(72, 171, 159)
}
.temp10 {
color: rgb(70, 168, 142)
}
.temp11 {
color: rgb(68, 166, 125)
}
.temp12 {
color: rgb(66, 164, 108)
}
.temp13 {
color: rgb(102, 173, 94)
}
.temp14 {
color: rgb(135, 190, 64)
}
.temp15 {
color: rgb(179, 204, 26)
}
.temp16 {
color: rgb(214, 213, 28)
}
.temp17 {
color: rgb(249, 202, 3)
}
.temp18 {
color: rgb(246, 181, 3)
}
.temp19 {
color: rgb(244, 150, 26)
}
.temp20 {
color: rgb(236, 110, 5)
}
.day {
font-family: "Roboto Slab", "Helvetica Neue", Helvetica, Arial, sans-serif;
text-transform: uppercase;
}
.summary::first-letter {
text-transform: capitalize
}

24
src/ember/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - Ember</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather Ember</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

24
src/es2016/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - ES2016</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather ES2016</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

BIN
src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

62
src/index.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TestMVC</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display4">TestMVC</div>
<div class="mui-row">
<div class="mui-col-md-4">
<div class="mui--text-headline">Introduction</div>
<div class="mui--text-body2">Blah blah blah</div>
</div>
<div class="mui-col-md-8">
<div class="mui--text-headline">Examples</div>
<div class="mui--text-accent">Pure Javascript</div>
<div class="mui-row">
<div class="mui-col-md-3 mui--text-center">
<a href="backbone/index.html" class="mui-btn">Backbone</a>
</div>
<div class="mui-col-md-3 mui--text-center">
<a href="react/index.html" class="mui-btn" disabled>React</a>
</div>
<div class="mui-col-md-3 mui--text-center">
<a href="aurelia/index.html" class="mui-btn" disabled>Aurelia</a>
</div>
<div class="mui-col-md-3 mui--text-center">
<a href="angular/index.html" class="mui-btn" disabled>Angular</a>
</div>
</div>
<div class="mui-row">
<div class="mui-col-md-3 mui--text-center">
<a href="ember/index.html" class="mui-btn" disabled>Ember</a>
</div>
<div class="mui-col-md-3 mui--text-center">
<a href="knockout/index.html" class="mui-btn" disabled>Knockout</a>
</div>
<div class="mui-col-md-3 mui--text-center">
<a href="vue/index.html" class="mui-btn" disabled>Vue</a>
</div>
<div class="mui-col-md-3 mui--text-center">
<a href="es2016/index.html" class="mui-btn" disabled>ES2016</a>
</div>
</div>
</div>
</div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

24
src/knockout/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - Knockout</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather Knockout</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

24
src/react/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - React</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather React</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

24
src/vue/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather - Vue</title>
<link href="//fonts.googleapis.com/css?family=Roboto+Slab:700-Ubuntu:500-Roboto:regular" rel="stylesheet">
<link href="//cdn.muicss.com/mui-0.9.26/css/mui.min.css" rel="stylesheet" type="text/css"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/weather-icons/2.0.9/css/weather-icons.css" rel="stylesheet" type="text/css"/>
<link href="/css/common.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="mui-container">
<div class="mui--text-display3">Weather Vue</div>
<div id="weather"></div>
</div>
<script src="//cdn.muicss.com/mui-0.9.26/js/mui.min.js"></script>
</body>
</html>

90
test/collection.js Normal file
View File

@ -0,0 +1,90 @@
const Backbone = require('backbone');
const _ = require('underscore');
const WCollection = require('../src/js/collection/weather');
const expect = require('expect.js');
const sinon = require('sinon');
const wData = { 'city':{ 'id':3333231, 'name':'Glasgow City', 'coord':{ 'lon':-4.25, 'lat':55.8667 }, 'country':'GB', 'population':0 }, 'cod':'200', 'message':1.3268098, 'cnt':5, 'list':[{ 'dt':1506945600, 'temp':{ 'day':10.69, 'min':9.99, 'max':10.69, 'night':10.22, 'eve':10.69, 'morn':10.69 }, 'pressure':1001.71, 'humidity':94, 'weather':[{ 'id':500, 'main':'Rain', 'description':'light rain', 'icon':'10d' }], 'speed':7.6, 'deg':279, 'clouds':92, 'rain':0.99 }, { 'dt':1507032000, 'temp':{ 'day':11.13, 'min':9.52, 'max':11.28, 'night':10.11, 'eve':10.33, 'morn':9.52 }, 'pressure':1014.59, 'humidity':80, 'weather':[{ 'id':500, 'main':'Rain', 'description':'light rain', 'icon':'10d' }], 'speed':7.43, 'deg':285, 'clouds':88, 'rain':0.44 }, { 'dt':1507118400, 'temp':{ 'day':10.98, 'min':8.26, 'max':10.98, 'night':8.26, 'eve':9.29, 'morn':10 }, 'pressure':1007.06, 'humidity':93, 'weather':[{ 'id':501, 'main':'Rain', 'description':'moderate rain', 'icon':'10d' }], 'speed':8.77, 'deg':267, 'clouds':92, 'rain':8.52 }, { 'dt':1507204800, 'temp':{ 'day':10.77, 'min':5.92, 'max':10.79, 'night':5.92, 'eve':9.56, 'morn':6.4 }, 'pressure':1012.43, 'humidity':91, 'weather':[{ 'id':500, 'main':'Rain', 'description':'light rain', 'icon':'10d' }], 'speed':7.26, 'deg':291, 'clouds':64 }, { 'dt':1507291200, 'temp':{ 'day':9.28, 'min':5.88, 'max':12.7, 'night':10.84, 'eve':12.7, 'morn':5.88 }, 'pressure':1001.13, 'humidity':0, 'weather':[{ 'id':501, 'main':'Rain', 'description':'moderate rain', 'icon':'10d' }], 'speed':4.32, 'deg':189, 'clouds':100, 'rain':9.37 }] };
const wResult = [
{
'date': '2/10',
'datelong': '2017-10-02T13:00:00+01:00',
'day': 'Mon',
'icon': 'wi-owm-500',
'summary': 'light rain',
'tempHigh': 10,
'tempLow': 9,
'time': 1506945600,
'timestamp': 1506945600
},
{
'date': '3/10',
'datelong': '2017-10-03T13:00:00+01:00',
'day': 'Tue',
'icon': 'wi-owm-500',
'summary': 'light rain',
'tempHigh': 11,
'tempLow': 9,
'time': 1507032000,
'timestamp': 1507032000
},
{
'date': '4/10',
'datelong': '2017-10-04T13:00:00+01:00',
'day': 'Wed',
'icon': 'wi-owm-501',
'summary': 'moderate rain',
'tempHigh': 10,
'tempLow': 8,
'time': 1507118400,
'timestamp': 1507118400
},
{
'date': '5/10',
'datelong': '2017-10-05T13:00:00+01:00',
'day': 'Thu',
'icon': 'wi-owm-500',
'summary': 'light rain',
'tempHigh': 10,
'tempLow': 5,
'time': 1507204800,
'timestamp': 1507204800
},
{
'date': '6/10',
'datelong': '2017-10-06T13:00:00+01:00',
'day': 'Fri',
'icon': 'wi-owm-501',
'summary': 'moderate rain',
'tempHigh': 12,
'tempLow': 5,
'time': 1507291200,
'timestamp': 1507291200
}
];
describe('Weather Collection', () => {
let collection;
sinon.stub(Backbone, 'sync').yieldsTo('success', wData);
beforeEach(() => {
collection = new WCollection();
});
it('should create an empty collection', () => {
expect(collection.length).to.be(0);
});
it('Should fetch the data', () => {
collection.fetch();
expect(collection.length).to.be(5);
});
it('Should reduce the data', () => {
collection.fetch();
expect(collection.toJSON()).to.eql(wResult);
});
});