This commit is contained in:
Alberto Basalo 2015-05-28 14:02:00 +02:00
Родитель b1f6d74381
Коммит 9b939b817d
56 изменённых файлов: 1406 добавлений и 138 удалений

Просмотреть файл

@ -0,0 +1,5 @@
module.exports = {
port: 3000,
mongoUrl: "mongodb://localhost:27017/cash-flow",
log: "debug"
};

Просмотреть файл

@ -3,54 +3,49 @@ var movimientosData = require('../data/movimientosData.js');
module.exports.routeMovimientos = function (app) {
app.route('/api/priv/movimientos')
.get(function (req, res, next) {
//res.json(movimientosData.getMovimientos(req.usuario));
movimientosData.gettingMovimientos(req.usuario)
.then(function (result) {
//console.log('OK: /api/priv/movimientos ->> ' + JSON.stringify(result));
res.status(200).json(result);
})
.fail(function (err) {
//console.error('ERR: /api/priv/movimientos ->> ' + JSON.stringify(err));
res.status(500).send(err);
});
})
.post(function (req, res, next) {
var movimiento = req.body;
movimiento.usuario = req.usuario;
//movimientosData.postMovimiento(movimiento);
//res.status(200).send();
movimientosData.postingMovimiento(movimiento)
.then(function (result) {
res.status(200).send();
})
.fail(function (err) {
res.status(500).send(err);
});
});
app.route('/api/priv/movimientos')
.get(function (req, res, next) {
promise2response(
movimientosData.gettingMovimientos(req.usuario), res);
})
.post(function (req, res, next) {
var movimiento = req.body;
movimiento.usuario = req.usuario;
var pro = movimientosData.postingMovimiento(movimiento);
promise2response(pro, res);
});
app.get('/api/priv/movimientos/:id', function (req, res, next) {
//res.json(movimientosData.getMovimiento(req.params.id, req.usuario));
movimientosData.gettingMovimiento(req.params.id, req.usuario)
.then(function (result) {
res.json(result[0]);
})
.fail(function (err) {
res.status(500).send(err);
});
});
function promise2response(pro, res) {
pro
.then(function (result) {
res.status(200).send();
})
.fail(function (err) {
res.status(500).send(err);
});
}
app.get('/api/priv/total', function (req, res, next) {
//res.json(movimientosData.getTotalUsuario(req.usuario));
movimientosData.gettingTotalUsuario(req.usuario)
.then(function (result) {
res.json(result);
})
.fail(function (err) {
res.status(500).send(err);
});
});
app.get('/api/priv/movimientos/:id', function (req, res, next) {
//res.json(movimientosData.getMovimiento(req.params.id, req.usuario));
movimientosData.gettingMovimiento(req.params.id, req.usuario)
.then(function (result) {
res.json(result[0]);
})
.fail(function (err) {
res.status(500).send(err);
});
});
app.get('/api/priv/total', function (req, res, next) {
//res.json(movimientosData.getTotalUsuario(req.usuario));
movimientosData.gettingTotalUsuario(req.usuario)
.then(function (result) {
res.json(result);
})
.fail(function (err) {
res.status(500).send(err);
});
});
}

Просмотреть файл

@ -3,6 +3,26 @@ var mondodb = require('mongodb');
var MongoClient = mondodb.MongoClient;
var mongoUrl = "mongodb://localhost:27017/control_caja";
function getUsuarios() {
MongoClient.connect(mongoUrl, alConectarse);
function alConectarse(err, db) {
if (err) {
console.error(err);
} else {
db.collection('usuarios').find({}).toArray(alRecibirArray);
function alRecibirArray(err, result) {
if (err) {
console.error(err);
} else {
return result;
}
}
}
}
}
exports.ObjectId = mondodb.ObjectID;
exports.connecting = connecting;
exports.finding = finding;
@ -11,78 +31,78 @@ exports.inserting = inserting;
exports.updating = updating;
function connecting(mongoCol) {
var deferred = Q.defer();
MongoClient.connect(mongoUrl, function (err, db) {
if (err) {
callback2Promise(err, null, deferred);
} else {
callback2Promise(null, db.collection(mongoCol), deferred);
}
});
return deferred.promise;
var deferred = Q.defer();
MongoClient.connect(mongoUrl, function (err, db) {
if (err) {
callback2Promise(err, null, deferred);
} else {
callback2Promise(null, db.collection(mongoCol), deferred);
}
});
return deferred.promise;
}
function finding(mongoCol, query) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.find(query).toArray(function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.find(query).toArray(function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function aggregating(mongoCol, query) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.aggregate(query, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.aggregate(query, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function inserting(mongoCol, document) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.insert(document, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.insert(document, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function updating(mongoCol, query, document) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.update(query, document, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.update(query, document, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function callback2Promise(err, result, deferred) {
if (err) {
console.error(err);
deferred.reject(err);
} else {
deferred.resolve(result);
}
if (err) {
console.error(err);
deferred.reject(err);
} else {
deferred.resolve(result);
}
}

Просмотреть файл

@ -2,27 +2,12 @@
var mongodb = require('./mongodb.js')
var mongoCol = "movimientos"
//module.exports.getMovimientos = function (usuario) {
// var movimientosUsuario = movimientos.filter(function (m) {
// return m.usuario = usuario;
// });
// return movimientosUsuario;
//}
module.exports.gettingMovimientos = function (usuario) {
return mongodb.finding(mongoCol, {
usuario: usuario
});
}
//module.exports.getMovimiento = function (movId, usuario) {
// var movimientoBuscado = movimientos.filter(function (movimiento) {
// return movimiento.id == movId && movimiento.usuario == usuario;
// })[0];
// return movimientoBuscado;
//}
module.exports.gettingMovimiento = function (movId, usuario) {
return mongodb.finding(mongoCol, {
_id: movId,
@ -30,12 +15,6 @@ module.exports.gettingMovimiento = function (movId, usuario) {
});
}
//module.exports.getTotalUsuario = function (usuario) {
// var totalUsuario = getTotalUsuario(usuario);
// return totalUsuario;
//}
module.exports.gettingTotalUsuario = function (usuario) {
return mongodb.aggregating(mongoCol, [
{
@ -55,18 +34,6 @@ module.exports.gettingTotalUsuario = function (usuario) {
}
]);
}
//module.exports.postMovimiento = function (movimiento) {
// maxId++;
// movimiento.id = maxId;
// movimientos.push(movimiento);
// var totalUsuario = getTotalUsuario(movimiento.usuario);
// if (movimiento.tipo == 'Ingreso')
// totalUsuario.ingresos += movimiento.importe;
// else
// totalUsuario.gastos += movimiento.importe;
// return getMovimientosUsuario(movimiento.usuario);
//}
module.exports.postingMovimiento = function (movimiento) {
return mongodb.inserting(mongoCol,movimiento);
}

Просмотреть файл

@ -0,0 +1,5 @@
module.exports = {
port: 3000,
mongoUrl: "mongodb://localhost:27017/cash-flow",
log: "debug"
};

Просмотреть файл

@ -0,0 +1,68 @@
<html lang="es" ng-app="controlCajaApp">
<head>
<meta charset="utf-8" />
<meta lang="es" />
<!-- configuración para url amigables-->
<base href="/">
<meta name="fragment" content="!">
<title>Control de Caja</title>
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
<meta name="description" content="Ejemplo Control de Caja en AngularJS por Alberto Basalo" />
<meta name="author" content="Alberto Basalo @ Ágora Binaria" />
<meta name="application-name" content="ControlAngularJS" />
<meta name="Keywords" content="AngularJS, ejemplo, tutorial, curso" />
<link rel="author" href="https://plus.google.com/+AlbertoBasalo71" />
<!-- Bootstrap core CSS -->
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top" name="navbar" role="navigation" ng-controller="MenuCtrl as menu">
<div class="navbar-inner">
<div class="container">
<ul class="nav navbar-nav">
<li ng-class="{ active: menu.isActive('total') }">
<a ui-sref="total" name="menu-total">Totales</a>
</li>
<li ng-class="{ active: menu.isActive('nuevo') }">
<a ui-sref="nuevo" name="menu-nuevo">Nuevo</a>
</li>
<li ng-class="{ active: menu.isActive('lista') }">
<a ui-sref="lista" name="menu-lista">Lista</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container text-center" style="padding-top:50px;" ui-view>
</div>
<ab-pie-pagina></ab-pie-pagina>
<!-- JavaScript References -->
<!-- la referencia a socket.io la emite el propio servidor-->
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-cookies.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js"></script>
<!-- Mover todo el contenido al directorio static-->
<script src="static/_comun/app.js"></script>
<script src="static/_comun/appHttpLog.js"></script>
<script src="static/_comun/appSecurity.js"></script>
<script src="static/_comun/menuCtrl.js"></script>
<!-- Referencia a la socket factory -->
<script src="static/_comun/socketFactory.js"></script>
<script src="static/registro/registroCtrl.js"></script>
<script src="static/controlcaja/cajaCtrl.js"></script>
<script src="static/movimiento/movimientoCtrl.js"></script>
<!-- Referencia al nuevo controlador para el dashboard-->
<script src="static/dashboard/dashboardCtrl.js"></script>
<script src="static/data/movimientosFactory.js"></script>
<script src="static/data/maestrosFactory.js"></script>
<script src="static/filtros/filtros.js"></script>
<script src="static/directivas/directivas.js"></script>
<script src="static/directivas/valoracion/valoracion.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,40 @@
angular.module('controlCajaApp', ['ui.router', 'ngCookies', 'ngResource', 'abFiltros', 'abDirectivas']);
angular.module('controlCajaApp').config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
// configuración para url amigables
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/');
$stateProvider
.state('total', {
url: '/',
controller: 'CajaCtrl as caja',
templateUrl: 'static/controlcaja/total.html'
})
.state('nuevo', {
url: '/nuevo',
controller: 'CajaCtrl as caja',
templateUrl: 'static/controlcaja/nuevo.html'
})
.state('lista', {
url: '/lista',
controller: 'CajaCtrl as caja',
templateUrl: 'static/controlcaja/lista.html'
})
.state('movimiento', {
url: '/movimiento/:_id',
controller: 'MovimientoCtrl as vm',
templateUrl: 'static/movimiento/movimiento.html'
})
.state('registro', {
url: '/registro',
controller: 'RegistroCtrl as registro',
templateUrl: 'static/registro/registro.html'
}).state('dashboard', {
url: '/dashboard',
controller: 'DashboardCtrl as vm',
templateUrl: 'static/dashboard/dashboard.html'
});
});

Просмотреть файл

@ -0,0 +1,21 @@
(function () {
function configuradorInterceptores($httpProvider) {
$httpProvider.interceptors.push(funcionInterceptoraLog);
}
function funcionInterceptoraLog($log) {
var interceptor = {};
interceptor.request = function (request) {
$log.info('request:' + request.url);
return request ;
};
interceptor.responseError = function (response) {
$log.error("excepción: " + response.status + " de :" + response.config.url);
}
return interceptor;
}
angular.module('controlCajaApp').config(configuradorInterceptores);
}());

Просмотреть файл

@ -0,0 +1,29 @@
(function () {
function configuradorInterceptores($httpProvider) {
$httpProvider.interceptors.push(funcionInterceptoraSeguridad);
}
function funcionInterceptoraSeguridad($q, $injector, $cookieStore, $rootScope) {
var interceptor = {};
interceptor.request = function (request) {
request.headers["sessionId"] = $cookieStore.get("sessionId");
return request || $q.when(request);
};
interceptor.responseError = function (response) {
var state = $injector.get('$state');
if (response.status === 401) {
$rootScope.mensaje = "No hay derecho!!!";
state.go('registro');
} else if (response.status === 419) {
$rootScope.mensaje = "Estoy caduco!!!";
$cookieStore.remove("sessionId")
state.go('registro');
};
return $q.reject(response);
}
return interceptor;
}
angular.module('controlCajaApp').config(configuradorInterceptores);
}());

Просмотреть файл

@ -0,0 +1,8 @@
(function () {
var menuCtrl = function ($state) {
this.isActive = function (estado) {
return $state.is(estado);
}
}
angular.module('controlCajaApp').controller('MenuCtrl',menuCtrl);
}());

Просмотреть файл

@ -0,0 +1,3 @@
<section name="total">
<h2>No se ha encontrado lo que buscabas</h2>
</section>

Просмотреть файл

@ -0,0 +1,24 @@
(function () {
angular.module('controlCajaApp').factory('socketFactory', function ($rootScope) {
var socket;
return {
connect: function(){
socket= io.connect();
console.log("IO: connected" );
},
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
console.log("IN: " + eventName + " : " + JSON.stringify(args));
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data) {
console.log("OUT: " + eventName + " : " + JSON.stringify(data));
socket.emit(eventName, data);
}
}
});
}());

Просмотреть файл

@ -0,0 +1,35 @@
(function () {
var cajaCtrl = function (maestrosFactory, movimientosFactory) {
var vm = this;
vm.titulo = "Controla tu Cash Flow";
vm.maestros = maestrosFactory.get();
vm.nuevoMovimiento = new movimientosFactory.movimientos();
vm.nuevoMovimiento.esIngreso = 1;
vm.nuevoMovimiento.fecha = new Date();
vm.movimientos = movimientosFactory.movimientos.query();
vm.total = movimientosFactory.total.get();
vm.guardarMovimiento = function () {
vm.nuevoMovimiento.tipo = vm.tipo(vm.nuevoMovimiento);
vm.nuevoMovimiento.$save()
.then(function (postedData) {
vm.movimientos = movimientosFactory.movimientos.query();
vm.total = movimientosFactory.total.get();
vm.nuevoMovimiento.importe = 0;
});
}
vm.balance = function () {
return vm.total.ingresos - vm.total.gastos
}
vm.tipo = function (movimiento) {
return movimiento.esIngreso && 'Ingreso' || 'Gasto'
}
}
angular.module('controlCajaApp').controller('CajaCtrl', cajaCtrl);
}());

Просмотреть файл

@ -0,0 +1,28 @@
<section name="Lista">
<ab-cabecera>Estos son tus movimientos recientes.</ab-cabecera>
<br>
<label class="control-label" for="importe">Filtrar:</label>
<input type="text" name="filtro" placeholder="qué buscas?" class="input" ng-model="valorBuscado">
<button class="btn-primary" ng-click="valorCorte=0">Ver todos los movimientos</button>
<button class="btn-danger" ng-click="valorCorte=1000">Sólo grandes movimientos</button>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th><a href="" ng-click="campo = 'fecha'; sentido = campo == 'fecha' && !sentido">Fecha</a>
</th>
<th>Tipo</th>
<th>Categoría</th>
<th><a href="" ng-click="campo = 'importe'; sentido = campo == 'importe' && !sentido">Importe</a>
</th>
<th>Valoración</th>
</tr>
</thead>
<tbody>
<tr
ng-repeat="movimientoRepeater in caja.movimientos | abGranImporte:valorCorte | filter:valorBuscado | orderBy:campo:sentido"
ab-fila-movimiento movimientodirectiva="movimientoRepeater"
ab-seleccionado >
</tbody>
</table>
</section>

Просмотреть файл

@ -0,0 +1,68 @@
<section name="nuevoMovimiento" class="row-fluid">
<ab-cabecera>Introduce tus movimientos.</ab-cabecera>
<form class="form-horizontal text-left">
<fieldset>
<div class="row-fluid">
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="tipo">Tipo</label>
<div class="controls">
<div class="btn-group">
<button type="button" class="btn btn-success" ng-class="{'active':caja.nuevoMovimiento.esIngreso==1}" ng-click="caja.nuevoMovimiento.esIngreso=1; caja.nuevoMovimiento.esGasto=0">
<span ng-class="{'small':caja.nuevoMovimiento.esIngreso==0}">+ Ingreso</span>
</button>
<button type="button" class="btn btn-danger" ng-class="{'active':caja.nuevoMovimiento.esGasto==1}" ng-click="caja.nuevoMovimiento.esIngreso=0; caja.nuevoMovimiento.esGasto=1">
<span ng-class="{'small':caja.nuevoMovimiento.esGasto==0}">- Gasto</span>
</button>
</div>
</div>
</div>
<div class="control-group">
<label class="control-label" for="categ">Categoría</label>
<div class="controls">
<select ng-show="caja.nuevoMovimiento.esIngreso" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasIngresos"></select>
<select ng-show="caja.nuevoMovimiento.esGasto" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasGastos"></select>
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="fecha">Fecha</label>
<div class="controls">
<input type="date" name="fecha" class="input" ng-model="caja.nuevoMovimiento.fecha">
</div>
</div>
<div class="control-group">
<label class="control-label" for="importe">Importe</label>
<div class="controls">
<input type="number" name="importe" class="input" ng-model="caja.nuevoMovimiento.importe">
</div>
</div>
</div>
</div>
<div class="row-fluid">
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="texto">Texto</label>
<div class="controls">
<input type="text" name="texto" class="input" ng-model="caja.nuevoMovimiento.texto">
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="valoracion">Valoración</label>
<div class="controls">
<div ab-valoracion valor="caja.nuevoMovimiento.valoracion" max="10" solo-lectura="false"></div>
</div>
</div>
</div>
</div>
<div class="text-right">
<button style="margin-top: 20px" type="button" class="btn btn-lg btn-primary" ng-click="caja.guardarMovimiento()" name="guardar">
<span>Guardar {{ caja.tipo(caja.nuevoMovimiento) }}</span>
</button>
</div>
</fieldset>
</form>
</section>

Просмотреть файл

@ -0,0 +1,34 @@
<section name="total">
<ab-cabecera>Comprueba de dónde viene y a dónde va tu dinero.</ab-cabecera>
<div class="row-fluid">
<div class="row placeholders">
<div class="col-xs-8 col-sm-4 placeholder">
<h1>
<span class="label label-success" name="ingresos">
{{ caja.total.ingresos | number:2 }} €
</span>
</h1>
<h4>Total ingresos</h4>
<span class="text-muted">Acumulado</span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h1>
<span class="label label-danger" name="gastos">
{{ caja.total.gastos | number:2 }} €
</span>
</h1>
<h4>Total gastos</h4>
<span class="text-muted">Acumulado</span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h1>
<span class="label " ng-class="{'label-success': caja.balance()>=0 , 'label-danger' : caja.balance()<0}" name="balance">
{{ caja.balance() | number:2 }} €
</span>
</h1>
<h4>Balance</h4>
<span class="text-muted">Ingresos-Gastos</span>
</div>
</div>
</div>
</section>

Просмотреть файл

@ -0,0 +1,31 @@
<section name="total">
<ab-cabecera>Comprueba de dónde viene y a dónde va tu dinero.</ab-cabecera>
<div class="row-fluid">
<div class="row placeholders">
<div class="col-xs-8 col-sm-4 placeholder">
<h1>
<span class="label label-info" name="usuarios">
{{ vm.total.usuarios | number:0 }}
</span>
</h1>
<h4>Usuarios</h4>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h1>
<span class="label label-success" name="ingresos">
{{ vm.total.ingresos | number:2 }} €
</span>
</h1>
<h4>Total ingresos</h4>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h1>
<span class="label label-danger" name="gastos">
{{ vm.total.gastos | number:2 }} €
</span>
</h1>
<h4>Total gastos</h4>
</div>
</div>
</div>
</section>

Просмотреть файл

@ -0,0 +1,14 @@
(function() {
"use strict";
angular
.module("controlCajaApp")
.controller("DashboardCtrl", function (socketFactory) {
var vm = this;
socketFactory.connect();
socketFactory.on('total_updated', function (msgIn) {
vm.total = msgIn[0];
});
});
}());

Просмотреть файл

@ -0,0 +1,7 @@
(function () {
var maestrosFactory =   function ($resource)  {
return $resource("/api/pub/maestros/",{},{get: {cache: true}});
};
angular.module('controlCajaApp').factory('maestrosFactory', maestrosFactory);
}());

Просмотреть файл

@ -0,0 +1,15 @@
(function () {
var movimientosFactory =   function ($resource)  {
var factory  =   {};
factory.movimientos =  $resource("/api/priv/movimientos/:id", {
id: "@id"
})
factory.total = $resource("/api/priv/total/");
return factory;
};
angular.module('controlCajaApp').factory('movimientosFactory', movimientosFactory);
}());

Просмотреть файл

@ -0,0 +1,74 @@
(function () {
function piePagina() {
return {
restrict: 'AE',
replace: 'true',
template: '<footer class="container"><hr/><p class="text-center">Desarrollado con AngularJS by Google. Por Alberto Basalo - <a href="http://agorabinaria.com">Ágora Binaria SL</a></p></footer>'
};
};
function cabecera() {
return {
restrict: 'AE',
replace: 'true',
transclude: true,
templateUrl: '/static/directivas/tpl-cabecera.html'
};
};
function filaMovimiento() {
return {
restrict: 'A',
templateUrl: '/static/directivas/tpl-fila-movimiento.html',
scope: {
movimientoplantilla: "=movimientodirectiva"
}
};
}
function seleccionado() {
return {
link: function ($scope, element, attrs) {
element.bind('mouseenter', function () {
element.css('background-color', 'lightyellow');
});
element.bind('mouseleave', function () {
element.css('background-color', 'white');
});
}
};
}
function plugin() {
return {
link: function (scope, element, attrs) {
var init = scope.$eval(attrs.ngModel);
var min = scope.$eval(attrs.abSliderMin);
var max = scope.$eval(attrs.abSliderMax);
$(element).plugin({
value: init,
min: min,
max: max,
tooltip: 'hide'
});
scope.$watch(attrs.ngModel, function (valor) {
$(element).plugin('setValue', valor);
});
$(element).plugin().on('slide', function (evento) {
scope.$apply(function () {
scope[attrs.ngModel] = evento.value;
});
});
}
}
}
angular.module('abDirectivas', [])
.directive('abPiePagina', piePagina)
.directive('abCabecera', cabecera)
.directive('abFilaMovimiento', filaMovimiento)
.directive('abSeleccionado', seleccionado)
.directive('abPlugin', plugin);
}());

Просмотреть файл

@ -0,0 +1,6 @@
<div class="well">
<header>
<h2>Cash Flow</h2>
</header>
<div class="lead" ng-transclude></div>
</div>

Просмотреть файл

@ -0,0 +1,10 @@
<tr>
<td class="text-left"><a ui-sref="movimiento({_id:movimientoplantilla._id})">{{movimientoplantilla._id }}</a> </td>
<td class="text-left">{{movimientoplantilla.fecha | date}}</td>
<td class="text-left">{{movimientoplantilla.tipo}}</td>
<td class="text-left">{{movimientoplantilla.categoria}}</td>
<td class="text-left" ng-class="{'text-success': movimientoplantilla.tipo=='Ingreso', 'text-danger' : movimientoplantilla.tipo=='Gasto'}">
{{movimientoplantilla.importe | number:2}} €
</td>
<td class="text-left"><ab-valoracion valor="movimientoplantilla.valoracion" max="10" solo-lectura="true"></ab-valoracion></td>
</tr>

Просмотреть файл

@ -0,0 +1,12 @@
<span>
<span
ng-repeat="estrella in estrellas"
ng-click="marcar($index)" >
<span class="lead"
ng-class=
"{'text-danger h3': estrella.marcada,
'text-muted small': !estrella.marcada}">
<b><u>*</u></b>
</span>
</span>
</span>

Просмотреть файл

@ -0,0 +1,38 @@
(function () {
var valoracion = function () {
return {
restrict: 'AE',
templateUrl: '/static/directivas/valoracion/tpl-valoracion.html',
scope: {
valor: '=',
max: '@',
soloLectura: '@'
},
link: function (scope, elem, attrs) {
function actualizar() {
if(!scope.valor)scope.valor=1;
scope.estrellas = [];
for (var i = 0; i < scope.max; i++) {
var estrella = {
marcada: (i < scope.valor)
};
scope.estrellas.push(estrella);
}
};
scope.marcar = function (indice) {
if (scope.soloLectura && scope.soloLectura === 'true') {
return;
}
scope.valor = indice + 1;
actualizar();
};
actualizar();
}
}
}
angular.module('abDirectivas')
.directive('abValoracion', valoracion);
}());

Просмотреть файл

@ -0,0 +1,71 @@
(function () {
function limpiarCadena() {
var funcionFiltro = function (cadena) {
if (cadena) {
var resultado = cadena.toLowerCase();
var patron = /[^-A-Za-z0-9]+/g;
return resultado.replace(patron, '_');
}
};
return funcionFiltro;
}
function recortar() {
var funcionFiltro = function (cadena, largo, quitarInicio) {
if (!cadena) {
return ''
}
if (!largo) {
largo = 10
}
if (cadena.length <= largo) {
return cadena
}
if (quitarInicio) {
return '...' + cadena.substring(cadena.length - largo)
} else {
return cadena.substring(0, largo) + '...'
}
};
return funcionFiltro;
}
function rellenarVacios() {
var funcionFiltro = function (cadena) {
try {
if (!cadena || cadena === undefined || cadena.trim() === "") {
return '---';
};
} catch (err) {
return '---';
}
return cadena;
}
return funcionFiltro;
}
function granImporte() {
var funcionFiltro = function (movimientos, valorCorte) {
if (valorCorte) {
var filtrados = [];
for (var i = 0; i < movimientos.length; i++) {
var mov = movimientos[i];
if (mov.importe >= valorCorte) {
filtrados.push(mov);
}
}
return filtrados;
} else {
return movimientos;
}
};
return funcionFiltro;
}
angular.module('abFiltros', [])
.filter('abLimpiarCadena', limpiarCadena)
.filter('abRecortar', recortar)
.filter('abRellenarVacios', rellenarVacios)
.filter('abGranImporte', granImporte);
}());

Просмотреть файл

@ -0,0 +1,55 @@
<section name="verMovimiento" class="row-fluid">
<form class="form-horizontal text-left">
<fieldset>
<div id="legend">
<legend class="">Ver el movimiento {{ vm.movimiento.id}} </legend>
</div>
<div class="row-fluid">
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="tipo">Tipo</label>
<div class="controls">
<div class="btn-group">
{{ vm.movimiento.tipo}}
</div>
</div>
</div>
<div class="control-group">
<label class="control-label" for="categ">Categoría</label>
<div class="controls">
{{ vm.movimiento.categoria }}
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="fecha">Fecha</label>
<div class="controls">
{{ vm.movimiento.fecha || date }}
</div>
</div>
<div class="control-group">
<label class="control-label" for="importe">Importe</label>
<div class="controls">
{{ vm.movimiento.importe }}
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="control-group">
<label class="control-label" for="texto">Texto</label>
<div class="controls">
{{ vm.movimiento.texto | abRellenarVacios | abRecortar | abLimpiarCadena }}
</div>
</div>
<div class="control-group">
<label class="control-label" for="valoracion">Valoración</label>
<div class="controls">
<span ab-valoracion valor="caja.nuevoMovimiento.valoracion" max="10" solo-lectura="false"></span>
</div>
</div>
</div>
</div>
</fieldset>
</form>
</section>

Просмотреть файл

@ -0,0 +1,7 @@
(function () {
var movimientoCtrl = function ($stateParams, movimientosFactory) {
var vm = this;
vm.movimiento = movimientosFactory.movimientos.get({id:$stateParams._id});
}
angular.module('controlCajaApp').controller('MovimientoCtrl', movimientoCtrl);
}());

Просмотреть файл

@ -0,0 +1,37 @@
<div class="container">
<div class="row">
<div class="span12">
<form class="form-horizontal">
<fieldset>
<div id="legend">
<legend class="">Login</legend>
</div>
<div class="control-group">
<!-- email -->
<label class="control-label" for="email">Email</label>
<div class="controls">
<input type="email" ng-model="registro.usuario.email" name="email" class="input-xlarge">
</div>
</div>
<div class="control-group">
<!-- Password-->
<label class="control-label" for="password">Password</label>
<div class="controls">
<input type="password" ng-model="registro.usuario.password" name="password" class="input-xlarge">
</div>
</div>
<div class="control-group">
<!-- Button -->
<div class="controls">
<button class="btn btn-primary" ng-click="registro.entrar()">Login</button>
</div>
<!-- Button -->
<div class="controls">
<button class="btn btn-success" ng-click="registro.registrar()" name="registrar">Registro</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,34 @@
(function () {
var registroCtrl = function ($rootScope, $state, $http, $cookieStore, socketFactory) {
var urlBase = "http://localhost:3000/api/";
var vm = this;
vm.usuario = {};
vm.entrar = function () {
$http.post(urlBase + 'sesiones/', vm.usuario)
.success(function (data) {
afterLogIn(data);
});
}
vm.registrar = function () {
$http.post(urlBase + 'usuarios/', vm.usuario)
.success(function (data) {
afterLogIn(data);
});
}
function afterLogIn(data) {
$rootScope.nombre = vm.usuario.email;
$cookieStore.put("sessionId", data);
socketFactory.connect();
socketFactory.on('wellcome', function (msgIn) {
console.log("Me he conectado: " + JSON.stringify(msgIn));
socketFactory.emit("ackClient", "thanks");
});
socketFactory.on('ackServer', function (msgIn) {
console.log("Han recibido mi mensaje: " + JSON.stringify(msgIn));
});
$state.go("total");
}
}
angular.module('controlCajaApp').controller('RegistroCtrl', registroCtrl);
}());

18
26-socket-io/package.json Normal file
Просмотреть файл

@ -0,0 +1,18 @@
{
"name": "ControlCashFlow",
"version": "0.0.0",
"description": "Controla tu flujo de caja",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Alberto Basalo",
"license": "BSD-2-Clause",
"dependencies": {
"body-parser": "~1.0.2",
"express": "~4.12.1",
"mongodb": "^2.0.26",
"q": "^1.2.0",
"socket.io": "^1.3.5"
}
}

14
26-socket-io/server.js Normal file
Просмотреть файл

@ -0,0 +1,14 @@
var app = require('./server/config.js').configApp();
var socket = require('./server/socket.js').initIO(app);
require('./server/seguridad.js').seguridad(app);
console.log('ready');
require('./server/maestros.js').routeMaestros(app);
require('./server/movimientos.js').routeMovimientos(app, socket.emitter);
console.log('steady');
// El encargado de escuchar es el servicio http, en lugar de express
// Esto se hace para soportar las llamadas desde socket.io
socket.server.listen(3000);
console.log('go');

Просмотреть файл

@ -0,0 +1,33 @@
module.exports.configApp = function () {
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(function (peticion, respuesta, siguiente) {
console.log("recibida petición: " + peticion.url);
if (peticion.body && Object.keys(peticion.body).length > 0) {
console.log("body: " + JSON.stringify(peticion.body));
}
siguiente();
});
app.use(bodyParser());
// rewrites para que permita usar rutas sin #
app.get('/*', function (req, res, next) {
if ((req.url.indexOf("static/") >= 0) || (req.url.indexOf("api/") >= 0)) {
next();
} else {
res.sendFile('./index.html', {
root: path.join(__dirname, '../client')
});
}
});
app.use(express.static(__dirname + './../client'));
return app;
}

Просмотреть файл

@ -0,0 +1,89 @@
var Q = require('q');
var mondodb = require('mongodb');
var MongoClient = mondodb.MongoClient;
var mongoUrl = "mongodb://localhost:27017/control_caja";
exports.ObjectId = mondodb.ObjectID;
exports.connecting = connecting;
exports.finding = finding;
exports.inserting = inserting;
exports.updating = updating;
exports.aggregating = aggregating;
function connecting(mongoCol) {
var deferred = Q.defer();
MongoClient.connect(mongoUrl, function (err, db) {
if (err) {
callback2Promise(err, null, deferred);
} else {
callback2Promise(null, db.collection(mongoCol), deferred);
}
});
return deferred.promise;
}
function finding(mongoCol, query) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.find(query).toArray(function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function inserting(mongoCol, document) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.insert(document, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function updating(mongoCol, query, document) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.update(query, document, function (err, result) {
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function aggregating(mongoCol, pipeline) {
var deferred = Q.defer();
connecting(mongoCol)
.then(function (colDb) {
colDb.aggregate(pipeline, function (err, result) {
console.log(JSON.stringify(result));
callback2Promise(err, result, deferred);
});
})
.fail(function (err) {
callback2Promise(err, result, deferred);
});
return deferred.promise;
}
function callback2Promise(err, result, deferred) {
if (err) {
console.error(err);
deferred.reject(err);
} else {
deferred.resolve(result);
}
}

Просмотреть файл

@ -0,0 +1,19 @@
var mongodb = require('./mongodb.js')
var mongoCol = "movimientos"
exports.findingByUsuario = function (usuario) {
return mongodb.finding(mongoCol, {
usuario: usuario
});
}
exports.inserting = function (movimiento) {
return mongodb.inserting(mongoCol, movimiento);
}
exports.findingByIdUsuario = function (_id, usuario) {
return mongodb.finding(mongoCol, {
_id: new mongodb.ObjectId(_id),
usuario: usuario
});
}

Просмотреть файл

@ -0,0 +1,44 @@
var mongodb = require('./mongodb.js')
var mongoCol = "usuarios"
exports.findingByEmail = function (email) {
return mongodb.finding(mongoCol, {
email: email
});
}
exports.findingByEmailPassword = function (email, password) {
return mongodb.finding(mongoCol, {
email: email,
password: password
});
}
exports.inserting = function (usuario) {
return mongodb.inserting(mongoCol, usuario);
}
exports.updating = function (usuario) {
return mongodb.updating(mongoCol, {
email: usuario.email
},
usuario);
}
exports.findingTotal = function () {
return mongodb.aggregating(mongoCol, [{
$group: {
_id: null,
usuarios: {
$sum: 1
},
ingresos: {
$sum: "$total.ingresos"
},
gastos: {
$sum: "$total.gastos"
}
}
}]);
}

Просмотреть файл

@ -0,0 +1,9 @@
module.exports.routeMaestros = function (app) {
app.get('/api/pub/maestros', function (req, res, next) {
var maestros = {
categoriasIngresos: ['Nómina', 'Ventas', 'Intereses Depósitos'],
categoriasGastos: ['Hipotéca', 'Compras', 'Impuestos']
};
res.json(maestros);
});
}

Просмотреть файл

@ -0,0 +1,79 @@
var movimientosData = require('./data/movimientosData.js');
var usuariosData = require('./data/usuariosData.js');
module.exports.routeMovimientos = function (app, emitter) {
app.route('/api/priv/movimientos')
.get(function (req, res, next) {
movimientosData.findingByUsuario(req.usuario)
.then(function (data) {
res.json(data);
})
.fail(function (err) {
res.status(500).send(err);
});
})
.post(function (req, res, next) {
var movimiento = req.body;
movimiento.usuario = req.usuario;
movimientosData.inserting(movimiento)
.then(function (data) {
usuariosData.findingByEmail(req.usuario)
.then(function (data) {
var usuario = data[0];
if (usuario.total === undefined) {
usuario.total = {
ingresos: 0,
gastos: 0
};
}
if (movimiento.tipo == 'Ingreso') {
usuario.total.ingresos += movimiento.importe;
} else {
usuario.total.gastos += movimiento.importe;
}
usuariosData.updating(usuario)
.then(function (data) {
// Emitir mensaje por socket
usuariosData.findingTotal()
.then(function (data) {
emitter("total_updated", data);
})
.fail(function (err) {
console.log(err);
});
res.json(data);
})
.fail(function (err) {
res.status(500).send(err);
});
})
.fail(function (err) {
res.status(500).send(err);
});
})
.fail(function (err) {
res.status(500).send(err);
});
});
app.get('/api/priv/movimientos/:id', function (req, res, next) {
movimientosData.findingByIdUsuario(req.params.id, req.usuario)
.then(function (data) {
res.json(data[0]);
})
.fail(function (err) {
res.status(500).send(err);
});
});
app.get('/api/priv/total', function (req, res, next) {
usuariosData.findingByEmail(req.usuario)
.then(function (data) {
res.json(data[0].total);
})
.fail(function (err) {
res.status(500).send(err);
});
});
}

Просмотреть файл

@ -0,0 +1,84 @@
var usuariosData = require('./data/usuariosData.js')
var sesiones = [];
module.exports.seguridad = function (app) {
app.use('/api/priv/', function (req, res, next) {
var sessionId = req.get('sessionId');
var sesion = getSesion(sessionId);
if (sesion) {
if (esSesionValida(sesion)) {
sesion.timeStamp = new Date();
req.usuario = sesion.email;
next();
} else {
console.log('Sesión caducada:' + JSON.stringify(sesion));
res.status(419).send('Sesión caducada');
}
} else {
res.status(401).send('Credencial inválida');
}
});
// API - REST
// SECURITY
app.route('/api/usuarios')
.post(function (req, res, next) {
var usuario = req.body;
usuariosData.findingByEmail(usuario.email)
.then(function (data) {
if (data[0]) {
console.log('email ya registrado:' + usuario.email);
res.status(409).send('email ' + usuario.email + ' ya registrado');
} else {
console.log('registrando:' + usuario.email);
usuariosData.inserting(usuario)
.then(function (data) {
res.json(newSession(usuario.email));
});
};
})
.fail(function (err) {
res.status(500).send(err);
});
});
app.route('/api/sesiones')
.post(function (req, res, next) {
var usuario = req.body;
usuariosData.findingByEmailPassword(usuario.email, usuario.password)
.then(function (data) {
if (data) {
console.log('aceptado:' + usuario.email);
res.json(newSession(usuario.email));
} else {
console.log('Credencial inválida:' + usuario.email);
res.status(401).send('Credencial inválida');
}
})
.fail(function (err) {
res.status(500).send(err);
});
});
function getSesion(sessionId) {
return sesiones.filter(function (s) {
return s.sessionId == sessionId;
})[0]
}
function esSesionValida(sesion) {
return (new Date() - sesion.timeStamp) < 20 * 60 * 1000;
}
function newSession(email) {
var sessionId = Math.random() * (88888) + 11111;
var timeStamp = new Date();
sesiones.push({
sessionId: sessionId,
email: email,
timeStamp: timeStamp
});
return sessionId;
}
}

Просмотреть файл

@ -0,0 +1,32 @@
var io;
module.exports.initIO = function (app) {
var server = require('http').Server(app);
io = require('socket.io')(server);
function conectar(socket) {
console.log("IN: conectado!!!" + socket.client.conn.remoteAddress);
var saludo = {
serverPid: process.pid,
date: new Date()
};
socket.emit('wellcome', saludo);
socket.on('ackClient', function (data) {
console.log("IN: mensaje: " + data);
socket.emit('ackServer', data);
});
}
io.on("connect", conectar);
// Devolvemos el servidor http que hay bajo express y un puntero a una función para emitir mensajes
return {
server: server,
emitter: emitirCanalMensaje
};
}
function emitirCanalMensaje(canal, mensaje) {
console.log("OUT: " + canal);
io.sockets.emit(canal, mensaje);
}

Двоичные данные
27-e2e/Protractor.pdf Normal file

Двоичный файл не отображается.

21
27-e2e/conf.js Normal file
Просмотреть файл

@ -0,0 +1,21 @@
var HtmlReporter = require('protractor-html-screenshot-reporter');
// configuración de reporter para generar screenshots e informes
var reporter = new HtmlReporter({
baseDirectory: './screenshots',
// takeScreenShotsOnlyForFailedSpecs: true,
docName: 'protractor-report.html'
});
// configuración básica del protractor
exports.config = {
// ruta del web driver
seleniumAddress: 'http://localhost:4444/wd/hub',
// Array de pruebas secuenciales
specs: ['./index/spec.js', './ingreso/spec.js'],
// Agregar el reporte html
onPrepare: function () {
jasmine.getEnv().addReporter(reporter);
}
}

4
27-e2e/index/conf.js Normal file
Просмотреть файл

@ -0,0 +1,4 @@
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['spec.js']
}

27
27-e2e/index/spec.js Normal file
Просмотреть файл

@ -0,0 +1,27 @@
// host que vamos a probar
var host = 'http://localhost:3000/'
// Cada describe es un grupo de pruebas
describe('index elements', function () {
// cada it es una prueba
it('should have a title', function () {
// se pide y espera por una página
browser.get(host);
// condición que se prueba
expect(browser.getTitle()).toEqual('Control de Caja');
});
it('should have navbar', function () {
browser.get(host);
// se usa un api propio para acceder al DOM
var navbar = element(by.name('navbar'));
// comprobaciones de existencia
expect(navbar.isPresent()).toBe(true);
});
it('should have three menu items', function() {
browser.get(host);
// comprobaciones de número
expect(element.all(by.tagName ('li')).count()).toBe(3);
});
});

4
27-e2e/ingreso/conf.js Normal file
Просмотреть файл

@ -0,0 +1,4 @@
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['spec.js']
}

24
27-e2e/ingreso/spec.js Normal file
Просмотреть файл

@ -0,0 +1,24 @@
var host = 'http://localhost:3000/';
describe('ingreso |', function () {
it('should save an ingreso', function () {
browser.get(host);
var menuNuevo = element(by.name('menu-nuevo'));
// Navegación a otra página
menuNuevo.click()
.then(function () {
// función asíncrona
var importe = element(by.name('importe'));
importe.clear();
importe.sendKeys('1000');
element(by.name('guardar')).click();
// comprobar en totales
var menutotal = element(by.name('menu-total'));
menutotal.click()
.then(function () {
var ingresos = element(by.name('ingresos'));
// comprobamos el texto, incluído el formato...
expect(ingresos.getText()).toEqual('1,000.00 €');
});
});
});
});

Просмотреть файл

@ -0,0 +1 @@
{"description":"should have three menu items|index elements","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at [object Object].<anonymous> (C:\\code\\academia\\protractor\\control-caja\\e2e\\index\\spec.js:25:50)\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\jasminewd\\index.js:94:14\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at [object Object].webdriver.promise.ControlFlow.runEventLoop_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1518:8)\n at [object Object].wrapper [as _onTimeout] (timers.js:261:14)\n at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)","screenShotFile":"0046003a-008a-00be-0096-00a20009007d.png"}

Двоичные данные
27-e2e/screenshots/0046003a-008a-00be-0096-00a20009007d.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 45 KiB

Просмотреть файл

@ -0,0 +1 @@
{"description":"should have a title|index elements","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at [object Object].<anonymous> (C:\\code\\academia\\protractor\\control-caja\\e2e\\index\\spec.js:11:30)\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\jasminewd\\index.js:94:14\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at [object Object].webdriver.promise.ControlFlow.runEventLoop_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1518:8)\n at [object Object].wrapper [as _onTimeout] (timers.js:261:14)\n at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)","screenShotFile":"00780088-00db-0011-0032-0015004c00fb.png"}

Двоичные данные
27-e2e/screenshots/00780088-00db-0011-0032-0015004c00fb.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 45 KiB

Просмотреть файл

@ -0,0 +1 @@
{"description":"should have navbar|index elements","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at [object Object].<anonymous> (C:\\code\\academia\\protractor\\control-caja\\e2e\\index\\spec.js:19:30)\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\jasminewd\\index.js:94:14\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at [object Object].webdriver.promise.ControlFlow.runEventLoop_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1518:8)\n at [object Object].wrapper [as _onTimeout] (timers.js:261:14)\n at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)","screenShotFile":"00ec004d-000a-004e-0006-009a000f0043.png"}

Двоичные данные
27-e2e/screenshots/00ec004d-000a-004e-0006-009a000f0043.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 45 KiB

Просмотреть файл

@ -0,0 +1 @@
{"description":"should save an ingreso|ingreso |","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at C:\\code\\academia\\protractor\\control-caja\\e2e\\ingreso\\spec.js:20:52\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\lib\\element.js:770:12\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\goog\\base.js:1582:15\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at notify (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:465:12)\n at notifyAll (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:442:7)\n at resolve (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:420:7)\n at Object.fulfill (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:535:5)\n at notify (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:470:16)","screenShotFile":"00f400bd-0033-00cd-00a8-0011004b0005.png"}

Двоичные данные
27-e2e/screenshots/00f400bd-0033-00cd-00a8-0011004b0005.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 47 KiB

Просмотреть файл

@ -0,0 +1 @@
[{"description":"should have a title|index elements","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at [object Object].<anonymous> (C:\\code\\academia\\protractor\\control-caja\\e2e\\index\\spec.js:11:30)\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\jasminewd\\index.js:94:14\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at [object Object].webdriver.promise.ControlFlow.runEventLoop_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1518:8)\n at [object Object].wrapper [as _onTimeout] (timers.js:261:14)\n at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)","screenShotFile":"00780088-00db-0011-0032-0015004c00fb.png"},{"description":"should have navbar|index elements","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at [object Object].<anonymous> (C:\\code\\academia\\protractor\\control-caja\\e2e\\index\\spec.js:19:30)\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\jasminewd\\index.js:94:14\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at [object Object].webdriver.promise.ControlFlow.runEventLoop_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1518:8)\n at [object Object].wrapper [as _onTimeout] (timers.js:261:14)\n at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)","screenShotFile":"00ec004d-000a-004e-0006-009a000f0043.png"},{"description":"should have three menu items|index elements","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at [object Object].<anonymous> (C:\\code\\academia\\protractor\\control-caja\\e2e\\index\\spec.js:25:50)\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\jasminewd\\index.js:94:14\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at [object Object].webdriver.promise.ControlFlow.runEventLoop_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1518:8)\n at [object Object].wrapper [as _onTimeout] (timers.js:261:14)\n at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)","screenShotFile":"0046003a-008a-00be-0096-00a20009007d.png"},{"description":"should save an ingreso|ingreso |","passed":true,"os":"WIN8_1","browser":{"name":"chrome","version":"41.0.2272.89"},"message":"Passed.","trace":"Error: Failed expectation\n at C:\\code\\academia\\protractor\\control-caja\\e2e\\ingreso\\spec.js:20:52\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\lib\\element.js:770:12\n at C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\goog\\base.js:1582:15\n at [object Object].webdriver.promise.ControlFlow.runInNewFrame_ (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:1654:20)\n at notify (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:465:12)\n at notifyAll (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:442:7)\n at resolve (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:420:7)\n at Object.fulfill (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:535:5)\n at notify (C:\\Users\\Alberto\\AppData\\Roaming\\npm\\node_modules\\protractor\\node_modules\\selenium-webdriver\\lib\\webdriver\\promise.js:470:16)","screenShotFile":"00f400bd-0033-00cd-00a8-0011004b0005.png"}]

Просмотреть файл

@ -0,0 +1 @@
<html><head><meta charset='utf-8'/><title>Generated test report</title><style>body{font-family:Arial};ul,li{margin-left:0;padding-left:0;width:100%;font-weight:bold;}table{width:95%;text-align:left;border-spacing:0;border-collapse: separate;margin-bottom:5px;}li{font-weight:bold;padding-left:5px;list-style:none;}ul table li{font-weight: normal}th,td{padding: 10px;border: 1px solid #000;}td.desc-col{width:400px;}th.desc-col{width: 390px;}td.status-col{width:75px;}th.status-col{width: 75px;}td.browser-col{width:345px;}th.browser-col{width: 345px;}td.os-col{width:100px;}th.os-col{width: 100px;}td.msg-col{width:135px;}th.msg-col{width: 135px;}table.header{background-color: gray; color: #fff;margin-left:20px;}.traceinfo{position: fixed;top: 0; bottom: 0;left: 0;right:0;background: rgba(0,0,0,0.8);z-index: 99999;opacity:0;-webkit-transition: opacity 400ms ease-in;transition: opacity 400ms ease-in;pointer-events: none;}.traceinfo.visible{opacity:1;pointer-events: auto;}.traceinfo > div{width: 400px;position: relative;margin: 10% auto;padding: 5px 20px 13px 20px;background: #fff;}.traceinfo .close{background: #606061;color: #FFFFFF;line-height: 25px;position: absolute;right: -12px;text-align: center;top: -10px;width: 24px;text-decoration: none;font-weight: bold;}.traceinfo .close:hover{background: #00d9ff;}</style><script type='text/javascript'>function showTrace(e){window.event.srcElement.parentElement.getElementsByClassName('traceinfo')[0].className = 'traceinfo visible';}function closeTraceModal(e){window.event.srcElement.parentElement.parentElement.className = 'traceinfo';}function openModal(imageSource){var myWindow = window.open('','screenshotWindow');myWindow.document.write('<img src="' +imageSource + '" alt="screenshot" />');}</script> </head><body><h1>Test Results</h1><table class='header'><tr><th class='desc-col'>Description</th><th class='status-col'>Passed</th><th class='browser-col'>Browser</th><th class='os-col'>OS</th><th class='msg-col'>Message</th><th class='img-col'>Screenshot</th></tr></table><ul><li>index elements<ul><li><table><tr><td class="desc-col">should have a title</td><td class="status-col" style="color:#fff;background-color: green">true</td><td class="browser-col">chrome:41.0.2272.89</td><td class="os-col">WIN8_1</td><td class="msg-col">Passed.</td><td class="img-col"><a href="#" onclick="openModal('00780088-00db-0011-0032-0015004c00fb.png')">View </a></td></tr></table></li><li><table><tr><td class="desc-col">should have navbar</td><td class="status-col" style="color:#fff;background-color: green">true</td><td class="browser-col">chrome:41.0.2272.89</td><td class="os-col">WIN8_1</td><td class="msg-col">Passed.</td><td class="img-col"><a href="#" onclick="openModal('00ec004d-000a-004e-0006-009a000f0043.png')">View </a></td></tr></table></li><li><table><tr><td class="desc-col">should have three menu items</td><td class="status-col" style="color:#fff;background-color: green">true</td><td class="browser-col">chrome:41.0.2272.89</td><td class="os-col">WIN8_1</td><td class="msg-col">Passed.</td><td class="img-col"><a href="#" onclick="openModal('0046003a-008a-00be-0096-00a20009007d.png')">View </a></td></tr></table></li></ul></li><li><ul><li>ingreso <ul><li><table><tr><td class="desc-col">should save an ingreso</td><td class="status-col" style="color:#fff;background-color: green">true</td><td class="browser-col">chrome:41.0.2272.89</td><td class="os-col">WIN8_1</td><td class="msg-col">Passed.</td><td class="img-col"><a href="#" onclick="openModal('00f400bd-0033-00cd-00a8-0011004b0005.png')">View </a></td></tr></table></li></ul></li></ul></li></ul><div><h2><u>Results summary</u></h2><b>Total tests passed</b>: 4 <br/> <b>Total tests failed</b>: 0 <br/> This report generated on Thu Mar 19 2015 10:38:20 GMT+0100 (Hora estándar romance) </div></body></html>