@opentermsarchive/engine 7.1.0 → 7.2.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/README.md +6 -0
- package/package.json +3 -2
- package/scripts/declarations/validate/definitions.js +14 -3
- package/scripts/declarations/validate/index.mocha.js +19 -0
- package/src/archivist/extract/dom.js +75 -0
- package/src/archivist/extract/dom.test.js +207 -0
- package/src/archivist/extract/exposedFilters.js +25 -0
- package/src/archivist/extract/exposedFilters.test.js +208 -0
- package/src/archivist/extract/filter.js +59 -0
- package/src/archivist/extract/filter.test.js +194 -0
- package/src/archivist/extract/index.js +12 -145
- package/src/archivist/extract/index.test.js +76 -64
- package/src/archivist/extract/markdown.js +29 -0
- package/src/archivist/services/index.js +237 -173
- package/src/archivist/services/index.test.js +499 -7
|
@@ -1,14 +1,506 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
1
4
|
import chai from 'chai';
|
|
2
|
-
import
|
|
5
|
+
import sinon from 'sinon';
|
|
6
|
+
import sinonChai from 'sinon-chai';
|
|
3
7
|
|
|
4
8
|
import expectedServices from '../../../test/fixtures/services.js';
|
|
9
|
+
import * as exposedFilters from '../extract/exposedFilters.js';
|
|
10
|
+
|
|
11
|
+
import Service from './service.js';
|
|
12
|
+
import SourceDocument from './sourceDocument.js';
|
|
13
|
+
import Terms from './terms.js';
|
|
5
14
|
|
|
6
|
-
import
|
|
15
|
+
import { getDeclaredServicesIds, loadServiceDeclaration, loadServiceFilters, getServiceFilters, createSourceDocuments, createServiceFromDeclaration, load, loadWithHistory } from './index.js';
|
|
7
16
|
|
|
8
|
-
chai.use(
|
|
17
|
+
chai.use(sinonChai);
|
|
9
18
|
const { expect } = chai;
|
|
10
19
|
|
|
11
20
|
describe('Services', () => {
|
|
21
|
+
describe('#getDeclaredServicesIds', () => {
|
|
22
|
+
let readdir;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
readdir = sinon.stub(fs, 'readdir');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
sinon.restore();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
context('with valid JSON service declarations', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
readdir.resolves([
|
|
35
|
+
'serviceA.json',
|
|
36
|
+
'serviceB.json',
|
|
37
|
+
'serviceC.json',
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns service IDs from JSON files', async () => {
|
|
42
|
+
const serviceIds = await getDeclaredServicesIds();
|
|
43
|
+
|
|
44
|
+
expect(serviceIds).to.deep.equal([ 'serviceA', 'serviceB', 'serviceC' ]);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
context('with mixed file types', () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
readdir.resolves([
|
|
51
|
+
'serviceA.json',
|
|
52
|
+
'serviceB.txt',
|
|
53
|
+
'serviceC.js',
|
|
54
|
+
'serviceD.json',
|
|
55
|
+
'readme.md',
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('filters out non-JSON files', async () => {
|
|
60
|
+
const serviceIds = await getDeclaredServicesIds();
|
|
61
|
+
|
|
62
|
+
expect(serviceIds).to.deep.equal([ 'serviceA', 'serviceD' ]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
context('with history files', () => {
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
readdir.resolves([
|
|
69
|
+
'serviceA.json',
|
|
70
|
+
'serviceA.history.json',
|
|
71
|
+
'serviceB.json',
|
|
72
|
+
'serviceB.history.json',
|
|
73
|
+
'serviceC.json',
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('excludes history files', async () => {
|
|
78
|
+
const serviceIds = await getDeclaredServicesIds();
|
|
79
|
+
|
|
80
|
+
expect(serviceIds).to.deep.equal([ 'serviceA', 'serviceB', 'serviceC' ]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
context('with complex filenames', () => {
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
readdir.resolves([
|
|
87
|
+
'service-with-dashes.json',
|
|
88
|
+
'Service With Spaces.json',
|
|
89
|
+
'service·A.json',
|
|
90
|
+
'Service B!.json',
|
|
91
|
+
'service_with_underscores.json',
|
|
92
|
+
'service.with.dots.json',
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('handles complex service ID patterns', async () => {
|
|
97
|
+
const serviceIds = await getDeclaredServicesIds();
|
|
98
|
+
|
|
99
|
+
expect(serviceIds).to.deep.equal([
|
|
100
|
+
'service-with-dashes',
|
|
101
|
+
'Service With Spaces',
|
|
102
|
+
'service·A',
|
|
103
|
+
'Service B!',
|
|
104
|
+
'service_with_underscores',
|
|
105
|
+
'service.with.dots',
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
context('with an empty directory', () => {
|
|
111
|
+
beforeEach(() => {
|
|
112
|
+
readdir.resolves([]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('returns an empty array', async () => {
|
|
116
|
+
const serviceIds = await getDeclaredServicesIds();
|
|
117
|
+
|
|
118
|
+
expect(serviceIds).to.deep.equal([]);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
context('when directory read fails', () => {
|
|
123
|
+
beforeEach(() => {
|
|
124
|
+
readdir.rejects(new Error('ENOENT: no such file or directory'));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('throws the original error', async () => {
|
|
128
|
+
await expect(getDeclaredServicesIds()).to.be.rejectedWith('ENOENT: no such file or directory');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
context('with JSON files containing "history" but not as suffix', () => {
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
readdir.resolves([
|
|
135
|
+
'history-service.json',
|
|
136
|
+
'service-history.json',
|
|
137
|
+
'service.history.json',
|
|
138
|
+
'normal-service.json',
|
|
139
|
+
]);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('excludes only files with .history.json suffix', async () => {
|
|
143
|
+
const serviceIds = await getDeclaredServicesIds();
|
|
144
|
+
|
|
145
|
+
expect(serviceIds).to.deep.equal([
|
|
146
|
+
'history-service',
|
|
147
|
+
'service-history',
|
|
148
|
+
'normal-service',
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('#loadServiceDeclaration', () => {
|
|
155
|
+
let readFile;
|
|
156
|
+
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
readFile = sinon.stub(fs, 'readFile');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
afterEach(() => {
|
|
162
|
+
sinon.restore();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
context('with valid JSON service declaration', () => {
|
|
166
|
+
const serviceId = 'serviceA';
|
|
167
|
+
const validDeclaration = {
|
|
168
|
+
name: 'Service A',
|
|
169
|
+
terms: {
|
|
170
|
+
'Terms of Service': {
|
|
171
|
+
fetch: 'https://example.com/tos',
|
|
172
|
+
select: 'body',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
readFile.resolves(JSON.stringify(validDeclaration));
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('returns the parsed service declaration', async () => {
|
|
182
|
+
const result = await loadServiceDeclaration(serviceId);
|
|
183
|
+
|
|
184
|
+
expect(result).to.deep.equal(validDeclaration);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('reads from the correct file path', async () => {
|
|
188
|
+
await loadServiceDeclaration(serviceId);
|
|
189
|
+
|
|
190
|
+
expect(readFile).to.have.been.calledWith(sinon.match(filePath => filePath.endsWith(`${path.sep}serviceA.json`)));
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
context('when the declaration contains invalid JSON', () => {
|
|
195
|
+
const serviceId = 'invalidJson';
|
|
196
|
+
|
|
197
|
+
beforeEach(() => {
|
|
198
|
+
readFile.resolves('{ invalid json content');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('throws with a descriptive error message', async () => {
|
|
202
|
+
try {
|
|
203
|
+
await loadServiceDeclaration(serviceId);
|
|
204
|
+
expect.fail('Expected function to throw an error');
|
|
205
|
+
} catch (error) {
|
|
206
|
+
expect(error.message).to.include('The "invalidJson" service declaration is malformed and cannot be parsed');
|
|
207
|
+
expect(error.message).to.include('invalidJson.json');
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('#loadServiceFilters', () => {
|
|
214
|
+
let access;
|
|
215
|
+
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
access = sinon.stub(fs, 'access');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
afterEach(() => {
|
|
221
|
+
sinon.restore();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
context('when service filters file does not exist', () => {
|
|
225
|
+
const serviceId = 'serviceWithoutFilters';
|
|
226
|
+
|
|
227
|
+
beforeEach(() => {
|
|
228
|
+
const error = new Error('ENOENT: no such file or directory');
|
|
229
|
+
|
|
230
|
+
error.code = 'ENOENT';
|
|
231
|
+
access.rejects(error);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('returns an empty object', async () => {
|
|
235
|
+
const result = await loadServiceFilters(serviceId);
|
|
236
|
+
|
|
237
|
+
expect(result).to.be.empty;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('checks for file existence', async () => {
|
|
241
|
+
await loadServiceFilters(serviceId);
|
|
242
|
+
|
|
243
|
+
expect(access).to.have.been.calledWith(sinon.match(filePath => filePath.endsWith(`${path.sep}serviceWithoutFilters.filters.js`)));
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
context('with a service that has a filters file', () => {
|
|
248
|
+
let result;
|
|
249
|
+
|
|
250
|
+
before(async () => {
|
|
251
|
+
result = await loadServiceFilters('service_with_filters_history');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('has expected filters functions', () => {
|
|
255
|
+
expect(result.removePrintButton).to.be.a('function');
|
|
256
|
+
expect(result.removeShareButton).to.be.a('function');
|
|
257
|
+
expect(result.removePrintButton.name).to.equal('removePrintButton');
|
|
258
|
+
expect(result.removeShareButton.name).to.equal('removeShareButton');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('#getServiceFilters', () => {
|
|
264
|
+
it('returns undefined if filterNames is falsy', () => {
|
|
265
|
+
const result = getServiceFilters({}, undefined);
|
|
266
|
+
|
|
267
|
+
expect(result).to.be.undefined;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('returns filters from exposedFilters by string name', () => {
|
|
271
|
+
const filterNames = Object.keys(exposedFilters);
|
|
272
|
+
|
|
273
|
+
const filterName = filterNames[0];
|
|
274
|
+
const result = getServiceFilters({}, [filterName]);
|
|
275
|
+
|
|
276
|
+
expect(result).to.deep.equal([exposedFilters[filterName]]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('returns filters from serviceFilters by string name', () => {
|
|
280
|
+
const serviceFilters = { custom: () => 'custom' };
|
|
281
|
+
const result = getServiceFilters(serviceFilters, ['custom']);
|
|
282
|
+
|
|
283
|
+
expect(result).to.deep.equal([serviceFilters.custom]);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('returns undefined for unknown filter names', () => {
|
|
287
|
+
const result = getServiceFilters({}, ['notFound']);
|
|
288
|
+
|
|
289
|
+
expect(result).to.be.undefined;
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('wraps object-based filter config and preserves function name', () => {
|
|
293
|
+
const paramFilter = (dom, param) => param;
|
|
294
|
+
const serviceFilters = { paramFilter };
|
|
295
|
+
const result = getServiceFilters(serviceFilters, [{ paramFilter: 'foo' }]);
|
|
296
|
+
|
|
297
|
+
expect(result[0]).to.be.a('function');
|
|
298
|
+
expect(result[0].name).to.equal('paramFilter');
|
|
299
|
+
expect(result[0](null, 'context')).to.equal('foo');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('parameters passed to filters', () => {
|
|
303
|
+
let serviceLoadedFilters;
|
|
304
|
+
let passedDOM;
|
|
305
|
+
let passedContext;
|
|
306
|
+
|
|
307
|
+
before(() => {
|
|
308
|
+
serviceLoadedFilters = { testParamsFilter: (dom, params, context) => ({ dom, params, context }) };
|
|
309
|
+
passedDOM = '<div>test</div>';
|
|
310
|
+
passedContext = { location: 'https://example.com' };
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const testParameterPassing = params => {
|
|
314
|
+
const serviceDeclaredFilters = [{ testParamsFilter: params }];
|
|
315
|
+
const [loadedFilter] = getServiceFilters(serviceLoadedFilters, serviceDeclaredFilters);
|
|
316
|
+
const filterResult = loadedFilter(passedDOM, passedContext);
|
|
317
|
+
|
|
318
|
+
expect(filterResult.params).to.deep.equal(params);
|
|
319
|
+
expect(filterResult.dom).to.equal(passedDOM);
|
|
320
|
+
expect(filterResult.context).to.equal(passedContext);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
context('as a string', () => {
|
|
324
|
+
it('passes parameters correctly', () => {
|
|
325
|
+
testParameterPassing('param');
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
context('as an array', () => {
|
|
330
|
+
it('passes parameters correctly', () => {
|
|
331
|
+
testParameterPassing([ 'param1', 'param2' ]);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
context('as an object', () => {
|
|
336
|
+
it('passes parameters correctly', () => {
|
|
337
|
+
testParameterPassing({ param1: 'param1', param2: 'param2' });
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe('#createSourceDocuments', () => {
|
|
344
|
+
const realFilterNames = Object.keys(exposedFilters);
|
|
345
|
+
const SERVICE_ID = 'service_with_filters_history';
|
|
346
|
+
let result;
|
|
347
|
+
|
|
348
|
+
context('when terms declaration has only one source document', () => {
|
|
349
|
+
const termsDeclaration = {
|
|
350
|
+
fetch: 'https://example.com/terms',
|
|
351
|
+
executeClientScripts: true,
|
|
352
|
+
select: 'body',
|
|
353
|
+
remove: '.ads',
|
|
354
|
+
filter: [
|
|
355
|
+
realFilterNames[0],
|
|
356
|
+
'removePrintButton',
|
|
357
|
+
],
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
before(async () => {
|
|
361
|
+
result = await createSourceDocuments(SERVICE_ID, termsDeclaration);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('creates a single SourceDocument', () => {
|
|
365
|
+
expect(result).to.have.length(1);
|
|
366
|
+
expect(result[0]).to.be.instanceOf(SourceDocument);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('resolves both exposed and custom filters', () => {
|
|
370
|
+
const sourceDocument = result[0];
|
|
371
|
+
|
|
372
|
+
expect(sourceDocument.filters).to.be.an('array');
|
|
373
|
+
expect(sourceDocument.filters).to.have.length(2);
|
|
374
|
+
expect(sourceDocument.filters[0]).to.be.a('function');
|
|
375
|
+
expect(sourceDocument.filters[0]).to.equal(exposedFilters[realFilterNames[0]]);
|
|
376
|
+
expect(sourceDocument.filters[1]).to.be.a('function');
|
|
377
|
+
expect(sourceDocument.filters[1].name).to.equal('removePrintButton');
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('creates a SourceDocument with the correct properties', () => {
|
|
381
|
+
const sourceDocument = result[0];
|
|
382
|
+
|
|
383
|
+
expect(sourceDocument.location).to.equal(termsDeclaration.fetch);
|
|
384
|
+
expect(sourceDocument.executeClientScripts).to.equal(termsDeclaration.executeClientScripts);
|
|
385
|
+
expect(sourceDocument.contentSelectors).to.equal(termsDeclaration.select);
|
|
386
|
+
expect(sourceDocument.insignificantContentSelectors).to.equal(termsDeclaration.remove);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
context('when terms declaration has multiple source documents', () => {
|
|
391
|
+
const termsDeclaration = {
|
|
392
|
+
fetch: 'https://example.com/base',
|
|
393
|
+
executeClientScripts: false,
|
|
394
|
+
select: 'body',
|
|
395
|
+
remove: '.base-ads',
|
|
396
|
+
filter: [
|
|
397
|
+
realFilterNames[0],
|
|
398
|
+
'removePrintButton',
|
|
399
|
+
],
|
|
400
|
+
combine: [
|
|
401
|
+
{
|
|
402
|
+
fetch: 'https://example.com/doc1',
|
|
403
|
+
select: '.content',
|
|
404
|
+
filter: ['removeShareButton'],
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
executeClientScripts: true,
|
|
408
|
+
remove: '.doc2-ads',
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
before(async () => {
|
|
414
|
+
result = await createSourceDocuments(SERVICE_ID, termsDeclaration);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('creates multiple SourceDocuments', () => {
|
|
418
|
+
expect(result).to.have.length(2);
|
|
419
|
+
expect(result[0]).to.be.instanceOf(SourceDocument);
|
|
420
|
+
expect(result[1]).to.be.instanceOf(SourceDocument);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('resolves both exposed and custom filters', () => {
|
|
424
|
+
const firstSourceDocument = result[0];
|
|
425
|
+
const secondSourceDocument = result[1];
|
|
426
|
+
|
|
427
|
+
expect(firstSourceDocument.filters).to.be.an('array');
|
|
428
|
+
expect(firstSourceDocument.filters).to.have.length(1);
|
|
429
|
+
expect(firstSourceDocument.filters[0]).to.be.a('function');
|
|
430
|
+
expect(firstSourceDocument.filters[0].name).to.equal('removeShareButton');
|
|
431
|
+
|
|
432
|
+
expect(secondSourceDocument.filters).to.be.an('array');
|
|
433
|
+
expect(secondSourceDocument.filters).to.have.length(2);
|
|
434
|
+
expect(secondSourceDocument.filters[0]).to.be.a('function');
|
|
435
|
+
expect(secondSourceDocument.filters[0].name).to.equal('removeQueryParams');
|
|
436
|
+
expect(secondSourceDocument.filters[1]).to.be.a('function');
|
|
437
|
+
expect(secondSourceDocument.filters[1].name).to.equal('removePrintButton');
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('combines base properties and source document specific properties correctly', () => {
|
|
441
|
+
const firstSourceDocument = result[0];
|
|
442
|
+
|
|
443
|
+
expect(firstSourceDocument.location).to.equal('https://example.com/doc1');
|
|
444
|
+
expect(firstSourceDocument.contentSelectors).to.equal('.content');
|
|
445
|
+
expect(firstSourceDocument.executeClientScripts).to.equal(false);
|
|
446
|
+
expect(firstSourceDocument.insignificantContentSelectors).to.equal('.base-ads');
|
|
447
|
+
|
|
448
|
+
const secondSourceDocument = result[1];
|
|
449
|
+
|
|
450
|
+
expect(secondSourceDocument.location).to.equal(termsDeclaration.fetch);
|
|
451
|
+
expect(secondSourceDocument.contentSelectors).to.equal(termsDeclaration.select);
|
|
452
|
+
expect(secondSourceDocument.executeClientScripts).to.equal(true);
|
|
453
|
+
expect(secondSourceDocument.insignificantContentSelectors).to.equal('.doc2-ads');
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe('#createServiceFromDeclaration', () => {
|
|
459
|
+
const serviceId = 'service·A';
|
|
460
|
+
let result;
|
|
461
|
+
|
|
462
|
+
before(async () => {
|
|
463
|
+
result = await createServiceFromDeclaration(serviceId);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('creates a Service instance', () => {
|
|
467
|
+
expect(result).to.be.instanceOf(Service);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('sets correct service id', () => {
|
|
471
|
+
expect(result.id).to.equal(serviceId);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('sets correct service name', () => {
|
|
475
|
+
expect(result.name).to.equal('Service·A');
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('adds Terms for the declared terms type', () => {
|
|
479
|
+
const terms = result.getTerms({ type: 'Terms of Service' });
|
|
480
|
+
|
|
481
|
+
expect(terms).to.be.instanceOf(Terms);
|
|
482
|
+
expect(terms.type).to.equal('Terms of Service');
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('creates Terms with correct source documents', () => {
|
|
486
|
+
const terms = result.getTerms({ type: 'Terms of Service' });
|
|
487
|
+
|
|
488
|
+
expect(terms.sourceDocuments).to.be.an('array');
|
|
489
|
+
expect(terms.sourceDocuments).to.have.length(1);
|
|
490
|
+
expect(terms.sourceDocuments[0]).to.be.instanceOf(SourceDocument);
|
|
491
|
+
expect(terms.sourceDocuments[0]).to.deep.equal({
|
|
492
|
+
location: 'https://www.servicea.example/tos',
|
|
493
|
+
executeClientScripts: undefined,
|
|
494
|
+
contentSelectors: 'body',
|
|
495
|
+
insignificantContentSelectors: undefined,
|
|
496
|
+
filters: undefined,
|
|
497
|
+
content: undefined,
|
|
498
|
+
mimeType: undefined,
|
|
499
|
+
id: 'tos',
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
12
504
|
describe('#load', () => {
|
|
13
505
|
let result;
|
|
14
506
|
|
|
@@ -94,7 +586,7 @@ describe('Services', () => {
|
|
|
94
586
|
}
|
|
95
587
|
|
|
96
588
|
before(async () => {
|
|
97
|
-
result = await
|
|
589
|
+
result = await load();
|
|
98
590
|
});
|
|
99
591
|
|
|
100
592
|
describe('Service·A', async () => {
|
|
@@ -127,7 +619,7 @@ describe('Services', () => {
|
|
|
127
619
|
|
|
128
620
|
context('when specifying services to load', () => {
|
|
129
621
|
before(async () => {
|
|
130
|
-
result = await
|
|
622
|
+
result = await load([ 'service·A', 'Service B!' ]);
|
|
131
623
|
});
|
|
132
624
|
|
|
133
625
|
it('loads only the given services', () => {
|
|
@@ -263,7 +755,7 @@ describe('Services', () => {
|
|
|
263
755
|
}
|
|
264
756
|
|
|
265
757
|
before(async () => {
|
|
266
|
-
result = await
|
|
758
|
+
result = await loadWithHistory();
|
|
267
759
|
});
|
|
268
760
|
|
|
269
761
|
describe('Service·A', async () => {
|
|
@@ -296,7 +788,7 @@ describe('Services', () => {
|
|
|
296
788
|
|
|
297
789
|
context('when specifying services to load', () => {
|
|
298
790
|
before(async () => {
|
|
299
|
-
result = await
|
|
791
|
+
result = await loadWithHistory([ 'service·A', 'Service B!' ]);
|
|
300
792
|
});
|
|
301
793
|
|
|
302
794
|
it('loads only the given services', () => {
|