Skip to content

Commit

Permalink
Merge pull request #238 from Financial-Times/x-topic-search-as-class
Browse files Browse the repository at this point in the history
Extend Component class instead of using x-interaction
  • Loading branch information
dan-searle authored Feb 4, 2019
2 parents 0a020f4 + 80c2b45 commit 5ec60bc
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 229 deletions.
9 changes: 5 additions & 4 deletions __tests__/snapshots.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const renderer = require('react-test-renderer');
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const { h } = require('../packages/x-engine');

const {packages} = require('../monorepo.json');

Expand All @@ -16,16 +17,16 @@ for(const pkg of packageDirs) {
const storiesDir = path.resolve(pkgDir, 'stories');

if(fs.existsSync(storiesDir)) {
const { package: pkg, stories, component } = require(storiesDir);
const { presets = {default: {}} } = require(pkgDir);
const { package: pkg, stories, component: Component } = require(storiesDir);
const { presets = { default: {} } } = require(pkgDir);
const name = path.basename(pkg.name);

describe(pkg.name, () => {
for (const { title, data } of stories) {
for (const [ preset, options ] of Object.entries(presets)) {
for (const [preset, options] of Object.entries(presets)) {
it(`renders a ${preset} ${title} ${name}`, () => {
const props = { ...data, ...options };
const tree = renderer.create(component(props)).toJSON();
const tree = renderer.create(h(Component, props)).toJSON();
expect(tree).toMatchSnapshot();
});
}
Expand Down
107 changes: 38 additions & 69 deletions components/x-topic-search/__tests__/x-topic-search.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,28 @@ const { h } = require('@financial-times/x-engine');
const { mount } = require('@financial-times/x-test-utils/enzyme');
const { TopicSearch } = require('../');

const searchTerm = 'Dog';
const searchTermNoResult = 'Blobfish';
const searchTermAllFollowed = 'Cat';
const minSearchLength = 3;
const maxSuggestions = 3;
const apiUrl = 'api-url';
const alreadyFollowedTopics = [
{ uuid: 'Cat-House-id', name: 'Cat House' },
{ uuid: 'Cat-Food-id', name: 'Cat Food' },
{ uuid: 'Cat-Toys-id', name: 'Cat Toys' }
];
const buildSearchUrl = term => `${apiUrl}?count=${maxSuggestions}&partial=${term}`;
const FOLLOWED_TOPIC_ID1 = 'Cat-House-id';
const FOLLOWED_TOPIC_ID2 = 'Cat-Food-id';
const UNFOLLOWED_TOPIC_ID1 = 'Cat-Toys-id';

describe('x-topic-search', () => {
const waitForApiResponse = () => new Promise(resolve => { setTimeout(resolve, 500); });
const buildSearchUrl = term => `${apiUrl}?count=${maxSuggestions}&partial=${term}`;
const enterSearchTerm = searchTerm => {
target.find('input').simulate('input', { target: { value: searchTerm }});

return new Promise(resolve => { setTimeout(resolve, 400); });
};
let target;

beforeEach(() => {
const props = {
minSearchLength,
maxSuggestions,
apiUrl,
followedTopicIds: alreadyFollowedTopics.map(topic => topic.uuid),
followedTopicIds: [FOLLOWED_TOPIC_ID1, FOLLOWED_TOPIC_ID2],
};
target = mount(<TopicSearch {...props} />);
});
Expand All @@ -45,67 +44,66 @@ describe('x-topic-search', () => {
});

describe('given inputted text is shorter than minSearchLength', () => {
it('should not render result', () => {
const wordLessThanMin = searchTerm.slice(0, minSearchLength - 1);
const apiUrlWithResults = buildSearchUrl(wordLessThanMin);
const apiUrlWithResults = buildSearchUrl('a');

fetchMock.get(apiUrlWithResults, []);
fetchMock.get(apiUrlWithResults, []);

target.find('input').simulate('input', { target: { value: wordLessThanMin }});
beforeEach(() => {
return enterSearchTerm('a');
});

return waitForApiResponse().then(() => {
expect(fetchMock.called(apiUrlWithResults)).toBe(false);
expect(target.render().children('div')).toHaveLength(1);
});
it('does not make a request to the api or render any result', () => {
expect(fetchMock.called(apiUrlWithResults)).toBe(false);
expect(target.render().children('div')).toHaveLength(1);
});
});

describe('given searchTerm which has some topic suggestions to follow', () => {
const apiUrlWithResults = buildSearchUrl(searchTerm);
const topicSuggestions = [
{ id: 'Dog-House-id', prefLabel: 'Dog House', url: 'Dog-House-url' },
{ id: 'Dog-Food-id', prefLabel: 'Dog Food', url: 'Dog-Food-url' },
{ id: 'Dog-Toys-id', prefLabel: 'Dog Toys', url: 'Dog-Toys-url' }
const apiUrlWithResults = buildSearchUrl('Cat');
const results = [
{ id: FOLLOWED_TOPIC_ID1, prefLabel: 'Cat House', url: 'Cat-House-url' },
{ id: FOLLOWED_TOPIC_ID2, prefLabel: 'Cat Food', url: 'Cat-Food-url' },
{ id: UNFOLLOWED_TOPIC_ID1, prefLabel: 'Cat Toys', url: 'Cat-Toys-url' }
];

fetchMock.get(apiUrlWithResults, topicSuggestions);
fetchMock.get(apiUrlWithResults, results);

beforeEach(() => {
target.find('input').simulate('input', { target: { value: searchTerm } });

return waitForApiResponse();
return enterSearchTerm('Cat');
});

it('should render topics list with follow button', () => {
it('requests the topic suggestions with count set to maxSuggestions', () => {
expect(fetchMock.called(apiUrlWithResults)).toBe(true);
});

it('renders no more than the max number of suggestions', () => {
expect(target.render().children('div')).toHaveLength(2);
expect(target.render().find('li')).toHaveLength(maxSuggestions);
});

it('renders links and follow buttons for each suggestion', () => {
const suggestionsList = target.render().find('li');

expect(suggestionsList).toHaveLength(maxSuggestions);

topicSuggestions.forEach((topic, index) => {
results.forEach((topic, index) => {
const suggestion = suggestionsList.eq(index);

expect(suggestion.find('a').text()).toEqual(topic.prefLabel);
expect(suggestion.find('a').attr('href')).toEqual(topic.url);
expect(suggestion.find('button')).toHaveLength(1);
expect(suggestion.find('button').text()).toEqual(topic.id === UNFOLLOWED_TOPIC_ID1 ? 'Add to myFT' : 'Added');
});
});
})
});

describe('given searchTerm which has no topic suggestions to follow', () => {
const apiUrlNoResults = buildSearchUrl(searchTermNoResult);
const apiUrlNoResults = buildSearchUrl('Dog');

fetchMock.get(apiUrlNoResults, []);

beforeEach(() => {
target.find('input').simulate('input', { target: { value: searchTermNoResult } });

return waitForApiResponse();
return enterSearchTerm('Dog');
});

it('should render no topic message', () => {
it('requests from the api and renders the no matching topics message', () => {
expect(fetchMock.called(apiUrlNoResults)).toBe(true);

const resultContainer = target.render().children('div').eq(1);
Expand All @@ -114,33 +112,4 @@ describe('x-topic-search', () => {
expect(resultContainer.find('h2').text()).toMatch('No topics matching');
});
});

describe('given searchTerm which all the topics has been followed', () => {
const apiUrlAllFollowed = buildSearchUrl(searchTermAllFollowed);

fetchMock.get(apiUrlAllFollowed, alreadyFollowedTopics.map(topic => ({
id: topic.uuid,
prefLabel: topic.name,
url: topic.name.replace(' ', '-')
})));

beforeEach(() => {
target.find('input').simulate('input', { target: { value: searchTermAllFollowed } });

return waitForApiResponse();
});

it('should render already followed message with name of the topics', () => {
expect(fetchMock.called(apiUrlAllFollowed)).toBe(true);

const resultContainer = target.render().children('div').eq(1);

expect(resultContainer).toHaveLength(1);
expect(resultContainer.text())
.toMatch(
`You already follow ${alreadyFollowedTopics[0].name}, ${alreadyFollowedTopics[1].name} and ${alreadyFollowedTopics[2].name}`
);
});
});

});
1 change: 0 additions & 1 deletion components/x-topic-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"dependencies": {
"@financial-times/x-engine": "file:../../packages/x-engine",
"@financial-times/x-follow-button": "0.0.11",
"@financial-times/x-interaction": "^1.0.0-beta.6",
"classnames": "^2.2.6",
"debounce-promise": "^3.1.0"
},
Expand Down
57 changes: 0 additions & 57 deletions components/x-topic-search/src/ResultContainer.jsx

This file was deleted.

Loading

0 comments on commit 5ec60bc

Please sign in to comment.