fleetmap-reports 2.0.384 → 2.0.386
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lang/enGB.js +3 -0
- package/lang/esCL.js +3 -0
- package/lang/frFR.json +3 -0
- package/lang/ptBR.js +3 -0
- package/lang/ptPT.js +3 -0
- package/package.json +1 -1
- package/src/activity-report.js +71 -1
- package/src/index.js +10 -0
- package/src/rpm-report.js +194 -0
- package/src/tests/rpm.test.js +33 -0
package/lang/enGB.js
CHANGED
|
@@ -195,6 +195,9 @@ module.exports = {
|
|
|
195
195
|
titleStopReport: 'Stop Report',
|
|
196
196
|
titleDriverRankingReport: 'Driver Ranking Report',
|
|
197
197
|
titleSensorReport: 'Sensor Report',
|
|
198
|
+
titleRPMReport: 'RPM Report',
|
|
199
|
+
rpmThreshold: 'RPM Threshold',
|
|
200
|
+
timeAboveRPM: 'Time above threshold',
|
|
198
201
|
sensor: 'Sensor',
|
|
199
202
|
status: 'Status',
|
|
200
203
|
rank: 'Rank',
|
package/lang/esCL.js
CHANGED
|
@@ -185,6 +185,9 @@ module.exports = {
|
|
|
185
185
|
titleStopReport: 'Informe de Paradas',
|
|
186
186
|
titleDriverRankingReport: 'Informe de Ranking de Conductores',
|
|
187
187
|
titleSensorReport: 'Informe de Sensores',
|
|
188
|
+
titleRPMReport: 'Informe de RPM',
|
|
189
|
+
rpmThreshold: 'Umbral de RPM',
|
|
190
|
+
timeAboveRPM: 'Tiempo sobre el umbral',
|
|
188
191
|
sensor: 'Sensor',
|
|
189
192
|
status: 'Estado',
|
|
190
193
|
rank: 'Posición',
|
package/lang/frFR.json
CHANGED
|
@@ -196,6 +196,9 @@
|
|
|
196
196
|
"titleDelayedStartReport": "Rapport de Departs Tardifs",
|
|
197
197
|
"titleDriverRankingReport": "Rapport d'conducteur",
|
|
198
198
|
"titleSensorReport": "Rapport de capteurs",
|
|
199
|
+
"titleRPMReport": "Rapport de RPM",
|
|
200
|
+
"rpmThreshold": "Seuil de RPM",
|
|
201
|
+
"timeAboveRPM": "Temps au-dessus du seuil",
|
|
199
202
|
"sensor": "Capteur",
|
|
200
203
|
"status": "État",
|
|
201
204
|
"from": "Dans",
|
package/lang/ptBR.js
CHANGED
|
@@ -187,6 +187,9 @@ module.exports = {
|
|
|
187
187
|
titleStopReport: 'Relatório de Paragens',
|
|
188
188
|
titleDriverRankingReport: 'Relatório de Ranking de Motoristas',
|
|
189
189
|
titleSensorReport: 'Relatório de Sensores',
|
|
190
|
+
titleRPMReport: 'Relatório de RPM',
|
|
191
|
+
rpmThreshold: 'Limite de RPM',
|
|
192
|
+
timeAboveRPM: 'Tempo acima do limite',
|
|
190
193
|
sensor: 'Sensor',
|
|
191
194
|
status: 'Estado',
|
|
192
195
|
rank: 'Posição',
|
package/lang/ptPT.js
CHANGED
|
@@ -193,6 +193,9 @@ module.exports = {
|
|
|
193
193
|
titleStopReport: 'Relatório de Paragens',
|
|
194
194
|
titleDriverRankingReport: 'Relatório de Ranking de Condutores',
|
|
195
195
|
titleSensorReport: 'Relatório de Sensores',
|
|
196
|
+
titleRPMReport: 'Relatório de RPM',
|
|
197
|
+
rpmThreshold: 'Limite de RPM',
|
|
198
|
+
timeAboveRPM: 'Tempo acima do limite',
|
|
196
199
|
sensor: 'Sensor',
|
|
197
200
|
status: 'Estado',
|
|
198
201
|
rank: 'Posição',
|
package/package.json
CHANGED
package/src/activity-report.js
CHANGED
|
@@ -207,7 +207,8 @@ async function processDevices (from, to, devices, data, userData, traccarInstanc
|
|
|
207
207
|
endTime: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endTime,
|
|
208
208
|
endAddress: tripsByDay.length && tripsByDay[tripsByDay.length - 1].endAddress,
|
|
209
209
|
startTimeIsOut: tripsByDay.some(t => t.startTimeIsOut),
|
|
210
|
-
endTimeIsOut: tripsByDay.some(t => t.endTimeIsOut)
|
|
210
|
+
endTimeIsOut: tripsByDay.some(t => t.endTimeIsOut),
|
|
211
|
+
activity: getDayActivity(tripsByDay, fromDay, toDay)
|
|
211
212
|
})
|
|
212
213
|
}
|
|
213
214
|
} else {
|
|
@@ -232,6 +233,7 @@ async function processDevices (from, to, devices, data, userData, traccarInstanc
|
|
|
232
233
|
summaryCurrentDay.endOdometer = summaryByDevice.endOdometer
|
|
233
234
|
summaryCurrentDay.startTimeIsOut = tripsByDay.some(t => t.startTimeIsOut)
|
|
234
235
|
summaryCurrentDay.endTimeIsOut = tripsByDay.some(t => t.endTimeIsOut)
|
|
236
|
+
summaryCurrentDay.activity = getDayActivity(tripsByDay, fromDay, toDay)
|
|
235
237
|
summary.push(summaryCurrentDay)
|
|
236
238
|
}
|
|
237
239
|
}
|
|
@@ -285,6 +287,17 @@ async function processDevices (from, to, devices, data, userData, traccarInstanc
|
|
|
285
287
|
return devicesResult
|
|
286
288
|
}
|
|
287
289
|
|
|
290
|
+
// Trips of one day as [{ start, end }] in hours since the start of that day (0-24),
|
|
291
|
+
// used to draw the daily activity timeline on screen and in the PDF.
|
|
292
|
+
function getDayActivity (tripsByDay, fromDay, toDay) {
|
|
293
|
+
const dayStart = new Date(fromDay).getTime()
|
|
294
|
+
const dayEnd = new Date(toDay).getTime()
|
|
295
|
+
const toHours = time => (Math.min(Math.max(new Date(time).getTime(), dayStart), dayEnd) - dayStart) / 3600000
|
|
296
|
+
return tripsByDay
|
|
297
|
+
.map(t => ({ start: toHours(t.startTime), end: toHours(t.endTime) }))
|
|
298
|
+
.filter(a => a.end > a.start)
|
|
299
|
+
}
|
|
300
|
+
|
|
288
301
|
function processDrivers (from, to, drivers, driversData, userData) {
|
|
289
302
|
const driversResult = []
|
|
290
303
|
drivers.forEach(d => {
|
|
@@ -508,6 +521,63 @@ function exportActivityReportToPDFVehicleGroupByDay (doc, translations, reportDa
|
|
|
508
521
|
|
|
509
522
|
const style = getStyle(getUserPartner(userData.user))
|
|
510
523
|
addTable(doc, headers, data, footValues, style, 40)
|
|
524
|
+
addActivityTimeline(doc, translations, d.summary, weekDays)
|
|
525
|
+
})
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Draws one bar per day (00h-24h) below the table: grey when stopped, green while driving.
|
|
529
|
+
function addActivityTimeline (doc, translations, summary, weekDays) {
|
|
530
|
+
if (!summary.some(s => s.activity)) {
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
const pageWidth = doc.internal.pageSize.getWidth()
|
|
534
|
+
const pageHeight = doc.internal.pageSize.getHeight()
|
|
535
|
+
const labelX = 14
|
|
536
|
+
const left = 50
|
|
537
|
+
const width = pageWidth - 14 - left
|
|
538
|
+
const rowHeight = 4
|
|
539
|
+
const rowGap = 2
|
|
540
|
+
const bottom = pageHeight - 15
|
|
541
|
+
|
|
542
|
+
const drawAxis = (y) => {
|
|
543
|
+
doc.setFontSize(7)
|
|
544
|
+
doc.setTextColor(80, 80, 80)
|
|
545
|
+
for (let h = 0; h <= 24; h += 2) {
|
|
546
|
+
doc.text(String(h).padStart(2, '0') + 'h', left + h / 24 * width, y, { align: 'center' })
|
|
547
|
+
}
|
|
548
|
+
return y + 2
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
let y = doc.lastAutoTable.finalY + 10
|
|
552
|
+
if (y + 20 > bottom) {
|
|
553
|
+
doc.addPage()
|
|
554
|
+
y = 20
|
|
555
|
+
}
|
|
556
|
+
doc.setFillColor(103, 194, 58)
|
|
557
|
+
doc.rect(labelX, y - 2.5, 3, 3, 'F')
|
|
558
|
+
doc.setFontSize(8)
|
|
559
|
+
doc.setTextColor(0, 0, 0)
|
|
560
|
+
doc.text(translations.vehicleTable.moving_vehicles, labelX + 4, y)
|
|
561
|
+
doc.setFillColor(220, 220, 220)
|
|
562
|
+
doc.rect(labelX + 30, y - 2.5, 3, 3, 'F')
|
|
563
|
+
doc.text(translations.vehicleTable.stopped_vehicles, labelX + 34, y)
|
|
564
|
+
y = drawAxis(y + 6)
|
|
565
|
+
|
|
566
|
+
summary.forEach(s => {
|
|
567
|
+
if (y + rowHeight > bottom) {
|
|
568
|
+
doc.addPage()
|
|
569
|
+
y = drawAxis(20)
|
|
570
|
+
}
|
|
571
|
+
doc.setFontSize(7)
|
|
572
|
+
doc.setTextColor(0, 0, 0)
|
|
573
|
+
doc.text(new Date(s.date).toLocaleDateString() + ' - ' + weekDays[new Date(s.date).getDay()], labelX, y + rowHeight - 0.8)
|
|
574
|
+
doc.setFillColor(220, 220, 220)
|
|
575
|
+
doc.rect(left, y, width, rowHeight, 'F')
|
|
576
|
+
doc.setFillColor(103, 194, 58)
|
|
577
|
+
for (const a of (s.activity || [])) {
|
|
578
|
+
doc.rect(left + a.start / 24 * width, y, Math.max((a.end - a.start) / 24 * width, 0.3), rowHeight, 'F')
|
|
579
|
+
}
|
|
580
|
+
y += rowHeight + rowGap
|
|
511
581
|
})
|
|
512
582
|
}
|
|
513
583
|
|
package/src/index.js
CHANGED
|
@@ -240,5 +240,15 @@ function Reports (config, axios) {
|
|
|
240
240
|
this.sensorReportToPDF = (userData, reportData) => {
|
|
241
241
|
return require('./sensor-report').exportToPDF(userData, reportData)
|
|
242
242
|
}
|
|
243
|
+
|
|
244
|
+
this.rpmReport = (from, to, userData) => {
|
|
245
|
+
return require('./rpm-report').createRPMReport(from, to, userData, this.traccar)
|
|
246
|
+
}
|
|
247
|
+
this.rpmReportToPDF = (userData, reportData) => {
|
|
248
|
+
return require('./rpm-report').exportRPMReportToPDF(userData, reportData)
|
|
249
|
+
}
|
|
250
|
+
this.rpmReportToExcel = (userData, reportData) => {
|
|
251
|
+
return require('./rpm-report').exportRPMReportToExcel(userData, reportData)
|
|
252
|
+
}
|
|
243
253
|
}
|
|
244
254
|
module.exports = Reports
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const jsPDF = require('jspdf')
|
|
2
|
+
require('jspdf-autotable')
|
|
3
|
+
const { getStyle } = require('./reportStyle')
|
|
4
|
+
const { headerFromUser, addTable, pdfPageByDay } = require('./util/pdfDocument')
|
|
5
|
+
const { getUserPartner } = require('fleetmap-partners')
|
|
6
|
+
const traccar = require('./util/traccar')
|
|
7
|
+
const { getDates, getTranslations, convertMS } = require('./util/utils')
|
|
8
|
+
const { devicesToProcess } = require('./util/device')
|
|
9
|
+
const automaticReports = require('./automaticReports')
|
|
10
|
+
const { calculateRPMSections } = require('./partnerReports/performance-report')
|
|
11
|
+
|
|
12
|
+
const fileName = 'RPMReport'
|
|
13
|
+
const defaultThreshold = 2500
|
|
14
|
+
|
|
15
|
+
async function createRPMReport (from, to, userData, traccarInstance) {
|
|
16
|
+
console.log('Create RPMReport', from, to)
|
|
17
|
+
const allDevices = devicesToProcess(userData)
|
|
18
|
+
|
|
19
|
+
const allData = {
|
|
20
|
+
devices: [],
|
|
21
|
+
from,
|
|
22
|
+
to
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const dates = getDates(from, to, userData.user.attributes.timezone || getUserPartner(userData.user).timezone)
|
|
26
|
+
|
|
27
|
+
let deviceCount = 0
|
|
28
|
+
const sliced = automaticReports.sliceArray(allDevices, 20)
|
|
29
|
+
for (const devices of sliced) {
|
|
30
|
+
const { route } = await traccar.getAllInOne(traccarInstance, from, to, devices, true, false, false, false, deviceCount, allDevices.length, 10, 1)
|
|
31
|
+
for (const d of devices) {
|
|
32
|
+
const deviceRoute = route.filter(p => p.deviceId === d.id && p.attributes.rpm !== undefined)
|
|
33
|
+
if (deviceRoute.length) {
|
|
34
|
+
allData.devices.push(processDevice(d, deviceRoute, dates))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
deviceCount += devices.length
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return [allData]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getThreshold (device) {
|
|
44
|
+
return Number(device.attributes.highRPM) || defaultThreshold
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function dayKey (date) {
|
|
48
|
+
return date.getFullYear() + '-' + date.getMonth() + '-' + date.getDate()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// time between consecutive positions that are both above the threshold, attributed to the day of the first one
|
|
52
|
+
function processDevice (device, deviceRoute, dates) {
|
|
53
|
+
const threshold = getThreshold(device)
|
|
54
|
+
const days = dates.map(date => ({ date, time: 0 }))
|
|
55
|
+
calculateRPMSections(deviceRoute, threshold).forEach(section => {
|
|
56
|
+
for (let i = 1; i < section.length; i++) {
|
|
57
|
+
const start = new Date(section[i - 1].fixTime)
|
|
58
|
+
const duration = new Date(section[i].fixTime).getTime() - start.getTime()
|
|
59
|
+
const day = days.find(a => dayKey(a.date) === dayKey(start))
|
|
60
|
+
if (day) {
|
|
61
|
+
day.time += duration
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
return {
|
|
66
|
+
device,
|
|
67
|
+
threshold,
|
|
68
|
+
time: days.reduce((a, b) => a + b.time, 0),
|
|
69
|
+
days
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function exportRPMReportToPDF (userData, reportData) {
|
|
74
|
+
const translations = getTranslations(userData)
|
|
75
|
+
const weekDays = getWeekDays(translations)
|
|
76
|
+
const style = getStyle(getUserPartner(userData.user))
|
|
77
|
+
|
|
78
|
+
const orientation = userData.groupByDay ? 'p' : 'l'
|
|
79
|
+
// eslint-disable-next-line new-cap
|
|
80
|
+
const doc = new jsPDF.jsPDF(orientation)
|
|
81
|
+
await headerFromUser(doc, translations.report.titleRPMReport, userData.user, orientation)
|
|
82
|
+
|
|
83
|
+
if (userData.groupByDay) {
|
|
84
|
+
const headers = [translations.report.date, translations.report.timeAboveRPM]
|
|
85
|
+
reportData.devices.forEach((d, index) => {
|
|
86
|
+
pdfPageByDay(userData, d, index, doc, translations, reportData)
|
|
87
|
+
const data = d.days.map(day => [
|
|
88
|
+
new Date(day.date).toLocaleDateString() + ' - ' + weekDays[new Date(day.date).getDay()],
|
|
89
|
+
convertMS(day.time)
|
|
90
|
+
])
|
|
91
|
+
const footValues = [translations.report.rpmThreshold + ': ' + d.threshold, convertMS(d.time)]
|
|
92
|
+
addTable(doc, headers, data, footValues, style, 40, { 1: { halign: 'right' } })
|
|
93
|
+
})
|
|
94
|
+
} else {
|
|
95
|
+
doc.setFontSize(11)
|
|
96
|
+
doc.text(new Date(reportData.from).toLocaleString() + ' - ' + new Date(reportData.to).toLocaleString(), 20, 33)
|
|
97
|
+
const headers = [
|
|
98
|
+
translations.report.vehicle,
|
|
99
|
+
translations.settings.vehicle_licenseplate,
|
|
100
|
+
translations.report.group,
|
|
101
|
+
translations.report.rpmThreshold,
|
|
102
|
+
translations.report.timeAboveRPM
|
|
103
|
+
]
|
|
104
|
+
const data = reportData.devices.map(d => {
|
|
105
|
+
const group = userData.groups.find(g => d.device.groupId === g.id)
|
|
106
|
+
return [
|
|
107
|
+
d.device.name,
|
|
108
|
+
d.device.attributes.license_plate || '',
|
|
109
|
+
group ? group.name : '',
|
|
110
|
+
d.threshold,
|
|
111
|
+
convertMS(d.time)
|
|
112
|
+
]
|
|
113
|
+
})
|
|
114
|
+
const footValues = [
|
|
115
|
+
'Total:' + reportData.devices.length,
|
|
116
|
+
'',
|
|
117
|
+
'',
|
|
118
|
+
'',
|
|
119
|
+
convertMS(reportData.devices.reduce((a, b) => a + b.time, 0))
|
|
120
|
+
]
|
|
121
|
+
addTable(doc, headers, data, footValues, style, 35, { 3: { halign: 'right' }, 4: { halign: 'right' } })
|
|
122
|
+
}
|
|
123
|
+
return doc
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function exportRPMReportToExcel (userData, reportData) {
|
|
127
|
+
const translations = getTranslations(userData)
|
|
128
|
+
const weekDays = getWeekDays(translations)
|
|
129
|
+
|
|
130
|
+
const settings = {
|
|
131
|
+
sheetName: translations.report.titleRPMReport.substring(0, 31),
|
|
132
|
+
fileName
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const headers = [
|
|
136
|
+
{ label: translations.report.vehicle, value: 'name' },
|
|
137
|
+
{ label: translations.settings.vehicle_licenseplate, value: 'licenseplate' },
|
|
138
|
+
{ label: translations.report.group, value: 'group' },
|
|
139
|
+
{ label: translations.report.rpmThreshold, value: 'threshold' }
|
|
140
|
+
]
|
|
141
|
+
if (userData.groupByDay) {
|
|
142
|
+
headers.push(
|
|
143
|
+
{ label: translations.report.date, value: 'date' },
|
|
144
|
+
{ label: translations.report.weekDay, value: 'weekday' })
|
|
145
|
+
}
|
|
146
|
+
headers.push({ label: translations.report.timeAboveRPM, value: 'time' })
|
|
147
|
+
|
|
148
|
+
const data = []
|
|
149
|
+
reportData.devices.forEach(d => {
|
|
150
|
+
const group = userData.groups.find(g => d.device.groupId === g.id)
|
|
151
|
+
const row = {
|
|
152
|
+
name: d.device.name,
|
|
153
|
+
licenseplate: d.device.attributes.license_plate,
|
|
154
|
+
group: group ? group.name : '',
|
|
155
|
+
threshold: d.threshold
|
|
156
|
+
}
|
|
157
|
+
if (userData.groupByDay) {
|
|
158
|
+
data.push(...d.days.map(day => ({
|
|
159
|
+
...row,
|
|
160
|
+
date: new Date(day.date).toLocaleDateString(),
|
|
161
|
+
weekday: weekDays[new Date(day.date).getDay()],
|
|
162
|
+
time: convertMS(day.time)
|
|
163
|
+
})))
|
|
164
|
+
} else {
|
|
165
|
+
data.push({ ...row, time: convertMS(d.time) })
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
headers,
|
|
171
|
+
data,
|
|
172
|
+
settings
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function getWeekDays (translations) {
|
|
177
|
+
return [
|
|
178
|
+
translations.report.sunday,
|
|
179
|
+
translations.report.monday,
|
|
180
|
+
translations.report.tuesday,
|
|
181
|
+
translations.report.wednesday,
|
|
182
|
+
translations.report.thursday,
|
|
183
|
+
translations.report.friday,
|
|
184
|
+
translations.report.saturday
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
exports.processDevice = processDevice
|
|
189
|
+
exports.createRPMReport = createRPMReport
|
|
190
|
+
exports.exportRPMReportToPDF = exportRPMReportToPDF
|
|
191
|
+
exports.exportRPMReportToExcel = exportRPMReportToExcel
|
|
192
|
+
exports.create = createRPMReport
|
|
193
|
+
exports.exportToPDF = exportRPMReportToPDF
|
|
194
|
+
exports.exportToExcel = exportRPMReportToExcel
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
const assert = require('assert')
|
|
3
|
+
const { processDevice } = require('../rpm-report')
|
|
4
|
+
|
|
5
|
+
function position (fixTime, rpm) {
|
|
6
|
+
return { fixTime: new Date(fixTime).toISOString(), attributes: { rpm } }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe('RPM_Report', function () {
|
|
10
|
+
const dates = [new Date(2026, 9, 1, 12), new Date(2026, 9, 2, 12)]
|
|
11
|
+
const route = [
|
|
12
|
+
position(new Date(2026, 9, 1, 10, 0), 2000),
|
|
13
|
+
position(new Date(2026, 9, 1, 10, 1), 2600),
|
|
14
|
+
position(new Date(2026, 9, 1, 10, 2), 2700),
|
|
15
|
+
position(new Date(2026, 9, 1, 10, 4), 3000),
|
|
16
|
+
position(new Date(2026, 9, 1, 10, 5), 1500),
|
|
17
|
+
position(new Date(2026, 9, 2, 9, 0), 2600),
|
|
18
|
+
position(new Date(2026, 9, 2, 9, 0, 30), 2600)
|
|
19
|
+
]
|
|
20
|
+
it('sums time above the default threshold per day', () => {
|
|
21
|
+
const result = processDevice({ id: 1, attributes: {} }, route, dates)
|
|
22
|
+
assert.equal(result.threshold, 2500)
|
|
23
|
+
assert.equal(result.days[0].time, 3 * 60 * 1000)
|
|
24
|
+
assert.equal(result.days[1].time, 30 * 1000)
|
|
25
|
+
assert.equal(result.time, 3 * 60 * 1000 + 30 * 1000)
|
|
26
|
+
})
|
|
27
|
+
it('uses the device highRPM attribute as threshold', () => {
|
|
28
|
+
const result = processDevice({ id: 1, attributes: { highRPM: 2650 } }, route, dates)
|
|
29
|
+
assert.equal(result.threshold, 2650)
|
|
30
|
+
assert.equal(result.days[0].time, 2 * 60 * 1000)
|
|
31
|
+
assert.equal(result.days[1].time, 0)
|
|
32
|
+
})
|
|
33
|
+
})
|