From 441293dbe45230f31560be75e9b6e0cd3622fd40 Mon Sep 17 00:00:00 2001 From: Alexandre Stanislawski Date: Fri, 3 Mar 2017 17:07:46 +0100 Subject: [PATCH] feat(connector): test connectToggle --- dev/app.js | 23 +- dev/index.html | 1 + .../stats/__tests__/connectStats-test.js | 2 +- .../toggle/__tests__/connectToggle-test.js | 389 ++++++++++++++++++ src/connectors/toggle/connectToggle.js | 10 +- src/widgets/toggle/toggle.js | 4 +- 6 files changed, 420 insertions(+), 9 deletions(-) create mode 100644 src/connectors/toggle/__tests__/connectToggle-test.js diff --git a/dev/app.js b/dev/app.js index 0e9c7014f0..cad6cff70d 100644 --- a/dev/app.js +++ b/dev/app.js @@ -317,7 +317,7 @@ search.addWidget( instantsearch.widgets.toggle({ container: '#free-shipping', attributeName: 'free_shipping', - label: 'Free Shipping', + label: 'Free Shipping (toggle single value)', cssClasses: { header: 'facet-title', item: 'facet-value checkbox', @@ -330,6 +330,27 @@ search.addWidget( }) ); +search.addWidget( + instantsearch.widgets.toggle({ + container: '#google-amazon', + attributeName: 'brand', + label: 'Canon (not checked) or sony (checked)', + cssClasses: { + header: 'facet-title', + item: 'facet-value checkbox', + count: 'facet-count pull-right', + active: 'facet-active', + }, + values: { + on: 'Sony', + off: 'Canon', + }, + templates: { + header: 'Google or amazon (toggle two values)', + }, + }) +); + search.addWidget( instantsearch.widgets.menu({ container: '#categories', diff --git a/dev/index.html b/dev/index.html index f2803a43e5..c632648077 100644 --- a/dev/index.html +++ b/dev/index.html @@ -27,6 +27,7 @@

Instant search demo using instantsearch.js
+
diff --git a/src/connectors/stats/__tests__/connectStats-test.js b/src/connectors/stats/__tests__/connectStats-test.js index 4da9fcb64b..5339368f91 100644 --- a/src/connectors/stats/__tests__/connectStats-test.js +++ b/src/connectors/stats/__tests__/connectStats-test.js @@ -11,7 +11,7 @@ import connectStats from '../connectStats.js'; const fakeClient = {addAlgoliaAgent: () => {}}; describe('connectStats', () => { - it.only('Renders during init and render', () => { + it('Renders during init and render', () => { const container = document.createElement('div'); // test that the dummyRendering is called with the isFirstRendering // flag set accordingly diff --git a/src/connectors/toggle/__tests__/connectToggle-test.js b/src/connectors/toggle/__tests__/connectToggle-test.js new file mode 100644 index 0000000000..70329f6a75 --- /dev/null +++ b/src/connectors/toggle/__tests__/connectToggle-test.js @@ -0,0 +1,389 @@ +/* eslint-env mocha */ + +import expect from 'expect'; +import sinon from 'sinon'; + +import jsHelper from 'algoliasearch-helper'; +const SearchResults = jsHelper.SearchResults; + +import connectToggle from '../connectToggle.js'; + +const fakeClient = {addAlgoliaAgent: () => {}}; + +describe('connectToggle', () => { + it('Renders during init and render', () => { + const container = document.createElement('div'); + // test that the dummyRendering is called with the isFirstRendering + // flag set accordingly + const rendering = sinon.stub(); + const makeWidget = connectToggle(rendering); + + const attributeName = 'isShippingFree'; + const label = 'Free shipping?'; + const widget = makeWidget({ + container, + attributeName, + label, + }); + + const config = widget.getConfiguration(); + expect(config).toEqual({ + disjunctiveFacets: [attributeName], + }); + + const helper = jsHelper(fakeClient, '', config); + helper.search = sinon.stub(); + + widget.init({ + helper, + state: helper.state, + createURL: () => '#', + onHistoryChange: () => {}, + }); + + { // should call the rendering once with isFirstRendering to true + expect(rendering.callCount).toBe(1); + const isFirstRendering = rendering.lastCall.args[1]; + expect(isFirstRendering).toBe(true); + + // should provide good values for the first rendering + const {containerNode, collapsible, value, shouldAutoHideContainer} = rendering.lastCall.args[0]; + expect(containerNode).toBe(container); + expect(collapsible).toBe(false); + expect(value).toEqual({ + name: label, + count: null, + isRefined: false, + onFacetValue: { + name: label, + isRefined: false, + count: 0, + }, + offFacetValue: { + name: label, + isRefined: false, + count: 0, + }, + }); + expect(shouldAutoHideContainer).toBe(true); + } + + widget.render({ + results: new SearchResults(helper.state, [{ + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + 'false': 40, // eslint-disable-line + }, + }, + nbHits: 85, + }]), + state: helper.state, + helper, + createURL: () => '#', + }); + + { // Should call the rendering a second time, with isFirstRendering to false + expect(rendering.callCount).toBe(2); + const isFirstRendering = rendering.lastCall.args[1]; + expect(isFirstRendering).toBe(false); + + // should provide good values after the first search + const {containerNode, collapsible, value, shouldAutoHideContainer} = rendering.lastCall.args[0]; + expect(containerNode).toBe(container); + expect(collapsible).toBe(false); + expect(value).toEqual({ + name: label, + count: 45, + isRefined: false, + onFacetValue: { + name: label, + isRefined: false, + count: 45, + }, + offFacetValue: { + name: label, + isRefined: false, + count: 85, + }, + }); + expect(shouldAutoHideContainer).toBe(false); + } + }); + + it('Provides a function to add/remove a facet value', () => { + const container = document.createElement('div'); + const rendering = sinon.stub(); + const makeWidget = connectToggle(rendering); + + const attributeName = 'isShippingFree'; + const label = 'Free shipping?'; + const widget = makeWidget({ + container, + attributeName, + label, + }); + + const helper = jsHelper(fakeClient, '', widget.getConfiguration()); + helper.search = sinon.stub(); + + widget.init({ + helper, + state: helper.state, + createURL: () => '#', + onHistoryChange: () => {}, + }); + + { // first rendering + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(undefined); + const renderOptions = rendering.lastCall.args[0]; + const {toggleRefinement, value} = renderOptions; + expect(value).toEqual({ + name: label, + count: null, + isRefined: false, + onFacetValue: { + name: label, + isRefined: false, + count: 0, + }, + offFacetValue: { + name: label, + isRefined: false, + count: 0, + }, + }); + toggleRefinement(value, value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['true']); + toggleRefinement(value, !value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(undefined); + } + + widget.render({ + results: new SearchResults(helper.state, [{ + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + 'false': 40, // eslint-disable-line + }, + }, + nbHits: 85, + }]), + state: helper.state, + helper, + createURL: () => '#', + }); + + { // Second rendering + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(undefined); + const renderOptions = rendering.lastCall.args[0]; + const {toggleRefinement, value} = renderOptions; + expect(value).toEqual({ + name: label, + count: 45, + isRefined: false, + onFacetValue: { + name: label, + isRefined: false, + count: 45, + }, + offFacetValue: { + name: label, + isRefined: false, + count: 85, + }, + }); + toggleRefinement(value, value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['true']); + } + + widget.render({ + results: new SearchResults(helper.state, [{ + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + }, + }, + nbHits: 85, + }, { + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + 'false': 40, // eslint-disable-line + }, + }, + nbHits: 85, + }]), + state: helper.state, + helper, + createURL: () => '#', + }); + + { // Third rendering + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['true']); + const renderOptions = rendering.lastCall.args[0]; + const {toggleRefinement, value} = renderOptions; + expect(value).toEqual({ + name: label, + count: 85, + isRefined: true, + onFacetValue: { + name: label, + isRefined: true, + count: 45, + }, + offFacetValue: { + name: label, + isRefined: false, + count: 85, + }, + }); + toggleRefinement(value, value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(undefined); + } + }); + + it('Provides a function to toggle between two values', () => { + const container = document.createElement('div'); + const rendering = sinon.stub(); + const makeWidget = connectToggle(rendering); + + const attributeName = 'isShippingFree'; + const label = 'Free shipping?'; + const widget = makeWidget({ + container, + attributeName, + label, + values: { + on: 'true', + off: 'false', + }, + }); + + const helper = jsHelper(fakeClient, '', widget.getConfiguration()); + helper.search = sinon.stub(); + + widget.init({ + helper, + state: helper.state, + createURL: () => '#', + onHistoryChange: () => {}, + }); + + { // first rendering + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['false']); + const renderOptions = rendering.lastCall.args[0]; + const {toggleRefinement, value} = renderOptions; + expect(value).toEqual({ + name: label, + count: null, + isRefined: false, + onFacetValue: { + name: label, + isRefined: false, + count: 0, + }, + offFacetValue: { + name: label, + isRefined: true, + count: 0, + }, + }); + toggleRefinement(value, value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['true']); + toggleRefinement(value, !value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['false']); + } + + widget.render({ + results: new SearchResults(helper.state, [{ + facets: { + isShippingFree: { + 'false': 40, // eslint-disable-line + }, + }, + nbHits: 40, + }, { + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + 'false': 40, // eslint-disable-line + }, + }, + nbHits: 85, + }]), + state: helper.state, + helper, + createURL: () => '#', + }); + + { // Second rendering + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['false']); + const renderOptions = rendering.lastCall.args[0]; + const {toggleRefinement, value} = renderOptions; + expect(value).toEqual({ + name: label, + // the value is the one that is not selected + count: 45, + isRefined: false, + onFacetValue: { + name: label, + isRefined: false, + count: 45, + }, + offFacetValue: { + name: label, + isRefined: true, + count: 40, + }, + }); + toggleRefinement(value, value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['true']); + } + + widget.render({ + results: new SearchResults(helper.state, [{ + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + }, + }, + nbHits: 85, + }, { + facets: { + isShippingFree: { + 'true': 45, // eslint-disable-line + 'false': 40, // eslint-disable-line + }, + }, + nbHits: 85, + }]), + state: helper.state, + helper, + createURL: () => '#', + }); + + { // Third rendering + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['true']); + const renderOptions = rendering.lastCall.args[0]; + const {toggleRefinement, value} = renderOptions; + expect(value).toEqual({ + name: label, + count: 40, + isRefined: true, + onFacetValue: { + name: label, + isRefined: true, + count: 45, + }, + offFacetValue: { + name: label, + isRefined: false, + count: 40, + }, + }); + toggleRefinement(value, value.isRefined); + expect(helper.state.disjunctiveFacetsRefinements[attributeName]).toEqual(['false']); + } + }); +}); diff --git a/src/connectors/toggle/connectToggle.js b/src/connectors/toggle/connectToggle.js index 67314b3746..0603ceb406 100644 --- a/src/connectors/toggle/connectToggle.js +++ b/src/connectors/toggle/connectToggle.js @@ -137,7 +137,7 @@ const connectToggle = toggleRendering => ({ count: 0, }; - const facetValue = { + const value = { name: label, isRefined, count: null, @@ -149,7 +149,7 @@ const connectToggle = toggleRendering => ({ collapsible, createURL: this._createURL(state, isRefined), cssClasses, - facetValues: [facetValue], + value, shouldAutoHideContainer: autoHideContainer, templateProps: this._templateProps, toggleRefinement: this.toggleRefinement, @@ -178,7 +178,7 @@ const connectToggle = toggleRendering => ({ // if checkbox is checked, show: [x] free shipping (countWhenNotChecked) const nextRefinement = isRefined ? offFacetValue : onFacetValue; - const facetValue = { + const value = { name: label, isRefined, count: nextRefinement === undefined ? null : nextRefinement.count, @@ -190,8 +190,8 @@ const connectToggle = toggleRendering => ({ collapsible, createURL: this._createURL(state, isRefined), cssClasses, - facetValues: [facetValue], - shouldAutoHideContainer: autoHideContainer && (facetValue.count === 0 || facetValue.count === null), + value, + shouldAutoHideContainer: autoHideContainer && (value.count === 0 || value.count === null), templateProps: this._templateProps, toggleRefinement: this.toggleRefinement, containerNode, diff --git a/src/widgets/toggle/toggle.js b/src/widgets/toggle/toggle.js index 3c04084b3c..71e254f8ae 100644 --- a/src/widgets/toggle/toggle.js +++ b/src/widgets/toggle/toggle.js @@ -47,7 +47,7 @@ function defaultRendering({ collapsible, createURL, cssClasses, - facetValues, + value, shouldAutoHideContainer, templateProps, toggleRefinement, @@ -60,7 +60,7 @@ function defaultRendering({ collapsible={collapsible} createURL={createURL} cssClasses={cssClasses} - facetValues={facetValues} + facetValues={[value]} shouldAutoHideContainer={shouldAutoHideContainer} templateProps={templateProps} toggleRefinement={toggleRefinement}