@barchart/portfolio-api-common 1.7.0 → 1.10.0
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/.releases/1.10.0.md +3 -0
- package/.releases/1.8.0.md +3 -0
- package/.releases/1.9.0.md +4 -0
- package/.releases/1.9.1.md +4 -0
- package/lib/api/failures/PortfolioFailureType.js +38 -1
- package/lib/serialization/TransactionSchema.js +4 -2
- package/package.json +1 -1
- package/test/SpecRunner.js +62 -23
- package/test/specs/data/PositionSummaryFrameSpec.js +11 -11
|
@@ -154,6 +154,17 @@ module.exports = (() => {
|
|
|
154
154
|
return transactionCreateFailedInvalidInitialType;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
/**
|
|
158
|
+
* A transaction quantity cannot have a negative amount.
|
|
159
|
+
*
|
|
160
|
+
* @public
|
|
161
|
+
* @static
|
|
162
|
+
* @returns {FailureType}
|
|
163
|
+
*/
|
|
164
|
+
static get TRANSACTION_CREATE_FAILED_QUANTITY_NEGATIVE() {
|
|
165
|
+
return transactionCreateFailedQuantityNegative;
|
|
166
|
+
}
|
|
167
|
+
|
|
157
168
|
/**
|
|
158
169
|
* A valuation transaction cannot have a negative rate (or amount).
|
|
159
170
|
*
|
|
@@ -349,6 +360,28 @@ module.exports = (() => {
|
|
|
349
360
|
return transactionEditFailedTypeChanged;
|
|
350
361
|
}
|
|
351
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Conversion of transaction type is unsupported.
|
|
365
|
+
*
|
|
366
|
+
* @public
|
|
367
|
+
* @static
|
|
368
|
+
* @returns {FailureType}
|
|
369
|
+
*/
|
|
370
|
+
static get TRANSACTION_SWITCH_FAILED_INVALID_CONVERSION() {
|
|
371
|
+
return transactionSwitchFailedInvalidConversion;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Conversion of transaction type is not allowed. Dividends (or distributions)
|
|
376
|
+
* cannot be reinvested when the position is short.
|
|
377
|
+
*
|
|
378
|
+
* @public
|
|
379
|
+
* @static
|
|
380
|
+
* @returns {FailureType}
|
|
381
|
+
*/
|
|
382
|
+
static get TRANSACTION_SWITCH_FAILED_INVALID_REINVEST() {
|
|
383
|
+
return transactionSwitchFailedInvalidReinvest;
|
|
384
|
+
}
|
|
352
385
|
|
|
353
386
|
toString() {
|
|
354
387
|
return '[PortfolioFailureType]';
|
|
@@ -370,6 +403,7 @@ module.exports = (() => {
|
|
|
370
403
|
const transactionCreateFailedTypeInvalidForDirection = new FailureType('TRANSACTION_CREATE_FAILED_TYPE_INVALID_FOR_DIRECTION', 'Unable to process transaction, a {L|positionDirection.description} position would be created (i.e. you would have {L|positionDirection.sign} shares/units). {u|instrumentType.description} positions cannot have {L|positionDirection.description} positions.', false);
|
|
371
404
|
const transactionCreateFailedInvalidDirectionSwitch = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_DIRECTION_SWITCH', 'Unable to process transaction, the transaction would switch the position from {L|currentDirection.description} to {L|proposedDirection.description} (i.e. {L|currentDirection.sign} to {L|proposedDirection.sign} shares/units). This is not allowed. Please close the current position (i.e. zero it out) and then enter a second transaction.', false);
|
|
372
405
|
const transactionCreateFailedInvalidInitialType = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_INITIAL_TYPE', 'Unable to process operation because the first transaction would to be a {U|transactionType.description}, which is not allowed -- since {U|transactionType.description} transactions cannot open a position.', false);
|
|
406
|
+
const transactionCreateFailedQuantityNegative = new FailureType('TRANSACTION_CREATE_FAILED_QUANTITY_NEGATIVE', 'Unable to process transaction, quantity cannot be negative.');
|
|
373
407
|
const transactionCreateFailedValuationNegative = new FailureType('TRANSACTION_CREATE_FAILED_VALUATION_NEGATIVE', 'Unable to process operation, valuations cannot be negative.', false);
|
|
374
408
|
const transactionCreateFailedInvalidTermination = new FailureType('TRANSACTION_CREATE_FAILED_INVALID_TERMINATION', 'Unable to process operation, a {U|transactionType.description} must be the final transaction in the position history.', false);
|
|
375
409
|
const transactionCreateFailedAfterTermination = new FailureType('TRANSACTION_CREATE_FAILED_AFTER_TERMINATION', 'Unable to process operation, one or more transactions would exist after {L|termination}, the final day of trading for this instrument', false);
|
|
@@ -389,7 +423,10 @@ module.exports = (() => {
|
|
|
389
423
|
const transactionEditFailedInvalidDate = new FailureType('TRANSACTION_EDIT_FAILED_INVALID_DATE', 'Unable to edit transaction with given date.');
|
|
390
424
|
const transactionEditFailedNoTransaction = new FailureType('TRANSACTION_EDIT_FAILED_NO_TRANSACTION', 'Unable to edit transaction. The referenced transaction does not exist.', false);
|
|
391
425
|
const transactionEditFailedTypeReserved = new FailureType('TRANSACTION_EDIT_FAILED_TYPE_RESERVED', 'Unable to edit {U|type.description} transaction, this type of transaction is managed by the system.');
|
|
392
|
-
const transactionEditFailedTypeChanged = new FailureType('TRANSACTION_EDIT_FAILED_TYPE_CHANGED', 'Changing a transaction type is forbidden. You must delete the existing transaction then
|
|
426
|
+
const transactionEditFailedTypeChanged = new FailureType('TRANSACTION_EDIT_FAILED_TYPE_CHANGED', 'Changing a transaction type is forbidden. You must delete the existing transaction and then create a new transaction.');
|
|
427
|
+
|
|
428
|
+
const transactionSwitchFailedInvalidConversion = new FailureType('TRANSACTION_SWITCH_FAILED_INVALID_CONVERSION', 'Unable to convert transaction from {U|existing.description} to {U|desired.description}. This conversion is not supported.');
|
|
429
|
+
const transactionSwitchFailedInvalidReinvest = new FailureType('TRANSACTION_SWITCH_FAILED_INVALID_REINVEST', 'Unable to convert transaction from {U|existing.description} to {U|desired.description}. Reinvestment is not supported for short positions.');
|
|
393
430
|
|
|
394
431
|
return PortfolioFailureType;
|
|
395
432
|
})();
|
|
@@ -247,9 +247,10 @@ module.exports = (() => {
|
|
|
247
247
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
248
248
|
.withField('date', DataType.DAY)
|
|
249
249
|
.withField('price', DataType.DECIMAL, true)
|
|
250
|
-
.withField('quantity', DataType.DECIMAL)
|
|
250
|
+
.withField('quantity', DataType.DECIMAL, true)
|
|
251
251
|
.withField('fee', DataType.DECIMAL, true)
|
|
252
252
|
.withField('force', DataType.BOOLEAN, true)
|
|
253
|
+
.withField('close', DataType.BOOLEAN, true)
|
|
253
254
|
.schema
|
|
254
255
|
);
|
|
255
256
|
|
|
@@ -260,9 +261,10 @@ module.exports = (() => {
|
|
|
260
261
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
261
262
|
.withField('date', DataType.DAY)
|
|
262
263
|
.withField('price', DataType.DECIMAL)
|
|
263
|
-
.withField('quantity', DataType.DECIMAL)
|
|
264
|
+
.withField('quantity', DataType.DECIMAL, true)
|
|
264
265
|
.withField('fee', DataType.DECIMAL, true)
|
|
265
266
|
.withField('force', DataType.BOOLEAN, true)
|
|
267
|
+
.withField('close', DataType.BOOLEAN, true)
|
|
266
268
|
.schema
|
|
267
269
|
);
|
|
268
270
|
|
package/package.json
CHANGED
package/test/SpecRunner.js
CHANGED
|
@@ -5456,9 +5456,10 @@ module.exports = (() => {
|
|
|
5456
5456
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
5457
5457
|
.withField('date', DataType.DAY)
|
|
5458
5458
|
.withField('price', DataType.DECIMAL, true)
|
|
5459
|
-
.withField('quantity', DataType.DECIMAL)
|
|
5459
|
+
.withField('quantity', DataType.DECIMAL, true)
|
|
5460
5460
|
.withField('fee', DataType.DECIMAL, true)
|
|
5461
5461
|
.withField('force', DataType.BOOLEAN, true)
|
|
5462
|
+
.withField('close', DataType.BOOLEAN, true)
|
|
5462
5463
|
.schema
|
|
5463
5464
|
);
|
|
5464
5465
|
|
|
@@ -5469,9 +5470,10 @@ module.exports = (() => {
|
|
|
5469
5470
|
.withField('type', DataType.forEnum(TransactionType, 'TransactionType'))
|
|
5470
5471
|
.withField('date', DataType.DAY)
|
|
5471
5472
|
.withField('price', DataType.DECIMAL)
|
|
5472
|
-
.withField('quantity', DataType.DECIMAL)
|
|
5473
|
+
.withField('quantity', DataType.DECIMAL, true)
|
|
5473
5474
|
.withField('fee', DataType.DECIMAL, true)
|
|
5474
5475
|
.withField('force', DataType.BOOLEAN, true)
|
|
5476
|
+
.withField('close', DataType.BOOLEAN, true)
|
|
5475
5477
|
.schema
|
|
5476
5478
|
);
|
|
5477
5479
|
|
|
@@ -5692,7 +5694,7 @@ module.exports = (() => {
|
|
|
5692
5694
|
|
|
5693
5695
|
|
|
5694
5696
|
push(item) {
|
|
5695
|
-
this._array.
|
|
5697
|
+
this._array.push(item);
|
|
5696
5698
|
|
|
5697
5699
|
return item;
|
|
5698
5700
|
}
|
|
@@ -5709,7 +5711,7 @@ module.exports = (() => {
|
|
|
5709
5711
|
throw new Error('Stack is empty');
|
|
5710
5712
|
}
|
|
5711
5713
|
|
|
5712
|
-
return this._array.
|
|
5714
|
+
return this._array.pop();
|
|
5713
5715
|
}
|
|
5714
5716
|
/**
|
|
5715
5717
|
* Returns the next item in the stack (without removing it). Throws if the stack is empty.
|
|
@@ -5724,7 +5726,7 @@ module.exports = (() => {
|
|
|
5724
5726
|
throw new Error('Stack is empty');
|
|
5725
5727
|
}
|
|
5726
5728
|
|
|
5727
|
-
return this._array[
|
|
5729
|
+
return this._array[this._array.length - 1];
|
|
5728
5730
|
}
|
|
5729
5731
|
/**
|
|
5730
5732
|
* Returns true if the queue is empty; otherwise false.
|
|
@@ -5748,10 +5750,12 @@ module.exports = (() => {
|
|
|
5748
5750
|
scan(action) {
|
|
5749
5751
|
assert.argumentIsRequired(action, 'action', Function);
|
|
5750
5752
|
|
|
5751
|
-
this._array.
|
|
5753
|
+
for (let i = this._array.length - 1; i >= 0; i--) {
|
|
5754
|
+
action(this._array[i]);
|
|
5755
|
+
}
|
|
5752
5756
|
}
|
|
5753
5757
|
/**
|
|
5754
|
-
* Outputs an array of the
|
|
5758
|
+
* Outputs an array of the stack's items; without affecting the
|
|
5755
5759
|
* queue's internal state;
|
|
5756
5760
|
*
|
|
5757
5761
|
* @public
|
|
@@ -5760,7 +5764,7 @@ module.exports = (() => {
|
|
|
5760
5764
|
|
|
5761
5765
|
|
|
5762
5766
|
toArray() {
|
|
5763
|
-
return this._array.slice(0);
|
|
5767
|
+
return this._array.slice(0).reverse();
|
|
5764
5768
|
}
|
|
5765
5769
|
|
|
5766
5770
|
toString() {
|
|
@@ -8203,8 +8207,11 @@ const moment = require('moment-timezone');
|
|
|
8203
8207
|
|
|
8204
8208
|
module.exports = (() => {
|
|
8205
8209
|
'use strict';
|
|
8210
|
+
|
|
8211
|
+
const MILLISECONDS_PER_SECOND = 1000;
|
|
8206
8212
|
/**
|
|
8207
|
-
*
|
|
8213
|
+
* An immutable data structure that encapsulates (and lazy loads)
|
|
8214
|
+
* a moment (see https://momentjs.com/).
|
|
8208
8215
|
*
|
|
8209
8216
|
* @public
|
|
8210
8217
|
* @param {Number} timestamp
|
|
@@ -8220,7 +8227,7 @@ module.exports = (() => {
|
|
|
8220
8227
|
this._moment = null;
|
|
8221
8228
|
}
|
|
8222
8229
|
/**
|
|
8223
|
-
* The timestamp.
|
|
8230
|
+
* The timestamp (milliseconds since epoch).
|
|
8224
8231
|
*
|
|
8225
8232
|
* @public
|
|
8226
8233
|
* @returns {Number}
|
|
@@ -8249,6 +8256,34 @@ module.exports = (() => {
|
|
|
8249
8256
|
|
|
8250
8257
|
return this._moment;
|
|
8251
8258
|
}
|
|
8259
|
+
/**
|
|
8260
|
+
* Returns a new {@link Timestamp} instance shifted forward (or backward)
|
|
8261
|
+
* by a specific number of seconds.
|
|
8262
|
+
*
|
|
8263
|
+
* @public
|
|
8264
|
+
* @param {Number} milliseconds
|
|
8265
|
+
* @returns {Timestamp}
|
|
8266
|
+
*/
|
|
8267
|
+
|
|
8268
|
+
|
|
8269
|
+
add(milliseconds) {
|
|
8270
|
+
assert.argumentIsRequired(milliseconds, 'seconds', Number);
|
|
8271
|
+
return new Timestamp(this._timestamp + milliseconds, this._timezone);
|
|
8272
|
+
}
|
|
8273
|
+
/**
|
|
8274
|
+
* Returns a new {@link Timestamp} instance shifted forward (or backward)
|
|
8275
|
+
* by a specific number of seconds.
|
|
8276
|
+
*
|
|
8277
|
+
* @public
|
|
8278
|
+
* @param {Number} seconds
|
|
8279
|
+
* @returns {Timestamp}
|
|
8280
|
+
*/
|
|
8281
|
+
|
|
8282
|
+
|
|
8283
|
+
addSeconds(seconds) {
|
|
8284
|
+
assert.argumentIsRequired(seconds, 'seconds', Number);
|
|
8285
|
+
return this.add(seconds * MILLISECONDS_PER_SECOND);
|
|
8286
|
+
}
|
|
8252
8287
|
/**
|
|
8253
8288
|
* Returns the JSON representation.
|
|
8254
8289
|
*
|
|
@@ -8550,7 +8585,8 @@ module.exports = (() => {
|
|
|
8550
8585
|
},
|
|
8551
8586
|
|
|
8552
8587
|
/**
|
|
8553
|
-
* Set difference operation
|
|
8588
|
+
* Set difference operation, returning any item in "a" that is not
|
|
8589
|
+
* contained in "b" (using strict equality).
|
|
8554
8590
|
*
|
|
8555
8591
|
* @static
|
|
8556
8592
|
* @param {Array} a
|
|
@@ -8562,7 +8598,8 @@ module.exports = (() => {
|
|
|
8562
8598
|
},
|
|
8563
8599
|
|
|
8564
8600
|
/**
|
|
8565
|
-
* Set difference operation,
|
|
8601
|
+
* Set difference operation, returning any item in "a" that is not
|
|
8602
|
+
* contained in "b" (where the uniqueness is determined by a delegate).
|
|
8566
8603
|
*
|
|
8567
8604
|
* @static
|
|
8568
8605
|
* @param {Array} a
|
|
@@ -9465,6 +9502,7 @@ module.exports = (() => {
|
|
|
9465
9502
|
/**
|
|
9466
9503
|
* An implementation of the observer pattern.
|
|
9467
9504
|
*
|
|
9505
|
+
* @public
|
|
9468
9506
|
* @param {*} sender - The object which owns the event.
|
|
9469
9507
|
* @extends {Disposable}
|
|
9470
9508
|
*/
|
|
@@ -9538,6 +9576,7 @@ module.exports = (() => {
|
|
|
9538
9576
|
/**
|
|
9539
9577
|
* Returns true, if no handlers are currently registered.
|
|
9540
9578
|
*
|
|
9579
|
+
* @public
|
|
9541
9580
|
* @returns {boolean}
|
|
9542
9581
|
*/
|
|
9543
9582
|
|
|
@@ -17584,8 +17623,8 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
17584
17623
|
ranges = PositionSummaryFrame.YEARLY.getRanges(transactions);
|
|
17585
17624
|
});
|
|
17586
17625
|
|
|
17587
|
-
it('should have
|
|
17588
|
-
expect(ranges.length).toEqual(
|
|
17626
|
+
it('should have six ranges (assuming the current year is 2021)', () => {
|
|
17627
|
+
expect(ranges.length).toEqual(6);
|
|
17589
17628
|
});
|
|
17590
17629
|
|
|
17591
17630
|
it('the first range should be from 12-31-2014 to 12-31-2015', () => {
|
|
@@ -17971,7 +18010,7 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
17971
18010
|
});
|
|
17972
18011
|
});
|
|
17973
18012
|
|
|
17974
|
-
describe('and a year-to-date position summary ranges are processed for a transaction set that opened last year and has not yet closed (assuming its
|
|
18013
|
+
describe('and a year-to-date position summary ranges are processed for a transaction set that opened last year and has not yet closed (assuming its 2021)', () => {
|
|
17975
18014
|
let ranges;
|
|
17976
18015
|
|
|
17977
18016
|
beforeEach(() => {
|
|
@@ -17992,9 +18031,9 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
17992
18031
|
expect(ranges.length).toEqual(1);
|
|
17993
18032
|
});
|
|
17994
18033
|
|
|
17995
|
-
it('the first range should be from 12-31-
|
|
17996
|
-
expect(ranges[0].start.format()).toEqual('
|
|
17997
|
-
expect(ranges[0].end.format()).toEqual('
|
|
18034
|
+
it('the first range should be from 12-31-2020 to 12-31-2021', () => {
|
|
18035
|
+
expect(ranges[0].start.format()).toEqual('2020-12-31');
|
|
18036
|
+
expect(ranges[0].end.format()).toEqual('2021-12-31');
|
|
17998
18037
|
});
|
|
17999
18038
|
});
|
|
18000
18039
|
|
|
@@ -18004,14 +18043,14 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
18004
18043
|
beforeEach(() => {
|
|
18005
18044
|
const transactions = [
|
|
18006
18045
|
{
|
|
18007
|
-
date: new Day(
|
|
18046
|
+
date: new Day(2021, 1, 1),
|
|
18008
18047
|
snapshot: {
|
|
18009
18048
|
open: new Decimal(1)
|
|
18010
18049
|
},
|
|
18011
18050
|
type: TransactionType.BUY
|
|
18012
18051
|
},
|
|
18013
18052
|
{
|
|
18014
|
-
date: new Day(
|
|
18053
|
+
date: new Day(2021, 1, 2),
|
|
18015
18054
|
snapshot: {
|
|
18016
18055
|
open: new Decimal(0)
|
|
18017
18056
|
},
|
|
@@ -18026,9 +18065,9 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
18026
18065
|
expect(ranges.length).toEqual(1);
|
|
18027
18066
|
});
|
|
18028
18067
|
|
|
18029
|
-
it('the first range should be from 12-31-
|
|
18030
|
-
expect(ranges[0].start.format()).toEqual('
|
|
18031
|
-
expect(ranges[0].end.format()).toEqual('
|
|
18068
|
+
it('the first range should be from 12-31-2020 to 12-31-2021', () => {
|
|
18069
|
+
expect(ranges[0].start.format()).toEqual('2020-12-31');
|
|
18070
|
+
expect(ranges[0].end.format()).toEqual('2021-12-31');
|
|
18032
18071
|
});
|
|
18033
18072
|
});
|
|
18034
18073
|
|
|
@@ -31,8 +31,8 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
31
31
|
ranges = PositionSummaryFrame.YEARLY.getRanges(transactions);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
it('should have
|
|
35
|
-
expect(ranges.length).toEqual(
|
|
34
|
+
it('should have six ranges (assuming the current year is 2021)', () => {
|
|
35
|
+
expect(ranges.length).toEqual(6);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
it('the first range should be from 12-31-2014 to 12-31-2015', () => {
|
|
@@ -418,7 +418,7 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
418
418
|
});
|
|
419
419
|
});
|
|
420
420
|
|
|
421
|
-
describe('and a year-to-date position summary ranges are processed for a transaction set that opened last year and has not yet closed (assuming its
|
|
421
|
+
describe('and a year-to-date position summary ranges are processed for a transaction set that opened last year and has not yet closed (assuming its 2021)', () => {
|
|
422
422
|
let ranges;
|
|
423
423
|
|
|
424
424
|
beforeEach(() => {
|
|
@@ -439,9 +439,9 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
439
439
|
expect(ranges.length).toEqual(1);
|
|
440
440
|
});
|
|
441
441
|
|
|
442
|
-
it('the first range should be from 12-31-
|
|
443
|
-
expect(ranges[0].start.format()).toEqual('
|
|
444
|
-
expect(ranges[0].end.format()).toEqual('
|
|
442
|
+
it('the first range should be from 12-31-2020 to 12-31-2021', () => {
|
|
443
|
+
expect(ranges[0].start.format()).toEqual('2020-12-31');
|
|
444
|
+
expect(ranges[0].end.format()).toEqual('2021-12-31');
|
|
445
445
|
});
|
|
446
446
|
});
|
|
447
447
|
|
|
@@ -451,14 +451,14 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
451
451
|
beforeEach(() => {
|
|
452
452
|
const transactions = [
|
|
453
453
|
{
|
|
454
|
-
date: new Day(
|
|
454
|
+
date: new Day(2021, 1, 1),
|
|
455
455
|
snapshot: {
|
|
456
456
|
open: new Decimal(1)
|
|
457
457
|
},
|
|
458
458
|
type: TransactionType.BUY
|
|
459
459
|
},
|
|
460
460
|
{
|
|
461
|
-
date: new Day(
|
|
461
|
+
date: new Day(2021, 1, 2),
|
|
462
462
|
snapshot: {
|
|
463
463
|
open: new Decimal(0)
|
|
464
464
|
},
|
|
@@ -473,9 +473,9 @@ describe('After the PositionSummaryFrame enumeration is initialized', () => {
|
|
|
473
473
|
expect(ranges.length).toEqual(1);
|
|
474
474
|
});
|
|
475
475
|
|
|
476
|
-
it('the first range should be from 12-31-
|
|
477
|
-
expect(ranges[0].start.format()).toEqual('
|
|
478
|
-
expect(ranges[0].end.format()).toEqual('
|
|
476
|
+
it('the first range should be from 12-31-2020 to 12-31-2021', () => {
|
|
477
|
+
expect(ranges[0].start.format()).toEqual('2020-12-31');
|
|
478
|
+
expect(ranges[0].end.format()).toEqual('2021-12-31');
|
|
479
479
|
});
|
|
480
480
|
});
|
|
481
481
|
|