collection.e2e-spec.ts 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043
  1. /* tslint:disable:no-non-null-assertion */
  2. import { ROOT_COLLECTION_NAME } from '@vendure/common/lib/shared-constants';
  3. import {
  4. DefaultJobQueuePlugin,
  5. facetValueCollectionFilter,
  6. variantNameCollectionFilter,
  7. } from '@vendure/core';
  8. import { createTestEnvironment } from '@vendure/testing';
  9. import gql from 'graphql-tag';
  10. import path from 'path';
  11. import { initialData } from '../../../e2e-common/e2e-initial-data';
  12. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  13. import { pick } from '../../common/lib/pick';
  14. import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
  15. import {
  16. CollectionFragment,
  17. CreateCollectionInput,
  18. CreateCollectionMutation,
  19. DeletionResult,
  20. FacetValueFragment,
  21. GetAssetListQuery,
  22. GetProductsWithVariantIdsQuery,
  23. LanguageCode,
  24. SortOrder,
  25. } from './graphql/generated-e2e-admin-types';
  26. import * as Codegen from './graphql/generated-e2e-admin-types';
  27. import {
  28. CREATE_COLLECTION,
  29. DELETE_PRODUCT,
  30. DELETE_PRODUCT_VARIANT,
  31. GET_ASSET_LIST,
  32. GET_COLLECTIONS,
  33. UPDATE_COLLECTION,
  34. UPDATE_PRODUCT,
  35. UPDATE_PRODUCT_VARIANTS,
  36. } from './graphql/shared-definitions';
  37. import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
  38. import { awaitRunningJobs } from './utils/await-running-jobs';
  39. import { sortById } from './utils/test-order-utils';
  40. describe('Collection resolver', () => {
  41. const { server, adminClient, shopClient } = createTestEnvironment({
  42. ...testConfig(),
  43. plugins: [DefaultJobQueuePlugin],
  44. });
  45. let assets: GetAssetListQuery['assets']['items'];
  46. let facetValues: FacetValueFragment[];
  47. let electronicsCollection: CollectionFragment;
  48. let computersCollection: CollectionFragment;
  49. let pearCollection: CollectionFragment;
  50. let electronicsBreadcrumbsCollection: CollectionFragment;
  51. let computersBreadcrumbsCollection: CollectionFragment;
  52. let pearBreadcrumbsCollection: CollectionFragment;
  53. beforeAll(async () => {
  54. await server.init({
  55. initialData,
  56. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-collections.csv'),
  57. customerCount: 1,
  58. });
  59. await adminClient.asSuperAdmin();
  60. const assetsResult = await adminClient.query<
  61. Codegen.GetAssetListQuery,
  62. Codegen.GetAssetListQueryVariables
  63. >(GET_ASSET_LIST, {
  64. options: {
  65. sort: {
  66. name: SortOrder.ASC,
  67. },
  68. },
  69. });
  70. assets = assetsResult.assets.items;
  71. const facetValuesResult = await adminClient.query<Codegen.GetFacetValuesQuery>(GET_FACET_VALUES);
  72. facetValues = facetValuesResult.facets.items.reduce(
  73. (values, facet) => [...values, ...facet.values],
  74. [] as FacetValueFragment[],
  75. );
  76. }, TEST_SETUP_TIMEOUT_MS);
  77. afterAll(async () => {
  78. await server.destroy();
  79. });
  80. /**
  81. * Test case for https://github.com/vendure-ecommerce/vendure/issues/97
  82. */
  83. it('collection breadcrumbs works after bootstrap', async () => {
  84. const result = await adminClient.query<Codegen.GetCollectionBreadcrumbsQuery>(
  85. GET_COLLECTION_BREADCRUMBS,
  86. {
  87. id: 'T_1',
  88. },
  89. );
  90. expect(result.collection!.breadcrumbs[0].name).toBe(ROOT_COLLECTION_NAME);
  91. });
  92. describe('createCollection', () => {
  93. it('creates a root collection', async () => {
  94. const result = await adminClient.query<
  95. Codegen.CreateCollectionMutation,
  96. Codegen.CreateCollectionMutationVariables
  97. >(CREATE_COLLECTION, {
  98. input: {
  99. assetIds: [assets[0].id, assets[1].id],
  100. featuredAssetId: assets[1].id,
  101. filters: [
  102. {
  103. code: facetValueCollectionFilter.code,
  104. arguments: [
  105. {
  106. name: 'facetValueIds',
  107. value: `["${getFacetValueId('electronics')}"]`,
  108. },
  109. {
  110. name: 'containsAny',
  111. value: `false`,
  112. },
  113. ],
  114. },
  115. ],
  116. translations: [
  117. {
  118. languageCode: LanguageCode.en,
  119. name: 'Electronics',
  120. description: '',
  121. slug: 'electronics',
  122. },
  123. ],
  124. },
  125. });
  126. electronicsCollection = result.createCollection;
  127. expect(electronicsCollection).toMatchSnapshot();
  128. expect(electronicsCollection.parent!.name).toBe(ROOT_COLLECTION_NAME);
  129. });
  130. it('creates a nested collection', async () => {
  131. const result = await adminClient.query<
  132. Codegen.CreateCollectionMutation,
  133. Codegen.CreateCollectionMutationVariables
  134. >(CREATE_COLLECTION, {
  135. input: {
  136. parentId: electronicsCollection.id,
  137. translations: [
  138. {
  139. languageCode: LanguageCode.en,
  140. name: 'Computers',
  141. description: '',
  142. slug: 'computers',
  143. },
  144. ],
  145. filters: [
  146. {
  147. code: facetValueCollectionFilter.code,
  148. arguments: [
  149. {
  150. name: 'facetValueIds',
  151. value: `["${getFacetValueId('computers')}"]`,
  152. },
  153. {
  154. name: 'containsAny',
  155. value: `false`,
  156. },
  157. ],
  158. },
  159. ],
  160. },
  161. });
  162. computersCollection = result.createCollection;
  163. expect(computersCollection.parent!.name).toBe(electronicsCollection.name);
  164. });
  165. it('creates a 2nd level nested collection', async () => {
  166. const result = await adminClient.query<
  167. Codegen.CreateCollectionMutation,
  168. Codegen.CreateCollectionMutationVariables
  169. >(CREATE_COLLECTION, {
  170. input: {
  171. parentId: computersCollection.id,
  172. translations: [
  173. { languageCode: LanguageCode.en, name: 'Pear', description: '', slug: 'pear' },
  174. ],
  175. filters: [
  176. {
  177. code: facetValueCollectionFilter.code,
  178. arguments: [
  179. {
  180. name: 'facetValueIds',
  181. value: `["${getFacetValueId('pear')}"]`,
  182. },
  183. {
  184. name: 'containsAny',
  185. value: `false`,
  186. },
  187. ],
  188. },
  189. ],
  190. },
  191. });
  192. pearCollection = result.createCollection;
  193. expect(pearCollection.parent!.name).toBe(computersCollection.name);
  194. });
  195. it('slug is normalized to be url-safe', async () => {
  196. const { createCollection } = await adminClient.query<
  197. Codegen.CreateCollectionMutation,
  198. Codegen.CreateCollectionMutationVariables
  199. >(CREATE_COLLECTION, {
  200. input: {
  201. translations: [
  202. {
  203. languageCode: LanguageCode.en,
  204. name: 'Accessories',
  205. description: '',
  206. slug: 'Accessories!',
  207. },
  208. {
  209. languageCode: LanguageCode.de,
  210. name: 'Zubehör',
  211. description: '',
  212. slug: 'Zubehör!',
  213. },
  214. ],
  215. filters: [],
  216. },
  217. });
  218. expect(createCollection.slug).toBe('accessories');
  219. expect(createCollection.translations.find(t => t.languageCode === LanguageCode.en)?.slug).toBe(
  220. 'accessories',
  221. );
  222. expect(createCollection.translations.find(t => t.languageCode === LanguageCode.de)?.slug).toBe(
  223. 'zubehor',
  224. );
  225. });
  226. it('create with duplicate slug is renamed to be unique', async () => {
  227. const { createCollection } = await adminClient.query<
  228. Codegen.CreateCollectionMutation,
  229. Codegen.CreateCollectionMutationVariables
  230. >(CREATE_COLLECTION, {
  231. input: {
  232. translations: [
  233. {
  234. languageCode: LanguageCode.en,
  235. name: 'Accessories',
  236. description: '',
  237. slug: 'Accessories',
  238. },
  239. {
  240. languageCode: LanguageCode.de,
  241. name: 'Zubehör',
  242. description: '',
  243. slug: 'Zubehör',
  244. },
  245. ],
  246. filters: [],
  247. },
  248. });
  249. expect(createCollection.translations.find(t => t.languageCode === LanguageCode.en)?.slug).toBe(
  250. 'accessories-2',
  251. );
  252. expect(createCollection.translations.find(t => t.languageCode === LanguageCode.de)?.slug).toBe(
  253. 'zubehor-2',
  254. );
  255. });
  256. it('creates a root collection to became a 1st level collection later #779', async () => {
  257. const result = await adminClient.query<
  258. Codegen.CreateCollectionMutation,
  259. Codegen.CreateCollectionMutationVariables
  260. >(CREATE_COLLECTION, {
  261. input: {
  262. assetIds: [assets[0].id, assets[1].id],
  263. featuredAssetId: assets[1].id,
  264. filters: [
  265. {
  266. code: facetValueCollectionFilter.code,
  267. arguments: [
  268. {
  269. name: 'facetValueIds',
  270. value: `["${getFacetValueId('computers')}"]`,
  271. },
  272. {
  273. name: 'containsAny',
  274. value: `false`,
  275. },
  276. ],
  277. },
  278. ],
  279. translations: [
  280. {
  281. languageCode: LanguageCode.en,
  282. name: 'Computers Breadcrumbs',
  283. description: '',
  284. slug: 'computers_breadcrumbs',
  285. },
  286. ],
  287. },
  288. });
  289. computersBreadcrumbsCollection = result.createCollection;
  290. expect(computersBreadcrumbsCollection.parent!.name).toBe(ROOT_COLLECTION_NAME);
  291. });
  292. it('creates a root collection to be a parent collection for 1st level collection with id greater than child collection #779', async () => {
  293. const result = await adminClient.query<
  294. Codegen.CreateCollectionMutation,
  295. Codegen.CreateCollectionMutationVariables
  296. >(CREATE_COLLECTION, {
  297. input: {
  298. assetIds: [assets[0].id, assets[1].id],
  299. featuredAssetId: assets[1].id,
  300. filters: [
  301. {
  302. code: facetValueCollectionFilter.code,
  303. arguments: [
  304. {
  305. name: 'facetValueIds',
  306. value: `["${getFacetValueId('electronics')}"]`,
  307. },
  308. {
  309. name: 'containsAny',
  310. value: `false`,
  311. },
  312. ],
  313. },
  314. ],
  315. translations: [
  316. {
  317. languageCode: LanguageCode.en,
  318. name: 'Electronics Breadcrumbs',
  319. description: '',
  320. slug: 'electronics_breadcrumbs',
  321. },
  322. ],
  323. },
  324. });
  325. electronicsBreadcrumbsCollection = result.createCollection;
  326. expect(electronicsBreadcrumbsCollection.parent!.name).toBe(ROOT_COLLECTION_NAME);
  327. });
  328. it('creates a 2nd level nested collection #779', async () => {
  329. const result = await adminClient.query<
  330. Codegen.CreateCollectionMutation,
  331. Codegen.CreateCollectionMutationVariables
  332. >(CREATE_COLLECTION, {
  333. input: {
  334. parentId: computersBreadcrumbsCollection.id,
  335. translations: [
  336. {
  337. languageCode: LanguageCode.en,
  338. name: 'Pear Breadcrumbs',
  339. description: '',
  340. slug: 'pear_breadcrumbs',
  341. },
  342. ],
  343. filters: [
  344. {
  345. code: facetValueCollectionFilter.code,
  346. arguments: [
  347. {
  348. name: 'facetValueIds',
  349. value: `["${getFacetValueId('pear')}"]`,
  350. },
  351. {
  352. name: 'containsAny',
  353. value: `false`,
  354. },
  355. ],
  356. },
  357. ],
  358. },
  359. });
  360. pearBreadcrumbsCollection = result.createCollection;
  361. expect(pearBreadcrumbsCollection.parent!.name).toBe(computersBreadcrumbsCollection.name);
  362. });
  363. });
  364. describe('updateCollection', () => {
  365. it('updates with assets', async () => {
  366. const { updateCollection } = await adminClient.query<
  367. Codegen.UpdateCollectionMutation,
  368. Codegen.UpdateCollectionMutationVariables
  369. >(UPDATE_COLLECTION, {
  370. input: {
  371. id: pearCollection.id,
  372. assetIds: [assets[1].id, assets[2].id],
  373. featuredAssetId: assets[1].id,
  374. translations: [
  375. { languageCode: LanguageCode.en, description: 'Apple stuff ', slug: 'apple-stuff' },
  376. ],
  377. },
  378. });
  379. await awaitRunningJobs(adminClient, 5000);
  380. expect(updateCollection).toMatchSnapshot();
  381. pearCollection = updateCollection;
  382. });
  383. it('updating existing assets', async () => {
  384. const { updateCollection } = await adminClient.query<
  385. Codegen.UpdateCollectionMutation,
  386. Codegen.UpdateCollectionMutationVariables
  387. >(UPDATE_COLLECTION, {
  388. input: {
  389. id: pearCollection.id,
  390. assetIds: [assets[3].id, assets[0].id],
  391. featuredAssetId: assets[3].id,
  392. },
  393. });
  394. await awaitRunningJobs(adminClient, 5000);
  395. expect(updateCollection.assets.map(a => a.id)).toEqual([assets[3].id, assets[0].id]);
  396. });
  397. it('removes all assets', async () => {
  398. const { updateCollection } = await adminClient.query<
  399. Codegen.UpdateCollectionMutation,
  400. Codegen.UpdateCollectionMutationVariables
  401. >(UPDATE_COLLECTION, {
  402. input: {
  403. id: pearCollection.id,
  404. assetIds: [],
  405. },
  406. });
  407. await awaitRunningJobs(adminClient, 5000);
  408. expect(updateCollection.assets).toEqual([]);
  409. expect(updateCollection.featuredAsset).toBeNull();
  410. });
  411. });
  412. describe('querying', () => {
  413. it('collection by id', async () => {
  414. const result = await adminClient.query<
  415. Codegen.GetCollectionQuery,
  416. Codegen.GetCollectionQueryVariables
  417. >(GET_COLLECTION, {
  418. id: computersCollection.id,
  419. });
  420. if (!result.collection) {
  421. fail(`did not return the collection`);
  422. return;
  423. }
  424. expect(result.collection.id).toBe(computersCollection.id);
  425. });
  426. it('collection by slug', async () => {
  427. const result = await adminClient.query<
  428. Codegen.GetCollectionQuery,
  429. Codegen.GetCollectionQueryVariables
  430. >(GET_COLLECTION, {
  431. slug: computersCollection.slug,
  432. });
  433. if (!result.collection) {
  434. fail(`did not return the collection`);
  435. return;
  436. }
  437. expect(result.collection.id).toBe(computersCollection.id);
  438. });
  439. // https://github.com/vendure-ecommerce/vendure/issues/538
  440. it('falls back to default language slug', async () => {
  441. const result = await adminClient.query<
  442. Codegen.GetCollectionQuery,
  443. Codegen.GetCollectionQueryVariables
  444. >(
  445. GET_COLLECTION,
  446. {
  447. slug: computersCollection.slug,
  448. },
  449. { languageCode: LanguageCode.de },
  450. );
  451. if (!result.collection) {
  452. fail(`did not return the collection`);
  453. return;
  454. }
  455. expect(result.collection.id).toBe(computersCollection.id);
  456. });
  457. it(
  458. 'throws if neither id nor slug provided',
  459. assertThrowsWithMessage(async () => {
  460. await adminClient.query<Codegen.GetCollectionQuery, Codegen.GetCollectionQueryVariables>(
  461. GET_COLLECTION,
  462. {},
  463. );
  464. }, 'Either the Collection id or slug must be provided'),
  465. );
  466. it(
  467. 'throws if id and slug do not refer to the same Product',
  468. assertThrowsWithMessage(async () => {
  469. await adminClient.query<Codegen.GetCollectionQuery, Codegen.GetCollectionQueryVariables>(
  470. GET_COLLECTION,
  471. {
  472. id: computersCollection.id,
  473. slug: pearCollection.slug,
  474. },
  475. );
  476. }, 'The provided id and slug refer to different Collections'),
  477. );
  478. it('parent field', async () => {
  479. const result = await adminClient.query<
  480. Codegen.GetCollectionQuery,
  481. Codegen.GetCollectionQueryVariables
  482. >(GET_COLLECTION, {
  483. id: computersCollection.id,
  484. });
  485. if (!result.collection) {
  486. fail(`did not return the collection`);
  487. return;
  488. }
  489. expect(result.collection.parent!.name).toBe('Electronics');
  490. });
  491. // Tests fix for https://github.com/vendure-ecommerce/vendure/issues/361
  492. it('parent field resolved by CollectionEntityResolver', async () => {
  493. const { product } = await adminClient.query<
  494. Codegen.GetProductCollectionsWithParentQuery,
  495. Codegen.GetProductCollectionsWithParentQueryVariables
  496. >(GET_PRODUCT_COLLECTIONS_WITH_PARENT, {
  497. id: 'T_1',
  498. });
  499. expect(product?.collections.length).toBe(6);
  500. expect(product?.collections.sort(sortById)).toEqual([
  501. {
  502. id: 'T_10',
  503. name: 'Pear Breadcrumbs',
  504. parent: {
  505. id: 'T_8',
  506. name: 'Computers Breadcrumbs',
  507. },
  508. },
  509. {
  510. id: 'T_3',
  511. name: 'Electronics',
  512. parent: {
  513. id: 'T_1',
  514. name: '__root_collection__',
  515. },
  516. },
  517. {
  518. id: 'T_4',
  519. name: 'Computers',
  520. parent: {
  521. id: 'T_3',
  522. name: 'Electronics',
  523. },
  524. },
  525. {
  526. id: 'T_5',
  527. name: 'Pear',
  528. parent: {
  529. id: 'T_4',
  530. name: 'Computers',
  531. },
  532. },
  533. {
  534. id: 'T_8',
  535. name: 'Computers Breadcrumbs',
  536. parent: {
  537. id: 'T_1',
  538. name: '__root_collection__',
  539. },
  540. },
  541. {
  542. id: 'T_9',
  543. name: 'Electronics Breadcrumbs',
  544. parent: {
  545. id: 'T_1',
  546. name: '__root_collection__',
  547. },
  548. },
  549. ]);
  550. });
  551. // https://github.com/vendure-ecommerce/vendure/issues/981
  552. it('nested parent field in shop API', async () => {
  553. const { collections } = await shopClient.query<Codegen.GetCollectionNestedParentsQuery>(
  554. GET_COLLECTION_NESTED_PARENTS,
  555. );
  556. expect(collections.items[0].parent?.name).toBe(ROOT_COLLECTION_NAME);
  557. });
  558. it('children field', async () => {
  559. const result = await adminClient.query<
  560. Codegen.GetCollectionQuery,
  561. Codegen.GetCollectionQueryVariables
  562. >(GET_COLLECTION, {
  563. id: electronicsCollection.id,
  564. });
  565. if (!result.collection) {
  566. fail(`did not return the collection`);
  567. return;
  568. }
  569. expect(result.collection.children!.length).toBe(1);
  570. expect(result.collection.children![0].name).toBe('Computers');
  571. });
  572. it('breadcrumbs', async () => {
  573. const result = await adminClient.query<
  574. Codegen.GetCollectionBreadcrumbsQuery,
  575. Codegen.GetCollectionBreadcrumbsQueryVariables
  576. >(GET_COLLECTION_BREADCRUMBS, {
  577. id: pearCollection.id,
  578. });
  579. if (!result.collection) {
  580. fail(`did not return the collection`);
  581. return;
  582. }
  583. expect(result.collection.breadcrumbs).toEqual([
  584. { id: 'T_1', name: ROOT_COLLECTION_NAME, slug: ROOT_COLLECTION_NAME },
  585. {
  586. id: electronicsCollection.id,
  587. name: electronicsCollection.name,
  588. slug: electronicsCollection.slug,
  589. },
  590. {
  591. id: computersCollection.id,
  592. name: computersCollection.name,
  593. slug: computersCollection.slug,
  594. },
  595. { id: pearCollection.id, name: pearCollection.name, slug: pearCollection.slug },
  596. ]);
  597. });
  598. it('breadcrumbs for root collection', async () => {
  599. const result = await adminClient.query<
  600. Codegen.GetCollectionBreadcrumbsQuery,
  601. Codegen.GetCollectionBreadcrumbsQueryVariables
  602. >(GET_COLLECTION_BREADCRUMBS, {
  603. id: 'T_1',
  604. });
  605. if (!result.collection) {
  606. fail(`did not return the collection`);
  607. return;
  608. }
  609. expect(result.collection.breadcrumbs).toEqual([
  610. { id: 'T_1', name: ROOT_COLLECTION_NAME, slug: ROOT_COLLECTION_NAME },
  611. ]);
  612. });
  613. it('collections.assets', async () => {
  614. const { collections } = await adminClient.query<Codegen.GetCollectionsWithAssetsQuery>(gql`
  615. query GetCollectionsWithAssets {
  616. collections {
  617. items {
  618. assets {
  619. name
  620. }
  621. }
  622. }
  623. }
  624. `);
  625. expect(collections.items[0].assets).toBeDefined();
  626. });
  627. // https://github.com/vendure-ecommerce/vendure/issues/642
  628. it('sorting on Collection.productVariants.price', async () => {
  629. const { collection } = await adminClient.query<
  630. Codegen.GetCollectionQuery,
  631. Codegen.GetCollectionQueryVariables
  632. >(GET_COLLECTION, {
  633. id: computersCollection.id,
  634. variantListOptions: {
  635. sort: {
  636. price: SortOrder.ASC,
  637. },
  638. },
  639. });
  640. expect(collection!.productVariants.items.map(i => i.price)).toEqual([
  641. 3799, 5374, 6900, 7489, 7896, 9299, 13435, 14374, 16994, 93120, 94920, 108720, 109995, 129900,
  642. 139900, 219900, 229900,
  643. ]);
  644. });
  645. });
  646. describe('moveCollection', () => {
  647. it('moves a collection to a new parent', async () => {
  648. const result = await adminClient.query<
  649. Codegen.MoveCollectionMutation,
  650. Codegen.MoveCollectionMutationVariables
  651. >(MOVE_COLLECTION, {
  652. input: {
  653. collectionId: pearCollection.id,
  654. parentId: electronicsCollection.id,
  655. index: 0,
  656. },
  657. });
  658. expect(result.moveCollection.parent!.id).toBe(electronicsCollection.id);
  659. const positions = await getChildrenOf(electronicsCollection.id);
  660. expect(positions.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
  661. });
  662. it('re-evaluates Collection contents on move', async () => {
  663. await awaitRunningJobs(adminClient, 5000);
  664. const result = await adminClient.query<
  665. Codegen.GetCollectionProductsQuery,
  666. Codegen.GetCollectionProductsQueryVariables
  667. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
  668. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  669. 'Laptop 13 inch 8GB',
  670. 'Laptop 15 inch 8GB',
  671. 'Laptop 13 inch 16GB',
  672. 'Laptop 15 inch 16GB',
  673. 'Instant Camera',
  674. ]);
  675. });
  676. it('moves a 1st level collection to a new parent to check breadcrumbs', async () => {
  677. const result = await adminClient.query<
  678. Codegen.MoveCollectionMutation,
  679. Codegen.MoveCollectionMutationVariables
  680. >(MOVE_COLLECTION, {
  681. input: {
  682. collectionId: computersBreadcrumbsCollection.id,
  683. parentId: electronicsBreadcrumbsCollection.id,
  684. index: 0,
  685. },
  686. });
  687. expect(result.moveCollection.parent!.id).toBe(electronicsBreadcrumbsCollection.id);
  688. const positions = await getChildrenOf(electronicsBreadcrumbsCollection.id);
  689. expect(positions.map(i => i.id)).toEqual([computersBreadcrumbsCollection.id]);
  690. });
  691. it('breadcrumbs for collection with ids out of order', async () => {
  692. const result = await adminClient.query<
  693. Codegen.GetCollectionBreadcrumbsQuery,
  694. Codegen.GetCollectionBreadcrumbsQueryVariables
  695. >(GET_COLLECTION_BREADCRUMBS, {
  696. id: pearBreadcrumbsCollection.id,
  697. });
  698. if (!result.collection) {
  699. fail(`did not return the collection`);
  700. return;
  701. }
  702. expect(result.collection.breadcrumbs).toEqual([
  703. { id: 'T_1', name: ROOT_COLLECTION_NAME, slug: ROOT_COLLECTION_NAME },
  704. {
  705. id: electronicsBreadcrumbsCollection.id,
  706. name: electronicsBreadcrumbsCollection.name,
  707. slug: electronicsBreadcrumbsCollection.slug,
  708. },
  709. {
  710. id: computersBreadcrumbsCollection.id,
  711. name: computersBreadcrumbsCollection.name,
  712. slug: computersBreadcrumbsCollection.slug,
  713. },
  714. {
  715. id: pearBreadcrumbsCollection.id,
  716. name: pearBreadcrumbsCollection.name,
  717. slug: pearBreadcrumbsCollection.slug,
  718. },
  719. ]);
  720. });
  721. it('alters the position in the current parent 1', async () => {
  722. await adminClient.query<Codegen.MoveCollectionMutation, Codegen.MoveCollectionMutationVariables>(
  723. MOVE_COLLECTION,
  724. {
  725. input: {
  726. collectionId: computersCollection.id,
  727. parentId: electronicsCollection.id,
  728. index: 0,
  729. },
  730. },
  731. );
  732. const afterResult = await getChildrenOf(electronicsCollection.id);
  733. expect(afterResult.map(i => i.id)).toEqual([computersCollection.id, pearCollection.id]);
  734. });
  735. it('alters the position in the current parent 2', async () => {
  736. await adminClient.query<Codegen.MoveCollectionMutation, Codegen.MoveCollectionMutationVariables>(
  737. MOVE_COLLECTION,
  738. {
  739. input: {
  740. collectionId: pearCollection.id,
  741. parentId: electronicsCollection.id,
  742. index: 0,
  743. },
  744. },
  745. );
  746. const afterResult = await getChildrenOf(electronicsCollection.id);
  747. expect(afterResult.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
  748. });
  749. it('corrects an out-of-bounds negative index value', async () => {
  750. await adminClient.query<Codegen.MoveCollectionMutation, Codegen.MoveCollectionMutationVariables>(
  751. MOVE_COLLECTION,
  752. {
  753. input: {
  754. collectionId: pearCollection.id,
  755. parentId: electronicsCollection.id,
  756. index: -3,
  757. },
  758. },
  759. );
  760. const afterResult = await getChildrenOf(electronicsCollection.id);
  761. expect(afterResult.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
  762. });
  763. it('corrects an out-of-bounds positive index value', async () => {
  764. await adminClient.query<Codegen.MoveCollectionMutation, Codegen.MoveCollectionMutationVariables>(
  765. MOVE_COLLECTION,
  766. {
  767. input: {
  768. collectionId: pearCollection.id,
  769. parentId: electronicsCollection.id,
  770. index: 10,
  771. },
  772. },
  773. );
  774. const afterResult = await getChildrenOf(electronicsCollection.id);
  775. expect(afterResult.map(i => i.id)).toEqual([computersCollection.id, pearCollection.id]);
  776. });
  777. it(
  778. 'throws if attempting to move into self',
  779. assertThrowsWithMessage(
  780. () =>
  781. adminClient.query<
  782. Codegen.MoveCollectionMutation,
  783. Codegen.MoveCollectionMutationVariables
  784. >(MOVE_COLLECTION, {
  785. input: {
  786. collectionId: pearCollection.id,
  787. parentId: pearCollection.id,
  788. index: 0,
  789. },
  790. }),
  791. `Cannot move a Collection into itself`,
  792. ),
  793. );
  794. it(
  795. 'throws if attempting to move into a descendant of self',
  796. assertThrowsWithMessage(
  797. () =>
  798. adminClient.query<
  799. Codegen.MoveCollectionMutation,
  800. Codegen.MoveCollectionMutationVariables
  801. >(MOVE_COLLECTION, {
  802. input: {
  803. collectionId: pearCollection.id,
  804. parentId: pearCollection.id,
  805. index: 0,
  806. },
  807. }),
  808. `Cannot move a Collection into itself`,
  809. ),
  810. );
  811. async function getChildrenOf(parentId: string): Promise<Array<{ name: string; id: string }>> {
  812. const result = await adminClient.query<Codegen.GetCollectionsQuery>(GET_COLLECTIONS);
  813. return result.collections.items.filter(i => i.parent!.id === parentId);
  814. }
  815. });
  816. describe('deleteCollection', () => {
  817. let collectionToDeleteParent: CreateCollectionMutation['createCollection'];
  818. let collectionToDeleteChild: CreateCollectionMutation['createCollection'];
  819. let laptopProductId: string;
  820. beforeAll(async () => {
  821. const result1 = await adminClient.query<
  822. Codegen.CreateCollectionMutation,
  823. Codegen.CreateCollectionMutationVariables
  824. >(CREATE_COLLECTION, {
  825. input: {
  826. filters: [
  827. {
  828. code: variantNameCollectionFilter.code,
  829. arguments: [
  830. {
  831. name: 'operator',
  832. value: 'contains',
  833. },
  834. {
  835. name: 'term',
  836. value: 'laptop',
  837. },
  838. ],
  839. },
  840. ],
  841. translations: [
  842. {
  843. languageCode: LanguageCode.en,
  844. name: 'Delete Me Parent',
  845. description: '',
  846. slug: 'delete-me-parent',
  847. },
  848. ],
  849. assetIds: ['T_1'],
  850. },
  851. });
  852. collectionToDeleteParent = result1.createCollection;
  853. const result2 = await adminClient.query<
  854. Codegen.CreateCollectionMutation,
  855. Codegen.CreateCollectionMutationVariables
  856. >(CREATE_COLLECTION, {
  857. input: {
  858. filters: [],
  859. translations: [
  860. {
  861. languageCode: LanguageCode.en,
  862. name: 'Delete Me Child',
  863. description: '',
  864. slug: 'delete-me-child',
  865. },
  866. ],
  867. parentId: collectionToDeleteParent.id,
  868. assetIds: ['T_2'],
  869. },
  870. });
  871. collectionToDeleteChild = result2.createCollection;
  872. await awaitRunningJobs(adminClient, 5000);
  873. });
  874. it(
  875. 'throws for invalid collection id',
  876. assertThrowsWithMessage(async () => {
  877. await adminClient.query<
  878. Codegen.DeleteCollectionMutation,
  879. Codegen.DeleteCollectionMutationVariables
  880. >(DELETE_COLLECTION, {
  881. id: 'T_999',
  882. });
  883. }, "No Collection with the id '999' could be found"),
  884. );
  885. it('collection and product related prior to deletion', async () => {
  886. const { collection } = await adminClient.query<
  887. Codegen.GetCollectionProductsQuery,
  888. Codegen.GetCollectionProductsQueryVariables
  889. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  890. id: collectionToDeleteParent.id,
  891. });
  892. expect(collection!.productVariants.items.map(pick(['name']))).toEqual([
  893. { name: 'Laptop 13 inch 8GB' },
  894. { name: 'Laptop 15 inch 8GB' },
  895. { name: 'Laptop 13 inch 16GB' },
  896. { name: 'Laptop 15 inch 16GB' },
  897. ]);
  898. laptopProductId = collection!.productVariants.items[0].productId;
  899. const { product } = await adminClient.query<
  900. Codegen.GetProductCollectionsQuery,
  901. Codegen.GetProductCollectionsQueryVariables
  902. >(GET_PRODUCT_COLLECTIONS, {
  903. id: laptopProductId,
  904. });
  905. expect(product!.collections).toEqual([
  906. {
  907. id: 'T_3',
  908. name: 'Electronics',
  909. },
  910. {
  911. id: 'T_4',
  912. name: 'Computers',
  913. },
  914. {
  915. id: 'T_5',
  916. name: 'Pear',
  917. },
  918. {
  919. id: 'T_8',
  920. name: 'Computers Breadcrumbs',
  921. },
  922. {
  923. id: 'T_9',
  924. name: 'Electronics Breadcrumbs',
  925. },
  926. {
  927. id: 'T_10',
  928. name: 'Pear Breadcrumbs',
  929. },
  930. {
  931. id: 'T_11',
  932. name: 'Delete Me Parent',
  933. },
  934. {
  935. id: 'T_12',
  936. name: 'Delete Me Child',
  937. },
  938. ]);
  939. });
  940. it('deleteCollection works', async () => {
  941. const { deleteCollection } = await adminClient.query<
  942. Codegen.DeleteCollectionMutation,
  943. Codegen.DeleteCollectionMutationVariables
  944. >(DELETE_COLLECTION, {
  945. id: collectionToDeleteParent.id,
  946. });
  947. expect(deleteCollection.result).toBe(DeletionResult.DELETED);
  948. });
  949. it('deleted parent collection is null', async () => {
  950. const { collection } = await adminClient.query<
  951. Codegen.GetCollectionQuery,
  952. Codegen.GetCollectionQueryVariables
  953. >(GET_COLLECTION, {
  954. id: collectionToDeleteParent.id,
  955. });
  956. expect(collection).toBeNull();
  957. });
  958. it('deleted child collection is null', async () => {
  959. const { collection } = await adminClient.query<
  960. Codegen.GetCollectionQuery,
  961. Codegen.GetCollectionQueryVariables
  962. >(GET_COLLECTION, {
  963. id: collectionToDeleteChild.id,
  964. });
  965. expect(collection).toBeNull();
  966. });
  967. it('product no longer lists collection', async () => {
  968. const { product } = await adminClient.query<
  969. Codegen.GetProductCollectionsQuery,
  970. Codegen.GetProductCollectionsQueryVariables
  971. >(GET_PRODUCT_COLLECTIONS, {
  972. id: laptopProductId,
  973. });
  974. expect(product!.collections).toEqual([
  975. { id: 'T_3', name: 'Electronics' },
  976. { id: 'T_4', name: 'Computers' },
  977. { id: 'T_5', name: 'Pear' },
  978. {
  979. id: 'T_8',
  980. name: 'Computers Breadcrumbs',
  981. },
  982. {
  983. id: 'T_9',
  984. name: 'Electronics Breadcrumbs',
  985. },
  986. {
  987. id: 'T_10',
  988. name: 'Pear Breadcrumbs',
  989. },
  990. ]);
  991. });
  992. });
  993. describe('filters', () => {
  994. it('Collection with no filters has no productVariants', async () => {
  995. const result = await adminClient.query<
  996. Codegen.CreateCollectionSelectVariantsMutation,
  997. Codegen.CreateCollectionSelectVariantsMutationVariables
  998. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  999. input: {
  1000. translations: [
  1001. { languageCode: LanguageCode.en, name: 'Empty', description: '', slug: 'empty' },
  1002. ],
  1003. filters: [],
  1004. } as CreateCollectionInput,
  1005. });
  1006. expect(result.createCollection.productVariants.totalItems).toBe(0);
  1007. });
  1008. describe('facetValue filter', () => {
  1009. it('electronics', async () => {
  1010. const result = await adminClient.query<
  1011. Codegen.GetCollectionProductsQuery,
  1012. Codegen.GetCollectionProductsQueryVariables
  1013. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1014. id: electronicsCollection.id,
  1015. });
  1016. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1017. 'Laptop 13 inch 8GB',
  1018. 'Laptop 15 inch 8GB',
  1019. 'Laptop 13 inch 16GB',
  1020. 'Laptop 15 inch 16GB',
  1021. 'Curvy Monitor 24 inch',
  1022. 'Curvy Monitor 27 inch',
  1023. 'Gaming PC i7-8700 240GB SSD',
  1024. 'Gaming PC R7-2700 240GB SSD',
  1025. 'Gaming PC i7-8700 120GB SSD',
  1026. 'Gaming PC R7-2700 120GB SSD',
  1027. 'Hard Drive 1TB',
  1028. 'Hard Drive 2TB',
  1029. 'Hard Drive 3TB',
  1030. 'Hard Drive 4TB',
  1031. 'Hard Drive 6TB',
  1032. 'Clacky Keyboard',
  1033. 'USB Cable',
  1034. 'Instant Camera',
  1035. 'Camera Lens',
  1036. 'Tripod',
  1037. 'SLR Camera',
  1038. ]);
  1039. });
  1040. it('computers', async () => {
  1041. const result = await adminClient.query<
  1042. Codegen.GetCollectionProductsQuery,
  1043. Codegen.GetCollectionProductsQueryVariables
  1044. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1045. id: computersCollection.id,
  1046. });
  1047. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1048. 'Laptop 13 inch 8GB',
  1049. 'Laptop 15 inch 8GB',
  1050. 'Laptop 13 inch 16GB',
  1051. 'Laptop 15 inch 16GB',
  1052. 'Curvy Monitor 24 inch',
  1053. 'Curvy Monitor 27 inch',
  1054. 'Gaming PC i7-8700 240GB SSD',
  1055. 'Gaming PC R7-2700 240GB SSD',
  1056. 'Gaming PC i7-8700 120GB SSD',
  1057. 'Gaming PC R7-2700 120GB SSD',
  1058. 'Hard Drive 1TB',
  1059. 'Hard Drive 2TB',
  1060. 'Hard Drive 3TB',
  1061. 'Hard Drive 4TB',
  1062. 'Hard Drive 6TB',
  1063. 'Clacky Keyboard',
  1064. 'USB Cable',
  1065. ]);
  1066. });
  1067. it('photo AND pear', async () => {
  1068. const result = await adminClient.query<
  1069. Codegen.CreateCollectionSelectVariantsMutation,
  1070. Codegen.CreateCollectionSelectVariantsMutationVariables
  1071. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  1072. input: {
  1073. translations: [
  1074. {
  1075. languageCode: LanguageCode.en,
  1076. name: 'Photo AND Pear',
  1077. description: '',
  1078. slug: 'photo-and-pear',
  1079. },
  1080. ],
  1081. filters: [
  1082. {
  1083. code: facetValueCollectionFilter.code,
  1084. arguments: [
  1085. {
  1086. name: 'facetValueIds',
  1087. value: `["${getFacetValueId('pear')}", "${getFacetValueId(
  1088. 'photo',
  1089. )}"]`,
  1090. },
  1091. {
  1092. name: 'containsAny',
  1093. value: `false`,
  1094. },
  1095. ],
  1096. },
  1097. ],
  1098. } as CreateCollectionInput,
  1099. });
  1100. await awaitRunningJobs(adminClient, 5000);
  1101. const { collection } = await adminClient.query<
  1102. Codegen.GetCollectionQuery,
  1103. Codegen.GetCollectionQueryVariables
  1104. >(GET_COLLECTION, {
  1105. id: result.createCollection.id,
  1106. });
  1107. expect(collection!.productVariants.items.map(i => i.name)).toEqual(['Instant Camera']);
  1108. });
  1109. it('photo OR pear', async () => {
  1110. const result = await adminClient.query<
  1111. Codegen.CreateCollectionSelectVariantsMutation,
  1112. Codegen.CreateCollectionSelectVariantsMutationVariables
  1113. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  1114. input: {
  1115. translations: [
  1116. {
  1117. languageCode: LanguageCode.en,
  1118. name: 'Photo OR Pear',
  1119. description: '',
  1120. slug: 'photo-or-pear',
  1121. },
  1122. ],
  1123. filters: [
  1124. {
  1125. code: facetValueCollectionFilter.code,
  1126. arguments: [
  1127. {
  1128. name: 'facetValueIds',
  1129. value: `["${getFacetValueId('pear')}", "${getFacetValueId(
  1130. 'photo',
  1131. )}"]`,
  1132. },
  1133. {
  1134. name: 'containsAny',
  1135. value: `true`,
  1136. },
  1137. ],
  1138. },
  1139. ],
  1140. } as CreateCollectionInput,
  1141. });
  1142. await awaitRunningJobs(adminClient, 5000);
  1143. const { collection } = await adminClient.query<
  1144. Codegen.GetCollectionQuery,
  1145. Codegen.GetCollectionQueryVariables
  1146. >(GET_COLLECTION, {
  1147. id: result.createCollection.id,
  1148. });
  1149. expect(collection!.productVariants.items.map(i => i.name)).toEqual([
  1150. 'Laptop 13 inch 8GB',
  1151. 'Laptop 15 inch 8GB',
  1152. 'Laptop 13 inch 16GB',
  1153. 'Laptop 15 inch 16GB',
  1154. 'Instant Camera',
  1155. 'Camera Lens',
  1156. 'Tripod',
  1157. 'SLR Camera',
  1158. 'Hat',
  1159. ]);
  1160. });
  1161. it('bell OR pear in computers', async () => {
  1162. const result = await adminClient.query<
  1163. Codegen.CreateCollectionSelectVariantsMutation,
  1164. Codegen.CreateCollectionSelectVariantsMutationVariables
  1165. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  1166. input: {
  1167. parentId: computersCollection.id,
  1168. translations: [
  1169. {
  1170. languageCode: LanguageCode.en,
  1171. name: 'Bell OR Pear Computers',
  1172. description: '',
  1173. slug: 'bell-or-pear',
  1174. },
  1175. ],
  1176. filters: [
  1177. {
  1178. code: facetValueCollectionFilter.code,
  1179. arguments: [
  1180. {
  1181. name: 'facetValueIds',
  1182. value: `["${getFacetValueId('pear')}", "${getFacetValueId('bell')}"]`,
  1183. },
  1184. {
  1185. name: 'containsAny',
  1186. value: `true`,
  1187. },
  1188. ],
  1189. },
  1190. ],
  1191. } as CreateCollectionInput,
  1192. });
  1193. await awaitRunningJobs(adminClient, 5000);
  1194. const { collection } = await adminClient.query<
  1195. Codegen.GetCollectionQuery,
  1196. Codegen.GetCollectionQueryVariables
  1197. >(GET_COLLECTION, {
  1198. id: result.createCollection.id,
  1199. });
  1200. expect(collection!.productVariants.items.map(i => i.name)).toEqual([
  1201. 'Laptop 13 inch 8GB',
  1202. 'Laptop 15 inch 8GB',
  1203. 'Laptop 13 inch 16GB',
  1204. 'Laptop 15 inch 16GB',
  1205. 'Curvy Monitor 24 inch',
  1206. 'Curvy Monitor 27 inch',
  1207. ]);
  1208. });
  1209. });
  1210. describe('variantName filter', () => {
  1211. async function createVariantNameFilteredCollection(
  1212. operator: string,
  1213. term: string,
  1214. parentId?: string,
  1215. ): Promise<CollectionFragment> {
  1216. const { createCollection } = await adminClient.query<
  1217. Codegen.CreateCollectionMutation,
  1218. Codegen.CreateCollectionMutationVariables
  1219. >(CREATE_COLLECTION, {
  1220. input: {
  1221. parentId,
  1222. translations: [
  1223. {
  1224. languageCode: LanguageCode.en,
  1225. name: `${operator} ${term}`,
  1226. description: '',
  1227. slug: `${operator} ${term}`,
  1228. },
  1229. ],
  1230. filters: [
  1231. {
  1232. code: variantNameCollectionFilter.code,
  1233. arguments: [
  1234. {
  1235. name: 'operator',
  1236. value: operator,
  1237. },
  1238. {
  1239. name: 'term',
  1240. value: term,
  1241. },
  1242. ],
  1243. },
  1244. ],
  1245. },
  1246. });
  1247. await awaitRunningJobs(adminClient, 5000);
  1248. return createCollection;
  1249. }
  1250. it('contains operator', async () => {
  1251. const collection = await createVariantNameFilteredCollection('contains', 'camera');
  1252. const result = await adminClient.query<
  1253. Codegen.GetCollectionProductsQuery,
  1254. Codegen.GetCollectionProductsQueryVariables
  1255. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1256. id: collection.id,
  1257. });
  1258. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1259. 'Instant Camera',
  1260. 'Camera Lens',
  1261. 'SLR Camera',
  1262. ]);
  1263. });
  1264. it('startsWith operator', async () => {
  1265. const collection = await createVariantNameFilteredCollection('startsWith', 'camera');
  1266. const result = await adminClient.query<
  1267. Codegen.GetCollectionProductsQuery,
  1268. Codegen.GetCollectionProductsQueryVariables
  1269. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1270. id: collection.id,
  1271. });
  1272. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Camera Lens']);
  1273. });
  1274. it('endsWith operator', async () => {
  1275. const collection = await createVariantNameFilteredCollection('endsWith', 'camera');
  1276. const result = await adminClient.query<
  1277. Codegen.GetCollectionProductsQuery,
  1278. Codegen.GetCollectionProductsQueryVariables
  1279. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1280. id: collection.id,
  1281. });
  1282. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1283. 'Instant Camera',
  1284. 'SLR Camera',
  1285. ]);
  1286. });
  1287. it('doesNotContain operator', async () => {
  1288. const collection = await createVariantNameFilteredCollection('doesNotContain', 'camera');
  1289. const result = await adminClient.query<
  1290. Codegen.GetCollectionProductsQuery,
  1291. Codegen.GetCollectionProductsQueryVariables
  1292. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1293. id: collection.id,
  1294. });
  1295. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1296. 'Laptop 13 inch 8GB',
  1297. 'Laptop 15 inch 8GB',
  1298. 'Laptop 13 inch 16GB',
  1299. 'Laptop 15 inch 16GB',
  1300. 'Curvy Monitor 24 inch',
  1301. 'Curvy Monitor 27 inch',
  1302. 'Gaming PC i7-8700 240GB SSD',
  1303. 'Gaming PC R7-2700 240GB SSD',
  1304. 'Gaming PC i7-8700 120GB SSD',
  1305. 'Gaming PC R7-2700 120GB SSD',
  1306. 'Hard Drive 1TB',
  1307. 'Hard Drive 2TB',
  1308. 'Hard Drive 3TB',
  1309. 'Hard Drive 4TB',
  1310. 'Hard Drive 6TB',
  1311. 'Clacky Keyboard',
  1312. 'USB Cable',
  1313. 'Tripod',
  1314. 'Hat',
  1315. 'Boots',
  1316. ]);
  1317. });
  1318. // https://github.com/vendure-ecommerce/vendure/issues/927
  1319. it('nested variantName filter', async () => {
  1320. const parent = await createVariantNameFilteredCollection('contains', 'lap');
  1321. const parentResult = await adminClient.query<
  1322. Codegen.GetCollectionProductsQuery,
  1323. Codegen.GetCollectionProductsQueryVariables
  1324. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1325. id: parent.id,
  1326. });
  1327. expect(parentResult.collection?.productVariants.items.map(i => i.name)).toEqual([
  1328. 'Laptop 13 inch 8GB',
  1329. 'Laptop 15 inch 8GB',
  1330. 'Laptop 13 inch 16GB',
  1331. 'Laptop 15 inch 16GB',
  1332. ]);
  1333. const child = await createVariantNameFilteredCollection('contains', 'GB', parent.id);
  1334. const childResult = await adminClient.query<
  1335. Codegen.GetCollectionProductsQuery,
  1336. Codegen.GetCollectionProductsQueryVariables
  1337. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1338. id: child.id,
  1339. });
  1340. expect(childResult.collection?.productVariants.items.map(i => i.name)).toEqual([
  1341. 'Laptop 13 inch 8GB',
  1342. 'Laptop 15 inch 8GB',
  1343. 'Laptop 13 inch 16GB',
  1344. 'Laptop 15 inch 16GB',
  1345. ]);
  1346. });
  1347. });
  1348. describe('re-evaluation of contents on changes', () => {
  1349. let products: GetProductsWithVariantIdsQuery['products']['items'];
  1350. beforeAll(async () => {
  1351. const result = await adminClient.query<Codegen.GetProductsWithVariantIdsQuery>(gql`
  1352. query GetProductsWithVariantIds {
  1353. products(options: { sort: { id: ASC } }) {
  1354. items {
  1355. id
  1356. name
  1357. variants {
  1358. id
  1359. name
  1360. }
  1361. }
  1362. }
  1363. }
  1364. `);
  1365. products = result.products.items;
  1366. });
  1367. it('updates contents when Product is updated', async () => {
  1368. await adminClient.query<
  1369. Codegen.UpdateProductMutation,
  1370. Codegen.UpdateProductMutationVariables
  1371. >(UPDATE_PRODUCT, {
  1372. input: {
  1373. id: products[1].id,
  1374. facetValueIds: [
  1375. getFacetValueId('electronics'),
  1376. getFacetValueId('computers'),
  1377. getFacetValueId('pear'),
  1378. ],
  1379. },
  1380. });
  1381. await awaitRunningJobs(adminClient, 5000);
  1382. const result = await adminClient.query<
  1383. Codegen.GetCollectionProductsQuery,
  1384. Codegen.GetCollectionProductsQueryVariables
  1385. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
  1386. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1387. 'Laptop 13 inch 8GB',
  1388. 'Laptop 15 inch 8GB',
  1389. 'Laptop 13 inch 16GB',
  1390. 'Laptop 15 inch 16GB',
  1391. 'Curvy Monitor 24 inch',
  1392. 'Curvy Monitor 27 inch',
  1393. 'Instant Camera',
  1394. ]);
  1395. });
  1396. it('updates contents when ProductVariant is updated', async () => {
  1397. const gamingPc240GB = products
  1398. .find(p => p.name === 'Gaming PC')!
  1399. .variants.find(v => v.name.includes('240GB'))!;
  1400. await adminClient.query<
  1401. Codegen.UpdateProductVariantsMutation,
  1402. Codegen.UpdateProductVariantsMutationVariables
  1403. >(UPDATE_PRODUCT_VARIANTS, {
  1404. input: [
  1405. {
  1406. id: gamingPc240GB.id,
  1407. facetValueIds: [getFacetValueId('pear')],
  1408. },
  1409. ],
  1410. });
  1411. await awaitRunningJobs(adminClient, 5000);
  1412. const result = await adminClient.query<
  1413. Codegen.GetCollectionProductsQuery,
  1414. Codegen.GetCollectionProductsQueryVariables
  1415. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
  1416. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1417. 'Laptop 13 inch 8GB',
  1418. 'Laptop 15 inch 8GB',
  1419. 'Laptop 13 inch 16GB',
  1420. 'Laptop 15 inch 16GB',
  1421. 'Curvy Monitor 24 inch',
  1422. 'Curvy Monitor 27 inch',
  1423. 'Gaming PC i7-8700 240GB SSD',
  1424. 'Instant Camera',
  1425. ]);
  1426. });
  1427. it('correctly filters when ProductVariant and Product both have matching FacetValue', async () => {
  1428. const gamingPc240GB = products
  1429. .find(p => p.name === 'Gaming PC')!
  1430. .variants.find(v => v.name.includes('240GB'))!;
  1431. await adminClient.query<
  1432. Codegen.UpdateProductVariantsMutation,
  1433. Codegen.UpdateProductVariantsMutationVariables
  1434. >(UPDATE_PRODUCT_VARIANTS, {
  1435. input: [
  1436. {
  1437. id: gamingPc240GB.id,
  1438. facetValueIds: [getFacetValueId('electronics'), getFacetValueId('pear')],
  1439. },
  1440. ],
  1441. });
  1442. await awaitRunningJobs(adminClient, 5000);
  1443. const result = await adminClient.query<
  1444. Codegen.GetCollectionProductsQuery,
  1445. Codegen.GetCollectionProductsQueryVariables
  1446. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
  1447. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1448. 'Laptop 13 inch 8GB',
  1449. 'Laptop 15 inch 8GB',
  1450. 'Laptop 13 inch 16GB',
  1451. 'Laptop 15 inch 16GB',
  1452. 'Curvy Monitor 24 inch',
  1453. 'Curvy Monitor 27 inch',
  1454. 'Gaming PC i7-8700 240GB SSD',
  1455. 'Instant Camera',
  1456. ]);
  1457. });
  1458. });
  1459. describe('filter inheritance', () => {
  1460. let clothesCollectionId: string;
  1461. it('filter inheritance of nested collections (issue #158)', async () => {
  1462. const { createCollection: pearElectronics } = await adminClient.query<
  1463. Codegen.CreateCollectionSelectVariantsMutation,
  1464. Codegen.CreateCollectionSelectVariantsMutationVariables
  1465. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  1466. input: {
  1467. parentId: electronicsCollection.id,
  1468. translations: [
  1469. {
  1470. languageCode: LanguageCode.en,
  1471. name: 'pear electronics',
  1472. description: '',
  1473. slug: 'pear-electronics',
  1474. },
  1475. ],
  1476. filters: [
  1477. {
  1478. code: facetValueCollectionFilter.code,
  1479. arguments: [
  1480. {
  1481. name: 'facetValueIds',
  1482. value: `["${getFacetValueId('pear')}"]`,
  1483. },
  1484. {
  1485. name: 'containsAny',
  1486. value: `false`,
  1487. },
  1488. ],
  1489. },
  1490. ],
  1491. } as CreateCollectionInput,
  1492. });
  1493. await awaitRunningJobs(adminClient, 5000);
  1494. const result = await adminClient.query<
  1495. Codegen.GetCollectionProductsQuery,
  1496. Codegen.GetCollectionProductsQueryVariables
  1497. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearElectronics.id });
  1498. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
  1499. 'Laptop 13 inch 8GB',
  1500. 'Laptop 15 inch 8GB',
  1501. 'Laptop 13 inch 16GB',
  1502. 'Laptop 15 inch 16GB',
  1503. 'Curvy Monitor 24 inch',
  1504. 'Curvy Monitor 27 inch',
  1505. 'Gaming PC i7-8700 240GB SSD',
  1506. 'Instant Camera',
  1507. // no "Hat"
  1508. ]);
  1509. });
  1510. it('child collection with no inheritance', async () => {
  1511. const { createCollection: clothesCollection } = await adminClient.query<
  1512. Codegen.CreateCollectionSelectVariantsMutation,
  1513. Codegen.CreateCollectionSelectVariantsMutationVariables
  1514. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  1515. input: {
  1516. parentId: electronicsCollection.id,
  1517. translations: [
  1518. {
  1519. languageCode: LanguageCode.en,
  1520. name: 'clothes',
  1521. description: '',
  1522. slug: 'clothes',
  1523. },
  1524. ],
  1525. inheritFilters: false,
  1526. filters: [
  1527. {
  1528. code: facetValueCollectionFilter.code,
  1529. arguments: [
  1530. {
  1531. name: 'facetValueIds',
  1532. value: `["${getFacetValueId('clothing')}"]`,
  1533. },
  1534. {
  1535. name: 'containsAny',
  1536. value: `false`,
  1537. },
  1538. ],
  1539. },
  1540. ],
  1541. } as CreateCollectionInput,
  1542. });
  1543. await awaitRunningJobs(adminClient, 5000);
  1544. clothesCollectionId = clothesCollection.id;
  1545. const result = await adminClient.query<
  1546. Codegen.GetCollectionProductsQuery,
  1547. Codegen.GetCollectionProductsQueryVariables
  1548. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: clothesCollection.id });
  1549. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Hat', 'Boots']);
  1550. });
  1551. it('grandchild collection with inheritance (root -> no inherit -> inherit', async () => {
  1552. const { createCollection: footwearCollection } = await adminClient.query<
  1553. Codegen.CreateCollectionSelectVariantsMutation,
  1554. Codegen.CreateCollectionSelectVariantsMutationVariables
  1555. >(CREATE_COLLECTION_SELECT_VARIANTS, {
  1556. input: {
  1557. parentId: clothesCollectionId,
  1558. translations: [
  1559. {
  1560. languageCode: LanguageCode.en,
  1561. name: 'footwear',
  1562. description: '',
  1563. slug: 'footwear',
  1564. },
  1565. ],
  1566. inheritFilters: true,
  1567. filters: [
  1568. {
  1569. code: facetValueCollectionFilter.code,
  1570. arguments: [
  1571. {
  1572. name: 'facetValueIds',
  1573. value: `["${getFacetValueId('footwear')}"]`,
  1574. },
  1575. {
  1576. name: 'containsAny',
  1577. value: `false`,
  1578. },
  1579. ],
  1580. },
  1581. ],
  1582. } as CreateCollectionInput,
  1583. });
  1584. await awaitRunningJobs(adminClient, 5000);
  1585. const result = await adminClient.query<
  1586. Codegen.GetCollectionProductsQuery,
  1587. Codegen.GetCollectionProductsQueryVariables
  1588. >(GET_COLLECTION_PRODUCT_VARIANTS, { id: footwearCollection.id });
  1589. expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Boots']);
  1590. });
  1591. });
  1592. });
  1593. describe('Product collections property', () => {
  1594. it('returns all collections to which the Product belongs', async () => {
  1595. const result = await adminClient.query<
  1596. Codegen.GetCollectionsForProductsQuery,
  1597. Codegen.GetCollectionsForProductsQueryVariables
  1598. >(GET_COLLECTIONS_FOR_PRODUCTS, { term: 'camera' });
  1599. expect(result.products.items[0].collections).toEqual([
  1600. {
  1601. id: 'T_3',
  1602. name: 'Electronics',
  1603. },
  1604. {
  1605. id: 'T_5',
  1606. name: 'Pear',
  1607. },
  1608. {
  1609. id: 'T_9',
  1610. name: 'Electronics Breadcrumbs',
  1611. },
  1612. {
  1613. id: 'T_14',
  1614. name: 'Photo AND Pear',
  1615. },
  1616. {
  1617. id: 'T_15',
  1618. name: 'Photo OR Pear',
  1619. },
  1620. {
  1621. id: 'T_17',
  1622. name: 'contains camera',
  1623. },
  1624. {
  1625. id: 'T_19',
  1626. name: 'endsWith camera',
  1627. },
  1628. {
  1629. id: 'T_23',
  1630. name: 'pear electronics',
  1631. },
  1632. ]);
  1633. });
  1634. });
  1635. describe('productVariants list', () => {
  1636. it('does not list variants from deleted products', async () => {
  1637. await adminClient.query<Codegen.DeleteProductMutation, Codegen.DeleteProductMutationVariables>(
  1638. DELETE_PRODUCT,
  1639. {
  1640. id: 'T_2', // curvy monitor
  1641. },
  1642. );
  1643. await awaitRunningJobs(adminClient, 5000);
  1644. const { collection } = await adminClient.query<
  1645. Codegen.GetCollectionProductsQuery,
  1646. Codegen.GetCollectionProductsQueryVariables
  1647. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1648. id: pearCollection.id,
  1649. });
  1650. expect(collection!.productVariants.items.map(i => i.name)).toEqual([
  1651. 'Laptop 13 inch 8GB',
  1652. 'Laptop 15 inch 8GB',
  1653. 'Laptop 13 inch 16GB',
  1654. 'Laptop 15 inch 16GB',
  1655. 'Gaming PC i7-8700 240GB SSD',
  1656. 'Instant Camera',
  1657. ]);
  1658. });
  1659. // https://github.com/vendure-ecommerce/vendure/issues/1213
  1660. it('does not list deleted variants', async () => {
  1661. await adminClient.query<
  1662. Codegen.DeleteProductVariantMutation,
  1663. Codegen.DeleteProductVariantMutationVariables
  1664. >(DELETE_PRODUCT_VARIANT, {
  1665. id: 'T_18', // Instant Camera
  1666. });
  1667. await awaitRunningJobs(adminClient, 5000);
  1668. const { collection } = await adminClient.query<
  1669. Codegen.GetCollectionProductsQuery,
  1670. Codegen.GetCollectionProductsQueryVariables
  1671. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1672. id: pearCollection.id,
  1673. });
  1674. expect(collection!.productVariants.items.map(i => i.name)).toEqual([
  1675. 'Laptop 13 inch 8GB',
  1676. 'Laptop 15 inch 8GB',
  1677. 'Laptop 13 inch 16GB',
  1678. 'Laptop 15 inch 16GB',
  1679. 'Gaming PC i7-8700 240GB SSD',
  1680. // 'Instant Camera',
  1681. ]);
  1682. });
  1683. it('does not list disabled variants in Shop API', async () => {
  1684. await adminClient.query<
  1685. Codegen.UpdateProductVariantsMutation,
  1686. Codegen.UpdateProductVariantsMutationVariables
  1687. >(UPDATE_PRODUCT_VARIANTS, {
  1688. input: [{ id: 'T_1', enabled: false }],
  1689. });
  1690. await awaitRunningJobs(adminClient, 5000);
  1691. const { collection } = await shopClient.query<
  1692. Codegen.GetCollectionProductsQuery,
  1693. Codegen.GetCollectionProductsQueryVariables
  1694. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1695. id: pearCollection.id,
  1696. });
  1697. expect(collection!.productVariants.items.map(i => i.id).includes('T_1')).toBe(false);
  1698. });
  1699. it('does not list variants of disabled products in Shop API', async () => {
  1700. await adminClient.query<Codegen.UpdateProductMutation, Codegen.UpdateProductMutationVariables>(
  1701. UPDATE_PRODUCT,
  1702. {
  1703. input: { id: 'T_1', enabled: false },
  1704. },
  1705. );
  1706. await awaitRunningJobs(adminClient, 5000);
  1707. const { collection } = await shopClient.query<
  1708. Codegen.GetCollectionProductsQuery,
  1709. Codegen.GetCollectionProductsQueryVariables
  1710. >(GET_COLLECTION_PRODUCT_VARIANTS, {
  1711. id: pearCollection.id,
  1712. });
  1713. expect(collection!.productVariants.items.map(i => i.id).includes('T_1')).toBe(false);
  1714. expect(collection!.productVariants.items.map(i => i.id).includes('T_2')).toBe(false);
  1715. expect(collection!.productVariants.items.map(i => i.id).includes('T_3')).toBe(false);
  1716. expect(collection!.productVariants.items.map(i => i.id).includes('T_4')).toBe(false);
  1717. });
  1718. it('handles other languages', async () => {
  1719. await adminClient.query<Codegen.UpdateProductMutation, Codegen.UpdateProductMutationVariables>(
  1720. UPDATE_PRODUCT,
  1721. {
  1722. input: { id: 'T_1', enabled: true },
  1723. },
  1724. );
  1725. await adminClient.query<
  1726. Codegen.UpdateProductVariantsMutation,
  1727. Codegen.UpdateProductVariantsMutationVariables
  1728. >(UPDATE_PRODUCT_VARIANTS, {
  1729. input: [
  1730. {
  1731. id: 'T_2',
  1732. translations: [{ languageCode: LanguageCode.de, name: 'Taschenrechner 15 Zoll' }],
  1733. },
  1734. ],
  1735. });
  1736. const { collection } = await shopClient.query<
  1737. Codegen.GetCollectionProductsQuery,
  1738. Codegen.GetCollectionProductsQueryVariables
  1739. >(
  1740. GET_COLLECTION_PRODUCT_VARIANTS,
  1741. {
  1742. id: pearCollection.id,
  1743. },
  1744. { languageCode: LanguageCode.de },
  1745. );
  1746. expect(collection!.productVariants.items.map(i => i.name)).toEqual([
  1747. 'Taschenrechner 15 Zoll',
  1748. 'Laptop 13 inch 16GB',
  1749. 'Laptop 15 inch 16GB',
  1750. 'Gaming PC i7-8700 240GB SSD',
  1751. ]);
  1752. });
  1753. });
  1754. function getFacetValueId(code: string): string {
  1755. const match = facetValues.find(fv => fv.code === code);
  1756. if (!match) {
  1757. throw new Error(`Could not find a FacetValue with the code "${code}"`);
  1758. }
  1759. return match.id;
  1760. }
  1761. });
  1762. export const GET_COLLECTION = gql`
  1763. query GetCollection($id: ID, $slug: String, $variantListOptions: ProductVariantListOptions) {
  1764. collection(id: $id, slug: $slug) {
  1765. ...Collection
  1766. productVariants(options: $variantListOptions) {
  1767. items {
  1768. id
  1769. name
  1770. price
  1771. }
  1772. }
  1773. }
  1774. }
  1775. ${COLLECTION_FRAGMENT}
  1776. `;
  1777. export const MOVE_COLLECTION = gql`
  1778. mutation MoveCollection($input: MoveCollectionInput!) {
  1779. moveCollection(input: $input) {
  1780. ...Collection
  1781. }
  1782. }
  1783. ${COLLECTION_FRAGMENT}
  1784. `;
  1785. const GET_FACET_VALUES = gql`
  1786. query GetFacetValues {
  1787. facets {
  1788. items {
  1789. values {
  1790. ...FacetValue
  1791. }
  1792. }
  1793. }
  1794. }
  1795. ${FACET_VALUE_FRAGMENT}
  1796. `;
  1797. const GET_COLLECTION_PRODUCT_VARIANTS = gql`
  1798. query GetCollectionProducts($id: ID!) {
  1799. collection(id: $id) {
  1800. productVariants(options: { sort: { id: ASC } }) {
  1801. items {
  1802. id
  1803. name
  1804. facetValues {
  1805. code
  1806. }
  1807. productId
  1808. }
  1809. }
  1810. }
  1811. }
  1812. `;
  1813. const CREATE_COLLECTION_SELECT_VARIANTS = gql`
  1814. mutation CreateCollectionSelectVariants($input: CreateCollectionInput!) {
  1815. createCollection(input: $input) {
  1816. id
  1817. productVariants {
  1818. items {
  1819. name
  1820. }
  1821. totalItems
  1822. }
  1823. }
  1824. }
  1825. `;
  1826. const GET_COLLECTION_BREADCRUMBS = gql`
  1827. query GetCollectionBreadcrumbs($id: ID!) {
  1828. collection(id: $id) {
  1829. breadcrumbs {
  1830. id
  1831. name
  1832. slug
  1833. }
  1834. }
  1835. }
  1836. `;
  1837. const GET_COLLECTIONS_FOR_PRODUCTS = gql`
  1838. query GetCollectionsForProducts($term: String!) {
  1839. products(options: { filter: { name: { contains: $term } } }) {
  1840. items {
  1841. id
  1842. name
  1843. collections {
  1844. id
  1845. name
  1846. }
  1847. }
  1848. }
  1849. }
  1850. `;
  1851. const DELETE_COLLECTION = gql`
  1852. mutation DeleteCollection($id: ID!) {
  1853. deleteCollection(id: $id) {
  1854. result
  1855. message
  1856. }
  1857. }
  1858. `;
  1859. const GET_PRODUCT_COLLECTIONS = gql`
  1860. query GetProductCollections($id: ID!) {
  1861. product(id: $id) {
  1862. id
  1863. collections {
  1864. id
  1865. name
  1866. }
  1867. }
  1868. }
  1869. `;
  1870. const GET_PRODUCT_COLLECTIONS_WITH_PARENT = gql`
  1871. query GetProductCollectionsWithParent($id: ID!) {
  1872. product(id: $id) {
  1873. id
  1874. collections {
  1875. id
  1876. name
  1877. parent {
  1878. id
  1879. name
  1880. }
  1881. }
  1882. }
  1883. }
  1884. `;
  1885. const GET_COLLECTION_NESTED_PARENTS = gql`
  1886. query GetCollectionNestedParents {
  1887. collections {
  1888. items {
  1889. id
  1890. name
  1891. parent {
  1892. name
  1893. parent {
  1894. name
  1895. parent {
  1896. name
  1897. }
  1898. }
  1899. }
  1900. }
  1901. }
  1902. }
  1903. `;