Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2061-ticket_descriptor
This commit is contained in:
commit
6a021b5767
|
@ -54,6 +54,10 @@ pipeline {
|
|||
}
|
||||
}
|
||||
stage('Test') {
|
||||
when { not { anyOf {
|
||||
branch 'test'
|
||||
branch 'master'
|
||||
}}}
|
||||
environment {
|
||||
NODE_ENV = ""
|
||||
}
|
||||
|
|
|
@ -13,8 +13,15 @@ Required applications.
|
|||
|
||||
You will need to install globally the following items.
|
||||
```
|
||||
# sudo npm install -g jest gulp-cli
|
||||
$ sudo npm install -g jest gulp-cli
|
||||
```
|
||||
|
||||
For the usage of jest --watch on macOs.
|
||||
```
|
||||
$ brew install watchman
|
||||
```
|
||||
* [watchman](https://facebook.github.io/watchman/)
|
||||
|
||||
## Linux Only Prerequisites
|
||||
|
||||
Your user must be on the docker group to use it so you will need to run this command:
|
||||
|
|
|
@ -4,3 +4,4 @@ apps:
|
|||
instances: 1
|
||||
max_restarts: 3
|
||||
restart_delay: 15000
|
||||
node_args: --tls-min-v1.0
|
||||
|
|
|
@ -54,7 +54,7 @@ module.exports = class Docker {
|
|||
this.dbConf.port = netSettings.Ports['3306/tcp'][0]['HostPort'];
|
||||
}
|
||||
|
||||
await this.waitForHealthy();
|
||||
if (runChown) await this.wait();
|
||||
} catch (err) {
|
||||
if (this.isRandom)
|
||||
await this.rm();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
describe('Component vnField', () => {
|
||||
let $element;
|
||||
let controller;
|
||||
let $element;
|
||||
|
||||
beforeEach(ngModule('vnCore'));
|
||||
|
||||
|
@ -13,12 +13,84 @@ describe('Component vnField', () => {
|
|||
$element.remove();
|
||||
});
|
||||
|
||||
// Remove this block
|
||||
describe('clearDisabled binding', () => {
|
||||
it(`should enable the show property`, () => {
|
||||
controller.clearDisabled = true;
|
||||
describe('field get/set', () => {
|
||||
it('should do nothing when trying to set the same value again', () => {
|
||||
controller.field = '';
|
||||
jest.spyOn(controller, 'validateValue');
|
||||
|
||||
expect(controller.clearDisabled).toEqual(true);
|
||||
controller.field = '';
|
||||
|
||||
expect(controller.validateValue).toHaveBeenCalledTimes(0);
|
||||
expect(controller.classList).not.toContain('not-empty');
|
||||
});
|
||||
|
||||
it('should add the class no empty and then call validateValue()', () => {
|
||||
controller.field = '';
|
||||
jest.spyOn(controller, 'validateValue');
|
||||
|
||||
controller.field = 'someField';
|
||||
|
||||
expect(controller.validateValue).toHaveBeenCalledTimes(1);
|
||||
expect(controller.classList).toContain('not-empty');
|
||||
});
|
||||
});
|
||||
|
||||
describe('refreshHint()', () => {
|
||||
it('should add the class invalid if there is an error in the controller', () => {
|
||||
controller._error = true;
|
||||
|
||||
controller.refreshHint();
|
||||
|
||||
expect(controller.classList).toContain('invalid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onFocus()', () => {
|
||||
it('should add the class focus', () => {
|
||||
controller.onFocus(true);
|
||||
|
||||
expect(controller.classList).toContain('focused');
|
||||
});
|
||||
|
||||
it('should not add the class focus', () => {
|
||||
controller.onFocus(false);
|
||||
|
||||
expect(controller.classList).not.toContain('focuses');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildInput()', () => {
|
||||
it('should build an input based on the received type', () => {
|
||||
controller.buildInput('number');
|
||||
|
||||
expect(controller.input.tagName).toEqual('INPUT');
|
||||
expect(controller.input.type).toEqual('number');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateValue()', () => {
|
||||
it('should do nothing if there is no new error to show', () => {
|
||||
jest.spyOn(controller, 'refreshHint');
|
||||
controller.inputError = 'old validation message';
|
||||
controller.buildInput('number');
|
||||
controller.input.setCustomValidity('old validation message');
|
||||
|
||||
controller.validateValue();
|
||||
|
||||
expect(controller.refreshHint).not.toHaveBeenCalled();
|
||||
expect(controller.inputError).toEqual('old validation message');
|
||||
});
|
||||
|
||||
it('should update the input error and call refreshHint', () => {
|
||||
jest.spyOn(controller, 'refreshHint');
|
||||
controller.inputError = 'OLD validation message';
|
||||
controller.buildInput('number');
|
||||
controller.input.setCustomValidity('NEW validation message');
|
||||
|
||||
controller.validateValue();
|
||||
|
||||
expect(controller.refreshHint).toHaveBeenCalled();
|
||||
expect(controller.inputError).toEqual('NEW validation message');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -73,7 +73,7 @@ export function config($translateProvider, $translatePartialLoaderProvider, $htt
|
|||
return locale;
|
||||
if (langOptions.langAliases[locale])
|
||||
return langOptions.langAliases[locale];
|
||||
return fallbackLang;
|
||||
return langOptions.fallbackLang;
|
||||
});
|
||||
|
||||
$translatePartialLoaderProvider.addPart(appName);
|
||||
|
|
|
@ -48,7 +48,7 @@ function backWatch(done) {
|
|||
|
||||
// XXX: Workaround to avoid nodemon bug
|
||||
// https://github.com/remy/nodemon/issues/1346
|
||||
let commands = ['node --inspect ./node_modules/gulp/bin/gulp.js'];
|
||||
let commands = ['node --tls-min-v1.0 --inspect ./node_modules/gulp/bin/gulp.js'];
|
||||
if (!isWindows) commands.unshift('sleep 1');
|
||||
|
||||
nodemon({
|
||||
|
@ -136,7 +136,7 @@ function backTest(done) {
|
|||
const nodemon = require('gulp-nodemon');
|
||||
|
||||
nodemon({
|
||||
exec: ['node ./node_modules/gulp/bin/gulp.js'],
|
||||
exec: ['node --tls-min-v1.0 ./node_modules/gulp/bin/gulp.js'],
|
||||
args: ['backTestOnce'],
|
||||
watch: backSources,
|
||||
done: done
|
||||
|
|
|
@ -15,4 +15,4 @@ Show Pickup order: Ver orden de recogida
|
|||
Search claim by id or client name: Buscar reclamaciones por identificador o nombre de cliente
|
||||
Claim deleted!: Reclamación eliminada!
|
||||
claim: reclamación
|
||||
Photos: Fotos
|
||||
Photos: Fotos
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('client sendSms()', () => {
|
||||
let createdLog;
|
||||
|
||||
afterAll(async done => {
|
||||
await app.models.ClientLog.destroyById(createdLog.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should send a message and log it', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 9}}};
|
||||
let id = 101;
|
||||
let destination = 222222222;
|
||||
let message = 'this is the message created in a test';
|
||||
|
||||
let sms = await app.models.Client.sendSms(ctx, id, destination, message);
|
||||
|
||||
logId = sms.logId;
|
||||
|
||||
createdLog = await app.models.ClientLog.findById(logId);
|
||||
let json = JSON.parse(JSON.stringify(createdLog.newInstance));
|
||||
|
||||
expect(json.message).toEqual(message);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const soap = require('soap');
|
||||
|
||||
describe('sms send()', () => {
|
||||
it('should return the expected message and status code', async() => {
|
||||
const code = 200;
|
||||
const smsConfig = await app.models.SmsConfig.findOne();
|
||||
const soapClient = await soap.createClientAsync(smsConfig.uri);
|
||||
spyOn(soap, 'createClientAsync').and.returnValue(soapClient);
|
||||
spyOn(soapClient, 'sendSMSAsync').and.returnValue([{
|
||||
result: {
|
||||
$value:
|
||||
`<xtratelecom-sms-response>
|
||||
<sms>
|
||||
<codigo>
|
||||
${code}
|
||||
</codigo>
|
||||
<descripcion>
|
||||
Envio en procesamiento
|
||||
</descripcion>
|
||||
<messageId>
|
||||
1
|
||||
</messageId>
|
||||
</sms>
|
||||
<procesoId>
|
||||
444328681
|
||||
</procesoId>
|
||||
</xtratelecom-sms-response>`
|
||||
}
|
||||
}]);
|
||||
let ctx = {req: {accessToken: {userId: 1}}};
|
||||
let result = await app.models.Sms.send(ctx, 105, 'destination', 'My SMS Body');
|
||||
|
||||
expect(result.statusCode).toEqual(200);
|
||||
expect(result.status).toContain('Fake response');
|
||||
});
|
||||
});
|
|
@ -9,7 +9,7 @@ class Controller extends Descriptor {
|
|||
set entity(value) {
|
||||
super.entity = value;
|
||||
|
||||
if (this.$params.sendSMS)
|
||||
if (value && this.$params.sendSMS)
|
||||
this.showSMSDialog();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ module.exports = Self => {
|
|||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'string',
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
@ -53,6 +53,6 @@ module.exports = Self => {
|
|||
if (!isAllowed)
|
||||
throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
|
||||
|
||||
return await models.TicketTracking.create(params);
|
||||
return models.TicketTracking.create(params);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('setDelivered', {
|
||||
description: 'Changes the state of the received ticket ids to delivered',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ticketIds',
|
||||
description: 'the array of ticket ids to set as delivered',
|
||||
type: ['Number'],
|
||||
required: true,
|
||||
http: {source: 'body'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/setDelivered`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.setDelivered = async(ctx, ticketIds) => {
|
||||
let userId = ctx.req.accessToken.userId;
|
||||
let models = Self.app.models;
|
||||
|
||||
let state = await models.State.findOne({
|
||||
where: {
|
||||
code: 'delivered'
|
||||
},
|
||||
fields: ['id', 'name', 'alertLevel', 'code']
|
||||
});
|
||||
|
||||
let worker = await models.Worker.findOne({where: {userFk: userId}});
|
||||
|
||||
let promises = [];
|
||||
for (let id of ticketIds) {
|
||||
let promise = models.TicketTracking.changeState(ctx, {
|
||||
stateFk: state.id,
|
||||
workerFk: worker.id,
|
||||
ticketFk: id
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
return state;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('ticket setDelivered()', () => {
|
||||
let ticketOne;
|
||||
let ticketTwo;
|
||||
|
||||
beforeAll(async done => {
|
||||
let originalTicketOne = await app.models.Ticket.findById(8);
|
||||
let originalTicketTwo = await app.models.Ticket.findById(10);
|
||||
|
||||
originalTicketOne.id = null;
|
||||
originalTicketTwo.id = null;
|
||||
ticketOne = await app.models.Ticket.create(originalTicketOne);
|
||||
ticketTwo = await app.models.Ticket.create(originalTicketTwo);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(async done => {
|
||||
await app.models.Ticket.destroyById(ticketOne.id);
|
||||
await app.models.Ticket.destroyById(ticketTwo.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should return the state which has been applied to the given tickets', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 49}}};
|
||||
let delivered = await app.models.State.findOne({where: {code: 'delivered'}, fields: ['id']});
|
||||
|
||||
let params = [ticketOne.id, ticketTwo.id];
|
||||
let state = await app.models.TicketTracking.setDelivered(ctx, params);
|
||||
|
||||
expect(state.id).toEqual(delivered.id);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('ticket sendSms()', () => {
|
||||
let logId;
|
||||
|
||||
afterAll(async done => {
|
||||
await app.models.TicketLog.destroyById(logId);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should send a message and log it', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 9}}};
|
||||
let id = 11;
|
||||
let destination = 222222222;
|
||||
let message = 'this is the message created in a test';
|
||||
|
||||
let sms = await app.models.Ticket.sendSms(ctx, id, destination, message);
|
||||
|
||||
logId = sms.logId;
|
||||
|
||||
let createdLog = await app.models.TicketLog.findById(logId);
|
||||
let json = JSON.parse(JSON.stringify(createdLog.newInstance));
|
||||
|
||||
expect(json.message).toEqual(message);
|
||||
});
|
||||
});
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = function(Self) {
|
||||
require('../methods/ticket-tracking/changeState')(Self);
|
||||
require('../methods/ticket-tracking/setDelivered')(Self);
|
||||
|
||||
Self.validatesPresenceOf('stateFk', {message: 'State cannot be blank'});
|
||||
Self.validatesPresenceOf('workerFk', {message: 'Worker cannot be blank'});
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
<vn-td class="expendable">{{::ticket.province}}</vn-td>
|
||||
<vn-td>
|
||||
<span class="chip {{$ctrl.stateColor(ticket)}}">
|
||||
{{::ticket.state}}
|
||||
{{ticket.state}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td>{{::ticket.agencyMode}}</vn-td>
|
||||
|
@ -120,6 +120,13 @@
|
|||
</vn-data-viewer>
|
||||
<div fixed-bottom-right>
|
||||
<vn-vertical style="align-items: center;">
|
||||
<vn-button class="round sm vn-mb-sm"
|
||||
icon="print"
|
||||
ng-show="$ctrl.totalChecked > 0"
|
||||
ng-click="$ctrl.setDelivered()"
|
||||
vn-tooltip="Set as delivered and open delivery note(s)"
|
||||
tooltip-position="left">
|
||||
</vn-button>
|
||||
<vn-button class="round sm vn-mb-sm"
|
||||
icon="icon-recovery"
|
||||
ng-show="$ctrl.totalChecked > 0"
|
||||
|
|
|
@ -4,6 +4,37 @@ import UserError from 'core/lib/user-error';
|
|||
import './style.scss';
|
||||
|
||||
export default class Controller extends Section {
|
||||
constructor($element, $, vnReport) {
|
||||
super($element, $);
|
||||
this.vnReport = vnReport;
|
||||
}
|
||||
setDelivered() {
|
||||
const checkedTickets = this.checked;
|
||||
let ids = [];
|
||||
|
||||
for (let ticket of checkedTickets)
|
||||
ids.push(ticket.id);
|
||||
|
||||
this.$http.post('TicketTrackings/setDelivered', ids).then(res => {
|
||||
let state = res.data;
|
||||
for (let ticket of checkedTickets) {
|
||||
ticket.stateFk = state.id;
|
||||
ticket.state = state.name;
|
||||
ticket.alertLevel = state.alertLevel;
|
||||
ticket.alertLevelCode = state.code;
|
||||
}
|
||||
this.openDeliveryNotes(ids);
|
||||
});
|
||||
}
|
||||
|
||||
openDeliveryNotes(ids) {
|
||||
for (let id of ids) {
|
||||
this.vnReport.show('delivery-note', {
|
||||
ticketId: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openBalanceDialog() {
|
||||
const checkedTickets = this.checked;
|
||||
const description = [];
|
||||
|
@ -125,6 +156,7 @@ export default class Controller extends Section {
|
|||
return [minHour, maxHour];
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$element', '$scope', 'vnReport'];
|
||||
|
||||
ngModule.component('vnTicketIndex', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import './index.js';
|
||||
describe('Component vnTicketIndex', () => {
|
||||
let controller;
|
||||
let $httpBackend;
|
||||
let $window;
|
||||
let tickets = [{
|
||||
id: 1,
|
||||
|
@ -21,7 +22,8 @@ describe('Component vnTicketIndex', () => {
|
|||
|
||||
beforeEach(ngModule('ticket'));
|
||||
|
||||
beforeEach(inject(($componentController, _$window_) => {
|
||||
beforeEach(inject(($componentController, _$window_, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$window = _$window_;
|
||||
const $element = angular.element('<vn-ticket-index></vn-ticket-index>');
|
||||
controller = $componentController('vnTicketIndex', {$element});
|
||||
|
@ -85,6 +87,21 @@ describe('Component vnTicketIndex', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('setDelivered()/openDeliveryNotes()', () => {
|
||||
it('should perform a post to setDelivered and open tabs with the delivery notes', () => {
|
||||
controller.$.model = {data: tickets, refresh: () => {}};
|
||||
|
||||
$window.open = jest.fn();
|
||||
|
||||
$httpBackend.expect('POST', 'TicketTrackings/setDelivered').respond('ok');
|
||||
controller.setDelivered();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect($window.open).toHaveBeenCalledWith(`api/report/delivery-note?ticketId=${tickets[1].id}`);
|
||||
expect($window.open).toHaveBeenCalledWith(`api/report/delivery-note?ticketId=${tickets[2].id}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checked()', () => {
|
||||
it('should return an array of checked tickets', () => {
|
||||
controller.$.model = {data: tickets};
|
||||
|
|
|
@ -2,6 +2,7 @@ Weekly tickets: Tickets programados
|
|||
Go to lines: Ir a lineas
|
||||
Not available: No disponible
|
||||
Payment on account...: Pago a cuenta...
|
||||
Set as delivered and open delivery note(s): Marcar como servido/s y abrir albarán/es
|
||||
Closure: Cierre
|
||||
You cannot make a payment on account from multiple clients: No puedes realizar un pago a cuenta de clientes diferentes
|
||||
Filter by selection: Filtro por selección
|
||||
|
|
|
@ -44,7 +44,7 @@ class Controller extends Component {
|
|||
let salesIds = [];
|
||||
let modified = false;
|
||||
|
||||
if (!this.newDiscount) return;
|
||||
if (this.newDiscount == null) return;
|
||||
|
||||
for (let i = 0; i < this.edit.length; i++) {
|
||||
if (this.newDiscount != this.edit[0].discount || this.bulk || !this.newDiscount) {
|
||||
|
|
|
@ -99,8 +99,8 @@
|
|||
</vn-td>
|
||||
<vn-td shrink>
|
||||
<img
|
||||
ng-src="{{::$root.imagePath}}/50x50/{{sale.image}}"
|
||||
zoom-image="{{::$root.imagePath}}/1600x900/{{sale.image}}"
|
||||
ng-src="{{::$root.imagePath}}/catalog/50x50/{{sale.image}}"
|
||||
zoom-image="{{::$root.imagePath}}/catalog/1600x900/{{sale.image}}"
|
||||
on-error-src/>
|
||||
</vn-td>
|
||||
<vn-td vn-focus number>
|
||||
|
|
|
@ -56,15 +56,19 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
let entitlementRate = 0;
|
||||
absences.forEach(absence => {
|
||||
const isHoliday = absence.absenceType().code === 'holiday';
|
||||
const isHalfHoliday = absence.absenceType().code === 'halfHoliday';
|
||||
const absenceType = absence.absenceType();
|
||||
const isHoliday = absenceType.code === 'holiday';
|
||||
const isHalfHoliday = absenceType.code === 'halfHoliday';
|
||||
|
||||
if (isHoliday)
|
||||
calendar.holidaysEnjoyed += 1;
|
||||
if (isHalfHoliday)
|
||||
calendar.holidaysEnjoyed += 0.5;
|
||||
|
||||
entitlementRate += absenceType.holidayEntitlementRate;
|
||||
|
||||
absence.dated = new Date(absence.dated);
|
||||
absence.dated.setHours(0, 0, 0, 0);
|
||||
});
|
||||
|
@ -119,6 +123,9 @@ module.exports = Self => {
|
|||
|
||||
workedDays += Math.floor((endedTime - startedTime) / dayTimestamp);
|
||||
|
||||
if (workedDays > daysInYear())
|
||||
workedDays = daysInYear();
|
||||
|
||||
// Workcenter holidays
|
||||
let holidayList = contract.workCenter().holidays();
|
||||
for (let day of holidayList) {
|
||||
|
@ -137,10 +144,22 @@ module.exports = Self => {
|
|||
const maxHolidays = currentContract.holidays().days;
|
||||
calendar.totalHolidays = maxHolidays;
|
||||
|
||||
if (workedDays < 365)
|
||||
calendar.totalHolidays = Math.round(2 * maxHolidays * (workedDays) / 365) / 2;
|
||||
workedDays -= entitlementRate;
|
||||
|
||||
if (workedDays < daysInYear())
|
||||
calendar.totalHolidays = Math.round(2 * maxHolidays * (workedDays) / daysInYear()) / 2;
|
||||
}
|
||||
|
||||
function daysInYear() {
|
||||
const year = yearStarted.getFullYear();
|
||||
|
||||
return isLeapYear(year) ? 366 : 365;
|
||||
}
|
||||
|
||||
return [calendar, absences, holidays];
|
||||
};
|
||||
|
||||
function isLeapYear(year) {
|
||||
return year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
},
|
||||
"code": {
|
||||
"type": "String"
|
||||
},
|
||||
"holidayEntitlementRate": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,7 +42,7 @@
|
|||
"@babel/preset-env": "^7.7.7",
|
||||
"@babel/register": "^7.7.7",
|
||||
"angular-mocks": "^1.7.9",
|
||||
"babel-jest": "^24.9.0",
|
||||
"babel-jest": "^26.0.1",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"css-loader": "^2.1.0",
|
||||
|
@ -70,7 +70,7 @@
|
|||
"jasmine": "^3.5.0",
|
||||
"jasmine-reporters": "^2.3.2",
|
||||
"jasmine-spec-reporter": "^4.2.1",
|
||||
"jest": "^24.9.0",
|
||||
"jest": "^26.0.1",
|
||||
"jest-junit": "^8.0.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"merge-stream": "^1.0.1",
|
||||
|
|
Loading…
Reference in New Issue