elasticsearch-plugin.e2e-spec.ts 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. /* tslint:disable:no-non-null-assertion */
  2. import { SortOrder } from '@vendure/common/lib/generated-types';
  3. import { pick } from '@vendure/common/lib/pick';
  4. import { DefaultLogger, LogLevel, mergeConfig } from '@vendure/core';
  5. import { facetValueCollectionFilter } from '@vendure/core/dist/config/collection/default-collection-filters';
  6. import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN, SimpleGraphQLClient } from '@vendure/testing';
  7. import gql from 'graphql-tag';
  8. import path from 'path';
  9. import { initialData } from '../../../e2e-common/e2e-initial-data';
  10. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  11. import {
  12. AssignProductsToChannel,
  13. CreateChannel,
  14. CreateCollection,
  15. CreateFacet,
  16. CurrencyCode,
  17. DeleteProduct,
  18. DeleteProductVariant,
  19. LanguageCode,
  20. RemoveProductsFromChannel,
  21. SearchFacetValues,
  22. SearchGetPrices,
  23. SearchInput,
  24. UpdateCollection,
  25. UpdateProduct,
  26. UpdateProductVariants,
  27. UpdateTaxRate,
  28. } from '../../core/e2e/graphql/generated-e2e-admin-types';
  29. import { SearchProductsShop } from '../../core/e2e/graphql/generated-e2e-shop-types';
  30. import {
  31. ASSIGN_PRODUCT_TO_CHANNEL,
  32. CREATE_CHANNEL,
  33. CREATE_COLLECTION,
  34. CREATE_FACET,
  35. DELETE_PRODUCT,
  36. DELETE_PRODUCT_VARIANT,
  37. REMOVE_PRODUCT_FROM_CHANNEL,
  38. UPDATE_COLLECTION,
  39. UPDATE_PRODUCT,
  40. UPDATE_PRODUCT_VARIANTS,
  41. UPDATE_TAX_RATE,
  42. } from '../../core/e2e/graphql/shared-definitions';
  43. import { ElasticsearchPlugin } from '../src/plugin';
  44. import { SEARCH_PRODUCTS_SHOP } from './../../core/e2e/graphql/shop-definitions';
  45. import { awaitRunningJobs } from './../../core/e2e/utils/await-running-jobs';
  46. import { GetJobInfo, JobState, Reindex } from './graphql/generated-e2e-elasticsearch-plugin-types';
  47. describe('Elasticsearch plugin', () => {
  48. const { server, adminClient, shopClient } = createTestEnvironment(
  49. mergeConfig(testConfig, {
  50. plugins: [
  51. ElasticsearchPlugin.init({
  52. indexPrefix: 'e2e-tests',
  53. port: process.env.CI ? +(process.env.E2E_ELASTIC_PORT || 9200) : 9200,
  54. host: process.env.CI ? 'http://127.0.0.1' : 'http://192.168.99.100',
  55. }),
  56. ],
  57. }),
  58. );
  59. beforeAll(async () => {
  60. await server.init({
  61. initialData,
  62. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
  63. customerCount: 1,
  64. });
  65. await adminClient.asSuperAdmin();
  66. await adminClient.query(REINDEX);
  67. await awaitRunningJobs(adminClient);
  68. }, TEST_SETUP_TIMEOUT_MS);
  69. afterAll(async () => {
  70. await server.destroy();
  71. });
  72. function doAdminSearchQuery(input: SearchInput) {
  73. return adminClient.query<SearchProductsShop.Query, SearchProductsShop.Variables>(SEARCH_PRODUCTS, {
  74. input,
  75. });
  76. }
  77. async function testGroupByProduct(client: SimpleGraphQLClient) {
  78. const result = await client.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  79. SEARCH_PRODUCTS_SHOP,
  80. {
  81. input: {
  82. groupByProduct: true,
  83. },
  84. },
  85. );
  86. expect(result.search.totalItems).toBe(20);
  87. }
  88. async function testNoGrouping(client: SimpleGraphQLClient) {
  89. const result = await client.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  90. SEARCH_PRODUCTS_SHOP,
  91. {
  92. input: {
  93. groupByProduct: false,
  94. },
  95. },
  96. );
  97. expect(result.search.totalItems).toBe(34);
  98. }
  99. async function testMatchSearchTerm(client: SimpleGraphQLClient) {
  100. const result = await client.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  101. SEARCH_PRODUCTS_SHOP,
  102. {
  103. input: {
  104. term: 'camera',
  105. groupByProduct: true,
  106. },
  107. },
  108. );
  109. expect(result.search.items.map(i => i.productName)).toEqual([
  110. 'Instant Camera',
  111. 'Camera Lens',
  112. 'SLR Camera',
  113. ]);
  114. }
  115. async function testMatchFacetValueIds(client: SimpleGraphQLClient) {
  116. const result = await client.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  117. SEARCH_PRODUCTS_SHOP,
  118. {
  119. input: {
  120. facetValueIds: ['T_1', 'T_2'],
  121. groupByProduct: true,
  122. sort: {
  123. name: SortOrder.ASC,
  124. },
  125. },
  126. },
  127. );
  128. expect(result.search.items.map(i => i.productName)).toEqual([
  129. 'Clacky Keyboard',
  130. 'Curvy Monitor',
  131. 'Gaming PC',
  132. 'Hard Drive',
  133. 'Laptop',
  134. 'USB Cable',
  135. ]);
  136. }
  137. async function testMatchCollectionId(client: SimpleGraphQLClient) {
  138. const result = await client.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  139. SEARCH_PRODUCTS_SHOP,
  140. {
  141. input: {
  142. collectionId: 'T_2',
  143. groupByProduct: true,
  144. },
  145. },
  146. );
  147. expect(result.search.items.map(i => i.productName)).toEqual([
  148. 'Spiky Cactus',
  149. 'Orchid',
  150. 'Bonsai Tree',
  151. ]);
  152. }
  153. async function testSinglePrices(client: SimpleGraphQLClient) {
  154. const result = await client.query<SearchGetPrices.Query, SearchGetPrices.Variables>(
  155. SEARCH_GET_PRICES,
  156. {
  157. input: {
  158. groupByProduct: false,
  159. take: 3,
  160. sort: {
  161. price: SortOrder.ASC,
  162. },
  163. },
  164. },
  165. );
  166. expect(result.search.items).toEqual([
  167. {
  168. price: { value: 799 },
  169. priceWithTax: { value: 959 },
  170. },
  171. {
  172. price: { value: 1498 },
  173. priceWithTax: { value: 1798 },
  174. },
  175. {
  176. price: { value: 1550 },
  177. priceWithTax: { value: 1860 },
  178. },
  179. ]);
  180. }
  181. async function testPriceRanges(client: SimpleGraphQLClient) {
  182. const result = await client.query<SearchGetPrices.Query, SearchGetPrices.Variables>(
  183. SEARCH_GET_PRICES,
  184. {
  185. input: {
  186. groupByProduct: true,
  187. take: 3,
  188. term: 'laptop',
  189. },
  190. },
  191. );
  192. expect(result.search.items).toEqual([
  193. {
  194. price: { min: 129900, max: 229900 },
  195. priceWithTax: { min: 155880, max: 275880 },
  196. },
  197. ]);
  198. }
  199. describe('shop api', () => {
  200. it('group by product', () => testGroupByProduct(shopClient));
  201. it('no grouping', () => testNoGrouping(shopClient));
  202. it('matches search term', () => testMatchSearchTerm(shopClient));
  203. it('matches by facetValueId', () => testMatchFacetValueIds(shopClient));
  204. it('matches by collectionId', () => testMatchCollectionId(shopClient));
  205. it('single prices', () => testSinglePrices(shopClient));
  206. it('price ranges', () => testPriceRanges(shopClient));
  207. it('returns correct facetValues when not grouped by product', async () => {
  208. const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(
  209. SEARCH_GET_FACET_VALUES,
  210. {
  211. input: {
  212. groupByProduct: false,
  213. },
  214. },
  215. );
  216. expect(result.search.facetValues).toEqual([
  217. { count: 21, facetValue: { id: 'T_1', name: 'electronics' } },
  218. { count: 17, facetValue: { id: 'T_2', name: 'computers' } },
  219. { count: 4, facetValue: { id: 'T_3', name: 'photo' } },
  220. { count: 10, facetValue: { id: 'T_4', name: 'sports equipment' } },
  221. { count: 3, facetValue: { id: 'T_5', name: 'home & garden' } },
  222. { count: 3, facetValue: { id: 'T_6', name: 'plants' } },
  223. ]);
  224. });
  225. it('returns correct facetValues when grouped by product', async () => {
  226. const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(
  227. SEARCH_GET_FACET_VALUES,
  228. {
  229. input: {
  230. groupByProduct: true,
  231. },
  232. },
  233. );
  234. expect(result.search.facetValues).toEqual([
  235. { count: 10, facetValue: { id: 'T_1', name: 'electronics' } },
  236. { count: 6, facetValue: { id: 'T_2', name: 'computers' } },
  237. { count: 4, facetValue: { id: 'T_3', name: 'photo' } },
  238. { count: 7, facetValue: { id: 'T_4', name: 'sports equipment' } },
  239. { count: 3, facetValue: { id: 'T_5', name: 'home & garden' } },
  240. { count: 3, facetValue: { id: 'T_6', name: 'plants' } },
  241. ]);
  242. });
  243. it('omits facetValues of private facets', async () => {
  244. const { createFacet } = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(
  245. CREATE_FACET,
  246. {
  247. input: {
  248. code: 'profit-margin',
  249. isPrivate: true,
  250. translations: [{ languageCode: LanguageCode.en, name: 'Profit Margin' }],
  251. values: [
  252. {
  253. code: 'massive',
  254. translations: [{ languageCode: LanguageCode.en, name: 'massive' }],
  255. },
  256. ],
  257. },
  258. },
  259. );
  260. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  261. input: {
  262. id: 'T_2',
  263. // T_1 & T_2 are the existing facetValues (electronics & photo)
  264. facetValueIds: ['T_1', 'T_2', createFacet.values[0].id],
  265. },
  266. });
  267. const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(
  268. SEARCH_GET_FACET_VALUES,
  269. {
  270. input: {
  271. groupByProduct: true,
  272. },
  273. },
  274. );
  275. expect(result.search.facetValues).toEqual([
  276. { count: 10, facetValue: { id: 'T_1', name: 'electronics' } },
  277. { count: 6, facetValue: { id: 'T_2', name: 'computers' } },
  278. { count: 4, facetValue: { id: 'T_3', name: 'photo' } },
  279. { count: 7, facetValue: { id: 'T_4', name: 'sports equipment' } },
  280. { count: 3, facetValue: { id: 'T_5', name: 'home & garden' } },
  281. { count: 3, facetValue: { id: 'T_6', name: 'plants' } },
  282. ]);
  283. });
  284. it('encodes the productId and productVariantId', async () => {
  285. const result = await shopClient.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  286. SEARCH_PRODUCTS_SHOP,
  287. {
  288. input: {
  289. groupByProduct: false,
  290. take: 1,
  291. },
  292. },
  293. );
  294. expect(pick(result.search.items[0], ['productId', 'productVariantId'])).toEqual({
  295. productId: 'T_1',
  296. productVariantId: 'T_1',
  297. });
  298. });
  299. it('omits results for disabled ProductVariants', async () => {
  300. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  301. UPDATE_PRODUCT_VARIANTS,
  302. {
  303. input: [{ id: 'T_3', enabled: false }],
  304. },
  305. );
  306. await awaitRunningJobs(adminClient);
  307. const result = await shopClient.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  308. SEARCH_PRODUCTS_SHOP,
  309. {
  310. input: {
  311. groupByProduct: false,
  312. take: 3,
  313. },
  314. },
  315. );
  316. expect(result.search.items.map(i => i.productVariantId)).toEqual(['T_1', 'T_2', 'T_4']);
  317. });
  318. it('encodes collectionIds', async () => {
  319. const result = await shopClient.query<SearchProductsShop.Query, SearchProductsShop.Variables>(
  320. SEARCH_PRODUCTS_SHOP,
  321. {
  322. input: {
  323. groupByProduct: false,
  324. term: 'cactus',
  325. take: 1,
  326. },
  327. },
  328. );
  329. expect(result.search.items[0].collectionIds).toEqual(['T_2']);
  330. });
  331. });
  332. describe('admin api', () => {
  333. it('group by product', () => testGroupByProduct(adminClient));
  334. it('no grouping', () => testNoGrouping(adminClient));
  335. it('matches search term', () => testMatchSearchTerm(adminClient));
  336. it('matches by facetValueId', () => testMatchFacetValueIds(adminClient));
  337. it('matches by collectionId', () => testMatchCollectionId(adminClient));
  338. it('single prices', () => testSinglePrices(adminClient));
  339. it('price ranges', () => testPriceRanges(adminClient));
  340. describe('updating the index', () => {
  341. it('updates index when ProductVariants are changed', async () => {
  342. await awaitRunningJobs(adminClient);
  343. const { search } = await doAdminSearchQuery({ term: 'drive', groupByProduct: false });
  344. expect(search.items.map(i => i.sku)).toEqual([
  345. 'IHD455T1',
  346. 'IHD455T2',
  347. 'IHD455T3',
  348. 'IHD455T4',
  349. 'IHD455T6',
  350. ]);
  351. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  352. UPDATE_PRODUCT_VARIANTS,
  353. {
  354. input: search.items.map(i => ({
  355. id: i.productVariantId,
  356. sku: i.sku + '_updated',
  357. })),
  358. },
  359. );
  360. await awaitRunningJobs(adminClient);
  361. const { search: search2 } = await doAdminSearchQuery({
  362. term: 'drive',
  363. groupByProduct: false,
  364. });
  365. expect(search2.items.map(i => i.sku)).toEqual([
  366. 'IHD455T1_updated',
  367. 'IHD455T2_updated',
  368. 'IHD455T3_updated',
  369. 'IHD455T4_updated',
  370. 'IHD455T6_updated',
  371. ]);
  372. });
  373. it('updates index when ProductVariants are deleted', async () => {
  374. await awaitRunningJobs(adminClient);
  375. const { search } = await doAdminSearchQuery({ term: 'drive', groupByProduct: false });
  376. await adminClient.query<DeleteProductVariant.Mutation, DeleteProductVariant.Variables>(
  377. DELETE_PRODUCT_VARIANT,
  378. {
  379. id: search.items[0].productVariantId,
  380. },
  381. );
  382. await awaitRunningJobs(adminClient);
  383. const { search: search2 } = await doAdminSearchQuery({
  384. term: 'drive',
  385. groupByProduct: false,
  386. });
  387. expect(search2.items.map(i => i.sku)).toEqual([
  388. 'IHD455T2_updated',
  389. 'IHD455T3_updated',
  390. 'IHD455T4_updated',
  391. 'IHD455T6_updated',
  392. ]);
  393. });
  394. it('updates index when a Product is changed', async () => {
  395. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  396. input: {
  397. id: 'T_1',
  398. facetValueIds: [],
  399. },
  400. });
  401. await awaitRunningJobs(adminClient);
  402. const result = await doAdminSearchQuery({ facetValueIds: ['T_2'], groupByProduct: true });
  403. expect(result.search.items.map(i => i.productName)).toEqual([
  404. 'Gaming PC',
  405. 'Clacky Keyboard',
  406. 'USB Cable',
  407. 'Curvy Monitor',
  408. 'Hard Drive',
  409. ]);
  410. });
  411. it('updates index when a Product is deleted', async () => {
  412. const { search } = await doAdminSearchQuery({ facetValueIds: ['T_2'], groupByProduct: true });
  413. expect(search.items.map(i => i.productId)).toEqual(['T_3', 'T_5', 'T_6', 'T_2', 'T_4']);
  414. await adminClient.query<DeleteProduct.Mutation, DeleteProduct.Variables>(DELETE_PRODUCT, {
  415. id: 'T_5',
  416. });
  417. await awaitRunningJobs(adminClient);
  418. const { search: search2 } = await doAdminSearchQuery({
  419. facetValueIds: ['T_2'],
  420. groupByProduct: true,
  421. });
  422. expect(search2.items.map(i => i.productId)).toEqual(['T_3', 'T_6', 'T_2', 'T_4']);
  423. });
  424. it('updates index when a Collection is changed', async () => {
  425. await adminClient.query<UpdateCollection.Mutation, UpdateCollection.Variables>(
  426. UPDATE_COLLECTION,
  427. {
  428. input: {
  429. id: 'T_2',
  430. filters: [
  431. {
  432. code: facetValueCollectionFilter.code,
  433. arguments: [
  434. {
  435. name: 'facetValueIds',
  436. value: `["T_4"]`,
  437. type: 'facetValueIds',
  438. },
  439. {
  440. name: 'containsAny',
  441. value: `false`,
  442. type: 'boolean',
  443. },
  444. ],
  445. },
  446. ],
  447. },
  448. },
  449. );
  450. await awaitRunningJobs(adminClient);
  451. const result = await doAdminSearchQuery({ collectionId: 'T_2', groupByProduct: true });
  452. expect(result.search.items.map(i => i.productName)).toEqual([
  453. 'Road Bike',
  454. 'Skipping Rope',
  455. 'Boxing Gloves',
  456. 'Tent',
  457. 'Cruiser Skateboard',
  458. 'Football',
  459. 'Running Shoe',
  460. ]);
  461. });
  462. it('updates index when a Collection created', async () => {
  463. const { createCollection } = await adminClient.query<
  464. CreateCollection.Mutation,
  465. CreateCollection.Variables
  466. >(CREATE_COLLECTION, {
  467. input: {
  468. translations: [
  469. {
  470. languageCode: LanguageCode.en,
  471. name: 'Photo',
  472. description: '',
  473. },
  474. ],
  475. filters: [
  476. {
  477. code: facetValueCollectionFilter.code,
  478. arguments: [
  479. {
  480. name: 'facetValueIds',
  481. value: `["T_3"]`,
  482. type: 'facetValueIds',
  483. },
  484. {
  485. name: 'containsAny',
  486. value: `false`,
  487. type: 'boolean',
  488. },
  489. ],
  490. },
  491. ],
  492. },
  493. });
  494. await awaitRunningJobs(adminClient);
  495. const result = await doAdminSearchQuery({
  496. collectionId: createCollection.id,
  497. groupByProduct: true,
  498. });
  499. expect(result.search.items.map(i => i.productName)).toEqual([
  500. 'Instant Camera',
  501. 'Camera Lens',
  502. 'Tripod',
  503. 'SLR Camera',
  504. ]);
  505. });
  506. it('updates index when a taxRate is changed', async () => {
  507. await adminClient.query<UpdateTaxRate.Mutation, UpdateTaxRate.Variables>(UPDATE_TAX_RATE, {
  508. input: {
  509. // Default Channel's defaultTaxZone is Europe (id 2) and the id of the standard TaxRate
  510. // to Europe is 2.
  511. id: 'T_2',
  512. value: 50,
  513. },
  514. });
  515. await awaitRunningJobs(adminClient);
  516. const result = await adminClient.query<SearchGetPrices.Query, SearchGetPrices.Variables>(
  517. SEARCH_GET_PRICES,
  518. {
  519. input: {
  520. groupByProduct: true,
  521. term: 'laptop',
  522. } as SearchInput,
  523. },
  524. );
  525. expect(result.search.items).toEqual([
  526. {
  527. price: { min: 129900, max: 229900 },
  528. priceWithTax: { min: 194850, max: 344850 },
  529. },
  530. ]);
  531. });
  532. it('returns disabled field when not grouped', async () => {
  533. const result = await doAdminSearchQuery({ groupByProduct: false, term: 'laptop' });
  534. expect(result.search.items.map(pick(['productVariantId', 'enabled']))).toEqual([
  535. { productVariantId: 'T_1', enabled: true },
  536. { productVariantId: 'T_2', enabled: true },
  537. { productVariantId: 'T_3', enabled: false },
  538. { productVariantId: 'T_4', enabled: true },
  539. ]);
  540. });
  541. it('when grouped, disabled is false if at least one variant is enabled', async () => {
  542. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  543. UPDATE_PRODUCT_VARIANTS,
  544. {
  545. input: [{ id: 'T_1', enabled: false }, { id: 'T_2', enabled: false }],
  546. },
  547. );
  548. await awaitRunningJobs(adminClient);
  549. const result = await doAdminSearchQuery({ groupByProduct: true, term: 'laptop' });
  550. expect(result.search.items.map(pick(['productId', 'enabled']))).toEqual([
  551. { productId: 'T_1', enabled: true },
  552. ]);
  553. });
  554. it('when grouped, disabled is true if all variants are disabled', async () => {
  555. await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  556. UPDATE_PRODUCT_VARIANTS,
  557. {
  558. input: [{ id: 'T_4', enabled: false }],
  559. },
  560. );
  561. await awaitRunningJobs(adminClient);
  562. const result = await doAdminSearchQuery({ groupByProduct: true, take: 3, term: 'laptop' });
  563. expect(result.search.items.map(pick(['productId', 'enabled']))).toEqual([
  564. { productId: 'T_1', enabled: false },
  565. ]);
  566. });
  567. it('when grouped, disabled is true product is disabled', async () => {
  568. await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
  569. input: {
  570. id: 'T_3',
  571. enabled: false,
  572. },
  573. });
  574. await awaitRunningJobs(adminClient);
  575. const result = await doAdminSearchQuery({ groupByProduct: true, term: 'gaming' });
  576. expect(result.search.items.map(pick(['productId', 'enabled']))).toEqual([
  577. { productId: 'T_3', enabled: false },
  578. ]);
  579. });
  580. });
  581. describe('channel handling', () => {
  582. const SECOND_CHANNEL_TOKEN = 'second-channel-token';
  583. let secondChannel: CreateChannel.CreateChannel;
  584. beforeAll(async () => {
  585. const { createChannel } = await adminClient.query<
  586. CreateChannel.Mutation,
  587. CreateChannel.Variables
  588. >(CREATE_CHANNEL, {
  589. input: {
  590. code: 'second-channel',
  591. token: SECOND_CHANNEL_TOKEN,
  592. defaultLanguageCode: LanguageCode.en,
  593. currencyCode: CurrencyCode.GBP,
  594. pricesIncludeTax: true,
  595. defaultTaxZoneId: 'T_2',
  596. defaultShippingZoneId: 'T_1',
  597. },
  598. });
  599. secondChannel = createChannel;
  600. });
  601. it('adding product to channel', async () => {
  602. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  603. await adminClient.query<AssignProductsToChannel.Mutation, AssignProductsToChannel.Variables>(
  604. ASSIGN_PRODUCT_TO_CHANNEL,
  605. {
  606. input: { channelId: secondChannel.id, productIds: ['T_1', 'T_2'] },
  607. },
  608. );
  609. await awaitRunningJobs(adminClient);
  610. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  611. const { search } = await doAdminSearchQuery({ groupByProduct: true });
  612. expect(search.items.map(i => i.productId).sort()).toEqual(['T_1', 'T_2']);
  613. });
  614. it('removing product from channel', async () => {
  615. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  616. const { removeProductsFromChannel } = await adminClient.query<
  617. RemoveProductsFromChannel.Mutation,
  618. RemoveProductsFromChannel.Variables
  619. >(REMOVE_PRODUCT_FROM_CHANNEL, {
  620. input: {
  621. productIds: ['T_2'],
  622. channelId: secondChannel.id,
  623. },
  624. });
  625. await awaitRunningJobs(adminClient);
  626. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  627. const { search } = await doAdminSearchQuery({ groupByProduct: true });
  628. expect(search.items.map(i => i.productId)).toEqual(['T_1']);
  629. });
  630. it('reindexes in channel', async () => {
  631. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  632. const { reindex } = await adminClient.query<Reindex.Mutation>(REINDEX);
  633. await awaitRunningJobs(adminClient);
  634. const { job } = await adminClient.query<GetJobInfo.Query, GetJobInfo.Variables>(
  635. GET_JOB_INFO,
  636. { id: reindex.id },
  637. );
  638. expect(job!.state).toBe(JobState.COMPLETED);
  639. const { search } = await doAdminSearchQuery({ groupByProduct: true });
  640. expect(search.items.map(i => i.productId).sort()).toEqual(['T_1']);
  641. });
  642. });
  643. });
  644. });
  645. export const SEARCH_PRODUCTS = gql`
  646. query SearchProductsAdmin($input: SearchInput!) {
  647. search(input: $input) {
  648. totalItems
  649. items {
  650. enabled
  651. productId
  652. productName
  653. productPreview
  654. productVariantId
  655. productVariantName
  656. productVariantPreview
  657. sku
  658. }
  659. }
  660. }
  661. `;
  662. export const SEARCH_GET_FACET_VALUES = gql`
  663. query SearchFacetValues($input: SearchInput!) {
  664. search(input: $input) {
  665. totalItems
  666. facetValues {
  667. count
  668. facetValue {
  669. id
  670. name
  671. }
  672. }
  673. }
  674. }
  675. `;
  676. export const SEARCH_GET_PRICES = gql`
  677. query SearchGetPrices($input: SearchInput!) {
  678. search(input: $input) {
  679. items {
  680. price {
  681. ... on PriceRange {
  682. min
  683. max
  684. }
  685. ... on SinglePrice {
  686. value
  687. }
  688. }
  689. priceWithTax {
  690. ... on PriceRange {
  691. min
  692. max
  693. }
  694. ... on SinglePrice {
  695. value
  696. }
  697. }
  698. }
  699. }
  700. }
  701. `;
  702. const REINDEX = gql`
  703. mutation Reindex {
  704. reindex {
  705. id
  706. name
  707. state
  708. progress
  709. duration
  710. result
  711. }
  712. }
  713. `;
  714. const GET_JOB_INFO = gql`
  715. query GetJobInfo($id: String!) {
  716. job(jobId: $id) {
  717. id
  718. name
  719. state
  720. progress
  721. duration
  722. result
  723. }
  724. }
  725. `;