@financial-times/n-myft-ui 40.0.2 → 40.1.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.
@@ -3,10 +3,6 @@ orbs:
3
3
  tool-kit: financial-times/dotcom-tool-kit@5
4
4
  executors:
5
5
  node:
6
- docker:
7
- - image: cimg/node:18.20-browsers
8
- working_directory: /home/circleci/project/node_workspace
9
- node22_4-browsers:
10
6
  docker:
11
7
  - image: cimg/node:22.4-browsers
12
8
  working_directory: /home/circleci/project/node22_workspace
@@ -63,7 +59,6 @@ workflows:
63
59
  parameters:
64
60
  executor:
65
61
  - node
66
- - node22_4-browsers
67
62
  filters:
68
63
  tags:
69
64
  only: /^v\d+\.\d+\.\d+(-.+)?/
@@ -75,7 +70,6 @@ workflows:
75
70
  parameters:
76
71
  executor:
77
72
  - node
78
- - node22_4-browsers
79
73
  filters:
80
74
  tags:
81
75
  only: /^v\d+\.\d+\.\d+(-.+)?/
@@ -87,7 +81,6 @@ workflows:
87
81
  parameters:
88
82
  executor:
89
83
  - node
90
- - node22_4-browsers
91
84
  filters:
92
85
  tags:
93
86
  only: /^v\d+\.\d+\.\d+(-.+)?/
@@ -122,7 +115,6 @@ workflows:
122
115
  parameters:
123
116
  executor:
124
117
  - node
125
- - node22_4-browsers
126
118
  - tool-kit/build:
127
119
  name: tool-kit/build-<< matrix.executor >>
128
120
  requires:
@@ -131,7 +123,6 @@ workflows:
131
123
  parameters:
132
124
  executor:
133
125
  - node
134
- - node22_4-browsers
135
126
  - tool-kit/test:
136
127
  name: tool-kit/test-<< matrix.executor >>
137
128
  requires:
@@ -140,4 +131,3 @@ workflows:
140
131
  parameters:
141
132
  executor:
142
133
  - node
143
- - node22_4-browsers
package/.toolkitrc.yml CHANGED
@@ -23,7 +23,6 @@ hooks:
23
23
  options:
24
24
  "@dotcom-tool-kit/circleci":
25
25
  nodeVersion:
26
- - '18.20-browsers'
27
26
  - '22.4-browsers'
28
27
  "@dotcom-tool-kit/eslint": {
29
28
  files: "{,!(public)/**/}*.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "branch": "",
3
3
  "repo": "n-myft-ui",
4
- "version": "da6a59658803a3898eb4a2d02f16561dded775aa",
5
- "tag": "v40.0.2"
4
+ "version": "98257025e278efb50a8bb294bbada6961c55696e",
5
+ "tag": "v40.1.0"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/n-myft-ui",
3
- "version": "40.0.2",
3
+ "version": "40.1.0",
4
4
  "description": "Client side component for interaction with myft",
5
5
  "main": "server.js",
6
6
  "scripts": {
@@ -133,7 +133,7 @@
133
133
  "node": "22.11.0"
134
134
  },
135
135
  "engines": {
136
- "node": "18.x || 22.x"
136
+ "node": "22.x"
137
137
  },
138
138
  "x-dash": {
139
139
  "engine": {
@@ -1,5 +0,0 @@
1
- # n-myft-ui/unread-articles-indicator
2
-
3
- Note: this component is decommissioned but a small function (`getNewArticlesSinceTime`) of it is still in use by next-myft-page to display a "New" label under an article in the Timeline view only.
4
-
5
- All other previous functionality of the component has been removed.
@@ -1,25 +0,0 @@
1
- // date-fns from v2 doesn't accept String arguments anymore.
2
- // the detail => https://github.com/date-fns/date-fns/blob/HEAD/CHANGELOG.md#200---2019-08-20
3
- // By adding validation for dates before their functions allows us to know it when unexpected value passed.
4
-
5
- import isTodayOriginal from 'date-fns/src/isToday';
6
- import isAfterOriginal from 'date-fns/src/isAfter';
7
- import addMinutesOriginal from 'date-fns/src/addMinutes';
8
- import startOfDayOriginal from 'date-fns/src/startOfDay';
9
- import isValidOriginal from 'date-fns/src/isValid';
10
- import parseISO from 'date-fns/src/parseISO';
11
-
12
- const isValid = (date) => {
13
- if (!isValidOriginal(date)) {
14
- console.error("Invalid date passed", [date]); //eslint-disable-line
15
- }
16
- return date;
17
- };
18
-
19
- const isToday = (date) => isTodayOriginal(isValid(date));
20
- const isAfter = (date, dateToCompare) =>
21
- isAfterOriginal(isValid(date), isValid(dateToCompare));
22
- const addMinutes = (date, amount) => addMinutesOriginal(isValid(date), amount);
23
- const startOfDay = (date) => startOfDayOriginal(isValid(date));
24
-
25
- export { isToday, isAfter, addMinutes, startOfDay, isValid, parseISO };
@@ -1,19 +0,0 @@
1
- import { isAfter, addMinutes } from 'date-fns';
2
- import * as storage from './storage';
3
- const SESSION_THRESHOLD_MINUTES = 30;
4
-
5
- export default class DeviceSession {
6
- constructor () {
7
- this.expiry = storage.getDeviceSessionExpiry();
8
- const newExpiry = addMinutes(new Date(), SESSION_THRESHOLD_MINUTES);
9
- storage.setDeviceSessionExpiry(newExpiry);
10
- }
11
-
12
- isNewSession () {
13
- if (this.expiry) {
14
- return isAfter(new Date(), this.expiry);
15
- } else {
16
- return true;
17
- }
18
- }
19
- }
@@ -1,31 +0,0 @@
1
- import { startOfDay } from 'date-fns';
2
- import * as storage from './storage';
3
- import initialiseFeedStartTime from './initialise-feed-start-time';
4
- import sessionClient from 'next-session-client';
5
-
6
- let initialFeedStartTime;
7
- let userId;
8
-
9
- async function getValidSession () {
10
- if (!userId) {
11
- const { uuid } = await sessionClient.uuid();
12
- if (!uuid) throw new Error('No userId');
13
- userId = uuid;
14
- }
15
- return userId;
16
- }
17
-
18
- // Export used in next-myft -page to determine whether to add "New" label to articles in feed
19
- //KEEP: This function is in use in next-myft-page do not delete!
20
- export async function getNewArticlesSinceTime () {
21
- const user = await getValidSession();
22
- const dayStart = startOfDay(new Date());
23
-
24
- if (!initialFeedStartTime && storage.isAvailable()) {
25
- try {
26
- initialFeedStartTime = await initialiseFeedStartTime(user, new Date());
27
- } catch (e) {}
28
- }
29
-
30
- return initialFeedStartTime || dayStart;
31
- }
@@ -1,54 +0,0 @@
1
- import { isToday, startOfDay } from 'date-fns';
2
- import DeviceSession from './device-session';
3
- import * as storage from './storage';
4
- import { json as fetchJson } from 'fetchres';
5
-
6
- const deviceSession = new DeviceSession();
7
-
8
- export const fetchUserLastVisitedAt = (userId) => {
9
- const url = '/__myft/users/:userId/last-seen?source=next-myft'.replace(
10
- ':userId',
11
- userId
12
- );
13
- return fetch(url, { credentials: 'include' })
14
- .then(fetchJson)
15
- .then(({ lastSeen }) => new Date(lastSeen));
16
- };
17
-
18
- /**
19
- * @param {Date} now Date representing the time now
20
- * @param {Date} previousFeedStartTime Date representing the time we last used to determine if articles are new for the user
21
- * @return {Promise<Date>} date when we now determine articles to be 'new' for the user
22
- */
23
- const determineFeedStartTime = (userId, now, previousFeedStartTime) => {
24
- if (
25
- previousFeedStartTime &&
26
- isToday(previousFeedStartTime) &&
27
- !deviceSession.isNewSession()
28
- ) {
29
- return Promise.resolve(previousFeedStartTime);
30
- }
31
-
32
- return fetchUserLastVisitedAt(userId)
33
- .then((userLastVisitedAt) =>
34
- isToday(userLastVisitedAt) ? userLastVisitedAt : Promise.reject(new Error('No last visited user data today'))
35
- )
36
- .catch(() => startOfDay(now));
37
- };
38
-
39
- /**
40
- * Sets/updates the time after which articles must be published to count towards indicator count
41
- * @param {Date} now Date representing the time now
42
- */
43
- export default (userId, now) => {
44
- const previousFeedStartTime =
45
- storage.isAvailable() && storage.getFeedStartTime();
46
- return determineFeedStartTime(userId, now, previousFeedStartTime).then(
47
- (startTime) => {
48
- if (storage.isAvailable()) {
49
- storage.setFeedStartTime(startTime);
50
- }
51
- return startTime;
52
- }
53
- );
54
- };
@@ -1,35 +0,0 @@
1
- import { isValid } from 'date-fns';
2
-
3
- const DEVICE_SESSION_EXPIRY = 'deviceSessionExpiry';
4
- const FEED_START_TIME = 'newArticlesSinceTime';
5
-
6
- const isISOString = (str) => typeof str === 'string' && str.charAt(10) === 'T';
7
- const getStoredDate = (key) => {
8
- const value = window.localStorage.getItem(key);
9
- const date = new Date(value);
10
-
11
- return isISOString(value) && isValid(date) ? date : null;
12
- };
13
-
14
- export const getDeviceSessionExpiry = () =>
15
- getStoredDate(DEVICE_SESSION_EXPIRY);
16
-
17
- export const setDeviceSessionExpiry = (date) =>
18
- window.localStorage.setItem(DEVICE_SESSION_EXPIRY, date.toISOString());
19
-
20
- export const getFeedStartTime = () => getStoredDate(FEED_START_TIME);
21
-
22
- export const setFeedStartTime = (date) =>
23
- window.localStorage.setItem(FEED_START_TIME, date.toISOString());
24
-
25
- export const isAvailable = () => {
26
- try {
27
- const storage = window.localStorage;
28
- const x = '__storage_test__';
29
- storage.setItem(x, x);
30
- storage.removeItem(x);
31
- return true;
32
- } catch (e) {
33
- return false;
34
- }
35
- };
@@ -1,97 +0,0 @@
1
- /* global expect */
2
- import sinon from 'sinon';
3
- import addMinutes from 'date-fns/addMinutes';
4
- import parseISO from 'date-fns/parseISO';
5
-
6
- const expiryTimestamp = parseISO('2018-06-14T12:00:00.000Z');
7
- const timestampBeforeExpiry = '2018-06-14T11:40:00.000Z';
8
- const timestampAfterExpiry = '2018-06-14T12:10:00.000Z';
9
-
10
- const SESSION_THRESHOLD_MINUTES = 30;
11
-
12
- describe('DeviceSession', () => {
13
-
14
- let clock;
15
- let deviceSession;
16
- let deviceSessionExpiry;
17
-
18
- const mockStorage = {
19
- getDeviceSessionExpiry: sinon.stub().callsFake(() => deviceSessionExpiry),
20
- setDeviceSessionExpiry: sinon.spy()
21
- };
22
-
23
- const subjectInjector = require('inject-loader!../../components/unread-articles-indicator/device-session');
24
- const DeviceSession = subjectInjector({
25
- './storage': mockStorage
26
- });
27
-
28
- afterEach(() => {
29
- deviceSessionExpiry = undefined;
30
- clock.restore();
31
- sinon.resetHistory();
32
- });
33
-
34
- describe('constructor:', () => {
35
-
36
- const timeNow = timestampBeforeExpiry;
37
-
38
- beforeEach(() => {
39
- clock = sinon.useFakeTimers(new Date(timeNow));
40
- deviceSessionExpiry = expiryTimestamp;
41
- deviceSession = new DeviceSession();
42
- });
43
-
44
- it('should set this.expiry a timestamp from localStorage', () => {
45
- expect(mockStorage.getDeviceSessionExpiry).to.have.been.calledOnce;
46
- expect(deviceSession.expiry).to.equal(expiryTimestamp);
47
- });
48
-
49
- it('should call storage.setDeviceSessionExpiry with correct timestamp', () => {
50
- const correctTimestamp = addMinutes(parseISO(timeNow), SESSION_THRESHOLD_MINUTES);
51
-
52
- expect(mockStorage.setDeviceSessionExpiry).to.have.been.calledOnce;
53
- expect(mockStorage.setDeviceSessionExpiry).to.have.been.calledWith(correctTimestamp);
54
- });
55
- });
56
-
57
- describe('isNewSession:', () => {
58
-
59
- it('should return true if the user visits for the first time (initially no value in localStorage)', () => {
60
- const timeNow = '2018-06-14T08:00:00.000Z';
61
- const newExpiryTimestamp = addMinutes(parseISO(timeNow), SESSION_THRESHOLD_MINUTES);
62
-
63
- clock = sinon.useFakeTimers(new Date(timeNow));
64
- deviceSessionExpiry = undefined;
65
- deviceSession = new DeviceSession();
66
-
67
- expect(deviceSession.isNewSession()).to.be.true;
68
- expect(mockStorage.setDeviceSessionExpiry).to.have.been.calledWith(newExpiryTimestamp);
69
- });
70
-
71
- it('should return true if the user returns after session (initial value in localStorage is in the past)', () => {
72
- const timeNow = timestampAfterExpiry;
73
- const newExpiryTimestamp = addMinutes(parseISO(timeNow), SESSION_THRESHOLD_MINUTES);
74
-
75
- clock = sinon.useFakeTimers(new Date(timeNow));
76
- deviceSessionExpiry = expiryTimestamp;
77
- deviceSession = new DeviceSession();
78
-
79
- expect(deviceSession.isNewSession()).to.be.true;
80
- expect(mockStorage.setDeviceSessionExpiry).to.have.been.calledWith(newExpiryTimestamp);
81
- });
82
-
83
- it('should return false if the user returns within session (initial value in localStorage is in the future)', () => {
84
- const timeNow = timestampBeforeExpiry;
85
- const newExpiryTimestamp = addMinutes(parseISO(timeNow), SESSION_THRESHOLD_MINUTES);
86
-
87
- clock = sinon.useFakeTimers(new Date(timeNow));
88
- deviceSessionExpiry = expiryTimestamp;
89
- deviceSession = new DeviceSession();
90
-
91
- expect(deviceSession.isNewSession()).to.be.false;
92
- expect(mockStorage.setDeviceSessionExpiry).to.have.been.calledWith(newExpiryTimestamp);
93
- });
94
-
95
- });
96
-
97
- });
@@ -1,43 +0,0 @@
1
- /* global expect */
2
- import sinon from 'sinon';
3
-
4
- const FEED_START_TIME = '2018-06-05T10:00:00.000Z';
5
-
6
- describe('unread stories indicator', () => {
7
- let unreadArticlesComponent;
8
- let mockInitialiseFeedStartTime;
9
- let mockStorage;
10
- let isStorageAvailable;
11
-
12
- beforeEach(() => {
13
- mockStorage = {
14
- getFeedStartTime: sinon.stub().returns(FEED_START_TIME),
15
- setFeedStartTime: sinon.stub(),
16
- isAvailable: sinon.stub().callsFake(() => isStorageAvailable)
17
- };
18
-
19
- mockInitialiseFeedStartTime = sinon.stub().resolves();
20
-
21
- unreadArticlesComponent = require('inject-loader!../../components/unread-articles-indicator')({
22
- 'next-session-client': {
23
- uuid: sinon.stub().resolves({uuid: '00000000-0000-0000-0000-000000000000'})
24
- },
25
- './storage': mockStorage,
26
- './initialise-feed-start-time': mockInitialiseFeedStartTime
27
- });
28
- });
29
-
30
- describe('getNewsArticlesSinceTime', () => {
31
- it('should not do anything if storage is not available', () => {
32
- isStorageAvailable = false;
33
- unreadArticlesComponent.getNewArticlesSinceTime();
34
- expect(mockInitialiseFeedStartTime).to.not.have.been.called;
35
- });
36
-
37
- it('should initialise feed start time', () => {
38
- isStorageAvailable = true;
39
- return unreadArticlesComponent.getNewArticlesSinceTime();
40
- expect(mockInitialiseFeedStartTime).to.have.been.calledOnce;
41
- });
42
- });
43
- });
@@ -1,178 +0,0 @@
1
- /* global expect */
2
-
3
- import sinon from 'sinon';
4
- import fetchMock from 'fetch-mock';
5
-
6
- const mockDateFns = {
7
- isToday (x) {
8
- return x && x.toISOString().startsWith('2018-03-05T');
9
- },
10
- startOfDay (x) {
11
- return new Date(x.toISOString().slice(0,11)+'00:00:00Z');
12
- }
13
- };
14
-
15
- const TIME_NOW = new Date('2018-03-05T16:00:00Z');
16
- const START_OF_TODAY = new Date('2018-03-05T00:00:00Z');
17
- const VISIT_TIME_TODAY = new Date('2018-03-05T14:00:00Z');
18
- const VISIT_TIME_YESTERDAY = new Date('2018-03-04T14:00:00Z');
19
- const FEED_TIME_TODAY = new Date('2018-03-05T12:00:00Z');
20
- const FEED_TIME_YESTERDAY = new Date('2018-03-04T12:00:00Z');
21
- const USER_ID = '00000000-0000-0000-0000-000000000000';
22
-
23
- const mockSetFeedStartTime = sinon.stub();
24
-
25
-
26
- describe('initialiseFeedStartTime', () => {
27
- let isNewSession;
28
- let feedStartTime;
29
- let lastVisitTime;
30
- const injector = require('inject-loader!../../components/unread-articles-indicator/initialise-feed-start-time');
31
- const initialiseFeedStartTime = injector({
32
- 'date-fns': {
33
- isToday: mockDateFns.isToday,
34
- startOfDay: mockDateFns.startOfDay
35
- },
36
- './device-session': () => ({ isNewSession: () => isNewSession }),
37
- './storage': {
38
- setFeedStartTime: mockSetFeedStartTime,
39
- getFeedStartTime: sinon.stub().callsFake( () => feedStartTime ),
40
- isAvailable: sinon.stub().returns(true)
41
- }
42
- }).default;
43
-
44
- const expectStartTime = startTime => expect(mockSetFeedStartTime.firstCall.args[0].toISOString()).equal(startTime.toISOString());
45
-
46
- before(() => {
47
- fetchMock.get(`begin:/__myft/users/${USER_ID}/last-seen`, () => ({
48
- lastSeen: lastVisitTime && lastVisitTime.toISOString()
49
- }));
50
- });
51
- after(() => {
52
- fetchMock.restore();
53
- });
54
-
55
- context('with no previous feed start time', () => {
56
- before(() => {
57
- feedStartTime = undefined;
58
- });
59
- context('and previous visit today', () => {
60
- before(() => {
61
- mockSetFeedStartTime.resetHistory();
62
- lastVisitTime = VISIT_TIME_TODAY;
63
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
64
- });
65
- it('sets start time to visit time', () => {
66
- expectStartTime(VISIT_TIME_TODAY);
67
- });
68
- });
69
- context('and previous visit yesterday', () => {
70
- before(() => {
71
- mockSetFeedStartTime.resetHistory();
72
- lastVisitTime = VISIT_TIME_YESTERDAY;
73
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
74
- });
75
- it('sets start time to start of today', () => {
76
- expectStartTime(START_OF_TODAY);
77
- });
78
- });
79
- context('and no previous visit', () => {
80
- before(() => {
81
- mockSetFeedStartTime.resetHistory();
82
- lastVisitTime = undefined;
83
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
84
- });
85
- it('sets start time to start of today', () => {
86
- expectStartTime(START_OF_TODAY);
87
- });
88
- });
89
- });
90
- context('with previous feed start time today', () => {
91
- before(() => {
92
- feedStartTime = FEED_TIME_TODAY;
93
- });
94
- context('and no new session', () => {
95
- before(() => {
96
- mockSetFeedStartTime.resetHistory();
97
- lastVisitTime = VISIT_TIME_TODAY;
98
- return initialiseFeedStartTime(USER_ID, );
99
- });
100
- it('sets start time to previous feed start time', () => {
101
- expectStartTime(FEED_TIME_TODAY);
102
- });
103
- });
104
- context('and new session started', () => {
105
- before(() => {
106
- isNewSession = true;
107
- });
108
- after(() => {
109
- isNewSession = false;
110
- });
111
- context('and previous visit today', () => {
112
- before(() => {
113
- mockSetFeedStartTime.resetHistory();
114
- lastVisitTime = VISIT_TIME_TODAY;
115
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
116
- });
117
- it('sets start time to visit time', () => {
118
- expectStartTime(VISIT_TIME_TODAY);
119
- });
120
- });
121
- context('and previous visit yesterday', () => {
122
- before(() => {
123
- mockSetFeedStartTime.resetHistory();
124
- lastVisitTime = VISIT_TIME_YESTERDAY;
125
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
126
- });
127
- it('sets start time to start of today', () => {
128
- expectStartTime(START_OF_TODAY);
129
- });
130
- });
131
- context('and no previous visit', () => {
132
- before(() => {
133
- mockSetFeedStartTime.resetHistory();
134
- lastVisitTime = undefined;
135
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
136
- });
137
- it('sets start time to start of today', () => {
138
- expectStartTime(START_OF_TODAY);
139
- });
140
- });
141
- });
142
- });
143
- context('with previous feed start time yesterday', () => {
144
- before(() => {
145
- feedStartTime = FEED_TIME_YESTERDAY;
146
- });
147
- context('and previous visit today', () => {
148
- before(() => {
149
- mockSetFeedStartTime.resetHistory();
150
- lastVisitTime = VISIT_TIME_TODAY;
151
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
152
- });
153
- it('sets start time to visit time', () => {
154
- expectStartTime(VISIT_TIME_TODAY);
155
- });
156
- });
157
- context('and previous visit yesterday', () => {
158
- before(() => {
159
- mockSetFeedStartTime.resetHistory();
160
- lastVisitTime = VISIT_TIME_YESTERDAY;
161
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
162
- });
163
- it('sets start time to start of today', () => {
164
- expectStartTime(START_OF_TODAY);
165
- });
166
- });
167
- context('and no previous visit', () => {
168
- before(() => {
169
- mockSetFeedStartTime.resetHistory();
170
- lastVisitTime = undefined;
171
- return initialiseFeedStartTime(USER_ID, TIME_NOW);
172
- });
173
- it('sets start time to start of today', () => {
174
- expectStartTime(START_OF_TODAY);
175
- });
176
- });
177
- });
178
- });
@@ -1,142 +0,0 @@
1
- /* global expect */
2
-
3
- import sinon from 'sinon';
4
- import * as storage from '../../components/unread-articles-indicator/storage';
5
-
6
- describe('storage', () => {
7
- let clock;
8
- let mockStorage;
9
- let now;
10
-
11
- beforeEach(() => {
12
- mockStorage = {};
13
- now = new Date();
14
- sinon.stub(window.Storage.prototype, 'getItem').callsFake(key => mockStorage[key]);
15
- sinon.stub(window.Storage.prototype, 'setItem').callsFake((key, value) => mockStorage[key] = value);
16
- clock = sinon.useFakeTimers(now);
17
- });
18
-
19
- afterEach(() => {
20
- window.Storage.prototype.getItem.restore();
21
- window.Storage.prototype.setItem.restore();
22
- clock.restore();
23
- });
24
-
25
- describe('getDeviceSessionExpiry', () => {
26
- describe('given a valid iso date is stored', () => {
27
- beforeEach(() => {
28
- mockStorage.deviceSessionExpiry = now.toISOString();
29
- });
30
-
31
- it('should return the correct iso date', () => {
32
- expect(storage.getDeviceSessionExpiry().toISOString()).to.equal(now.toISOString());
33
- });
34
- });
35
-
36
- describe('given no value is stored', () => {
37
- beforeEach(() => {
38
- mockStorage.deviceSessionExpiry = null;
39
- });
40
-
41
- it('should return null', () => {
42
- expect(storage.getDeviceSessionExpiry()).to.equal(null);
43
- });
44
- });
45
-
46
- describe('given an invalid value is stored', () => {
47
- beforeEach(() => {
48
- mockStorage.deviceSessionExpiry = 'abc';
49
- });
50
-
51
- it('should return null', () => {
52
- expect(storage.getDeviceSessionExpiry()).to.equal(null);
53
- });
54
- });
55
-
56
- describe('given a unix timestamp is stored (old format)', () => {
57
- beforeEach(() => {
58
- mockStorage.deviceSessionExpiry = String(now.getTime());
59
- });
60
-
61
- it('should return null', () => {
62
- expect(storage.getDeviceSessionExpiry()).to.equal(null);
63
- });
64
- });
65
- });
66
-
67
- describe('setDeviceSessionExpiry', () => {
68
- it('should store the date as an iso string', () => {
69
- const date = new Date(2018, 6, 14, 11, 0, 0);
70
-
71
- storage.setDeviceSessionExpiry(date);
72
-
73
- expect(mockStorage.deviceSessionExpiry).to.equal(date.toISOString());
74
- });
75
- });
76
-
77
- describe('getFeedStartTime', () => {
78
- describe('given a valid iso date is stored', () => {
79
- beforeEach(() => {
80
- mockStorage.newArticlesSinceTime = now.toISOString();
81
- });
82
-
83
- it('should return the correct iso date', () => {
84
- expect(storage.getFeedStartTime().toISOString()).to.equal(now.toISOString());
85
- });
86
- });
87
-
88
- describe('given no value is stored', () => {
89
- beforeEach(() => {
90
- mockStorage.newArticlesSinceTime = null;
91
- });
92
-
93
- it('should return null', () => {
94
- expect(storage.getFeedStartTime()).to.equal(null);
95
- });
96
- });
97
-
98
- describe('given an invalid value is stored', () => {
99
- beforeEach(() => {
100
- mockStorage.newArticlesSinceTime = 'abc';
101
- });
102
-
103
- it('should return null', () => {
104
- expect(storage.getFeedStartTime()).to.equal(null);
105
- });
106
- });
107
-
108
- describe('given a unix timestamp is stored (old format)', () => {
109
- beforeEach(() => {
110
- mockStorage.newArticlesSinceTime = String(now.getTime());
111
- });
112
-
113
- it('should return the correct iso date', () => {
114
- expect(storage.getFeedStartTime()).to.equal(null);
115
- });
116
- });
117
- });
118
-
119
- describe('setFeedStartTime', () => {
120
- it('should store the date as an iso string', () => {
121
- const date = new Date(2018, 5, 1, 11, 30, 0);
122
-
123
- storage.setFeedStartTime(date);
124
-
125
- expect(mockStorage.newArticlesSinceTime).to.equal(date.toISOString());
126
- });
127
- });
128
-
129
- describe('isAvailable', () => {
130
- it('should return true if it is available', () => {
131
- sinon.stub(window.Storage.prototype, 'removeItem').callsFake((key) => delete mockStorage[key]);
132
-
133
- expect(storage.isAvailable()).to.be.true;
134
- });
135
-
136
- it('should return false if it is not available', () => {
137
- mockStorage = null;
138
-
139
- expect(storage.isAvailable()).to.be.false;
140
- });
141
- });
142
- });