* feat: fix of there is no article * feat: set the feed order in a data structure rather than in a view * feat: update yarn.lock * feat: push the items to the array * feat: update feed to only show billboards if we have the correct length of items in the feed * fix: organizedFeedItems.legth * feat: rename 'featured' to 'image' * feat: rename 'featured' to 'image' * feat: rename 'featured' to 'image' * feat: add some utilities for the feed that we can use in the FeedTest * feat/WIP: the setup for the feed tests and a first working test * test: imageItem * fix: the podcasts can be passed through as an array of objects in the feedItems so that they can be grouped in one card * feat: remove podcastEpisode state and the attribute in the object * fix: check that the items exist before trying to slice them * feat: setup the userdata and the podcast data in the test * whoops - commit the podcast episodes * feat: write soem more tests for the feed and including the podcasts * feat: add some more tests * feat: add more billboards tests to chcek the order of stuff * feat: set the timeframe not empty * feat: update the logic for organizaed feed by inserting the last on first * doc: jsdoc for functions * refactor: break the code up into smaller functions * refactor: make the code more readable and easier to follow * refactor: pull function out into a utlity * feat: add specs to utility * feat: chcek if pinned post * test the latest timeframe correctly * chore: update var name * move the podcast items out of the object clause * feat: update text * feat: add a pack file that duplicates initializeDisplayAdVisibility * feat: create callbacks that will help us to determine when the feed has been rendered so that we can observe the dsplay ads accordingly * chore: rename to billboards instead of display ad * feat: first pass at some error handling whilst maintaining the order of the items * test: for feed error * feat: abstract out some code * update the feed items for errors * chore: comment * feat: update the variable names * feat: update honeybadger message
300 lines
11 KiB
JavaScript
300 lines
11 KiB
JavaScript
/* eslint-disable no-irregular-whitespace */
|
|
import { h } from 'preact';
|
|
import { render, waitFor } from '@testing-library/preact';
|
|
import fetch from 'jest-fetch-mock';
|
|
import '@testing-library/jest-dom';
|
|
import { Feed } from '../Feed';
|
|
import {
|
|
feedPosts,
|
|
feedPostsWherePinnedAndImagePostsSame,
|
|
firstBillboard,
|
|
secondBillboard,
|
|
thirdBillboard,
|
|
podcastEpisodes,
|
|
} from './utilities/feedUtilities';
|
|
import '../../../assets/javascripts/lib/xss';
|
|
import '../../../assets/javascripts/utilities/timeAgo';
|
|
|
|
global.fetch = fetch;
|
|
|
|
describe('<Feed /> component', () => {
|
|
const getUserData = () => {
|
|
return {
|
|
followed_tag_names: ['javascript'],
|
|
followed_podcast_ids: [1], //should we make this dynamic
|
|
profile_image_90: 'mock_url_link',
|
|
name: 'firstname lastname',
|
|
username: 'username',
|
|
reading_list_ids: [],
|
|
};
|
|
};
|
|
|
|
beforeAll(() => {
|
|
global.userData = jest.fn(() => getUserData());
|
|
document.body.setAttribute('data-user', JSON.stringify(getUserData()));
|
|
|
|
const node = document.createElement('div');
|
|
node.setAttribute('id', 'followed-podcasts');
|
|
node.setAttribute('data-episodes', JSON.stringify(podcastEpisodes));
|
|
document.body.appendChild(node);
|
|
});
|
|
|
|
describe('feedItem organization', () => {
|
|
let callback;
|
|
beforeAll(() => {
|
|
fetch.mockResponseOnce(JSON.stringify(feedPosts), {
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
fetch.mockResponseOnce(firstBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(secondBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(thirdBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
|
|
callback = jest.fn();
|
|
render(<Feed timeFrame="" renderFeed={callback} />);
|
|
});
|
|
|
|
it('should return the correct length of feedItems', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems.length).toEqual(14);
|
|
});
|
|
});
|
|
|
|
it('should set the pinnedItem and place it correctly in the feed', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
const firstPinnedItem = feedPosts.find((o) => o.pinned === true);
|
|
expect(lastCallbackResult.pinnedItem).toEqual(firstPinnedItem);
|
|
expect(lastCallbackResult.feedItems[1]).toEqual(firstPinnedItem);
|
|
});
|
|
});
|
|
|
|
it('should set the imageItem and place it correctly in the feed', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
const firstImageItem = feedPosts.find(
|
|
(post) => post.main_image !== null,
|
|
);
|
|
expect(lastCallbackResult.imageItem).toEqual(firstImageItem);
|
|
expect(lastCallbackResult.feedItems[2]).toEqual(firstImageItem);
|
|
});
|
|
});
|
|
|
|
it('should place the billboards correctly within the feedItems', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems[0]).toEqual(firstBillboard);
|
|
expect(lastCallbackResult.feedItems[3]).toEqual(secondBillboard);
|
|
expect(lastCallbackResult.feedItems[9]).toEqual(thirdBillboard);
|
|
});
|
|
});
|
|
|
|
it('should place the podcasts correctly within feedItems', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems[4]).toEqual(podcastEpisodes);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when pinned and image posts are the same', () => {
|
|
let callback;
|
|
beforeAll(() => {
|
|
fetch.mockResponseOnce(
|
|
JSON.stringify(feedPostsWherePinnedAndImagePostsSame),
|
|
{ headers: { 'content-type': 'application/json' } },
|
|
);
|
|
fetch.mockResponseOnce(firstBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(secondBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(thirdBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
|
|
callback = jest.fn();
|
|
render(<Feed timeFrame="" renderFeed={callback} />);
|
|
});
|
|
|
|
it('should not set a pinned item', async () => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
const postAndImageItem = feedPostsWherePinnedAndImagePostsSame.find(
|
|
(post) => post.main_image !== null && post.pinned === true,
|
|
);
|
|
|
|
expect(lastCallbackResult.pinnedItem).toEqual(null);
|
|
expect(lastCallbackResult.imageItem).toEqual(postAndImageItem);
|
|
expect(lastCallbackResult.feedItems[1]).toEqual(postAndImageItem);
|
|
expect(lastCallbackResult.feedItems[2]).not.toEqual(postAndImageItem);
|
|
});
|
|
});
|
|
|
|
describe("when the timeframe prop is 'latest'", () => {
|
|
let callback;
|
|
beforeAll(() => {
|
|
fetch.mockResponseOnce(JSON.stringify(feedPosts), {
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
fetch.mockResponseOnce(firstBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(secondBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(thirdBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
|
|
callback = jest.fn();
|
|
render(<Feed timeFrame="latest" renderFeed={callback} />);
|
|
});
|
|
|
|
it('should not set the pinned items', async () => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.pinnedItem).toEqual(null);
|
|
});
|
|
|
|
it('should return the correct length of feedItems (by excluding pinned item)', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems.length).toEqual(13);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when we there isn't all three billboards on the home feed", () => {
|
|
describe("when there isn't a feed_second billboard", () => {
|
|
let callback;
|
|
beforeAll(() => {
|
|
fetch.mockResponseOnce(JSON.stringify(feedPosts), {
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
fetch.mockResponseOnce(firstBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
fetch.mockResponseOnce(undefined);
|
|
fetch.mockResponseOnce(thirdBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
|
|
callback = jest.fn();
|
|
render(<Feed timeFrame="" renderFeed={callback} />);
|
|
});
|
|
|
|
it('should return the correct length of feedItems', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems.length).toEqual(13);
|
|
});
|
|
});
|
|
|
|
it('should still amend the organization of the feedItems correctly', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems[0]).toEqual(firstBillboard);
|
|
// there is no second bilboard so podcasts get rendered in 4th place
|
|
expect(lastCallbackResult.feedItems[3]).toEqual(podcastEpisodes);
|
|
expect(lastCallbackResult.feedItems[8]).toEqual(thirdBillboard);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when there isn't a feed_first or feed_second billboard", () => {
|
|
let callback;
|
|
beforeAll(() => {
|
|
fetch.mockResponseOnce(JSON.stringify(feedPosts), {
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
fetch.mockResponseOnce(undefined);
|
|
fetch.mockResponseOnce(undefined);
|
|
fetch.mockResponseOnce(thirdBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
|
|
callback = jest.fn();
|
|
render(<Feed timeFrame="" renderFeed={callback} />);
|
|
});
|
|
|
|
it('should return the correct length of feedItems', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems.length).toEqual(12);
|
|
});
|
|
});
|
|
|
|
it('should still amend the organization of the feedItems correctly', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
|
|
const pinnedItem = feedPosts.find((o) => o.pinned === true);
|
|
// there is no first billboard
|
|
expect(lastCallbackResult.feedItems[0]).toEqual(pinnedItem);
|
|
// there is no second bilboard so podcasts get rendered in 3rd place
|
|
expect(lastCallbackResult.feedItems[2]).toEqual(podcastEpisodes);
|
|
expect(lastCallbackResult.feedItems[7]).toEqual(thirdBillboard);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when items that we fetch for the feed throw an error', () => {
|
|
const callback = jest.fn();
|
|
|
|
beforeAll(() => {
|
|
global.Honeybadger = { notify: jest.fn() };
|
|
|
|
fetch.mockResponseOnce(JSON.stringify(feedPosts), {
|
|
headers: { 'content-type': 'application/json' },
|
|
});
|
|
fetch.mockRejectOnce();
|
|
fetch.mockRejectOnce();
|
|
fetch.mockResponseOnce(thirdBillboard, {
|
|
headers: { 'content-type': 'text/html' },
|
|
});
|
|
render(<Feed timeFrame="" renderFeed={callback} />);
|
|
});
|
|
|
|
it('should render and return the other feedItems', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
expect(lastCallbackResult.feedItems.length).toEqual(12);
|
|
});
|
|
});
|
|
|
|
it('should organize the feedItems correctly', async () => {
|
|
await waitFor(() => {
|
|
const lastCallbackResult =
|
|
callback.mock.calls[callback.mock.calls.length - 1][0];
|
|
|
|
const pinnedItem = feedPosts.find((o) => o.pinned === true);
|
|
// we will not be rendering the first billboard since it errored
|
|
expect(lastCallbackResult.feedItems[0]).toEqual(pinnedItem);
|
|
// we will not be rendering the second billboard since it errored
|
|
// so podcasts get rendered in 3rd place
|
|
expect(lastCallbackResult.feedItems[2]).toEqual(podcastEpisodes);
|
|
expect(lastCallbackResult.feedItems[7]).toEqual(thirdBillboard);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|