This commit is contained in:
Vicente Falco 2018-01-17 14:58:48 +01:00
commit df03a41a68
61 changed files with 1156 additions and 512 deletions

24
Jenkinsfile vendored
View File

@ -43,11 +43,35 @@ node
stage ("Stopping/Removing Docker")
{
<<<<<<< HEAD
=======
<<<<<<< HEAD
step{
env.TAG = env.BUILD_NUMBER - 1;
sh "docker-compose down --rmi 'all'"
=======
environment {
env.BUILD_TAG = env.BUILD_NUMBER - 1;
>>>>>>> 1acfdf5950d33f4bf972dc2830e05d2f2b1ba99a
}
>>>>>>> e47138eda088c8d307f58d42cc34cbde7295946f
sh "docker-compose down --rmi 'all'"
}
stage ("Generar dockers")
{
<<<<<<< HEAD
=======
<<<<<<< HEAD
step{
env.TAG = env.BUILD_NUMBER + 1;
sh "docker-compose up -d --build"
=======
environment {
env.BUILD_TAG = env.BUILD_NUMBER;
>>>>>>> 1acfdf5950d33f4bf972dc2830e05d2f2b1ba99a
}
>>>>>>> e47138eda088c8d307f58d42cc34cbde7295946f
sh "docker-compose up -d --build"
}
}

View File

@ -1,53 +1,67 @@
# Project Title
One Paragraph of project description goes here
Salix is an Enterprise resource planning (ERP) integrated management of core business processes, in real-time and mediated by software and technology developed with the stack listed below.
Salix is also the scientific name of a beautifull tree! :)
### Prerequisites
For testing purposes you will need to install globally the following items:
npm install -g karma
npm install -g karma-cli
## Getting Started // ### Installing
Pull from repo.
install nodejs v6 or above.
You will need to install globally the following items:
$ npm install -g karma
$ npm install -g karma-cli
$ npm install -g gulp
$ npm install -g webpack
$ npm install -g nodemon
install nginx globally.
Ask a senior dev for the datasources.development.json files required to run the project.
## Getting Started // ### Installing
Pull from repository.
install nodejs v6.
Ask a senior developer for the datasources.development.json files required to run the project.
on root run:
npm install
gulp install
$ npm install
$ gulp install
lauching nginx:
./dev.sh
$ ./dev.sh
launching frontend:
gulp client
$ gulp client
or start nginx before client on sequence
$ gulp clientDev
launching backend:
gulp services
$ gulp services
or start the local database before services on sequence
$ gulp serivcesDev
Manually reset local fixtures:
$ gulp docker
to check docker images and containers status:
$ docker images
$ docker ps -a
## Running the tests
for client-side unit tests run from project's root:
karma start
$ karma start
for server-side unit tests run from project's root:
npm run testWatch or test for single run
$ npm run test
### Break down into end to end tests
on root run:
Run local database plus e2e paths:
$ gulp e2e
gulp docker
wait 10 secs for db to be ready
npm run e2e
Just the e2e paths as the fixtures are untainted:
$ npm run e2e
## Built With

View File

@ -5,11 +5,11 @@
</vn-auto>
<vn-auto pad-medium>
<vn-vertical>
<vn-horizontal ng-repeat="(field, title) in $ctrl.fieldsToShow" ng-if="$ctrl.client[field]">
<vn-horizontal ng-repeat="(field, title) in $ctrl.fieldsToShow">
<strong vn-auto>{{::title}}:</strong>
<vn-auto margin-small-left>
<span ng-if="field.includes('credit')">{{$ctrl.client[field] | number:2}} €</span>
<span ng-if="!field.includes('credit')">{{::$ctrl.client[field]}}</span>
<span ng-if="field.includes('credit')">{{$ctrl.client[field] || 0 | number:2}} €</span>
<span ng-if="!field.includes('credit')">{{$ctrl.client[field]}}</span>
</vn-auto>
</vn-horizontal>
</vn-vertical>

View File

@ -5,13 +5,19 @@
form="form"
save="post">
</vn-watcher>
<form pad-medium ng-submit="$ctrl.onSubmit()">
<form pad-medium name="form" ng-submit="$ctrl.onSubmit()">
<vn-card>
<vn-vertical pad-medium>
<vn-title vn-one margin-large-bottom>Add Greuge</vn-title>
<vn-horizontal>
<vn-textfield vn-one margin-medium-right label="Importe" field="$ctrl.greuge.amount" type="number" vn-focus></vn-textfield>
<vn-date-picker vn-one label="Date" model="$ctrl.greuge.shipped"></vn-date-picker>
<vn-textfield vn-one margin-medium-right label="Amount" field="$ctrl.greuge.amount" type="number" step="1" vn-focus></vn-textfield>
<vn-date-picker vn-one
label="Date"
model="$ctrl.greuge.shipped"
ini-options="{enableTime: true, dateFormat: 'd-m-Y h:i', time_24hr: true}"
>
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one margin-medium-right label="Comment" field="$ctrl.greuge.description"></vn-textfield>

View File

@ -5,7 +5,7 @@ class ClientGreugeCreate {
this.$ = $scope;
this.$state = $state;
this.greuge = {
shipped: $filter('date')(new Date(), 'yyyy-MM-dd')
shipped: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
};
}
onSubmit() {

View File

@ -0,0 +1,39 @@
import './greuge-create.js';
describe('Client', () => {
describe('Component vnClientGreugeCreate', () => {
let $componentController;
let $scope;
let $state;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = {
submit: () => {
return {
then: callback => {
callback();
}
};
}
};
controller = $componentController('vnClientGreugeCreate', {$scope: $scope});
}));
describe('onSubmit()', () => {
it('should call the function go() on $state to go to the greuges list', () => {
spyOn($state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('clientCard.greuge.list');
});
});
});
});

View File

@ -20,5 +20,6 @@
"Credit" : "Crédito",
"Secured credit": "Crédito asegurado",
"Verified data": "Datos comprobados",
"Mandate": "Mandato"
"Mandate": "Mandato",
"Amount": "Importe"
}

View File

@ -1,5 +1,11 @@
<vn-vertical ng-click="$ctrl.showDropDown = true">
<vn-textfield vn-auto label="{{$ctrl.label}}" model="$ctrl.displayValue" readonly="$ctrl.readonly"></vn-textfield>
<vn-vertical ng-click="$ctrl.showDropDown = true" tabindex="0">
<vn-textfield vn-auto
label="{{$ctrl.label}}"
model="$ctrl.displayValue"
readonly="$ctrl.readonly"
tab-index="-1"
>
</vn-textfield>
<vn-drop-down vn-auto
items="$ctrl.items"
show="$ctrl.showDropDown"

View File

@ -25,6 +25,9 @@ ul.vn-autocomplete {
}
vn-autocomplete {
position: relative;
vn-vertical {
outline:none;
}
.mdl-chip__action {
position: absolute;

View File

@ -30,6 +30,7 @@ class DatePicker extends Component {
this._modelView = null;
this._model = undefined;
this._optionsChecked = false;
this._waitingInit = 0;
this.hasFocus = false;
this.hasMouseIn = false;
componentHandler.upgradeElement($element[0].firstChild);
@ -39,12 +40,34 @@ class DatePicker extends Component {
return this._model;
}
set model(value) {
if (this._optionsChecked) {
this._waitingInit = 0;
this._model = value;
if (value && !this.modelView) {
let options = this._getOptions();
let initialDateFormat = (options && options.dateFormat) ? options.dateFormat : 'Y-m-d';
let initialDateFormat;
if (options && options.dateFormat) {
initialDateFormat = options.dateFormat;
} else {
initialDateFormat = this.$translate.use() === 'es' ? 'd-m-Y' : 'Y-m-d';
if (options.enableTime) {
initialDateFormat += ' H:i:s';
}
}
let format = this._formatFlat2Angular(initialDateFormat);
this.modelView = this.$filter('date')(value, format);
this._modelView = this.$filter('date')(new Date(this._model), format);
this.mdlUpdate();
}
} else if (this._waitingInit < 4) {
this._waitingInit++;
this.$timeout(() => {
this.model = value;
}, 250);
} else {
this.model = null;
this.modelView = '';
this._waitingInit = 0;
}
}
get modelView() {
@ -54,10 +77,9 @@ class DatePicker extends Component {
this._modelView = value;
this.input.value = value;
this._setModel(value);
this.$timeout(() => {
this.mdlUpdate();
}, 500);
}
onClear() {
this.modelView = null;
}
@ -67,9 +89,11 @@ class DatePicker extends Component {
}
}
mdlUpdate() {
this.$timeout(() => {
let mdlField = this.element.firstChild.MaterialTextfield;
if (mdlField)
mdlField.updateClasses_();
}, 500);
}
_formatFlat2Angular(string) { // change string Flatpickr format to angular format (d-m-Y -> dd-MM-yyyy)
@ -90,13 +114,7 @@ class DatePicker extends Component {
return parts.join('-');
}
_setModel(value) {
let model;
if (!value) {
model = undefined;
} else if (!this.iniOptions || (this.iniOptions.dateFormat && this.iniOptions.dateFormat.startsWith('Y-m-d'))) {
model = value;
} else {
_string2BackFormat(value) {
let formats = this.iniOptions.dateFormat.split(/[ZT.,/ :-]/);
let aux = value.split(/[ZT.,/ :-]/);
let date = {};
@ -137,7 +155,18 @@ class DatePicker extends Component {
hourStr += ':00';
}
}
model = `${dateStr} ${hourStr}`.trim();
return `${dateStr} ${hourStr}`.trim();
}
_setModel(value) {
let model;
let options = this._getOptions();
if (!value) {
model = undefined;
} else if (!options || (options.dateFormat && options.dateFormat.startsWith('Y-m-d'))) {
model = value;
} else {
model = this._string2BackFormat(value);
}
if (this.model !== model) {
@ -155,8 +184,8 @@ class DatePicker extends Component {
if (!this.iniOptions.locale)
this.iniOptions.locale = this.$translate.use();
if (!this.iniOptions.dateFormat && this.iniOptions.locale === 'es')
this.iniOptions.dateFormat = 'd-m-Y';
if (!this.iniOptions.dateFormat)
this.iniOptions.dateFormat = this.iniOptions.locale === 'es' ? 'd-m-Y' : 'Y-m-d';
else if (this.iniOptions.dateFormat) {
let format = this.iniOptions.dateFormat.split(/[ZT.,/ :-]/);
if (format.length <= 1) {
@ -193,6 +222,12 @@ class DatePicker extends Component {
this.vp = undefined;
}
$onChanges(objChange) {
if (objChange.iniOptions && objChange.iniOptions.currentValue) {
this.iniOptions = Object.assign(this.iniOptions, objChange.iniOptions.currentValue);
}
}
$onInit() {
this.initPicker();
}

View File

@ -38,6 +38,7 @@ describe('Component vnDatePicker', () => {
it(`should split the given string into parts`, () => {
controller.iniOptions = {dateFormat: 'd/m/Y'};
controller._optionsChecked = true;
controller.model = '2017-12-23';
expect(controller.modelView).toBe('23-12-2017');

View File

@ -29,7 +29,6 @@ export function directive(interpolate, compile, $window) {
throw new Error(`vnValidation: Entity '${entityName}' doesn't exist`);
let validations = entity.validations[fieldName];
if (!validations || validations.length == 0)
return;

View File

@ -7,7 +7,7 @@
</vn-auto>
<vn-auto>
<ul class="dropdown">
<li tabIndex="-1"
<li
ng-repeat="item in $ctrl.itemsFiltered track by $index"
ng-click="$ctrl.selectItem(item)"
ng-class="{'active': $index === $ctrl.activeOption, 'checked': item.checked}"
@ -19,7 +19,6 @@
<li
ng-if="$ctrl.loadMore&&!$ctrl.removeLoadMore"
class="dropdown__loadMore"
tabIndex="-1"
ng-class="{'active': $ctrl.itemsFiltered.length === $ctrl.activeOption, 'noMore' : !$ctrl.showLoadMore}"
ng-click="$ctrl.loadItems()"
translate="{{$ctrl.showLoadMore ? 'Show More' : 'No more results'}}"

View File

@ -1,5 +1,6 @@
import {module} from '../module';
import './style.scss';
import validKey from '../lib/keyCodes';
export default class DropDown {
constructor($element, $filter, $timeout) {
@ -29,6 +30,9 @@ export default class DropDown {
this.$timeout(() => {
this._tryToShow++;
this.show = true;
if (this.activeOption === -1) {
this.activeOption = 0;
}
}, 250);
} else {
this._tryToShow = 0;
@ -70,26 +74,27 @@ export default class DropDown {
}
_toggleDropDown(value, oldValue) {
if (value && !this._focusingFilter && !oldValue && this.filter) {
// open dropDown
this.$timeout(() => { // wait angular ngIf
this._setFocusInFilterInput();
this._eventScroll(value);
});
} else if (!value && oldValue) {
// close dropDown
this._eventScroll(value);
}
this.$timeout(() => {
this._eventScroll(value);
this._calculatePosition(value, oldValue);
});
}
_eventScroll(add) {
_eventScroll(add, num) {
let count = num || 0;
if (add) {
if (this.container) {
this.container.addEventListener('scroll', e => this.loadFromScroll(e));
} else {
// this.$timeout(() => { // falla al entrar por primera vez xq pierde el foco y cierra el dropdown
// this._setFocusInFilterInput();
// });
} else if (count < 4) {
count++;
this.$timeout(() => { // wait angular ngIf
this._eventScroll(add, count);
}, 250);
}
} else if (this.container) {
this.container.removeEventListener('scroll', e => this.loadFromScroll(e));
}
}
@ -169,43 +174,43 @@ export default class DropDown {
onKeydown(event) {
if (this.show) {
switch (event.keyCode) {
case 13: // Enter
if (event.keyCode === 13) { // Enter
this.$timeout(() => {
this.selectOption();
});
event.preventDefault();
break;
case 27: // Escape
} else if (event.keyCode === 27) { // Escape
this.clearSearch();
break;
case 38: // Arrow up
} else if (event.keyCode === 38) { // Arrow up
this.activeOption--;
this.$timeout(() => {
this.setScrollPosition();
}, 100);
break;
case 40: // Arrow down
} else if (event.keyCode === 40) { // Arrow down
this.activeOption++;
this.$timeout(() => {
this.setScrollPosition();
}, 100);
break;
case 35: // End
} else if (event.keyCode === 35) { // End
this.activeOption = this.itemsFiltered.length - 1;
this.$timeout(() => {
this.setScrollPosition();
}, 100);
break;
case 36: // Start
} else if (event.keyCode === 36) { // Start
this.activeOption = 0;
this.$timeout(() => {
this.setScrollPosition();
}, 100);
break;
default:
return;
} else if (this.filter) {
let oldValue = this.search || '';
if (validKey(event)) {
this.search = oldValue + String.fromCharCode(event.keyCode);
} else if (event.keyCode === 8) { // backSpace
this.search = oldValue.slice(0, -1);
}
} /* else {
console.error(`Error: keyCode ${event.keyCode} not supported`);
} */
}
}
setScrollPosition() {

View File

@ -1,7 +1,7 @@
import {module} from '../module';
const isFullEmpty = item => {
return (!item && item !== 0) || (typeof item === 'object' && !Object.keys(item).length);
return (item === null || item === undefined) || (typeof item === 'object' && !Object.keys(item).length);
};
export default isFullEmpty;

View File

@ -14,3 +14,4 @@ export {NAME as INTERPOLATE, Interpolate} from './interpolate';
export {NAME as COPY_OBJECT} from './copy';
export {NAME as EQUALS_OBJECT} from './equals';
export {NAME as GET_DATA_MODIFIED, factory as Modified} from './modified';
export {NAME as VALID_KEY} from './keyCodes';

View File

@ -0,0 +1,19 @@
import {module} from '../module';
const validKey = key => {
let keycode = key.keyCode || key;
let valid =
(keycode > 47 && keycode < 58) || // number keys
(keycode > 64 && keycode < 91) || // letter keys
(keycode > 95 && keycode < 112) || // numpad keys
(keycode > 185 && keycode < 193) || // ;=,-./` (in order)
(keycode > 218 && keycode < 223); // [\]' (in order)
return valid;
};
export default validKey;
export const NAME = 'validKey';
module.value(NAME, validKey);

View File

@ -2,7 +2,7 @@ import {validator} from 'vendor';
export const validators = {
presence: value => {
if (validator.isEmpty(value))
if (validator.isEmpty(value ? String(value) : ''))
throw new Error(`Value can't be empty`);
},
absence: value => {
@ -14,8 +14,8 @@ export const validators = {
min: conf.min || conf.is,
max: conf.max || conf.is
};
if (!validator.isLength(value, options)) {
let val = value ? String(value) : '';
if (!validator.isLength(val, options)) {
if (conf.is) {
throw new Error(`Value should be ${conf.is} characters long`);
} else if (conf.min && conf.max) {
@ -74,7 +74,7 @@ export function validate(value, conf) {
try {
checkNull(value, conf);
if (validator && value != null)
if (validator) // && value != null ??
validator(value, conf);
} catch (e) {
let message = conf.message ? conf.message : e.message;

View File

@ -1,8 +1,5 @@
<div
class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label"
tabindex="1"
ng-focus="$ctrl.hasFocus = true"
ng-blur="$ctrl.hasFocus = false"
ng-mouseenter="$ctrl.hasMouseIn = true"
ng-mouseleave="$ctrl.hasMouseIn = false">
<input
@ -12,7 +9,10 @@
ng-model="$ctrl.value"
vn-validation="{{$ctrl.rule}}"
ng-disabled="$ctrl.disabled"
ng-readonly="$ctrl.readonly"/>
ng-readonly="$ctrl.readonly"
ng-focus="$ctrl.hasFocus = true"
ng-blur="$ctrl.hasFocus = false"
/>
<div class="mdl-chip__action">
<i class="material-icons"
ng-if="$ctrl.hasInfo"

View File

@ -32,6 +32,9 @@ export default class Textfield extends Input {
this.hasValue = Boolean(this._value);
this.mdlUpdate();
}
set tabIndex(value) {
this.input.tabIndex = value;
}
mdlUpdate() {
let mdlField = this.$element[0].firstChild.MaterialTextfield;
if (mdlField)
@ -54,6 +57,7 @@ module.component('vnTextfield', {
disabled: '<?',
readonly: '<?',
rule: '@?',
type: '@?'
type: '@?',
tabIndex: '@?'
}
});

View File

@ -12,12 +12,13 @@ import isFullEmpty from '../lib/fullEmpty';
* properties are provided.
*/
export default class Watcher extends Component {
constructor($element, $scope, $state, $transitions, $http, vnApp, $translate) {
constructor($element, $scope, $state, $transitions, $http, vnApp, $translate, $attrs) {
super($element);
this.$scope = $scope;
this.$state = $state;
this.$http = $http;
this.$translate = $translate;
this.$attrs = $attrs;
this.vnApp = vnApp;
this.state = null;
@ -93,10 +94,10 @@ export default class Watcher extends Component {
(resolve, reject) => this.noChanges(reject)
);
}
let changedData = getModifiedData(this.data, this.orgData);
let changedData = (this.$attrs.save && this.$attrs.save.toLowerCase() === 'post') ? this.copyInNewObject(this.data) : getModifiedData(this.data, this.orgData);
if (this.save) {
this.save.model = this.copyInNewObject(changedData);
this.save.model = changedData; // this.copyInNewObject(changedData);
return new Promise((resolve, reject) => {
this.save.accept().then(
json => this.writeData({data: json}, resolve),
@ -180,6 +181,7 @@ export default class Watcher extends Component {
}
dataChanged() {
if (this.form && !this.form.$dirty) return false;
let newData = this.copyInNewObject(this.data);
return !isEqual(newData, this.orgData);
}
@ -194,7 +196,7 @@ export default class Watcher extends Component {
}
}
Watcher.$inject = ['$element', '$scope', '$state', '$transitions', '$http', 'vnApp', '$translate'];
Watcher.$inject = ['$element', '$scope', '$state', '$transitions', '$http', 'vnApp', '$translate', '$attrs'];
module.component('vnWatcher', {
template: require('./watcher.html'),

View File

@ -11,6 +11,7 @@ describe('Component vnWatcher', () => {
let vnApp;
let $translate;
let controller;
let $attrs;
beforeEach(() => {
angular.mock.module('client');
@ -25,7 +26,10 @@ describe('Component vnWatcher', () => {
$transitions = _$transitions_;
$httpBackend = _$httpBackend_;
$translate = _$translate_;
controller = $componentController('vnWatcher', {$scope, $element, $state, vnApp, $transitions, $httpBackend, $translate});
$attrs = {
save: "patch"
};
controller = $componentController('vnWatcher', {$scope, $element, $state, vnApp, $transitions, $httpBackend, $translate, $attrs});
}));
describe('$onInit()', () => {

View File

@ -2,6 +2,7 @@
"module": "item",
"name": "Items",
"icon": "/static/images/icon_item.png",
"validations" : true,
"routes": [
{
"url": "/item",

View File

@ -9,16 +9,9 @@
<div style="max-width: 70em; margin: 0 auto;">
<vn-card>
<vn-vertical pad-large>
<vn-title>Create item</vn-title>
<vn-horizontal>
<vn-textfield vn-one label="Size" field="$ctrl.item.size" vn-focus></vn-textfield>
<vn-textfield vn-five label="Category" field="$ctrl.item.category"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Stems" field="$ctrl.item.stems"></vn-textfield>
<vn-textfield vn-five label="Description" field="$ctrl.item.description"></vn-textfield>
</vn-horizontal>
<vn-title>New item</vn-title>
<vn-horizontal>
<vn-textfield vn-one label="Name" field="$ctrl.item.name" vn-focus></vn-textfield>
<vn-autocomplete vn-one
url="/item/api/ItemTypes"
label="Type"
@ -27,15 +20,21 @@
field="$ctrl.item.typeFk"
>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
url="/item/api/Inks"
label="Ink"
show-field="name"
url="/item/api/Intrastats"
label="Intrastat"
show-field="description"
value-field="id"
field="$ctrl.item.inkFk"
field="$ctrl.item.intrastatFk"
order="description ASC"
filter-search="{where: {description: {regexp: 'search'}} }"
>
<tpl-item>{{$parent.$parent.item.description}}</tpl-item>
</vn-autocomplete>
<vn-textfield vn-one label="Relevancy" field="$ctrl.item.relevancy" type="number"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
@ -45,18 +44,10 @@
show-field="name"
value-field="id"
field="$ctrl.item.originFk"
>
</vn-autocomplete>
<vn-autocomplete vn-one
url="/item/api/Producers"
label="Producer"
show-field="name"
value-field="id"
field="$ctrl.item.producerFk"
>
</vn-autocomplete>
></vn-autocomplete>
<vn-one></vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>

View File

@ -1,16 +1,21 @@
import ngModule from '../module';
class ItemCreate {
constructor() {
this.item = {};
constructor($scope, $state) {
this.$ = $scope;
this.$state = $state;
this.item = {
relevancy: 0
};
}
onSubmit() {
this.$.watcher.submit().then(
json => this.$state.go('item.card.basic', {id: json.data.id})
json => this.$state.go('item.card.data', {id: json.data.id})
);
}
}
ItemCreate.$inject = ['$scope', '$state'];
ngModule.component('vnItemCreate', {
template: require('./item-create.html'),

View File

@ -4,5 +4,8 @@
"Category": "Categoría",
"Description": "Descripción",
"Size": "Tamaño",
"Type": "Tipo"
"Type": "Tipo",
"Name": "Nombre",
"Relevancy": "Relevancia",
"New item": "Nuevo artículo"
}

View File

@ -1,14 +1,14 @@
/* eslint no-console: 0 */
import Nightmare from 'nightmare';
export default function createNightmare(width = 1100, height = 600) {
const nightmare = new Nightmare({show: true, typeInterval: 10}).viewport(width, height);
export default function createNightmare(width = 1280, height = 720) {
const nightmare = new Nightmare({show: true, typeInterval: 10, x: 0, y: 0}).viewport(width, height);
nightmare.on('page', function(type, message, error) {
nightmare.on('page', (type, message, error) => {
fail(error);
});
nightmare.on('console', function(type, message) {
nightmare.on('console', (type, message) => {
if (type === 'error') {
fail(message);
}

View File

@ -78,6 +78,9 @@ export default {
IBANInput: `${components.vnTextfield}[name="iban"]`,
dueDayInput: `${components.vnTextfield}[name="dueDay"]`,
cancelNotificationButton: 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > vn-client-billing-data > vn-dialog > div > form > div.button-bar > tpl-buttons > button:nth-child(1)',
receivedCoreVNHCheckbox: `${components.vnCheck}[label='Received core VNH'] > label > input`,
receivedCoreVNLCheckbox: `${components.vnCheck}[label='Received core VNL'] > label > input`,
receivedB2BVNLCheckbox: `${components.vnCheck}[label='Received B2B VNL'] > label > input`,
saveButton: `${components.vnSubmit}`
},
addresses: {
@ -121,5 +124,19 @@ export default {
creditInput: `${components.vnTextfield}[name="credit"]`,
saveButton: `${components.vnSubmit}`,
firstCreditText: 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > ui-view > vn-client-credit-list > vn-card > div > vn-vertical > vn-one > vn-horizontal:nth-child(1) > vn-one'
},
greuge: {
greugeButton: `${components.vnMenuItem}[ui-sref="clientCard.greuge.list"]`,
addGreugeFloatButton: `${components.vnFloatButton}`,
amountInput: `${components.vnTextfield}[name="amount"]`,
descriptionInput: `${components.vnTextfield}[name="description"]`,
typeInput: `${components.vnAutocomplete}[field="$ctrl.greuge.greugeTypeFk"] > vn-vertical > ${components.vnTextfield}`,
typeSecondOption: `${components.vnAutocomplete}[field="$ctrl.greuge.greugeTypeFk"] > vn-vertical > vn-drop-down > vn-vertical > vn-auto:nth-child(2) > ul > li`,
saveButton: `${components.vnSubmit}`,
firstGreugeText: 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > ui-view > vn-client-greuge-list > vn-card > div > vn-vertical > vn-one > vn-horizontal'
},
mandate: {
mandateButton: `${components.vnMenuItem}[ui-sref="clientCard.mandate"]`,
firstMandateText: 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > vn-client-mandate > vn-card > div > vn-vertical > vn-one > vn-horizontal'
}
};

View File

@ -86,7 +86,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toEqual('No changes to save');
expect(result).toEqual('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -100,7 +100,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -115,7 +115,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -130,7 +130,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -145,7 +145,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -175,7 +175,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -190,7 +190,7 @@ describe('create client path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));

View File

@ -427,7 +427,7 @@ describe('Edit fiscalData path', () => {
.catch(catchErrors(done));
});
it('should check the invoice by address checkbox', done => {
it('should uncheck the invoice by address checkbox', done => {
nightmare
.waitToClick(selectors.fiscalData.invoiceByAddressCheckboxInput)
.waitToClick(selectors.fiscalData.saveButton)
@ -440,7 +440,7 @@ describe('Edit fiscalData path', () => {
.catch(catchErrors(done));
});
it('should confirm invoice by address checkbox is checked', done => {
it('should confirm invoice by address checkbox is unchecked', done => {
nightmare
.waitForSnackbarReset()
.waitToClick(selectors.basicData.basicDataButton)
@ -451,7 +451,7 @@ describe('Edit fiscalData path', () => {
return document.querySelector(selector).checked;
}, selectors.fiscalData.invoiceByAddressCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
expect(value).toBeFalsy();
done();
})
.catch(catchErrors(done));
@ -470,7 +470,7 @@ describe('Edit fiscalData path', () => {
.catch(catchErrors(done));
});
it('should confirm Verified data checkbox is checked', done => {
it('should confirm Verified data checkbox is unchecked', done => {
nightmare
.waitForSnackbarReset()
.waitToClick(selectors.basicData.basicDataButton)
@ -481,7 +481,7 @@ describe('Edit fiscalData path', () => {
return document.querySelector(selector).checked;
}, selectors.fiscalData.verifiedDataCheckboxInput)
.then(value => {
expect(value).toBeTruthy();
expect(value).toBeFalsy();
done();
})
.catch(catchErrors(done));

View File

@ -160,35 +160,123 @@ describe('Edit pay method path', () => {
.catch(catchErrors(done));
});
// BUG IBAN validation seems to break down whenever the due day is edited plus due day shouldnt accep values above 31.
// it(`should edit the due day`, done => {
// nightmare
// .clearInput(selectors.payMethod.dueDayInput)
// .type(selectors.payMethod.dueDayInput, '25')
// .waitToClick(selectors.payMethod.saveButton)
// .waitToClick(selectors.payMethod.cancelNotificationButton)
// .wait(selectors.globalItems.snackbarIsActive)
// .getInnerText(selectors.globalItems.snackbarIsActive)
// .then(result => {
// expect(result).toEqual('Data saved!');
// done();
// })
// .catch(catchErrors(done));
// });
// it('should confirm the due day have been edited', done => {
// nightmare
// .waitForSnackbarReset()
// .waitToClick(selectors.basicData.basicDataButton)
// .wait(selectors.basicData.nameInput)
// .waitToClick(selectors.payMethod.payMethodButton)
// .wait(selectors.payMethod.dueDayInput)
// .getInputValue(selectors.payMethod.dueDayInput)
// .then(result => {
// expect(result).toEqual('25');
// done();
// })
// .catch(catchErrors(done));
// });
it(`should edit the due day`, done => {
nightmare
.clearInput(selectors.payMethod.dueDayInput)
.type(selectors.payMethod.dueDayInput, '60')
.waitToClick(selectors.payMethod.saveButton)
.waitToClick(selectors.payMethod.cancelNotificationButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toEqual('Data saved!');
done();
})
.catch(catchErrors(done));
});
it('should confirm the due day have been edited', done => {
nightmare
.waitForSnackbarReset()
.waitToClick(selectors.basicData.basicDataButton)
.wait(selectors.basicData.nameInput)
.waitToClick(selectors.payMethod.payMethodButton)
.wait(selectors.payMethod.dueDayInput)
.getInputValue(selectors.payMethod.dueDayInput)
.then(result => {
expect(result).toEqual('60');
done();
})
.catch(catchErrors(done));
});
it('should uncheck the Received core VNH checkbox', done => {
nightmare
.waitToClick(selectors.payMethod.receivedCoreVNHCheckbox)
.waitToClick(selectors.payMethod.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toEqual('Data saved!');
done();
})
.catch(catchErrors(done));
});
it('should confirm Received core VNH checkbox is unchecked', done => {
nightmare
.waitForSnackbarReset()
.waitToClick(selectors.basicData.basicDataButton)
.wait(selectors.basicData.nameInput)
.waitToClick(selectors.payMethod.payMethodButton)
.wait(selectors.payMethod.receivedCoreVNHCheckbox)
.evaluate(selector => {
return document.querySelector(selector).checked;
}, selectors.payMethod.receivedCoreVNHCheckbox)
.then(value => {
expect(value).toBeFalsy();
done();
})
.catch(catchErrors(done));
});
it('should uncheck the Received core VNL checkbox', done => {
nightmare
.waitToClick(selectors.payMethod.receivedCoreVNLCheckbox)
.waitToClick(selectors.payMethod.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toEqual('Data saved!');
done();
})
.catch(catchErrors(done));
});
it('should confirm Received core VNL checkbox is unchecked', done => {
nightmare
.waitForSnackbarReset()
.waitToClick(selectors.basicData.basicDataButton)
.wait(selectors.basicData.nameInput)
.waitToClick(selectors.payMethod.payMethodButton)
.wait(selectors.payMethod.receivedCoreVNLCheckbox)
.evaluate(selector => {
return document.querySelector(selector).checked;
}, selectors.payMethod.receivedCoreVNLCheckbox)
.then(value => {
expect(value).toBeFalsy();
done();
})
.catch(catchErrors(done));
});
it('should uncheck the Received B2B VNL checkbox', done => {
nightmare
.waitToClick(selectors.payMethod.receivedB2BVNLCheckbox)
.waitToClick(selectors.payMethod.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toEqual('Data saved!');
done();
})
.catch(catchErrors(done));
});
it('should confirm Received B2B VNL checkbox is unchecked', done => {
nightmare
.waitForSnackbarReset()
.waitToClick(selectors.basicData.basicDataButton)
.wait(selectors.basicData.nameInput)
.waitToClick(selectors.payMethod.payMethodButton)
.wait(selectors.payMethod.receivedB2BVNLCheckbox)
.evaluate(selector => {
return document.querySelector(selector).checked;
}, selectors.payMethod.receivedB2BVNLCheckbox)
.then(value => {
expect(value).toBeFalsy();
done();
})
.catch(catchErrors(done));
});
});

View File

@ -103,7 +103,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -117,7 +117,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -132,7 +132,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -147,7 +147,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -162,7 +162,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -178,7 +178,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -193,7 +193,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -207,7 +207,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
@ -222,7 +222,7 @@ describe('Edit addresses path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Error');
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));

View File

@ -2,13 +2,12 @@ import config from '../helpers/config.js';
import createNightmare from '../helpers/nightmare';
import selectors from '../helpers/selectors.js';
import {catchErrors} from '../../services/utils/jasmineHelpers';
import { fail } from 'assert';
const nightmare = createNightmare();
const moduleAccessViewHashURL = '#!/';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
fdescribe('Add credit path', () => {
describe('Add credit path', () => {
describe('warm up', () => {
it('should warm up login and fixtures', done => {
nightmare
@ -104,7 +103,7 @@ fdescribe('Add credit path', () => {
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toEqual('Data saved');
expect(result).toEqual('Data saved!');
done();
})
.catch(catchErrors(done));

View File

@ -0,0 +1,180 @@
import config from '../helpers/config.js';
import createNightmare from '../helpers/nightmare';
import selectors from '../helpers/selectors.js';
import {catchErrors} from '../../services/utils/jasmineHelpers';
const nightmare = createNightmare();
const moduleAccessViewHashURL = '#!/';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
describe('Add greuge path', () => {
describe('warm up', () => {
it('should warm up login and fixtures', done => {
nightmare
.login()
.waitForURL(moduleAccessViewHashURL)
.waitToClick(selectors.globalItems.logOutButton)
.then(() => {
done();
})
.catch(catchErrors(done));
});
});
it('should log in', done => {
nightmare
.login()
.waitForURL(moduleAccessViewHashURL)
.url()
.then(url => {
expect(url).toEqual(config.url + moduleAccessViewHashURL);
done();
})
.catch(catchErrors(done));
});
it('should make sure the language is English', done => {
nightmare
.changeLanguageToEnglish()
.then(() => {
done();
})
.catch(catchErrors(done));
});
it('should click on the Clients button of the top bar menu', done => {
nightmare
.waitToClick(selectors.globalItems.applicationsMenuButton)
.wait(selectors.globalItems.applicationsMenuVisible)
.waitToClick(selectors.globalItems.clientsButton)
.wait(selectors.clientsIndex.createClientButton)
.url()
.then(url => {
expect(url).toEqual(config.url + '#!/clients');
done();
})
.catch(catchErrors(done));
});
it('should search for the user Petter Parker', done => {
nightmare
.wait(selectors.clientsIndex.searchResult)
.type(selectors.clientsIndex.searchClientInput, 'Petter Parker')
.click(selectors.clientsIndex.searchButton)
.waitForNumberOfElements(selectors.clientsIndex.searchResult, 1)
.countSearchResults(selectors.clientsIndex.searchResult)
.then(result => {
expect(result).toEqual(1);
done();
})
.catch(catchErrors(done));
});
it(`should click on the search result to access to the client's greuge`, done => {
nightmare
.waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter Parker')
.waitToClick(selectors.clientsIndex.searchResult)
.waitToClick(selectors.greuge.greugeButton)
.waitForURL('greuge/list')
.url()
.then(url => {
expect(url).toContain('greuge/list');
done();
})
.catch(catchErrors(done));
});
it(`should click on the add greuge button`, done => {
nightmare
.waitToClick(selectors.greuge.addGreugeFloatButton)
.waitForURL('greuge/create')
.url()
.then(url => {
expect(url).toContain('greuge/create');
done();
})
.catch(catchErrors(done));
});
it(`should receive an error if all fields are empty but date on submit`, done => {
nightmare
.click(selectors.credit.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
});
it(`should receive an error if all fields are empty but date and amount on submit`, done => {
nightmare
.type(selectors.greuge.amountInput, 999)
.click(selectors.greuge.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
});
it(`should receive an error if all fields are empty but date and description on submit`, done => {
nightmare
.clearInput(selectors.greuge.amountInput)
.type(selectors.greuge.descriptionInput, 'new armor for Batman!')
.click(selectors.greuge.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
});
it(`should receive an error if all fields are empty but date and type on submit`, done => {
nightmare
.clearInput(selectors.greuge.descriptionInput)
.waitToClick(selectors.greuge.typeInput)
.waitToClick(selectors.greuge.typeSecondOption)
.click(selectors.greuge.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Some fields are invalid');
done();
})
.catch(catchErrors(done));
});
it(`should create a new greuge with all its data`, done => {
nightmare
.type(selectors.greuge.amountInput, 999)
.type(selectors.greuge.descriptionInput, 'new armor for Batman!')
.click(selectors.greuge.saveButton)
.wait(selectors.globalItems.snackbarIsActive)
.getInnerText(selectors.globalItems.snackbarIsActive)
.then(result => {
expect(result).toContain('Data saved!');
done();
})
.catch(catchErrors(done));
});
it('should confirm the greuge was added to the list', done => {
nightmare
.waitForSnackbarReset()
.wait(selectors.greuge.firstGreugeText)
.getInnerText(selectors.greuge.firstGreugeText)
.then(value => {
expect(value).toContain(999);
expect(value).toContain('new armor for Batman!');
expect(value).toContain('Diff');
done();
})
.catch(catchErrors(done));
});
});

View File

@ -0,0 +1,99 @@
import config from '../helpers/config.js';
import createNightmare from '../helpers/nightmare';
import selectors from '../helpers/selectors.js';
import {catchErrors} from '../../services/utils/jasmineHelpers';
const nightmare = createNightmare();
const moduleAccessViewHashURL = '#!/';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
describe('mandate path', () => {
describe('warm up', () => {
it('should warm up login and fixtures', done => {
nightmare
.login()
.waitForURL(moduleAccessViewHashURL)
.waitToClick(selectors.globalItems.logOutButton)
.then(() => {
done();
})
.catch(catchErrors(done));
});
});
it('should log in', done => {
nightmare
.login()
.waitForURL(moduleAccessViewHashURL)
.url()
.then(url => {
expect(url).toEqual(config.url + moduleAccessViewHashURL);
done();
})
.catch(catchErrors(done));
});
it('should make sure the language is English', done => {
nightmare
.changeLanguageToEnglish()
.then(() => {
done();
})
.catch(catchErrors(done));
});
it('should click on the Clients button of the top bar menu', done => {
nightmare
.waitToClick(selectors.globalItems.applicationsMenuButton)
.wait(selectors.globalItems.applicationsMenuVisible)
.waitToClick(selectors.globalItems.clientsButton)
.wait(selectors.clientsIndex.createClientButton)
.url()
.then(url => {
expect(url).toEqual(config.url + '#!/clients');
done();
})
.catch(catchErrors(done));
});
it('should search for the user Petter Parker', done => {
nightmare
.wait(selectors.clientsIndex.searchResult)
.type(selectors.clientsIndex.searchClientInput, 'Petter Parker')
.click(selectors.clientsIndex.searchButton)
.waitForNumberOfElements(selectors.clientsIndex.searchResult, 1)
.countSearchResults(selectors.clientsIndex.searchResult)
.then(result => {
expect(result).toEqual(1);
done();
})
.catch(catchErrors(done));
});
it(`should click on the search result to access to the client's mandate`, done => {
nightmare
.waitForTextInElement(selectors.clientsIndex.searchResult, 'Petter')
.waitToClick(selectors.clientsIndex.searchResult)
.waitToClick(selectors.mandate.mandateButton)
.waitForURL('mandate')
.url()
.then(url => {
expect(url).toContain('mandate');
done();
})
.catch(catchErrors(done));
});
it('should confirm the client has a mandate of the CORE type', done => {
nightmare
.wait(selectors.mandate.firstMandateText)
.getInnerText(selectors.mandate.firstMandateText)
.then(value => {
expect(value).toContain('1');
expect(value).toContain('WAY');
expect(value).toContain('CORE');
done();
})
.catch(catchErrors(done));
});
});

View File

@ -18,8 +18,8 @@ var SpecReporter = require('jasmine-spec-reporter').SpecReporter;
jasmine.loadConfig({
spec_files: [
'./e2e/helpers/extensions.js',
'./e2e/**/*[sS]pec.js'
'./e2e/**/*[sS]pec.js',
'./e2e/helpers/extensions.js'
],
helpers: [
'/services/utils/jasmineHelpers.js'

View File

@ -1,4 +1,5 @@
var gulp = require('gulp');
const jasmine = require('gulp-jasmine');
var gutil = require('gulp-util');
var wrap = require('gulp-wrap');
var concat = require('gulp-concat');
@ -37,6 +38,15 @@ gulp.task('client', ['clean'], function() {
return gulp.start('watch', 'routes', 'locales', 'webpack-dev-server');
});
gulp.task('nginxRestart', callback => {
let isWindows = /^win/.test(process.platform);
let command = isWindows ? '.\\dev.cmd' : './dev.sh';
exec(command, (err, stdout, stderr) => {
console.log(stdout);
callback(err);
});
});
gulp.task('services', () => {
process.env.NODE_ENV = gutil.env.env || 'development';
const pathServices = './services/';
@ -49,6 +59,25 @@ gulp.task('services', () => {
});
});
gulp.task('clientDev', callback => {
runSequence('nginxRestart', 'client', callback);
});
gulp.task('servicesDev', callback => {
let isWindows = /^win/.test(process.platform);
let command = isWindows ? 'docker inspect dblocal | findstr Status' : 'docker inspect dblocal | grep Status';
gutil.env.env = 'test';
exec(command, (err, stdout, stderr) => {
let isNotRunning = !stdout.includes('running');
if (isNotRunning) {
runSequence('docker', 'waitForMySQL', 'services');
} else {
runSequence('services');
}
callback(err);
});
});
gulp.task('clean', function() {
return del([`${buildDir}/*`, `!${buildDir}/templates`, `!${buildDir}/images`], {force: true});
});
@ -154,34 +183,33 @@ gulp.task('test', callback => {
// e2e tests
gulp.task('e2e', callback => {
runSequence('docker', 'runDockerLogs', 'endToEndTests', callback);
runSequence('docker', 'waitForMySQL', 'endToEndTests', callback);
});
gulp.task('runDockerLogs', callback => {
gulp.task('waitForMySQL', callback => {
let maxInterval = 30000;
let interval = 1000;
let timer = 0;
console.log('Waiting for MySQL init process...');
let waitForLocaldb = setInterval(() => {
if (timer < 15000) {
timer += 1000;
if (timer < maxInterval) {
timer += interval;
exec('docker logs --tail 4 dblocal', (err, stdout, stderr) => {
if (stdout.includes('MySQL init process done. Ready for start up.')) {
console.log('MySQL init process done.');
clearInterval(waitForLocaldb);
callback(err);
}
});
} else {
console.log('MySQL connection not established whithin 15 secs!');
console.log(`MySQL connection not established whithin ${maxInterval / 1000} secs!`);
clearInterval(waitForLocaldb);
}
}, 1000);
}, interval);
});
gulp.task('endToEndTests', callback => {
exec('npm run e2e', (err, stdout, stderr) => {
console.log(stdout);
callback(err);
});
gulp.src('./e2e_tests.js')
.pipe(jasmine({reporter: 'none'}));
});
// docker dblocal

View File

@ -44,6 +44,7 @@
"gulp-concat": "^2.6.0",
"gulp-extend": "^0.2.0",
"gulp-install": "^1.1.0",
"gulp-jasmine": "^3.0.0",
"gulp-print": "^2.0.1",
"gulp-util": "^3.0.7",
"gulp-wrap": "^0.13.0",
@ -57,7 +58,9 @@
"karma-jasmine": "^1.1.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.4",
"md5": "^2.2.1",
"merge-stream": "^1.0.1",
"mysql": "^2.15.0",
"nightmare": "^2.10.0",
"node-sass": "^3.11.0",
"raw-loader": "*",
@ -71,8 +74,7 @@
"build": "webpack --progress --colors",
"dev": "webpack-dev-server --progress --colors",
"lint": "eslint ./ --cache --ignore-pattern .gitignore",
"test": "node services_tests",
"testWatcher": "nodemon -q services_tests.js -w services",
"test": "nodemon -q services_tests.js -w services",
"e2e": "node e2e_tests"
}
}

View File

@ -8,7 +8,6 @@
"posttest": "npm run lint && nsp check"
},
"dependencies": {
"md5": "^2.2.1"
},
"repository": {
"type": "git",

View File

@ -1,4 +1,5 @@
var app = require('../../../server/server');
let app = require('../../../server/server');
let md5 = require('md5');
module.exports = function(Client) {
Client.remoteMethod('createUserProfile', {
@ -23,7 +24,7 @@ module.exports = function(Client) {
let user = {
name: data.userName,
email: firstEmail,
password: parseInt(Math.random() * 100000000000000)
password: md5(parseInt(Math.random() * 100000000000000))
};
app.models.Account.beginTransaction('READ COMMITTED', (error, transaction) => {

View File

@ -1,31 +1,77 @@
import app from '../../../../server/server';
import {catchErrors} from '../../../../../../services/utils/jasmineHelpers';
let app = require('../../../../server/server');
import {restoreFixtures} from '../../../../../../services/db/testing_fixtures';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
describe('Client Create()', () => {
let data = {
name: 'MaxEisenhardt',
userName: 'Magneto',
email: 'magneto@marvel.com',
fi: 'X-tax number',
socialName: 'The X-Men'
beforeEach(() => {
restoreFixtures();
});
let newAccountData = {
name: 'Wade',
userName: 'Deadpool',
email: 'Deadpool@marvel.com',
fi: 'DP',
socialName: 'Deadpool Marvel'
};
it('should createa new account', done => {
app.models.Client.createUserProfile(data, () => {
app.models.Account.findOne({where: {name: data.userName}})
it('should find Charles Xavier', done => {
app.models.Account.findOne({where: {name: 'CharlesXavier'}})
.then(account => {
expect(account.name).toEqual(data.userName);
app.models.Client.findOne({where: {name: data.name}})
expect(account.name).toEqual('CharlesXavier');
done();
})
.catch(catchErrors(done));
});
it("should not find Deadpool as he's not created yet", done => {
app.models.Account.findOne({where: {name: newAccountData.userName}})
.then(account => {
expect(account).toEqual(null);
app.models.Client.findOne({where: {name: newAccountData.name}})
.then(client => {
expect(client.id).toEqual(account.id);
expect(client.name).toEqual(data.name);
expect(client.email).toEqual(data.email);
expect(client.fi).toEqual(data.fi);
expect(client.socialName).toEqual(data.socialName);
expect(client).toEqual(null);
done();
});
})
.catch(catchErrors(done));
});
it('should not be able to create a user if exists', done => {
app.models.Client.findOne({where: {name: 'Charles Xavier'}})
.then(client => {
let formerAccountData = {
name: client.name,
userName: client.userName,
email: client.email,
fi: client.fi,
socialName: client.socialName
};
expect(app.models.Client.createUserProfile(formerAccountData)).toBeFalsy();
done();
});
});
// awaiting for fixtures
// it('should create a new account', done => {
// app.models.Client.createUserProfile(data => {
// app.models.Account.findOne({where: {name: data.userName}})
// .then(account => {
// expect(account.name).toEqual(data.userName);
// app.models.Client.findOne({where: {name: data.name}})
// .then(client => {
// expect(client.id).toEqual(account.id);
// expect(client.name).toEqual(data.name);
// expect(client.email).toEqual(data.email);
// expect(client.fi).toEqual(data.fi);
// expect(client.socialName).toEqual(data.socialName);
// done();
// });
// })
// .catch(catchErrors(done));
// });
// });
});

View File

@ -10,7 +10,7 @@ module.exports = function(Self) {
let userId = token && token.userId;
let app = require('../../server/server');
let Employee = app.models.Employee;
Employee.findOne({where: {userFk: userId}}, function (err, user){
Employee.findOne({where: {userFk: userId}}, (err, user) => {
if (user) {
ctx.instance.employeeFk = user.id;
next();

View File

@ -18,15 +18,9 @@ module.exports = function(Self) {
// Validations
Self.validatesUniquenessOf('name', {
message: 'El nombre debe ser único'
});
Self.validatesUniquenessOf('fi', {
message: 'El NIF/CIF debe ser único'
});
Self.validatesPresenceOf('socialName', {
message: 'Debe especificarse la razón social'
});
Self.validatesUniquenessOf('socialName', {
message: 'La razón social debe ser única'
});

View File

@ -22,7 +22,8 @@
"description": "Fiscal indentifier"
},
"socialName": {
"type": "string"
"type": "string",
"required": true
},
"contact": {
"type": "string"

View File

@ -1,4 +1,9 @@
module.exports = function(Self) {
require('../methods/greuge/filter.js')(Self);
require('../methods/greuge/totalGreuge.js')(Self);
Self.validatesLengthOf('description', {
max: 45,
message: 'La description debe tener maximo 45 caracteres'
});
};

View File

@ -14,10 +14,12 @@
"description": "Identifier"
},
"description": {
"type": "String"
"type": "String",
"required": true
},
"amount": {
"type": "Number"
"type": "Number",
"required": true
},
"shipped": {
"type": "date"
@ -35,7 +37,8 @@
"greugeType": {
"type": "belongsTo",
"model": "GreugeType",
"foreignKey": "greugeTypeFk"
"foreignKey": "greugeTypeFk",
"required": true
}
}
}

View File

@ -1,6 +1,7 @@
FROM mysql:5.6.37
ENV MYSQL_ALLOW_EMPTY_PASSWORD yes
ENV TZ GMT-1
COPY localDB01StructureAccount.sql /docker-entrypoint-initdb.d
COPY localDB02StructureVn2008.sql /docker-entrypoint-initdb.d

View File

@ -459,7 +459,7 @@ INSERT INTO `vn2008`.`empresa_grupo`(`empresa_grupo_id`, `grupo`)
INSERT INTO `vn2008`.`empresa`(`id`, `abbreviation`, `registro`, `gerente_id`, `alta`, `baja`, `logo`, `oficial`, `cyc`, `rgb`, `mail`, `cuno`, `ODBC_DATE`, `Id_Cliente`, `digito_factura`, `Id_Proveedores_account`, `morosidad`, `empresa_grupo`)
VALUES
('442', 'WAY', 'Wayne Industries, Inc. operates as a warehouse for steel products. Wayne Industries, Inc. was founded in 1989 and is based in Wayne, Michigan.', '2', '1989-11-19', NULL, NULL, '1', '1', '00FF00', 'BruceWayne@verdnatura.es', NULL, '1989-08-11 12:31:22', '10', '1', NULL, '1', '1');
(442, 'WAY', 'Wayne Industries, Inc. operates as a warehouse for steel products. Wayne Industries, Inc. was founded in 1989 and is based in Wayne, Michigan.', '2', '1989-11-19', NULL, NULL, '1', '1', '00FF00', 'BruceWayne@verdnatura.es', NULL, '1989-08-11 12:31:22', '10', '1', NULL, '1', '1');
INSERT INTO `salix`.`Ticket`(`id`, `agencyFk`, `employeeFk`, `date`, `hour`, `clientFk`, `addressFk`)
VALUES
@ -525,5 +525,5 @@ INSERT INTO `vn`.`mandateType`(`id`, `name`)
INSERT INTO `vn`.`mandate`(`id`, `clientFk`, `companyFk`, `code`, `created`, `mandateTypeFk`)
VALUES
(1, 1, 442, '1-1', CURDATE(), 2);
(1, 2, 442, '1-1', CURDATE(), 2);

View File

@ -0,0 +1,16 @@
let mysql = require('mysql');
let connection = mysql.createConnection({
multipleStatements: false,
host: 'localhost',
user: 'root',
password: '',
database: 'salix'
});
export function restoreFixtures() {
// connection.connect();
// connection.query('CALL truncateDatabase');
// connection.disconect();
// console.log('fixtures restored');
}

View File

@ -18,10 +18,10 @@
}
},
"relations": {
"taxGroup": {
"taxClass": {
"type": "belongsTo",
"model": "TaxGroup",
"foreignKey": "taxGroupFk"
"model": "TaxClass",
"foreignKey": "taxClassFk"
},
"taxCode": {
"type": "belongsTo",

View File

@ -1,3 +1,6 @@
module.exports = function(Self) {
require('../methods/item/filter.js')(Self);
Self.validatesPresenceOf('name', {message: 'Cannot be blank'});
Self.validatesPresenceOf('originFk', {message: 'Cannot be blank'});
};

View File

@ -14,7 +14,8 @@
"description": "Identifier"
},
"name": {
"type": "String"
"type": "String",
"required": true
},
"size": {
"type": "Number"

View File

@ -1,9 +1,9 @@
{
"name": "TaxGroup",
"name": "TaxClass",
"base": "VnModel",
"options": {
"mysql": {
"table": "taxGroup",
"table": "taxClass",
"database": "vn"
}
},

View File

@ -42,7 +42,7 @@
"Intrastat": {
"dataSource": "vn"
},
"TaxGroup": {
"TaxClass": {
"dataSource": "vn"
},
"TaxCode": {

View File

@ -24,4 +24,3 @@
"acquireTimeout": 20000
}
}

View File

@ -1,5 +1,7 @@
require('babel-core/register')({presets: ['es2015']});
process.env.NODE_ENV = 'test';
process.on('warning', warning => {
console.log(warning.name);
console.log(warning.message);
@ -17,13 +19,12 @@ var jasmine = new Jasmine();
var SpecReporter = require('jasmine-spec-reporter').SpecReporter;
jasmine.loadConfig({
spec_dir: 'services/',
spec_dir: 'services',
spec_files: [
'**/specs/*[sS]pec.js'
'client/common/**/*[sS]pec.js'
],
helpers: [
// to implement
// '/api/utils/jasmineHelpers.js'
'/services/utils/jasmineHelpers.js'
]
});
@ -36,6 +37,5 @@ jasmine.addReporter(new SpecReporter({
}
}));
exports.start = () => {
jasmine.execute();
};