list-query-builder.e2e-spec.ts 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. import { LogicalOperator } from '@vendure/common/lib/generated-types';
  2. import { mergeConfig } from '@vendure/core';
  3. import { createTestEnvironment } from '@vendure/testing';
  4. import gql from 'graphql-tag';
  5. import path from 'path';
  6. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  9. import { ListQueryPlugin } from './fixtures/test-plugins/list-query-plugin';
  10. import { LanguageCode, SortOrder } from './graphql/generated-e2e-admin-types';
  11. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  12. import { fixPostgresTimezone } from './utils/fix-pg-timezone';
  13. fixPostgresTimezone();
  14. describe('ListQueryBuilder', () => {
  15. const { server, adminClient, shopClient } = createTestEnvironment(
  16. mergeConfig(testConfig(), {
  17. apiOptions: {
  18. shopListQueryLimit: 10,
  19. adminListQueryLimit: 30,
  20. },
  21. plugins: [ListQueryPlugin],
  22. }),
  23. );
  24. beforeAll(async () => {
  25. await server.init({
  26. initialData,
  27. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  28. customerCount: 3,
  29. });
  30. await adminClient.asSuperAdmin();
  31. }, TEST_SETUP_TIMEOUT_MS);
  32. afterAll(async () => {
  33. await server.destroy();
  34. });
  35. function getItemLabels(items: any[]): string[] {
  36. return items.map((x: any) => x.label).sort();
  37. }
  38. describe('pagination', () => {
  39. it('all en', async () => {
  40. const { testEntities } = await adminClient.query(
  41. GET_LIST,
  42. {
  43. options: {},
  44. },
  45. { languageCode: LanguageCode.en },
  46. );
  47. expect(testEntities.totalItems).toBe(6);
  48. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
  49. expect(testEntities.items.map((i: any) => i.name)).toEqual([
  50. 'apple',
  51. 'bike',
  52. 'cake',
  53. 'dog',
  54. 'egg',
  55. 'baum', // if default en lang does not exist, use next available lang
  56. ]);
  57. });
  58. it('all de', async () => {
  59. const { testEntities } = await adminClient.query(
  60. GET_LIST,
  61. {
  62. options: {},
  63. },
  64. { languageCode: LanguageCode.de },
  65. );
  66. expect(testEntities.totalItems).toBe(6);
  67. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
  68. expect(testEntities.items.map((i: any) => i.name)).toEqual([
  69. 'apfel',
  70. 'fahrrad',
  71. 'kuchen',
  72. 'hund',
  73. 'egg', // falls back to en translation when de doesn't exist
  74. 'baum',
  75. ]);
  76. });
  77. it('take', async () => {
  78. const { testEntities } = await adminClient.query(GET_LIST, {
  79. options: {
  80. take: 2,
  81. },
  82. });
  83. expect(testEntities.totalItems).toBe(6);
  84. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  85. });
  86. it('skip', async () => {
  87. const { testEntities } = await adminClient.query(GET_LIST, {
  88. options: {
  89. skip: 2,
  90. },
  91. });
  92. expect(testEntities.totalItems).toBe(6);
  93. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
  94. });
  95. it('skip negative is ignored', async () => {
  96. const { testEntities } = await adminClient.query(GET_LIST, {
  97. options: {
  98. skip: -1,
  99. },
  100. });
  101. expect(testEntities.totalItems).toBe(6);
  102. expect(testEntities.items.length).toBe(6);
  103. });
  104. it('take zero is ignored', async () => {
  105. const { testEntities } = await adminClient.query(GET_LIST, {
  106. options: {
  107. take: 0,
  108. },
  109. });
  110. expect(testEntities.totalItems).toBe(6);
  111. expect(testEntities.items.length).toBe(6);
  112. });
  113. it('take negative is ignored', async () => {
  114. const { testEntities } = await adminClient.query(GET_LIST, {
  115. options: {
  116. take: -1,
  117. },
  118. });
  119. expect(testEntities.totalItems).toBe(6);
  120. expect(testEntities.items.length).toBe(6);
  121. });
  122. it(
  123. 'take beyond adminListQueryLimit',
  124. assertThrowsWithMessage(async () => {
  125. await adminClient.query(GET_LIST, {
  126. options: {
  127. take: 50,
  128. },
  129. });
  130. }, 'Cannot take more than 30 results from a list query'),
  131. );
  132. it(
  133. 'take beyond shopListQueryLimit',
  134. assertThrowsWithMessage(async () => {
  135. await shopClient.query(GET_LIST, {
  136. options: {
  137. take: 50,
  138. },
  139. });
  140. }, 'Cannot take more than 10 results from a list query'),
  141. );
  142. });
  143. describe('string filtering', () => {
  144. it('eq', async () => {
  145. const { testEntities } = await adminClient.query(GET_LIST, {
  146. options: {
  147. filter: {
  148. label: {
  149. eq: 'B',
  150. },
  151. },
  152. },
  153. });
  154. expect(getItemLabels(testEntities.items)).toEqual(['B']);
  155. });
  156. it('notEq', async () => {
  157. const { testEntities } = await adminClient.query(GET_LIST, {
  158. options: {
  159. filter: {
  160. label: {
  161. notEq: 'B',
  162. },
  163. },
  164. },
  165. });
  166. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'D', 'E', 'F']);
  167. });
  168. it('contains', async () => {
  169. const { testEntities } = await adminClient.query(GET_LIST, {
  170. options: {
  171. filter: {
  172. description: {
  173. contains: 'adip',
  174. },
  175. },
  176. },
  177. });
  178. expect(getItemLabels(testEntities.items)).toEqual(['C']);
  179. });
  180. it('notContains', async () => {
  181. const { testEntities } = await adminClient.query(GET_LIST, {
  182. options: {
  183. filter: {
  184. description: {
  185. notContains: 'te',
  186. },
  187. },
  188. },
  189. });
  190. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'E', 'F']);
  191. });
  192. it('in', async () => {
  193. const { testEntities } = await adminClient.query(GET_LIST, {
  194. options: {
  195. filter: {
  196. label: {
  197. in: ['A', 'C'],
  198. },
  199. },
  200. },
  201. });
  202. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C']);
  203. });
  204. it('notIn', async () => {
  205. const { testEntities } = await adminClient.query(GET_LIST, {
  206. options: {
  207. filter: {
  208. label: {
  209. notIn: ['A', 'C'],
  210. },
  211. },
  212. },
  213. });
  214. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E', 'F']);
  215. });
  216. it('isNull true', async () => {
  217. const { testEntities } = await adminClient.query(GET_LIST, {
  218. options: {
  219. filter: {
  220. nullableString: {
  221. isNull: true,
  222. },
  223. },
  224. },
  225. });
  226. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'F']);
  227. });
  228. it('isNull false', async () => {
  229. const { testEntities } = await adminClient.query(GET_LIST, {
  230. options: {
  231. filter: {
  232. nullableString: {
  233. isNull: false,
  234. },
  235. },
  236. },
  237. });
  238. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'E']);
  239. });
  240. describe('regex', () => {
  241. it('simple substring', async () => {
  242. const { testEntities } = await adminClient.query(GET_LIST, {
  243. options: {
  244. filter: {
  245. description: {
  246. regex: 'or',
  247. },
  248. },
  249. },
  250. });
  251. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'D']);
  252. });
  253. it('start of string', async () => {
  254. const { testEntities } = await adminClient.query(GET_LIST, {
  255. options: {
  256. filter: {
  257. description: {
  258. regex: '^in',
  259. },
  260. },
  261. },
  262. });
  263. expect(getItemLabels(testEntities.items)).toEqual(['E']);
  264. });
  265. it('end of string', async () => {
  266. const { testEntities } = await adminClient.query(GET_LIST, {
  267. options: {
  268. filter: {
  269. description: {
  270. regex: 'or$',
  271. },
  272. },
  273. },
  274. });
  275. expect(getItemLabels(testEntities.items)).toEqual(['D']);
  276. });
  277. it('alternation', async () => {
  278. const { testEntities } = await adminClient.query(GET_LIST, {
  279. options: {
  280. filter: {
  281. description: {
  282. regex: 'dolor|tempor',
  283. },
  284. },
  285. },
  286. });
  287. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D']);
  288. });
  289. it('complex', async () => {
  290. const { testEntities } = await adminClient.query(GET_LIST, {
  291. options: {
  292. filter: {
  293. description: {
  294. regex: '(dolor|tempor)|inc[i]?d[^a]d.*nt',
  295. },
  296. },
  297. },
  298. });
  299. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E']);
  300. });
  301. });
  302. });
  303. describe('ID filtering', () => {
  304. it('eq', async () => {
  305. const { testEntities } = await adminClient.query(GET_LIST, {
  306. options: {
  307. filter: {
  308. ownerId: {
  309. eq: 'T_13',
  310. },
  311. },
  312. },
  313. });
  314. expect(getItemLabels(testEntities.items)).toEqual(['D']);
  315. });
  316. it('notEq', async () => {
  317. const { testEntities } = await adminClient.query(GET_LIST, {
  318. options: {
  319. filter: {
  320. ownerId: {
  321. notEq: 'T_13',
  322. },
  323. },
  324. },
  325. });
  326. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'E', 'F']);
  327. });
  328. it('in', async () => {
  329. const { testEntities } = await adminClient.query(GET_LIST, {
  330. options: {
  331. filter: {
  332. ownerId: {
  333. in: ['T_10', 'T_15'],
  334. },
  335. },
  336. },
  337. });
  338. expect(getItemLabels(testEntities.items)).toEqual(['A', 'F']);
  339. });
  340. it('in with empty set', async () => {
  341. const { testEntities } = await adminClient.query(GET_LIST, {
  342. options: {
  343. filter: {
  344. ownerId: {
  345. in: [],
  346. },
  347. },
  348. },
  349. });
  350. expect(getItemLabels(testEntities.items)).toEqual([]);
  351. });
  352. it('notIn', async () => {
  353. const { testEntities } = await adminClient.query(GET_LIST, {
  354. options: {
  355. filter: {
  356. ownerId: {
  357. notIn: ['T_10', 'T_15'],
  358. },
  359. },
  360. },
  361. });
  362. expect(getItemLabels(testEntities.items)).toEqual(['B', 'C', 'D', 'E']);
  363. });
  364. it('notIn with empty set', async () => {
  365. const { testEntities } = await adminClient.query(GET_LIST, {
  366. options: {
  367. filter: {
  368. ownerId: {
  369. notIn: [],
  370. },
  371. },
  372. },
  373. });
  374. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
  375. });
  376. it('isNull true', async () => {
  377. const { testEntities } = await adminClient.query(GET_LIST, {
  378. options: {
  379. filter: {
  380. nullableId: {
  381. isNull: true,
  382. },
  383. },
  384. },
  385. });
  386. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'F']);
  387. });
  388. it('isNull false', async () => {
  389. const { testEntities } = await adminClient.query(GET_LIST, {
  390. options: {
  391. filter: {
  392. nullableId: {
  393. isNull: false,
  394. },
  395. },
  396. },
  397. });
  398. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'E']);
  399. });
  400. describe('regex', () => {
  401. it('simple substring', async () => {
  402. const { testEntities } = await adminClient.query(GET_LIST, {
  403. options: {
  404. filter: {
  405. description: {
  406. regex: 'or',
  407. },
  408. },
  409. },
  410. });
  411. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'D']);
  412. });
  413. it('start of string', async () => {
  414. const { testEntities } = await adminClient.query(GET_LIST, {
  415. options: {
  416. filter: {
  417. description: {
  418. regex: '^in',
  419. },
  420. },
  421. },
  422. });
  423. expect(getItemLabels(testEntities.items)).toEqual(['E']);
  424. });
  425. it('end of string', async () => {
  426. const { testEntities } = await adminClient.query(GET_LIST, {
  427. options: {
  428. filter: {
  429. description: {
  430. regex: 'or$',
  431. },
  432. },
  433. },
  434. });
  435. expect(getItemLabels(testEntities.items)).toEqual(['D']);
  436. });
  437. it('alternation', async () => {
  438. const { testEntities } = await adminClient.query(GET_LIST, {
  439. options: {
  440. filter: {
  441. description: {
  442. regex: 'dolor|tempor',
  443. },
  444. },
  445. },
  446. });
  447. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D']);
  448. });
  449. it('complex', async () => {
  450. const { testEntities } = await adminClient.query(GET_LIST, {
  451. options: {
  452. filter: {
  453. description: {
  454. regex: '(dolor|tempor)|inc[i]?d[^a]d.*nt',
  455. },
  456. },
  457. },
  458. });
  459. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E']);
  460. });
  461. });
  462. });
  463. describe('boolean filtering', () => {
  464. it('eq', async () => {
  465. const { testEntities } = await adminClient.query(GET_LIST, {
  466. options: {
  467. filter: {
  468. active: {
  469. eq: false,
  470. },
  471. },
  472. },
  473. });
  474. expect(getItemLabels(testEntities.items)).toEqual(['C', 'E', 'F']);
  475. });
  476. it('isNull true', async () => {
  477. const { testEntities } = await adminClient.query(GET_LIST, {
  478. options: {
  479. filter: {
  480. nullableBoolean: {
  481. isNull: true,
  482. },
  483. },
  484. },
  485. });
  486. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'F']);
  487. });
  488. it('isNull false', async () => {
  489. const { testEntities } = await adminClient.query(GET_LIST, {
  490. options: {
  491. filter: {
  492. nullableBoolean: {
  493. isNull: false,
  494. },
  495. },
  496. },
  497. });
  498. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'E']);
  499. });
  500. });
  501. describe('number filtering', () => {
  502. it('eq', async () => {
  503. const { testEntities } = await adminClient.query(GET_LIST, {
  504. options: {
  505. filter: {
  506. order: {
  507. eq: 1,
  508. },
  509. },
  510. },
  511. });
  512. expect(getItemLabels(testEntities.items)).toEqual(['B']);
  513. });
  514. it('lt', async () => {
  515. const { testEntities } = await adminClient.query(GET_LIST, {
  516. options: {
  517. filter: {
  518. order: {
  519. lt: 1,
  520. },
  521. },
  522. },
  523. });
  524. expect(getItemLabels(testEntities.items)).toEqual(['A']);
  525. });
  526. it('lte', async () => {
  527. const { testEntities } = await adminClient.query(GET_LIST, {
  528. options: {
  529. filter: {
  530. order: {
  531. lte: 1,
  532. },
  533. },
  534. },
  535. });
  536. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  537. });
  538. it('gt', async () => {
  539. const { testEntities } = await adminClient.query(GET_LIST, {
  540. options: {
  541. filter: {
  542. order: {
  543. gt: 1,
  544. },
  545. },
  546. },
  547. });
  548. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
  549. });
  550. it('gte', async () => {
  551. const { testEntities } = await adminClient.query(GET_LIST, {
  552. options: {
  553. filter: {
  554. order: {
  555. gte: 1,
  556. },
  557. },
  558. },
  559. });
  560. expect(getItemLabels(testEntities.items)).toEqual(['B', 'C', 'D', 'E', 'F']);
  561. });
  562. it('between', async () => {
  563. const { testEntities } = await adminClient.query(GET_LIST, {
  564. options: {
  565. filter: {
  566. order: {
  567. between: {
  568. start: 2,
  569. end: 4,
  570. },
  571. },
  572. },
  573. },
  574. });
  575. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
  576. });
  577. it('isNull true', async () => {
  578. const { testEntities } = await adminClient.query(GET_LIST, {
  579. options: {
  580. filter: {
  581. nullableNumber: {
  582. isNull: true,
  583. },
  584. },
  585. },
  586. });
  587. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'F']);
  588. });
  589. it('isNull false', async () => {
  590. const { testEntities } = await adminClient.query(GET_LIST, {
  591. options: {
  592. filter: {
  593. nullableNumber: {
  594. isNull: false,
  595. },
  596. },
  597. },
  598. });
  599. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'E']);
  600. });
  601. });
  602. describe('date filtering', () => {
  603. it('before', async () => {
  604. const { testEntities } = await adminClient.query(GET_LIST, {
  605. options: {
  606. filter: {
  607. date: {
  608. before: '2020-01-20T10:00:00.000Z',
  609. },
  610. },
  611. },
  612. });
  613. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  614. });
  615. it('before on same date', async () => {
  616. const { testEntities } = await adminClient.query(GET_LIST, {
  617. options: {
  618. filter: {
  619. date: {
  620. before: '2020-01-15T17:00:00.000Z',
  621. },
  622. },
  623. },
  624. });
  625. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  626. });
  627. it('after', async () => {
  628. const { testEntities } = await adminClient.query(GET_LIST, {
  629. options: {
  630. filter: {
  631. date: {
  632. after: '2020-01-20T10:00:00.000Z',
  633. },
  634. },
  635. },
  636. });
  637. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
  638. });
  639. it('after on same date', async () => {
  640. const { testEntities } = await adminClient.query(GET_LIST, {
  641. options: {
  642. filter: {
  643. date: {
  644. after: '2020-01-25T09:00:00.000Z',
  645. },
  646. },
  647. },
  648. });
  649. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
  650. });
  651. it('between', async () => {
  652. const { testEntities } = await adminClient.query(GET_LIST, {
  653. options: {
  654. filter: {
  655. date: {
  656. between: {
  657. start: '2020-01-10T10:00:00.000Z',
  658. end: '2020-01-20T10:00:00.000Z',
  659. },
  660. },
  661. },
  662. },
  663. });
  664. expect(getItemLabels(testEntities.items)).toEqual(['B']);
  665. });
  666. it('isNull true', async () => {
  667. const { testEntities } = await adminClient.query(GET_LIST, {
  668. options: {
  669. filter: {
  670. nullableDate: {
  671. isNull: true,
  672. },
  673. },
  674. },
  675. });
  676. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'F']);
  677. });
  678. it('isNull false', async () => {
  679. const { testEntities } = await adminClient.query(GET_LIST, {
  680. options: {
  681. filter: {
  682. nullableDate: {
  683. isNull: false,
  684. },
  685. },
  686. },
  687. });
  688. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'E']);
  689. });
  690. });
  691. describe('multiple filters with filterOperator', () => {
  692. it('default AND', async () => {
  693. const { testEntities } = await adminClient.query(GET_LIST, {
  694. options: {
  695. filter: {
  696. description: {
  697. contains: 'Lorem',
  698. },
  699. active: {
  700. eq: false,
  701. },
  702. },
  703. },
  704. });
  705. expect(getItemLabels(testEntities.items)).toEqual([]);
  706. });
  707. it('explicit AND', async () => {
  708. const { testEntities } = await adminClient.query(GET_LIST, {
  709. options: {
  710. filter: {
  711. description: {
  712. contains: 'Lorem',
  713. },
  714. active: {
  715. eq: false,
  716. },
  717. },
  718. filterOperator: LogicalOperator.AND,
  719. },
  720. });
  721. expect(getItemLabels(testEntities.items)).toEqual([]);
  722. });
  723. it('explicit OR', async () => {
  724. const { testEntities } = await adminClient.query(GET_LIST, {
  725. options: {
  726. filter: {
  727. description: {
  728. contains: 'Lorem',
  729. },
  730. active: {
  731. eq: false,
  732. },
  733. },
  734. filterOperator: LogicalOperator.OR,
  735. },
  736. });
  737. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'E', 'F']);
  738. });
  739. it('explicit OR with 3 filters', async () => {
  740. const { testEntities } = await adminClient.query(GET_LIST, {
  741. options: {
  742. filter: {
  743. description: {
  744. contains: 'eiusmod',
  745. },
  746. active: {
  747. eq: false,
  748. },
  749. order: {
  750. lt: 3,
  751. },
  752. },
  753. filterOperator: LogicalOperator.OR,
  754. },
  755. });
  756. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
  757. });
  758. it('explicit OR with empty filters object', async () => {
  759. const { testEntities } = await adminClient.query(GET_LIST, {
  760. options: {
  761. filter: {},
  762. filterOperator: LogicalOperator.OR,
  763. },
  764. });
  765. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
  766. });
  767. });
  768. describe('sorting', () => {
  769. it('sort by string', async () => {
  770. const { testEntities } = await adminClient.query(GET_LIST, {
  771. options: {
  772. sort: {
  773. label: SortOrder.DESC,
  774. },
  775. },
  776. });
  777. expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
  778. });
  779. it('sort by number', async () => {
  780. const { testEntities } = await adminClient.query(GET_LIST, {
  781. options: {
  782. sort: {
  783. order: SortOrder.DESC,
  784. },
  785. },
  786. });
  787. expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
  788. });
  789. it('sort by date', async () => {
  790. const { testEntities } = await adminClient.query(GET_LIST, {
  791. options: {
  792. sort: {
  793. date: SortOrder.DESC,
  794. },
  795. },
  796. });
  797. expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
  798. });
  799. it('sort by ID', async () => {
  800. const { testEntities } = await adminClient.query(GET_LIST, {
  801. options: {
  802. sort: {
  803. id: SortOrder.DESC,
  804. },
  805. },
  806. });
  807. expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
  808. });
  809. it('sort by translated field en', async () => {
  810. const { testEntities } = await adminClient.query(GET_LIST, {
  811. options: {
  812. sort: {
  813. name: SortOrder.ASC,
  814. },
  815. },
  816. });
  817. expect(testEntities.items.map((x: any) => x.name)).toEqual([
  818. 'apple',
  819. 'baum', // falling back to de here
  820. 'bike',
  821. 'cake',
  822. 'dog',
  823. 'egg',
  824. ]);
  825. });
  826. it('sort by translated field de', async () => {
  827. const { testEntities } = await adminClient.query(
  828. GET_LIST,
  829. {
  830. options: {
  831. sort: {
  832. name: SortOrder.ASC,
  833. },
  834. },
  835. },
  836. { languageCode: LanguageCode.de },
  837. );
  838. expect(testEntities.items.map((x: any) => x.name)).toEqual([
  839. 'apfel',
  840. 'baum',
  841. 'egg',
  842. 'fahrrad',
  843. 'hund',
  844. 'kuchen',
  845. ]);
  846. });
  847. it('sort by translated field en with take', async () => {
  848. const { testEntities } = await adminClient.query(GET_LIST, {
  849. options: {
  850. sort: {
  851. name: SortOrder.ASC,
  852. },
  853. take: 4,
  854. },
  855. });
  856. expect(testEntities.items.map((x: any) => x.name)).toEqual(['apple', 'baum', 'bike', 'cake']);
  857. });
  858. it('sort by translated field de with take', async () => {
  859. const { testEntities } = await adminClient.query(
  860. GET_LIST,
  861. {
  862. options: {
  863. sort: {
  864. name: SortOrder.ASC,
  865. },
  866. take: 4,
  867. },
  868. },
  869. { languageCode: LanguageCode.de },
  870. );
  871. expect(testEntities.items.map((x: any) => x.name)).toEqual(['apfel', 'baum', 'egg', 'fahrrad']);
  872. });
  873. });
  874. describe('calculated fields', () => {
  875. it('filter by simple calculated property', async () => {
  876. const { testEntities } = await adminClient.query(GET_LIST, {
  877. options: {
  878. filter: {
  879. descriptionLength: {
  880. lt: 12,
  881. },
  882. },
  883. },
  884. });
  885. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  886. });
  887. it('filter by calculated property with join', async () => {
  888. const { testEntities } = await adminClient.query(GET_LIST, {
  889. options: {
  890. filter: {
  891. price: {
  892. lt: 14,
  893. },
  894. },
  895. },
  896. });
  897. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'E']);
  898. });
  899. it('sort by simple calculated property', async () => {
  900. const { testEntities } = await adminClient.query(GET_LIST, {
  901. options: {
  902. sort: {
  903. descriptionLength: SortOrder.ASC,
  904. },
  905. },
  906. });
  907. expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C', 'F']);
  908. });
  909. it('sort by calculated property with join', async () => {
  910. const { testEntities } = await adminClient.query(GET_LIST, {
  911. options: {
  912. sort: {
  913. price: SortOrder.ASC,
  914. },
  915. },
  916. });
  917. expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C', 'F']);
  918. });
  919. });
  920. describe('multiple clauses', () => {
  921. it('sort by translated field en & filter', async () => {
  922. const { testEntities } = await adminClient.query(GET_LIST, {
  923. options: {
  924. sort: {
  925. name: SortOrder.ASC,
  926. },
  927. filter: {
  928. order: {
  929. gte: 1,
  930. },
  931. },
  932. },
  933. });
  934. expect(testEntities.items.map((x: any) => x.name)).toEqual([
  935. 'baum',
  936. 'bike',
  937. 'cake',
  938. 'dog',
  939. 'egg',
  940. ]);
  941. });
  942. it('sort by translated field de & filter', async () => {
  943. const { testEntities } = await adminClient.query(
  944. GET_LIST,
  945. {
  946. options: {
  947. sort: {
  948. name: SortOrder.ASC,
  949. },
  950. filter: {
  951. order: {
  952. gte: 1,
  953. },
  954. },
  955. },
  956. },
  957. { languageCode: LanguageCode.de },
  958. );
  959. expect(testEntities.items.map((x: any) => x.name)).toEqual([
  960. 'baum',
  961. 'egg',
  962. 'fahrrad',
  963. 'hund',
  964. 'kuchen',
  965. ]);
  966. });
  967. it('sort by translated field de & filter & pagination', async () => {
  968. const { testEntities } = await adminClient.query(
  969. GET_LIST,
  970. {
  971. options: {
  972. sort: {
  973. name: SortOrder.ASC,
  974. },
  975. filter: {
  976. order: {
  977. gte: 1,
  978. },
  979. },
  980. take: 2,
  981. skip: 1,
  982. },
  983. },
  984. { languageCode: LanguageCode.de },
  985. );
  986. expect(testEntities.items.map((x: any) => x.name)).toEqual(['egg', 'fahrrad']);
  987. });
  988. });
  989. // https://github.com/vendure-ecommerce/vendure/issues/1586
  990. it('using the getMany() of the resulting QueryBuilder', async () => {
  991. const { testEntitiesGetMany } = await adminClient.query(GET_ARRAY_LIST, {});
  992. expect(testEntitiesGetMany.sort((a: any, b: any) => a.id - b.id).map((x: any) => x.price)).toEqual([
  993. 11, 9, 22, 14, 13, 33,
  994. ]);
  995. });
  996. // https://github.com/vendure-ecommerce/vendure/issues/1611
  997. describe('translations handling', () => {
  998. const allTranslations = [
  999. [
  1000. { languageCode: LanguageCode.en, name: 'apple' },
  1001. { languageCode: LanguageCode.de, name: 'apfel' },
  1002. ],
  1003. [
  1004. { languageCode: LanguageCode.en, name: 'bike' },
  1005. { languageCode: LanguageCode.de, name: 'fahrrad' },
  1006. ],
  1007. ];
  1008. function getTestEntityTranslations(testEntities: { items: any[] }) {
  1009. // Explicitly sort the order of the translations as it was being non-deterministic on
  1010. // the mysql CI tests.
  1011. return testEntities.items.map((e: any) =>
  1012. e.translations.sort((a: any, b: any) => (a.languageCode < b.languageCode ? 1 : -1)),
  1013. );
  1014. }
  1015. it('returns all translations with default languageCode', async () => {
  1016. const { testEntities } = await shopClient.query(GET_LIST_WITH_TRANSLATIONS, {
  1017. options: {
  1018. take: 2,
  1019. sort: {
  1020. order: SortOrder.ASC,
  1021. },
  1022. },
  1023. });
  1024. const testEntityTranslations = getTestEntityTranslations(testEntities);
  1025. expect(testEntities.items.map((e: any) => e.name)).toEqual(['apple', 'bike']);
  1026. expect(testEntityTranslations).toEqual(allTranslations);
  1027. });
  1028. it('returns all translations with non-default languageCode', async () => {
  1029. const { testEntities } = await shopClient.query(
  1030. GET_LIST_WITH_TRANSLATIONS,
  1031. {
  1032. options: {
  1033. take: 2,
  1034. sort: {
  1035. order: SortOrder.ASC,
  1036. },
  1037. },
  1038. },
  1039. { languageCode: LanguageCode.de },
  1040. );
  1041. const testEntityTranslations = getTestEntityTranslations(testEntities);
  1042. expect(testEntities.items.map((e: any) => e.name)).toEqual(['apfel', 'fahrrad']);
  1043. expect(testEntityTranslations).toEqual(allTranslations);
  1044. });
  1045. });
  1046. describe('customPropertyMap', () => {
  1047. it('filter by custom string field', async () => {
  1048. const { testEntities } = await shopClient.query(GET_LIST_WITH_ORDERS, {
  1049. options: {
  1050. sort: {
  1051. label: SortOrder.ASC,
  1052. },
  1053. filter: {
  1054. customerLastName: { contains: 'zieme' },
  1055. },
  1056. },
  1057. });
  1058. expect(testEntities.items).toEqual([
  1059. {
  1060. id: 'T_1',
  1061. label: 'A',
  1062. name: 'apple',
  1063. orderRelation: {
  1064. customer: {
  1065. firstName: 'Hayden',
  1066. lastName: 'Zieme',
  1067. },
  1068. id: 'T_1',
  1069. },
  1070. },
  1071. {
  1072. id: 'T_4',
  1073. label: 'D',
  1074. name: 'dog',
  1075. orderRelation: {
  1076. customer: {
  1077. firstName: 'Hayden',
  1078. lastName: 'Zieme',
  1079. },
  1080. id: 'T_4',
  1081. },
  1082. },
  1083. ]);
  1084. });
  1085. it('sort by custom string field', async () => {
  1086. const { testEntities } = await shopClient.query(GET_LIST_WITH_ORDERS, {
  1087. options: {
  1088. sort: {
  1089. customerLastName: SortOrder.ASC,
  1090. },
  1091. },
  1092. });
  1093. expect(testEntities.items.map((i: any) => i.orderRelation.customer)).toEqual([
  1094. { firstName: 'Trevor', lastName: 'Donnelly' },
  1095. { firstName: 'Trevor', lastName: 'Donnelly' },
  1096. { firstName: 'Marques', lastName: 'Sawayn' },
  1097. { firstName: 'Marques', lastName: 'Sawayn' },
  1098. { firstName: 'Hayden', lastName: 'Zieme' },
  1099. { firstName: 'Hayden', lastName: 'Zieme' },
  1100. ]);
  1101. });
  1102. });
  1103. describe('relations in customFields', () => {
  1104. it('should resolve relations in customFields successfully', async () => {
  1105. const { testEntities } = await shopClient.query(GET_LIST_WITH_CUSTOM_FIELD_RELATION, {
  1106. options: {
  1107. filter: {
  1108. label: { eq: 'A' },
  1109. },
  1110. },
  1111. });
  1112. expect(testEntities.items).toEqual([
  1113. {
  1114. id: 'T_1',
  1115. label: 'A',
  1116. customFields: {
  1117. relation: [{ id: 'T_1', data: 'A' }],
  1118. },
  1119. },
  1120. ]);
  1121. });
  1122. });
  1123. });
  1124. const GET_LIST = gql`
  1125. query GetTestEntities($options: TestEntityListOptions) {
  1126. testEntities(options: $options) {
  1127. totalItems
  1128. items {
  1129. id
  1130. label
  1131. name
  1132. date
  1133. }
  1134. }
  1135. }
  1136. `;
  1137. const GET_LIST_WITH_TRANSLATIONS = gql`
  1138. query GetTestEntitiesWithTranslations($options: TestEntityListOptions) {
  1139. testEntities(options: $options) {
  1140. totalItems
  1141. items {
  1142. id
  1143. label
  1144. name
  1145. date
  1146. translations {
  1147. languageCode
  1148. name
  1149. }
  1150. }
  1151. }
  1152. }
  1153. `;
  1154. const GET_LIST_WITH_ORDERS = gql`
  1155. query GetTestEntitiesWithTranslations($options: TestEntityListOptions) {
  1156. testEntities(options: $options) {
  1157. totalItems
  1158. items {
  1159. id
  1160. label
  1161. name
  1162. orderRelation {
  1163. id
  1164. customer {
  1165. firstName
  1166. lastName
  1167. }
  1168. }
  1169. }
  1170. }
  1171. }
  1172. `;
  1173. const GET_ARRAY_LIST = gql`
  1174. query GetTestEntitiesArray($options: TestEntityListOptions) {
  1175. testEntitiesGetMany(options: $options) {
  1176. id
  1177. label
  1178. name
  1179. date
  1180. price
  1181. }
  1182. }
  1183. `;
  1184. const GET_LIST_WITH_CUSTOM_FIELD_RELATION = gql`
  1185. query GetTestWithCustomFieldRelation($options: TestEntityListOptions) {
  1186. testEntities(options: $options) {
  1187. items {
  1188. id
  1189. label
  1190. customFields {
  1191. relation {
  1192. id
  1193. data
  1194. }
  1195. }
  1196. }
  1197. }
  1198. }
  1199. `;