reducer.test.ts 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  1. import { cloneDeep } from 'lodash';
  2. import { reducerTester } from '../../../../../test/core/redux/reducerTester';
  3. import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from '../../constants';
  4. import { QueryVariableModel, VariableOption } from '../../types';
  5. import {
  6. cleanPickerState,
  7. hideOptions,
  8. initialOptionPickerState as optionsPickerInitialState,
  9. moveOptionsHighlight,
  10. OPTIONS_LIMIT,
  11. optionsPickerReducer,
  12. OptionsPickerState,
  13. showOptions,
  14. toggleAllOptions,
  15. toggleOption,
  16. updateOptionsAndFilter,
  17. updateOptionsFromSearch,
  18. updateSearchQuery,
  19. } from './reducer';
  20. const getVariableTestContext = (extend: Partial<OptionsPickerState>) => {
  21. return {
  22. initialState: {
  23. ...optionsPickerInitialState,
  24. ...extend,
  25. },
  26. };
  27. };
  28. describe('optionsPickerReducer', () => {
  29. describe('when toggleOption is dispatched', () => {
  30. const opsAll = [
  31. { text: '$__all', value: '$__all', selected: true },
  32. { text: 'A', value: 'A', selected: false },
  33. { text: 'B', value: 'B', selected: false },
  34. ];
  35. const opsA = [
  36. { text: '$__all', value: '$__all', selected: false },
  37. { text: 'A', value: 'A', selected: true },
  38. { text: 'B', value: 'B', selected: false },
  39. ];
  40. const opsAB = [
  41. { text: '$__all', value: '$__all', selected: false },
  42. { text: 'A', value: 'A', selected: true },
  43. { text: 'B', value: 'B', selected: true },
  44. ];
  45. const expectToggleOptionState = (args: {
  46. options: any;
  47. multi: any;
  48. forceSelect: any;
  49. clearOthers: any;
  50. option: any;
  51. expectSelected: any;
  52. }) => {
  53. const { initialState } = getVariableTestContext({
  54. options: args.options,
  55. multi: args.multi,
  56. selectedValues: args.options.filter((o: any) => o.selected),
  57. });
  58. const payload = {
  59. forceSelect: args.forceSelect,
  60. clearOthers: args.clearOthers,
  61. option: { text: args.option, value: args.option, selected: true },
  62. };
  63. const expectedAsRecord: any = args.expectSelected.reduce((all: any, current: any) => {
  64. all[current] = current;
  65. return all;
  66. }, {});
  67. reducerTester<OptionsPickerState>()
  68. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  69. .whenActionIsDispatched(toggleOption(payload))
  70. .thenStateShouldEqual({
  71. ...initialState,
  72. selectedValues: args.expectSelected.map((value: any) => ({ value, text: value, selected: true })),
  73. options: args.options.map((option: any) => {
  74. return { ...option, selected: !!expectedAsRecord[option.value] };
  75. }),
  76. });
  77. };
  78. describe('When toggleOption with undefined option is dispatched', () => {
  79. it('should update selected values', () => {
  80. const { initialState } = getVariableTestContext({
  81. options: [],
  82. selectedValues: [],
  83. });
  84. const payload = {
  85. forceSelect: false,
  86. clearOthers: true,
  87. option: undefined,
  88. };
  89. reducerTester<OptionsPickerState>()
  90. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  91. .whenActionIsDispatched(toggleOption(payload))
  92. .thenStateShouldEqual({
  93. ...initialState,
  94. selectedValues: [],
  95. options: [],
  96. });
  97. });
  98. });
  99. describe('toggleOption for multi value variable', () => {
  100. const multi = true;
  101. describe('and value All is selected in options', () => {
  102. const options = opsAll;
  103. it.each`
  104. option | forceSelect | clearOthers | expectSelected
  105. ${'A'} | ${true} | ${false} | ${['A']}
  106. ${'A'} | ${false} | ${false} | ${['A']}
  107. ${'A'} | ${true} | ${true} | ${['A']}
  108. ${'A'} | ${false} | ${true} | ${['A']}
  109. ${'B'} | ${true} | ${false} | ${['B']}
  110. ${'B'} | ${false} | ${false} | ${['B']}
  111. ${'B'} | ${true} | ${true} | ${['B']}
  112. ${'B'} | ${false} | ${true} | ${['B']}
  113. ${'$__all'} | ${true} | ${false} | ${['$__all']}
  114. ${'$__all'} | ${false} | ${false} | ${['$__all']}
  115. ${'$__all'} | ${true} | ${true} | ${['$__all']}
  116. ${'$__all'} | ${false} | ${true} | ${['$__all']}
  117. `(
  118. 'and we toggle $option with options: { forceSelect: $forceSelect, clearOthers: $clearOthers } we expect $expectSelected to be selected',
  119. ({ option, forceSelect, clearOthers, expectSelected }) =>
  120. expectToggleOptionState({
  121. options,
  122. multi,
  123. option,
  124. clearOthers,
  125. forceSelect,
  126. expectSelected,
  127. })
  128. );
  129. });
  130. describe('and value A is selected in options', () => {
  131. const options = opsA;
  132. it.each`
  133. option | forceSelect | clearOthers | expectSelected
  134. ${'A'} | ${true} | ${false} | ${['A']}
  135. ${'A'} | ${false} | ${false} | ${['$__all']}
  136. ${'A'} | ${true} | ${true} | ${['A']}
  137. ${'A'} | ${false} | ${true} | ${['$__all']}
  138. ${'B'} | ${true} | ${true} | ${['B']}
  139. ${'B'} | ${false} | ${true} | ${['B']}
  140. ${'B'} | ${true} | ${false} | ${['A', 'B']}
  141. ${'B'} | ${false} | ${false} | ${['A', 'B']}
  142. `(
  143. 'and we toggle $option with options: { forceSelect: $forceSelect, clearOthers: $clearOthers } we expect $expectSelected to be selected',
  144. ({ option, forceSelect, clearOthers, expectSelected }) =>
  145. expectToggleOptionState({
  146. options,
  147. multi,
  148. option,
  149. clearOthers,
  150. forceSelect,
  151. expectSelected,
  152. })
  153. );
  154. });
  155. describe('and values A + B is selected in options', () => {
  156. const options = opsAB;
  157. it.each`
  158. option | forceSelect | clearOthers | expectSelected
  159. ${'A'} | ${true} | ${false} | ${['A', 'B']}
  160. ${'A'} | ${false} | ${false} | ${['B']}
  161. ${'A'} | ${true} | ${true} | ${['A']}
  162. ${'A'} | ${false} | ${true} | ${['$__all']}
  163. ${'B'} | ${true} | ${true} | ${['B']}
  164. ${'B'} | ${false} | ${true} | ${['$__all']}
  165. ${'B'} | ${true} | ${false} | ${['A', 'B']}
  166. ${'B'} | ${false} | ${false} | ${['A']}
  167. `(
  168. 'and we toggle $option with options: { forceSelect: $forceSelect, clearOthers: $clearOthers } we expect $expectSelected to be selected',
  169. ({ option, forceSelect, clearOthers, expectSelected }) =>
  170. expectToggleOptionState({
  171. options,
  172. multi,
  173. option,
  174. clearOthers,
  175. forceSelect,
  176. expectSelected,
  177. })
  178. );
  179. });
  180. });
  181. describe('toggleOption for single value variable', () => {
  182. const multi = false;
  183. describe('and value All is selected in options', () => {
  184. const options = opsAll;
  185. it.each`
  186. option | forceSelect | clearOthers | expectSelected
  187. ${'A'} | ${true} | ${false} | ${['A']}
  188. ${'A'} | ${false} | ${false} | ${['A']}
  189. ${'A'} | ${true} | ${true} | ${['A']}
  190. ${'A'} | ${false} | ${true} | ${['A']}
  191. ${'B'} | ${true} | ${false} | ${['B']}
  192. ${'B'} | ${false} | ${false} | ${['B']}
  193. ${'B'} | ${true} | ${true} | ${['B']}
  194. ${'B'} | ${false} | ${true} | ${['B']}
  195. ${'$__all'} | ${true} | ${false} | ${['$__all']}
  196. ${'$__all'} | ${false} | ${false} | ${['$__all']}
  197. ${'$__all'} | ${true} | ${true} | ${['$__all']}
  198. ${'$__all'} | ${false} | ${true} | ${['$__all']}
  199. `(
  200. 'and we toggle $option with options: { forceSelect: $forceSelect, clearOthers: $clearOthers } we expect $expectSelected to be selected',
  201. ({ option, forceSelect, clearOthers, expectSelected }) =>
  202. expectToggleOptionState({
  203. options,
  204. multi,
  205. option,
  206. clearOthers,
  207. forceSelect,
  208. expectSelected,
  209. })
  210. );
  211. });
  212. describe('and value A is selected in options', () => {
  213. const options = opsA;
  214. it.each`
  215. option | forceSelect | clearOthers | expectSelected
  216. ${'A'} | ${true} | ${false} | ${['A']}
  217. ${'A'} | ${false} | ${false} | ${['$__all']}
  218. ${'A'} | ${true} | ${true} | ${['A']}
  219. ${'A'} | ${false} | ${true} | ${['$__all']}
  220. ${'B'} | ${true} | ${false} | ${['B']}
  221. ${'B'} | ${false} | ${false} | ${['B']}
  222. ${'B'} | ${true} | ${true} | ${['B']}
  223. ${'B'} | ${false} | ${true} | ${['B']}
  224. `(
  225. 'and we toggle $option with options: { forceSelect: $forceSelect, clearOthers: $clearOthers } we expect $expectSelected to be selected',
  226. ({ option, forceSelect, clearOthers, expectSelected }) =>
  227. expectToggleOptionState({
  228. options,
  229. multi,
  230. option,
  231. clearOthers,
  232. forceSelect,
  233. expectSelected,
  234. })
  235. );
  236. });
  237. });
  238. });
  239. describe('when showOptions is dispatched', () => {
  240. it('then correct values should be selected', () => {
  241. const { initialState } = getVariableTestContext({});
  242. const payload = {
  243. type: 'query',
  244. query: '',
  245. options: [
  246. { text: 'All', value: '$__all', selected: false },
  247. { text: 'A', value: 'A', selected: false },
  248. { text: 'B', value: 'B', selected: true },
  249. ],
  250. multi: false,
  251. id: '0',
  252. } as QueryVariableModel;
  253. reducerTester<OptionsPickerState>()
  254. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  255. .whenActionIsDispatched(showOptions(payload))
  256. .thenStateShouldEqual({
  257. ...initialState,
  258. options: payload.options,
  259. id: payload.id,
  260. multi: payload.multi,
  261. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  262. queryValue: '',
  263. });
  264. });
  265. });
  266. describe('when showOptions is dispatched and picker has queryValue and variable has searchFilter', () => {
  267. it('then state should be correct', () => {
  268. const query = '*.__searchFilter';
  269. const queryValue = 'a search query';
  270. const selected = { text: 'All', value: '$__all', selected: true };
  271. const { initialState } = getVariableTestContext({});
  272. const payload = {
  273. type: 'query',
  274. query,
  275. options: [selected, { text: 'A', value: 'A', selected: false }, { text: 'B', value: 'B', selected: false }],
  276. multi: false,
  277. id: '0',
  278. queryValue,
  279. } as QueryVariableModel;
  280. reducerTester<OptionsPickerState>()
  281. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  282. .whenActionIsDispatched(showOptions(payload))
  283. .thenStateShouldEqual({
  284. ...initialState,
  285. options: payload.options,
  286. queryValue,
  287. id: payload.id,
  288. multi: payload.multi,
  289. selectedValues: [selected],
  290. });
  291. });
  292. });
  293. describe('when showOptions is dispatched and queryValue and variable has no searchFilter', () => {
  294. it('then state should be correct', () => {
  295. const query = '*.';
  296. const queryValue: any = null;
  297. const current = { text: ALL_VARIABLE_TEXT, selected: true, value: [ALL_VARIABLE_VALUE] };
  298. const options = [
  299. { text: 'All', value: '$__all', selected: true },
  300. { text: 'A', value: 'A', selected: false },
  301. { text: 'B', value: 'B', selected: false },
  302. ];
  303. const { initialState } = getVariableTestContext({});
  304. const payload = { type: 'query', id: '0', current, query, options, queryValue } as QueryVariableModel;
  305. reducerTester<OptionsPickerState>()
  306. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  307. .whenActionIsDispatched(showOptions(payload))
  308. .thenStateShouldEqual({
  309. ...initialState,
  310. id: '0',
  311. queryValue: '',
  312. selectedValues: [
  313. {
  314. text: ALL_VARIABLE_TEXT,
  315. selected: true,
  316. value: ALL_VARIABLE_VALUE,
  317. },
  318. ],
  319. options: options,
  320. });
  321. });
  322. });
  323. describe('when hideOptions is dispatched', () => {
  324. it('then state should be correct', () => {
  325. const { initialState } = getVariableTestContext({
  326. options: [
  327. { text: 'All', value: '$__all', selected: true },
  328. { text: 'A', value: 'A', selected: false },
  329. { text: 'B', value: 'B', selected: false },
  330. ],
  331. queryValue: 'a search',
  332. highlightIndex: 1,
  333. id: '0',
  334. });
  335. reducerTester<OptionsPickerState>()
  336. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  337. .whenActionIsDispatched(hideOptions())
  338. .thenStateShouldEqual({ ...optionsPickerInitialState });
  339. });
  340. });
  341. describe('when changeQueryVariableHighlightIndex is dispatched with -1 and highlightIndex is 0', () => {
  342. it('then state should be correct', () => {
  343. const { initialState } = getVariableTestContext({ highlightIndex: 0 });
  344. reducerTester<OptionsPickerState>()
  345. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  346. .whenActionIsDispatched(moveOptionsHighlight(-1))
  347. .thenStateShouldEqual({
  348. ...initialState,
  349. highlightIndex: 0,
  350. });
  351. });
  352. });
  353. describe('when changeQueryVariableHighlightIndex is dispatched with -1 and highlightIndex is 1', () => {
  354. it('then state should be correct', () => {
  355. const { initialState } = getVariableTestContext({
  356. highlightIndex: 1,
  357. options: [
  358. { text: 'A', value: 'A', selected: false },
  359. { text: 'B', value: 'B', selected: false },
  360. ],
  361. });
  362. reducerTester<OptionsPickerState>()
  363. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  364. .whenActionIsDispatched(moveOptionsHighlight(-1))
  365. .thenStateShouldEqual({
  366. ...initialState,
  367. highlightIndex: 0,
  368. });
  369. });
  370. });
  371. describe('when changeQueryVariableHighlightIndex is dispatched with 1 and highlightIndex is same as options.length', () => {
  372. it('then state should be correct', () => {
  373. const { initialState } = getVariableTestContext({
  374. highlightIndex: 1,
  375. options: [
  376. { text: 'A', value: 'A', selected: false },
  377. { text: 'B', value: 'B', selected: false },
  378. ],
  379. });
  380. reducerTester<OptionsPickerState>()
  381. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  382. .whenActionIsDispatched(moveOptionsHighlight(1))
  383. .thenStateShouldEqual({
  384. ...initialState,
  385. highlightIndex: 1,
  386. });
  387. });
  388. });
  389. describe('when changeQueryVariableHighlightIndex is dispatched with 1 and highlightIndex is below options.length', () => {
  390. it('then state should be correct', () => {
  391. const { initialState } = getVariableTestContext({
  392. highlightIndex: 0,
  393. options: [
  394. { text: 'A', value: 'A', selected: false },
  395. { text: 'B', value: 'B', selected: false },
  396. ],
  397. });
  398. reducerTester<OptionsPickerState>()
  399. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  400. .whenActionIsDispatched(moveOptionsHighlight(1))
  401. .thenStateShouldEqual({
  402. ...initialState,
  403. highlightIndex: 1,
  404. });
  405. });
  406. });
  407. describe('when toggleAllOptions is dispatched', () => {
  408. it('should toggle all values except All to true', () => {
  409. const { initialState } = getVariableTestContext({
  410. options: [
  411. { text: 'All', value: '$__all', selected: false },
  412. { text: 'A', value: 'A', selected: false },
  413. { text: 'B', value: 'B', selected: false },
  414. ],
  415. selectedValues: [],
  416. multi: true,
  417. });
  418. reducerTester<OptionsPickerState>()
  419. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  420. .whenActionIsDispatched(toggleAllOptions())
  421. .thenStateShouldEqual({
  422. ...initialState,
  423. options: [
  424. { text: 'All', value: '$__all', selected: false },
  425. { text: 'A', value: 'A', selected: true },
  426. { text: 'B', value: 'B', selected: true },
  427. ],
  428. selectedValues: [
  429. { text: 'A', value: 'A', selected: true },
  430. { text: 'B', value: 'B', selected: true },
  431. ],
  432. });
  433. });
  434. it('should toggle all values to false when $_all is selected', () => {
  435. const { initialState } = getVariableTestContext({
  436. options: [
  437. { text: 'All', value: '$__all', selected: true },
  438. { text: 'A', value: 'A', selected: false },
  439. { text: 'B', value: 'B', selected: false },
  440. ],
  441. selectedValues: [{ text: 'All', value: '$__all', selected: true }],
  442. multi: true,
  443. });
  444. reducerTester<OptionsPickerState>()
  445. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  446. .whenActionIsDispatched(toggleAllOptions())
  447. .thenStateShouldEqual({
  448. ...initialState,
  449. options: [
  450. { text: 'All', value: '$__all', selected: false },
  451. { text: 'A', value: 'A', selected: false },
  452. { text: 'B', value: 'B', selected: false },
  453. ],
  454. selectedValues: [],
  455. });
  456. });
  457. it('should toggle all values to false when a option is selected', () => {
  458. const { initialState } = getVariableTestContext({
  459. options: [
  460. { text: 'All', value: '$__all', selected: false },
  461. { text: 'A', value: 'A', selected: false },
  462. { text: 'B', value: 'B', selected: true },
  463. ],
  464. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  465. multi: true,
  466. });
  467. reducerTester<OptionsPickerState>()
  468. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  469. .whenActionIsDispatched(toggleAllOptions())
  470. .thenStateShouldEqual({
  471. ...initialState,
  472. options: [
  473. { text: 'All', value: '$__all', selected: false },
  474. { text: 'A', value: 'A', selected: false },
  475. { text: 'B', value: 'B', selected: false },
  476. ],
  477. selectedValues: [],
  478. });
  479. });
  480. });
  481. describe('when updateOptionsAndFilter is dispatched and queryValue exists', () => {
  482. it('then state should be correct', () => {
  483. const queryValue = 'A';
  484. const options = [
  485. { text: 'All', value: '$__all', selected: true },
  486. { text: 'A', value: 'A', selected: false },
  487. { text: 'B', value: 'B', selected: false },
  488. ];
  489. const { initialState } = getVariableTestContext({ queryValue });
  490. reducerTester<OptionsPickerState>()
  491. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  492. .whenActionIsDispatched(updateOptionsAndFilter(options))
  493. .thenStateShouldEqual({
  494. ...initialState,
  495. options: [
  496. { text: 'All', value: '$__all', selected: true },
  497. { text: 'A', value: 'A', selected: false },
  498. ],
  499. selectedValues: [{ text: 'All', value: '$__all', selected: true }],
  500. queryValue: 'A',
  501. highlightIndex: 0,
  502. });
  503. });
  504. describe('but option is null', () => {
  505. it('then state should be correct', () => {
  506. const queryValue = 'A';
  507. const options: any = [
  508. { text: 'All', value: '$__all', selected: true },
  509. { text: null, value: null, selected: false },
  510. { text: [null], value: [null], selected: false },
  511. ];
  512. const { initialState } = getVariableTestContext({ queryValue });
  513. reducerTester<OptionsPickerState>()
  514. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  515. .whenActionIsDispatched(updateOptionsAndFilter(options))
  516. .thenStateShouldEqual({
  517. ...initialState,
  518. options: [{ text: 'All', value: '$__all', selected: true }],
  519. selectedValues: [{ text: 'All', value: '$__all', selected: true }],
  520. queryValue: 'A',
  521. highlightIndex: 0,
  522. });
  523. });
  524. });
  525. describe('and option count is are greater then OPTIONS_LIMIT', () => {
  526. it('then state should be correct', () => {
  527. const queryValue = 'option:1337';
  528. const options = [];
  529. for (let index = 0; index <= OPTIONS_LIMIT + 337; index++) {
  530. options.push({ text: `option:${index}`, value: `option:${index}`, selected: false });
  531. }
  532. const { initialState } = getVariableTestContext({ queryValue });
  533. reducerTester<OptionsPickerState>()
  534. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  535. .whenActionIsDispatched(updateOptionsAndFilter(options))
  536. .thenStateShouldEqual({
  537. ...cloneDeep(initialState),
  538. options: [{ text: 'option:1337', value: 'option:1337', selected: false }],
  539. selectedValues: [],
  540. queryValue: 'option:1337',
  541. highlightIndex: 0,
  542. });
  543. });
  544. });
  545. });
  546. describe('when value is selected and filter is applied but then removed', () => {
  547. it('then state should be correct', () => {
  548. const queryValue = 'A';
  549. const options: VariableOption[] = [
  550. { text: 'All', value: '$__all', selected: false },
  551. { text: 'A', value: 'A', selected: false },
  552. { text: 'B', value: 'B', selected: false },
  553. ];
  554. const { initialState } = getVariableTestContext({
  555. options,
  556. });
  557. reducerTester<OptionsPickerState>()
  558. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  559. .whenActionIsDispatched(toggleOption({ option: options[2], forceSelect: false, clearOthers: false }))
  560. .thenStateShouldEqual({
  561. ...initialState,
  562. options: [
  563. { text: 'All', value: '$__all', selected: false },
  564. { text: 'A', value: 'A', selected: false },
  565. { text: 'B', value: 'B', selected: true },
  566. ],
  567. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  568. })
  569. .whenActionIsDispatched(updateSearchQuery(queryValue))
  570. .thenStateShouldEqual({
  571. ...initialState,
  572. options: [
  573. { text: 'All', value: '$__all', selected: false },
  574. { text: 'A', value: 'A', selected: false },
  575. { text: 'B', value: 'B', selected: true },
  576. ],
  577. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  578. queryValue: 'A',
  579. })
  580. .whenActionIsDispatched(updateOptionsAndFilter(options))
  581. .thenStateShouldEqual({
  582. ...initialState,
  583. options: [
  584. { text: 'All', value: '$__all', selected: false },
  585. { text: 'A', value: 'A', selected: false },
  586. ],
  587. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  588. queryValue: 'A',
  589. highlightIndex: 0,
  590. })
  591. .whenActionIsDispatched(updateSearchQuery(''))
  592. .thenStateShouldEqual({
  593. ...initialState,
  594. options: [
  595. { text: 'All', value: '$__all', selected: false },
  596. { text: 'A', value: 'A', selected: false },
  597. ],
  598. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  599. queryValue: '',
  600. highlightIndex: 0,
  601. })
  602. .whenActionIsDispatched(updateOptionsAndFilter(options))
  603. .thenStateShouldEqual({
  604. ...initialState,
  605. options: [
  606. { text: 'All', value: '$__all', selected: false },
  607. { text: 'A', value: 'A', selected: false },
  608. { text: 'B', value: 'B', selected: true },
  609. ],
  610. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  611. queryValue: '',
  612. highlightIndex: 0,
  613. });
  614. });
  615. });
  616. describe('when value is toggled back and forth', () => {
  617. it('then state should be correct', () => {
  618. const options: VariableOption[] = [
  619. { text: 'All', value: '$__all', selected: false },
  620. { text: 'A', value: 'A', selected: false },
  621. { text: 'B', value: 'B', selected: false },
  622. ];
  623. const toggleOptionAction = toggleOption({
  624. option: options[2],
  625. forceSelect: false,
  626. clearOthers: false,
  627. });
  628. const { initialState } = getVariableTestContext({
  629. options,
  630. });
  631. reducerTester<OptionsPickerState>()
  632. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  633. .whenActionIsDispatched(toggleOptionAction)
  634. .thenStateShouldEqual({
  635. ...initialState,
  636. options: [
  637. { text: 'All', value: '$__all', selected: false },
  638. { text: 'A', value: 'A', selected: false },
  639. { text: 'B', value: 'B', selected: true },
  640. ],
  641. selectedValues: [{ text: 'B', value: 'B', selected: true }],
  642. })
  643. .whenActionIsDispatched(toggleOptionAction)
  644. .thenStateShouldEqual({
  645. ...initialState,
  646. options: [
  647. { text: 'All', value: '$__all', selected: true },
  648. { text: 'A', value: 'A', selected: false },
  649. { text: 'B', value: 'B', selected: false },
  650. ],
  651. selectedValues: [{ text: 'All', value: '$__all', selected: true }],
  652. });
  653. });
  654. });
  655. describe('when updateOptionsFromSearch is dispatched and variable has searchFilter', () => {
  656. it('then state should be correct', () => {
  657. const searchQuery = '__searchFilter';
  658. const options = [
  659. { text: 'All', value: '$__all', selected: true },
  660. { text: 'A', value: 'A', selected: false },
  661. { text: 'B', value: 'B', selected: false },
  662. ];
  663. const { initialState } = getVariableTestContext({
  664. queryValue: searchQuery,
  665. });
  666. reducerTester<OptionsPickerState>()
  667. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  668. .whenActionIsDispatched(updateOptionsFromSearch(options))
  669. .thenStateShouldEqual({
  670. ...initialState,
  671. options: options,
  672. selectedValues: [{ text: 'All', value: '$__all', selected: true }],
  673. highlightIndex: 0,
  674. });
  675. });
  676. });
  677. describe('when updateSearchQuery is dispatched', () => {
  678. it('then state should be correct', () => {
  679. const searchQuery = 'A';
  680. const { initialState } = getVariableTestContext({});
  681. reducerTester<OptionsPickerState>()
  682. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  683. .whenActionIsDispatched(updateSearchQuery(searchQuery))
  684. .thenStateShouldEqual({
  685. ...initialState,
  686. queryValue: searchQuery,
  687. });
  688. });
  689. });
  690. describe('when large data for updateOptionsAndFilter', () => {
  691. it('then state should be correct', () => {
  692. const searchQuery = 'option:11256';
  693. const options: VariableOption[] = [];
  694. for (let index = 0; index <= OPTIONS_LIMIT + 11256; index++) {
  695. options.push({ text: `option:${index}`, value: `option:${index}`, selected: false });
  696. }
  697. const { initialState } = getVariableTestContext({
  698. queryValue: searchQuery,
  699. });
  700. reducerTester<OptionsPickerState>()
  701. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  702. .whenActionIsDispatched(updateOptionsAndFilter(options))
  703. .thenStateShouldEqual({
  704. ...cloneDeep(initialState),
  705. options: [{ text: 'option:11256', value: 'option:11256', selected: false }],
  706. selectedValues: [],
  707. queryValue: 'option:11256',
  708. highlightIndex: 0,
  709. });
  710. });
  711. });
  712. describe('when large data for updateOptionsFromSearch is dispatched and variable has searchFilter', () => {
  713. it('then state should be correct', () => {
  714. const searchQuery = '__searchFilter';
  715. const options = [{ text: 'All', value: '$__all', selected: true }];
  716. for (let i = 0; i <= OPTIONS_LIMIT + 137; i++) {
  717. options.push({ text: `option${i}`, value: `option${i}`, selected: false });
  718. }
  719. const { initialState } = getVariableTestContext({
  720. queryValue: searchQuery,
  721. });
  722. reducerTester<OptionsPickerState>()
  723. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  724. .whenActionIsDispatched(updateOptionsFromSearch(options))
  725. .thenStateShouldEqual({
  726. ...initialState,
  727. options: options.slice(0, OPTIONS_LIMIT),
  728. selectedValues: [{ text: 'All', value: '$__all', selected: true }],
  729. highlightIndex: 0,
  730. });
  731. });
  732. });
  733. describe('when large data for showOptions', () => {
  734. it('then state should be correct', () => {
  735. const { initialState } = getVariableTestContext({});
  736. const payload = {
  737. type: 'query',
  738. query: '',
  739. options: [{ text: 'option0', value: 'option0', selected: false }],
  740. multi: false,
  741. id: '0',
  742. } as QueryVariableModel;
  743. const checkOptions = [];
  744. for (let index = 0; index < OPTIONS_LIMIT; index++) {
  745. checkOptions.push({ text: `option${index}`, value: `option${index}`, selected: false });
  746. }
  747. for (let i = 1; i <= OPTIONS_LIMIT + 137; i++) {
  748. payload.options.push({ text: `option${i}`, value: `option${i}`, selected: false });
  749. }
  750. reducerTester<OptionsPickerState>()
  751. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  752. .whenActionIsDispatched(showOptions(payload))
  753. .thenStateShouldEqual({
  754. ...initialState,
  755. options: checkOptions,
  756. id: '0',
  757. multi: false,
  758. queryValue: '',
  759. });
  760. });
  761. });
  762. describe('when cleanPickerState is dispatched', () => {
  763. it('then state should be correct', () => {
  764. const { initialState } = getVariableTestContext({
  765. highlightIndex: 19,
  766. multi: true,
  767. id: 'some id',
  768. options: [{ text: 'A', value: 'A', selected: true }],
  769. queryValue: 'a query value',
  770. selectedValues: [{ text: 'A', value: 'A', selected: true }],
  771. });
  772. reducerTester<OptionsPickerState>()
  773. .givenReducer(optionsPickerReducer, cloneDeep(initialState))
  774. .whenActionIsDispatched(cleanPickerState())
  775. .thenStateShouldEqual({ ...optionsPickerInitialState });
  776. });
  777. });
  778. });