pryv 3.0.0 → 3.0.2
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/package.json +1 -1
- package/src/Connection.js +2 -1
- package/src/lib/buildSearchParams.js +26 -0
- package/src/lib/getEventStreamed.js +4 -2
- package/src/utils.js +2 -1
- package/test/Accesses.test.js +126 -0
- package/test/Account.test.js +73 -0
- package/test/AuthController.test.js +193 -0
- package/test/Browser.AuthController.test.js +4 -4
- package/test/Browser.test.js +3 -3
- package/test/Connection.edge-cases.test.js +102 -0
- package/test/Connection.test.js +88 -43
- package/test/CookieUtils.test.js +69 -0
- package/test/LoginButton.test.js +196 -0
- package/test/LoginMessages.test.js +48 -0
- package/test/PryvError.test.js +40 -0
- package/test/Service.test.js +10 -10
- package/test/ServiceAssets.test.js +7 -7
- package/test/Streams.test.js +135 -0
- package/test/buildSearchParams.test.js +61 -0
- package/test/json-parser.test.js +198 -0
- package/test/utils.test.js +6 -6
package/test/Connection.test.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/* global describe, it, before, after, beforeEach, afterEach, expect, JSDOM, pryv, Blob, FormData */
|
|
6
6
|
|
|
7
7
|
// URL and URLSearchParams are native in Node.js and browsers
|
|
8
|
-
const cuid = require('
|
|
8
|
+
const { createId: cuid } = require('@paralleldrive/cuid2');
|
|
9
9
|
const testData = require('../../../test/test-data');
|
|
10
10
|
|
|
11
11
|
let conn = null;
|
|
@@ -17,15 +17,15 @@ if (isNode) { // node
|
|
|
17
17
|
readFileSync = require('fs').readFileSync;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
describe('Connection', () => {
|
|
20
|
+
describe('[CONX] Connection', () => {
|
|
21
21
|
before(async function () {
|
|
22
22
|
this.timeout(15000);
|
|
23
23
|
await testData.prepare();
|
|
24
24
|
conn = new pryv.Connection(testData.apiEndpointWithToken);
|
|
25
25
|
|
|
26
|
-
// create some events
|
|
27
|
-
const toBeDeletedId = cuid();
|
|
28
|
-
const toBeTrashed = cuid();
|
|
26
|
+
// create some events (prefix with 'c' for v2 event ID pattern)
|
|
27
|
+
const toBeDeletedId = 'c' + cuid();
|
|
28
|
+
const toBeTrashed = 'c' + cuid();
|
|
29
29
|
await conn.api([
|
|
30
30
|
{
|
|
31
31
|
method: 'events.create',
|
|
@@ -74,35 +74,35 @@ describe('Connection', () => {
|
|
|
74
74
|
]);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
describe('.service', function () {
|
|
78
|
-
it('return a pryv.Service object', async () => {
|
|
77
|
+
describe('[CSRV] .service', function () {
|
|
78
|
+
it('[CSRA] return a pryv.Service object', async () => {
|
|
79
79
|
const service = conn.service;
|
|
80
80
|
expect(service instanceof pryv.Service).to.equal(true);
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
describe('.username() ', function () {
|
|
85
|
-
it('return the username of this connection', async () => {
|
|
84
|
+
describe('[CUSR] .username() ', function () {
|
|
85
|
+
it('[CUSA] return the username of this connection', async () => {
|
|
86
86
|
const username = await conn.username();
|
|
87
87
|
expect(username).to.equal(testData.username);
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
describe('.apiOne()', function () {
|
|
91
|
+
describe('[CAOX] .apiOne()', function () {
|
|
92
92
|
this.timeout(15000);
|
|
93
|
-
it('.apiOne("events.get")', async () => {
|
|
93
|
+
it('[CAOA] .apiOne("events.get")', async () => {
|
|
94
94
|
const res = await conn.apiOne('events.get');
|
|
95
95
|
expect(res.events).to.exist;
|
|
96
96
|
});
|
|
97
|
-
it('.apiOne("events.get")', async () => {
|
|
97
|
+
it('[CAOB] .apiOne("events.get") with path', async () => {
|
|
98
98
|
const res = await conn.apiOne('events.get', {}, 'events');
|
|
99
99
|
expect(Array.isArray(res)).to.equal(true);
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
describe('.api()', function () {
|
|
103
|
+
describe('[CAPX] .api()', function () {
|
|
104
104
|
this.timeout(15000);
|
|
105
|
-
it('.api() events.get', async () => {
|
|
105
|
+
it('[CAPA] .api() events.get', async () => {
|
|
106
106
|
const res = await conn.api(
|
|
107
107
|
[
|
|
108
108
|
{
|
|
@@ -113,7 +113,7 @@ describe('Connection', () => {
|
|
|
113
113
|
expect(res.length).to.equal(1);
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
it('.api() events.get split in chunks', async () => {
|
|
116
|
+
it('[CAPB] .api() events.get split in chunks', async () => {
|
|
117
117
|
conn.options.chunkSize = 2;
|
|
118
118
|
const res = await conn.api(
|
|
119
119
|
[
|
|
@@ -124,7 +124,7 @@ describe('Connection', () => {
|
|
|
124
124
|
expect(res.length).to.equal(3);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
it('.api() events.get with handleResult call', async () => {
|
|
127
|
+
it('[CAPC] .api() events.get with handleResult call', async () => {
|
|
128
128
|
conn.options.chunkSize = 2;
|
|
129
129
|
|
|
130
130
|
let resultsReceivedCount = 0;
|
|
@@ -144,7 +144,7 @@ describe('Connection', () => {
|
|
|
144
144
|
expect(res.length).to.equal(resultsReceivedCount);
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
-
it('.api() events.get with async handleResult call', async () => {
|
|
147
|
+
it('[CAPD] .api() events.get with async handleResult call', async () => {
|
|
148
148
|
conn.options.chunkSize = 2;
|
|
149
149
|
|
|
150
150
|
let resultsReceivedCount = 0;
|
|
@@ -169,7 +169,7 @@ describe('Connection', () => {
|
|
|
169
169
|
expect(res.length).to.equal(resultsReceivedCount);
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
-
it('.api() events.get split in chunks and send percentages', async () => {
|
|
172
|
+
it('[CAPE] .api() events.get split in chunks and send percentages', async () => {
|
|
173
173
|
conn.options.chunkSize = 2;
|
|
174
174
|
const percentres = { 1: 67, 2: 100 };
|
|
175
175
|
let count = 1;
|
|
@@ -185,7 +185,7 @@ describe('Connection', () => {
|
|
|
185
185
|
expect(res.length).to.equal(3);
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
it('.api() with callbacks', (done) => {
|
|
188
|
+
it('[CAPF] .api() with callbacks', (done) => {
|
|
189
189
|
conn.api(
|
|
190
190
|
[
|
|
191
191
|
{ method: 'events.get', params: {} }
|
|
@@ -199,8 +199,8 @@ describe('Connection', () => {
|
|
|
199
199
|
});
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
describe('Attachments', () => {
|
|
203
|
-
it('Node Only: Create event with attachment from file', async function () {
|
|
202
|
+
describe('[CATX] Attachments', () => {
|
|
203
|
+
it('[CATA] Node Only: Create event with attachment from file', async function () {
|
|
204
204
|
if (!isNode) { this.skip(); }
|
|
205
205
|
const res = await conn.createEventWithFile({
|
|
206
206
|
type: 'picture/attached',
|
|
@@ -216,7 +216,7 @@ describe('Connection', () => {
|
|
|
216
216
|
expect(res.event.attachments[0].fileName).to.equal('Y.png');
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
it('Node Only: Create event with attachment from Buffer', async function () {
|
|
219
|
+
it('[CATB] Node Only: Create event with attachment from Buffer', async function () {
|
|
220
220
|
if (!isNode) { this.skip(); }
|
|
221
221
|
|
|
222
222
|
const fileData = readFileSync('./test/Y.png');
|
|
@@ -234,7 +234,7 @@ describe('Connection', () => {
|
|
|
234
234
|
expect(res.event.attachments[0].fileName).to.equal('Y.png');
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
it('Browser Only: Create event with attachment from Buffer', async function () {
|
|
237
|
+
it('[CATC] Browser Only: Create event with attachment from Buffer', async function () {
|
|
238
238
|
if (isNode) { this.skip(); }
|
|
239
239
|
|
|
240
240
|
const blob = new Blob(['Hello'], { type: 'text/txt' });
|
|
@@ -252,7 +252,7 @@ describe('Connection', () => {
|
|
|
252
252
|
expect(res.event.attachments[0].fileName).to.equal('Hello.txt');
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
-
it('Browser Only: Create event with attachment formData', async function () {
|
|
255
|
+
it('[CATD] Browser Only: Create event with attachment formData', async function () {
|
|
256
256
|
if (isNode) { this.skip(); }
|
|
257
257
|
|
|
258
258
|
const formData = new FormData();
|
|
@@ -274,14 +274,14 @@ describe('Connection', () => {
|
|
|
274
274
|
});
|
|
275
275
|
});
|
|
276
276
|
|
|
277
|
-
describe('HF events', async function () {
|
|
277
|
+
describe('[CHFX] HF events', async function () {
|
|
278
278
|
before(async function () {
|
|
279
279
|
if (!await conn.service.supportsHF()) {
|
|
280
280
|
this.skip();
|
|
281
281
|
}
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
-
it('Add data points to HF event', async () => {
|
|
284
|
+
it('[CHFA] Add data points to HF event', async () => {
|
|
285
285
|
const res = await conn.api([{
|
|
286
286
|
method: 'events.create',
|
|
287
287
|
params: {
|
|
@@ -305,22 +305,30 @@ describe('Connection', () => {
|
|
|
305
305
|
});
|
|
306
306
|
});
|
|
307
307
|
|
|
308
|
-
describe('.get()', () => {
|
|
309
|
-
it('/events', async () => {
|
|
308
|
+
describe('[CGTX] .get()', () => {
|
|
309
|
+
it('[CGTA] /events', async () => {
|
|
310
310
|
const res = await conn.get('events', { limit: 1 });
|
|
311
311
|
expect(res.events.length).to.equal(1);
|
|
312
312
|
});
|
|
313
|
+
|
|
314
|
+
it('[CGTB] /events with params', async () => {
|
|
315
|
+
const res = await conn.get('events', { fromTime: 0, toTime: Date.now() / 1000, limit: 10000, types: ['note/txt'], streams: ['data', 'monitor-test'] });
|
|
316
|
+
expect(res.events.length > 0).to.be.true;
|
|
317
|
+
for (const event of res.events) {
|
|
318
|
+
expect(event.type).to.equal('note/txt');
|
|
319
|
+
}
|
|
320
|
+
});
|
|
313
321
|
});
|
|
314
322
|
|
|
315
|
-
describe('time', () => {
|
|
316
|
-
it('deltatime property', async () => {
|
|
323
|
+
describe('[CTMX] time', () => {
|
|
324
|
+
it('[CTMA] deltatime property', async () => {
|
|
317
325
|
await conn.get('events', { limit: 1 });
|
|
318
326
|
expect(conn.deltaTime).to.be.within(-2, 2);
|
|
319
327
|
});
|
|
320
328
|
});
|
|
321
329
|
|
|
322
|
-
describe('API', () => {
|
|
323
|
-
it('endpoint property', async () => {
|
|
330
|
+
describe('[CEPX] API', () => {
|
|
331
|
+
it('[CEPA] endpoint property', async () => {
|
|
324
332
|
const [protocol, hostPath] = testData.serviceInfo.api.split('://');
|
|
325
333
|
const challenge = protocol + '://' + conn.token + '@' + hostPath.replace('{username}', testData.username);
|
|
326
334
|
const apiEndpoint = conn.apiEndpoint;
|
|
@@ -328,12 +336,12 @@ describe('Connection', () => {
|
|
|
328
336
|
});
|
|
329
337
|
});
|
|
330
338
|
|
|
331
|
-
describe('Streamed event get', function () {
|
|
339
|
+
describe('[CSTX] Streamed event get', function () {
|
|
332
340
|
this.timeout(15000);
|
|
333
341
|
const now = (new Date()).getTime() / 1000 + 1000;
|
|
334
342
|
|
|
335
|
-
describe('Node & Browser', function () {
|
|
336
|
-
it('streaming ', async () => {
|
|
343
|
+
describe('[CSNX] Node & Browser', function () {
|
|
344
|
+
it('[CSNA] streaming ', async () => {
|
|
337
345
|
const queryParams = { fromTime: 0, toTime: now, limit: 10000 };
|
|
338
346
|
let eventsCount = 0;
|
|
339
347
|
function forEachEvent (event) { eventsCount++; }
|
|
@@ -341,7 +349,44 @@ describe('Connection', () => {
|
|
|
341
349
|
expect(eventsCount).to.equal(res.eventsCount);
|
|
342
350
|
});
|
|
343
351
|
|
|
344
|
-
it('streaming
|
|
352
|
+
it('[CSNZ] streaming with query params', async () => {
|
|
353
|
+
const queryParams = { fromTime: 0, toTime: Date.now() / 1000, limit: 10000, types: ['note/txt'], streams: ['data', 'monitor-test'] };
|
|
354
|
+
let eventsCount = 0;
|
|
355
|
+
function forEachEvent (event) {
|
|
356
|
+
eventsCount++;
|
|
357
|
+
}
|
|
358
|
+
const res = await conn.getEventsStreamed(queryParams, forEachEvent);
|
|
359
|
+
expect(eventsCount).to.equal(res.eventsCount);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('[CSNY] streaming with roneous query params', async () => {
|
|
363
|
+
const queryParams = { fromTime: 0, toTime: Date.now() / 1000, limit: 10000, types: ['note/txt'], streams: ['bogus', 'bogus2'] };
|
|
364
|
+
let eventsCount = 0;
|
|
365
|
+
function forEachEvent (event) {
|
|
366
|
+
eventsCount++;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
const res = await conn.getEventsStreamed(queryParams, forEachEvent);
|
|
370
|
+
expect(eventsCount).to.equal(res.eventsCount);
|
|
371
|
+
} catch (e) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
throw new Error('Should fail');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('[CSNB] streaming includesDeletion', async () => {
|
|
378
|
+
// Create, trash, and delete events to ensure we have all three states
|
|
379
|
+
const trashId = 'c' + cuid();
|
|
380
|
+
const deleteId = 'c' + cuid();
|
|
381
|
+
const setupRes = await conn.api([
|
|
382
|
+
{ method: 'events.create', params: { id: trashId, streamIds: ['data'], type: 'note/txt', content: 'to trash' } },
|
|
383
|
+
{ method: 'events.create', params: { id: deleteId, streamIds: ['data'], type: 'note/txt', content: 'to delete' } },
|
|
384
|
+
{ method: 'events.delete', params: { id: trashId } },
|
|
385
|
+
{ method: 'events.delete', params: { id: deleteId } },
|
|
386
|
+
{ method: 'events.delete', params: { id: deleteId } }
|
|
387
|
+
]);
|
|
388
|
+
// Verify setup succeeded
|
|
389
|
+
expect(setupRes[0].event, 'event creation failed: ' + JSON.stringify(setupRes[0].error)).to.exist;
|
|
345
390
|
const queryParams = { fromTime: 0, toTime: now, limit: 10000, includeDeletions: true, modifiedSince: 0, state: 'all' };
|
|
346
391
|
let eventsCount = 0;
|
|
347
392
|
let trashedCount = 0;
|
|
@@ -363,14 +408,14 @@ describe('Connection', () => {
|
|
|
363
408
|
expect(trashedCount).to.be.gt(0);
|
|
364
409
|
});
|
|
365
410
|
|
|
366
|
-
it('no-events ', async () => {
|
|
411
|
+
it('[CSNC] no-events ', async () => {
|
|
367
412
|
const queryParams = { fromTime: 0, toTime: now, types: ['type/unexistent'] };
|
|
368
413
|
function forEachEvent (event) { }
|
|
369
414
|
const res = await conn.getEventsStreamed(queryParams, forEachEvent);
|
|
370
415
|
expect(0).to.equal(res.eventsCount);
|
|
371
416
|
});
|
|
372
417
|
|
|
373
|
-
it('no-events includeDeletions', async () => {
|
|
418
|
+
it('[CSND] no-events includeDeletions', async () => {
|
|
374
419
|
const queryParams = { fromTime: 0, toTime: now, types: ['type/unexistent'], includeDeletions: true, modifiedSince: 0 };
|
|
375
420
|
function forEachEvent (event) { }
|
|
376
421
|
const res = await conn.getEventsStreamed(queryParams, forEachEvent);
|
|
@@ -380,7 +425,7 @@ describe('Connection', () => {
|
|
|
380
425
|
});
|
|
381
426
|
|
|
382
427
|
if (typeof window === 'undefined') {
|
|
383
|
-
describe('Browser mock', function () {
|
|
428
|
+
describe('[CSBX] Browser mock', function () {
|
|
384
429
|
beforeEach(function () {
|
|
385
430
|
const dom = new JSDOM('<!DOCTYPE html>', { url: 'http://localhost/' });
|
|
386
431
|
global.document = dom.window.document;
|
|
@@ -394,7 +439,7 @@ describe('Connection', () => {
|
|
|
394
439
|
delete global.location;
|
|
395
440
|
});
|
|
396
441
|
|
|
397
|
-
it(' with fetch', async () => {
|
|
442
|
+
it('[CSBA] with fetch', async () => {
|
|
398
443
|
const queryParams = { fromTime: 0, toTime: now, limit: 10000 };
|
|
399
444
|
let eventsCount = 0;
|
|
400
445
|
function forEachEvent (event) { eventsCount++; }
|
|
@@ -405,7 +450,7 @@ describe('Connection', () => {
|
|
|
405
450
|
}
|
|
406
451
|
});
|
|
407
452
|
|
|
408
|
-
describe('Access Info', () => {
|
|
453
|
+
describe('[CAIX] Access Info', () => {
|
|
409
454
|
let newUser;
|
|
410
455
|
let accessInfoUser;
|
|
411
456
|
before(async () => {
|
|
@@ -444,13 +489,13 @@ describe('Connection', () => {
|
|
|
444
489
|
]);
|
|
445
490
|
});
|
|
446
491
|
|
|
447
|
-
it('has same username', () => {
|
|
492
|
+
it('[CAIA] has same username', () => {
|
|
448
493
|
expect(accessInfoUser).to.exist;
|
|
449
494
|
expect(accessInfoUser.name).to.exist;
|
|
450
495
|
expect(newUser.access.name).to.equal(accessInfoUser.name);
|
|
451
496
|
});
|
|
452
497
|
|
|
453
|
-
it('has same token', () => {
|
|
498
|
+
it('[CAIB] has same token', () => {
|
|
454
499
|
expect(accessInfoUser.token).to.exist;
|
|
455
500
|
expect(newUser.access.token).to.equal(accessInfoUser.token);
|
|
456
501
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/lib-js/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/* global describe, it, before, after, expect, JSDOM */
|
|
6
|
+
|
|
7
|
+
const CookieUtils = require('../src/Browser/CookieUtils');
|
|
8
|
+
|
|
9
|
+
describe('[COKX] CookieUtils', function () {
|
|
10
|
+
let cleanupDom = false;
|
|
11
|
+
|
|
12
|
+
before(async () => {
|
|
13
|
+
if (typeof document !== 'undefined') return; // in browser
|
|
14
|
+
cleanupDom = true;
|
|
15
|
+
const dom = new JSDOM('<!DOCTYPE html>', {
|
|
16
|
+
url: 'http://localhost/'
|
|
17
|
+
});
|
|
18
|
+
global.document = dom.window.document;
|
|
19
|
+
global.window = dom.window;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
after(async () => {
|
|
23
|
+
if (!cleanupDom) return;
|
|
24
|
+
delete global.document;
|
|
25
|
+
delete global.window;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('[COKA] set() and get() cookie', async function () {
|
|
29
|
+
const testKey = 'test-cookie-key';
|
|
30
|
+
const testValue = { foo: 'bar', num: 123 };
|
|
31
|
+
CookieUtils.set(testKey, testValue);
|
|
32
|
+
const retrieved = CookieUtils.get(testKey);
|
|
33
|
+
expect(retrieved).to.deep.equal(testValue);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('[COKB] get() returns undefined for non-existent cookie', async function () {
|
|
37
|
+
const result = CookieUtils.get('non-existent-cookie-key');
|
|
38
|
+
expect(result).to.be.undefined;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('[COKC] del() removes cookie', async function () {
|
|
42
|
+
const testKey = 'cookie-to-delete';
|
|
43
|
+
CookieUtils.set(testKey, { data: 'test' });
|
|
44
|
+
expect(CookieUtils.get(testKey)).to.exist;
|
|
45
|
+
CookieUtils.del(testKey);
|
|
46
|
+
// After deletion, the cookie is set to { deleted: true } with expiration in the past
|
|
47
|
+
// The cookie may still exist but with deleted flag
|
|
48
|
+
const afterDel = CookieUtils.get(testKey);
|
|
49
|
+
if (afterDel) {
|
|
50
|
+
expect(afterDel.deleted).to.be.true;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('[COKD] set() with custom expiration', async function () {
|
|
55
|
+
const testKey = 'cookie-with-expiry';
|
|
56
|
+
const testValue = 'expires-soon';
|
|
57
|
+
CookieUtils.set(testKey, testValue, 30);
|
|
58
|
+
const retrieved = CookieUtils.get(testKey);
|
|
59
|
+
expect(retrieved).to.equal(testValue);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('[COKE] handles special characters in values', async function () {
|
|
63
|
+
const testKey = 'special-chars';
|
|
64
|
+
const testValue = { text: 'hello=world&foo=bar', unicode: '日本語' };
|
|
65
|
+
CookieUtils.set(testKey, testValue);
|
|
66
|
+
const retrieved = CookieUtils.get(testKey);
|
|
67
|
+
expect(retrieved).to.deep.equal(testValue);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/lib-js/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/* global describe, it, before, after, expect, JSDOM, testData */
|
|
6
|
+
|
|
7
|
+
const LoginButton = require('../src/Browser/LoginButton');
|
|
8
|
+
const Service = require('../src/Service');
|
|
9
|
+
const AuthStates = require('../src/Auth/AuthStates');
|
|
10
|
+
|
|
11
|
+
describe('[LBTX] LoginButton', function () {
|
|
12
|
+
this.timeout(20000);
|
|
13
|
+
|
|
14
|
+
let service;
|
|
15
|
+
let cleanupDom = false;
|
|
16
|
+
let dom;
|
|
17
|
+
|
|
18
|
+
before(async function () {
|
|
19
|
+
await testData.prepare();
|
|
20
|
+
service = new Service(testData.serviceInfoUrl);
|
|
21
|
+
await service.info();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
before(async () => {
|
|
25
|
+
if (typeof document !== 'undefined') return;
|
|
26
|
+
cleanupDom = true;
|
|
27
|
+
dom = new JSDOM('<!DOCTYPE html><body><span id="loginButton"></span></body>', {
|
|
28
|
+
url: 'http://localhost/'
|
|
29
|
+
});
|
|
30
|
+
global.document = dom.window.document;
|
|
31
|
+
global.window = dom.window;
|
|
32
|
+
global.location = dom.window.location;
|
|
33
|
+
global.navigator = { userAgent: 'Safari' };
|
|
34
|
+
global.confirm = () => true;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
after(async () => {
|
|
38
|
+
if (!cleanupDom) return;
|
|
39
|
+
delete global.document;
|
|
40
|
+
delete global.window;
|
|
41
|
+
delete global.location;
|
|
42
|
+
delete global.confirm;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('[LBCX] Constructor and Init', function () {
|
|
46
|
+
it('[LBCA] creates LoginButton with valid settings', async function () {
|
|
47
|
+
const settings = {
|
|
48
|
+
spanButtonID: 'loginButton',
|
|
49
|
+
authRequest: {
|
|
50
|
+
requestingAppId: 'test-app',
|
|
51
|
+
requestedPermissions: []
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const loginBtn = new LoginButton(settings, service);
|
|
55
|
+
expect(loginBtn.authSettings).to.equal(settings);
|
|
56
|
+
expect(loginBtn.service).to.equal(service);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('[LBCB] init() sets up button and auth controller', async function () {
|
|
60
|
+
const settings = {
|
|
61
|
+
spanButtonID: 'loginButton',
|
|
62
|
+
authRequest: {
|
|
63
|
+
requestingAppId: 'test-app',
|
|
64
|
+
requestedPermissions: []
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const loginBtn = new LoginButton(settings, service);
|
|
68
|
+
await loginBtn.init();
|
|
69
|
+
expect(loginBtn.auth).to.exist;
|
|
70
|
+
expect(loginBtn._cookieKey).to.include('pryv-libjs-');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('[LBAX] Authorization data', function () {
|
|
75
|
+
let loginBtn;
|
|
76
|
+
|
|
77
|
+
before(async function () {
|
|
78
|
+
const settings = {
|
|
79
|
+
spanButtonID: 'loginButton',
|
|
80
|
+
authRequest: {
|
|
81
|
+
requestingAppId: 'test-app-auth',
|
|
82
|
+
requestedPermissions: []
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
loginBtn = new LoginButton(settings, service);
|
|
86
|
+
await loginBtn.init();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('[LBAA] saveAuthorizationData and getAuthorizationData work', function () {
|
|
90
|
+
const authData = { apiEndpoint: 'https://test.pryv.me', username: 'testuser' };
|
|
91
|
+
loginBtn.saveAuthorizationData(authData);
|
|
92
|
+
const retrieved = loginBtn.getAuthorizationData();
|
|
93
|
+
expect(retrieved).to.deep.equal(authData);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('[LBAB] deleteAuthorizationData removes data', async function () {
|
|
97
|
+
loginBtn.saveAuthorizationData({ test: 'data' });
|
|
98
|
+
await loginBtn.deleteAuthorizationData();
|
|
99
|
+
const retrieved = loginBtn.getAuthorizationData();
|
|
100
|
+
// After deletion, either undefined or has deleted flag
|
|
101
|
+
if (retrieved) {
|
|
102
|
+
expect(retrieved.deleted).to.be.true;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('[LBOX] onClick', function () {
|
|
108
|
+
it('[LBOA] onClick calls auth.handleClick', async function () {
|
|
109
|
+
const settings = {
|
|
110
|
+
spanButtonID: 'loginButton',
|
|
111
|
+
authRequest: {
|
|
112
|
+
requestingAppId: 'test-app-click',
|
|
113
|
+
requestedPermissions: []
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const loginBtn = new LoginButton(settings, service);
|
|
117
|
+
await loginBtn.init();
|
|
118
|
+
|
|
119
|
+
let handleClickCalled = false;
|
|
120
|
+
loginBtn.auth.handleClick = function () { handleClickCalled = true; };
|
|
121
|
+
|
|
122
|
+
loginBtn.onClick();
|
|
123
|
+
expect(handleClickCalled).to.be.true;
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('[LBSX] onStateChange', function () {
|
|
128
|
+
let loginBtn;
|
|
129
|
+
|
|
130
|
+
before(async function () {
|
|
131
|
+
const settings = {
|
|
132
|
+
spanButtonID: 'loginButton',
|
|
133
|
+
authRequest: {
|
|
134
|
+
requestingAppId: 'test-app-state',
|
|
135
|
+
requestedPermissions: []
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
loginBtn = new LoginButton(settings, service);
|
|
139
|
+
await loginBtn.init();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('[LBSA] handles LOADING state', async function () {
|
|
143
|
+
await loginBtn.onStateChange({ status: AuthStates.LOADING });
|
|
144
|
+
expect(loginBtn.text).to.equal('...');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('[LBSB] handles INITIALIZED state', async function () {
|
|
148
|
+
await loginBtn.onStateChange({ status: AuthStates.INITIALIZED });
|
|
149
|
+
expect(loginBtn.text).to.include('Signin');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('[LBSC] handles AUTHORIZED state', async function () {
|
|
153
|
+
await loginBtn.onStateChange({
|
|
154
|
+
status: AuthStates.AUTHORIZED,
|
|
155
|
+
username: 'testuser',
|
|
156
|
+
apiEndpoint: 'https://test.pryv.me'
|
|
157
|
+
});
|
|
158
|
+
expect(loginBtn.text).to.equal('testuser');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('[LBSD] handles ERROR state', async function () {
|
|
162
|
+
await loginBtn.onStateChange({
|
|
163
|
+
status: AuthStates.ERROR,
|
|
164
|
+
message: 'Test error'
|
|
165
|
+
});
|
|
166
|
+
expect(loginBtn.text).to.include('Error');
|
|
167
|
+
expect(loginBtn.text).to.include('Test error');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('[LBSE] handles SIGNOUT state when confirmed', async function () {
|
|
171
|
+
// Save some auth data first
|
|
172
|
+
loginBtn.saveAuthorizationData({ test: 'data' });
|
|
173
|
+
await loginBtn.onStateChange({ status: AuthStates.SIGNOUT });
|
|
174
|
+
// Confirm is mocked to return true
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('[LBSF] handles unknown state gracefully', async function () {
|
|
178
|
+
// Should log warning but not throw
|
|
179
|
+
await loginBtn.onStateChange({ status: 'UNKNOWN_STATE' });
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('[LBWX] Without spanButtonID', function () {
|
|
184
|
+
it('[LBWA] init works without span (logs warning)', async function () {
|
|
185
|
+
const settings = {
|
|
186
|
+
authRequest: {
|
|
187
|
+
requestingAppId: 'test-app-no-span',
|
|
188
|
+
requestedPermissions: []
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const loginBtn = new LoginButton(settings, service);
|
|
192
|
+
await loginBtn.init();
|
|
193
|
+
expect(loginBtn.loginButtonSpan).to.be.null;
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/lib-js/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/* global describe, it, expect */
|
|
6
|
+
|
|
7
|
+
const Messages = require('../src/Auth/LoginMessages');
|
|
8
|
+
|
|
9
|
+
describe('[MSGX] LoginMessages', function () {
|
|
10
|
+
it('[MSGA] returns English messages by default', function () {
|
|
11
|
+
const msgs = Messages('en');
|
|
12
|
+
expect(msgs.LOADING).to.equal('...');
|
|
13
|
+
expect(msgs.ERROR).to.equal('Error');
|
|
14
|
+
expect(msgs.LOGIN).to.equal('Signin');
|
|
15
|
+
expect(msgs.SIGNOUT_CONFIRM).to.equal('Logout?');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('[MSGB] returns French messages when available', function () {
|
|
19
|
+
const msgs = Messages('fr');
|
|
20
|
+
expect(msgs.ERROR).to.equal('Erreur');
|
|
21
|
+
expect(msgs.LOGIN).to.equal('Login');
|
|
22
|
+
expect(msgs.SIGNOUT_CONFIRM).to.equal('Se déconnecter ?');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('[MSGC] falls back to English for unavailable language', function () {
|
|
26
|
+
const msgs = Messages('de');
|
|
27
|
+
expect(msgs.LOADING).to.equal('...');
|
|
28
|
+
expect(msgs.ERROR).to.equal('Error');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('[MSGD] uses custom definitions when provided', function () {
|
|
32
|
+
const customDefs = {
|
|
33
|
+
LOADING: { en: 'Loading...', fr: 'Chargement...' },
|
|
34
|
+
CUSTOM: { en: 'Custom Message', fr: 'Message personnalisé' }
|
|
35
|
+
};
|
|
36
|
+
const msgs = Messages('en', customDefs);
|
|
37
|
+
expect(msgs.LOADING).to.equal('Loading...');
|
|
38
|
+
expect(msgs.CUSTOM).to.equal('Custom Message');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('[MSGE] custom definitions fall back to English', function () {
|
|
42
|
+
const customDefs = {
|
|
43
|
+
TEST: { en: 'English only' }
|
|
44
|
+
};
|
|
45
|
+
const msgs = Messages('fr', customDefs);
|
|
46
|
+
expect(msgs.TEST).to.equal('English only');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* [BSD-3-Clause](https://github.com/pryv/lib-js/blob/master/LICENSE)
|
|
4
|
+
*/
|
|
5
|
+
/* global describe, it, expect */
|
|
6
|
+
|
|
7
|
+
const PryvError = require('../src/lib/PryvError');
|
|
8
|
+
|
|
9
|
+
describe('[PERX] PryvError', function () {
|
|
10
|
+
it('[PERA] creates error with message', function () {
|
|
11
|
+
const error = new PryvError('Test error message');
|
|
12
|
+
expect(error.message).to.equal('Test error message');
|
|
13
|
+
expect(error.name).to.equal('PryvError');
|
|
14
|
+
expect(error).to.be.instanceOf(Error);
|
|
15
|
+
expect(error).to.be.instanceOf(PryvError);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('[PERB] includes innerObject when provided', function () {
|
|
19
|
+
const innerError = new Error('Inner error');
|
|
20
|
+
const error = new PryvError('Outer message', innerError);
|
|
21
|
+
expect(error.innerObject).to.equal(innerError);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('[PERC] accepts object as innerObject', function () {
|
|
25
|
+
const innerObj = { code: 'ERR_001', details: 'some details' };
|
|
26
|
+
const error = new PryvError('Error with object', innerObj);
|
|
27
|
+
expect(error.innerObject).to.deep.equal(innerObj);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('[PERD] has proper stack trace', function () {
|
|
31
|
+
const error = new PryvError('Stack test');
|
|
32
|
+
expect(error.stack).to.exist;
|
|
33
|
+
expect(error.stack).to.include('PryvError');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('[PERE] innerObject is undefined when not provided', function () {
|
|
37
|
+
const error = new PryvError('No inner');
|
|
38
|
+
expect(error.innerObject).to.be.undefined;
|
|
39
|
+
});
|
|
40
|
+
});
|