@quintype/framework 7.10.4 → 7.10.5-infinite-scroll-tests.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quintype/framework",
3
- "version": "7.10.4",
3
+ "version": "7.10.5-infinite-scroll-tests.0",
4
4
  "description": "Libraries to help build Quintype Node.js apps",
5
5
  "main": "index.js",
6
6
  "engines": {
@@ -31,8 +31,8 @@
31
31
  "homepage": "https://github.com/quintype/quintype-node-framework#readme",
32
32
  "dependencies": {
33
33
  "@ampproject/toolbox-optimizer": "2.8.3",
34
- "@quintype/amp": "^2.5.0",
35
- "@quintype/backend": "^2.3.1",
34
+ "@quintype/amp": "^2.6.0",
35
+ "@quintype/backend": "^2.3.2",
36
36
  "@quintype/components": "^3.0.0",
37
37
  "@quintype/prerender-node": "^3.2.26",
38
38
  "@quintype/seo": "^1.39.0",
@@ -57,7 +57,7 @@ class InfiniteScrollAmp {
57
57
  };
58
58
  if (this.infiniteScrollSource === "relatedStoriesApi") {
59
59
  const relatedStoriesList = await this.client.getRelatedStories(storyId, null, params);
60
- if (!relatedStoriesList)
60
+ if (!relatedStoriesList || (relatedStoriesList["related-stories"] && !relatedStoriesList["related-stories"].length) || relatedStoriesList["related-stories"].error || relatedStoriesList === null)
61
61
  return new Error();
62
62
  return filteredItems = this.getFilteredApiItems(relatedStoriesList["related-stories"]);
63
63
  } else {
@@ -3,17 +3,8 @@
3
3
 
4
4
  const assert = require("assert");
5
5
  const InfiniteScrollAmp = require("../../../server/amp/helpers/infinite-scroll");
6
- const { getTextStory } = require("../../data/amp-test-data");
7
6
 
8
7
  function getClientStub({
9
- getStoryById = (id) =>
10
- new Promise((resolve) => {
11
- if (id === "4444")
12
- resolve({
13
- story: getTextStory({ "story-content-id": "7f3d5bdb-ec52-4047-ac0d-df4036ec974b" }),
14
- });
15
- resolve(null);
16
- }),
17
8
  getCollectionBySlug = (slug) =>
18
9
  new Promise((resolve) => {
19
10
  if (slug === "amp-infinite-scroll")
@@ -122,7 +113,156 @@ function getClientStub({
122
113
  } = {}) {
123
114
  return {
124
115
  getCollectionBySlug,
125
- getStoryById,
116
+ };
117
+ }
118
+
119
+ function getRelatedStoriesClientStub({
120
+ getRelatedStories = () =>
121
+ new Promise((resolve) => {
122
+ resolve({
123
+ "related-stories": [
124
+ {
125
+ "story-template": "text",
126
+ headline: "aaa",
127
+ "story-content-id": 1111,
128
+ slug: "sports/aa",
129
+ "hero-image-s3-key": "aa/a.jpg",
130
+ access: "public",
131
+ },
132
+ {
133
+ "story-template": "visual-story",
134
+ headline: "bbb",
135
+ "story-content-id": 2222,
136
+ slug: "sports/bb",
137
+ "hero-image-s3-key": "bb/b.jpg",
138
+ access: "public",
139
+ },
140
+ {
141
+ "story-template": "text",
142
+ headline: "ccc",
143
+ "story-content-id": 3333,
144
+ slug: "sports/cc",
145
+ "hero-image-s3-key": "cc/c.jpg",
146
+ access: "public",
147
+ },
148
+ {
149
+ "story-template": "text",
150
+ headline: "ddd",
151
+ "story-content-id": 4444,
152
+ slug: "sports/dd",
153
+ "hero-image-s3-key": "dd/d.jpg",
154
+ access: "public",
155
+ },
156
+ {
157
+ "story-template": "text",
158
+ headline: "eee",
159
+ "story-content-id": 5555,
160
+ slug: "politics/ee",
161
+ "hero-image-s3-key": "ee/e.jpg",
162
+ access: "public",
163
+ },
164
+ {
165
+ "story-template": "text",
166
+ headline: "fff",
167
+ "story-content-id": 6666,
168
+ slug: "politics/ff",
169
+ "hero-image-s3-key": "ff/f.jpg",
170
+ access: "public",
171
+ },
172
+ {
173
+ "story-template": "text",
174
+ headline: "ggg",
175
+ "story-content-id": 7777,
176
+ slug: "politics/gg",
177
+ "hero-image-s3-key": "gg/g.jpg",
178
+ access: "public",
179
+ },
180
+ {
181
+ "story-template": "text",
182
+ headline: "hhh",
183
+ "story-content-id": 8888,
184
+ slug: "politics/hh",
185
+ "hero-image-s3-key": "hh/h.jpg",
186
+ access: "public",
187
+ },
188
+ ],
189
+ });
190
+ }),
191
+ } = {}) {
192
+ return {
193
+ getRelatedStories,
194
+ };
195
+ }
196
+
197
+ function getCustomApiStoriesClientStub({
198
+ getCustomApiStories = () =>
199
+ new Promise((resolve) => {
200
+ resolve(
201
+ [
202
+ {
203
+ title: "aaa",
204
+ url: "sports/aa",
205
+ image: "aa/a.jpg",
206
+ },
207
+ {
208
+ title: "bbb",
209
+ url: "sports/bb",
210
+ image: "bb/b.jpg",
211
+ },
212
+ {
213
+ title: "ccc",
214
+ url: "sports/cc",
215
+ image: "cc/c.jpg",
216
+ },
217
+ {
218
+ title: "ddd",
219
+ url: "sports/dd",
220
+ image: "dd/d.jpg",
221
+ },
222
+ {
223
+ title: "eee",
224
+ url: "politics/ee",
225
+ image: "ee/e.jpg",
226
+ },
227
+ {
228
+ title: "fff",
229
+ url: "politics/ff",
230
+ image: "ff/f.jpg",
231
+ },
232
+ {
233
+ title: "ggg",
234
+ url: "politics/gg",
235
+ image: "gg/g.jpg",
236
+ },
237
+ {
238
+ title: "hhh",
239
+ url: "politics/hh",
240
+ image: "hh/h.jpg",
241
+ },
242
+ ]
243
+ );
244
+ }),
245
+ } = {}) {
246
+ return {
247
+ getCustomApiStories,
248
+ };
249
+ }
250
+ class CustomInfiniteScrollAmp {
251
+ constructor({ client, publisherConfig, queryParams, infiniteScrollSource }) {
252
+ this.client = client;
253
+ this.publisherConfig = publisherConfig;
254
+ this.queryParams = queryParams;
255
+ this.infiniteScrollSource = infiniteScrollSource;
256
+ }
257
+
258
+ async getCustomStoryList({ type = "inlineConfig" }) {
259
+ const customStories = await this.client.getCustomApiStories();
260
+ if (!customStories || customStories === null || customStories.length === 0)
261
+ return new Error();
262
+ if (type === "remoteConfig") {
263
+ return JSON.stringify({ pages: customStories });
264
+ }
265
+ return JSON.stringify(customStories);
126
266
  };
127
267
  }
128
268
  const dummyPublisherConfig = {
@@ -130,6 +270,7 @@ const dummyPublisherConfig = {
130
270
  };
131
271
 
132
272
  describe("getInitialInlineConfig method of InfiniteScrollAmp helper function", function () {
273
+ // collection
133
274
  it("should throw err if storyId isn't passed", async function () {
134
275
  const infiniteScrollAmp = new InfiniteScrollAmp({
135
276
  ampConfig: {},
@@ -230,9 +371,166 @@ describe("getInitialInlineConfig method of InfiniteScrollAmp helper function", f
230
371
  // it("should take the first 'n' stories", async function() {
231
372
  // // this test needs to be written after https://github.com/quintype/quintype-node-framework/pull/202 is merged
232
373
  // })
374
+ /// related-stories
375
+ it("should throw err if storyId isn't passed for relatedStoriesApi", async function () {
376
+ const infiniteScrollAmp = new InfiniteScrollAmp({
377
+ ampConfig: {},
378
+ client: getRelatedStoriesClientStub(),
379
+ publisherConfig: dummyPublisherConfig,
380
+ infiniteScrollSource: "relatedStoriesApi",
381
+ });
382
+ const inlineConfig = await infiniteScrollAmp.getInitialInlineConfig({ storyId: "" });
383
+ assert.strictEqual(inlineConfig instanceof Error, true);
384
+ assert.throws(() => {
385
+ throw new Error("Required params for getInitialInlineConfig missing");
386
+ }, inlineConfig);
387
+ });
388
+
389
+ it("should return null if relatedStoriesApi infinite scroll collection doesn't exist", async function () {
390
+ const clientStub = getRelatedStoriesClientStub({
391
+ getRelatedStories: () =>
392
+ new Promise((resolve) => {
393
+ resolve(null);
394
+ }),
395
+ });
396
+ const infiniteScrollAmp = new InfiniteScrollAmp({
397
+ ampConfig: {},
398
+ client: clientStub,
399
+ publisherConfig: dummyPublisherConfig,
400
+ infiniteScrollSource: "relatedStoriesApi",
401
+ });
402
+ const inlineConfig = await infiniteScrollAmp.getInitialInlineConfig({
403
+ storyId: 2222,
404
+ });
405
+ assert.strictEqual(inlineConfig, null);
406
+ });
407
+
408
+ it("should return null if relatedStoriesApi infinite scroll collection contains no stories", async function () {
409
+ const clientStub = getRelatedStoriesClientStub({
410
+ getRelatedStories: () =>
411
+ new Promise((resolve) => {
412
+ resolve({
413
+ "related-stories": [],
414
+ });
415
+ }),
416
+ });
417
+ const infiniteScrollAmp = new InfiniteScrollAmp({
418
+ ampConfig: {},
419
+ client: clientStub,
420
+ publisherConfig: dummyPublisherConfig,
421
+ infiniteScrollSource: "relatedStoriesApi",
422
+ });
423
+ const inlineConfig = await infiniteScrollAmp.getInitialInlineConfig({
424
+ storyId: 2222,
425
+ });
426
+ assert.strictEqual(inlineConfig, null);
427
+ });
428
+
429
+ it("should remove visual stories from relatedStoriesApi infinite scroll", async function () {
430
+ const clientStub = getRelatedStoriesClientStub();
431
+ const infiniteScrollAmp = new InfiniteScrollAmp({
432
+ ampConfig: {},
433
+ client: clientStub,
434
+ publisherConfig: dummyPublisherConfig,
435
+ infiniteScrollSource: "relatedStoriesApi",
436
+ });
437
+ const inlineConfig = await infiniteScrollAmp.getInitialInlineConfig({
438
+ storyId: 3333,
439
+ });
440
+
441
+ assert.strictEqual(false, /sports\/bb/.test(inlineConfig));
442
+ assert.strictEqual(false, /bb\/b.jpg/.test(inlineConfig));
443
+ });
444
+
445
+ it("Related Stories Response should be in JSON format as per AMP spec", async function () {
446
+ // https://amp.dev/documentation/components/amp-next-page/
447
+ const clientStub = getRelatedStoriesClientStub();
448
+ const infiniteScrollAmp = new InfiniteScrollAmp({
449
+ ampConfig: {},
450
+ client: clientStub,
451
+ publisherConfig: dummyPublisherConfig,
452
+ infiniteScrollSource: "relatedStoriesApi",
453
+ });
454
+ const inlineConfig = await infiniteScrollAmp.getInitialInlineConfig({
455
+ storyId: 2222,
456
+ });
457
+ function isInlineConfigStructureValid(jsonStr) {
458
+ const stories = JSON.parse(jsonStr);
459
+ if (!stories.length) throw new Error("Can't verify empty array!");
460
+ stories.forEach((story) => {
461
+ const keys = Object.keys(story);
462
+ if (keys.includes("image") && keys.includes("url") && keys.includes("title") && keys.length === 3) return;
463
+ throw new Error("Invalid InlineConfigStructure");
464
+ });
465
+ return true;
466
+ }
467
+ assert.strictEqual(isInlineConfigStructureValid(inlineConfig), true);
468
+ });
469
+
470
+ /// custom-stories
471
+ it("should return null if CustomApi Stories infinite scroll collection doesn't exist", async function () {
472
+ const clientStub = getCustomApiStoriesClientStub({
473
+ getCustomApiStories: () =>
474
+ new Promise((resolve) => {
475
+ resolve(null);
476
+ }),
477
+ });
478
+ const customInfiniteScrollAmp = new CustomInfiniteScrollAmp({
479
+ client: clientStub,
480
+ publisherConfig: dummyPublisherConfig,
481
+ infiniteScrollSource: "custom",
482
+ });
483
+ const inlineConfig = await customInfiniteScrollAmp.getCustomStoryList({
484
+ type: "inlineConfig",
485
+ });
486
+ assert.strictEqual(inlineConfig instanceof Error, true);
487
+ });
488
+
489
+ it("should return null if CustomApi Stories infinite scroll collection contains no stories", async function () {
490
+ const clientStub = getCustomApiStoriesClientStub({
491
+ getCustomApiStories: () =>
492
+ new Promise((resolve) => {
493
+ resolve([]);
494
+ }),
495
+ });
496
+ const customInfiniteScrollAmp = new CustomInfiniteScrollAmp({
497
+ client: clientStub,
498
+ publisherConfig: dummyPublisherConfig,
499
+ infiniteScrollSource: "custom",
500
+ });
501
+ const inlineConfig = await customInfiniteScrollAmp.getCustomStoryList({
502
+ type: "inlineConfig",
503
+ });
504
+ assert.strictEqual(inlineConfig instanceof Error, true);
505
+ });
506
+
507
+ it("CustomApi Stories Response should be in JSON format as per AMP spec", async function () {
508
+ // https://amp.dev/documentation/components/amp-next-page/
509
+ const clientStub = getCustomApiStoriesClientStub();
510
+ const customInfiniteScrollAmp = new CustomInfiniteScrollAmp({
511
+ client: clientStub,
512
+ publisherConfig: dummyPublisherConfig,
513
+ infiniteScrollSource: "custom",
514
+ });
515
+ const inlineConfig = await customInfiniteScrollAmp.getCustomStoryList({
516
+ type: "inlineConfig",
517
+ });
518
+ function isInlineConfigStructureValid(jsonStr) {
519
+ const stories = JSON.parse(jsonStr);
520
+ if (!stories.length) throw new Error("Can't verify empty array!");
521
+ stories.forEach((story) => {
522
+ const keys = Object.keys(story);
523
+ if (keys.includes("image") && keys.includes("url") && keys.includes("title") && keys.length === 3) return;
524
+ throw new Error("Invalid InlineConfigStructure");
525
+ });
526
+ return true;
527
+ }
528
+ assert.strictEqual(isInlineConfigStructureValid(inlineConfig), true);
529
+ });
233
530
  });
234
531
 
235
532
  describe("getResponse method of InfiniteScrollAmp helper function", function () {
533
+ // collection
236
534
  it("should throw an error if 'story-id' isn't passed as query param", async function () {
237
535
  const infiniteScrollAmp = new InfiniteScrollAmp({
238
536
  ampConfig: {},
@@ -240,7 +538,7 @@ describe("getResponse method of InfiniteScrollAmp helper function", function ()
240
538
  publisherConfig: dummyPublisherConfig,
241
539
  queryParams: { foo: "bar" },
242
540
  });
243
- const jsonResponse = await infiniteScrollAmp.getResponse({ itemsTaken: 2 });
541
+ const jsonResponse = await infiniteScrollAmp.getResponse();
244
542
  assert.strictEqual(jsonResponse instanceof Error, true);
245
543
  assert.throws(() => {
246
544
  throw new Error(`Query param "story-id" missing`);
@@ -259,7 +557,7 @@ describe("getResponse method of InfiniteScrollAmp helper function", function ()
259
557
  publisherConfig: dummyPublisherConfig,
260
558
  queryParams: { "story-id": 2222 },
261
559
  });
262
- const jsonResponse = await infiniteScrollAmp.getResponse({ itemsTaken: 2 });
560
+ const jsonResponse = await infiniteScrollAmp.getResponse();
263
561
  assert.strictEqual(jsonResponse instanceof Error, true);
264
562
  });
265
563
  it("should remove current story from response", async function () {
@@ -270,7 +568,7 @@ describe("getResponse method of InfiniteScrollAmp helper function", function ()
270
568
  publisherConfig: dummyPublisherConfig,
271
569
  queryParams: { "story-id": 4444 },
272
570
  });
273
- const jsonResponse = await infiniteScrollAmp.getResponse({ itemsTaken: 2 });
571
+ const jsonResponse = await infiniteScrollAmp.getResponse();
274
572
  assert.strictEqual(false, /sports\/dd/.test(jsonResponse));
275
573
  assert.strictEqual(false, /dd\/d.jpg/.test(jsonResponse));
276
574
  });
@@ -282,7 +580,7 @@ describe("getResponse method of InfiniteScrollAmp helper function", function ()
282
580
  publisherConfig: dummyPublisherConfig,
283
581
  queryParams: { "story-id": 4444 },
284
582
  });
285
- const jsonResponse = await infiniteScrollAmp.getResponse({ itemsTaken: 2 });
583
+ const jsonResponse = await infiniteScrollAmp.getResponse();
286
584
  assert.strictEqual(false, /sports\/bb/.test(jsonResponse));
287
585
  assert.strictEqual(false, /bb\/b.jpg/.test(jsonResponse));
288
586
  });
@@ -295,7 +593,7 @@ describe("getResponse method of InfiniteScrollAmp helper function", function ()
295
593
  publisherConfig: dummyPublisherConfig,
296
594
  queryParams: { "story-id": 4444 },
297
595
  });
298
- const jsonResponse = await infiniteScrollAmp.getResponse({ itemsTaken: 2 });
596
+ const jsonResponse = await infiniteScrollAmp.getResponse();
299
597
  function isJsonConfigStructureValid(jsonStr) {
300
598
  const parsed = JSON.parse(jsonStr);
301
599
  const stories = parsed.pages;
@@ -312,4 +610,120 @@ describe("getResponse method of InfiniteScrollAmp helper function", function ()
312
610
  // it("should omit the first 'n' stories, take the rest", async function() {
313
611
  // // this test needs to be written after https://github.com/quintype/quintype-node-framework/pull/202 is merged
314
612
  // })
613
+ // related-stories
614
+ it("should throw an error if 'story-id' isn't passed as query param in relatedStoriesApi", async function () {
615
+ const infiniteScrollAmp = new InfiniteScrollAmp({
616
+ ampConfig: {},
617
+ client: getRelatedStoriesClientStub(),
618
+ publisherConfig: dummyPublisherConfig,
619
+ queryParams: { foo: "bar" },
620
+ infiniteScrollSource: "relatedStoriesApi",
621
+ });
622
+ const jsonResponse = await infiniteScrollAmp.getResponse();
623
+ assert.strictEqual(jsonResponse instanceof Error, true);
624
+ assert.throws(() => {
625
+ throw new Error(`Query param "story-id" missing`);
626
+ }, jsonResponse);
627
+ });
628
+
629
+ it("should throw an error if relatedStoriesApi infinite scroll collection doesn't exist", async function () {
630
+ const clientStub = getRelatedStoriesClientStub({
631
+ getRelatedStories: () =>
632
+ new Promise((resolve) => {
633
+ resolve(null);
634
+ }),
635
+ });
636
+ const infiniteScrollAmp = new InfiniteScrollAmp({
637
+ ampConfig: {},
638
+ client: clientStub,
639
+ publisherConfig: dummyPublisherConfig,
640
+ queryParams: { "story-id": 2222 },
641
+ infiniteScrollSource: "relatedStoriesApi",
642
+ });
643
+ const jsonResponse = await infiniteScrollAmp.getResponse();
644
+ assert.strictEqual(jsonResponse instanceof Error, true);
645
+ });
646
+
647
+ it("should remove visual stories from relatedStoriesApi response", async function () {
648
+ const clientStub = getRelatedStoriesClientStub();
649
+ const infiniteScrollAmp = new InfiniteScrollAmp({
650
+ ampConfig: {},
651
+ client: clientStub,
652
+ publisherConfig: dummyPublisherConfig,
653
+ queryParams: { "story-id": 4444 },
654
+ infiniteScrollSource: "relatedStoriesApi",
655
+ });
656
+ const jsonResponse = await infiniteScrollAmp.getResponse();
657
+ assert.strictEqual(false, /sports\/bb/.test(jsonResponse));
658
+ assert.strictEqual(false, /bb\/b.jpg/.test(jsonResponse));
659
+ });
660
+
661
+ it("should format JSON as per AMP spec in relatedStoriesApi", async function () {
662
+ // https://amp.dev/documentation/components/amp-next-page/
663
+ const clientStub = getRelatedStoriesClientStub();
664
+ const infiniteScrollAmp = new InfiniteScrollAmp({
665
+ ampConfig: {},
666
+ client: clientStub,
667
+ publisherConfig: dummyPublisherConfig,
668
+ queryParams: { "story-id": 4444 },
669
+ infiniteScrollSource: "relatedStoriesApi",
670
+ });
671
+ const jsonResponse = await infiniteScrollAmp.getResponse();
672
+ function isJsonConfigStructureValid(jsonStr) {
673
+ const parsed = JSON.parse(jsonStr);
674
+ const stories = parsed.pages;
675
+ if (!stories.length) throw new Error("Can't verify empty array!");
676
+ stories.forEach((story) => {
677
+ const keys = Object.keys(story);
678
+ if (keys.includes("image") && keys.includes("url") && keys.includes("title") && keys.length === 3) return;
679
+ throw new Error("Invalid InlineConfigStructure");
680
+ });
681
+ return true;
682
+ }
683
+ assert.strictEqual(isJsonConfigStructureValid(jsonResponse), true);
684
+ });
685
+
686
+ // custom-stories
687
+ it("should throw an error if customApi infinite scroll collection doesn't exist", async function () {
688
+ const clientStub = getCustomApiStoriesClientStub({
689
+ getCustomApiStories: () =>
690
+ new Promise((resolve) => {
691
+ resolve(null);
692
+ }),
693
+ });
694
+ const customInfiniteScrollAmp = new CustomInfiniteScrollAmp({
695
+ client: clientStub,
696
+ publisherConfig: dummyPublisherConfig,
697
+ infiniteScrollSource: "custom",
698
+ });
699
+ const jsonResponse = await customInfiniteScrollAmp.getCustomStoryList({
700
+ type: "remoteConfig",
701
+ });
702
+ assert.strictEqual(jsonResponse instanceof Error, true);
703
+ });
704
+
705
+ it("should format JSON as per AMP spec in customApi list", async function () {
706
+ // https://amp.dev/documentation/components/amp-next-page/
707
+ const clientStub = getCustomApiStoriesClientStub();
708
+ const customInfiniteScrollAmp = new CustomInfiniteScrollAmp({
709
+ client: clientStub,
710
+ publisherConfig: dummyPublisherConfig,
711
+ infiniteScrollSource: "custom",
712
+ });
713
+ const jsonResponse = await customInfiniteScrollAmp.getCustomStoryList({
714
+ type: "remoteConfig",
715
+ });
716
+ function isJsonConfigStructureValid(jsonStr) {
717
+ const parsed = JSON.parse(jsonStr);
718
+ const stories = parsed.pages;
719
+ if (!stories.length) throw new Error("Can't verify empty array!");
720
+ stories.forEach((story) => {
721
+ const keys = Object.keys(story);
722
+ if (keys.includes("image") && keys.includes("url") && keys.includes("title") && keys.length === 3) return;
723
+ throw new Error("Invalid InlineConfigStructure");
724
+ });
725
+ return true;
726
+ }
727
+ assert.strictEqual(isJsonConfigStructureValid(jsonResponse), true);
728
+ });
315
729
  });