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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. import { mergeConfig } from '@vendure/core';
  2. import { createTestEnvironment } from '@vendure/testing';
  3. import gql from 'graphql-tag';
  4. import path from 'path';
  5. import { initialData } from '../../../e2e-common/e2e-initial-data';
  6. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  7. import { ListQueryPlugin } from './fixtures/test-plugins/list-query-plugin';
  8. import { LanguageCode, SortOrder } from './graphql/generated-e2e-admin-types';
  9. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  10. import { fixPostgresTimezone } from './utils/fix-pg-timezone';
  11. fixPostgresTimezone();
  12. describe('ListQueryBuilder', () => {
  13. const { server, adminClient, shopClient } = createTestEnvironment(
  14. mergeConfig(testConfig, {
  15. apiOptions: {
  16. shopListQueryLimit: 10,
  17. adminListQueryLimit: 30,
  18. },
  19. plugins: [ListQueryPlugin],
  20. }),
  21. );
  22. beforeAll(async () => {
  23. await server.init({
  24. initialData,
  25. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  26. customerCount: 1,
  27. });
  28. await adminClient.asSuperAdmin();
  29. }, TEST_SETUP_TIMEOUT_MS);
  30. afterAll(async () => {
  31. await server.destroy();
  32. });
  33. function getItemLabels(items: any[]): string[] {
  34. return items.map((x: any) => x.label).sort();
  35. }
  36. describe('pagination', () => {
  37. it('all en', async () => {
  38. const { testEntities } = await adminClient.query(
  39. GET_LIST,
  40. {
  41. options: {},
  42. },
  43. { languageCode: LanguageCode.en },
  44. );
  45. expect(testEntities.totalItems).toBe(5);
  46. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E']);
  47. expect(testEntities.items.map((i: any) => i.name)).toEqual([
  48. 'apple',
  49. 'bike',
  50. 'cake',
  51. 'dog',
  52. 'egg',
  53. ]);
  54. });
  55. it('all de', async () => {
  56. const { testEntities } = await adminClient.query(
  57. GET_LIST,
  58. {
  59. options: {},
  60. },
  61. { languageCode: LanguageCode.de },
  62. );
  63. expect(testEntities.totalItems).toBe(5);
  64. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E']);
  65. expect(testEntities.items.map((i: any) => i.name)).toEqual([
  66. 'apfel',
  67. 'fahrrad',
  68. 'kuchen',
  69. 'hund',
  70. 'egg', // falls back to en translation when de doesn't exist
  71. ]);
  72. });
  73. it('take', async () => {
  74. const { testEntities } = await adminClient.query(GET_LIST, {
  75. options: {
  76. take: 2,
  77. },
  78. });
  79. expect(testEntities.totalItems).toBe(5);
  80. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  81. });
  82. it('skip', async () => {
  83. const { testEntities } = await adminClient.query(GET_LIST, {
  84. options: {
  85. skip: 2,
  86. },
  87. });
  88. expect(testEntities.totalItems).toBe(5);
  89. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
  90. });
  91. it('skip negative is ignored', async () => {
  92. const { testEntities } = await adminClient.query(GET_LIST, {
  93. options: {
  94. skip: -1,
  95. },
  96. });
  97. expect(testEntities.totalItems).toBe(5);
  98. expect(testEntities.items.length).toBe(5);
  99. });
  100. it('take zero is ignored', async () => {
  101. const { testEntities } = await adminClient.query(GET_LIST, {
  102. options: {
  103. take: 0,
  104. },
  105. });
  106. expect(testEntities.totalItems).toBe(5);
  107. expect(testEntities.items.length).toBe(5);
  108. });
  109. it('take negative is ignored', async () => {
  110. const { testEntities } = await adminClient.query(GET_LIST, {
  111. options: {
  112. take: -1,
  113. },
  114. });
  115. expect(testEntities.totalItems).toBe(5);
  116. expect(testEntities.items.length).toBe(5);
  117. });
  118. it(
  119. 'take beyond adminListQueryLimit',
  120. assertThrowsWithMessage(async () => {
  121. await adminClient.query(GET_LIST, {
  122. options: {
  123. take: 50,
  124. },
  125. });
  126. }, 'Cannot take more than 30 results from a list query'),
  127. );
  128. it(
  129. 'take beyond shopListQueryLimit',
  130. assertThrowsWithMessage(async () => {
  131. await shopClient.query(GET_LIST, {
  132. options: {
  133. take: 50,
  134. },
  135. });
  136. }, 'Cannot take more than 10 results from a list query'),
  137. );
  138. });
  139. describe('string filtering', () => {
  140. it('eq', async () => {
  141. const { testEntities } = await adminClient.query(GET_LIST, {
  142. options: {
  143. filter: {
  144. label: {
  145. eq: 'B',
  146. },
  147. },
  148. },
  149. });
  150. expect(getItemLabels(testEntities.items)).toEqual(['B']);
  151. });
  152. it('notEq', async () => {
  153. const { testEntities } = await adminClient.query(GET_LIST, {
  154. options: {
  155. filter: {
  156. label: {
  157. notEq: 'B',
  158. },
  159. },
  160. },
  161. });
  162. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'D', 'E']);
  163. });
  164. it('contains', async () => {
  165. const { testEntities } = await adminClient.query(GET_LIST, {
  166. options: {
  167. filter: {
  168. description: {
  169. contains: 'adip',
  170. },
  171. },
  172. },
  173. });
  174. expect(getItemLabels(testEntities.items)).toEqual(['C']);
  175. });
  176. it('notContains', async () => {
  177. const { testEntities } = await adminClient.query(GET_LIST, {
  178. options: {
  179. filter: {
  180. description: {
  181. notContains: 'te',
  182. },
  183. },
  184. },
  185. });
  186. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'E']);
  187. });
  188. it('in', async () => {
  189. const { testEntities } = await adminClient.query(GET_LIST, {
  190. options: {
  191. filter: {
  192. label: {
  193. in: ['A', 'C'],
  194. },
  195. },
  196. },
  197. });
  198. expect(getItemLabels(testEntities.items)).toEqual(['A', 'C']);
  199. });
  200. it('notIn', async () => {
  201. const { testEntities } = await adminClient.query(GET_LIST, {
  202. options: {
  203. filter: {
  204. label: {
  205. notIn: ['A', 'C'],
  206. },
  207. },
  208. },
  209. });
  210. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E']);
  211. });
  212. describe('regex', () => {
  213. it('simple substring', async () => {
  214. const { testEntities } = await adminClient.query(GET_LIST, {
  215. options: {
  216. filter: {
  217. description: {
  218. regex: 'or',
  219. },
  220. },
  221. },
  222. });
  223. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'D']);
  224. });
  225. it('start of string', async () => {
  226. const { testEntities } = await adminClient.query(GET_LIST, {
  227. options: {
  228. filter: {
  229. description: {
  230. regex: '^in',
  231. },
  232. },
  233. },
  234. });
  235. expect(getItemLabels(testEntities.items)).toEqual(['E']);
  236. });
  237. it('end of string', async () => {
  238. const { testEntities } = await adminClient.query(GET_LIST, {
  239. options: {
  240. filter: {
  241. description: {
  242. regex: 'or$',
  243. },
  244. },
  245. },
  246. });
  247. expect(getItemLabels(testEntities.items)).toEqual(['D']);
  248. });
  249. it('alternation', async () => {
  250. const { testEntities } = await adminClient.query(GET_LIST, {
  251. options: {
  252. filter: {
  253. description: {
  254. regex: 'dolor|tempor',
  255. },
  256. },
  257. },
  258. });
  259. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D']);
  260. });
  261. it('complex', async () => {
  262. const { testEntities } = await adminClient.query(GET_LIST, {
  263. options: {
  264. filter: {
  265. description: {
  266. regex: '(dolor|tempor)|inc[i]?d[^a]d.*nt',
  267. },
  268. },
  269. },
  270. });
  271. expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E']);
  272. });
  273. });
  274. });
  275. describe('boolean filtering', () => {
  276. it('eq', async () => {
  277. const { testEntities } = await adminClient.query(GET_LIST, {
  278. options: {
  279. filter: {
  280. active: {
  281. eq: false,
  282. },
  283. },
  284. },
  285. });
  286. expect(getItemLabels(testEntities.items)).toEqual(['C', 'E']);
  287. });
  288. });
  289. describe('number filtering', () => {
  290. it('eq', async () => {
  291. const { testEntities } = await adminClient.query(GET_LIST, {
  292. options: {
  293. filter: {
  294. order: {
  295. eq: 1,
  296. },
  297. },
  298. },
  299. });
  300. expect(getItemLabels(testEntities.items)).toEqual(['B']);
  301. });
  302. it('lt', async () => {
  303. const { testEntities } = await adminClient.query(GET_LIST, {
  304. options: {
  305. filter: {
  306. order: {
  307. lt: 1,
  308. },
  309. },
  310. },
  311. });
  312. expect(getItemLabels(testEntities.items)).toEqual(['A']);
  313. });
  314. it('lte', async () => {
  315. const { testEntities } = await adminClient.query(GET_LIST, {
  316. options: {
  317. filter: {
  318. order: {
  319. lte: 1,
  320. },
  321. },
  322. },
  323. });
  324. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  325. });
  326. it('gt', async () => {
  327. const { testEntities } = await adminClient.query(GET_LIST, {
  328. options: {
  329. filter: {
  330. order: {
  331. gt: 1,
  332. },
  333. },
  334. },
  335. });
  336. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
  337. });
  338. it('gte', async () => {
  339. const { testEntities } = await adminClient.query(GET_LIST, {
  340. options: {
  341. filter: {
  342. order: {
  343. gte: 1,
  344. },
  345. },
  346. },
  347. });
  348. expect(getItemLabels(testEntities.items)).toEqual(['B', 'C', 'D', 'E']);
  349. });
  350. it('between', async () => {
  351. const { testEntities } = await adminClient.query(GET_LIST, {
  352. options: {
  353. filter: {
  354. order: {
  355. between: {
  356. start: 2,
  357. end: 4,
  358. },
  359. },
  360. },
  361. },
  362. });
  363. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
  364. });
  365. });
  366. describe('date filtering', () => {
  367. it('before', async () => {
  368. const { testEntities } = await adminClient.query(GET_LIST, {
  369. options: {
  370. filter: {
  371. date: {
  372. before: '2020-01-20T10:00:00.000Z',
  373. },
  374. },
  375. },
  376. });
  377. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  378. });
  379. it('before on same date', async () => {
  380. const { testEntities } = await adminClient.query(GET_LIST, {
  381. options: {
  382. filter: {
  383. date: {
  384. before: '2020-01-15T17:00:00.000Z',
  385. },
  386. },
  387. },
  388. });
  389. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  390. });
  391. it('after', async () => {
  392. const { testEntities } = await adminClient.query(GET_LIST, {
  393. options: {
  394. filter: {
  395. date: {
  396. after: '2020-01-20T10:00:00.000Z',
  397. },
  398. },
  399. },
  400. });
  401. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
  402. });
  403. it('after on same date', async () => {
  404. const { testEntities } = await adminClient.query(GET_LIST, {
  405. options: {
  406. filter: {
  407. date: {
  408. after: '2020-01-25T09:00:00.000Z',
  409. },
  410. },
  411. },
  412. });
  413. expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
  414. });
  415. it('between', async () => {
  416. const { testEntities } = await adminClient.query(GET_LIST, {
  417. options: {
  418. filter: {
  419. date: {
  420. between: {
  421. start: '2020-01-10T10:00:00.000Z',
  422. end: '2020-01-20T10:00:00.000Z',
  423. },
  424. },
  425. },
  426. },
  427. });
  428. expect(getItemLabels(testEntities.items)).toEqual(['B']);
  429. });
  430. });
  431. describe('sorting', () => {
  432. it('sort by string', async () => {
  433. const { testEntities } = await adminClient.query(GET_LIST, {
  434. options: {
  435. sort: {
  436. label: SortOrder.DESC,
  437. },
  438. },
  439. });
  440. expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
  441. });
  442. it('sort by number', async () => {
  443. const { testEntities } = await adminClient.query(GET_LIST, {
  444. options: {
  445. sort: {
  446. order: SortOrder.DESC,
  447. },
  448. },
  449. });
  450. expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
  451. });
  452. it('sort by date', async () => {
  453. const { testEntities } = await adminClient.query(GET_LIST, {
  454. options: {
  455. sort: {
  456. date: SortOrder.DESC,
  457. },
  458. },
  459. });
  460. expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
  461. });
  462. it('sort by ID', async () => {
  463. const { testEntities } = await adminClient.query(GET_LIST, {
  464. options: {
  465. sort: {
  466. id: SortOrder.DESC,
  467. },
  468. },
  469. });
  470. expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
  471. });
  472. it('sort by translated field en', async () => {
  473. const { testEntities } = await adminClient.query(GET_LIST, {
  474. options: {
  475. sort: {
  476. name: SortOrder.ASC,
  477. },
  478. },
  479. });
  480. expect(testEntities.items.map((x: any) => x.name)).toEqual([
  481. 'apple',
  482. 'bike',
  483. 'cake',
  484. 'dog',
  485. 'egg',
  486. ]);
  487. });
  488. it('sort by translated field de', async () => {
  489. const { testEntities } = await adminClient.query(
  490. GET_LIST,
  491. {
  492. options: {
  493. sort: {
  494. name: SortOrder.ASC,
  495. },
  496. },
  497. },
  498. { languageCode: LanguageCode.de },
  499. );
  500. expect(testEntities.items.map((x: any) => x.name)).toEqual([
  501. 'apfel',
  502. 'egg',
  503. 'fahrrad',
  504. 'hund',
  505. 'kuchen',
  506. ]);
  507. });
  508. it('sort by translated field en with take', async () => {
  509. const { testEntities } = await adminClient.query(GET_LIST, {
  510. options: {
  511. sort: {
  512. name: SortOrder.ASC,
  513. },
  514. take: 3,
  515. },
  516. });
  517. expect(testEntities.items.map((x: any) => x.name)).toEqual(['apple', 'bike', 'cake']);
  518. });
  519. it('sort by translated field de with take', async () => {
  520. const { testEntities } = await adminClient.query(
  521. GET_LIST,
  522. {
  523. options: {
  524. sort: {
  525. name: SortOrder.ASC,
  526. },
  527. take: 3,
  528. },
  529. },
  530. { languageCode: LanguageCode.de },
  531. );
  532. expect(testEntities.items.map((x: any) => x.name)).toEqual(['apfel', 'egg', 'fahrrad']);
  533. });
  534. });
  535. describe('calculated fields', () => {
  536. it('filter by simple calculated property', async () => {
  537. const { testEntities } = await adminClient.query(GET_LIST, {
  538. options: {
  539. filter: {
  540. descriptionLength: {
  541. lt: 12,
  542. },
  543. },
  544. },
  545. });
  546. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
  547. });
  548. it('filter by calculated property with join', async () => {
  549. const { testEntities } = await adminClient.query(GET_LIST, {
  550. options: {
  551. filter: {
  552. price: {
  553. lt: 14,
  554. },
  555. },
  556. },
  557. });
  558. expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'E']);
  559. });
  560. it('sort by simple calculated property', async () => {
  561. const { testEntities } = await adminClient.query(GET_LIST, {
  562. options: {
  563. sort: {
  564. descriptionLength: SortOrder.ASC,
  565. },
  566. },
  567. });
  568. expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C']);
  569. });
  570. it('sort by calculated property with join', async () => {
  571. const { testEntities } = await adminClient.query(GET_LIST, {
  572. options: {
  573. sort: {
  574. price: SortOrder.ASC,
  575. },
  576. },
  577. });
  578. expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C']);
  579. });
  580. });
  581. describe('multiple clauses', () => {
  582. it('sort by translated field en & filter', async () => {
  583. const { testEntities } = await adminClient.query(GET_LIST, {
  584. options: {
  585. sort: {
  586. name: SortOrder.ASC,
  587. },
  588. filter: {
  589. order: {
  590. gte: 1,
  591. },
  592. },
  593. },
  594. });
  595. expect(testEntities.items.map((x: any) => x.name)).toEqual(['bike', 'cake', 'dog', 'egg']);
  596. });
  597. it('sort by translated field de & filter', async () => {
  598. const { testEntities } = await adminClient.query(
  599. GET_LIST,
  600. {
  601. options: {
  602. sort: {
  603. name: SortOrder.ASC,
  604. },
  605. filter: {
  606. order: {
  607. gte: 1,
  608. },
  609. },
  610. },
  611. },
  612. { languageCode: LanguageCode.de },
  613. );
  614. expect(testEntities.items.map((x: any) => x.name)).toEqual(['egg', 'fahrrad', 'hund', 'kuchen']);
  615. });
  616. it('sort by translated field de & filter & pagination', async () => {
  617. const { testEntities } = await adminClient.query(
  618. GET_LIST,
  619. {
  620. options: {
  621. sort: {
  622. name: SortOrder.ASC,
  623. },
  624. filter: {
  625. order: {
  626. gte: 1,
  627. },
  628. },
  629. take: 2,
  630. skip: 1,
  631. },
  632. },
  633. { languageCode: LanguageCode.de },
  634. );
  635. expect(testEntities.items.map((x: any) => x.name)).toEqual(['fahrrad', 'hund']);
  636. });
  637. });
  638. });
  639. const GET_LIST = gql`
  640. query GetTestEntities($options: TestEntityListOptions) {
  641. testEntities(options: $options) {
  642. totalItems
  643. items {
  644. id
  645. label
  646. name
  647. date
  648. }
  649. }
  650. }
  651. `;